Chapter 15

  画面遷移

heyhey1028
heyhey1028
2023.02.17に更新

さて1つの画面について説明してきましたが、複数の画面がある場合にはその画面を行ったり来たりと遷移する必要があると思います。

戻るボタンを押して、1つ前の画面に戻ったり、ホーム画面に一気に戻ったりと複雑な画面遷移を Flutter ではどのように管理しているのでしょうか?

この章では Flutter の画面遷移の仕組みについて見ていきましょう

画面遷移の全体像

Flutter では Navigatorというクラスに対し、Routeクラスでラップしたページを渡す事でページの遷移を行います。

Navigatorクラスは全てのページを入れる容器のようなものとイメージしてください。ページを遷移する行為は、その容器にページをどんどん積み上げていく(スタックする)イメージです。

ユーザーが実際に見ているページは積まれているページの一番上のページになります。新しいページが積まれると、今度は一番上に積まれたそのページがユーザーが見るページになるというイメージです。

下記のように折り重なってくるページの一番上だけをユーザーは見ていますが、過去に表示したページは下にあって見えないだけで存在しています。

ただ、ひたすらページを積み上げていくだけだとその分だけメモリがドンドンと消費され、動作も重くなってしまい、いつかクラッシュしてしまいます 🤯

そこで Navigator には、ページを追加するだけでなく、積み上げたページを削除したり、置き換えたりと様々な操作ができるようになっています。

開発者はそれらの操作を使って、Navigator に積まれているページを最適に保ちつつ画面遷移を行っていきます。

実際のコード

イメージを掴んでいただいたところで実際のコードを見てみましょう。

以下が最も基本的な画面遷移のコードになります。「あるページから PageB に遷移する」を実現するコードです。

Navigator.of(context).push(
    MaterialPageRoute(
        builder: (context) => PageB()
    ),
);

Navigatorクラスのpushメソッドに、PageBクラスをラップしたMaterialPageRouteクラスを渡しています。

これによりNavigatorクラスにPageBがスタックされ、ユーザーにはPageBが表示されます。

MaterialPageRouteは前述のRouteクラスを継承したクラスでマテリアルデザインに沿ったページ遷移を行う為のRouteクラスです。

このMaterialPageRouteクラスのbuilderというパラメータはコールバック関数を受け取り、その返り値としてNavigatorクラスに渡したいページを指定します。

MaterialPageRoute(
    builder: (context) => PageB(),
),

最後にこの画面遷移処理をボタンのonTapに渡し、「ボタンを押す=>画面遷移する」機能を実装します。

    FloatinActionButton(
        onTap: () {
            Navigator.of(context).push(
                MaterialPageRoute(
                    builder: (context) => PageB(),
                ),
            );
        },
    )

上記ではFloatingActionButtononTapに渡し、FloatingActionButtonをタップすることで画面遷移の処理が走ります。

またof.(context)も非常に重要な意味を持ちますが、一旦はおまじないだと思ってください。

様々なメソッド

push

上でも紹介した最も基本的な 「次のページに遷移する」 メソッドです。

pop

「前のページに遷移する」 メソッドです。

Navigator.of(context).pop();

Navigatorクラスの一番上にスタックされている Route オブジェクトを取り除きます。その結果、その次にスタックされている Route オブジェクト(1つ前のページ)が表示されます。

一番上にスタックされている Route オブジェクトを取り除くので、ページ指定は不要です。

pushReplacement

「現在のページを新しいページと入れ替える」 メソッドです。

Navigator.of(context).pushReplacement(
    MaterialPageRoute(
        builder: (context) => PageB(),
    ),
);

一番上にスタックされているRouteオブジェクトを、新しいRouteオブジェクトと差し替えます。元のRouteオブジェクトは破棄されるので、popなどで戻ることはできません。

pushAndRemoveUntil

「次のページに遷移しつつ、特定の条件のページまで過去のページを取り除く」 メソッドです。

第一引数に遷移先のRouteオブジェクトを渡し、第二引数に取り除くRouteオブジェクトの条件を渡します。スタックされたRouteオブジェクトを上から順に条件式が適用され、trueが返ってくるまでRouteオブジェクトを取り除き続けます。

第二引数のコールバック関数では、Routeオブジェクトを受け取るので、パス名判定などに使うことができます。

// 全ての過去のページを取り除く
Navigator.of(context).pushAndRemoveUntil(
    MaterialPageRoute(
        builder: (context) => PageD(),
    ),
    (route) => false,
);

// パス名が'/home'のページに辿り着くまで過去のページを取り除く
Navigator.of(context).pushAndRemoveUntil(
    MaterialPageRoute(
        builder: (context) => PageD(),
    ),
    (route) => route.settings.name == '/home',
);

下記では 遷移と同時に home という名前のページまで過去のページを取り除いています。

popUntil

「指定のページまで一気に戻る」 メソッドです。

引数にコールバック関数を渡し、そのコールバック関数がtrueを返すまで、スタックされているRouteオブジェクトを取り除きます。

コールバックで受け取るRouteオブジェクトは、pushAndRemoveUntilと同様にパス名判定などに使うことができます。

// 特定のページまで戻る
Navigator.of(context).popUntil((route) => route.settings.name == '/user');

// 一番最初のページまで戻る
Navigator.of(context).popUntil((route) => route.isFirst);

pop 時に値を渡す

前の画面に戻るpopメソッドでは、戻る際に値を渡すこともできます。

値を返す画面へ遷移する際に、返り値を受け取る前提で処理を記述する必要があります。

返り値を受け取る処理は非同期処理となる為、asyncawaitを使用します。

画面遷移では値が返されない場合もあるので、返り値の型はnullableな型として定義しましょう(<String?>の部分)

// 値を受け取る前提で遷移する
...
onTap: () async {
   final String? result = await Navigator.of(context).push<String?>(
        MaterialPageRoute(
            builder: (context) => PageB(),
        ),
    );
    print(result);
},
...

// 値を持って、前の画面に戻る
Navigator.of(context).pop('戻る際に渡したい値');

まとめ

以上、画面遷移の仕組みとそのメソッドについて紹介しました。開発をする際は常に画面スタックがどのような状態になっているのか意識しながら適切に遷移を行うようにしましょう。

それでは次は API 通信について学んでいきましょう!