【问题标题】:Animate ListView item to full screen in Flutter在 Flutter 中将 ListView 项目动画化为全屏
【发布时间】:2018-10-31 14:18:26
【问题描述】:

我想让我的列表项在点击时执行this animation (mp4)。我尝试使用AnimatedCrossFade,但它要求它的两个孩子处于同一水平,例如详细视图与 ListView 交叉淡入淡出,而不是被点击的项目。事实上,Hero 动画似乎是唯一可以跨小部件制作动画的动画。

我在使用英雄时遇到问题。它应该包装列表项吗? Widget 子树在 Hero 源/目标中是否显着不同是否重要?另外,英雄动画可以与LocalHistoryRoutes 或staggered animations一起使用吗?

编辑

现在看起来我需要做的是使用Overlay,困难的部分是我需要将所选项目添加到屏幕上被点击的相同位置的叠加层,然后是动画部分会很容易。可能在这里使用的是目标/追随者模式,例如CompositedTransformTarget

【问题讨论】:

  • 动画结束后你想做什么?保持它作为一个打开的屏幕,将有未来的过渡或关闭它作为唯一的选择?
  • 在您的情况下,将全屏内容显示到新路线中是否有效?
  • @RémiRousselet 是的

标签: animation flutter


【解决方案1】:

您可以只使用Hero 小部件来制作这种动画。这是我的例子:

及源代码:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new FirstPage(title: 'Color Palette'),
    );
  }
}

class FirstPage extends StatefulWidget {
  FirstPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _FirstPageState createState() => new _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  final palette = [
    {'#E53935': 0xFFE53935},
    {'#D81B60': 0xFFD81B60},
    {'#8E24AA': 0xFF8E24AA},
    {'#5E35B1': 0xFF5E35B1},
    {'#3949AB': 0xFF3949AB},
    {'#1E88E5': 0xFF1E88E5},
    {'#039BE5': 0xFF039BE5},
    {'#00ACC1': 0xFF00ACC1},
    {'#00897B': 0xFF00897B},
    {'#43A047': 0xFF43A047},
    {'#7CB342': 0xFF7CB342},
    {'#C0CA33': 0xFFC0CA33},
  ];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Container(
        child: new ListView.builder(
            itemCount: palette.length,
            itemBuilder: (context, index) => new Hero(
                  tag: palette[index].keys.first,
                  child: new GestureDetector(
                    onTap: () {
                      Navigator
                          .of(context)
                          .push(new ColorPageRoute(palette[index]));
                    },
                    child: new Container(
                      height: 64.0,
                      width: double.infinity,
                      color: new Color(palette[index].values.first),
                      child: new Center(
                        child: new Hero(
                          tag: 'text-${palette[index].keys.first}',
                          child: new Text(
                            palette[index].keys.first,
                            style: Theme.of(context).textTheme.title.copyWith(
                                  color: Colors.white,
                                ),
                          ),
                        ),
                      ),
                    ),
                  ),
                )),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  final Map<String, int> color;

  SecondPage({this.color});

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Color'),
      ),
      body: new Hero(
        tag: color.keys.first,
        child: new Container(
          color: new Color(color.values.first),
          child: new Center(
            child: new Hero(
              tag: 'text-${color.keys.first}',
              child: new Text(
                color.keys.first,
                style:
                    Theme.of(context).textTheme.title.copyWith(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class ColorPageRoute extends MaterialPageRoute {
  ColorPageRoute(Map<String, int> color)
      : super(
            builder: (context) => new SecondPage(
                  color: color,
                ));

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return FadeTransition(opacity: animation, child: child);
  }
}

【讨论】:

  • 我认为它也需要覆盖应用栏。因此覆盖
  • 如果不是我想也没关系。我确实想要高程部分,但它可以在导航和触发英雄动画之前运行。我需要一两天的时间来试试这个。
  • 你能告诉我那次投诉的细节吗?
  • Hero 是最简单的解决方案。但它有一些问题。例如仅在 Navigator 中可用。这在很多情况下都很痛苦。
【解决方案2】:

有人为此目的编写了一个惊人的 dart 包:https://pub.dev/packages/morpheus#-readme-tab-

然后,您需要做的就是使用 MorpheusPageRoute,其余的由包处理。

...
Navigator.push(
  context,
  MorpheusPageRoute(
    builder: (context) => MyWidget(title: title),
  ),
);
...

【讨论】:

    【解决方案3】:

    我只是作弊并将整个东西包装在一个堆栈中 - 底层将是一个带有 AppBar 的页面,而顶层在绘制之前是透明的。

    onTap,将 ListTile 复制到顶部表面,然后英雄动画将填满整个屏幕。它不是很优雅,但是该框架(还)没有提供轻松覆盖 AppBar 的功能,因此准备好为其他棘手的动画绘制的画布可能是足智多谋的。

    【讨论】:

    • 我还没有找到将小部件复制到叠加层上的方法。如果有办法在屏幕上获取小部件的位置,我可以做到。我敢打赌有,但我还没有找到。我应该看看英雄动画源代码。
    【解决方案4】:

    我无法评论或编辑 Lucas 的帖子(新帐户),但您还需要提供动画开始的小部件的 parentKey:

    final widgetKey = GlobalKey();
    ...
    ListTile(
      key: widgetKey,
      title: Text('My ListItem'),
      onTap: () => Navigator.push(
        context,
        MorpheusPageRoute(
          builder: (context) => MyNewPage(),
          parentKey: widgetKey,
        ),
      ),
    ),
    

    https://pub.dev/packages/morpheus

    【讨论】:

      猜你喜欢
      • 2019-01-25
      • 2016-04-09
      • 1970-01-01
      • 2022-12-31
      • 2022-06-11
      • 2019-10-20
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多