2020/10/25 Flutter : アニメーションチュートリアルの訳

 

このチュートリアルでは、Flutter で明示的なアニメーションを作成する方法を示します。アニメーション ライブラリで重要な概念、クラス、メソッドをいくつか紹介した後、5 つのアニメーションの例を紹介します。この例は互いに構築され、アニメーション ライブラリのさまざまな側面を紹介します。

Flutter SDK には、フェードトランジションサイズトランジションスライドトランジションなどの暗黙的なトランジション アニメーションも用意されています。これらの単純なアニメーションは、開始点と終了点を設定することによってトリガーされます。ここで説明する明示的なアニメーションよりも実装が簡単です。


Contents

Essential animation concepts and classes

ポイントは?

  • AnimationクラスはFlutterのアニメーションライブラリのコアクラスであり、アニメーションを動かすために使われる値を補間します。
  • Animationオブジェクトはアニメーションの現在の状態(state)を把握しています。(例えば、スタートしているか、停止しているか、順方向の再生か、逆方向か?など。)しかし、スクリーン上でどのように表示されているかは把握していません。
  • AnimationControllerはAnimationクラスを管理します。
  • CurvedAnimationクラスは、非リニアカーブのアニメーション進行を定義します。
  • Tweenは、アニメーション化されているオブジェクトによって使用されるデータの範囲を補間します。 たとえば、Tweenは、赤から青、または0から255への補間を定義することができます。
  • リスナーとStatusListenerを使用して、アニメーションの状態の変化を監視します。
Flutterのアニメーションシステムは、Animationオブジェクトに基づいています。 ウィジェットは、現在の値を読み取って状態の変化を聞くことにより、これらのアニメーションをビルド関数に直接組み込むことができます。または、他のウィジェットに渡す、より複雑なアニメーションの基礎としてアニメーションを使用することもできます。

Animation<double>

Flutterでは、Animationオブジェクトはスクリーン上の表示は感知しません。Animatinクラスは、「現在値」と「ステート」(dismissed(開始時点)かcompleted(終了時点))を把握する抽象クラスです。

最もよく使われるanimationの型はAnimation<double>型です。

Animationオブジェクトは、特定の期間(duration)にわたって2つの値の間で補間された数値を順次生成します。

Animationオブジェクトの出力は、線形、曲線(非線形)、階段関数、またはその他の考案可能なマッピングである可能性があります。

線形、曲線など、予めFlutterで用意されている出力を使うこともできるし、独自に出力を定義することもできる、ということ。

Animationオブジェクトの制御方法によっては、逆方向に実行したり、途中で方向を切り替えたりすることもできます。

Animationクラスはdouble以外の型を補間することもできます。Animation<Color>、あるいはAnimation<Size>などです。

Animationオブジェクトはstateを持っています。Animationオブジェクトの現在の値は.value(メンバフィールド(メンバプロパティ))を利用して参照できます。


Curved­Animation

CurvedAnimationは非リニアカーブのアニメーションの進行を定義します。

animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);

Note:Curvesクラスには、多くの一般的によく使われるcurveが定義されています。また自分で定義することもできます。例えば、

import 'dart:math';

class ShakeCurve extends Curve {
  @override
  double transform(double t) => sin(t * pi * 2);
}

Browse the Curves documentation for a complete listing (with visual previews) of the Curves constants that ship with Flutter.

↑非常にわかりやすいので見た方が良いです。

CurvedAnimationとAnimationController(次のセクションで説明)はどちらもAnimation <double>タイプであるため、これらを交換可能に渡すことができます。 CurvedAnimationは、変更するオブジェクトをラップします。curveを実装するためにAnimationControllerをサブクラス化する必要はありません。


Animation­Controller

AnimationControllerは、ハードウェアが新しいフレームの準備ができるたびに新しい値を生成する特別なアニメーションオブジェクトです。

デフォルトでは、AnimationControllerは、指定された期間(duration)中に0.0から1.0までの数値をリニアに生成します。 たとえば、このコードはAnimationオブジェクトを作成しますが、実行を開始しません。

controller =
    AnimationController(duration: const Duration(seconds: 2), vsync: this);

AnimationControllerクラスはAnimation<double>クラスを継承しています。ですからAnimationオブジェクトが必要な場所でAnimationControllerオブジェクトを使うことができます。

AnimationControllerクラスには、アニメーションをコントロールするためのメソッドが追加されています。

例えば、

controller.forward();

↑メソッドを呼び出すことで、アニメーションをスタートさせることができます。

アニメーションのvalueの生成は画面の更新に関連しており、通常は1秒あたり60の数字が生成されます。

アニメーションのvalueが変化する(新たに生成される)たびに、Animationオブジェクトは、addListenerメソッドにより追加されたリスナーを呼び出します。

To create a custom display list for each child, see RepaintBoundary.

工事中🏗

AnimationControllerを作成するときは、vsync引数を渡します。

vsyncの存在は、オフスクリーンアニメーションが不要なリソースを消費するのを防ぎます。

StatefulWidget(のサブクラス)の定義時にSingleTickerProviderStateMixinをミックスインすることにより、StatefulWidget(のサブクラス)をvsyncとして扱うことができます。

簡単にいうと

class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
  Animation<double> animation;
  AnimationController controller;
  //...
  //...

}

↑のようにミックスインすれば_LogoAppStateでアニメーションを扱えるようになる、ということ。

GitHubのanimate1でサンプルを見ることができます。

Note:場合によっては、位置がAnimationControllerの0.0〜1.0の範囲を超えることがあります。 たとえば、fling()関数を使用すると、速度、力、および位置を(Forceオブジェクトを介して)提供できます。 位置は任意であるため、0.0から1.0の範囲外にすることができます。

AnimationControllerが0.0〜1.0の範囲を超えていない場合でも、CurvedAnimationは0.0から1.0の範囲を超える可能性があります。 選択したカーブに応じて、CurvedAnimationの出力は入力よりも広い範囲を持つことができます。 たとえば、Curves.elasticInなどの弾性曲線(elastic curves)は、デフォルトの範囲を大幅にオーバーシュートまたはアンダーシュートします。


Tween

デフォルトでは、AnimationControllerオブジェクトの範囲は0.0から1.0です。 別の範囲または別のデータ型が必要な場合は、Tweenを使用して、別の範囲またはデータ型に補間するようにアニメーションを構成できます。

たとえば、次のTweenは-200.0から0.0になります。

tween = Tween<double>(begin: -200, end: 0);

Tweenクラス(インスタンス)は状態を持たないオブジェクトで、beginとendのみを保持します。

Tweenの唯一の仕事は、入力範囲から出力範囲へのマッピングを定義することです。 入力範囲は通常0.0〜1.0ですが、それ以外でも問題ありません。

Tweenは、Animation <T>ではなく、Animatable <T>を継承します。Animatableクラスは、 アニメーションのようにdouble型を出力する必要はありません。

たとえば、ColorTweenは、2つの色の間の進行を指定します。

colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54);

Tweenオブジェクトは状態を保持しません。

代わりに、アニメーションの現在の値にマッピング関数を適用するevaluate(Animation <double>アニメーション)メソッドを提供します。

Animationオブジェクトの現在の値は、.valueメソッドで確認できます。 evaluate関数は、アニメーション値がそれぞれ0.0と1.0のときに開始と終了が返されるようにするなど、いくつかのハウスキーピングも実行します。


Tween.animate

Tweenオブジェクトを使う場合、Tweenのanimate()メソッドにcontroller(AnimationController)オブジェクトを渡して呼び出します。

例えば、以下のコードは、duration(期間)が500ms、値が0〜255のアニメーションを生成し、alphaにセットします。

AnimationController controller = AnimationController(
    duration: const Duration(milliseconds: 500), vsync: this);

Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller);

Note:animate()メソッドが返すのはAnimation型です。Animatable型ではありません。

以下のサンプルはcontroller、curve、Tweenを宣言しています。

AnimationController controller = AnimationController(
    duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve =
    CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(curve);

Animation notifications

AnimationオブジェクトはListenersとStatusListenersを持つことができます。

ListenersはaddListener()メソッドにより追加します。

StatusListenersはaddStatusListener()メソッドにより追加します。

アニメーションのvalueが変化するたびにListenerが呼び出されます。

最も普通の使用方法は、Listenerの中でsetState()メソッドを呼び出すことでリビルドを発生させることです。

これにより画面が書き換えられてアニメーションが展開される。setState()を呼び出さないと、アニメーションのvalueは変化するが画面は全く変わらない、という状態。

StatusListenerは、アニメーションの開始、終了、順方向の進行、逆方向の進行時に呼び出されます。

これらのstatusはAnimationStatus列挙型で定義されています。

次のセクションではaddListener()メソッドのサンプルを示します。

addStatusListener()メソッドのサンプルはMonitoring the progress of the animationで見ることができます。

 


Animation examples

このセクションでは5つのアニメーションexampleを見ていきます。

Rendering animations

ポイントは?

  • addListener()メソッドとsetState()メソッドを使ってウィジェットにアニメーションを追加する方法

  • アニメーションが新しい値(value)を生成する度に、addListener()メソッドがsetState()メソッドを呼び出します。

  • 必要なvsyncパラメータと共に、AnimationControllerを宣言する方法。

  • Dart言語のカスケードノーテーションを理解する。(“..addListener”)

  • プライベートなクラスをを定義するためにクラス名の最初に_(アンダースコア)をつける。

これまで、時間の経過とともに一連の数値(value)を生成する方法を学習しました。

Animationオブジェクトのvalueを使ってレンダリングする(画面上でアニメーションを展開させる)には、Animationオブジェクトをウィジェットのメンバーとして保存し、その値を使用して描画方法を決定します。

 

App source: animate0

↓ただロゴを表示するだけのサンプル。アニメーションは無い。

 

import 'package:flutter/material.dart';

void main() => runApp(LogoApp());

class LogoApp extends StatefulWidget {
  _LogoAppState createState() => _LogoAppState();
}

class _LogoAppState extends State<LogoApp> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 10),
        height: 300,
        width: 300,
        child: FlutterLogo(),
      ),
    );
  }
}

 




下のサンプルは、ロゴをアニメーション化してゼロからフルサイズに拡大するように変更された同じコードを示しています。

AnimationControllerを定義するときは、vsyncオブジェクトを渡す必要があります。 vsyncパラメーターについては、AnimationControllerセクションで説明しています。

↓AnimationControllerでアニメーション化(AnimatedWidgetなどは使わない。)

↓(自分でaddListener+setState書くパターン)

↓App source: animate1

import 'package:flutter/animation.dart';
import 'package:flutter/material.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: 5), vsync: this);
    animation = Tween<double>(begin: 0, end: 300).animate(controller)
      ..addListener(() {
        setState(() {
          // value自体はcontrollerが勝手に進める(変化させる)ので、
          // 値を変化させるコードを書く必要は無い。
          // ただvalue変更の都度リビルドを発生させる必要があるので、このように
          // addListenerのコールバック内でsetState()メソッドの呼び出しは必要。
        });
      });
    controller.forward(); //←アニメーションをスタートさせるメソッド
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 10),
        height: animation.value, //←(1)
        width: animation.value,  //←(1)
        child: FlutterLogo(),
      ),
    );
  }
  @override
  void dispose() {
    controller.dispose(); //←(2)
    super.dispose();
  }
}

addListener()関数はsetState()を呼び出すため、アニメーションが新しい番号を生成するたびに、現在のフレームがダーティとしてマークされ、build()が再度呼び出されます。

build()メソッド内では、Containerウィジェットのwidthとheightが変化します。なぜならContainerのwidthパラメータとheightパラメータにanimation.valueが設定されているからです(1)。

メモリリークを防ぐために、Stateオブジェクトが破棄されたら、コントローラーを破棄します(2)。

アニメーションオブジェクトは容量が大きいので、disposeを忘れるとパフォーマンスに悪影響がある。ということで

disposeを忘れないようにしましょう。

メモリリーク:プログラミングにおけるバグの一種。プログラムが確保したメモリの一部、または全部を解放するのを忘れ、確保したままになってしまうことを言う。

https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%AF

これで初めてのアニメーションを作ることができました!


Dart language tricks:Dartのカスケードノーテーションについて。

二つのドットを用いて

..addListener()

のように記述します。このシンタックスはanimate()メソッドの返り値に対してaddListener()メソッドを呼び出す、ということを意味します。

以下のサンプルを考えます。

animation = Tween<double>(begin: 0, end: 300).animate(controller)
  ..addListener(() {
    // ···
  });

このサンプルは以下のようにコードと同じ意味です。

animation = Tween<double>(begin: 0, end: 300).animate(controller);
animation.addListener(() {
    // ···
  });

カスケードノーテーションについてはこちらに詳しい説明があります(Dart Language Tour)




Simplifying with Animated­Widget

ポイントは?

アニメーションを使用するウィジェットの作り方として(addListener()とsetState()を使用した方法の代わりに)、ヘルパークラスであるAnimatedWidgetクラスを使用する方法を見ていく。

再利用できるアニメーションを展開するウィジェットをAnimatedWidgetを使って作る(定義する)。

ウィジェットからトランジションを分離するには、「Refactoring with AnimatedBuilder」セクションに示すように、AnimatedBuilderを使用します。

Examples of AnimatedWidgets in the Flutter API: AnimatedBuilderAnimatedModalBarrierDecoratedBoxTransitionFadeTransitionPositionedTransitionRelativePositionedTransitionRotationTransitionScaleTransitionSizeTransitionSlideTransition.


ベースクラスであるAnimatedWidgetを使うことで、ウィジェットコード(AnimatedLogo)をアニメーション用コード(_LogoAppState)から切り離すことができます。

AnimatedWidget(のサブクラス、つまりAnimatedLogo)はanimationを保持するStateオブジェクトを保持する必要がありません。

animationは_LogoAppStateが状態として保持して、それを(AnimatedLogoのインスタンス生成時に)AnimatedLogoに渡す形。

class AnimatedLogo extends AnimatedWidget {
  AnimatedLogo({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;
    return Center(
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 10),
        height: animation.value,
        width: animation.value,
        child: FlutterLogo(),
      ),
    );
  }
}

AnimatedLogoは、渡されたanimationの現在の値を使って自分自身を描画します。

LogoAppはAnimationControllerとTweenを管理しています。そしてAnimationオブジェクトをAnimatedLogoに渡します。

↓(AnimatedWidget(のサブクラス)を使ったパターン)

animate1ではaddListener()+setState()のコードを自分で書いたが、AnimatedWidget内でその部分は実装してくれているので、AnimatedWidgetのサブクラスを定義すれば、addListener()+setState()のコードを自分で書かなくて済む。

その「AnimatedWidgetサブクラス」にanimationを渡して、buildメソッドにアニメーション化させたいものを書けば、それだけでアニメーション化できる、というつくり。

animate1よりは少し楽、ということか。

↑のサンプルコードにもあるし、↓の動画でも言及されている通り、「AnimatedWidgetサブクラス」のコンストラクタでスーパークラス(AnimatedWIdget)にanimationを渡す必要がある、と。

App source: animate2




Monitoring the progress of the animation

ポイントは?

●animationの状態の変化を通知するためにaddStatusListener()メソッドを使います。状態の変化とはアニメーションの開始、停止、または逆方向の再生などです。

●アニメーションが完了した時、あるいは開始の状態に戻った時に、方向を逆転することで、アニメーションを無限ループさせます。


完了(finishing)・順方向の進行(moving forward)・あるいは逆方向の進行(reversing)など、アニメーションの状態の変化を知ることは有用です(役に立つ)。

addStatusListener()メソッドを使うと、アニメーションの状態の変化を知る(察知する)ことができます。

以下のコードは一つ前のサンプルを書き直して、アニメーションの状態の変化をリッスンして、print文でコンソールに出力しています。

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)
      ..addStatusListener((state) => print('$state'));
    controller.forward();
  }
  // ...
}

このコードを実行すると、

AnimationStatus.forward
AnimationStatus.completed

このように出力されます。


次にaddStatusListener()メソッドを使って、アニメーションの開始地点と終了地点で進行方向を反転させます。これで”breathing”エフェクトを作り出せます。

animation = Tween<double>(begin: 0, end: 300).animate(controller)
..addStatusListener((status){
  print("$status");
  if(status==AnimationStatus.completed) {
    controller.reverse();
  }else if (status == AnimationStatus.dismissed) {
    controller.forward();
  }
});

↓これもanimate2と同じくAnimatedWidget(のサブクラス)を使ったパターン。_LogoAppState内でaddStatusListenerを使って無限ループを実装している。

App source: animate3




Refactoring with AnimatedBuilder

ポイントは?

●AnimatedBuilderはトランジションをレンダーする方法を理解しています。

●AnimatedBuilder自体はウィジェットをレンダーする方法を知りません。AnimatedBuilder自体はAnimationオブジェクトを管理しません。

「ウィジェットをレンダーする方法」はbuilderプロパティとして受け取るので「自身では知らない」という意味。animationオブジェクトは(AnimatedWidgetと同様)親から受け取る、管理するのは親ウィジェット。

●アニメーションを別のウィジェットのビルドメソッドの一部として記述するために使用します。再利用可能なアニメーションを使用してウィジェットを定義するだけの場合は、「Simplifying with AnimatedWidget」セクションに示すように、 AnimatedWidgetを使用します。

  • Examples of AnimatedBuilders in the Flutter API: BottomSheetExpansionTilePopupMenuProgressIndicatorRefreshIndicatorScaffoldSnackBarTabBarTextField.

サンプルanimate3の一つの問題点は、アニメーションを変えたい場合、ロゴをレンダーするウィジェットを変えないといけない、という点です。

具体的にいうと、AnimatedLogoクラス(ロゴをレンダーするウィジェット)を修正しないと、アニメーションを変えられない、という点。

それぞれの役割に対応したクラスに分割した方がより良い構成になります。

  • ロゴをレンダーする役割(LogoWedget)
  • Animationオブジェクトを定義する役割(_LogoAppStateクラス)
  • トランジションをレンダーする役割(GrowTransitionクラス)

この分離はAnimatedBuilderクラスを使うことで成し遂げることができます。

AnimatedBuilderはレンダーツリーの中で分離されたクラスです。

AnimatedWidgetやAnimatedBuilderは自動的にAnimationオブジェクトからの通知をリッスンして、必要に応じてウィジェットツリーをdirtyな状態(リビルドが必要な状態)とします。

アニメーションの値の変更に応じてリビルドが行われる、ということ。

ですからあなたが自分でaddListener()を呼び出すコードを書く必要はありません。

サンプルanimate4のウィジェットツリーは以下のようになります。

AnimatedBuilder widget tree

ウィジェットツリーの下から見ていきます。ロゴをレンダーするコード(LogoWidget)は難しくないですね。

class LogoWidget extends StatelessWidget {
  // Leave out the height and width so it fills the animating parent
  Widget build(BuildContext context) => Container(
        margin: EdgeInsets.symmetric(vertical: 10),
        child: FlutterLogo(),
      );
}

上記のウィジェットツリーの2,3,4番目のブロックは全てGrowTransitionウィジェットのbuild()メソッド内で生成されています。(下記コード参照)

GrowTransitionウィジェット自体はstatelessであり、トランジションアニメーションを定義するために必要なfinalな変数を保持しています。

GrowTransitionのbuildメソッドがAnimatedBuilderウィジェットを生成し返します。

AnimatedBuilderウィジェットはbuilderパラメータに(anonymous builder)コールバック(無名関数)が必要です。

(このサンプルanimate4では)AnimatedBuilderウィジェットのchildパラメータにLogoWidgetオブジェクトが渡ります。

アニメーショントランジションのレンダリング機能は(Anonymous builder)コールバックによって発生します。

そのAnonymous builderコールバックは、適切なサイズのコンテナーを作成して、LogoWidgetを強制的に縮小します。

以下のコードのトリッキーな点の1つは、childが2回指定されているように見えることです。

何が起こっているのかというと、GrowTransitionのchildフィールドにセットされたウィジェットがAnimatedBuilderに渡され、AnimatedBuilderがそれを匿名クロージャ(Anonymous builderコールバック)に渡し、匿名クロージャがそのオブジェクトをchildとして使用します。
最終的な結果として、AnimatedBuilderがレンダーツリーの2つのウィジェットの間に挿入されます。

class GrowTransition extends StatelessWidget {
  GrowTransition({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) => Center(
        child: AnimatedBuilder(
            animation: animation,
            builder: (context, child) => Container(
                  height: animation.value,
                  width: animation.value,
                  child: child,
                ),
            child: child),
      );
}

 


最終的に、アニメーションを初期化するコードは、animate2の例と非常によく似ています。

(_LogoAppStateの)initState()メソッド内でAnimationControllerとTweenを生成して、animate()メソッドで両者を結び付けます。

_LogoAppStateのbuild()メソッドは、GrowTransitionオブジェクトを返します。

そのGrowTransitionコンストラクタには、

childパラメータとしてLogoWidget()、

そしてanimationパラメータとしてトランジションを操作するanimationオブジェクトが渡されます。

この三つはまさに

  • ロゴをレンダーする役割(LogoWedget)
  • Animationオブジェクトを定義する役割(_LogoAppStateクラス)
  • トランジションをレンダーする役割(GrowTransitionクラス)

この三つの要素です。

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) => Scaffold(
        body: SafeArea(
          child: GrowTransition(
            child: LogoWidget(),
            animation: animation,
          ),
        ),
      );

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

↓AnimatedBuilderを用いたパターン。

App source: animate4

AnimatedBuilder使用例としてこのページもどうぞ




Simultaneous animations

ポイントは?

CurvesクラスではCurvedAnimationでよく使う多くのカーブがすでに用意されています。

このセクションでは、AnimatedWIdgetを使って継続的にin and out するアニメーションを実装したmonitoring the progress of the animation (animate3)を修正してサンプルを進めていきます。

不透明度(opacity)が透明(transparent)から不透明(opaque)にアニメートしているときに、インandアウトを(同時に)アニメートする場合を考えてみます。

透明→不透明(透明度(opacity)のアニメーション)

小→大(サイズのアニメーション)

が同時に発生する。


Note:この例は、同じアニメーションコントローラで複数のトゥイーンを使用する方法を示しています。各トゥイーンは、アニメーションで異なる効果を管理します。説明のみを目的としています。本番コードで不透明度とサイズをトゥイーンする場合は、代わりにFadeTransitionとSizeTransitionを使用する可能性があります。


各tweenは、アニメーションの側面を管理します。例えば:

controller =
    AnimationController(duration: const Duration(seconds: 2), vsync: this);
sizeAnimation = Tween<double>(begin: 0, end: 300).animate(controller);
opacityAnimation = Tween<double>(begin: 0.1, end: 1).animate(controller);

上記のようにして、sizeはsizeAnimation.valueで取得できますし、opacityはopacityAnimation.valueで取得できます。

しかしAnimatedWidgetは一つのAnimationオブジェクトしか受け取ることができません。

(sizeAnimationとopacityAnimationの両方を受け取ることができない)

ですから、サンプルではそれぞれのTweenオブジェクトを生成して、それをanimationと結び付けてそれぞれのアニメーションの値を計算しています。

(1)AnimatedLogoは、一つのanimationを受け取る。

(2)AnimatedLogo内でサイズ用と透明度用、それぞれのTweenを生成する。

(3)それぞれのTweenと受け取ったanimationを結び付けて、それぞれのanimationの値を(evaluateメソッドで)取得する。

App source: animate5


Next steps

このチュートリアルでは、Tweensを使用してFlutterでアニメーションを作成するための基礎を提供しましたが、他にも多くのクラスを探索する必要があります。

特殊なTweenクラス、マテリアルデザインに固有のアニメーション、ReverseAnimation、共有要素の遷移(Heroアニメーションとも呼ばれます)、物理(physics)シミュレーション、fling()メソッドを調べることができます。
利用可能な最新のドキュメントと例については、アニメーションのランディングページを参照してください。

 

参考

https://flutter.dev/docs/development/ui/animations/tutorial

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です