[読書会]ページ遷移アニメーションの実装

(このページは、docs.flutter.dev(https://docs.flutter.dev/cookbook/animation/page-route-animation)のリファレンスブログです)

 Materialデザイン等のデザイン言語は、ルート(画面)間の標準的な遷移動作を定義していますが、時にはカスタム遷移アニメーションを加えることでアプリをよりユニークにすることができます。
 PageRouteBuilder クラスを使うことでカスタマイズされた画面遷移アニメーションを実装することができます。
 PageRouteBuilder Animation オブジェクトを提供し、このオブジェクトを Tween Curve オブジェクトと組み合わせて使用することで、遷移アニメーションをカスタマイズすることが可能です。

1.PageRouteBuilder の設定

 PageRouteBuilder を使用して、Route を設定する方法について触れています。

(1)概要

 PageRouteBuilder を使用すると、カスタマイズした遷移アニメーションを持つ新しいルートを作成できます
PageRouteBuilder には2つのコールバック( pageBuilder transitionsBuilder があります。
pageBuilder ルートのコンテンツ(ページ)を構築し、transitionsBuilder ルートの遷移を構築します。

(2)注意点

 transitionsBuilder の child パラメータは、pageBuilder から返されるウィジェットです。
pageBuilder 関数は最初のRoute構築時にのみ呼出され、遷移中は child 状態は不変なので、フレームワークは余分な作業をしません。

2.Tween の作成

 Tween を作成して、ページを下から上へとアニメーションさせる方法について触れています。

(1)概要

 新しいページを下から上にアニメーションさせる為、Offset(0, 1) から Offset(0, 0)(Offset.zero で定義)までアニメーションを行います。この Offset は 2Dベクトルで、FractionalTranslation ウィジェットの位置を示します。dy 引数を1に設定することで、ページの全高分の垂直移動を表現できます。

(2)FractionalTranslation ウィジェットについて(補足)

 FractionalTranslation ウィジェットは、Flutterにおいて、子ウィジェットを親ウィジェットのサイズに基づいて位置を変更するためのウィジェットです。FractionalTranslationは、Offsetを使用して位置の変換を行い、子ウィジェットをその位置に移動させます。例えば、Offset(0.5, 0.5)を指定すると、子ウィジェットは親ウィジェットの幅と高さの半分だけ右下に移動します。

 このウィジェットを使うことで、ウィジェットを親コンテナの領域内で簡単に移動でき、複雑なアニメーションや位置調整を行う際に役立ちます。ただし、先程のサンプルコードには直接FractionalTranslationが実装されていないので、この概念はアニメーションの動きを説明するために使われているだけで、実際には他の方法で類似のアニメーションを構築しています。

(例えば、transitionsBuilder内でTweenを使用してOffsetを設定し、アニメーションを行う部分で間接的に位置の変換が行われていますが、FractionalTranslationウィジェット自体は使われていません。)

(3)transitionsBuilder の詳細

 transitionsBuilder のコールバックは、アニメーションを構築するための animation パラメータを受け取ります。
 この animation は、0から1の間の値を生成する Animation です。この Animation を Tween を使って Animation に変換し、画面の移動を表現します。

Tweenを作成してアニメーションを設定する部分(例):
transitionsBuilder: (context, animation, secondaryAnimation, child) {
  const begin = Offset(0.0, 1.0); // 開始位置(ページの全高下)
  const end = Offset.zero;        // 終了位置(0,0:元の位置)
  final tween = Tween(begin: begin, end: end);
  final offsetAnimation = animation.drive(tween); // Animation<Offset>を生成
  return child;
},

3.AnimatedWidget の利用

 AnimatedWidget クラスを継承した一連のウィジェットでは、アニメーションの値が変化するたびに再構築されます。
(例えば、SlideTransition ウィジェットは、Animation を受け取り、アニメーションの値が変化するたびに子ウィジェットを移動させます。このとき、内部的に FractionalTranslation ウィジェットを使用して位置の変換が行われます。)

 この AnimatedWidget を使用し、アニメーションの進行に連れてウィジェットを自動的に再構築するコードを簡潔に記述できます。
(例えば、SlideTransition を使用して新しいルートをアニメーション付きで表示するコードは以下のように記述されます。)

SlideTransition を使用して新しいルートをアニメーション付きで表示する(例):
transitionsBuilder: (context, animation, secondaryAnimation, child) {
  const begin = Offset(0.0, 1.0); // 下から始まる位置
  const end = Offset.zero; // 終了位置(元の位置)
  final tween = Tween(begin: begin, end: end); // Tween で開始と終了を定義
  final offsetAnimation = animation.drive(tween); // アニメーションで Tween を駆動

  return SlideTransition(
    position: offsetAnimation, // SlideTransition の位置をアニメーションに基づいて設定
    child: child, // アニメーションさせるウィジェット
  );
},

 このように、SlideTransition は AnimatedWidget を拡張しているため、アニメーションの進行に応じて位置を変更し、アニメーションを簡単に管理できます。これにより、複雑なアニメーションもシンプルなコードで実現可能です。

4.CurveTween の利用

 Flutter では、アニメーションの進行速度を調整するために、さまざまな緩急のある曲線(イージング曲線)が提供されています。これにより、アニメーションが時間の経過に伴ってどのように変化するかを制御できます。Curves クラスは、よく使われるプリセットの曲線を提供しており、その中には、アニメーションを早く開始してゆっくり終了させる Curves.easeOut などがあります。

 Curve を使うには、CurveTween を新しく作成し、その curve プロパティに希望する曲線を渡します。

CurveTween を新しく作成し、その curve プロパティに希望する曲線を渡す(例):
var curve = Curves.ease;
var curveTween = CurveTween(curve: curve);

 この新しい CurveTween は、0 から 1 までの値を生成します。このステップでは、前のステップで作成した Tween と組み合わせて使用されます。これにより、アニメーションが滑らかで自然な動きを実現できます。

 このアプローチを使うことで、アニメーションの進行をより柔軟に制御でき、アプリの視覚的な魅力を向上させることができます。

5.2つの Tween の組合せ

 Flutterでアニメーションを滑らかに制御するために、2つのTweenを組み合わせることができます。
これには、chain() メソッドを使用します。
まず、アニメーションの開始と終了位置を設定し、使用する曲線を指定します。

アニメーションの開始と終了位置を設定し、使用する曲線を指定する(例):
const begin = Offset(0.0, 1.0); // アニメーションの開始位置(ページの下から)
const end = Offset.zero;        // アニメーションの終了位置(ページの元の位置)
const curve = Curves.ease;      // アニメーションの進行をスムーズにする曲線

var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

 chain() を使用して Tween と CurveTween を結合することで、アニメーションの進行に曲線の効果を追加できます。

 この Tween animation.drive() に渡すことで、新しい Animation が作成され、SlideTransition ウィジェットに渡されます。これにより、アニメーションの位置の変更がスムーズになります

Dart
return SlideTransition(
  position: animation.drive(tween), // チェーンしたTweenを使用してアニメーションを駆動
  child: child,                     // アニメーション対象の子ウィジェット
);

 この方法を使用すると、ページ遷移時に新しいページが下から上へスライドしながら表示され、カスタムアニメーションを簡単に実装できます。SlideTransition は position プロパティを通じて Animation を使用し、アニメーションが進むにつれてウィジェットの位置を変化させます。これにより、自然で視覚的に魅力的なページ遷移が実現します。

6.サンプルコード

画面遷移アニメーション(例):
import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Page1(),
    ),
  );
}

class Page1 extends StatelessWidget {
  const Page1({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).push(_createRoute());
          },
          child: const Text('Go!'),
        ),
      ),
    );
  }
}

Route _createRoute() {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => const Page2(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      const begin = Offset(0.0, 1.0);
      const end = Offset.zero;
      const curve = Curves.ease;

      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  );
}

class Page2 extends StatelessWidget {
  const Page2({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: const Center(
        child: Text('Page 2'),
      ),
    );
  }
}

コメントを残す