flutter 动画json_Flutter-动画-实践篇

一、了解AnimatedWidget

通常我们给一个Widget添加动画的时候都需要监听Animation的addListener()方法,并在这个方法里面不停的调用setState()方法通知Weight进行重绘。

AnimatedWidget是Flutter封装的用于执行动画的助手类。使用它可以使我们创建一个可重用动画的Widget。而且我们也不必关心Weight在什么时候需要重绘,因为AnimatedWidget中会自动调用addListener()和setState()。

AnimatedWidget实际上是一个StatefulWidget,它里面是定义了一套Widget并由外部将执行的动画传进来,然后根据Animation的value使各个Widget做出相应的改变。

Flutter封装了很多AnimatedWidget的助手类。SlideTransition、AlignTransition、PositionedTransition、RelativePositionedTransition等等。

//自定义AnimatedWidget

class CustomAnimatedWidget extendsAnimatedWidget {

CustomAnimatedWidget({Key key, Animationanimation})

:super(key: key, listenable: animation);

Widget build(BuildContext context) {final Animation custom =animation;return newCenter(

child:newContainer(

height: animation.value,

width: animation.value,

child:newContainer(),

),

);

}

}//使用CustomAnimatedWidget

class CustomApp extendsStatefulWidget {

_CustomAppState createState()=> new_CustomAppState();

}class _CustomAppState extends Statewith SingleTickerProviderStateMixin {

AnimationController controller;

Animationanimation;

initState() {super.initState();

controller= newAnimationController(

duration:const Duration(milliseconds: 5000), vsync: this);

animation= new Tween(begin: 0.0, end: 100.0).animate(controller);

controller.forward();

}

Widget build(BuildContext context) {return newCustomAnimatedWidget(animation: animation);

}

dispose() {

controller.dispose();super.dispose();

}

}

View Code

二、了解AnimatedBuilder

AnimatedBuilder相比较AnimatedWidget来说更加纯粹。它不知道如何渲染Widget,也不会管理Animatiion。个人感觉AnimatedWidget是一个执行具体动画的控件,而AnimatedBuilder则是一个执行特定动画的容器。

Flutter中也创建了很多基于AnimatedBuilder的控件。例如:BottomSheet、 PopupMenu、ProgressIndicator、RefreshIndicator、Scaffold、SnackBar、TabBar、TextField。

//自定义CustomAnimatedBuilder

class CustomAnimatedBuilder extendsStatelessWidget {

GrowTransition({this.child, this.animation});finalWidget child;final Animationanimation;

Widget build(BuildContext context) {return newCenter(

child:newAnimatedBuilder(

animation: animation,

builder: (BuildContext context, Widget child) {return newContainer(

height: animation.value, width: animation.value, child: child);

},

child: child),

);

}

}//使用CustomAnimatedBuilder

class CustomApp extendsStatefulWidget {

_CustomAppState createState()=> new_CustomAppState();

}class _CustomAppState extends Statewith TickerProviderStateMixin {

Animation animation;

AnimationController controller;

initState() {super.initState();

controller= newAnimationController(

duration:const Duration(milliseconds: 1000), vsync: this);final CurvedAnimation curve =

newCurvedAnimation(parent: controller, curve: Curves.easeIn);

animation= new Tween(begin: 0.0, end: 100.0).animate(curve);

controller.forward();

}

Widget build(BuildContext context) {return newCustomAnimatedBuilder(child: ‘自己的view’, animation: animation);

}

dispose() {

controller.dispose();super.dispose();

}

}

View Code

三、动画示例

1、位移动画

只要Widget能在一定的时间内按照一定的规则位移一定的距离,那边是产生了位移动画。可以通过改变Widget本身的margin,也可以通过改变父容器的padding,也可以通过SlideTransition的Offset产生位移,也可使用Matrix4的transform产生移动(Matrix4解释和使用)。下面看示例:

//位移动画 copy 代码可以直接使用

import 'package:flutter/material.dart';class TransferAnim extendsStatefulWidget {

@override

_TransferAnimState createState()=>_TransferAnimState();

}//ignore: slash_for_doc_comments/*** 这个实现 实际上是改变 父容器的padding/margin完成的*/

class _TransferAnimState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

Animationanim;

AnimationslideTransition;

@overridevoidinitState() {super.initState();

_initAnim();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("位移动画"),

),

body: Column(

children:[

Expanded(

child: Container(

padding: anim.value,

child: Center(

child: Container(

width:100,

height:50,

margin: ,

color: Colors.amber,

child: Center(

child: Text("位移动画"),

),

),

),

),

),

Expanded(

child: Container(

child: Center(

child: SlideTransition(

position: slideTransition,

child: Container(

width:100,

height:50,

color: Colors.amber,

child: Center(

child: Text("位移动画"),

),

),

),

),

),

),

],

),

);

}void_initAnim() {

_controller=AnimationController(

vsync:this,

duration: Duration(

seconds:3,

),

);

anim= newEdgeInsetsTween(

begin: EdgeInsets.only(left:0, top: 0),

end: EdgeInsets.only(left:100, top: 150),

).animate(_controller);//Offset 这里解释一下,是相对于自己移动的比例倍数

slideTransition = Tween(

begin: Offset(0, 0),

end: Offset(0, 2),

).animate(_controller);

anim.addListener(() {

setState(() {});

});

anim.addStatusListener((status) {

debugPrint('fanlei => $status');switch(status) {caseAnimationStatus.dismissed:

_controller?.forward();break;caseAnimationStatus.forward:break;caseAnimationStatus.reverse:break;caseAnimationStatus.completed:

_controller?.reverse();break;

}

});

_controller?.forward();

}

@overridevoiddispose() {

_controller?.dispose();super.dispose();

}

}

View Code

2、旋转动画

旋转动画就是一个Weight以某个点或者某个坐标轴旋转。可以使用Container()的transform(接受Matrix4)属性,也可以使用RotationTransition()执行旋转动画。下面看示例:

import 'package:flutter/material.dart';class RotateAnim extendsStatefulWidget {

@override

_RotateAnimState createState()=>_RotateAnimState();

}//ignore: slash_for_doc_comments

class _RotateAnimState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

Animationanim;

AnimationdoubleAnim;

@overridevoidinitState() {super.initState();

_initAnim();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("旋转动画"),

),

body: Container(

child: Column(

children:[

Expanded(

child: Center(

child: Container(

transform: anim.value,

width:200,

height:50,

color: Colors.amber,

child: Center(

child: Text("旋转动画矩阵变换"),

),

),

),

),

Expanded(

child: Center(

child: RotationTransition(

turns: doubleAnim,

child: Container(

width:200,

height:50,

color: Colors.greenAccent,

child: Center(

child: Text("旋转动画Rotation"),

),

),

),

),

),

],

),

),

);

}void_initAnim() {

_controller=AnimationController(

vsync:this,

duration: Duration(

seconds:3,

),

);

anim= newMatrix4Tween(

begin: Matrix4.rotationZ(0),

end: Matrix4.rotationZ(2.0),

).animate(_controller);

anim.addListener(() {

setState(() {});

});

anim.addStatusListener((status) {

debugPrint('fanlei => $status');switch(status) {caseAnimationStatus.dismissed:

_controller?.forward();break;caseAnimationStatus.forward:break;caseAnimationStatus.reverse:break;caseAnimationStatus.completed:

_controller?.reverse();break;

}

});

doubleAnim= Tween(

begin:0,

end:1.0,

).animate(_controller);

_controller?.forward();

}

@overridevoiddispose() {

_controller?.dispose();super.dispose();

}

}

View Code

3、缩放动画

本文档使用了两种方式来达到缩放的效果。一种是直接使用Animation改变Container()的width和height的值。另一种则是使用ScaleTransition(),其原理也是使用了Matrix4。下面看代码示例:

import 'package:flutter/material.dart';class ScaleAnim extendsStatefulWidget {

@override

_ScaleAnimState createState()=>_ScaleAnimState();

}//ignore: slash_for_doc_comments

class _ScaleAnimState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

AnimationanimWidth;

AnimationanimHeight;

AnimationanimScale;

@overridevoidinitState() {super.initState();

_initAnim();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("缩放动画"),

),

body: Container(

child: Column(

children:[

Expanded(

child: Center(

child: Container(

width: animWidth.value,

height: animHeight.value,

color: Colors.amber,

child: Center(

child: Text("缩放动画"),

),

),

),

),

Expanded(

child: ScaleTransition(

scale: animScale,

child: Center(

child: Container(

width:200,

height:50,

color: Colors.greenAccent,

child: Center(

child: Text("缩放动画"),

),

),

),

),

),

],

),

),

);

}void_initAnim() {

_controller=AnimationController(

vsync:this,

duration: Duration(

seconds:3,

),

);

animWidth= new Tween(

begin:100,

end:300,

).animate(_controller);

animWidth.addListener(() {

setState(() {});

});

animHeight= new Tween(

begin:50,

end:150,

).animate(_controller);

animScale= new Tween(

begin:1,

end:1.5,

).animate(_controller);

animWidth.addStatusListener((status) {

debugPrint('fanlei => $status');switch(status) {caseAnimationStatus.dismissed:

_controller?.forward();break;caseAnimationStatus.forward:break;caseAnimationStatus.reverse:break;caseAnimationStatus.completed:

_controller?.reverse();break;

}

});

_controller?.forward();

}

@overridevoiddispose() {

_controller?.dispose();super.dispose();

}

}

View Code

4、透明度动画(渐隐渐现)

本文档使用了两种方式来达到渐隐渐现的效果。第一种使用了Opacity(),通过Animation改变其opacity属性。第二种则是使用FadeTransition()。下面看代码示例:

import 'package:flutter/material.dart';class FadeAnim extendsStatefulWidget {

@override

_FadeAnimState createState()=>_FadeAnimState();

}//ignore: slash_for_doc_comments

class _FadeAnimState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

Animationanim;

AnimationfadeTransition;

@overridevoidinitState() {super.initState();

_initAnim();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("透明度动画"),

),

body: Container(

child: Column(

children:[

Expanded(

child: Center(

child: Opacity(

opacity: anim.value,

child: Container(

width:200,

height:50,

color: Colors.amber,

child: Center(

child: Text("透明度动画"),

),

),

),

),

),

Expanded(

child: Center(

child: FadeTransition(

opacity: fadeTransition,

child: Container(

width:200,

height:50,

color: Colors.greenAccent,

child: Center(

child: Text("透明度动画"),

),

),

),

),

),

],

),

),

);

}void_initAnim() {

_controller=AnimationController(

vsync:this,

duration: Duration(

seconds:3,

),

);

anim= new Tween(

begin:1,

end:0.2,

).animate(_controller);

anim.addListener(() {

setState(() {});

});

anim.addStatusListener((status) {

debugPrint('fanlei => $status');switch(status) {caseAnimationStatus.dismissed:

_controller?.forward();break;caseAnimationStatus.forward:break;caseAnimationStatus.reverse:break;caseAnimationStatus.completed:

_controller?.reverse();break;

}

});

fadeTransition= Tween(

begin:0,

end:1,

).animate(_controller);

_controller?.forward();

}

@overridevoiddispose() {

_controller?.dispose();super.dispose();

}

}

View Code

5、非线性曲线动画

非线性曲线动画其主要类是使用CurvedAnimation创建一个非线性的Animation。CurvedAnimation的curve属性接受一个Curve类。Flutter SDK API中的Curves类是Flutter已经为我们写好了的各种非线性曲线。Curves非线性gif示例。下面看代码示例:

import 'package:flutter/material.dart';//本示例融合前面四种动画

class NonlinearAnim extendsStatefulWidget {

@override

_NonlinearAnimState createState()=>_NonlinearAnimState();

}class _NonlinearAnimState extends Statewith SingleTickerProviderStateMixin {//位移

AnimationController _animController;

CurvedAnimation _translateCurved;

Animation_translateAnim;

CurvedAnimation _scaleCurved;

Animation_scaleAnim;//旋转

CurvedAnimation _rotationCurved;

Animation_rotationAnim;//透明度

CurvedAnimation _opacityCurved;

Animation_opacityAnim;

@overridevoidinitState() {

_initTransfer();

_initScale();

_initRotation();

_initOpacity();

_initListener();super.initState();

}

@overridevoiddispose() {

_animController?.dispose();super.dispose();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("非线性曲线动画"),

),

body: Container(

child: GridView.count(

crossAxisCount:2,

padding: EdgeInsets.only(left:10, right: 10, top: 10),

mainAxisSpacing:10,

crossAxisSpacing:10,

children:[

GestureDetector(

onTap: () {if (!_animController.isAnimating) {

_animController.forward();

}

},

child: Container(

color: Colors.amber,

child: Stack(

children:[

Align(

alignment: Alignment.topCenter,

child: Text("bounceOut"),

),

Align(

alignment: Alignment.bottomCenter,

child: Transform.translate(

offset: Offset(0, _translateAnim.value),

child: Container(

height:40,

width:40,

color: Colors.white,

),

),

)

],

),

),

),//GestureDetector(//onTap: (){//_animController.forward();//},//child: ,//),

GestureDetector(

onTap: () {

_animController.forward();

},

child: Container(

color: Colors.cyan,

child: Center(

child: Container(

width: _scaleAnim.value,

height: _scaleAnim.value,

color: Colors.white,

),

),

),

),

GestureDetector(

onTap: () {

_animController.forward();

},

child: Container(

color: Colors.green,

child: Center(

child: RotationTransition(

turns: _rotationAnim,

child: Container(

height:40,

width:40,

color: Colors.white,

),

),

),

),

),//AnimatedOpacity(opacity: null, duration: null)

Container(

color: Colors.indigoAccent,

child: Center(

child: Opacity(

opacity:_getOpacityValue(_opacityAnim.value),

child: Container(

height:40,

width:40,

color: Colors.white,

),

),

),

),

],

),

),

);

}void_initTransfer() {

_animController=AnimationController(vsync:this, duration: Duration(seconds: 2));

_translateCurved=CurvedAnimation(parent: _animController, curve: Curves.bounceOut);

_translateAnim= Tween(

begin:0,

end:-100,

).animate(_translateCurved);

_translateAnim.addListener(() {

setState(() {});

});

}void_initScale() {

_scaleCurved=CurvedAnimation(parent: _animController, curve: Curves.easeInOutBack);

_scaleAnim= Tween(

begin:40,

end:140,

).animate(_scaleCurved);

}void_initRotation() {

_rotationCurved=CurvedAnimation(parent: _animController, curve: Curves.easeInOutBack);

_rotationAnim= Tween(

begin:0,

end:1,

).animate(_rotationCurved);

}void_initOpacity() {

_opacityCurved=CurvedAnimation(parent: _animController, curve: Curves.elasticInOut);

_opacityAnim= Tween(

begin:0,

end:1,

).animate(_opacityCurved);

}double _getOpacityValue(doubleopacity) {double temp = 0;if (opacity < 0) {

temp= 0;returntemp;

}if (opacity > 1) {

temp= 1;returntemp;

}

temp=opacity;returntemp;

}void_initListener() {

_animController.addStatusListener((status) {switch(status) {caseAnimationStatus.dismissed:

_animController.forward();break;caseAnimationStatus.forward://TODO: Handle this case.

break;caseAnimationStatus.reverse://TODO: Handle this case.

break;caseAnimationStatus.completed:

_animController.reverse();break;

}

});

_animController.forward();

}

}

View Code

6、使用AnimatedWidget构建可重用动画

编写一个继承AnimatedWidget的Widget,在此Widget里我们可以编写自己想要的样式并执行相应的动画。下面是代码示例:

import 'package:flutter/material.dart';class AnimWidgetPage extendsStatefulWidget {

@override

_AnimWidgetPageState createState()=>_AnimWidgetPageState();

}class _AnimWidgetPageState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

CurvedAnimation _curved;

Animation_anim;

@overridevoidinitState() {

_controller= AnimationController(vsync: this,duration: Duration(milliseconds: 1500));

_curved=CurvedAnimation(parent: _controller, curve: Curves.bounceInOut);

_anim= Tween(begin: 1, end: 5).animate(_curved);

_controller.forward();super.initState();

}

@overridevoiddispose() {

_controller.dispose();super.dispose();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("AnimatedWidget"),

),

body: Container(

child: Center(

child: CustomAnimWidget(

animation: _anim,

),

),

),

);

}

}class CustomAnimWidget extendsAnimatedWidget {

CustomAnimWidget({Key key, Animationanimation})

:super(key: key, listenable: animation);

Widget build(BuildContext context) {final Animation animation =listenable;returnCenter(

child: Container(

height:200,

width:200,

child: ScaleTransition(

scale: animation,

child: Icon(

Icons.android,

color: Colors.green,

),

),

),

);

}

}

View Code

7、组合动画

组合动画顾名思义就是一个或多个Widget被几个动画同时作用。

import 'package:flutter/material.dart';class CombinationAnimPage extendsStatefulWidget {

@override

_CombinationAnimPageState createState()=>_CombinationAnimPageState();

}class _CombinationAnimPageState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;//曲线

CurvedAnimation _curvedAnimation;//缩放

Animation_scaleAnim;//旋转

Animation_rotationAnim;

@overridevoidinitState() {

_controller= AnimationController(vsync: this,duration: Duration(milliseconds: 2000));

_curvedAnimation=CurvedAnimation(parent: _controller, curve: Curves.bounceInOut);

_scaleAnim= Tween(

begin:1,

end:5,

).animate(_curvedAnimation);

_rotationAnim= Tween(

begin:0,

end:1,

).animate(_curvedAnimation);

_controller.addStatusListener((status) {switch(status) {caseAnimationStatus.dismissed:

_controller.forward();break;caseAnimationStatus.forward://TODO: Handle this case.

break;caseAnimationStatus.reverse://TODO: Handle this case.

break;caseAnimationStatus.completed:

_controller.reverse();break;

}

});

_controller.forward();super.initState();

}

@overridevoiddispose() {

_controller.dispose();super.dispose();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

title: Text("组合动画"),

centerTitle:true,

),

body: Container(

child: Center(

child: ScaleTransition(

scale: _scaleAnim,

child: RotationTransition(

turns: _rotationAnim,

child: Icon(

Icons.android,

color: Colors.green,

),

),

),

),

),

);

}

}

View Code

8、使用AnimatedBuilder构建动画

AnimatedBuilder将动画和视图分离。它接受一个Animation和一个Widget。下面是代码示例:

import 'package:flutter/material.dart';class CustomAnimBuildPage extendsStatefulWidget {

@override

_CustomAnimBuildPageState createState()=>_CustomAnimBuildPageState();

}class _CustomAnimBuildPageState extends Statewith SingleTickerProviderStateMixin {

AnimationController _controller;

Animationanimation;

AnimationcolorAnim;

@overridevoidinitState() {

_controller=AnimationController(vsync:this, duration: Duration(seconds: 1));

animation= Tween(

begin:50,

end:200,

).animate(_controller);

colorAnim=ColorTween(begin: Colors.amber, end: Colors.deepPurple)

.animate(_controller);

_controller.addStatusListener((status) {switch(status) {caseAnimationStatus.dismissed:

_controller.forward();break;caseAnimationStatus.forward://TODO: Handle this case.

break;caseAnimationStatus.reverse://TODO: Handle this case.

break;caseAnimationStatus.completed:

_controller.reverse();break;

}

});

_controller.forward();super.initState();

}

@overridevoiddispose() {

_controller.dispose();super.dispose();

}

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

title: Text("AnimatedBuilder"),

centerTitle:true,

),

body: Container(

child: Center(

child: _CustomTransition(

child: Icon(

Icons.android,

size:50,

color: Colors.green,

),

animation: animation,

colorAnim: colorAnim,

),

),

),

);

}

}class _CustomTransition extendsStatelessWidget {

Widget child;

Animationanimation;

AnimationcolorAnim;

_CustomTransition({this.child, this.animation, this.colorAnim});

@override

Widget build(BuildContext context) {returnContainer(

child: AnimatedBuilder(

animation: animation,

builder: (BuildContext context, Widget widget) {returnContainer(

color: colorAnim.value,

width: animation.value,

height: animation.value,

child: Transform.translate(

offset: Offset(animation.value-50, 0),

child: child,

),

);

},

),

);

}

}

View Code

9、列表动画

列表动画本质上也是每个Item做相应的动画。下面是官方代码示例:

import 'package:flutter/material.dart';class AnimatedListSample extendsStatefulWidget {

@override

_AnimatedListSampleState createState()=> new_AnimatedListSampleState();

}class _AnimatedListSampleState extends State{final GlobalKey _listKey =

new GlobalKey();

ListModel_list;int_selectedItem;int _nextItem; //The next item inserted when the user presses the '+' button.

@overridevoidinitState() {super.initState();

_list= new ListModel(

listKey: _listKey,

initialItems:[0, 1, 2],

removedItemBuilder: _buildRemovedItem,

);

_nextItem= 3;

}//Used to build list items that haven't been removed.

Widget _buildItem(

BuildContext context,int index, Animationanimation) {return newCardItem(

animation: animation,

item: _list[index],

selected: _selectedItem==_list[index],

onTap: () {

setState(() {

_selectedItem= _selectedItem == _list[index] ? null: _list[index];

});

},

);

}//Used to build an item after it has been removed from the list. This method is//needed because a removed item remains visible until its animation has//completed (even though it's gone as far this ListModel is concerned).//The widget will be used by the [AnimatedListState.removeItem] method's//[AnimatedListRemovedItemBuilder] parameter.

Widget _buildRemovedItem(int item, BuildContext context, Animationanimation) {return newCardItem(

animation: animation,

item: item,

selected:false,//No gesture detector here: we don't want removed items to be interactive.

);

}//Insert the "next item" into the list model.

void_insert() {final int index =_selectedItem== null ?_list.length : _list.indexOf(_selectedItem);

_list.insert(index, _nextItem++);

}//Remove the selected item from the list model.

void_remove() {if (_selectedItem != null) {

_list.removeAt(_list.indexOf(_selectedItem));

setState(() {

_selectedItem= null;

});

}

}

@override

Widget build(BuildContext context) {return newMaterialApp(

home:newScaffold(

appBar:newAppBar(

title:const Text('AnimatedList'),

actions:[newIconButton(

icon:constIcon(Icons.add_circle),

onPressed: _insert,

tooltip:'insert a new item',

),newIconButton(

icon:constIcon(Icons.remove_circle),

onPressed: _remove,

tooltip:'remove the selected item',

),

],

),

body:newPadding(

padding:const EdgeInsets.all(16.0),

child:newAnimatedList(

key: _listKey,

initialItemCount: _list.length,

itemBuilder: _buildItem,

),

),

),

);

}

}/// Keeps a Dart List in sync with an AnimatedList.// The [insert] and [removeAt] methods apply to both the internal list and the/// animated list that belongs to [listKey].// This class only exposes as much of the Dart List API as is needed by the/// sample app. More list methods are easily added, however methods that mutate the/// list must make the same changes to the animated list in terms of/// [AnimatedListState.insertItem] and [AnimatedList.removeItem].

class ListModel{

ListModel({

@requiredthis.listKey,

@requiredthis.removedItemBuilder,

IterableinitialItems,

}) :assert(listKey != null),assert(removedItemBuilder != null),

_items= new List.from(initialItems ?? []);final GlobalKeylistKey;finaldynamic removedItemBuilder;final List_items;

AnimatedListState get _animatedList=>listKey.currentState;void insert(intindex, E item) {

_items.insert(index, item);

_animatedList.insertItem(index);

}

E removeAt(intindex) {final E removedItem =_items.removeAt(index);if (removedItem != null) {

_animatedList.removeItem(index,

(BuildContext context, Animationanimation) {returnremovedItemBuilder(removedItem, context, animation);

});

}returnremovedItem;

}int get length =>_items.length;

E operator [](int index) =>_items[index];int indexOf(E item) =>_items.indexOf(item);

}/// Displays its integer item as 'item N' on a Card whose color is based on/// the item's value. The text is displayed in bright green if selected is true./// This widget's height is based on the animation parameter, it varies/// from 0 to 128 as the animation varies from 0.0 to 1.0.

class CardItem extendsStatelessWidget {constCardItem(

{Key key,

@requiredthis.animation,this.onTap,

@requiredthis.item,this.selected: false})

:assert(animation != null),assert(item != null && item >= 0),assert(selected != null),super(key: key);final Animationanimation;finalVoidCallback onTap;final intitem;finalbool selected;

@override

Widget build(BuildContext context) {

TextStyle textStyle=Theme.of(context).textTheme.display1;if(selected)

textStyle= textStyle.copyWith(color: Colors.lightGreenAccent[400]);return newPadding(

padding:const EdgeInsets.all(2.0),

child:newSizeTransition(

axis: Axis.vertical,

sizeFactor: animation,

child:newGestureDetector(

behavior: HitTestBehavior.opaque,

onTap: onTap,

child:newSizedBox(

height:128.0,

child:newCard(

color: Colors.primaries[item%Colors.primaries.length],

child:newCenter(

child:new Text('Item $item', style: textStyle),

),

),

),

),

),

);

}

}

View Code

10、共享元素动画

所谓共享元素动画可以简单的理解为两个页面共用同一个元素。但是其实是两个页面的的两个元素被相同的Tag所标记,再进行页面跳转的时候被框架识别,从而执行相应的动画。Flutter中使用共享元素动画需要使用Hero这个StatefulWidget。Hero的tag属性标记两个元素。下面是代码示例:

import 'package:flutter/material.dart';import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';class HeroAnimation extendsStatelessWidget {

Widget build(BuildContext context) {//timeDilation = 5.0;//1.0 means normal animation speed.

returnScaffold(

appBar: AppBar(

title: Text('Basic Hero Animation'),

centerTitle:true,

),

body: GridView.count(

crossAxisCount:2,

children:[

ItemView(myData[0], 150),

ItemView(myData[1], 150),

ItemView(myData[2], 150),

ItemView(myData[3], 150),

ItemView(myData[4], 150),

ItemView(myData[5], 150),

ItemView(myData[6], 150),

ItemView(myData[7], 150),

ItemView(myData[8], 150),

ItemView(myData[9], 150),

],

),

);

}

}

Widget getHeroAnim2(ItemModel itemModel) {returnScaffold(

appBar: AppBar(

title: Text("共享元素"),

centerTitle:true,

),

body: Container(

alignment: Alignment.topLeft,

child: ItemView(itemModel,400),

),

);

}

List myData = [

ItemModel(

title:'啦啦啦1111',

imgUrl:'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2717595227,1512397782&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦2222',

imgUrl:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3454574876,1377139334&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦3333',

imgUrl:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1499844476,2082399552&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦4444',

imgUrl:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1938482571,2420691429&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦5555',

imgUrl:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3548575507,3156953806&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦6666',

imgUrl:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3484495061,2102329231&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦7777',

imgUrl:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3562330430,950864085&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦8888',

imgUrl:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2985783351,2052499916&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦9999',

imgUrl:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=311914474,2668302507&fm=26&gp=0.jpg'),

ItemModel(

title:'啦啦啦0000',

imgUrl:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2471845590,913308006&fm=26&gp=0.jpg'),

];//数据类型

classItemModel {

String title;

String imgUrl;

ItemModel({this.title, this.imgUrl});

}class ItemView extendsStatelessWidget {

ItemModel model;doubleheight;

ItemView(this.model, this.height);

@override

Widget build(BuildContext context) {returnGestureDetector(

onTap: () {

Navigator.of(context).push(new MaterialPageRoute(

builder: (BuildContext context) {returngetHeroAnim2(model);

},

),

);

},

child: Container(

alignment: Alignment.center,

child: SizedBox(

width: height,

height: height,

child: Container(

decoration: BoxDecoration(

borderRadius: BorderRadius.all(Radius.circular(6)),

color: Colors.white),

child: Hero(//一个viewTree下面不能有相同的

tag: model.imgUrl,

child: Material(

color: Colors.transparent,

child: Column(

children:[

Expanded(

child: Image.network(

model.imgUrl,

fit: BoxFit.cover,

)),

Text(model.title),

],

),

),

),

),

),

),

);

}

}

View Code

11、拖拽动画

拖拽需要使用GestureDetector()监听用户手势,其函数会返回一个DragUpdateDetails对象,这个对象可以获取当前手指位移的坐标,然后通过Offset()给Widget设置偏移量。下面是代码示例:

import 'package:flutter/material.dart';class DragAnimPage extendsStatefulWidget {

@override

_DragAnimPageState createState()=>_DragAnimPageState();

}class _DragAnimPageState extends State{double mDx = 0;double mDy = 0;

GlobalKey _globalKey= newGlobalKey();

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

title: Text("拖拽动画"),

centerTitle:true,

),

body: Container(

child: Transform.translate(

offset: Offset(mDx, mDy),

child: GestureDetector(

onPanUpdate: (dragUpdateDetails) {

mDx=dragUpdateDetails.globalPosition.dx;

mDy=dragUpdateDetails.globalPosition.dy;

setState(() {});

},

child: Container(

width:100,

height:50,

alignment: Alignment.center,

color: Colors.indigoAccent,

key: _globalKey,

child: Text("拖拽"),

),

),

),

),

);

}

}

View Code

12、第三方动画

①、Lottie

Lottie动画是Airbnb公司出的一款跨平台的动画框架(基础篇有介绍链接)。下面是代码示例:

import 'package:flutter/material.dart';import 'package:flutter_lottie/flutter_lottie.dart';class LottieAnimPage extendsStatefulWidget {

@override

_LottieAnimPageState createState()=>_LottieAnimPageState();

}class _LottieAnimPageState extends State{

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

title: Text("Lottie动画"),

centerTitle:true,

),

body: Container(

padding: EdgeInsets.all(20),

child: Center(

child: LottieView.fromFile(

filePath:"assets/anim/8075-the-frog-to-drive.json",

autoPlay:true,

loop:true,

reverse:true,

onViewCreated: (lottieController) {

},

),

),

),

);

}

}

View Code

②、Flare

Flare动画框架是Flutter官方推荐的一个动画框架(详细介绍请看基础篇)。下面是代码示例:

import 'package:flare_flutter/flare_actor.dart';import 'package:flutter/material.dart';class FlareAnimPage extendsStatefulWidget {

@override

_FlareAnimPageState createState()=>_FlareAnimPageState();

}class _FlareAnimPageState extends State{

@override

Widget build(BuildContext context) {returnScaffold(

appBar: AppBar(

centerTitle:true,

title: Text("Flare动画(官方推荐)"),

),

body: Container(

child: Column(

children:[

Expanded(

child: FlareActor("assets/anim/Filip.flr",

alignment: Alignment.center,

fit: BoxFit.contain,

animation:'idle',

),

),

],

),

),

);

}

}

View Code

四、Demo代码地址

参考文献:

你可能感兴趣的:(flutter,动画json)