如果你是一名安卓开发者,应该很熟悉 共享元素变换(Shared Element Transition)这个概念,它可以通过几行代码,就在两个Activity或者Fragment之间做出流畅的转场动画。
Google把这个概念也带到了Flutter里面,这就是我们今天要讲的主角——Hero控件。通过Hero,我们可以在两个路由之间,做出流畅的转场动画。注意,是两个路由(Route),在Flutter里面,Dialog也是路由,因此完全可以使用在Dialog的切换上。
我们看下效果图:
shared_element.gif
Hero的使用
我们现在有两个元素:源控件和目标控件。要实现元素共享,首先,我们要将两个控件分别用Hero包裹,同时为它们设置相同的tag
源路由中的Hero:
Hero(
tag: 'hero',
child: Container(
color: Colors.lightGreen,
width: 50.0,
height: 50.0,
));
目标路由中的Hero:
Hero(
tag: 'hero',
child: Container(
color: Colors.orange,
width: 150.0,
height: 120.0,
));
接着,给源路由页面添加路由跳转逻辑:
GestureDetector(
child: Hero(
tag: 'hero',
child: Container(
color: Colors.orange,
width: 150.0,
height: 120.0,
)),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_) {
return ElementDetailPage();
}));
},
);
就是这么简单,只需两步,你就可以完成这个Hero过度动画了,是不是超级方便呢?
Hero变换时做了什么?
Hero就是一个动画,所以我们将其拆分成三部分来说:动画开始时、动画进行中和动画结束时。
动画开始时:t=0.0
在这个时间点,Flutter做了三件事:
- 计算目标Hero的位置,然后算出对应的Rect;
- 把源Hero复制一份,绘制到Overlay上(就是绘制一个与源Hero大小、位置完全相同的Hero,作为目标Hero),然后改变它的Z轴属性,让它能显示在所有路由之上;
- 把源Hero移出屏幕。
动画进行时
动画的进行是依靠了 Tween<Rect> 来实现的,这个东西在写动画时总是会用到,大家应该不陌生;通过Hero的createRectTween
属性,将这个变换Tween<Rect>传给H