💻

[Flutter] 名前付きルートに引数を渡して画面遷移する際、引数をもとにウィジェットを動的に初期化する方法

2020/07/10に公開

前提

原始的な画面遷移

最も原始的な画面遷移の方法は、以下のように Navigator.push() を使う方法です。

onPressed: () {
  Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) => SomePageWidget(),
    ),
  );
}

参考:Navigate to a new screen and back - Flutter

名前付きルートを使った画面遷移

画面の数や画面遷移の数がある程度多いアプリになってくると、上記の方法だとコードの重複が気になります。

この問題は以下のように名前付きルートを使うことで解決できます。

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => FirstPageWidget(),
    '/second': (context) => SecondPageWidget(),
    '/third': (context) => ThirdPageWidget(title: 'Third page'),
  },
);
onPressed: () => Navigator.of(context).pushNamed('/third')

MaterialApp のコンストラクタ引数でルーティングを定義して、 Navigator.pushNamed() にルート名を渡すことで画面遷移を行うことができるわけです。便利ですね!

参考:Navigate with named routes - Flutter

名前付きルートに引数を渡して画面遷移

さて、遷移先のページのウィジェットが引数なしで初期化できる場合や、初期化に必要な引数が定数の場合は、上記の方法で何も問題ありません。

が、今いるページでしか知り得ない情報を引数として渡して次ページのウィジェットを初期化したいような場合は、少し厄介です。

Navigator.push() を使った原始的な画面遷移だったなら

onPressed: () {
  Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) => SomePageWidget(someDynamicValue),
    ),
  );
}

のようにウィジェットのコンストラクタに直接引数を渡すことができるので何も難しいことはなかったのですが、名前付きルートを使っている場合はこうは行きません。

どうすればいいかというと、 MaterialApp のコンストラクタ引数 onGenerateRoute を使います。

具体的には以下のような感じです。

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => FirstPageWidget(),
    '/second': (context) => SecondPageWidget(),
    '/third': (context) => ThirdPageWidget(title: 'Third page'),
  },
  onGenerateRoute: (settings) {
    if (settings.name == '/fourth') {
      return MaterialPageRoute(
        builder: (context) => FourthPageWidget(settings.arguments),
      );
    }
    return null;
  },
);
onPressed: () => Navigator.of(context).pushNamed('/fourth', arguments: someDynamicValue)

通常の routes: を使ったルート定義とは別に、 onGenerateRoute: のコールバック内でルート名をフックして動的に遷移先ページのウィジェットをビルドする感じです。

コールバックが受け取る settingsarguments に、 Navigator.pushNamed() の引数 arguments で渡された値が入っています。( arguments と複数形の名前ですが、特に配列のようなデータ構造を期待しているわけではなく 型は Object です

参考:Pass arguments to a named route - Flutter # Alternatively, extract the arguments using onGenerateRoute
参考:FlutterのNavigationとRoutingを理解する

引数で渡された値をただ表示できればいいだけの場合

引数で渡した値をもとに遷移先ページのウィジェットを 初期化 する必要がなくて、単に渡した値を 表示 できればいいだけという場合には、以下のように build() メソッド内で ModalRoute.settings.arguments にアクセスすることで渡された引数を受け取ることができます。

class SecondPageWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {

    // Navigatorに渡された引数をここで受け取る
    final args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text(args['title']),
      ),
      body: Center(
        child: Text(args['message']),
      ),
    );
  }
}
onPressed: () => Navigator.of(context).pushNamed('/second', arguments: {'title': someDynamicValue1, 'message': someDynamicValue2})

参考:Pass arguments to a named route - Flutter

まとめ

  • Flutterで名前付きルートに引数を渡して画面遷移する際、引数をもとにウィジェットを動的に生成するには、 MaterialApp のコンストラクタ引数 onGenerateRoute を使う
  • ウィジェットの初期化までは不要で単に引数で渡した値を遷移先のページで表示したいだけの場合は、 build() メソッド内で ModalRoute.settings.arguments から引数を受け取れる
GitHubで編集を提案

Discussion