Flutter 動畫入門
原理類似於 Android
的屬性動畫,和 Widget
分離,在一定時間內生成一系列的值,值可以是 int
, double
, color
或者 string
等等,每隔N毫秒,或者N秒鐘獲取到最新的值去替換掉 Widget
上的值,同時重新整理佈局,如果重新整理間隔足夠小就能起到動畫的作用,例如構造一個 Widget
,他的 width
, height
由動畫來控制,一定時間動態更新值使 Widget
產生動效:
new Container( margin: new EdgeInsets.symmetric(vertical: 10.0), height: animation.value, width: animation.value, child: new FlutterLogo(), ), 複製程式碼
接下來看下如何使用 Animation
構建一個動態更新 Widget
的簡單場景
- 匯入
Animation
類import 'package:flutter/animation.dart';
- 建立
StatefulWidget
,建立Animation
的實現類AnimationController
,來構造一個最簡單的屬性動畫,並且應用到Widget
上。
class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin { AnimationController animationController; @override void initState() { // TODO: implement initState super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000)); animationController.addListener(() { setState(() {}); }); animationController.forward(); //啟動動畫 } @override Widget build(BuildContext context) { print('tag' + animationController.value.toString()); return Center( child: Container( width: animationController.value, height: animationController.value * 100, color: Colors.red, ), ); } } 複製程式碼
可以看到回撥列印的是從0-1的值,要返回其他型別的數值就需要用到 Tween
, Tween
有許多子類例如:

class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation animation; @override void initState() { // TODO: implement initState super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000)); animation = Tween(begin: 0.0,end: 100.0).animate(animationController); animationController.addListener(() { setState(() {}); }); animationController.forward(); //啟動動畫 } @override Widget build(BuildContext context) { print('tag' + animationController.value.toString()); return Center( child: Container( width: animation.value.toDouble(), height: animation.value.toDouble() , color: Colors.red, ), ); } } 複製程式碼
2.使用 AnimatedWidget
來簡化程式碼 AnimatedWidget
,省去了 addListener()
以及 setState()
交給 AnimatedWidget
處理,感覺也沒省掉很多。。
class TestContainer extends AnimatedWidget { TestContainer({Key key,Animation animation}) : super(key: key, listenable: animation); @override Widget build(BuildContext context) { // TODO: implement build Animation animation = listenable; returnCenter( child: Container( width: animation.value.toDouble(), height: animation.value.toDouble() , color: Colors.red, ), ); } } class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation animation; @override void initState() { // TODO: implement initState super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000)); animation = Tween(begin: 0.0,end: 100.0).animate(animationController); animationController.forward(); //啟動動畫 } @override Widget build(BuildContext context) { return TestContainer(animation: animation,); } } 複製程式碼
3.使用 AnimatedWidget
相關 Api
來再次簡化程式碼,例如使用 RotationTransition
將 Widget
在3秒鐘內旋轉360度
class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation animation; @override void initState() { // TODO: implement initState super.initState(); animationController = AnimationController( vsync: this, duration: Duration(seconds: 10)); animation = Tween(begin: 0.0, end: 1).animate(animationController); animationController.forward(); //啟動動畫 } @override Widget build(BuildContext context) { return RotationTransition(turns: animation, child: Center( child: Container(color: Colors.red, width: 100, height: 100,)),); } } 複製程式碼
4.對動畫過程進行監聽
animation.addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); } }); 複製程式碼
5.使用 AnimationBuilder
,將控制元件和動畫的控制過程進行封裝
class TestTransition extends StatelessWidget { TestTransition({this.child, this.animation}); final Widget child; final Animation<double> animation; Widget build(BuildContext context) { return new Center( child: new AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget child) { return new Container( height: animation.value, width: animation.value, child: child); }, child: child), ); } } class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation animation; @override void initState() { // TODO: implement initState super.initState(); animationController = AnimationController(vsync: this, duration: Duration(seconds: 10)); animation = Tween(begin: 0.0, end: 100.0).animate(animationController); animationController.forward(); //啟動動畫 } @override Widget build(BuildContext context) { return TestTransition( child: Center( child: Container( color: Colors.red, )), animation: animation, ); } } 複製程式碼