🕌

【Flutter】まじでわからんAnimationControllerを整理する

2024/02/21に公開

AnimationController難しくないですか?

整理しよう。

まずアニメーションってくらいだから、StatefulWidgetが大枠なのは自明。

class AnimatedProgressIndicator extends StatefulWidget 

なにかの値の変化を検知してそのアニメーションを動かすとするならば、
その検知対象をもらう必要があり。
コンストラクタでvalueにもらう。

class AnimatedProgressIndicator extends StatefulWidget {
  final double value;

  const AnimatedProgressIndicator({
    super.key,
    required this.value,
  });

で、StatefulWidgetなのでstateの生成は必須。

class AnimatedProgressIndicator extends StatefulWidget {
  final double value;

  const AnimatedProgressIndicator({
    super.key,
    required this.value,
  });

  @override
  State<AnimatedProgressIndicator> createState() {
    return _AnimatedProgressIndicatorState();
  }
}

ここまでは普通通りというか、教科書通り。
問題はstate側。

とりあえず、サンプルソースを貼ってから考える。

class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Color?> _colorAnimation;
  late Animation<double> _curveAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 1200),
      vsync: this,
    );

    final colorTween = TweenSequence([
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.red, end: Colors.orange),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: ColorTween(begin: Colors.yellow, end: Colors.green),
        weight: 1,
      ),
    ]);

    _colorAnimation = _controller.drive(colorTween);
    _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
  }

initStateで、いろいろと初期化する。
AnimationControllerってなに?っていうことでとりあえずドキュメントみてみる。

https://api.flutter.dev/flutter/animation/AnimationController-class.html

アニメーション用のコントローラー。
このクラスを使用すると、次のようなタスクを実行できます。
アニメーションを順方向または逆方向に再生したり、アニメーションを停止したりできます。
アニメーションを特定の値に設定します。
アニメーションのupperBound値とlowerBound値を定義します。
物理シミュレーションを使用して、投げ飛ばすアニメーション効果を作成します。
デフォルトでは、AnimationControllerは、指定された期間中に 0.0 から 1.0 の範囲の値を線形に生成します。アニメーション コントローラーは、アプリを実行しているデバイスが新しいフレームを表示する準備ができるたびに、新しい値を生成します (通常、この速度は 1 秒あたり約 60 値です)。

わけわからん。
とりあえずアニメーションをコントロールする人。である。

で、そのコントロールする人は、drive関数をもっている。

    _colorAnimation = _controller.drive(colorTween);
    _curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));

これ何してんの?って話。

chatgptにきいてみる。

User
_colorAnimation = _controller.drive(colorTween);
ってどういう意味だっけ。driveってなにするの

ChatGPT
_colorAnimation = _controller.drive(colorTween);というコードは、Flutterのアニメーションフレームワークにおいて、AnimationController _controllerのアニメーション出力を特定のアニメーション効果(この場合は色の変化を表すcolorTween)に「適用」する操作を行います。ここで使われるdriveメソッドは、アニメーションの値を変換して、新しいアニメーションを生成するためのメカニズムです。

driveメソッドの役割と動作
値の変換: driveメソッドは、AnimationControllerからの数値のストリーム(通常は0から1まで)を、別の形式や範囲のアニメーション値に変換します。この変換にはTween(補間)オブジェクトが使用され、アニメーションの入力値に基づいて、異なる形式の出力値(この場合は色)を生成します。
アニメーションのカスタマイズ: driveを使用することで、基本的な進捗値に基づいて様々なアニメーション効果を実現できます。例えば、数値を色に変換したり、特定の範囲の数値に補間したりすることができます。
colorTweenとの組み合わせ
この例では、colorTweenはTweenSequence<Color>のインスタンスであり、複数の色の間で段階的に変化するアニメーションを定義しています。_controllerの進捗に応じて、colorTweenは適切な色値を計算し、その結果が_colorAnimationとして使用されます。

具体的な使用例
dart
Copy code
_colorAnimation = _controller.drive(colorTween);
このコード行により、_controllerの進捗(0から1までの値)はcolorTweenによって色の変化に変換され、その結果のアニメーションが_colorAnimationとして利用可能になります。これをLinearProgressIndicatorのvalueColorプロパティに適用することで、進捗バーの色がアニメーションに応じて変化します。

結論
driveメソッドは、基本的なアニメーションの進捗値をより具体的なアニメーション効果に「運転(ドライブ)」するために使用され、これによりFlutterでリッチでカスタマイズ可能なアニメーションを実装することができます。

ということです。
要するにTweenオブジェクトに従ったアニメーションに変換してくれるっていうことのようです。
戻り値はAnimation<U>。

Animation<U> drive<U>(Animatable<U> child)

じゃあそれどうやって使うのって話。

_AnimatedProgressIndicatorStateクラスのbuildで使う。

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) => LinearProgressIndicator(
        value: _curveAnimation.value,
        valueColor: _colorAnimation,
        backgroundColor: _colorAnimation.value?.withOpacity(0.4),
      ),
    );
  }

アニメーションを作るときはAnimatedBuilderをリターンするみたい。
animationプロパティにはさっきつくった、AnimationControllerのインスタンスを指定する。
valueプロパティには、実際の値を設定する。実際の値というのはアニメーション対象の値。

_curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));

ちなみにこれはイージングカーブというアニメーションにしてねという指定らしい。
他にもeaseとか、easeInToLinearとかいろいろあるらし。

個人的わからんポイントとしてこれ。

value: _curveAnimation.value,

カーブアニメーションの値を設定しているのはなんで?って思っていた。

value: widget.value

じゃだめなの?と。

結論からいうとこれでも今回のサンプルに関しては動く。
なぜならwidget.valueの値が0から1の範囲だから。
LinearProgressIndicatorのvalueプロパティは進捗を示すものだから進捗が0パーセントから100パーセント。なので0から1が設定範囲。

widget.valueが0から1以外の値をとるならつかえないから、
_curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
で生成したCurveTweenの値で確実に0から1の値を設定しましょうねってことだと理解。

で、今のところ、実際に監視している値ってアニメーションクラス把握していなくない?って思ったあなた。賢いです。

なのでトリガーがあります。

  @override
  void didUpdateWidget(oldWidget) {
    super.didUpdateWidget(oldWidget);
    _controller.animateTo(widget.value);
  }

こやつがトリガーです。
_AnimatedProgressIndicatorStateは
AnimatedProgressIndicatorウィジェットのプロパティの変更を監視しています。
なので、プロパティ(インプットテキストなど)に変更が入るとコールされます。

なんでコールされるのかは、

class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>

って宣言しているのでAnimatedProgressIndicatorを監視すると言っているからです。
監視しているのはフレームワーク側なので我々はどうやって監視しているのか?まではわからんというか知りたきゃ裏までソース追うしかないです。

以上。

Discussion