我怎样才能拥有这样的动画 SliverAppBar?

How can I have an animated SliverAppBar like this?

我想让这些动画流畅,我尝试使用 AnimatedSizeAnimatedOpacityAnimatedPositioned 但几乎没有错误。而且我不知道如何将这些与 SliverAppBar 一起使用。在其他示例中,我看到有人使用 LayoutBuilder 但他们没有共享完整代码,因此我无法对其进行测试。我将分享我的代码片段,但它在两种状态之间的转换非常奇怪。

我要CircleAvatar变小换位置 我希望 Icon(Icons.arrow_back)Text('Previous Page') 消失。 我也希望我的名字和工作消失。 我想让那三个 Containers 变小并改变位置。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dialog Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: CustomScrollView(
          slivers: [
            SliverAppBar(
              elevation: 0,
              pinned: true,
              expandedHeight: 190,
              collapsedHeight: kToolbarHeight + 8,
              flexibleSpace: FlexibleSpaceBar(
                collapseMode: CollapseMode.none,
                centerTitle: true,
                titlePadding: EdgeInsetsDirectional.only(
                  start: 20.0,
                  end: 20.0,
                  top: 12.0,
                  bottom: 12.0,
                ),
                title: SafeArea(
                  child: Column(
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          CircleAvatar(
                            radius: 16,
                          ),
                          Row(
                            children: [
                              Container(
                                decoration: BoxDecoration(
                                  color: Colors.white10,
                                  borderRadius: BorderRadius.all(
                                    Radius.circular(12),
                                  ),
                                ),
                                padding: EdgeInsets.all(8.0),
                                child: Icon(Icons.person),
                              ),
                              SizedBox(
                                width: 8,
                              ),
                              Container(
                                decoration: BoxDecoration(
                                  color: Colors.white10,
                                  borderRadius: BorderRadius.all(
                                    Radius.circular(12),
                                  ),
                                ),
                                padding: EdgeInsets.all(8.0),
                                child: Icon(Icons.menu),
                              ),
                              SizedBox(
                                width: 8,
                              ),
                              Container(
                                decoration: BoxDecoration(
                                  color: Colors.white10,
                                  borderRadius: BorderRadius.all(
                                    Radius.circular(12),
                                  ),
                                ),
                                padding: EdgeInsets.all(8.0),
                                child: Icon(Icons.message),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
                background: SafeArea(
                  child: Column(
                    children: [
                      Container(
                        alignment: Alignment.topLeft,
                        child: Row(
                          children: [
                            IconButton(
                              icon: Icon(Icons.arrow_back),
                              onPressed: () {},
                            ),
                            Text(
                              'PREVIOUS PAGE',
                              style: TextStyle(
                                fontSize: 12,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.only(
                          left: 20,
                          top: 12,
                          bottom: 24,
                        ),
                        child: Row(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            CircleAvatar(
                              radius: 20,
                            ),
                            SizedBox(
                              width: 12,
                            ),
                            Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Text(
                                  'Sertan Hakkı İmamoğlu',
                                  style: TextStyle(
                                    fontSize: 18,
                                    color: Colors.white,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                                Text(
                                  'Student',
                                  style: TextStyle(
                                    fontSize: 14,
                                    color: Colors.grey,
                                  ),
                                ),
                                SizedBox(
                                  height: 12,
                                ),
                                Row(
                                  children: [
                                    Container(
                                      decoration: BoxDecoration(
                                        color: Colors.white10,
                                        borderRadius: BorderRadius.all(
                                          Radius.circular(12),
                                        ),
                                      ),
                                      padding: EdgeInsets.symmetric(
                                        horizontal: 34,
                                        vertical: 12,
                                      ),
                                      child: Icon(Icons.menu),
                                    ),
                                    SizedBox(
                                      width: 4,
                                    ),
                                    Container(
                                      decoration: BoxDecoration(
                                        color: Colors.white10,
                                        borderRadius: BorderRadius.all(
                                          Radius.circular(12),
                                        ),
                                      ),
                                      padding: EdgeInsets.symmetric(
                                        horizontal: 34,
                                        vertical: 12,
                                      ),
                                      child: Icon(Icons.person),
                                    ),
                                    SizedBox(
                                      width: 4,
                                    ),
                                    Container(
                                      decoration: BoxDecoration(
                                        color: Colors.white10,
                                        borderRadius: BorderRadius.all(
                                          Radius.circular(12),
                                        ),
                                      ),
                                      padding: EdgeInsets.symmetric(
                                        horizontal: 34,
                                        vertical: 12,
                                      ),
                                      child: Icon(Icons.message),
                                    ),
                                  ],
                                )
                              ],
                            )
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Container(
                    color: index.isOdd ? Colors.white : Colors.black12,
                    height: 100.0,
                    child: Center(
                      child: Text('$index', textScaleFactor: 5),
                    ),
                  );
                },
                childCount: 20,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

这是我对你的问题的看法,正如 pskink 所建议的,我使用了 SliverPersistentHeader 并试图让它响应,所以当尺寸减小时它有一点动画。

示例代码

class CustomPageHeader extends SliverPersistentHeaderDelegate {
  CustomPageHeader({
    required double collapsedHeight,
    required double expandedHeight,
  }) : minExtent = collapsedHeight, maxExtent = expandedHeight;

  @override
  final double minExtent;

  @override
  final double maxExtent;

  Widget _buildBtn(IconData icon, double scale) {
    double horizontal = 34.0 * scale;
    horizontal = horizontal < 8 ? 8.0 : horizontal;

    double vertical = 12.0 * scale;
    vertical = vertical < 8 ? 8.0 : vertical;

    return Container(
      decoration: const BoxDecoration(
        color: Colors.white10,
        borderRadius: BorderRadius.all(
          Radius.circular(12),
        ),
      ),
      padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical),
      child: Icon(icon),
    );
  }

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    final theme = Theme.of(context);
    final backgroundColor =
        theme.appBarTheme.backgroundColor ?? theme.colorScheme.primary;
    final scale = 1 - shrinkOffset / maxExtent;
    final isReduced = shrinkOffset >= maxExtent * 0.12;
    final wrappedBtns = Wrap(
      spacing: 8,
      children: [
        _buildBtn(Icons.menu, scale),
        _buildBtn(Icons.person, scale),
        _buildBtn(Icons.message, scale),
      ],
    );
    
    double avatarRadius = 20.0 * scale;
    avatarRadius = avatarRadius > 16 ? avatarRadius : 16.0;

    return Container(
      padding: const EdgeInsets.only(
        left: 20,
        top: 12,
        bottom: 12,
      ),
      color: backgroundColor,
      child: Column(
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              CircleAvatar(radius: avatarRadius),
              !isReduced
                  ? const SizedBox(width: 12)
                  : Expanded(child: Container()),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  if (!isReduced)
                    Text(
                      'Sertan Hakkı İmamoğlu',
                      style: TextStyle(
                        fontSize: 18 * scale,
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  if (!isReduced)
                    Text(
                      'Student',
                      style: TextStyle(
                        fontSize: 14 * scale,
                        color: Colors.grey,
                      ),
                    ),
                ],
              ),
              if (isReduced) wrappedBtns,
            ],
          ),
          Flexible(
            child: Container(
              constraints: const BoxConstraints(maxHeight: 12),
            ),
          ),
          if (!isReduced) wrappedBtns,
        ],
      ),
    );
  }

  @override
  bool shouldRebuild(_) => true;
}

像这样在你的条子列表中使用它:

SliverPersistentHeader(
  pinned: true,
  delegate: CustomPageHeader(
    collapsedHeight: kToolbarHeight + 8,
    expandedHeight: 190,
  ),
),

Try the full test code on DartPad

代码是可以完善的,但应该对你有足够的帮助,这样你就可以改进它并自己继续。