Chapter 05

画面レイアウトを学ぼう

このチャプターでは、自分のプロフィールを表示する簡単な名刺アプリの作成を通して、画面レイアウトのカスタマイズ方法を学びます。
完成アプリの見た目はこんな感じ。前回と比べると、ちゃんとアプリっぽい見た目になったと思います。
ss-chapter05-01
完成イメージ

🏅 このチャプターで学べること

  1. 画面レイアウトでよく使うウィジェットの扱い方
  2. HotReload / HotRestart の違いと使い方

完成版のソースコードはGithubで公開しているので、適宜参照してください。
https://github.com/o-matsu/zenn_flutter_beginnersbook/tree/main/chapter05_MiCard

それでは早速始めましょう。

アプリの構成

画面レイアウト

まずは、先ほどの完成イメージがどの様に構成されているのか分解して見てみます。

シンプルな画面ですが、レイアウトについて次の要件が挙げられそうです。

  1. 画像やテキスト等のウィジェットを縦に並べる
  2. 縦に並べたウィジェットを上下左右中央に表示する

Widget のツリー構造

さらに分解図をもとに、どういう役割のウィジェットを、どう組み合わせるか、ツリー構造を考えてみます。

必要なウィジェットのイメージ図
大体のイメージができたら、それらを実現できる具体的なウィジェットを検討します。
私の場合はこんな手順で検討しています。

  1. 自分の知っている又は使ったことのあるウィジェットを利用する
  2. 公式カタログから探す
  3. ググってみる
  4. 誰かに聞く
☕️ ちょこっとアドバイス

実績を積んでノウハウが溜まれば、① だけで済む事が増え、早く開発できる様になります。
④ は周りに聞ける人がいない場合、Stack Overflow などを活用して web 上で質問するのも有効です。慣れないうちは ④ が早いと思いますが、まずは自分で調べる癖をつけておく事をお勧めします。何も調べずに聞きに行くばかりでは、そのうち愛想を尽かされます。

また、web での情報収集では、日本語だけでなく英語も積極的に参照するようにしましょう。情報量がぐんと増えて、課題解決が楽になります。テキストを読むだけなら、Google 翻訳を使えば簡単に理解できると思います。

あくまで私個人の進め方ですが、Flutter 開発に関わらず、色々な課題に応用できるので是非活用してください。

検討を経て、今回のアプリは、次のような構造で実現できる事が分かりました。

具体的なウィジェット構成
これで全体の構成が決まったので、それぞれのウィジェットを実装していきます。

ウィジェットの解説

特徴的な 3 つのウィジェットの扱い方を確認していきましょう。

Column (Row)

本アプリのレイアウト要件である①縦に並べる②上下左右中央揃えを同時に実現している頼もしいウィジェット。
今回使用したプロパティは次の2つ。

  1. children
    子要素を縦に並べて表示します。
  2. mainAxisAlignment
    縦方向のどの位置に子要素を配置するかを決めます。デフォルトは上揃え。今回は上下中央に表示するため、[center]としました。
    横方向の指定には、 crossAxisAlignment というプロパティを使いますが、こちらはデフォルトで中央揃えとなっているので、指定する必要はありません。

このウィジェットの特徴は、子要素を複数持つ事ができる点です。同じように、子要素を複数指定して、横並びに表示する場合には Row ウィジェットが利用できます。

main.dart
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [...]
)

Card + ListTile


Card + ListTile ウィジェット
この白い背景、よく見ると、角が丸くなっていたり、薄いシャドウが付いていたりと、細かいデザインが必要になりそうです。忠実に、背景を Container で囲って、Row でアイコンとテキストを横並びにして、プロパティを一つずつカスタムしていく、、という方法もありますが、地味に大変ですよね。
しかし、このカードっぽいデザインはよく使われるので、予め気の利いたウィジェットが用意されています。それが、Card と ListTile というウィジェット。

Card は白い背景にあたる部分で、デフォルトの状態で、角が丸くなっていたり、シャドウがついていたり、カスタマイズなしでそのまま実用できるものになっています。

ListTile は Card の子要素としてよく使われるウィジェットで、よくある画像と文字を組み合わせたリスト表示を簡単に実現できます。デフォルトで余白の設定等も入っており、とても便利です。

ListTile の表示パターン

実際のコードはこの様に使います。実にシンプルですね。

main.dart
Card(
  child: ListTile(
    leading: Icon(
      Icons.phone,
      color: Colors.teal,
    ),
    title: Text(...),
  ),
),

🍎 Icon ウィジェット

ListTile の leading プロパティで指定している Icon ウィジェットでは、好きなアイコンを選んで表示する事ができます。アイコンはこちらから探してください。
https://material.io/resources/icons/?style=baseline
また、アイコンの色や、大きさはプロパティから自由にカスタマイズする事も可能です。

TextStyle


お洒落フォント
こちらの目を引くお洒落なフォント。これは Text ウィジェットの style プロパティ内に TextStyle というウィジェットを配置して実現しています。
TextStyle ではフォントの他にも文字の大きさ、太さ、色などを細かく指定できます。
実際に今回記述した内容は以下の通り。

main.dart
Text(
  "O-matsu",
  style: TextStyle(
    fontFamily: "Pacifico",
    fontSize: 40.0,
    fontWeight: FontWeight.bold,
    color: Colors.white,
  ),
),

fontFamily でフォントを指定する為には、事前の準備が必要です。
手順としては、前回の画像表示と同様、予めプロジェクト内にデータを配置して、パスを設定します。

フォントデータ自体は Google が公開しているフリーフォントを利用しました。こちらのサイトから、好きなフォントの*.ttfファイルをダウンロードして、適当なフォルダに格納します。

https://fonts.google.com/

次に、pubspec.yaml内に次の様に記述します。(今回は fonts フォルダ配下に TTF ファイルを格納しました。)

pubspec.yaml
  fonts:
    - family: Pacifico
      fonts:
        - asset: fonts/Pacifico-Regular.ttf

実装のポイント

🖥 アプリのソースコード
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(MyApp()); // Statelessウィジェットの呼び出し
}

// Statelessウィジェットを定義
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.teal,
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircleAvatar(
              radius: 50.0,
              backgroundColor: Colors.white,
              backgroundImage: AssetImage("images/face.jpg"),
            ),
            Text(
              "O-matsu",
              style: TextStyle(
                fontFamily: "Pacifico",
                fontSize: 40.0,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
            Text(
              "FLUTTER DEVELOPER",
              style: TextStyle(...),
            ),
            SizedBox(
              height: 30.0,
              width: 150.0,
              child: Divider(
                color: Colors.teal[100],
              ),
            ),
            Card(
              margin: EdgeInsets.symmetric(
                vertical: 10,
                horizontal: 50,
              ),
              child: ListTile(
                leading: Icon(
                  Icons.phone,
                  color: Colors.teal,
                ),
                title: Text(
                  "+81 999 9999",
                  style: TextStyle(
                    fontFamily: "SourceSansPro",
                    fontSize: 20.0,
                    color: Colors.teal[900],
                  ),
                ),
              ),
            ),
            Card(...),
          ],
        ),
      ),
    );
  }
}

冗長な部分を一部省略しています。全て見たい場合は、Github を参照してください。

このコードのポイントは、StatelessWidget クラスです。
前回までは、main 関数の中に直接ウィジェットを実装していましたが、StatelessWidget の Build 関数内に実装する事で、Chapter.2で紹介した HotReload という強力な機能を利用できる様になります。

🔥 HotReload
コードの修正が即座に反映される機能。
修正部分だけを処理するので、コードの規模が大きくなっても処理速度には影響しないというのも大きなポイント。
通常 1 分程かかるような、再ビルドの待ち時間が 3 秒程になるので、開発効率が格段に向上します。
ちなみに HotRestart という機能もあり、データをリセットしたい場合にはこちらを利用します。

まとめ

画面レイアウトの作り方については大体理解できたように思います。
Widget の考え方が、HTML/CSS と近いので、web 開発の経験がある方はすぐに習得できそうですね。
今回は全く動きのない静的な画面を作成したので、次回は動的に表示が変化するアプリに挑戦します。