FlutterでBottom sheet内で画面遷移を実装しようとすると意外と手こずります。
showModalBottomSheet
して、Bottom sheet内でNavigator.push
などしてもpush先の詳細画面がモーダル内で表示されないためです。
モーダル内で画面遷移を実現する
モーダル内で画面遷移を実現するポイントは2点です。
- Navigatorをネストさせる
- 画面を閉じるときはroot側のNavigatorを参照する
1. Navigatorをネストさせる
通常はMaterialAppが保持するNavigatorが、RootのNavigatorとして機能しています。それに加えモーダル用の自前Navigatorを作成することで、モーダル内の画面遷移が実現できます。
具体的なコードは下記です。
Navigatorでモーダルのページを包んで、onGenerateRoute
でルートを定義します。
※ Navigatorクラスの "Nesting Navigators"の章を参照
child: RaisedButton(
color: Colors.blue,
textColor: Colors.white,
onPressed: () => showModalBottomSheet(
context: context,
// 自前でモーダル用Navigatorを作成
builder: (context) => Navigator(
onGenerateRoute: (context) => MaterialPageRoute<ModalPage>(
builder: (context) => ModalPage(),
),
),
),
child: Text('Show modal'),
),
2. 画面を閉じるときはroot側のNavigatorを参照する
1.の対応をするとモーダル内での画面遷移はうまくいきますが、X
ボタンなどでモーダルを閉じるときに下のような挙動になり、うまく閉じません
これは1.の対応をした際、Routeがネストされた構造になっているからです。ネストした子のRoute側でpop
するのでモーダルが残ったままになります
これは、Navigator.of(context, rootNavigator: true)
でRootのNavigatorを指定することで解決できます
return Scaffold(
appBar: AppBar(
leading: IconButton(
// 閉じるときはネストしているModal内のRouteではなく、root側のNavigatorを指定する必要がある
onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
icon: Icon(Icons.close)),
title: Text('Modal'),
),
完成
おまけ
触って動かせるDemoをDartPadに置いておいたので、ご活用ください
https://dartpad.dartlang.org/65afaedbe5016c4d5c4f20549c575e55