我是一名初级开发人员。
我想做一个像我提到的咖啡应用程序一样的主页滚动,但我不知道如何在互联网上搜索。
第一个图像弹出在滑块应用栏正下方的区域上方。有一个类似将容器放在容器顶部的事件。
我可能没有解释清楚,所以我想分享视频。
视频在这里: https://drive.google.com/file/d/1xcGZggU_kQXvEjpDCa8iqkGCtytSUfoA/view?usp=sharing
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart' show clampDouble;
class CustomFlexibleSpaceBar extends StatefulWidget {
const CustomFlexibleSpaceBar({
super.key,
this.title,
this.background,
this.centerTitle,
this.titlePadding,
this.collapseMode = CollapseMode.parallax,
this.stretchModes = const <StretchMode>[StretchMode.zoomBackground],
this.expandedTitleScale = 1.5,
});
final Widget? title;
final Widget? background;
final bool? centerTitle;
final EdgeInsetsGeometry? titlePadding;
final CollapseMode collapseMode;
final List<StretchMode> stretchModes;
final double expandedTitleScale;
@override
// ignore: library_private_types_in_public_api
_CustomFlexibleSpaceBarState createState() => _CustomFlexibleSpaceBarState();
}
class _CustomFlexibleSpaceBarState extends State<CustomFlexibleSpaceBar> {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final FlexibleSpaceBarSettings settings = context
.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>()!;
final List<Widget> children = <Widget>[];
final double deltaExtent = settings.maxExtent - settings.minExtent;
final double t = clampDouble(
1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent,
0.0,
1.0);
if (widget.background != null) {
final double fadeStart =
math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
const double fadeEnd = 1.0;
assert(fadeStart <= fadeEnd);
double height = settings.maxExtent;
if (widget.stretchModes.contains(StretchMode.zoomBackground) &&
constraints.maxHeight > height) {
height = constraints.maxHeight;
}
final double topPadding = _getCollapsePadding(t, settings);
children.add(Positioned(
top: topPadding,
left: 0.0,
right: 0.0,
height: height,
child: widget.background!,
));
if (widget.stretchModes.contains(StretchMode.blurBackground) &&
constraints.maxHeight > settings.maxExtent) {
final double blurAmount =
(constraints.maxHeight - settings.maxExtent) / 10;
children.add(Positioned.fill(
child: BackdropFilter(
filter: ui.ImageFilter.blur(
sigmaX: blurAmount,
sigmaY: blurAmount,
),
child: Container(
color: Colors.transparent,
),
),
));
}
}
if (widget.title != null) {
final ThemeData theme = Theme.of(context);
Widget? title;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
title = widget.title;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
title = Semantics(
namesRoute: true,
child: widget.title,
);
}
if (widget.stretchModes.contains(StretchMode.fadeTitle) &&
constraints.maxHeight > settings.maxExtent) {
final double stretchOpacity = 1 -
clampDouble((constraints.maxHeight - settings.maxExtent) / 100,
0.0, 1.0);
title = Opacity(
opacity: stretchOpacity,
child: title,
);
}
final double opacity = settings.toolbarOpacity;
if (opacity > 0.0) {
TextStyle titleStyle = theme.primaryTextTheme.titleLarge!;
titleStyle = titleStyle.copyWith(
color: titleStyle.color!.withOpacity(opacity),
);
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
final EdgeInsetsGeometry padding = widget.titlePadding ??
EdgeInsetsDirectional.only(
start: effectiveCenterTitle && !(settings.hasLeading ?? false)
? 0.0
: 72.0,
bottom: 16.0,
);
final double scaleValue =
Tween<double>(begin: widget.expandedTitleScale, end: 1.0)
.transform(t);
final Matrix4 scaleTransform = Matrix4.identity()
..scale(scaleValue, scaleValue, 1.0);
final Alignment titleAlignment =
_getTitleAlignment(effectiveCenterTitle);
children.add(Container(
padding: padding,
child: Transform(
alignment: titleAlignment,
transform: scaleTransform,
child: Align(
alignment: titleAlignment,
child: DefaultTextStyle(
style: titleStyle,
child: LayoutBuilder(
builder:
(BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth / scaleValue,
alignment: titleAlignment,
child: title,
);
},
),
),
),
),
));
}
}
return ClipRect(child: Stack(children: children));
},
);
}
double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) {
switch (widget.collapseMode) {
case CollapseMode.pin:
return -(settings.maxExtent - settings.currentExtent);
case CollapseMode.none:
return 0.0;
case CollapseMode.parallax:
final double deltaExtent = settings.maxExtent - settings.minExtent;
return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t);
}
}
bool _getEffectiveCenterTitle(ThemeData theme) {
if (widget.centerTitle != null) {
return widget.centerTitle!;
}
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return false;
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return true;
}
}
Alignment _getTitleAlignment(bool effectiveCenterTitle) {
if (effectiveCenterTitle) {
return Alignment.bottomCenter;
}
final TextDirection textDirection = Directionality.of(context);
switch (textDirection) {
case TextDirection.rtl:
return Alignment.bottomRight;
case TextDirection.ltr:
return Alignment.bottomLeft;
}
}
}