フューチャー技術ブログ

Flutterレイアウト入門

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

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

Scaffold ウィジェットでベースとなるレイアウト構造を作成

FlutterのUIは、ウィジェットと呼ばれる部品を組み合わせて構築します。

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

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

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

ウィジェットの配置に必須のColumn, Row ウィジェット

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

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設定の表示位置

意外と難しいウィジェットのサイズ調整

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

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

ただし、与えられた最大値・最小値からどのようにサイズを決定するかは、ウィジェットによって異なり、またウィジェットの親子関係によっても変わってきます。各ウィジェットのサイズ決定については私自身まだ理解できていない部分が多くありますが、一例として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 ウィジェットでラップした際のContainer Widgetのサイズ

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

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

垂直方向へのサイズの拡張は、Column ウィジェットの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が作れるため、かなりスピーディーに開発ができる印象です。その反面、ウィジェットのサイズ決定の理解に難しさも感じています。

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

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