最近 Flutter を触っている。
フロントエンドでユーザーに確認のモーダルを出すときの自分が好きな実装パターンの一つである、
「モーダルを表示しユーザーに確認を問い、モーダルを閉じた後に結果を bool で返す非同期関数」の Flutter 版を書いた。
挙動は以下の通り。
1. モーダルを表示
2. ユーザーに確認を問う
3. モーダルを閉じる
3.1. ユーザーが OK を押した場合: true を返す
3.2. ユーザーが Cancel を押した場合: false を返す
3.3. ユーザーが OK も Cancel も押さずにモーダルを閉じた場合: false を返す
お好きな Confirm Widget を作成する。とりあえず今回は以下のようなもの。
class SimpleConfirm extends StatelessWidget {
const SimpleConfirm({
Key? key,
required this.title,
required this.body,
this.onOk,
this.onCancel,
}) : super(key: key);
final String title;
final String body;
final VoidCallback? onOk;
final VoidCallback? onCancel;
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(title),
content: Text(body),
actions: [
Padding(
padding: const EdgeInsets.all(8),
child: GestureDetector(
child: const Text('Cancel'),
onTap: () {
onCancel?.call();
Navigator.pop(context);
},
)),
Padding(
padding: const EdgeInsets.all(12),
child: GestureDetector(
child: const Text('OK'),
onTap: () {
onOk?.call();
Navigator.pop(context);
},
)),
],
);
}
}
これを呼び出す関数は以下。
Future<bool> showConfirm(
BuildContext context, {
required String title,
required String body,
}) async {
final completer = Completer<bool>();
await showDialog(
context: context,
builder: (context) => SimpleConfirm(
title: title,
body: body,
onOk: () => completer.complete(true),
onCancel: () => completer.complete(false),
),
);
if (!completer.isCompleted) {
completer.complete(false);
}
return completer.future;
}
showDialog
を await することで、Confirm Widget の外側をタップしたり端末の戻るボタンで Widget を閉じた場合にも対処できる。その場合は completer が未解決のままなので、complete(false)
してあげれば良い。
こうすることで barrierDismissible: false
にしたり、戻るボタンの挙動を停止したりする必要がないため、不必要に普段の UI から乖離させなくて済む。
以下のように呼び出すことができる。
final ok = await showConfirm(
ctx,
title: "Are you sure?",
body: "Once you delete, you can't undo it.",
);
if (ok) {
// call API
}