Navigator 2.0と以前のバージョンとの大きな違い
これまでの画面遷移(以下、Navigator 1と示す)は、画面上のアクションに応じて、画面変更を命令するという流れでした。それに対して、Navigator 2.0(以下、Navigator2)では、画面スタックの状態を管理するというようになっています。文章で説明しても、少々イメージがわきにくいというところもあるので図1のように違いを示しました。
例えばNavigator1では、PUSH/POPという概念を使って、画面スタックを変更します。PUSHであればスタック上の一番上に追加し、POPであれば一番上から画面を取り除くというようにです。これらの説明は前回したので、詳しくはそちらを参照してください。
Navigator2では、画面スタックを直接コントールすることで画面の切り替えを行い、それに合わせてパスなどを変更します。そのため、明示的な画面変更方法ではなく概念的で少々わかりにくくなるかと思います。
また、パスを使った操作については、必ずしも必要なわけではありません。もちろん、パスを利用しない場合にはそのメリットがなくなります。しかし、構造としてわかりやすくなるため、まずはパスを使った画面遷移を使わない場合を説明し、その後パスを使った画面遷移を使う場合として説明します。
パスを使わない場合のNavigator2の利用方法
まずは、シンプルな構造で、Navigator2での画面スタックのコントロールに主に焦点をあてた説明します。このサンプルの動作イメージとプログラムの主要な要素との関係をあらわしたのが図2です。
各画面であるProductListPageとProductItemPageは、双方共にWidgetのサブクラスです。このプログラムについての説明は今回は割愛させていただきます。また、データを管理するクラスとしてProductItemクラスを用意しました。このクラスのコードがリスト1です。
class ProductItem { final String id; // (1) データのID(パスを使ったサンプルで利用) final String title; // (2) データのタイトル(一覧のタイトル・詳細画面のタイトル) ProductItem(this.id, this.title); }
そして、Navigator2を使った画面遷移のプログラムがリスト2です。
class _MainApp1 extends State{ // (1) 詳細画面として表示する場合のデータ ProductItem _selectedItem; // (2) ProductListPageでListをタップした時に呼ばれるメソッド void _onTapItem(ProductItem item) { setState(() { _selectedItem = item; }); } // (3) 管理しているデータ List items = [ ProductItem("id1", "商品A"), ProductItem("id2", "商品B") ]; @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Nav2 Sample 1', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), // (4) 最初のページとしてNavigatorクラスを指定する home: Navigator( // (5) 表示する画面スタック pages: [ MaterialPage(child: ProductListPage(this.items, _onTapItem)), // (6) 詳細画面を表示する時には、画面を追加する if (_selectedItem != null) MaterialPage(child: ProductItemPage(_selectedItem)), ], // (7) popが呼ばれた時の処理 ( pages[]を指定したら、必ず必要です ) onPopPage: (route, result) { // (8) popできる画面がない場合の対応 if (!route.didPop(result)) { return false; } // (9) popできた場合には、データをクリアする setState(() { _selectedItem = null; }); return true; }, )); } }
(1)は、詳細画面を表示する際のオブジェクトを管理するための変数です。このオブジェクトがnullの場合には一覧画面の表示とし、インスタンスが設定されている場合には、詳細画面が表示されるということを想定しています。
(2)の_onTapItemメソッドは、一覧画面の各リストアイテム部分がタップされた時にコールされることを想定したメソッドです。タップされると詳細画面を表示するためのインスタンスを(1)の変数に設定します。また、この変数が設定されたタイミングで画面更新が生じるようにsetStateを使ってオブジェクトを設定しています。
(3)は、今回表示するデータの一覧です。実際の利用ケースでは、このデータをネットワークから取得するような場合が多いはずです。
そして、Navigator2では、(4)のようにMaterialAppのhomeプロパティにNavigatorクラスを指定します。今までの例では、Navigatorクラスはスタティックな利用でしたが、今回はWidgetとして扱っています。
(5)のpagesプロパティが、実際のページスタックを管理している部分です。_selectedItemにインスタンスが設定されている場合には、詳細画面を表示する必要があるので、(6)のように画面スタックに追加します。この配列の順番が画面スタックになるので、順番を反対にすると表示されません。
また、(7)が、戻るボタンなどを押した場合、つまりpopが呼び出された場合にどのような動作をするかを定義します。一覧が画面が表示されている場合には戻るべき画面はないため、(8)のように処理を抑制しています。
そして、今回の例では戻るボタンは詳細から一覧画面への戻る操作になるため、(9)のように既に選択されたいたデータをクリアし、画面を更新します。以上のように一見複雑に見えますが、やっていることはNavigatorクラスのpagesプロパティを変更することです。