Flutterのダイアログ(Dialog)を理解しよう

モバイル開発

はじめに

Flutterでアプリを開発するとき、ユーザーに確認を求めたり情報を入力してもらいたい場面が出てくることがあるのではないでしょうか。 そんな場面で活躍するのが、 ダイアログ (Dialog) です。 ここでは、Flutterにおけるダイアログの基本的な役割や表示方法を解説していきます。

ただ枠を出すだけではなく、業務システムの利用規約確認や、エラーの通知などにも活用できます。 初心者の方でも読んだあとに自分で実装しやすいように、具体的なコード例を見ながら学んでいきましょう。

この記事を読むとわかること

  • Flutterのダイアログの種類 とその役割
  • 代表的な AlertDialogSimpleDialogModalBottomSheet の使い方
  • 実務でよくある利用シーンと、使い分けの考え方
  • カスタマイズしたレイアウトを使う方法

ここで紹介するサンプルコードを通じて、ダイアログに対してどのように画面遷移や状態管理を組み合わせればいいかも把握できるでしょう。 ぜひ参考にしてみてください。

Flutterにおけるダイアログの概要

Flutterでダイアログを扱うときは、画面上に小さなウィンドウを重ねて表示するイメージです。 ユーザーの操作を強制的に止めるような使い方だけでなく、何かを選択させたり軽い確認をする場面でも役立ちます。

実務では、アプリが複数のステップを伴う機能を提供するときに、ダイアログをステップ間のガイドとして使う場合があります。 例えば、フォーム入力の途中で未保存の変更がある際の注意をユーザーに促すなど、UI全体を切り替えたくないが重要なアクションを求めたいときに便利です。

ダイアログの基本的な表示手順

  1. showDialogウィジェット を使う
  2. context を渡す
  3. ダイアログのウィジェット(AlertDialogなど)をビルドする

表示が完了すると、背景が半透明になった上にダイアログが前面に表示されます。 何かしらの操作でダイアログを閉じる(Navigator.popなど)まで、ユーザーは他の画面要素を操作できなくなります。

AlertDialogの使い方

ダイアログといえば、最もよく見かけるのが AlertDialog ではないでしょうか。 OKボタンやキャンセルボタンを設置して、ユーザーの選択を求めるときに便利です。

実際に「削除しますか?」のような確認を取る場面を想定しながら、サンプルコードを見ていきましょう。

import 'package:flutter/material.dart';

class AlertDialogExample extends StatelessWidget {
  const AlertDialogExample({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('AlertDialog Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    title: const Text('確認'),
                    content: const Text('本当に削除しますか?'),
                    actions: [
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context, 'キャンセル');
                        },
                        child: const Text('キャンセル'),
                      ),
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context, 'OK');
                        },
                        child: const Text('OK'),
                      ),
                    ],
                  );
                },
              );
            },
            child: const Text('ダイアログを表示'),
          ),
        ),
      ),
    );
  }
}

上の例では、showDialog を使って AlertDialog を表示しています。 content にメッセージを入れ、actions の中にボタンを配置することで、ユーザーが次の行動を選択できるようにしています。

SimpleDialogの使い方

AlertDialogに比べて、もう少しシンプルな情報提示や選択肢のリスト表示を行いたい場合は、SimpleDialog が便利です。 タイトルと選択肢を用意し、クリックした結果を受け取る仕組みはAlertDialogと似ています。

以下の例では、ユーザーに対して複数の選択肢を提示し、どれを選んだかを画面に表示するイメージです。

import 'package:flutter/material.dart';

class SimpleDialogExample extends StatefulWidget {
  const SimpleDialogExample({Key? key}) : super(key: key);

  
  _SimpleDialogExampleState createState() => _SimpleDialogExampleState();
}

class _SimpleDialogExampleState extends State<SimpleDialogExample> {
  String _selected = '';

  void _showSimpleDialog() async {
    final result = await showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        return SimpleDialog(
          title: const Text('どれを選びますか?'),
          children: [
            SimpleDialogOption(
              onPressed: () {
                Navigator.pop(context, 'Aを選択');
              },
              child: const Text('Aを選択'),
            ),
            SimpleDialogOption(
              onPressed: () {
                Navigator.pop(context, 'Bを選択');
              },
              child: const Text('Bを選択'),
            ),
          ],
        );
      },
    );
    if (result != null) {
      setState(() {
        _selected = result;
      });
    }
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('SimpleDialog Example')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _showSimpleDialog,
                child: const Text('SimpleDialogを表示'),
              ),
              const SizedBox(height: 20),
              Text('選択結果: $_selected'),
            ],
          ),
        ),
      ),
    );
  }
}

このサンプルでは、showDialog の返り値を受け取り、それを_selectedという変数に代入しています。 そして、画面に選択結果を表示することで、ユーザーがどの選択肢をタップしたかを可視化できるわけです。

ModalBottomSheetを使ったダイアログ風UI

ダイアログというと画面中央にポップアップするイメージがありますが、Flutterでは画面下部からせり上がる ModalBottomSheet もよく利用されます。 特にスマートフォンでのユーザー体験を考えると、下から出てくるUIは親指の届きやすい範囲にボタンを配置しやすいというメリットがあります。

import 'package:flutter/material.dart';

class ModalBottomSheetExample extends StatelessWidget {
  const ModalBottomSheetExample({Key? key}) : super(key: key);

  void _showModalSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return SizedBox(
          height: 200,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text('これはModalBottomSheetです'),
                ElevatedButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: const Text('閉じる'),
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ModalBottomSheet Example')),
        body: Center(
          child: ElevatedButton(
            onPressed: () => _showModalSheet(context),
            child: const Text('BottomSheetを表示'),
          ),
        ),
      ),
    );
  }
}

上記のように、画面下からシートが表示されるため、中央にポップアップするダイアログよりもカジュアルな印象を与えられます。 利用シーンとしては、アクションの選択肢リストをまとめて提示したり、画像アップロード時のオプションを選ぶなどが考えられます。

実務でよくあるダイアログの活用シーン

ここでは、現場で使われそうなダイアログの活用例をいくつか挙げてみます。 実際の業務フローを想定しつつ、どのようなところでダイアログが役に立つのか確認してみましょう。

保存や削除など重要な操作の確認

ユーザーが意図せずデータを削除してしまわないように、本当に実行していいかどうか を確認するときは、ダイアログを使うのが定番です。 いきなり処理を走らせるのではなく、ダイアログでワンクッション置くことで誤操作を防げます。

マルチステップの入力フォーム

フォームを複数ページに分けているアプリでは、ステップを移動するときに確認のダイアログを出すことが多いです。 特に未保存の情報がある場合は、「次へ進むと入力内容が消えてしまいますがよろしいですか?」のように注意を促すと、ユーザーが混乱しにくくなります。

パスワード再設定などユーザー情報へのアクセス

ユーザー情報の管理画面などでパスワードをリセットするときに、ダイアログを使って最終確認を行います。 一度変更すると取り消しに時間がかかる場合があるため、重要な操作として認識してもらうのに役立ちます。

ダイアログにはユーザー操作の流れを止める効果があります。 そのため、あまりにも頻繁に表示すると煩わしさを感じさせてしまうかもしれません。

ダイアログのカスタマイズ

ダイアログをもっと自由にデザインしたいときは、Custom Widget を使ってウィジェットを組み合わせる方法がおすすめです。 標準のAlertDialogやSimpleDialogも便利ですが、社内システムのブランディングやテーマカラーに合わせたい場合には、より細かくレイアウトを調整する必要が出てきます。

テーマを活用した見た目の統一

MaterialAppではテーマを設定し、ダイアログにも反映させることができます。 色やフォントをまとめて指定することで、アプリ全体の見た目を統一できます。

import 'package:flutter/material.dart';

class CustomThemeExample extends StatelessWidget {
  const CustomThemeExample({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        dialogTheme: DialogTheme(
          backgroundColor: Colors.yellow[50],
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8.0),
          ),
        ),
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('カスタムテーマ例')),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    title: const Text('テーマ適用'),
                    content: const Text('ダイアログにカスタムテーマが適用されています。'),
                    actions: [
                      TextButton(
                        onPressed: () => Navigator.pop(context),
                        child: const Text('閉じる'),
                      ),
                    ],
                  );
                },
              );
            },
            child: const Text('ダイアログを表示'),
          ),
        ),
      ),
    );
  }
}

ここでは dialogTheme を設定し、背景色や角の丸みなどを変更しています。 アプリのブランドカラーを使えば、ダイアログを開いた瞬間に統一感を感じてもらえるでしょう。

独自ウィジェットを埋め込む

もし、より複雑なレイアウトを持つフォームや画像などをダイアログ内に配置したい場合は、AlertDialog などの content に自作のウィジェットをそのまま入れてしまう方法があります。 例えば、小さな入力欄や画像プレビューをダイアログの中に設置すれば、別画面を遷移することなくユーザーに操作させることができます。

import 'package:flutter/material.dart';

class CustomContentDialogExample extends StatelessWidget {
  const CustomContentDialogExample({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('カスタムコンテンツ例')),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    title: const Text('独自ウィジェット'),
                    content: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const Text('ここに複雑なUIを配置可能です。'),
                        TextField(
                          decoration: const InputDecoration(
                            labelText: '入力してみましょう',
                          ),
                        ),
                      ],
                    ),
                    actions: [
                      TextButton(
                        onPressed: () => Navigator.pop(context),
                        child: const Text('閉じる'),
                      ),
                    ],
                  );
                },
              );
            },
            child: const Text('ダイアログを表示'),
          ),
        ),
      ),
    );
  }
}

上記のコードのように、contentColumnTextField などを配置するだけで、独自のUIを持ったダイアログを実装することができます。 実務でも、入力フォームを別画面にわざわざ分けなくてもいい場面で重宝するので、UI設計の幅が広がりますね。

ダイアログの閉じ方と結果の受け取り

ダイアログは Navigator.pop(context, 値) で閉じるのが一般的です。 返す値を指定すれば、メイン画面で受け取って処理を続行できます。

業務アプリでは、OKボタンでアクションを実行して、キャンセルなら何もしないといった分岐を設けたいときが多いでしょう。 そういった場合は以下のようにダイアログを閉じるときに値を戻し、呼び出し元がそれを判定する方法が使えます。

onPressed: () {
  Navigator.pop(context, 'OK');
}

そして呼び出し側は await showDialog<String>(...) の戻り値を result として受け取り、分岐を行います。 「OK」が戻ってきたならデータを削除、「キャンセル」なら何もしないといった処理を書けます。

実務での注意点やベストプラクティス

ダイアログは便利ですが、多用するとユーザーが「ダイアログ地獄」に陥る危険があります。 以下のポイントに気をつけると、より良い使い方ができるでしょう。

  • 必要なタイミングだけ 表示する
  • ボタンのラベルは誰が見ても分かりやすい文言にする
  • 同じ操作で繰り返しダイアログを出すのは避ける
  • 大量の情報を詰め込みすぎない

ダイアログに長文や複数の入力欄を一度に盛り込みすぎると、画面切り替えよりも分かりにくくなる可能性があります。

ユーザーがストレスなく操作できる範囲で、ダイアログを取り入れていきましょう。

まとめ

Flutterでダイアログを扱う方法として、AlertDialogやSimpleDialog、ModalBottomSheetなどを紹介しました。 これらを上手に使うことで、アプリの操作性が向上し、ユーザーにとっても分かりやすい画面遷移を実現できます。

実務では、削除や保存など重要な操作の確認を促すためにダイアログを利用するケースが多いかもしれません。 また、複雑な入力フォームや画像プレビューなどを実装するなら、独自ウィジェットを組み込む方法も有力な選択肢です。

しかし、便利であるからこそ、ユーザー体験を妨げないように適切なタイミングと回数で使うことがポイントです。 皆さんのアプリケーションに合わせて、ダイアログの表示タイミングやデザインを工夫してみてはいかがでしょうか。

Flutterをマスターしよう

この記事で学んだFlutterの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。