フューチャー技術ブログ

Flutterレイアウト入門

Dart/Flutter連載の4記事目は、Flutterでの画面レイアウトの入門です。

Flutterを触ったことがない方にも「こんな感じで画面が作れるんだな」というイメージがつくようお伝えできればと思います。また、私がつまずいたWidgetのサイズ調整についても記載します。

Scaffold Widgetでベースとなるレイアウト構造を作成

FlutterのUIは、Widgetと呼ばれる部品を組み合わせて構築します。

画面のベースとなるレイアウト構造は、Scaffold Widgetで定義します。Scaffold Widgetには、appBar, body, botomNavigationBar等のプロパティが用意されており、それぞれに各Widgetを配置することでページ上部のAppBarや下部のナビゲーションバー等を簡単に配置できます。ページのメインコンテンツはbodyに定義します。

FAB(フローティングアクションボタン)と呼ばれる、スマホアプリのUIでよく見かける画面上の浮いているようなボタンについても、Scaffoldプロパティに用意されており、配置位置も簡単に定義できます。

Scaffold(
// AppBarの表示
appBar: AppBar(
title: Text('Flutter Demo'),
),
// ページのメインコンテンツ
body: ...,
// フッターのナビゲーション
bottomNavigationBar: ...,
// FAB(フローティングアクションボタン)
floatingActionButton: ...,
// FABの位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
)
フローティングアクションボタン

Widgetの配置に必須のColumn, Row Widget

Widgetを垂直に並べたいときは、Column Widget, 水平に並べたいときはRow Widgetを用いて配置します。メイン軸方向にどのように配置するかをmainAxisAlignmentプロパティで定義することができ、Column Widgetの場合は垂直方向、Row Widgetの場合は水平方向を指します。

body: Column(
mainAxisAlignment: MainAxisAlignment.start, // ここで配置の仕方を定義
children: <Widget>[
Card(color: Colors.yellow, child: Text('text01', style: TextStyle(fontSize: 40))),
Card(color: Colors.teal, child: Text('text01', style: TextStyle(fontSize: 40))),
Card(color: Colors.blueAccent, child: Text('text01', style: TextStyle(fontSize: 40))),
],
),
mainAxisAlignmentのstart,center,spaceEvenly設定の表示位置

意外と難しいWidgetのサイズ調整

Widgetのサイズは、直接的には指定せずにFlutterの自動調整に任せる場合も多く、異なる画面サイズにも柔軟に対応できて便利です。ただし明示的に指定していないため、意図しないサイズになってしまい戸惑うことがよくありました。

Widgetは、親Widgetから与えられた幅・高さの最大値と最小値をもとに、自身のサイズを決定します。
https://flutter.dev/docs/development/ui/layout/constraints

ただし、与えられた最大値・最小値からどのようにサイズを決定するかは、Widgetによって異なり、またWidgetの親子関係によっても変わってきます。各Widgetのサイズ決定については私自身まだ理解できていない部分が多くありますが、一例としてContainer Widgetのサイズの挙動について紹介します。Container Widgetは明示的にサイズを指定することも可能ですが、今回は直接的な指定は行わない場合のサイズの挙動・調整について記載します。

Container Widgetのサイズ

Container Widgetのサイズは、子要素の有無で違ってきます。

// 子要素がない場合は可能な限り最大サイズになる
body: Container(color: Colors.yellow),

// 子要素がある場合は子要素に応じた最小限のサイズになる
body: Container(color: Colors.yellow, child: Text('text')),
mainAxisAlignmentのstart,center,spaceEvenly設定の表示位置

Column Widgetでラップした際のContainer Widgetのサイズ

単純にColumn Widgetでラップした場合は、最小限のサイズになります。

さらにContainer WidgetをExpanded Widgetでラップすると、メイン軸方向(Columnの場合は垂直方向)にサイズを拡張してくれます。また、Expanded WidgetのFlexプロパティで、各Widgetを均等な大きさにしたり大きさの割合を指定できます。

垂直方向へのサイズの拡張は、Column WidgetのCrossAxisAlignmentプロパティにCrossAxisAlignment.stretchを指定することで可能になります。

// Column Widgetのみ
body: Column(
children: <Widget>[
Container(color: Colors.yellow, child: Text('text', style: TextStyle(fontSize: 40))),
Container(color: Colors.teal, child: Text('text', style: TextStyle(fontSize: 40))),
],
),

// Column Widget + Expanded Widget
body: Column(
children: <Widget>[
Expanded(flex: 1, child: Container(color: Colors.yellow, child: Text('text', style: TextStyle(fontSize: 40)))),
Expanded(flex: 1, child: Container(color: Colors.teal, child: Text('text', style: TextStyle(fontSize: 40)))),
],
),

// Column Widget + Expanded Widget + CrossAxisAlignment.stretch
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(flex: 1, child: Container(color: Colors.yellow, child: Text('text', style: TextStyle(fontSize: 40)))),
Expanded(flex: 1, child: Container(color: Colors.teal, child: Text('text', style: TextStyle(fontSize: 40)))),
],
),
mainAxisAlignmentのstart,center,spaceEvenly設定の表示位置

おわりに

Flutterの画面構築は細かく位置やサイズを指定しなくても、それらしいUIが作れるため、かなりスピーディーに開発ができる印象です。その反面、Widgetのサイズ決定の理解に難しさも感じています。

今回はレイアウト作成の導入的な記事となりましたが、より理解が進んだ後にWidgetのサイズ決定についても記事化できればと思います。

Dart/Flutter連載の4記事目でした。明日は真野さんのFlutterで技術ブログRSSリーダーの記事です。