老孟導讀:此篇文章是 Flutter 動畫系列文章第三篇,后續還有動畫序列、過度動畫、轉場動畫、自定義動畫等。
Flutter 系統提供了20多個動畫組件,只要你把前面【動畫核心】(文末有鏈接)的文章看明白了,這些組件對你來說是非常輕松的,這些組件大部分都是對常用操作的封裝。
顯示動畫組件
回顧上一篇【動畫核心】的文章中創建動畫三個必須的步驟:
- 創建 AnimationController。
- 監聽 AnimationController,調用
setState
刷新UI。 - 釋放 AnimationController。
看第二步,每個動畫都需要這個步驟,因此對其封裝,命名為 MyAnimatedWidget:
class MyAnimatedWidget extends StatefulWidget {
final AnimationController controller;
final Widget child;
const MyAnimatedWidget(
{Key key, @required this.controller, @required this.child})
: super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> {
@override
void initState() {
super.initState();
widget.controller.addListener(() {
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void dispose() {
super.dispose();
widget.controller.dispose();
}
}
自定義的動畫組件只有兩個功能:
- 監聽 AnimationController,調用
setState
。 - 釋放 AnimationController。
而 AnimationController 的創建需要開發者自行創建,為什么封裝在自定義組件內?這個后面會介紹。
其實這個組件不用我們自己封裝,因為系統已經封裝好了,在學習 Flutter 的過程中自定義組件是非常重要的,因此多封裝一些組件,即使是系統已經存在的,用自己和系統的進行對比,可以極大的提高我們自定義組件的能力。
系統封裝的類似上面的組件是 AnimatedWidget,此類是抽象類,源代碼:
區別:
-
我們使用 監聽 AnimationController,調用
setState
,而系統使用 Listenable,Listenable 是一個維護偵聽器列表的對象,用於通知客戶端該對象已被更新。Listenable 有兩個變體:
- ValueListenable :擴展[Listenable]接口的接口,具有當前值的概念。
- Animation:一個擴展[ValueListenable]接口的接口,添加方向(正向或反向)的概念。
AnimationController 的繼承結構:
AnimationController 也是繼承自 Listenable,因此使用 Listenable 適用的范圍更廣,不僅僅可以用於 Animation ,還可以用於 ChangeNotifier。
-
由於使用了 Listenable,因此監聽和釋放使用
listenable.addListener
和listenable.removeListener
。
AnimatedWidget 是一個抽象類,不能直接使用,其子類包括:
以 ScaleTransition 為例使用方式:
class AnimationDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AnimationDemo();
}
class _AnimationDemo extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
@override
void initState() {
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
_animation = Tween(begin: .5, end: .1).animate(_animationController);
//開始動畫
_animationController.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _animation,
child: Container(
height: 200,
width: 200,
color: Colors.red,
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
和【動畫核心】中寫法唯一的不同是不需要主動調用 setState
。
AnimatedWidget 其他子類的用法類似,不在一一介紹,其他組件的詳細用法可到 http://laomengit.com/flutter/widgets/widgets_structure.html 中查看。
隱式動畫組件
AnimatedWidget 只是封裝了 setState
,系統是否有封裝 AnimationController、Tween、Curve且自動管理AnimationController的組件呢?有的,此組件就是 ImplicitlyAnimatedWidget,ImplicitlyAnimatedWidget 也是一個抽象類,其子類包括:
以 AnimatedOpacity 為例使用方式:
class AnimatedWidgetDemo extends StatefulWidget {
@override
_AnimatedWidgetDemoState createState() => _AnimatedWidgetDemoState();
}
class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo> {
double _opacity = 1.0;
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedOpacity(
opacity: _opacity,
duration: Duration(seconds: 2),
child: GestureDetector(
onTap: () {
setState(() {
_opacity = 0;
});
},
child: Container(
height: 60,
width: 150,
color: Colors.blue,
),
),
),
);
}
}
使用 AnimatedOpacity 我們並沒有主動創建 AnimationController 和 Tween,是因為 AnimatedOpacity 內部已經創建了。
所以別看 Flutter 內置了20多種動畫組件,90% 都是對上面兩種方式的封裝,分別稱為隱式動畫組件 和 顯示動畫組件:
- 隱式動畫組件:只需提供給組件動畫開始、結束值,組件創建 AnimationController、Curve、Tween,執行動畫,釋放AnimationController,我們稱之為隱式動畫組件,隱式動畫組件有: AnimatedAlign、AnimatedContainer、AnimatedDefaultTextStyle、AnimatedOpacity、AnimatedPadding、AnimatedPhysicalModel、AnimatedPositioned、AnimatedPositionedDirectional、AnimatedTheme、SliverAnimatedOpacity、TweenAnimationBuilder、AnimatedContainer 等。
- 顯示動畫組件:需要設置 AnimationController,控制動畫的執行,使用顯式動畫可以完成任何隱式動畫的效果,甚至功能更豐富一些,不過你需要管理該動畫的 AnimationController 生命周期,AnimationController 並不是一個控件,所以需要將其放在 stateful 控件中。顯示動畫組件有:AlignTransition、AnimatedBuilder、AnimatedModalBarrier、DecoratedBoxTransition、DefaultTextStyleTransition、PositionedTransition、RelativePositionedTransition、RotationTransition、ScaleTransition、SizeTransition、SlideTransition 、FadeTransition 等。
不難看出,使用隱式動畫控件,代碼更簡單,而且無需管理 AnimationController 的生命周期,有人覺得隱式動畫組件多方便啊,為什么還要顯示動畫組件呢?因為:封裝的越復雜,使用越簡單,往往伴隨着功能越不豐富。比如想讓動畫一直重復執行,隱式動畫組件是無法實現的。
顯示動畫組件和隱式動畫組件中各有一個萬能的組件,它們是 AnimatedBuilder 和 TweenAnimationBuilder,當系統中不存在我們想要的動畫組件時,可以使用這兩個組件,以 AnimatedBuilder 為例,實現 Container 大小和顏色同時動畫,
class AnimatedBuilderDemo extends StatefulWidget {
@override
_AnimatedBuilderDemoState createState() => _AnimatedBuilderDemoState();
}
class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Color> _colorAnimation;
Animation<Size> _sizeAnimation;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 2));
_colorAnimation =
ColorTween(begin: Colors.blue, end: Colors.red).animate(_controller);
_sizeAnimation =
SizeTween(begin: Size(100.0, 50.0), end: Size(200.0, 100.0))
.animate(_controller);
_controller.forward();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, widget) {
return Container(
width: _sizeAnimation.value.width,
height: _sizeAnimation.value.height,
color: _colorAnimation.value,
);
},
),
);
}
}
AnimatedBuilder 和 TweenAnimationBuilder 本質上和其他動畫組件沒有區別,只是給了我們更高的靈活性。
如何選取
Flutter 內置的動畫組件分為兩種:隱式動畫組件 和 顯示動畫組件 ,顯示動畫組件只封裝了 setState
方法,需要開發者創建 AnimationController,並管理 AnimationController。隱式動畫組件封裝了 AnimationController、Curve、Tween,只需提供給組件動畫開始、結束值,其余由系統管理。
隱式動畫組件可以完成效果,顯示動畫組件都可以完成,那么什么時候使用隱式動畫組件?什么時候使用顯示動畫組件?
- 判斷你的動畫組件是否一直重復,比如一直轉圈的loading動畫,如果是選擇顯式動畫。
- 判斷你的動畫組件是否需要多個組件聯動,如果是選擇顯式動畫。
- 判斷你的動畫組件是否需要組合動畫,如果是選擇顯式動畫。
- 如果上面三個條件都是否,就選擇隱式動畫組件,判斷是否已經內置動畫組件,如果沒有,使用 TweenAnimationBuilder,有就直接使用內置動畫組件。
- 選擇顯式動畫組件,判斷是否已經內置動畫組件,如果沒有,使用 AnimatedBuilder,有就直接使用內置動畫組件。
邏輯圖如下:
還有一個簡單的區分辦法:如果你的動畫相對比較簡單,動畫從一種狀態過渡到另一種狀態,不需要單獨控制 AnimationController,這種情況下,隱式動畫組件一般可以就可以實現。
不過也沒有必要特別糾結使用隱式動畫組件還是顯示動畫組件,不管使用哪一種,實現效果即可。
交流
老孟Flutter博客地址(330個控件用法):http://laomengit.com
歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:
![]() |
![]() |