この記事はFlutter 全部俺 Advent Calendar 16日目の記事です。
このアドベントカレンダーについて
このアドベントカレンダーは @itome が全て書いています。
基本的にFlutterの公式ドキュメントとソースコードを参照しながら書いていきます。誤植や編集依頼はTwitterにお願いします。
応用的なアニメーションを作る手順
Flutterのカスタムアニメーションを作るときは、
AnimationControllerの用意- 各種
Tweenの割り当て AnimationのWidgetへの割り当て
の順番で考えていきます。
AnimationControllerの用意
アニメーションを扱うWidgetはTickerProviderStateMixinなどを使いやすいように、
StatefulWidgetにしておくのがいいです。
まずは、initStateでActionControllerを用意しておきましょう。
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
}
各種Tweenの割り当て
作ったActionCreatorにTweenを割り当てて0.0~1.0のdouble値を
必要な値にマッピングしたAnimationを作ります。
AnimationControllerのdrive関数にTweenを渡すか、
Tweenのanimate関数にAnimationControllerを渡すことで、Animationが取得できます。
複数のTweenをつなげることもできます。例えばTweenAとTweenBをつなげると
元の値(0.0~1.0) → TweenAで変換した値 →TweenBで変換した値
のようになります。つまり、以下のコードは全て同じ挙動をします。ケースに分けて使い分けましょう。
...
Animation<Color> _animation;
@override
void initState() {
...
final curvedAnimation = CurveTween(curve: Curves.bounceIn).animate(_controller);
_animation = ColorTween(begin: Colors.blue, end: Colors.red,).animate(_controller);
}
...
Animation<Color> _animation;
@override
void initState() {
...
_animation = _controller
.drive(CurveTween(curve: Curves.bounceIn))
.drive(ColorTween(begin: Colors.blue, end: Colors.red));
}
...
Animation<Color> _animation;
@override
void initState() {
...
_animation = ColorTween(begin: Colors.blue, end: Colors.red)
.chain(CurveTween(curve: Curves.bounceIn))
.animate(_controller);
}
AnimationのWidgetへの割り当て
Animationの現在の値が_animation.valueで取得できるので、Widgetのパラメーターとして使います。
@override
Widget build(BuildContext context) {
return Container(color: _animation.value)
}
しかし、これでは_animationの値が変化してもWidgetの色を変えることができないので、
AnimatedBuilderを使います。
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, _) {
return Container(color: _animation.value)
},
);
}
AnimatedBuilderはanimation: に渡したAnimationの値が変わると
builderで作られるWidgetが再描画されるWidgetです。
これを使うことで_animationの値に合わせてWidgetの色が変わるようになります。
また、AnimatedWidgetを継承してWidgetをつくることでも、値の変化に対応することが可能です。
AnimatedWidgetの場合も仕組みは同じで、super(listenable: )に渡したAnimationが更新される
たびにbuild関数が再実行されます。
class ColorTransition extends AnimatedWidget {
const ColorTransition({Animation color}): super(listenable: color);
Animation<Color> get _color => listenable;
@override
Widget build(BuildContext context) {
return Container(color: _color.value);
}
}
Animated系WidgetとTransition系Widget
ここまではアニメーションをすべて自前で実装する手順を紹介していましたが、 デフォルトで用意されているWidgetを使うことで、もっと手軽にアニメーションを組むことができるようになります。
FlutterにはAnimatedContainerやAnimatedThemeなど、Animated~という名前のWidgetと
SlideTransitionやFadeTransitionなどの~Transitionという名前のWidgetがあります。
これらはどちらもアニメーションを行うWidgetで、AnimatedPositionedとPositionedTransitionのように
同じアニメーションをできるWidgetがそれぞれ用意されていたりするのでややこしいですが、
両者の違いは自前でAnimationControllerを持っているかどうかです。
Animated系Widgetは自前でAnimationControllerを持っているので、こうなってほしいという状態を渡すだけで
勝手にそこに向かって動いてくれます。
int _padding = 0;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
AnimatedPositioned(
top: 0,
left: _padding,
width: 100,
height: 100,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
child: ...,
),
],
);
}
上のコードでは_paddingの値をsetState(() => { _padding = ... })で変えるだけで、
500ミリ秒かけてアニメーションをしてくれます。
一方Transition系Widgetは自前ではAnimationControllerを持っていないので、そとから渡してあげる必要があります。
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
PositionedTransition(
rect: _animationController.drive(
RelativeRectTween(
begin: RelativeRect.fromLTRB(0, 0, 100, 100),
end: RelativeRect.fromLTRB(200, 0, 100, 100),
),
),
child: ...,
)
],
);
}
Animated系WidgetはこちらでAnimationControllerを用意しなくていい分、より手軽に使うことができます。
一方こちらからAnimationControllerを渡せるTransitionは、アニメーションを途中で止めたり逆再生したりと
Animated系Widgetではできなかったような柔軟な操作もできます。
目的のアニメーションがAnimated系にもTransition系にも用意されている場合は、まずAnimated系Widgetから検討してみて、
それでもうまくアニメーションが作れなかったときに、Transition系Widgetを試してみるのがいいと思います。
どのようなAnimated系WidgetやTransition系Widgetが用意されているかは、
monoさんの以下の記事を参考にしてください。
Flutterのお手軽にアニメーションを扱えるAnimated系Widgetをすべて紹介
https://link.medium.com/7471nO5Mi2
FlutterのTransition系アニメーションWidgetをすべて紹介
15日目: Flutterのアニメーションを理解する(前編) :
https://itome.team/blog/2019/12/flutter-advent-calendar-day15
17日目: FlutterのAnimatedWidgetを使いこなす :
https://itome.team/blog/2019/12/flutter-advent-calendar-day17