【Flutter】ModalRouteを使ったカスタムダイアログの実装

もくじ

こんにちは。スマホアプリをメインに開発しているロッキーカナイです。

記事が大分ご無沙汰になってしまいましたが、今回はFlutterでカスタムダイアログの実装について紹介したい思います。

まずFlutterでダイアログを実装する場合は、SimpleDialogAlertDialogを使うと思うのですが、クライアントの要望にあったオシャンティなダイアログを作るとなると、これらでは実現出来ません。そこでカスタムダイアログを用いて実装します。

ではカスタムダイアログの実装を見てみましょう。

実装説明

ModalRouteを継承したページを用意し、モーダル画面遷移させて現在のページの上にかさねる感じになります。

よって、現在のページ上にボタンがあったとしてもタッチは届かず、ダイアログとしての機能を満たします。

ModalRouteを継承したルートクラスを実装

ModalRouteを継承したModalOverlayクラスを追加します。

import 'package:flutter/material.dart';

/*
 * モーダルオーバーレイ
 */
class ModalOverlay extends ModalRoute<void> {

  // ダイアログ内のWidget
  final Widget contents;

  // Androidのバックボタンを有効にするか
  final bool isAndroidBackEnable;

  ModalOverlay(this.contents, {this.isAndroidBackEnable = true}) : super();

  @override
  Duration get transitionDuration => Duration(milliseconds: 100);
  @override
  bool get opaque => false;
  @override
  bool get barrierDismissible => false;
  @override
  Color get barrierColor => Colors.black.withOpacity(0.5);
  @override
  String get barrierLabel => null;
  @override
  bool get maintainState => true;


  @override
  Widget buildPage(
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      ) {
    return Material(
      type: MaterialType.transparency,
      child: SafeArea(
        child: _buildOverlayContent(context),
      ),
    );
  }

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
    return FadeTransition(
      opacity: animation,
      child: ScaleTransition(
        scale: animation,
        child: child,
      ),
    );
  }

  Widget _buildOverlayContent(BuildContext context) {
    return Center(
      child: dialogContent(context),
    );
  }

  Widget dialogContent(BuildContext context) {
    return WillPopScope(
      child: this.contents,
      onWillPop: () {
        return Future(() => isAndroidBackEnable);
      },
    ); 
  }
}

Androidで実機のバックボタンを押すとダイアログが閉じてしまうので、有効無効の切り替えができるようにしました。dialogContentメソッドのWillPopScopeクラスのonWillPopでポップする際にコールバックが呼ばれて、bool値を返すことで切り替えしてます。

また、ModalOverlayではcontentsというWidgetをもらってそれを表示させるようにしました。

ダイアログのコンテンツクラスを実装

CustomDialogというクラスを作り、ここでModalOverlayのコンテンツを作って渡すのと、push遷移させます。

import 'package:flutter/material.dart';
import 'package:test_project/ModalOverlay.dart';

class CustomDialog {

  BuildContext context;
  CustomDialog(this.context) : super();

  /*
   * 表示
   */
  void showCustomDialog() {

    Navigator.push(
      context,
      ModalOverlay(
        Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[

              Stack(
                children: <Widget>[

                  Image.asset(
                    "images/fukidashi.png",
                    fit: BoxFit.fitWidth,
                  ),

                  Container(
                      height: 200.0,
                      child: Center(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[

                            /*
                             * タイトル
                             */
                            Text(
                              "カスタムダイアログ",
                              style: TextStyle(
                                fontSize: 30.0,
                                fontWeight: FontWeight.bold,
                                locale: Locale("ja", "JP"),
                              ),
                            ),

                            /*
                             * メッセージ
                             */
                            Text(
                              "こんな感じでダイアログが出せるよ",
                              style: TextStyle(
                                fontSize: 16.0,
                                locale: Locale("ja", "JP"),
                              ),
                            ),
                          ],
                        ),
                      )
                  )
                ],
              ),

              /*
               * OKボタン
               */
              Row(
                children: <Widget>[
                  Expanded(
                    child: Container(),
                  ),

                  FlatButton(
                    color: Colors.blue,
                    child: Text(
                      "OK",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 18.0,
                        fontWeight: FontWeight.bold,
                        locale: Locale("ja", "JP"),
                      ),
                    ),
                    onPressed: () {

                      hideCustomDialog();
                    },
                  ),

                  SizedBox(
                    width: 80.0,
                  )
                ],
              ),
            ],
          )
        ),
        isAndroidBackEnable: false,
      ),
    );
  }


  /*
   * 非表示
   */
  void hideCustomDialog() {

    Navigator.of(context).pop();
  }
}

呼び出しは以下のようにします。

onPressed: () {

   CustomDialog(
      context,
   ).showCustomDialog();
},

フキダシはここのを使用しました。

まとめ

いかがでしたでしょうか?

ModalRouteを使ってカスタムダイアログの実装を紹介をしました。

今回作ったModalOverlayを用いて、通信中などで使うインジケータビューとしても使うことができますので、次回はその紹介をしたいと思います。

いつかマイブームのキックボクシングの記事書けたらいいなぁ。

関連する記事

もっと見る