概要
WillPopScope を使った際にiOSで 'swipe back gesture' が使えなくなる問題の対処が地味に難しかったので紹介します。
解決策について
この問題については、
で議論されていましたがまともな解決策が出ていないようでした。
そこで、いろいろ調べてみたところ
というパッケージにたどり着き、これを使うことで思い通りの実装ができました。
実装
インストールやthemeの設定は本家の方を参照していただくとして、工夫した点だけ紹介します。
サンプルだと、
@override
Widget build(BuildContext context) {
return WillPopScope(
child: _MyScreenContent(),
onWillPop: _hasChanges ? _onWillPop : null,
);
}
という実装になっています。しかし、_hasChanges
は戻るボタンを押した際の値ではなく、レンダリング時の値になってしまうので戻るボタンを押したタイミングで変更があったかどうかというのは_onWillPop
の中で実装する必要があります。
しかし、次のように実装すると
Future<bool> _onWillPop() async {
if(!_hasChanges){
Navigator.of(context).popUntil(ModalRoute.withName('/'));
return true;
}
return await showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text("保存しますか?"),
actions: <Widget>[
TextButton(
onPressed: () {
...
},
child: Text(
"はい",
),
),
TextButton(
onPressed: () {
Navigator.of(context).popUntil(ModalRoute.withName('/'));
},
child: Text(
"いいえ",
),
),
],
),
) ??
false;
}
変更があった場合は想定通りの動きをしますが、変更がなかった場合、ページが思い通り遷移してくれません。
この問題については、次のように変更することで解決しました。
if(!_hasChanges){
await Future.delayed(const Duration(microseconds: 1)); // <-追加
if (!mounted) return true; // <-追加
Navigator.of(context).popUntil(ModalRoute.withName('/'));
return true;
}
原因はよくわかっていないですが、何かしらの処理を挟まないと、Navigator.of(context).popUntil(ModalRoute.withName('/'));
がうまく実装されないようだったので、ごくわずかな時間だけ待たせることで思い通りの動作をさせることができました。
参考文献
余談
別の方法として、
のようにスワイプ処理を通してページ遷移させようと試みたのですが、なぜかうまくいきませんでした。。。