Flutterでプラットフォームによって違うUIのダイアログを表示してみた。

Flutterでプラットフォーム毎に異なるUIを表示する実装をしてみたので、そのご紹介です。
2018.08.29

大阪オフィスの山田です。家の中でよくねこを紛失しますが、大体布団の中にいます。今回は、プラットフォーム毎に、異なるUIのダイアログを表示してみます。

今回やること

前回作ったチャットアプリにサインイン機能を追加しましたので、今回はサインアウト機能をつけます。その際に表示する確認ダイアログをiOS、Androidで表示を変えてみたいと思います。

完成イメージ

iOS

Android

※画像はタブレット端末のスクリーンショットです

開発環境

flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.13.5 17F77, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.1)
[✓] iOS toolchain - develop for iOS devices (Xcode 9.4)
[✓] Android Studio (version 3.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.26.1)
[✓] Connected devices (1 available)

flutter --version

Flutter 0.5.1 • channel beta • https://github.com/flutter/flutter.git
Framework • revision c7ea3ca377 (3 months ago) • 2018-05-29 21:07:33 +0200
Engine • revision 1ed25ca7b7
Tools • Dart 2.0.0-dev.58.0.flutter-f981f09760

実装

前準備

前回作ったチャットアプリの続きから始めます。プラットフォーム毎に処理を切り替える実装自体は、前回からの続きからでなくても参考にできるかと思います。

実装

まずは、ナビゲーションバーの右上にSignOutボタンを表示する部分です。ログインしている時にのみボタンを表示するようにしています。実装は以下の通りです。

import 'dart:io';
  import 'package:flutter/cupertino.dart';
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text("Firebase Chat"),
        actions: _buildAppBarActionButton()
      ),
      body: Container(
          child: _user == null ? _buildGoogleSignInButton() : _buildChatArea()),
    );
  }

  List<Widget> _buildAppBarActionButton() {
    return _user == null ? null :
      <Widget>[
        MaterialButton(
          onPressed: () {
            StatelessWidget dialog;
            dialog = Platform.isIOS ? 
            _buildSignOutDialogiOS() 
            : _buildSignOutDialogAndroid();

            showDialog(
              context: context,
              builder: (context) {
                return dialog;
              }
            );
          },
          child: Text(
            "SignOut",
            style: TextStyle(
              fontSize: 14.0,
              color: Colors.white
            ),
          ),
        )
      ];
  }

dart:ioをimportして、Platform.isXXXメソッドでプラットフォームを判定します。_buildSignOutDialogXXXメソッドで各プラットフォームに対応したダイアログを生成します。ダイアログを生成するメソッドは以下の通りです。

Widget _buildSignOutDialogAndroid() {
    return AlertDialog(
      title: Text("Confirm"),
      content: Text("Are you sure you want to Sign out?"),
      actions: <Widget>[
        FlatButton(
          child: const Text("Cancel"),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
        FlatButton(
          child: const Text("SignOut"),
          onPressed: () { 
            Navigator.pop(context);
            _signOut(); 
          },
        )
      ]
    );
  }

  Widget _buildSignOutDialogiOS() {
    return CupertinoAlertDialog(
      title: Text("Confirm"),
      content: Text("Are you sure you want to Sign out?"),
      actions: <Widget>[
        CupertinoDialogAction(
          child: const Text("Cancel"),
          isDefaultAction: true,
          onPressed: () {
            Navigator.pop(context);
          },
        ),
        CupertinoDialogAction(
          child: const Text("SignOut"),
          isDestructiveAction: true,
          onPressed: () { 
            Navigator.pop(context);
            _signOut(); 
          },
        )
      ]
    );
  }
  
  void _signOut() {
    _auth.signOut()
      .then((_) {
        setState(() {
          _user = null;
        });
      })
      .catchError((error) {
        print(error);
      });
  }

AndroidはAlertDialog, FlatButton、iOSはCupertinoAlertDialog, CuprtinoDialogActionクラスを使用しています。どちらも処理はほぼ同じでSignOutボタンがタップされたらサインアウトする処理を実行します。うーん、ここはほとんど処理が同じだし、なんとか共通化したいなぁ。。。今回、実装したソースはこちらに載せておきます。

最後に

いかがだったでしょうか。Flutterで完結する内容であれば、Flutterでプラットフォーム毎に処理を分けることができました。あんまり分けすぎるとFlutterを使うメリットも小さくなってしまうので、可能な限り統一して進めたい、というのが所感です。

参考文献