概要
Webで言うところのPOSTでアクセスするような、ユーザーが1つ前の画面に戻ってほしくないシチュエーションがあると思います。
willPopScopeを使え、で終わりなのですが日本語の記事がなかった上に筆者の英語力がヘボ過ぎて英語の記事も見つけられなかったのでメモとして残します。
雑にtwitterで呟いたらへぶんさんが教えてくれました…圧倒的感謝…🙏
https://twitter.com/heavenOSK/status/1217020388552478723?s=20僕はよく戻らせたくない画面をWillPopScopeで囲んでる。
— へぶん🦌 (@heavenOSK) January 14, 2020
他にやり方があったら、教えてほしい。https://t.co/duWOwEoLpp https://t.co/AaR4VXuF2b
解説
APIドキュメントを読めば説明するまでもないと思いますが、一応やってみます。
前提
こんな感じの画面があると仮定してください。ルーティングやスタイルの部分は省略します。
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('画面1')),
bottomNavigationBar: BottomAppBar(
FlatButton(
onPressed: () => Navigator.of(context).pushNamed(Screen2.routeName),
child: Text('次の画面に遷移する')
),
);
}
}
class Screen2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('画面2'))
)
}
}
gifが分かりにくいかもしれませんが、画面2から画面1に戻れてしまっていますが、今回はこれを制限します。
willPopScopeでラップする
やることは簡単で、遷移後のwidgetをwillPopScopeでラップしてください。
_willPopScope.onWillPopにはFuture<bool>
を返す関数を登録しましょう。今回は戻ろうとした時に何もしないのでただtrue
を返す虚無関数です。(voidではない)
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _willPopCallback,
child: Scaffold(
body: Center(child: Text('画面1')),
bottomNavigationBar: BottomAppBar(
FlatButton(
onPressed: () => Navigator.of(context).pushNamed(Screen2.routeName),
child: Text('次の画面に遷移する')
),
),
);
}
Future<bool> _willPopCallback() async {
return true;
}
}
class Screen2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('画面2'))
)
}
}
結果
ツールの都合上マウスポインタが見えないですが、右にスワイプしても画面遷移しなくなりました。
willPopScopeの説明にも今開いているModalRouteを消す試みを拒否するコールバックを登録するとあるので、責務がこれだけで実装も非常に簡単ですね。お疲れ様でした。
Registers a callback to veto attempts by the user to dismiss the enclosing ModalRoute.