6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

go_router 使ってみた(画面間の情報共有)

Last updated at Posted at 2022-01-24

前回、go_routerを使って単純な画面遷移を実装してみました。
前回の記事はこちら

今回はもう少し複雑にして、
画面間で情報の受け渡しを行うことを考えたいと思います。

サンプルコードを用意した上で解説しますので、
どのように画面遷移を扱うことになるか、ぜひ見てってください。

画面遷移の考え方と、状態管理手法##

画面構成は以下のようになっています。

今回実装するのは本のリストページから本の詳細ページへの画面遷移です。
手続的な画面遷移(Navigator 1.0)では、画面遷移時にBookオブジェクトを手渡していました。

go_routerを使った画面遷移でもそのままBookオブジェクトを手渡しすることは可能ですが、
この場合、ディープリンク(アドレスバーに直接パスを打ち込んでの画面遷移)はできなくなります。

そのため、以下のような手順を踏んで画面遷移を行います。

1. 遷移のために与えられたパスから、パラメータを取得する
2. パラメータから対応するオブジェクトを取得する
3. オブジェクトをページに渡し画面を遷移(構築)する

1.と3.についてはgo_routerにて対応可能ですが、
2.のパラメータから対応するオブジェクトを取得するためには、
オブジェクトのリストをあらかじめ共有しておき、リストから取得する、といった手順が必要となります。
今回はオブジェクトのリストが追加や削除ができる可変なものだと想定し、
リストの状態を状態管理手法(Provider)を使って管理し実装しました。

サンプルコード##

今回のサンプルコードはこちらになります。

サンプルコードの内容を解説しますので、合わせてご覧ください。

サブルートの構築##

画面間の情報の受け渡しについて説明する前にサブルートについて説明します。

今回の画面遷移は本のリストページからの遷移です。
詳細画面はリストページ以外からは遷移できません。
このことを強調するために、サブルートにて詳細画面を実装します。

コードは以下のようになります。

main.dart
//画面の情報を定義する
  final _router = GoRouter(
    initialLocation: '/',
    routes: [

      //省略

      GoRoute(
          name: 'list',
          path: '/list',
          pageBuilder: (context, state) => MaterialPage(
                key: state.pageKey,
                child: const BookListPage(),
              ),
                    //1
          routes: [
            GoRoute(
                name: 'detail',
                //2
                path: 'detail/:id',
                pageBuilder: (context, state) {

                                  //省略   

                }),
          ]),
    ],

    //省略

  );

//1
GoRouteのインスタンスの中にroutesを定義することができます。
今回でいえば、ListのサブルートとしてDetailが定義されます。
//2
サブルートのパスは'親のパス + サブルートのパス'になります。
今回でいえば、detailページのパスは/list/detail/1となります。
(:idについては後述します。)

パラメータの取得##

サブルートのパスに:idという記載がありました。(//2)
この部分が、画面遷移時に受け渡すパラメータとなります。

例えば、パラメータとして1を渡すとしたら、画面遷移のためのコードは以下のようになります。

context.go('/list/detail/1');

:idと記載していた場所の文字列がパラメータとして渡されるわけです。

また、もっと明確にパラメータを渡す方法として、以下の遷移の仕方もあります。

//こちらでも良い
context.goNamed('detail',params: {'id' : '1'});

渡されたパラメータの利用##

上記で渡されたパラメータは、pageBuilderstateを使用して、
state.params['id']として取り出すことが可能です。

サンプルコードの使用例を記載します。

main.dart
  //画面の情報を定義する
  final _router = GoRouter(

            //省略

      GoRoute(

                    //省略

          routes: [
            GoRoute(
                name: 'detail',
                path: 'detail/:id',
                pageBuilder: (context, state) {
                                     //3
                  Book? book = Provider.of<BookList>(context)
                      .bookFromId(state.params['id']!);
                  //4
                  //エラーページへ遷移
                  if (book == null) {
                    return MaterialPage(
                      key: state.pageKey,
                      child: Scaffold(
                          appBar: AppBar(
                            title: Text('ページが存在しません'),
                          ),
                          body: Container()),
                    );
                  }
                  return MaterialPage(
                    key: state.pageKey,
                    //5
                    child: BookDetailPage(
                      book: book,
                    ),
                  );                }),
          ]),
    ],
    
    //省略

  );

//3
BookListProviderの状態として管理しているため、少し複雑な書き方となっていますが、やっていることはシンプルです。
渡されたパラメータをstate.params['id']を使って取得し、
BookListというオブジェクトからbookFromIdメソッドを用いidの値を使ってBookオブジェクトを取り出しています。
bookFromIdメソッドは以下のような実装となります。

book_list.dart
class BookList {
  final List<Book> books;

  BookList._({required this.books});

  //省略

  Book? bookFromId(String id) {
    try {
      return books.where((element) => element.id.toString() == id).first;
    } catch (e) {
      return null;
    }
  }
}

//4
ディープリンクにて遷移した場合、idの値に対応したBookがない場合があります。
これに対応するためnullかどうかで条件分岐しています。
//5
取得したBookオブジェクトを詳細ページに手渡しています

上記のようにすることで、go_routerを使って値の受け渡しを実装できました。

サンプルコードをchromeにて実行し、

  • 本のリスト画面で本のタイトルをタップすることで、対応した詳細画面に移動すること
  • 戻るボタンが存在し、押すと前の画面に戻ること
  • 本のidがアドレスバーのパスに表示されること
  • パスを直接打ち込むことで、対応する詳細ページに移動すること

を確認してみてください。

Providerの使い方は今回の内容から外れるため、省略します。
サンプルコードにて実装内容を確認してください。

まとめ##

今回はgo_routerを使った画面遷移にて値を受け渡す方法について解説しました。

ディープリンクを実装する関係から、遷移時に受け渡せるのがStringだけ、
というのがgo_routerの難しいポイントだと思いました。
ただ、ここさえ乗り越えればかなり簡単に実装可能です。

ぜひ使ってみてください。

参考##

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?