我想点击时,我的列表项会执行this animation(mp4)。我尝试使用AnimatedCrossFade
,但它要求两个孩子处于同一级别,例如详细视图与ListView交叉淡入而不是轻敲项目。实际上,似乎Hero
动画是唯一可以在小部件上制作动画的动画。
我在使用英雄时遇到了麻烦。它应该包装列表项吗?如果Widget子树在Hero源/目标中有显着差异,这是否重要?此外,Hero动画可以与LocalHistoryRoute
s或staggered animations使用吗?
修改
现在看起来我需要做的就是使用Overlay
,我需要将所选项目添加到屏幕上相同位置的叠加层。点击,然后动画部分将很容易。这里可能使用的是目标/跟随者模式,例如CompositedTransformTarget
答案 0 :(得分:19)
您可以使用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);
}
}
答案 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,
),
),
),