在Flutter中将ListView项目设置为全屏幕

时间:2018-05-21 21:54:26

标签: animation flutter

我想点击时,我的列表项会执行this animationmp4)。我尝试使用AnimatedCrossFade,但它要求两个孩子处于同一级别,例如详细视图与ListView交叉淡入而不是轻敲项目。实际上,似乎Hero动画是唯一可以在小部件上制作动画的动画。

我在使用英雄时遇到了麻烦。它应该包装列表项吗?如果Widget子树在Hero源/目标中有显着差异,这是否重要?此外,Hero动画可以与LocalHistoryRoute s或staggered animations使用吗?

修改

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

4 个答案:

答案 0 :(得分:19)

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

enter image description here

和源代码:

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);
  }
}

答案 1 :(得分:0)

我只是作弊并将整个东西包裹在一个堆栈中 - 底层将是一个带有AppBar的页面,顶层将是透明的,直到画上。

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

答案 2 :(得分:0)

为此,有人写了一个很棒的飞镖包装:https://pub.dev/packages/morpheus#-readme-tab-

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

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

答案 3 :(得分:0)

我无法评论或编辑 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