在為 Widget 添加動畫效果的過程中我們不難發(fā)現(xiàn),Animation 僅提供動畫的數(shù)據(jù)漓帅,因此我們還需要監(jiān)聽動畫執(zhí)行進度,并在回調(diào)中使用 setState 強制刷新界面才能看到動畫效果罢坝∠侍玻考慮到這些步驟都是固定的,F(xiàn)lutter 提供了兩個類來幫我們簡化這一步驟鸿市,即 AnimatedWidget 與 AnimatedBuilder锯梁。
AnimatedWidget
class WidgetAnimateWidget extends StatefulWidget {
@override
? StatecreateState()=>_WidgetAnimateWidgetState();
}
class _WidgetAnimateWidgetState extends Statewith SingleTickerProviderStateMixin {
AnimationController?controller;
late Animation animation;
@override
? void initState() {
super.initState();
// 創(chuàng)建動畫周期為1秒的AnimationController對象
? ? controller =AnimationController(
vsync:this, duration:const Duration(milliseconds:3000));
final CurvedAnimation curve =CurvedAnimation(
parent:controller!, curve:Curves.easeIn);
// 創(chuàng)建從50到200線性變化的Animation對象
? ? animation =Tween(begin:10.0, end:200.0).animate(curve);
// 啟動動畫
? ? controller!.repeat(reverse:true);
}
@override
? Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body:AnimatedLogo(animation:animation,)
));
}
@override
? void dispose() {
// 釋放資源
? ? controller!.dispose();
super.dispose();
}
}
class AnimatedLogo extends AnimatedWidget {
AnimatedLogo({Key? key,required Animation animation})
:super(key: key, listenable: animation);
Widget build(BuildContext context) {
Animation animation =listenable as Animation;
return Center(
child:Container(
height:animation.value,
width:animation.value,
child:FlutterLogo(),
),
);
}
}
在 AnimatedLogo 的 build 方法中,我們使用 Animation 的 value 作為 logo 的寬和高焰情。這樣做對于簡單組件的動畫沒有任何問題陌凳,但如果動畫的組件比較復(fù)雜,一個更好的解決方案是内舟,將動畫和渲染職責(zé)分離:logo 作為外部參數(shù)傳入合敦,只做顯示;而尺寸的變化動畫則由另一個類去管理验游。這個分離工作充岛,我們可以借助 AnimatedBuilder 來完成。與 AnimatedWidget 類似耕蝉,AnimatedBuilder 也會自動監(jiān)聽 Animation 對象的變化崔梗,并根據(jù)需要將該控件樹標(biāo)記為 dirty 以自動刷新 UI。
class BuilderAnimateWidget extends StatefulWidget {
@override
? StatecreateState() {
return _BuilderAnimateState();
}
}
class _BuilderAnimateState extends Statewith SingleTickerProviderStateMixin {
late AnimationController controller;
late Animationanimation;
@override
? void initState() {
super.initState();
// 創(chuàng)建動畫周期為1秒的AnimationController對象
? ? controller =AnimationController(
vsync:this, duration:const Duration(milliseconds:3000));
final CurvedAnimation curve =CurvedAnimation(
parent:controller, curve:Curves.easeInOut);
// 創(chuàng)建從50到200線性變化的Animation對象
? ? animation =Tween(begin:10.0, end:200.0).animate(curve);
// 啟動動畫
? ? controller.repeat(reverse:true);
}
@override
? Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body:Center(
child:AnimatedBuilder(
animation:animation,
//child:FlutterLogo(),
? ? ? ? ? ? ? ? ? ? builder: (context, child) =>Container(
width:animation.value,
height:animation.value,
child:FlutterLogo(),
)
)
)
));
}
@override
? void dispose() {
// 釋放資源
? ? controller.dispose();
super.dispose();
}
}
hero 動畫
通過 Hero赔硫,我們可以在兩個頁面的共享元素之間炒俱,做出流暢的頁面切換效果。
為了實現(xiàn)共享元素變換爪膊,我們需要將這兩個組件分別用 Hero 包裹权悟,并同時為它們設(shè)置相同的 tag “hero”。
class Page2 extends StatelessWidget {
Widget build(BuildContext context) {
return? Scaffold(
appBar:AppBar(title:Text('Page1'),),
body:GestureDetector(
child:Row(children: [
Hero(
tag:'hero',// 設(shè)置共享 tag
? ? ? ? ? ? ? ? child:Container(
width:100, height:100,
child:FlutterLogo())
),
Text('點擊Logo查看Hero效果')
],),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_)=>Page2()));
},
)
);
}
}
class Page2 extends StatelessWidget {
@override
? Widget build(BuildContext context) {
return? Scaffold(
appBar:AppBar(title:Text('Page2'),),
body:Hero(
tag:'hero',// 設(shè)置共享 tag
? ? ? ? ? ? child:Container(
width:300, height:300,
child:FlutterLogo()
))
);
}
}