1
0

More than 1 year has passed since last update.

ColumnとRowで、CrossAxisAlignmentより細かい単位で子要素の位置を指定するぞ~

Last updated at Posted at 2023-01-26

tl;dr

  • Alignを使うと大体いい感じになる
  • Alignがなぜか適用されないとき
    • ColumnならColumnIntrinsicWidthで囲む
    • RowならRowIntrinsicHeightで囲む

これはなに

flutterで画面を作るときによく使うウィジェット、Column, RowのプロパティにcrossAxisAlignmentというのがありますね
楽にchildrenの位置を指定できるのですが、たまに「子要素一個一個に対して別々に寄せる方向を指定したい」みたいな場合もあります。
これを解消するときの方法とハマりポイントを説明します。

Alignを使おう

例えば、以下のような画面を作りたいとします:
image.png

このような場合、以下のような感じで書くと実現できます。

...
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のContainerAlignで囲んでalignmentを指定するだけですね。
非常にシンプルです。

おや、Rowの様子が‥

さて、Alignの使い方を覚えたあなたのもとにデザイナーさんがやってきてこう言います

「次こんな感じで作って欲しいんですけど~」
「あ、2番の箱の高さは固定で300でお願いします!」

image.png

「了解です!すぐ終わりますよ~」(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効かないのこれ」

image.png

と、こういう場面ではAlignを使ってもいい感じになりません。

Intrinsic〇〇を使おう

Alignがうまくいかないのは、Flutterの描画の仕組み的に仕方ないからです。
Flutterでは画面レイアウトを計算する際、親要素が子要素に、子要素が取れる最大・最小のサイズを渡します。すると、子要素は自身のサイズを計算し、その結果を親に返します。
これを再帰的に繰り返していくことでレイアウトを作成していきます。
このような手法を採用しているお陰で計算量は少なく済むようですが、代わりに制約も大きくなってしまっているらしいです。
上で紹介したようなシチュエーションでAlignがうまくいかないのもこれが原因らしいです。

このようなときに使えるのがIntrinsicHeightIntrinsicWidthというウィジェットになります。
言語能力がないのでうまく説明できませんが、これを使えばいい感じになります。やったね。

ということで、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'),
                    ),
                  ),
                ],
              ),
            )
          ],
        ),
...

実際に動かしてみるとこんな感じ、ちゃんとデザイン通りになっていますね
image.png

まとめ

Flutterの画面描画の仕組みめんどくさいね

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0