tl;dr
-
Align
を使うと大体いい感じになる -
Align
がなぜか適用されないとき-
Column
ならColumn
をIntrinsicWidth
で囲む -
Row
ならRow
をIntrinsicHeight
で囲む
-
これはなに
flutterで画面を作るときによく使うウィジェット、Column
, Row
のプロパティにcrossAxisAlignment
というのがありますね
楽にchildrenの位置を指定できるのですが、たまに「子要素一個一個に対して別々に寄せる方向を指定したい」みたいな場合もあります。
これを解消するときの方法とハマりポイントを説明します。
Align
を使おう
このような場合、以下のような感じで書くと実現できます。
...
Row(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('1'),
),
),
Expanded(
child: Container(
height: double.infinity,
color: Colors.blue,
child: const Center(
child: Text('2'),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('3'),
),
),
],
)
...
まあ書いてあるのをそのままって感じですが、1と3のContainer
をAlign
で囲んでalignment
を指定するだけですね。
非常にシンプルです。
おや、Rowの様子が‥
さて、Alignの使い方を覚えたあなたのもとにデザイナーさんがやってきてこう言います
「次こんな感じで作って欲しいんですけど~」
「あ、2番の箱の高さは固定で300でお願いします!」
「了解です!すぐ終わりますよ~」(Rowを画面の上に寄せたいからColumnでRowを囲って、あとはRowの子要素をAlign
で囲むだけでしょ そーれ)
...
Column(
children: [
Row(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('1'),
),
),
Expanded(
child: Container(
height: 300,
color: Colors.blue,
child: const Center(
child: Text('2'),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('3'),
),
),
],
)
],
),
...
「...あれ、なんでAlign効かないのこれ」
と、こういう場面ではAlignを使ってもいい感じになりません。
Intrinsic〇〇を使おう
Align
がうまくいかないのは、Flutterの描画の仕組み的に仕方ないからです。
Flutterでは画面レイアウトを計算する際、親要素が子要素に、子要素が取れる最大・最小のサイズを渡します。すると、子要素は自身のサイズを計算し、その結果を親に返します。
これを再帰的に繰り返していくことでレイアウトを作成していきます。
このような手法を採用しているお陰で計算量は少なく済むようですが、代わりに制約も大きくなってしまっているらしいです。
上で紹介したようなシチュエーションでAlign
がうまくいかないのもこれが原因らしいです。
このようなときに使えるのがIntrinsicHeight
、IntrinsicWidth
というウィジェットになります。
言語能力がないのでうまく説明できませんが、これを使えばいい感じになります。やったね。
ということで、IntrinsicHeight
を使って修正したプログラムが以下になります。
...
Column(
children: [
IntrinsicHeight(
child: Row(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('1'),
),
),
Expanded(
child: Container(
height: 300,
color: Colors.blue,
child: const Center(
child: Text('2'),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: 50,
height: 50,
color: Colors.amber,
child: const Text('3'),
),
),
],
),
)
],
),
...
実際に動かしてみるとこんな感じ、ちゃんとデザイン通りになっていますね
まとめ
Flutterの画面描画の仕組みめんどくさいね