ゲームデザイン部エンジニア中村です。リモートワーク中は昼飯自炊勢です。
前回はFlutterによるアプリ開発<導入編>をお送りしました。今回はFlutterのウィジェットを実際に並べていき、アプリらしい画面を整えていきましょう。
そのためには、Flutterの深層までの理解は必要ありません。この記事ではレイアウトを作成するための知識だけを提供します。一部のソースコードについてはおまじないと思っていただければ問題ありません。エンジニア初心者やデザイナの方でもコピー&ペーストするだけでレイアウトが作れるようにしてきます。
ウィジェットレイアウトの基本
初期生成ファイルの理解
まずはプロジェクト生成時に自動で作られたファイルの内容をおおよそ理解しましょう。
ここでは自動生成のコメントをすべて削除した状態にしています。また、ここで編集するファイルはlib/main.dart
のみです。その中でどのようにウィジェットを指定し、テキストやボタンを表示しているのか見ていきましょう。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@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: Colors.blue
です。これはFlutterに事前に用意されたテーマカラーを指定することでヘッダやボタンなどの基本色を指定することができます。Colors.green
やColors.cyan
などが用意されています。
また、MyHomePage(title: 'Flutter Demo Home Page')
の箇所ではヘッダに表示するタイトルを指定しています。ここも自由に指定すると良いでしょう。
上記2箇所について、今回は以下のように編集しました。
~~
primarySwatch: Colors.green,
~~
home: MyHomePage(title: 'Dashboard')
続いて、ウィジェットのレイアウトをしている箇所です。
Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'右下のボタンをクリック',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
Scaffold
は工事などに利用する「足場」という意味で、足場を組んでから内部の部品を組み立てていくというイメージです。Flutter/Dartによるコードの構造として、ウィジェット名()
としたときの()
の中に更にウィジェットが記述される形になっています。
Center
についてはchild:
に対してウィジェットを指定するため、これがそのまま親子関係になります。Column
は複数の子要素を持つことからchildren:
に対してウィジェットの配列を指定します。
FloatingActionButton
は右下に表示されているボタンです。これはScafold
に対して1つだけ指定することができるウィジェットであり、Column
やCenter
などの子要素になることはありません。
ハンズオンでレイアウトする
CenterとColumnの基本レイアウト構造
それではソースコードを簡単に編集していきましょう。まずは見やすくするためにScafold
の内部をCenter
とその中のText
だけにします。
Scaffold(
body: Center(
child: Text(
'これはテストです',
),
),
);
ヘッダもフローティングボタンもなくなり、中央にテキストが表示されるだけとなりました。これにより、Center
は子要素を中央に表示するウィジェットであるとわかると思います。
続いて、Center
をColumn
に書き換え、Text
を3つに増やしましょう。このとき、Column
はchild
ではなくchildren
の指定となります。
Scaffold(
body: Column(
children: <Widget> [
Text('これはテストです'),
Text('これはテストです'),
Text('これはテストです'),
]
)
),
);
左上にテキストが3つ並ぶ形となりました。Column
はchildren
に指定された子要素を縦に並べるウィジェットです。現状では中央に寄せる設定をしていないためこのような表示になります。
ここでColumn
内部を以下のように書き換えましょう。重要なのはmainAxisAlignment
とcrossAxisAlignment
です。
Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget> [
Text('これはテストです'),
Text('これはテストですテストです'),
Text('これはテストですテストですテストです'),
]
)
),
);
テキストが中央に表示され、長さの違うテキストは右寄せになっています。これがmainAxisAlignment
とcrossAxisAlignment
の効果です。以下の画像にその効果を図示します。
緑の矩形がColumn
自身を表します。縦方向には親要素を満たすようになりますが、横方向には子要素の最大幅に合わせます。
この中でMainAxis
とは、Column
の場合子要素を積み重ねる縦方向の意味になります。つまり、MainAxisAlignment.center
とすることで青い矩形で示した子要素のまとまりを縦方向に対して中央に配置することができます。
そして、CrossAxis
はそれに直交する方向となるため赤い矩形で示した子要素自体がそれぞれ横方向に整列することになります。今回はそれぞれのテキストの長さが異なり、CrossAxisAlignment.end
としたことから最も横幅が長い子要素に合わせて右揃えとなっています。同様に、start
とすれば左揃え、center
とすれば中央揃えを表現できます。
Column
は縦に積み重ねるウィジェットですが、横に並べるウィジェットとしてRow
も存在します。Row
の場合には要素を横並びにするため、MainAxis
は横方向となり、CrossAxis
は縦方向となります。Column
とRow
はレイアウトを作成する上で頻出ウィジェットですのでMainAxis
とCrossAxis
を覚えておきましょう。
リストを表示する
SNSやショッピングアプリなどではリストビューがよく使われていると思います。Flutterではそういったリストを表示するためのウィジェットが用意されています。以下のようにコードを書き換えてみましょう。
Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
ListTile(
leading: Icon(Icons.person),
title: Text("人物アイコン"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.mail),
title: Text("メールアイコン"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.map),
title: Text("地図アイコン"),
onTap: () {},
)
],
),
)
);
ListView
ウィジェットのchildren
に対して子要素を指定するとそれだけでリストを表示することができます。このときListTile
ウィジェットを利用すると、アイコンとテキストの組み合わせというよく見るリストを作ることができます。onTap
を指定することでそれぞれのリスト項目をタッチした際の処理も追加できます。
また、ListView
の親要素となっているPadding
ウィジェットはCSS
などでも見られるpadding
を意味し、レイアウトでは頻繁に使われるため覚えておきましょう。
続いて、同様のリスト表示であるグリッドビューを試します。様々なレイアウト方式で、コレクションビューやタイルビューと呼ばれるリスト表示です。以下のようにコードを編集しましょう。
Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
children: [
ListTile(
leading: Icon(Icons.person),
title: Text("人物アイコン"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.mail),
title: Text("メールアイコン"),
onTap: () {},
),
ListTile(
leading: Icon(Icons.map),
title: Text("地図アイコン"),
onTap: () {},
)
],
),
)
);
GridView.count
でグリッドを作成し、children
に子要素を指定します。今回はListView
と同じListTile
を指定しているのでアイコンとタイトルの表示のみです。それでもグリッドビューらしく自動で矩形表示が行われます。GridView.count
としてウィジェットを指定すると、crossAxisCount
に横方向に要素をいくつ並べるかを指定します。タブレットなどの大きい端末では大きい数値にすると見やすくなるでしょう。
データからリストを作る
ショッピングアプリは表示する商品情報を常にサーバから取得しなければなりません。そのためには、取得したデータからレイアウトを表示すべきです。今回の例ではリストに表示する子要素をサーバから取得し、ListTile
のタイトルに反映されなければなりません。これを実現するために、まずは用意されたデータからListTile
を生成してみましょう。
// 最上部2行目付近
const datas = [
{'icon': Icons.person,'title': "人物アイコン",},
{'icon': Icons.mail,'title': "メールアイコン",},
{'icon': Icons.map,'title': "地図アイコン",},
{'icon': Icons.map,'title': "地図アイコン",},
{'icon': Icons.map,'title': "地図アイコン",},
{'icon': Icons.play_arrow,'title': "再生ボタン",},
{'icon': Icons.photo_sharp,'title': "写真アイコン",},
];
~~~~
Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: datas.map[1]e) =>
ListTile(leading: Icon(e['icon']),
title: Text(e['title']),
onTap: () {},
)
).toList(),
),
… Continue reading.toList(),
);
}
},
),
)
);
上記画像のように天気の表示ができたでしょうか。HTTP通信などで時間がかかる処理を待つ場合にはFutureBuilder
を使用します。これはfuture
に指定した処理が終わるのを待ってウィジェットを表示することができます。
builder
の中でif(!snapshot.hasData)
という書き方をしていますが、これはFutureBuilder
が待っている間、データの取得ができていないため代わりとなるウィジェットを表示しています。今回はCircularProgressIndicator
を指定し円形の読込中マークを表示しました。データの読み込みが完了するとその結果がsnapshot.data
に渡されますので、API結果のJSON文字列をデコードし、得られたデータからListTile
ウィジェットを作成しています。せっかくなので天気マークはAPIで得られる画像を利用しています。
まとめ
今回はFlutterの基本的なレイアウト構造であるCenter
/Column
と、様々なアプリでよく利用されるListView
とGridView
について説明しました。さらにHTTP通信結果をレイアウトに反映する方法について記述しましたので、これだけでも簡単なSNSやチャットツールなどを作ることができるかと思います。
Flutterにはまだまだたくさんのウィジェットがありますので、やってみたいレイアウトなどがあれば公式ドキュメントのウィジェット一覧が非常に参考になるのでご一読ください。
次回はFlutterとFirebaseの連携についてまとめたいと思います。
References
↑1 | e) =>
ListTile(leading: Icon(e['icon']),
title: Text(e['title']),
onTap: () {},
)
).toList(),
),
)
);
ソースコード上部の
このようにすることで与えられたデータから一覧を表示することができます。 それでは実際にサーバからデータを取得し表示してみましょう。今回は天気予報APIをお借りして東京都の天気を取得し一覧表示します。 まずはHTTP通信できるようにしなければなりません。以下のように
これでhttpプラグインが利用できるようになったので、
プラグインを利用するために書き換える箇所は以上です。それでは天気予報APIから東京都の天気を取得して表示してみます。以下のような書き換えを行います。
|
---|