• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

user-interface - 当屏幕在 flutter 中滚动时动画小部件位置(包括 GIF)

[复制链接]
菜鸟教程小白 发表于 2022-9-1 00:11:14 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我正在尝试为两行小部件设置动画,以作为一个滚动折叠成 1 行这些小部件。我试图在 SliverAppBar 中实现这种行为。

为了澄清起见,我在此处包含了一个 GIF 以供引用。我希望您在应用栏中看到的行为,但不是 1 行到 2,我希望 2 行变为 1。

这是我迄今为止所拥有的快速片段。我将 2 个包含 3 个 Row 小部件的 shrinkableBox 小部件包装到一个 Wrap 小部件中。我通过连接到 _scrollController.offset 并进行一些计算来动态调整这些框的大小。这些行确实会动态移动,但它们不会动画化,而是突然移动。

  double kExpandedHeight = 300.0;

  Widget build(BuildContext context) {
    double size = !_scrollController.hasClients || _scrollController.offset == 0 ? 75.0 : 75 - math.min(45.0, (45 / kExpandedHeight * math.min(_scrollController.offset, kExpandedHeight) * 1.5));
    return Scaffold(
      body: CustomScrollView(
          controller: _scrollController,
          slivers: <Widget>[           
            SliverAppBar(
              pinned: true,
              expandedHeight: kExpandedHeight,

          title: new Text(
            "Title!",
          ),
          bottom: PreferredSize(child: Wrap(
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),                      
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),
                Row(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,

                children: <Widget>[
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),
                  ShrinkableBox(
                    onClick: () {
                      print("tapped");
                    },
                    size: size,
                  ),
                ],
              ),
            ],
          ), preferredSize: new Size.fromHeight(55),),
        )
   // ...
   // ...Other sliver list content here...
   // ...



Best Answer-推荐答案


您可以将Stack和Positioned窗口小部件一起使用,以根据需要放置ShrinkableBoxes。由于控制动画的是滚动偏移,因此您不需要使用动画小部件或动画 Controller 或类似的东西。这是一个工作示例,它通过线性插入框的初始和最终位置来计算位置(您可以通过将 Curves.linear 更改为其他曲线来获得不同的动画路径):

import 'dart:math' as math;
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: Home()));
}

class Home extends StatefulWidget {
  @override
  State createState() => HomeState();
}

class HomeState extends State<Home> {
  static const double kExpandedHeight = 300.0;

  static const double kInitialSize = 75.0;

  static const double kFinalSize = 30.0;

  static const List<Color> kBoxColors = [
    Colors.red,
    Colors.green,
    Colors.yellow,
    Colors.purple,
    Colors.orange,
    Colors.grey,
  ];

  ScrollController _scrollController = new ScrollController();

  @override
  void initState() {
    _scrollController.addListener(() {
      setState(() { /* State being set is the Scroll Controller's offset */ });
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
  }

  Widget build(BuildContext context) {
    double size = !_scrollController.hasClients || _scrollController.offset == 0
        ? 75.0
        : 75 -
            math.min(45.0,
                (45 / kExpandedHeight * math.min(_scrollController.offset, kExpandedHeight) * 1.5));

    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            expandedHeight: kExpandedHeight,
            title: Text("Title!"),
            bottom: PreferredSize(
              preferredSize: Size.fromHeight(55),
              child: buildAppBarBottom(size),
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return ListTile(title: Text('Item $index'));
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget buildAppBarBottom(double size) {
    double t = (size - kInitialSize) / (kFinalSize - kInitialSize);

    const double initialContainerHeight = 2 * kInitialSize;
    const double finalContainerHeight = kFinalSize;

    return Container(
      height: lerpDouble(initialContainerHeight, finalContainerHeight, t),
      child: LayoutBuilder(
        builder: (context, constraints) {
          List<Widget> stackChildren = [];
          for (int i = 0; i < 6; i++) {
            Offset offset = getInterpolatedOffset(i, constraints, t);
            stackChildren.add(Positioned(
              left: offset.dx,
              top: offset.dy,
              child: buildSizedBox(size, kBoxColors[i]),
            ));
          }

          return Stack(children: stackChildren);
        },
      ),
    );
  }

  Offset getInterpolatedOffset(int index, BoxConstraints constraints, double t) {
    Curve curve = Curves.linear;
    double curveT = curve.transform(t);

    Offset a = getOffset(index, constraints, kInitialSize, 3);
    Offset b = getOffset(index, constraints, kFinalSize, 6);

    return Offset(
      lerpDouble(a.dx, b.dx, curveT),
      lerpDouble(a.dy, b.dy, curveT),
    );
  }

  Offset getOffset(int index, BoxConstraints constraints, double size, int columns) {
    int x = index % columns;
    int y = index ~/ columns;
    double horizontalMargin = (constraints.maxWidth - size * columns) / 2;

    return Offset(horizontalMargin + x * size, y * size);
  }

  Widget buildSizedBox(double size, Color color) {
    return Container(
      height: size,
      width: size,
      color: color,
    );
  }
}

关于user-interface - 当屏幕在 flutter 中滚动时动画小部件位置(包括 GIF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55056721/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap