flutter - 我怎样才能有一个像这样的动画SliverAppBar?

标签 flutter dart user-interface animation visual-studio-code

This is the SliverAppBar extended And this is the SliverAppBar collapsed

我希望这些动画之间能够平滑,我尝试使用 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 的建议,这是我对您的问题的看法我使用了 SliverPercientHeader 并尝试使其具有响应能力,因此当尺寸减小时,它会出现一些动画。

示例代码

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

该代码是完美的,但应该对您有足够的帮助,以便您可以改进它并自己继续。

关于flutter - 我怎样才能有一个像这样的动画SliverAppBar?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68903417/

相关文章:

c# - 当 OnClick 代码在后台执行时禁用 Winforms 按钮

flutter ;类 AMSupportURLConnectionDelegate 在两者中都实现了 ?? (0x1eb0e27a0) 和 ?? (0x1162f02b8)

flutter - 如何动态改变FloatingActionButton的颜色?

flutter - Flutter 异步调用的最佳实践?

flutter - 将类转换/转换为父类类型

android - 实现特殊 UI 元素列表的最佳方式?

java - 灵活的 Swing 输入对话框

当我 ScrollView 时,在屏幕顶部 flutter 修复标签栏

android - 如何在Flutter中设置Center()传单 map

dart - 如何在 Polymer.dart 中使用 Material 设计的内置 css 颜色类?