300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Flutter(十六)——Hero动画

Flutter(十六)——Hero动画

时间:2020-03-03 00:28:42

相关推荐

Flutter(十六)——Hero动画

本文目录

前言基本用法实现原理

前言

在前面实践组件的开发中,我们做了一个登录的界面,里面有一个组件Hero,不知道大家是否记得?当时没有展开来说,是因为它属于动画的内容,本文就要重点讲解Hero动画。

做过Java开发Android的程序员应该都清楚,Shared Element Transition可以让Activity或Fragment做出流畅的动画,同样,在Flutter开发中,Hero动画也能实现类似的效果。简单来说,Hero的作用就是在路由之间做出流畅的转场动画。

基本用法

Hero组件的用法是需要同时定义源组件和目标组件,其中源组件和目标组件被Hero包裹在需要动画控制的组件外面,如果有一方不指定,在有些情况下,界面就会卡死,我们先来看看它的基本用法,首先是main.dart代码:

class _MyHomePageState extends State<MyHomePage> {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("我是第一个界面"),),body: Center(child: GestureDetector(child: Hero(tag: "tag1",child: FlutterLogo(size: 200,),),onTap: () {Navigator.push(context, MaterialPageRoute(builder: (BuildContext context)=>CustomFlutterLogoPage()));},),),);}}

代码很简单,就是监听点击事件ontap,hero包裹FlutterLogo组件,然后点击跳转到第二个界面。接着我们再来看看第二个页面CustomFlutterLogo.dart的代码:

import 'package:flutter/material.dart';class CustomFlutterLogoPage extends StatefulWidget {CustomFlutterLogoPage({Key key, this.title}) : super(key: key);final String title;@override_CustomFlutterLogoState createState() => _CustomFlutterLogoState();}class _CustomFlutterLogoState extends State<CustomFlutterLogoPage> {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("我是第二个页面"),),body: Center(child: Hero(tag: "tag2",child: CustomFlutterLogo(size: 400,name: "我是第二个页面",),),),);}}class CustomFlutterLogo extends StatelessWidget{final double size;final String name;CustomFlutterLogo({this.size=200.0,this.name});@overrideWidget build(BuildContext context) {return Container(child: Center(child: FlutterLogo(size: this.size,),),);}}

这段代码也很简单,就是常用的组件,只是在外层套了一层Hero动画组件,不过这里有一点我们需要注意,hero里面有一个tag属性,必须写上,不然会报错,不信的读者,可以删除后运行试试。

实现原理

我们基本已经掌握了Hero路由跳转动画的用法,但我们不能只看表面,不明其原理,因为后面讲解的动画也会涉及到这些知识,所以我们必须掌握。

Hero动画,它的整个运动过程分为3个步骤,即动画开始(t=0.0),动画进行中,动画结束(t=1.0),下面是Hero动画运动示意图:

如上图所示,两个路由之间还有一个Overlay层。在动画开始时,Flutter会计算出Hero的位置并复制一份,然后绘制到Overlay层上。复制的Hero和源Hero的大小是一致的,并且该Hero是在所有路由之上。在动画实现的过程中,Flutter会逐渐把源Hero移除屏幕。在动画进行中Flutter是依靠Tween来实现,通过createRectTween属性把Tween传给Hero。Hero内部默认使用MeterialRectArcTween的曲线路径进行移动动画的操作。在动画结束时,Flutter将Overlay中的Hero移除,且完成了Hero在目标路由上的显示,这时Overlay是空白的。

Hero中所有变换都是通过HeroController来实现的,HeroController是在MeterialApp中通过initState和didUpdateWidget方法来完成初始化的,源码如下所示:

class _MaterialAppState extends State<MaterialApp>{HeroController heroController;@overridevoid initState(){super.initState();_heroController=HeroController(createRectTween:_createRectTween);_updateNavigator();}@overridevoid didUpdateWidget(MaterialApp oldWidget){super.didUpdateWidget(oldWidget);if(widget.navigatorKey!=oldWidget.navigatorKey){_heroController=HeroController(createRectTween:_createRectTween);}_updateNavigator();}RectTween _createRectTween(Rect begin,Rect end){return MaterialRectArcTween(begin:begin,end:end);}}

在初始化HeroController时,Flutter携带了一个参数,就是_createRectTween,该参数返回的默认项就是MaterialRectArcTween。Flutter源码里还为我们实现了第二种RectTween返回值,即MaterialRectCenterArcTween。由此可见,可以对createRectTween进行自定义。我们再看看HeroController的具体内容,代码如下:

@overridevoid didPush(Route<dynamic> route,Route<dynamic> previousRoute){assert(navigator!=null);assert(route!=null);_maybeStartHeroTransition(previousRoute,route,HeroFlightDirection.push,false);}@overridevoid didPop(Route<dynamic> route,Route<dynamic> previousRoute){assert(navigator!=null);assert(route!=null);_maybeStartHeroTransition(route,previousRoute,HeroFlightDirection.pop,false);}

HeroController其实继承的是NavigatorObserver。在路由操作的didPush和didPop回调方法里,可以调用_maybeStartHeroTransition,并通过WidgetsBinding把源路由,目标路由,HeroController关联起来。在使用didPush和didPop回调时,通过调用_startHeroTransition方法让Hero动起来,只不过前者是正向的,后者是逆向的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。