動畫的基本使用代碼示例:
class AnimationDemo extends StatefulWidget {
@override
_AnimationDemoState createState() => _AnimationDemoState();
}
class _AnimationDemoState extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _curvedAnimation;
Animation _sizeAnimation;
@override
void initState() {
super.initState();
// 創建AnimationController
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
// 設置Curve的值
_curvedAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.linear,
);
// Tween
_sizeAnimation = Tween(begin: 50.0, end: 150.0).animate(_curvedAnimation);
// 監聽動畫值改變
_controller.addListener(() {
setState(() {});
});
// 監聽動畫狀態改變
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reverse();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animation Demo'),
),
body: Center(
child: Icon(
Icons.favorite,
color: Colors.red,
size: _sizeAnimation.value,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
if (_controller.isAnimating) {
_controller.stop();
print(_controller.status);
} else if (_controller.status == AnimationStatus.forward) {
_controller.forward();
} else if (_controller.status == AnimationStatus.reverse) {
_controller.reverse();
} else {
_controller.forward();
}
},
),
);
}
@override
void dispose() {
// 釋放動畫資源
_controller.dispose();
super.dispose();
}
}
使用AnimatedWidget簡化
上面的示例通過addListener()
和setState()
來更新UI,這一步其實是通用的,如果每個動畫中都加這么一句是比較繁瑣的,還會造成不必要的widget重建。AnimatedWidget
類封裝了調用setState()
的細節,并允許我們將widget
分離出來。優化后的代碼:
class _AnimatedIcon extends AnimatedWidget {
_AnimatedIcon(Animation animation) : super(listenable: animation);
@override
Widget build(BuildContext context) {
final Animation animation = listenable;
return Icon(
Icons.favorite,
color: Colors.red,
size: animation.value,
);
}
}
class _AnimationDemoState extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _curvedAnimation;
Animation _sizeAnimation;
@override
void initState() {
super.initState();
// 創建AnimationController
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
// 設置Curve的值
_curvedAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.linear,
);
// Tween
_sizeAnimation = Tween(begin: 50.0, end: 150.0).animate(_curvedAnimation);
// 監聽動畫狀態改變
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reverse();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animation Demo'),
),
body: Center(
child: _AnimatedIcon(_sizeAnimation),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
if (_controller.isAnimating) {
_controller.stop();
print(_controller.status);
} else if (_controller.status == AnimationStatus.forward) {
_controller.forward();
} else if (_controller.status == AnimationStatus.reverse) {
_controller.reverse();
} else {
_controller.forward();
}
},
),
);
}
@override
void dispose() {
// 釋放動畫資源
_controller.dispose();
super.dispose();
}
}
用AnimatedBuilder重構
用AnimatedWidget
可以從動畫中分離出widget,而動畫的渲染過程仍然在AnimatedWidget
中,如果再添加一個widget
另一個動畫效果,那么需要再實現一個AnimatedWidget
,這樣不是很優雅,如果能把渲染過程也抽象出來,那就會好很多,而AnimatedBuilder
正是將渲染邏輯分離出來,代碼示例:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animation Demo'),
),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Icon(
Icons.favorite,
color: Colors.red,
size: _sizeAnimation.value,
);
},
),
),
}
通過AnimatedBuilder
可以封裝常見的過渡效果來復用動畫,代碼示例:
class GrowTransition extends StatelessWidget {
GrowTransition({this.child, this.animation});
final Widget child;
final Animation animation;
Widget build(BuildContext context) {
return Center(
child: new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return Container(
height: animation.value,
width: animation.value,
child: child,
);
},
child: child,
),
);
}
}
Flutter中正是通過這種方式封裝了很多動畫,如:FadeTransition
、ScaleTransition
、SizeTransition
等。