FlutterのWidgetをコードを動かしながら学ぶ: AnimatedContainer編

手軽にアニメーションが利用できるImplicitlyAnimatedWidgetの派生クラス、AnimatedContainerについて、Flutter Widget of the Weekで扱われていたので使ってみてソースコードやドキュメントを読んだ内容をまとめたものになります。
2020.04.21

Flutter は動画コンテンツも充実していて週毎にウィジェットを紹介している Flutter Widget of the Week では短い動画でウィジェットの概要を掴むことができる動画を視聴できます。

Flutter Widget of the Week を見るだけでも勉強になるのですが、記憶力が良くないので、視聴によるインプットだけでなく手元でコードを動かしてそれをブログ記事にする所までやって後から思い出しやすいようにしてみるシリーズです。

これまでの記事は以下のリンクから参照できます。

今回取り上げるのは AnimatedContainer です。

AnimatedContainer

指定したDurationに対して徐々に値を変更してアニメーションしてくれるContainerです。Containerの各プロパティを変更するだけでアニメーションを作れるので便利そうです。

ドキュメントは以下です。

ドキュメントには以下のように記載されています。

Animated version of Container that gradually changes its values over a period of time.

AnimatedContainer とは

Flutter Widget of the Week で AnimatedContainer をとりあげた動画は以下です。AnimatedContainer のドキュメントを見ると、複数の行または列でその子のウィジェットを表示するためのウィジェットと記載されています。

AnimatedContainer を使ってみる

提供されているプロパティの変化をアニメーションしてくれるこのウィジェットを使えば色やサイズ、配置など、様々なプロパティを指定するだけでアニメーションを実装できます。最初はContainerのwidthとheightをFloatingActionButtonのタップの度に変更するよう実装してみます。

実行結果は以下のようになります。

今回はStatefulWidgetとsetStateを使って変数の値を書き換えてアニメーションを実現しています。

ImplicityAnimatedWidget

AnimatedControllerのソースコードを読むとImplicitlyAnimatedWidgetというウィジェットを継承していることがわかります。

class AnimatedContainer extends ImplicitlyAnimatedWidget {

ImplicitlyAnimatedWidgetはプロパティの変化をアニメーションさせるウィジェットを構築するための抽象クラスとドキュメントには記載があります。ImplicitlyAniamtedWidgetを理解することがこれを継承するAnimatedContainerを含めたウィジェットの挙動を理解するのに繋がりそうです。

An abstract class for building widgets that animate changes to their properties.

ImplicitlyAnimatedWidgetは初期状態ではアニメーションが行われないことが特徴です。プロパティの値の変化が会った時に指定されたDurationに応じてアニメーションが行われます。

Duration

ImplicitlyAnimatedWidgetのdurationプロパティはプロパティのパラメータをアニメーションさせる期間を指定するクラスです。ソースコードを読んでコンストラクタとdurationプロパティの定義を確認してみるとfinalキーワードが付与されていてコンストラクタで値が渡されることを保証していることがわかります。curveプロパティも同じ扱いですが一旦はdurationプロパティだけに目を向けます。

const ImplicitlyAnimatedWidget({
    Key key,
    this.curve = Curves.linear,
    @required this.duration,
    this.onEnd,
  }) : assert(curve != null),
       assert(duration != null),
       super(key: key);

  /// The curve to apply when animating the parameters of this container.
  final Curve curve;

  /// The duration over which to animate the parameters of this container.
  final Duration duration;

Durationは、ある時点から別の時点への差(期間)を表すクラスです。

ドキュメントには宣言方法が記載されてあります。

Duration fastestMarathon = new Duration(hours:2, minutes:3, seconds:2);

Curve

次は、先程一旦脇に置いておいたAnimatedContainerが継承している抽象クラスImplicitlyAnimatedWidgetのコンストラクタでコンストラクタ引数に渡すことが必須のcurveプロパティの話をします。

durationプロパティの時と同じ様にプロパティの型について見ていく前にこのプロパティがImplicitylyAnimatedWidgetでどのように扱われているか見てみます。

The curve to apply when animating the parameters of this container.

定義を見るとわかったようなわからないような、となるのでCurveクラスのドキュメントを見てみます。

Curveクラスはある単位間隔から単位間隔へのマッピングを表すイージングを定義します。

イージングを使うことで直線的な値の変化でなく加速(ease in)または減速(ease out)する自然なアニメーションが表現できます。イージングという言葉についてはGoogleが公開しているWeb Fundamentalsというドキュメントのイージングについて扱ったページのドキュメントなどがわかりやすいです。

FlutterではColorという色を表現するクラスの値を指定する時にColorのコンストラクタでColorを指定することもありますが、Colorsというクラスに定義されたstaticな定数を渡すことで指定することも多いです。同じ様にCurveクラスの値を指定するためにCurvesクラスにstatic定数が定義されています。

ソースコードだけを見てCurvesに定義された定数を選択するのも良いですがFlutterではCurvesに定義された各定数の詳細な動きがビジュアライズされたページを提供しています。

AnimatedContainer のその他主要なプロパティについて

Containerで提供されているプロパティはAnimatedContainerでも提供されています。例えばContainerの子ウィジェットの配置を設定できるalignmentをアニメーションさせようとすると以下のようなコードになります。ここからはそのまま動くようにcodepenの埋め込みを使用します。codepenの動作がSafariだと安定しない所がありました(iOSのSafariで確認)。Chromeでの閲覧・利用推奨です。

See the Pen alignmentのアニメーション by Nobuyuki Tanabe (@nabeatsu) on CodePen.

子ウィジェットとのPaddingを調整できるpaddingプロパティの切り替わりをアニメーションさせるには以下のようなコードになります。

See the Pen animated_container_sample2 by Nobuyuki Tanabe (@nabeatsu) on CodePen.

transformプロパティの型はMatrix4クラスです。ドキュメントを読むと4D Matrixという言葉が出てきます。

4D Matrix. Values are stored in column major order.

行列計算でどのようにtransform(変形)するか自由に設定できますが知識がなくてもfactoryコンストラクタを利用することでいくつかのパターンから選択できます。

要素を移動させるtransformが設定できるtranslationValuesを使ってみます。ソースコードは以下です。

/// Translation matrix.
  factory Matrix4.translationValues(double x, double y, double z) =>
      new Matrix4.zero()
        ..setIdentity()
        ..setTranslationRaw(x, y, z);

codepenは以下になります。

See the Pen animated_container_sample3 by Nobuyuki Tanabe (@nabeatsu) on CodePen.

要素を回転させるtransformを設定できるrotationX、rotationY、rotationZがあります。

factoryコンストラクタは以下です。

/// Rotation of [radians_] around Z.
  factory Matrix4.rotationZ(double radians) => new Matrix4.zero()
    .._m4storage[15] = 1.0
    ..setRotationZ(radians);

See the Pen animated_container_sample4 by Nobuyuki Tanabe (@nabeatsu) on CodePen.

AnimatedContainerなどのImplicitなアニメーションウィジェットの位置付け

利用するだけで簡単にアニメーションが適用できるImplicitlyAnimatedWidgetの派生クラスなどのImplicit widgetsの位置付けについてはFlutterのお手軽にアニメーションを扱えるAnimated系Widgetをすべて紹介 - Flutter 🇯🇵 - Mediumという記事で紹介されていたセッション動画のスライドの1ページがわかりやすいと思います。

動画はこちら

動画や記事にあるようにAnimationControllerを使う前にAnimatedContainerなどの手軽に使えるウィジェットで解決できないか検討してみるようにしたいと思いました。実は過去に書いた記事で紹介したUdemyのコースではAnimationControllerの使い方について詳細に紹介されていました。そこでAnimatedContainerならもっと簡単に終わるプロパティの変化をアニメーションさせる実装を行ったのを覚えています。同じことをいくつもの方法で実現できることを知ることは引き出しが増えていざという時に役に立ったりするのでコードを書くのと並行してインプットを続けていきたいと思います。

まとめ

記事の主題とはそれますがcodepenの埋め込みがFlutter対応したとのことだったので使ってみました。プログラミング初心者の頃を思い出すとそのままで動くコードがありがたかったなと思い出したのも全コードを載せるcodepenを使ってみた理由です。 Flutterの学習・開発は始めたばかりで説明やその裏にある認識に誤りがあるかもしれません。なにかお気づきの際はコメントなどでお知らせください。