【Flutter/Dart】アラートダイアログやモーダルの実装方法!showDialog

この記事からわかること
- Flutter/Dartのアラートダイアログやモーダルの実装方法
- showModalBottomSheetやshowDialog、AlertDialog Widgetの使い方
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Koala
- Xcode:16.0
- Flutter:3.29.2
- Dart:3.7.2
- Mac M1:Sonoma 14.6.1
Flutterでアラートダイアログやモーダルの実装方法
Flutterで「ボタンを押下した後の結果表示するアラートダイアログ」や「入力画面や編集画面を表示するモーダル画面」の実装方法をまとめていきます。またFlutterではAndroidライクなマテリアルデザインとiOSライクなクパチーノデザインが存在し、アラートダイアログでも存在するので使い分けて開発することが可能です。
アラート(マテリアルデザイン)

アラート(クパチーノデザイン)

モーダル

※ モーダルもマテリアルとクパチーノが用意されていますが、UIに大きく変化はありません。
マテリアルデザインのアラートダイアログの実装方法
マテリアルデザインのアラートダイアログを表示させるにはshowDialog
メソッドを使用します。これを使用することで画面の中央にポップアップ表示させることが可能になります。ダイアログに表示させたい内容はAlertDialog
を使用します。title
、content
、actions
からそれぞれ指定が可能で、actions
の中にボタン Widgetを渡すことで任意の処理を実行することが可能です。
ダイアログを明示的に閉じたい場合はNavigator.of(context).pop()
を使用します。
void showMyDialog(BuildContext context) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text("確認"),
content: Text("この操作を実行しますか?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("キャンセル"),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("OK"),
),
],
);
},
);
}

周りをタップで閉じないようにする
デフォルトではダイアログの周りをタップするとダイアログが非表示になりますが、これを非表示にしたくない場合はbarrierDismissible
にfalse
を指定すればOKです。
showDialog(
context: context,
barrierDismissible: false, // ダイアログの周りをタップで閉じなくなる
builder: (BuildContext context) {
// UI
}
)
クパチーノデザインのアラートダイアログの実装方法
クパチーノデザインのアラートダイアログを表示させるにはshowCupertinoDialog
メソッドを使用します。基本的な使い方はshowDialog
と変わりません。
void _showConfirmAlert(BuildContext context) {
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text("確認"),
content: Text("この操作を実行しますか?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("キャンセル"),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("OK"),
),
],
);
},
);
}

またクパチーノデザインのアラートダイアログでは特に設定をしなくてもダイアログの周りをタップしても非表示にならないようです。
マテリアルデザインのモーダルの実装方法
マテリアルデザインのモーダル画面を表示させるにはshowModalBottomSheet
メソッドを使用します。これを使用することで画面の下部からシートがスライドして表示されるようになります。モーダル画面のサイズはUIのサイズに影響します。なのでMediaQuery
を使用して画面自体のサイズを取得しておくと綺麗にモーダル表示できます。
ただ高さのサイズを一定以上に指定するにはisScrollControlled
をtrue
にしておく必要があります。
void showModalView(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.9,
child: Column(
children: [
Spacer(),
Text("モーダル画面", style: TextStyle(fontSize: 20)),
Spacer(),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text("閉じる"),
),
Spacer(),
],
),
);
},
);
}

クパチーノデザインのモーダルの実装方法
クパチーノデザインのモーダル画面を表示させるにはshowCupertinoModalPopup
メソッドを使用します。基本的な使い方はshowModalBottomSheet
と変わりません。ただ角丸や背景色は明示的にBoxDecoration
で指定しないと期待通りになりませんでした。
void _showModalView(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.9,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
children: [
Spacer(),
Text("モーダル画面", style: TextStyle(fontSize: 20)),
Spacer(),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text("閉じる"),
),
Spacer(),
],
),
);
},
);
}
モーダルが表示されないエラーの原因と解消
showModalBottomSheet
やshowCupertinoModalPopup
を使用しているのにモーダルが表示されないことがあります。この場合は以下のようなエラーが出ていることが多いです。
Another exception was thrown: No MaterialLocalizations found.
上記のエラーは「マテリアルローカリゼーションが見つからない」というエラーです。これは実装しているアプリのマテリアル/クパチーノテーマの構造に問題がある可能性があります。アプリのルートでMaterialApp
/CupertinoApp
を指定すると思いますが、これが使用するモーダルと同じになっているか確認してみてください。他にもScaffold
/CupertinoPageScaffold
やMaterialPageRoute
/CupertinoPageRoute
なども統一しておくと安心です。
void main() {
// MaterialApp => Android
// CupertinoApp => iOS
runApp(const CupertinoApp(home: MyCupertinoPage()));
}
class MyCupertinoPage extends StatelessWidget {
const MyCupertinoPage({super.key});
@override
Widget build(BuildContext context) {
// Scaffold => Android
// CupertinoPageScaffold => iOS
// MaterialPageRoute => Android
// CupertinoPageRoute => iOS
return CupertinoPageScaffold(
child: Center(
child: ElevatedButton(
onPressed: () => {_showModalView(context)},
child: Text("SHOW"),
),
),
);
}
void _showModalView(BuildContext context) { ... }
}
データを受け渡す
アラートやモーダル側にデータを渡す場合は引数に指定すればOKですが、アラートやモーダル側で入力された値などを呼び出し元に渡すことも可能になっています。方法は簡単でawait
を使用して結果を待機させ、モーダル側ではNavigator.pop(データ)
形式で実装するだけです。
Future<void> _showMyModal(BuildContext context) async {
// 返り値としてpopで指定した値を取得することができる
final result = await showModalBottomSheet<String>(
context: context,
builder: (context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("値を返します"),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop("モーダルからの値");
},
child: Text("閉じる"),
),
],
),
);
},
);
if (result != null) {
print("モーダルから受け取った値: $result");
}
}
// 呼び出し
_showMyModal(context);
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。