[読書会]暗黙的アニメーション化されたAnimatedFooウィジェットによる簡単なアニメーション

(参考ブログ)
Flutter animation basics with implicit animations
(暗黙的なアニメーションを使ったFlutterアニメーションの基礎)(by Alethea K.Flowers)」

 上記のブログでは(タイトルにもある様に)暗黙的なアニメーションを使ったFlutterアニメーションについて基礎的なノウハウが記載されています。以下では、ざっくり要点だけを列記してあります。

 こちらの記事では、Flutterでアニメーションを実装するための基礎について解説されています。
特に、初心者でも簡単に利用できる「暗黙的(インプリシット)アニメーション」に焦点を当てています。

 この記事は、Flutterアプリにアニメーションを追加する最も簡単な方法に焦点を当て、アニメーションの専門的な知識がなくても実装できることを示しています。また、アニメーションの基礎となるウィジェットや用語を紹介し、シリーズの他の記事で役立つ背景知識も提供しています。加えて、アニメーションの用語やFlutterで使われる基本的なアニメーションのウィジェットについても触れ、初心者がすぐに使えるような内容になっています。

 動画形式でも同様の内容が提供されているため、好みに応じて視聴しながら学習することも可能です。

目次 index【閲覧時間3分】 click !!>>

1.暗黙的にアニメーション化されたウィジェット

(参照動画)Implicit Animations

 Flutterには、通常のウィジェットにアニメーション機能を加えた「インプリシットアニメーション付きウィジェット」と呼ばれるウィジェットのシリーズがあります。(例えば、Container ウィジェットアニメーション機能を追加した AnimatedContainer や、Positioned ウィジェットアニメーションバージョンである AnimatedPositioned 等が含まれます。)

 これらのウィジェットはプロパティの変更自動的にアニメーション化します。(例えば、StatefulWidget 内で setState を使ってプロパティの値を変更すると、ウィジェットが自動的に以前の値から新しい値までアニメーションで遷移するように設定されています。)この仕組みにより、開発者は特別なアニメーション制御コードを追加することなく、簡単にアニメーション効果をアプリに導入することが可能です。

 これらのウィジェットは、アプリにアニメーションを追加する際にまず使うべきものであり、複雑なコードを追加せずにアニメーションを実現できるため、非常に扱い易いのが特徴です。

2.AnimatedContainer ウィジェット

2-1.Container 再描画による画像の幅の即時更新(アニメーション効果なし)

 以下のコードでは、Container ウィジェットを使ってボタン操作でサイズを変更するシンプルな例を示しています。
(このコードの中では、ボタンを押す度に _bigger というフラグが true から false、またはその逆に切り替わります。
このフラグの状態に応じて Container の width プロパティが 100 または 500 に変化しますが、この変更は即座に行われアニメーションは適用されません。)

Container 再描画による画像の幅の即時更新(アニメーション効果なし)(例):
@override
Widget build(BuildContext context) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      //Container ウィジェットは、画像とともに表示されますが、
      //_bigger の値に応じて幅が変わります。
      Container(
        width: _bigger ? 100 : 500, //`_bigger` の値に応じて幅が変わる
        child: Image.asset('assets/star.png'), //表示する画像
      ),
      //ボタン (RaisedButton) の onPressed コールバックで 
      //setState() が呼び出され、_bigger の値が切り替わります。
      //この操作により、Container が再描画され、
      //幅が新しい値に即座に更新されますが、アニメーション効果はありません。
      RaisedButton(
        onPressed: () => setState(() {
          _bigger = !_bigger; //ボタンが押されるたびに `_bigger` の値を反転
        }),
        child: Icon(Icons.star), //ボタンのアイコン
      ),
    ],
  );
}

2-2.AnimatedContainer に変更して duration パラメータを追加(アニメーション追加)

 次に、Container を AnimatedContainer に置き換えduration パラメータを追加します。
これにより、ボタンを押して幅を変更すると、1秒間かけてスムーズにサイズが変わるアニメーションが追加されます。
AnimatedContainer は、古い値と新しい値の間を滑らかに遷移する「補間(interpolation)」を自動的に行ってくれます。
これで、ボタンが押されると、コンテナーは以前の幅の値から新しい値まで徐々にアニメーション化されます。

AnimatedContainer に変更して duration パラメータを追加(アニメーション追加)(例):
AnimatedContainer(
  width: _bigger ? 100 : 500,
  child: Image.asset('assets/star.png'),
  duration: Duration(seconds: 1),
),

2-3.AnimatedContainer の decoration プロパティに BoxDecoration を設定(補完しながらアニメーション実行)

 の説明では、AnimatedContainer がどのようにプロパティの変化をスムーズにアニメーションさせるかを説明しています。このアニメーションの過程で、古い値から新しい値へと徐々に変化させることを「補間(interpolation)」と呼びます。AnimatedContainer は、プロパティが変化するたびに自動的にその間の値を補間してアニメーションを作成します。

 この補間は、AnimatedContainer のすべてのプロパティに適用されます。たとえば、decoration プロパティ内のグラデーション(gradient)を変更すると、AnimatedContainer が古いグラデーションから新しいグラデーションにスムーズに変化するように補間を行います。

 以下のコード例では、AnimatedContainer の decoration プロパティに BoxDecoration が設定されています。その中の RadialGradient の colors と stops の値が _bigger の値に応じて変化します。このとき、AnimatedContainer が古い stops の値から新しい値へと補間しながらアニメーションを実行します。

(width は不要です。AnimatedContainer のアニメーション効果を得るために必要なのは、アニメーションさせたいプロパティの変化と duration です。この場合、アニメーションの対象は BoxDecoration の gradient プロパティであり、width プロパティの設定は必須ではありません。) 

AnimatedContainer の decoration プロパティに BoxDecoration を設定(補完しながらアニメーション実行)(例):
AnimatedContainer(
  duration: Duration(seconds: 1), // アニメーションの持続時間を指定
  decoration: BoxDecoration(
    gradient: RadialGradient(
      colors: [Colors.purple, Colors.transparent],
      stops: [ _bigger ? 0.2 : 0.5, 1.0]
    ),
  ),
),

3.接続時間と曲線でアニメーションを制御する

3-1.curve プロパティに Curves クラスを設定(加速と減速の効果を与える)

 こちらの説明では、アニメーションの補間方法カーブ (Curve) を使って制御する方法について述べられています。

 デフォルトでは、AnimatedContainer の補間(値の変化)は線形(リニア)で行われますが、これを curve プロパティを使用して変えることができます。Curves クラスにはさまざまなカーブが用意されており、例えば Curves.easeInOutQuint は、より強調された五次カーブ(quintic curve)で、開始と終了が滑らかで中間で速くなるという特性を持っています。これにより、アニメーションに自然な加速と減速の効果を加えることができます。

curve プロパティに Curves クラスを設定(加速と減速の効果を与える)(例):
AnimatedContainer(
  width: _bigger ? 100 : 500,  // アニメーションの対象プロパティ
  child: Image.asset('assets/star.png'),
  duration: Duration(seconds: 1),  // アニメーションの持続時間
  curve: Curves.easeInOutQuint,  // 補間のカーブを指定
),

3-2.様々な Curves クラス(遅い開始で徐々に加速/バウンド/不連続な動き等)

 こちらの説明は、Flutterでアニメーションに個性を与えるために「カーブ(Curves)」を使用する方法について説明しています。Flutterにはさまざまなビルトインのカーブが用意されており、これによりアニメーションの動きを簡単に調整できます。また、自分でカスタムカーブを定義することも可能です。

(1)既存カーブの種類(Curves クラスの”静的フィールド”)

 ビルトインのカーブには、Curves.easeIn Curves.bounceOut などのさまざまな種類があり、各々異なる動きを表現します。
(例えば、Curves.easeIn は開始が遅く徐々に加速するカーブで、Curves.bounceOut はバウンドするような動きを持っています。また、不連続な動き(鋸歯状の動き)を表現する SawTooth カーブもあります。)
(補足)Curves. の後ろにある部分は、Curves クラスの静的フィールドといいます。

(2)カスタムカーブの定義

 カスタムカーブも独自に定義可能で、以下はサイン関数を使った SineCurve というカーブの例です。
このカーブは、アニメーションにバウンドするような効果を与えます。

カスタムカーブの定義(例):
class SineCurve extends Curve {
  final double count; //サイン波の周期数を表す

  SineCurve({this.count = 1});

  @override
  double transformInternal(double t) {
    return sin(count * 2 * pi * t) * 0.5 + 0.5; //サイン関数(カーブを定義)
  }
}
上記カスタムカーブの使用方法(例):
AnimatedContainer(
  width: _bigger ? 100 : 500,
  child: Image.asset('assets/star.png'),
  duration: Duration(seconds: 1),
  curve: SineCurve(count: 3), // カスタムカーブを指定
),

4.ここまでの要点

 要約すると、Flutterには多くの暗黙的(Implicitly)アニメーションウィジェットが用意されており、これらは一般的なウィジェットのアニメーションバージョンです。これらのウィジェットのアニメーションを制御するために、duration(期間)curve(カーブ)プロパティを使用できます

 特にAnimatedContainerは、見た目を変えるための様々なプロパティを持ち全てが自動的に補間(interpolation)されるため、非常に強力なウィジェットです。(他の暗黙的アニメーションウィジェットも、コードの複雑さを増やさずに簡単にアニメーションを追加するための便利なオプションです。)

 また、これらのウィジェットを必ずしもStatefulWidgetに入れてsetStateで更新する必要はありません
(例えば、StreamBuilder FutureBuilder を使ってアニメーションをトリガーすることも可能です。この方法により、非同期データに基づいてウィジェットの更新を行い、その際にアニメーションを活用できます。)

5.補足(アニメーション作成時の選択肢)

 このサイトには、アニメーション作成時の選択肢についてのフローチャートがありますが、これをごく簡単なメモ程度に以下に転記しておきます。

(1)簡単なアニメーション

 単なるUI要素(既存図形、既存画像)を使った簡単な動作(移動、拡大、色の変更)によるアニメーション

  → ① テキストベース

    → AnimatedDefaultTextStyle を利用する

  → ② 方向性のあるアニメーション

    → 暗黙的アニメーション化された AnimatedFoo ウイジェットがあれば利用する ⇒ 以下の「6.(2)」(このブログ)

    → (既存組込が無ければ)TweenAnimationBuilder で独自にカスタマイズする ⇒ 以下の「6.(3)」

  → ③ 逆戻りや不連続なアニメーション

    → 既存の FooTransition ウィジェットがあれば利用する
      (例:SlideTransition、FadeTransition 等) ⇒ 以下の「6.(4)」

    → 既存組込が無い ⇒ 以下の「6.(5)」

      → 独立したウィジェットにしたい

        → AnimatedWidget をサブクラス化して独自のアニメーションを定義する
          (状態は持たないが、管理がしやすくなる)

      → 別のビルドメソッドにネストしたい

        → AnimatedBuilder を利用する
          (対象の一部だけを更新する形でアニメーションを行う)

(2)複雑なアニメーション

 ① 複雑なグラフィックスを含んだアニメーション

  → アニメーションフレームワーク(Flare、Lottie、等)を利用する

 ② その他(上記A.B.でパフォーマンスが悪い場合)

  → CustomPainter を利用する

6.アニメーションの掘り下げ

 暗黙的にアニメーションするウィジェットはアニメーションを追加する最初の選択肢ですが、Flutterのアニメーションシステムが提供するものはこれだけではありません。 このシリーズの続きでは、Flutterのアニメーションシステムの下層を探索し、アニメーションシステムを直接使って高度なアニメーションを構築する方法を紹介します。

(1)自分に最適な Flutter アニメーションウィジェットを選択するには

(2)(この記事)暗黙的なアニメーション(pre-packaged implicit animation)の基礎

 (参照動画)Implicit Animations

(3)暗黙的なアニメーションのカスタマイズ(TweenAnimationBuilder の利用)

(参照動画)TweenAnimationBuilder

(4)(ビルドインウィジェットがある場合)明示的な方向性のあるアニメーション

(参照動画)Built-in Explicit Animations

(5)(ビルドインウィジェットが無い場合)AnimatedBuilder または AnimatedWidget のサブクラスを利用する

(参照動画)AnimatedBuilder / AnimatedWidget

(6)アニメーションの詳細

(参考動画)Animation Deep Dive

7.参考資料

(1)Implicit Animations Codelab

 このコードラボでは、暗黙的アニメーションの使用方法をステップバイステップで解説し、インタラクティブな例も含まれています。AnimatedContainer他の暗黙的アニメーションウィジェットを使い、コードの複雑さを抑えながら簡単にアニメーションを追加する方法が学べます。

(2)Animations Tutorial

 このチュートリアルでは、Flutterのアニメーションパッケージに含まれる基本的なクラス(AnimationController、Animatable、Curves、リスナー、ビルダー)について説明しています。Tweenアニメーションを使い、異なるアニメーションAPIの使用方法を順に紹介し、カスタムの明示的(Explicit)アニメーションの作成方法も学べます。

(3)Zero to One with Flutter (パート1とパート2)

 Mediumの記事で、Tweenアニメーションを使ってアニメーションチャートを作成する手順が紹介されています。基礎から応用までアニメーションの進化を段階的に学べる内容です。

(4)Write Your First Flutter App on the Web

 このコードラボでは、ユーザが入力フォームを埋めていく際に進捗状況をアニメーションで表示する方法を解説しています。Webアプリでアニメーションを実装する際に役立つ内容となっています。

8.アニメーションの種類

 Flutterのアニメーションには大きく分けて「Tweenアニメーション」「物理ベースのアニメーション」2種類があり、それぞれ異なる特性があります。

8-1.2種類(Tweenアニメーション/物理ベースのアニメーション)

(1)Tween Animation

 Tweenアニメーション(Tweenは「in-betweening」の略)では、開始点と終了点、タイムライン、そして遷移のタイミングや速度を定義するカーブを指定します。この情報をもとに、フレームワークが開始点から終了点までの中間状態を計算し、アニメーションを進行させます。
(例えば、サイズが小さい状態から大きい状態へのアニメーションや、色が変わるアニメーションなど、2つの状態間をスムーズに遷移させたい場合に利用されます。Flutterの基本的なアニメーションリソース(Animations tutorialなど)でも、例としてTweenアニメーションが使用されています。)

(2)Physics-based Animation

 物理ベースのアニメーションは、実世界の動きを模倣するために使用されます。
(例えば、ボールを投げたときに、ボールがどこに、どのタイミングで着地するかは、速度や地面までの距離などの物理的な要素に依存します。)

 Flutterでは、スプリング(バネ)のような力学モデルを使って、自然な動きや減速・加速を表現できます。
(これにより、例えばバウンドや反発といった動きを実現することが可能です。)

8-2.参考リソース

 このように、Tweenアニメーションは基本的に開始点から終了点までのスムーズな遷移を実現し、物理ベースのアニメーションは現実世界の動きを表現するためのものです。それぞれの特性を活かして、より自然で豊かなユーザ体験を提供するアニメーションを構築できます。

9.プリセットアニメーション(Pre-canned Animations)

 Flutterには、よく使われるアニメーションパターン簡単に導入できる「プリセットアニメーション」が用意されています
特に Materialデザインのウィジェットを使っている場合は、pub.dev にある animations パッケージ を活用することができます。
このパッケージには、次の様な一般的なアニメーションパターンが含まれています

9-1.Container Transforms

 コンテナのサイズや位置、外観を変化させるアニメーション。
(例えば、リストアイテムの詳細ページへの遷移などで使われることが多いです。)

9-2.Shared Axis Transitions

 同じ方向(垂直・水平・ズーム)の動きを使って、2つの要素の切り替えをスムーズに表現するアニメーション
(例えば、設定画面から詳細設定画面への遷移に使われます。)

9-3.Fade Through Transitions

 前の画面がフェードアウトし、次の画面がフェードインするアニメーションで、切り替えの際に一瞬で消えるようなエフェクトが特徴です。(例えば、ナビゲーション間の遷移がシンプルで、明確な区切りを表現したい場合に適しています。)

9-4.Fade Transitions

 ウィジェットを徐々に表示または非表示にす基本的なフェードアニメーション。
(例えば、ポップアップや通知など、画面上の要素をさりげなく表示したい場合に便利です。)

10.一般的なアニメーションのパターン

 ほとんどのUXデザイナーやモーションデザイナーは、UIをデザインする際に特定のアニメーションパターンを繰り返し使用することに気づきます。 このセクションでは、よく使われるアニメーションパターンをいくつかリストアップし、さらに詳しく学べる場所を紹介します。

10-1.アニメーションのサンプル

URL: https://flutter.github.io/samples/web/animations/

10-2.Flutterサンプルとアプリの厳選リスト

URL: https://flutter.github.io/samples/#

10-3.共有エレメントの移行

 共有エレメントトランジションとは、ユーザーがページ上の要素(一般的には画像)を選択し、選択した要素がアニメーションで新しい詳細ページへ移動するパターンのことです。Flutterでは、Heroウィジェットを使用することで、ルート(ページ)間での共有エレメントトランジションを簡単に実装できます。

(1)ヒーロー・アニメーション 2つのスタイルのヒーロー・アニメーションの作り方

 Heroウィジェットを使ったアニメーションには、次の2つのスタイルがあります。

① 位置とサイズの変更を伴ってページ間を飛ぶヒーロー

 要素ページ間で移動しながら位置やサイズが変化します。
(例えば、リストの画像をタップして、詳細ページに遷移する際に画像が大きく表示されるような動きを表現できます。
または、主人公は位置や大きさを変えながら、あるページから別のページへ飛んでいく、等)

② 形状が円から四角に変わりながらページ間を飛ぶヒーロー

 移動中ヒーローの境界(形状)が変化します。
(例えば、円形の画像が四角いフレームに拡張されて表示されるような、より視覚的に変化のあるトランジションを実現できます。
または、主人公の境界線は、あるページから別のページに飛ぶにつれて、円から四角へと形を変える、等)

(2)HeroNavigatorPageRouteクラスのAPIドキュメントも参照してください。

 Flutterで共有エレメントトランジションを実装する際には、以下のクラスも併せて利用すると便利です。

  • Hero
     共有エレメントトランジションを実現するための主要なウィジェットです。
  • Navigator
     ルート間のナビゲーションを管理します。
    Heroウィジェットを使ったページ間の遷移を実行するために使います。
  • PageRoute
     ルートに対するページトランジションを制御します。
    異なるページ間スムーズなトランジション構成するために利用します。

11.ステガードアニメーション

11-1.概要

 ステガードアニメーションとは、1つのアニメーションをいくつかの小さな動きに分け各々にタイミングをずらして動きをつけるアニメーションの手法です。各小さなアニメーションは、連続して行われたり、部分的にまたは完全に重なり合って動くこともあります。Flutterでは、ステガードアニメーションを使うことで、より複雑でリズミカルなアニメーションを作成することができます。

11-2.実装例

 Flutterでは、AnimationController Tween を使用して、アニメーションの一部に ディレイ(遅延)を与えることができます。また、Interval クラスを使って、各小さなアニメーションの開始・終了時間を指定し、全体のアニメーションの中で異なるタイミングで再生されるように設定できます。

(例えば、画面上で複数のボックスが連続してアニメーションするような場面で、各ボックスが順次異なるタイミングでフェードインするように設定することで、滑らかな流れを感じさせることができます。)

 詳細なサンプルや実装方法については、Staggered Animations で確認できます。

 これにより、画面の見栄えがよりリッチになり、ユーザにとって視覚的に楽しい体験を提供できます。

12.その他のリソース

 Flutterアニメーションについてさらに学ぶためのリソースは以下のとおりです:

これらのリソースを活用することで、Flutterでのアニメーション開発の理解が深まり、より洗練されたUI体験を提供できるようになります。

コメントを残す