はじめに
この様に拡大とフェードアウトをシーケンシャルに設定したい場合にIntervalを使うと良い感じに書けたので紹介します。
AnimationControllerのwhenCompleteを使う方法
Flutter初心者の私がシーケンシャルなAnimationを設定する方法としてまず辿り着いたのがこの方法です。
まず、拡大用のAnimationControllerとフェードアウト用のAnimationControllerをそれぞれ用意します。
そして、次の様なロジックで動かします。
- 拡大用のAnimationを再生
- 拡大用のAnimationが完了したら、とフェードアウト用のAnimationを再生
- フェードアウト用のAnimationが完了したら、拡大用のAnimationを再生
コード
class _HomeWidgetState extends State<HomeWidget> with TickerProviderStateMixin {
late AnimationController _fadeAnimCtlr;
late Animation<double> _fadeAnimation;
late AnimationController _scaleAnimCtlr;
late Animation<double> _scaleAnimation;
@override
void initState() {
_fadeAnimCtlr = AnimationController(vsync: this, duration: Duration(milliseconds: 1000));
_fadeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(_fadeAnimCtlr)
..addListener(() {
setState(() {});
});
_scaleAnimCtlr = AnimationController(vsync: this, duration: Duration(milliseconds: 1000));
_scaleAnimation = Tween<double>(begin: 1.0, end: 3.0).animate(_scaleAnimCtlr)
..addListener(() {
setState(() {});
});
repeatAnimation();
}
void repeatAnimation() {
_scaleAnimCtlr.forward().whenComplete(() {
_fadeAnimCtlr.forward().whenComplete(() {
_scaleAnimCtlr.reset();
_fadeAnimCtlr.reset();
repeatAnimation();
});
});
}
@override
void dispose() {
_fadeAnimCtlr.dispose();
_scaleAnimCtlr.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
return Stack(
children: <Widget>[
Positioned(
top: screenSize.height * 0.2,
left: screenSize.width * 0.5,
child: ScaleTransition(
scale: _scaleAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: Text("Hello\nWorld",
style: TextStyle(
fontSize: 20,
color: Colors.white,
))),
),
),
],
);
}
}
Intervalを使う方法
次にここの情報から学んだIntervalを使った方法です。
拡大フェードアウト用のひとつのAnimationControllerを用意し、Intervalを使ってそれぞれのAnimationが動くタイミングを指定します。
全体として2秒のAnimationの前半を拡大、後半をフェードアウトを動作させるというイメージです。
コード
class _HomeWidgetState extends State<HomeWidget> with SingleTickerProviderStateMixin {
late AnimationController _fadeScaleAnimCtlr;
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
@override
void initState() {
_fadeScaleAnimCtlr = AnimationController(vsync: this, duration: Duration(milliseconds: 2000));
_fadeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(parent: _fadeScaleAnimCtlr, curve: Interval(0.5, 1.0)))
..addListener(() {
setState(() {});
});
_scaleAnimation = Tween<double>(begin: 1.0, end: 3.0).animate(
CurvedAnimation(parent: _fadeScaleAnimCtlr, curve: Interval(0.0, 0.5)))
..addListener(() {
setState(() {});
});
_fadeScaleAnimCtlr.repeat();
}
@override
void dispose() {
_fadeScaleAnimCtlr.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
return Stack(
children: <Widget>[
Positioned(
top: screenSize.height * 0.2,
left: screenSize.width * 0.5,
child: ScaleTransition(
scale: _scaleAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: Text("Hello\nWorld",
style: TextStyle(
fontSize: 20,
color: Colors.white,
))),
),
),
],
);
}
}
まとめ
前者の方法も後者の方法もメリット、デメリットがあると思います。
メリット | デメリット | |
---|---|---|
whenComplete | 直感的に分かりやすい | Animation毎にContorllerが必要 Tickerが複数必要なのでアプリの動作コストが高い |
Interval | AnmationContorllerがひとつで済む Tickerがひとつで済むのでアプリの動作コストが低い |
直感的に分かりづらい |
恐らく、Animationを設定する対象(Widget)が同じであればIntervalを使った方がすっきりして良いと思いますが、別であればAnimationControllerを分けた方が後々良かったりするのではないかと思っています。