动画小部件对齐

时间:2019-12-29 17:14:52

标签: flutter

我正在构建一个自定义的灵活应用栏,以在NestedScrollView中使用,并且遇到了动画问题。

我想要实现的是这样的:

enter image description here

在展开状态下,文本与“个人资料”图片的顶部对齐(橙色),但是当栏折叠时,其最终在中心对齐。我还需要所有元素(文本+图片)进行相应缩放。

我可以使用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的值缩放元素,但是我不知道是如何为文本部分的对齐方式设置动画,以使其结束完全与图片在中心对齐。

有什么想法吗? :)

1 个答案:

答案 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;
  }
}