Flutter レイアウトの基本 ——Column 縦レイアウト#
Columnは縦方向に子ビューをレイアウトする Widget で、Rowに似ています。子ビューを満たしたい場合は、Expandedで子ビューを包むことができます。
Columnはスクロールできません(通常、Columnを使用する場合、子ビューの内容は親ビューの高さを超えてはいけません)。もし多くの子ビューがあり、スクロールが必要な場合は、ListViewを使用することをお勧めします。
横方向にレイアウトしたい場合は、Rowを使用します。
要素が 1 つだけの場合は、AlignまたはCenterを使用してレイアウトを考慮できます。
基本的な使用法#
Column の一般的なプロパティは以下の通りです:
- Column の一般的なプロパティ
- children: 子ビュー
- textDirection: 子ビューの水平方向のレイアウト方向
- TextDirection.ltr: 左から右
- TextDirection.rtl: 右から左
- verticalDirection: 子ビューの縦方向のレイアウト方向
- VerticalDirection.down: 上から下、デフォルトはこれ
- VerticalDirection.up: 下から上
- mainAxisSize: 子ビューが親ビューの縦方向に占めるサイズ
- MainAxisSize.min: 最小、これを設定した後に mainAxisAlignment を設定すると、表示効果はすべて start の効果と一致します
- MainAxisSize.max: 最大、デフォルトはこれで、親ビューのサイズに従います
- mainAxisAlignment: 子ビューの親ビュー上のレイアウト方法、縦方向のレイアウト
- MainAxisAlignment.spaceAround: 子ビュー間と子ビューから親ビューまでの距離に間隔があります
- MainAxisAlignment.center: すべての子ビューが中央に配置されます
- MainAxisAlignment.end: すべての子ビューが最後尾に配置されます
- MainAxisAlignment.spaceBetween: 子ビュー間に等しい間隔があり、親ビューとの間隔はありません
- MainAxisAlignment.spaceEvenly: 子ビュー間と子ビューから親ビューまでの距離に間隔があり、すべての間隔が等しいです
- MainAxisAlignment.start: すべての子ビューが最初に配置されます
- crossAxisAlignment: 子ビューの水平方向のレイアウト方法
- CrossAxisAlignment.start: 水平左寄せレイアウト
- CrossAxisAlignment.end: 水平右寄せレイアウト
- CrossAxisAlignment.center: 水平中央寄せレイアウト、デフォルトはこれ
- CrossAxisAlignment.stretch
- CrossAxisAlignment.baseline
次に一つずつ見ていきましょう:
textDirection の効果:#
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textDirection: TextDirection.ltr,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('TextDirection rtl'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
効果は以下の通りです:
効果からわかるように、textDirectionは子ビューの水平方向のレイアウト方向ですが、ここでcrossAxisAlignmentも同時に設定されていることに注意が必要です。設定しない場合、表示効果はCrossAxisAlignment.centerと一致し、textDirectionのみを設定しても効果はありません。興味がある方は自分で検証してみてください。
Ps:CrossAxisAlignment.centerの効果は、想像しているように画面全体の幅が中央に揃うわけではありません。実際には、最も長い子ビューの幅に合わせて中央に揃えられます。
verticalDirection の効果#
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
verticalDirection: VerticalDirection.down,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('VerticalDirection up'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
効果は以下の通りです:
mainAxisSize && mainAxisAlignment の効果#
mainAxisSize の効果:
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisAlignment spaceAround'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
効果は以下の通りです:
同様に、mainAxisAlignmentをspaceAroundに設定した場合、mainAxisSizeをminとmaxに設定した違いがわかります。maxを設定すると全画面に適応され、minを設定すると効果がありません。
mainAxisAlignment(mainAxisSizeがmaxの場合)の効果:
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisAlignment spaceEvenly'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
効果は以下の通りです:
図からわかるように、mainAxisSizeがmaxの場合、mainAxisAlignmentの異なる値の表示効果がわかります。
crossAxisAlignment の効果#
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
表示効果は以下の通りです:
上記から、crossAxisAlignmentの異なる値の表示効果がわかります。
ただし、値が baseline の場合、エラーが発生しました。エラーは
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ 次のアサーションがMyApp(dirty)のビルド中にスローされました: crossAxisAlignmentをCrossAxisAlignment.baselineで指定する場合はtextBaselineが必要です 'package:flutter/src/widgets/basic.dart': アサーションに失敗しました: line 4369 pos 15: 'crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null'
なぜでしょうか?エラーメッセージから推測すると、rossAxisAlignment.baselineを設定した場合、textBaselineプロパティを設定する必要があります。したがって、textBaselineプロパティを追加すれば解決できます。
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.alphabetic,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('TextBaseline alphabetic'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment baseline'),
FlutterLogo()
],
),
),
);
}
}
効果は以下の通りです:
textBaseline の異なる値にはどのような違いがあるのでしょうか?参考にしてくださいFlutter の TextBaseline 列挙型における alphabetic と ideographic の違いは何ですか
スクリーンショットは以下の通りです:
公式の注意点#
注意が必要な点:1#
Columnの子ビューにExpandedまたはFlexibleの子ビューがあり、さらにこのColumn Widget が別のColumn Widget やListViewまたは他の不定高さの Widget に配置されている場合、エラーが発生します。
検証コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.ideographic,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Column(
children: [Expanded(child: FlutterLogo())],
),
],
),
),
);
}
}
エラーは以下の通りです:
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ レイアウトを実行中に次のアサーションがスローされました: RenderFlexの子は非ゼロのフレックスを持っていますが、受信した高さの制約は無制限です。
エラーの原因は:
Expandedを使用する場合、親ビューの高さが固定されている必要があります。Expandedで包まれた子ビューは親ビューの領域を埋める必要があります。しかし、ColumnやListViewなどの可動ビューにネストされている場合、親ビューの高さは固定されていないため、Expandedは埋めることができません。
解決策:
外側がColumnの場合、内側のColumnをExpandedで包むことができます。これにより、内側のColumn Widget に外側のColumn Widget の高さを埋めるように指示することになり、高さが確定します。
コードは以下の通りです:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.ideographic,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Expanded(
child: Column(
children: [Expanded(child: FlutterLogo())],
),
),
],
),
),
);
}
}
外側がListViewや他の可動ビューの場合、可動ビューの内容の高さは不明であるため、なぜ不定高さのビューが外側にあるのかを考慮する必要があります。この場合、Column Widget の子ビューにExpandedやFlexibleを使用することは通常避けるべきです。
注意が必要な点:2#
Rowと同様に、子ビューの内容が親ビューの領域を超えた場合、Flutter はデバッグモードで黄色の警告を表示します。効果は以下の通りです:
解決策:
Columnの代わりにListViewを使用して、子ビューの内容をスクロール可能にします。