Flutter給我們提供了很多而且很好用的內置動畫,這些動畫僅僅需要簡單的幾行代碼就可以實現一些不錯的效果,Flutter的動畫分為補間動畫和基於物理的動畫,基於物理的動畫我們先不說。 補間動畫很簡單,Android裡面也有補間動畫,就是給UI設置初始的狀態和結束狀態,經過我們定義的一段時間,系統去... ...
如需轉載,請註明出處:Flutter學習筆記(36)--常用內置動畫
Flutter給我們提供了很多而且很好用的內置動畫,這些動畫僅僅需要簡單的幾行代碼就可以實現一些不錯的效果,Flutter的動畫分為補間動畫和基於物理的動畫,基於物理的動畫我們先不說。
補間動畫很簡單,Android裡面也有補間動畫,就是給UI設置初始的狀態和結束狀態,經過我們定義的一段時間,系統去幫助我們實現開始到結束的過渡變化,這就是補間動畫。
今天我們要看的Flutter的內置動畫就是補間動畫,根據Flutter提供的動畫組件,我們去設置初始、結尾的狀態,並且定義一下這個變化過程所需要的時間,再經過系統的處理(其實就是setState())來達到動畫的效果。
接下來我們會寫一下常用的內置動畫組件,並且提供一下動畫效果的gif,方便大家更直觀的去理解。
- AnimatedContainer
看到Container我們就會知道這是一個帶有動畫屬性的容器組件,這個組件可以定義大小、顏色等屬性,那麼我們是不是就可以給這個組件設置初始和結束的大小及顏色的屬性值,然後通過系統來幫助我們來補足中間過程的動畫呢?
答案是可以的,下麵看一下demo和動畫效果:
class _MyHomePageState extends State<MyHomePage> { double _width = 100.0; double _height = 100.0; Color _color = Colors.red; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: AnimatedContainer( width: _width, height: _height, duration: Duration(seconds: 2), color: _color, curve: Curves.bounceInOut, ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _width = 300.0; _height = 300.0; _color = Colors.green; }); }, tooltip: 'Increment', child: Icon(Icons.adjust), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
demo很簡單,就是先定義好組件初始的大小和顏色,點擊按鈕,在按鈕事件裡面去更改大小和顏色的屬性值。這裡唯一需要特別說一下就是curve這個屬性。
curve指的是動畫曲線?我開始的時候不理解這個動畫曲線是什麼意思,後來看了一組圖之後,豁然開朗。demo裡面curve我們用的是Curves.bounceInOut。如下:
它其實就是一個非線性的動畫的變化形式(變化過程)也可以理解為就是一種函數,也不知道這麼說大家能不能理解。
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_decelerate.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_sine.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quad.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_cubic.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quart.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quint.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_expo.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_circ.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_back.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_sine.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quad.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quart.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quint.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_expo.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_circ.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_back.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_sine.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quad.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_cubic.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quart.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quint.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_expo.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_circ.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_back.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_out.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_out_slow_in.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_slow_middle.mp4} /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4}
這裡是每一種curve曲線的表現形式,大家可以看看,也可以在demo裡面多嘗試,或者可以看另一篇博客,有動畫曲線Curves 效果。
-
AnimatedCrossFade
Flutter中文網:一個widget,在兩個孩子之間交叉淡入,並同時調整他們的尺寸。
個人說明:CrossFade,故名思意,淡入淡出,AnimatedCrossFade組件包含兩個子widget,一個firstChild一個secondChild,這兩個組件根據狀態(我們自己定義的一個標識)改變狀態,
一個淡入,一個淡出,同時改變大小或顏色等屬性。
class _MyHomePageState extends State<MyHomePage> { bool _showFirst = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: AnimatedCrossFade( firstChild: Container( width: 100, height: 100, color: Colors.red, alignment: Alignment.center, child: Text('firstChild'), ), secondChild: Container( width: 200, height: 200, color: Colors.green, alignment: Alignment.center, child: Text('secondChild'), ), duration: Duration(seconds: 3), crossFadeState: _showFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond, ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _showFirst = false; }); }, tooltip: 'Increment', child: Icon(Icons.adjust), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
-
Hero
Hero常用於頁面跳轉的過長動畫,比如電商App有一個商品列表,列表的每個item都有一張縮略圖,點擊會跳轉到詳情頁面,在Flutter中將圖片從一個路由飛到另一個路由稱為hero動畫,儘管相同的動作有時也稱為 共用元素轉換。
class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Hero( tag: 'heroTag', child: ClipOval( child: Image.asset( 'images/banner.png', width: 60, height: 60, fit: BoxFit.cover, ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { Navigator.push(context, MaterialPageRoute(builder: (_) { return new HeroPage(); })); }); }, tooltip: 'Increment', child: Icon(Icons.adjust), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
詳情頁面:
import 'package:flutter/material.dart'; class HeroPage extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'HeroPage', home: Scaffold( appBar: AppBar( title: Text('HeroPage'), ), body: Center( child: GestureDetector( child: Hero( tag: 'heroTag', child: ClipOval( child: Image.asset( 'images/banner.png', width: 300, height: 300, fit: BoxFit.cover, ), ), ), onTap: () { Navigator.pop(context); }, ), ), ), ); } }
註:特別強調一下,為了將兩個頁面的元素關聯起來,hero有個tag標識,前後兩個頁面的tag標識必須一樣,不然的話元素是關聯不起來的,也就意味著不會產生hero動畫。
1.同級tag不允許相同。
2.前後頁面想要有hero動畫,tag必須相同。
3.前後關聯起來的hero組件,其各自內部的child組件不是必須一樣的,就是說前面的hero的子組件可以是image,後面的hero的子組件可以是image以外的其他組件。
-
AnimatedBuilder
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<double> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 3), vsync: this); _animation = new Tween(begin: 0.0, end: 200.0).animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: AnimatedBuilder( animation: _animation, builder: (BuildContext context, Widget child) { return Center( child: Container( color: Colors.red, width: _animation.value, height: _animation.value, child: child, ), ); }, )); } }
AnimationController:動畫控制器(定義動畫過程時長)。
Animation:動畫變化區間值(也可以說是開始和結束的關鍵幀值),demo里定義的值為初始0,結束200。
_animation.value:關鍵幀值是0和200,_animation.value的值為0--200之間連續變化的值(0-1-2-3-...-198-199-200)。
-
DecoratedBoxTransition
Decortated可以給容器添加各種外觀裝飾,比如增加圓角、陰影等裝飾。DecoratedBox的動畫版本,可以給它的Decoration不同屬性使用動畫
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<Decoration> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 3), vsync: this); _animation = DecorationTween( begin: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(0.0)), color: Colors.red), end: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30.0)), color: Colors.green)) .animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center( child: DecoratedBoxTransition( decoration: _animation, child: Container( width: 100, height: 100, ), ), ), ); } }
-
FadeTransition
透明度變化動畫,因為透明度也是在0-1之間變化的,所以animation就還繼續用double類型的就可以了。
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<double> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this); _animation = Tween(begin: 1.0, end: 0.0).animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center( child: FadeTransition( opacity: _animation, child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Colors.red, ), ), ), ), ); } }
- RotationTransition
旋轉動畫,對widget使用旋轉動畫 1~360°(Tween(begin: 0.0, end: 1.0))這裡的0-1指的是0°-360°
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<double> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this); _animation = Tween(begin: 0.0, end: 1.0).animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center( child: RotationTransition( turns: _animation, child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Colors.red, ), child: Center(child: Text('data')), ), ), ), ); } }
-
ScaleTransition
縮放動畫,Tween(begin: 1.0, end: 0.2)指的是原大小的倍數,demo里是由原大小縮小到原來的0.2倍。
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<double> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this); _animation = Tween(begin: 1.0, end: 0.2).animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center( child: ScaleTransition( scale: _animation, child: Container( width: 200, height: 200, decoration: BoxDecoration( color: Colors.red, ), child: Center(child: Text('data')), ), ), ), ); } }
-
SizeTransition
僅一個方向進行縮放
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { Animation<double> _animation; AnimationController _animationController; @override void initState() { _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this); _animation = Tween(begin: 1.0, end: 0.2).animate(_animationController); _animationController.forward(); super.initState(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Center( child: SizeTransition( axis: Axis.horizontal, sizeFactor: _animation, child: Center( child: Container( width: 200, height: 200, decoration: BoxDecoration( color: Colors.red, ), child: Center(child: Text('data')), ), ), ), ), ); } }
以上!有任何疑問歡迎留言!