我正在构建一个自定义的灵活应用栏,以在NestedScrollView
中使用,并且遇到了动画问题。
我想要实现的是这样的:
在展开状态下,文本与“个人资料”图片的顶部对齐(橙色),但是当栏折叠时,其最终在中心对齐。我还需要所有元素(文本+图片)进行相应缩放。
我可以使用LayoutBuilder
和一些数学知识来访问条形的当前扩展因子
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double paddingTop = MediaQuery.of(context).padding.top;
double maxExtent = kExpandedHeight + paddingTop;
double minExtent = kToolbarHeight + paddingTop;
final double deltaExtent = maxExtent - minExtent;
// 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t = (1.0 - (constraints.maxHeight - minExtent) / deltaExtent)
.clamp(0.0, 1.0);
// t can be used to animate here
});
我已经设法使用Transform
小部件和t
的值缩放元素,但是我不知道是如何为文本部分的对齐方式设置动画,以使其结束完全与图片在中心对齐。
有什么想法吗? :)
答案 0 :(得分:1)
尝试一下
class Act_Demo extends StatefulWidget {
@override
_Act_DemoState createState() => _Act_DemoState();
}
class _Act_DemoState extends State<Act_Demo> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: CustomScrollView(
slivers: <Widget>[
TransitionAppBar(
backgroundColor: Colors.red,
extent: 150,
avatar: ListTile(
title: Text("Name", style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),),
subtitle: Text("abc@gmail.com"),
trailing: CircleAvatar(backgroundColor: Colors.orange,radius: 30.0,),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Container(
child: ListTile(
title: Text("${index}a"),
));
}, childCount: 25))
],
),
),
);
}
}
。
class TransitionAppBar extends StatelessWidget {
final Widget avatar;
final double extent;
final Color backgroundColor;
TransitionAppBar({this.avatar, this.backgroundColor = Colors.transparent, this.extent = 200, Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: _TransitionAppBarDelegate(
avatar: avatar,
backgroundColor: backgroundColor,
extent: extent > 150 ? extent : 150
),
);
}
}
class _TransitionAppBarDelegate extends SliverPersistentHeaderDelegate {
final _avatarAlignTween = AlignmentTween(begin: Alignment.center, end: Alignment.topCenter);
final Widget avatar;
final double extent;
final Color backgroundColor;
_TransitionAppBarDelegate({this.avatar, this.backgroundColor, this.extent = 200})
: assert(avatar != null),
assert(backgroundColor != null),
assert(extent == null || extent >= 150);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final progress = shrinkOffset / maxExtent;
final avatarAlign = _avatarAlignTween.lerp(progress);
return Container(
color: backgroundColor,
child: Align(
alignment: avatarAlign,
child: Container(
child: avatar,
),
),
);
}
@override
double get maxExtent => extent;
@override
double get minExtent => 70;
@override
bool shouldRebuild(_TransitionAppBarDelegate oldDelegate) {
return avatar != oldDelegate.avatar;
}
}