我对 Appbar 有这些要求,但我找不到解决它们的方法。
我创建了几个模型来帮助获得所需的结果。
这是拉伸(stretch)时的 Appbar:
这是关闭时的 Appbar:
最佳答案
您可以创建自己的SliverAppBar
通过扩展 SliverPersistentHeaderDelegate
.
平移、缩放和不透明度更改将在 build(...)
中完成。方法,因为这将在范围更改期间调用(通过滚动),minExtent <-> maxExtent
.
这是一个示例代码。
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
delegate: MySliverAppBar(
title: 'Sample',
minWidth: 50,
minHeight: 25,
leftMaxWidth: 200,
leftMaxHeight: 100,
rightMaxWidth: 100,
rightMaxHeight: 50,
shrinkedTopPos: 10,
),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, int i) => Container(
height: 50,
color: Color.fromARGB(
255,
Random().nextInt(255),
Random().nextInt(255),
Random().nextInt(255),
),
),
childCount: 50,
),
),
],
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
MySliverAppBar({
required this.title,
required this.minWidth,
required this.minHeight,
required this.leftMaxWidth,
required this.leftMaxHeight,
required this.rightMaxWidth,
required this.rightMaxHeight,
this.titleStyle = const TextStyle(fontSize: 26),
this.shrinkedTopPos = 0,
});
final String title;
final TextStyle titleStyle;
final double minWidth;
final double minHeight;
final double leftMaxWidth;
final double leftMaxHeight;
final double rightMaxWidth;
final double rightMaxHeight;
final double shrinkedTopPos;
final GlobalKey _titleKey = GlobalKey();
double? _topPadding;
double? _centerX;
Size? _titleSize;
double get _shrinkedTopPos => _topPadding! + shrinkedTopPos;
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
if (_topPadding == null) {
_topPadding = MediaQuery.of(context).padding.top;
}
if (_centerX == null) {
_centerX = MediaQuery.of(context).size.width / 2;
}
if (_titleSize == null) {
_titleSize = _calculateTitleSize(title, titleStyle);
}
double percent = shrinkOffset / (maxExtent - minExtent);
percent = percent > 1 ? 1 : percent;
return Container(
color: Colors.red,
child: Stack(
children: <Widget>[
_buildTitle(shrinkOffset),
_buildLeftImage(percent),
_buildRightImage(percent),
],
),
);
}
Size _calculateTitleSize(String text, TextStyle style) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: 1,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size;
}
Widget _buildTitle(double shrinkOffset) => Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(top: _topPadding!),
child: Opacity(
opacity: shrinkOffset / maxExtent,
child: Text(title, key: _titleKey, style: titleStyle),
),
),
);
double getScaledWidth(double width, double percent) =>
width - ((width - minWidth) * percent);
double getScaledHeight(double height, double percent) =>
height - ((height - minHeight) * percent);
/// 20 is the padding between the image and the title
double get shrinkedHorizontalPos =>
(_centerX! - (_titleSize!.width / 2)) - minWidth - 20;
Widget _buildLeftImage(double percent) {
final double topMargin = minExtent;
final double rangeLeft =
(_centerX! - (leftMaxWidth / 2)) - shrinkedHorizontalPos;
final double rangeTop = topMargin - _shrinkedTopPos;
final double top = topMargin - (rangeTop * percent);
final double left =
(_centerX! - (leftMaxWidth / 2)) - (rangeLeft * percent);
return Positioned(
left: left,
top: top,
child: Container(
width: getScaledWidth(leftMaxWidth, percent),
height: getScaledHeight(leftMaxHeight, percent),
color: Colors.black,
),
);
}
Widget _buildRightImage(double percent) {
final double topMargin = minExtent + (rightMaxHeight / 2);
final double rangeRight =
(_centerX! - (rightMaxWidth / 2)) - shrinkedHorizontalPos;
final double rangeTop = topMargin - _shrinkedTopPos;
final double top = topMargin - (rangeTop * percent);
final double right =
(_centerX! - (rightMaxWidth / 2)) - (rangeRight * percent);
return Positioned(
right: right,
top: top,
child: Container(
width: getScaledWidth(rightMaxWidth, percent),
height: getScaledHeight(rightMaxHeight, percent),
color: Colors.white,
),
);
}
@override
double get maxExtent => 300;
@override
double get minExtent => _topPadding! + 50;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
false;
}
关于flutter - 如何在 SliverAppBar 中为项目的位置设置动画以在关闭时围绕标题移动它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66825376/