Animation
Flutter動畫庫的核心類,任何動畫都基於Animation
Animation
對象能夠知道當前動畫的狀態(value
值),不過屏幕上顯示的效果是一律不知的,即Animation
對象只負責動畫過程當中的數值變化AnimationController
管理動畫,例如播放、反轉,停用等CurvedAnimation
用於實現非線性動畫,如快進慢出,慢進快出Tween
區間過渡插值,如數字從0到255,動畫從黃到綠,分別能夠用IntTween
、ColorTween
下面是一個沒有動畫,寬高固定爲300的部件(Widget)bash
import 'package:flutter/material.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
// 寬高固定
height: 300,
width: 300,
child: FlutterLogo(),
),
);
}
}
複製代碼
實現圖片從小到大(從寬高爲0到寬高爲300),步驟以下:less
import 'package:flutter/animation.dart';
,並混入SingleTickerProviderStateMixin
Tween
建立動畫類,令值在0~300之間變化addListener
方法調用setState
更新value
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
// 定義動畫變量
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
// 實例化控制類,時長3秒, vsync能夠在當前動畫對用戶不可見時節省機器資源(不進行動畫計算)
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
// 在0~300之間變更
animation = Tween<double>(begin: 0, end: 300).animate(controller)
..addListener(() {
// 經過setState令動畫中的value產生變化
setState(() {});
});
// 開始播放動畫
controller.forward();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
// setState時value的變化產生在這裏
height: animation.value,
width: animation.value,
child: FlutterLogo(),
),
);
}
@override
void dispose() {
// 註銷動畫實例,釋放內存
controller.dispose();
super.dispose();
}
}
複製代碼
使用AnimatedWidget
組件簡化示例1的代碼,再也不須要手動調用setState
,步驟以下:ide
AnimatedWidget
建立動畫部件Animation
參數,並傳遞給父構造函數的listenable
參數build
中經過listenable
得到動畫中的value
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
// 再也不須要手動setState
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
@override
Widget build(BuildContext context) => AnimatedLogo(
animation: animation,
);
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class AnimatedLogo extends AnimatedWidget {
// 構造參數中的第二個參數爲Animation,傳遞給父構造函數的listenable
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
// 經過listenable獲取value
final Animation<double> animation = listenable;
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
height: animation.value,
width: animation.value,
child: FlutterLogo(),
),
);
}
}
複製代碼
監控動畫狀態進度,並在過程當中根據狀態改變更畫,實現動畫的無限循環,步驟以下:函數
addStatusListener
completed
(播放完畢)時,調用reverse()
方法反轉動畫,使動畫回到初始狀態dismissed
,此時再調用forward()
方法繼續播放,實現無限循環import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
/***
* 經過addStatusListener得到當前動畫進度,實現無限循環的動畫
*
* AnimationStatus的四個狀態
* 1. AnimationStatus.forward 動畫播放
* 2. AnimationStatus.completed 動畫播放完成
* 3. AnimationStatus.dismissed 動畫回到初始狀態後中止
* 4. AnimationStatus.reverse 動畫反轉
*/
animation = Tween<double>(begin: 0, end: 300).animate(controller)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 動畫完成後反轉
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
// 反轉回初始狀態時繼續播放,實現無限循環
controller.forward();
}
});
// 播放動畫
controller.forward();
}
@override
Widget build(BuildContext context) => AnimatedLogo(
animation: animation,
);
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class AnimatedLogo extends AnimatedWidget {
// 構造參數中的第二個參數爲Animation,傳遞給父構造函數的listenable
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
// 經過listenable獲取value
final Animation<double> animation = listenable;
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
height: animation.value,
width: animation.value,
child: FlutterLogo(),
),
);
}
}
複製代碼
在前面的動畫中能夠看到,部件顯示的內容和動畫混合在一塊兒了,Flutter官網給出了一個可使動畫和部件內容分離的寫法 按我理解,全部只是改變寬高(僅當前示例)的內容部件均可以使用這個GrowTransition動畫過渡部件 步驟以下:動畫
AnimatedBuilder
編寫過渡的部分import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
// 建立動畫
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
// 將內容部件和動畫組合起來
@override
Widget build(BuildContext context) => GrowTransition(
animation: animation,
child: LogoWidget(),
);
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
// 內容部件
class LogoWidget extends StatelessWidget {
Widget build(BuildContext context) => Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: FlutterLogo(),
);
}
// 動畫過渡部件
class GrowTransition extends StatelessWidget {
GrowTransition({this.child, this.animation});
// 須要有動畫過渡效果的部件
final Widget child;
// 爲部件使用的動畫
final Animation<double> animation;
Widget build(BuildContext context) => Center(
/**
* AnimatedBuilder將寬高定義爲過渡狀態
* 按我理解,全部只是改變寬高的內容部件均可以使用這個GrowTransition動畫過渡部件
* 固然也能夠定義一個只改變透明度的動畫過渡部件複用
*/
child: AnimatedBuilder(
animation: animation,
builder: (context, child) => Container(
height: animation.value,
width: animation.value,
child: child,
),
child: child),
);
}
複製代碼
非線性動畫很好理解,就慢進快出,快進慢出等。使用CurvedAnimation
類,步驟以下:ui
AnimationController
,定義相關參數CurvedAnimation
,是得其parent
參數爲前一步建立的動畫控制器,參數curve
爲動畫效果Tween
過渡值,調用evaluate()
方法,參數即爲上一步建立的CurvedAnimation
對象forward()
播放動畫便可import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(LogoApp());
class LogoApp extends StatefulWidget {
_LogoAppState createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
// 曲線動畫的核心的代碼
animation = CurvedAnimation(parent: controller, curve: Curves.easeOut);
controller.forward();
}
@override
Widget build(BuildContext context) => AnimatedLogo(
animation: animation,
);
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class AnimatedLogo extends AnimatedWidget {
// 曲線動畫使用的過渡效果
static final _sizeTween = Tween<double>(begin: 0, end: 300);
// 構造參數中的第二個參數爲Animation,傳遞給父構造函數的listenable
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
// 經過listenable獲取value
final Animation<double> animation = listenable;
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
// 曲線動畫部分開始
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
// 曲線動畫部分結束
child: FlutterLogo(),
),
);
}
}
複製代碼
補充,另外一個簡單示例(修改動畫示例1便可)this
/**
* 非曲線動畫的的另外一個示例
* 把animate的參數從controller改爲animate便可
*/
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut);
animation = Tween<double>(begin: 0, end: 300).animate(curve);
controller.forward();
複製代碼