システム開発部のTです。
以前、ELM327を使ったアプリの技術記事も書きましたが、今回はマルチプラットフォーム開発ってことで、自ら勉強しながら学んだことを記事にしていこうと企んでいます。

今回も実際にアプリを組みながら、ざっくりとレイアウトを勉強していこう!の精神で記事を書いていきます。

なお、Flutterの説明については、以下を参考にしていただければと思います。

開発アプリについて

基本的なUIレイアウトについて学びたいと思ったときに、丁度いいのが電卓!
ということで、電卓アプリを作っていきたいと思います。

開発環境について

本件ではインストール手順等は説明しませんが、主に以下を利用していきます。

  • Android Studio
    本件で利用します。
    Flutter、Dartプラグインはあらかじめインストール済みにしておきましょう。
  • Xcode
    コードの記述はしませんが、シミュレータ動作に利用します。

セットアップ手順については、以下の公式サイトの手順がわかりやすいです。

https://flutter.dev/docs/get-started/install/macos

電卓アプリのプロジェクトの作成

新規プロジェクトの作成手順については、以下をご覧ください。

https://flutter.dev/docs/get-started/test-drive?tab=androidstudio

プロジェクトを開く

AndroidStudioを起動し、「Open an exixtring Android Studio project」を押下してください。

先程作られた「my_app_name」を選択して「Open」します。

プロジェクトが正常に開くと、以下の画面になります。

プロジェクト無事に開けたでしょうか。
では、早速アプリを動かしてみましょう!
大丈夫!このプロジェクトが出来たときに、サンプルとして動作するようになっているので、すぐに試すことが可能なのです!

サンプルコードを動かしてみる

まず、どの機種上で動かすかを決めましょう。
AndroidStudioのメニューに機種を選択するセレクトメニューがあるので、そこから選択します。今回は「iPhone11ProMax」のシミュレータを選択しました。AndroidStudioなのに、iPhoneのシミュレータが選択できるって何だか不思議な感じですよね・・・。

なお、iPhoneのシミュレータについては、XCodeからテストしたい機種のシミュレータをあらかじめ起動していないとメニュー上に表示されないようです。

「Open iOS Simulator」のメニューがあり、これでも起動しますが何が起動されるか分からないので(シミュレータ内の最新機種?)、テストしたい機種があれば、あらかじめ起動しておいてください。Androidも同様。
実機で確認する場合、USBデバッグ接続のみでOKです。

起動すると、こんな感じで出てくるはずです。
左がiPhone、そして右がAndroidで表示した画面になります。

さすがクロスプラットフォーム開発環境で作ったものなので、見た目はそっくりですね。Toolbarのタイトル表示のみ若干異なる(OSに即した)表示になっていますが、ほぼ同じになっています。

先に紹介したサンプルデモコードの内容は、以下となっております。

編集前コード
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

電卓アプリのイメージを決める

ここから、電卓アプリのレイアウトを作っていきましょう!
さて・・・、無事に作ることができるのか・・・?

電卓のイメージはこうだ!

ダークなイメージで作ろうと思う。
これを作るのが最終目的!

テーマを決める

今回のタイトルにもなっているテーマを決める!
レイアウトを決めたところで、今度は全体のテーマを決めます!
ここでいうテーマはデザイン全体のテーマのこと。
俗に言うダークテーマとか・・・。
今回は私のダークなイメージを反映したくダークテーマ一択にしようと思います。

まずは、プロジェクト作成時に組まれている以下のコードを見てみましょう。

main.dart
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

上記の赤文字のところが全体のテーマを決めているようです。
ここで気にするのが、primarySwatchですが、ここにテーマ色を設定することで、全体のテーマ色を簡単に決められるようですね。

例えば、primarySwatchに対してColors.amberを指定すると、以下のようになります。
(左:変更前、右:変更後)
全体の色が変更され、かつフォントも色に即したものになっていますね。
このように簡単にテーマが決められるってなんて楽なんでしょう・・・。

では、私的には、こんな明るいテーマは嫌なので、ブラックなテーマに切り替えましょう。
primarySwatchに対してColors.blackを指定したところ、ビルドエラーになってしまいました・・・。Flutterは私の野望を阻止しようとしているのでしょうか???
とにかく、エラーの内容を見てみましょう。

Build Result
The following _TypeError was thrown building MyApp(dirty):
type 'Color' is not a subtype of type 'MaterialColor'
The relevant error-causing widget was:
MyApp file:///Users/tomoakihirosawa/Documents/develop/my_app_name/lib/main.dart:4:10
When the exception was thrown, this was the stack:
0 MyApp.build (package:my_app_name/main.dart:14:31)
1 StatelessElement.build (package:flutter/src/widgets/framework.dart:4576:28)
2 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4502:15)
3 Element.rebuild (package:flutter/src/widgets/framework.dart:4218:5)
4 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2627:33)
…

うーん・・・、どうやら指定した色がマテリアルカラーじゃないよ!と言っているようです。むむむ・・・。
ってことで、それぞれのカラーのTypeを見てみたところ、こうなっていました。

さっきのAmberカラー
エラーになったブラック

上記のTypeを見てもらうとわかりますが、Typeが「MaterialColor」でないと駄目みたいです。
残念なことに、他のブラックシリーズを見ても、Typeが「MaterialColor」はありませんでした・・・。
ダークなテーマはできないのか・・・?

いやいや、そんなことありません!
Flutterには上記とは別にダークテーマが用意されています。
もちろん、その逆のライトテーマも。
設定は非常に簡単!

main.dart
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),  <ーーこれだけ
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

なんてことでしょう・・・。
こうも簡単にダークになりました。
ちなみに、右はライトテーマを設定しました。

本件では、ダークテーマ一択なので、上記のテーマをそのまま使いたいと思います。
しかし、自分でカラーテーマを決めたい場合はどうするか?
当然、その方法もありますので、紹介しますね!

オリジナルテーマを設定

本件を決めるうえで、以下のサイトを利用しています。
今回は、ちょっと青っぽいテーマを適用するとします。

Androidのリソース的には、以下のような色イメージ。

<!--?xml version="1.0" encoding="UTF-8"?-->
<resources>
  <color name="primaryColor">#00acc1</color>
  <color name="primaryLightColor">#5ddef4</color>
  <color name="primaryDarkColor">#007c91</color>
  <color name="primaryTextColor">#000000</color>
</resources>

画面のイメージ

上記を反映させたのが、以下になります。

main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Color(0xFF00acc1),
primaryColorLight: Color(0xFF5ddef4),
primaryColorDark: Color(0xFF007c91),
primaryTextTheme: TextTheme(headline6: TextStyle(color: Color(0xFF000000))),
floatingActionButtonTheme: FloatingActionButtonThemeData(backgroundColor: Color(0xFF00acc1)),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

画面イメージは以下。

意外なのは、ヘッダーのタイトルやフローティングアクションボタンは個別で定義しないといけないようです。本件での検証時では、PrimaryColorのみで反映されなかったのですが、もしかしたら一括で定義可能な設定があるのかもしれません。
要確認かな。

まとめ

今回は最初に決めるテーマに対して記事にしてみましたが、いかがでしたでしょうか。
深堀りすると、キリがないのでここまでとしますが、カスタマイズの余地は十分あるし、事前に決められているテーマもあるので、非常に敷居は低いのではと思います。

さて、テーマもダークテーマに決まったことですし、次回はWidgetを利用してレイアウトを作成していきたいと思います。