👯‍♂️

【Flutter】Rowの子Widgetの高さを揃える方法

2022/06/12に公開1

はじめに

下記の画像みたいな

  1. それぞれの行の高さが同じなTable
  2. 背景色が崩れない

を実装したかったのですが、かなり沼にハマったのでメモを残します。

スクリーンショット => 成功画像

スクリーンショット => 失敗画像

解決したcode全文

実装コード => IntrinsicHeight
intrinsic_height.dart
import 'package:flutter/material.dart';

class IntrinsicHeightScreen extends StatelessWidget {
  const IntrinsicHeightScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _customTableRow("会社名", "hogehoge株式会社", isFirst: true),
            _customTableRow("住所", "東京都〇〇〇〇"),
            _customTableRow("電話番号", "03-××××-××××"),
            _customTableRow("沿革", """令和2年4月:hogehoge株式会社設立
令和2年8月:東京に○○店をオープン
令和3年6月:社名を○○へ変更
令和3年5月:売上高1000億円を達成
令和3年12月:マザーズへ株式を上場
          """),
          ],
        ),
      )),
    );
  }

  Widget _customTableRow(String firstText, String secondText, {bool isFirst = false}) {
    return Container(
      decoration: (isFirst)
          ? BoxDecoration(border: Border.all(color: const Color(0XFF000000)))
          : const BoxDecoration(
              border: Border(
                left: BorderSide(color: Color(0XFF000000)),
                right: BorderSide(color: Color(0XFF000000)),
                bottom: BorderSide(color: Color(0XFF000000)),
              ),
            ),
      child: IntrinsicHeight(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              flex: 1,
              child: ColoredBox(
                color: const Color(0XFFD3D3D3),
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0),
                  child: Text(firstText),
                ),
              ),
            ),
            const VerticalDivider(color: Color(0XFF000000), width: 1),
            Expanded(
              flex: 2,
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0),
                child: Text(secondText),
              ),
            )
          ],
        ),
      ),
    );
  }
}

解説

IntrinsicHeightクラスが解決してくれました。
https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html
このIntrinsicHeightクラスでRowクラスを囲むと高さを合わせてくれます。

IntrinsicHeightクラスの説明(Flutter.dev引用)

子ウィジェットのサイズを、子ウィジェット固有の高さにするウィジェットです。

このクラスは、例えば高さが無制限で使用できる場合に、無限に拡張しようとする子ウィジェットを、より合理的な高さにサイズ変更させたい場合に有用です。

このウィジェットが子に渡す制約は、親の制約に従います。そのため、制約が子の最大固有高さを満たすのに十分大きくない場合、子は他の場合より小さい高さを取得します。同様に、高さの最小制約が子の最大固有高さより大きい場合、子には他の場合より大きな高さが与えられます。

ただし下記の説明の通り、処理が重たいので使い過ぎには注意が必要みたいです。

IntrinsicHeightクラスの注意点(Flutter.dev引用)

このクラスは、最終的なレイアウトフェーズの前に投機的なレイアウトパスを追加するため、比較的高価です。可能な限り、このクラスの使用は避けてください。最悪の場合、このウィジェットは、ツリーの深さが O(N²) になるようなレイアウトになる可能性があります。

解決するまでの経緯

Tableクラスを使って実装を試みる

初めはTableクラスを使って実装しようとしてました。
少し調べて実装したところ、下のスクショ画像のように「お、うまくいくじゃん。Table便利!」って思ってたんです。

実装コード => Table
intrinsic_height.dart
import 'package:flutter/material.dart';

class IntrinsicHeightScreen extends StatelessWidget {
  const IntrinsicHeightScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Center(
            child: Table(
              columnWidths: const <int, TableColumnWidth>{
                0: FlexColumnWidth(1),
                1: FlexColumnWidth(2),
              },
              border: TableBorder.all(),
              children: [
                _customTableRow("会社名", "hogehoge株式会社"),
                _customTableRow("住所", "東京都〇〇〇〇"),
                _customTableRow("電話番号", "03-××××-××××"),
              ],
            ),
          ),
        ),
      ),
    );
  }

  TableRow _customTableRow(String firstText, String secondText) {
    return TableRow(
      children: [
        Container(
          padding: const EdgeInsets.all(8.0),
          color: const Color(0xFFD3D3D3),
          child: Text(firstText),
        ),
        Container(
          padding: const EdgeInsets.all(8.0),
          color: const Color(0xFFFFFFFF),
          child: Text(secondText),
        ),
      ],
    );
  }
}
スクリーンショット => Tableで実装

文字数が長いと背景色が...

上のコードにもう一行沿革というフィールドを追加して、長めのテキストを入れてみました。
すると...

スクリーンショット => 背景色が...

沿革の背景色が下まで広がってくれません。
今回の実装では複数行になるような長めのTextが入ることが予想されたので、このままではいけません。
どうにかして背景色を下まで広げられないかやってみました。

色々やってみるも結論Tableでは実装できず

せっかく便利なTableクラス。使いこなして必ずや実装を!と意気込みましたが、結局夢叶わずでした笑

Tableをやめ、ColumnRowで実装してみた(IntrinsicHeight)

結論IntrinsicHeightクラスでRowを囲んでやると綺麗になります!逆にIntrinsicHeightを使用せず、単にRowで実装しようとすると、先ほどのTableで実装したものの二の舞になります...

実装コード => IntrinsicHeight
intrinsic_height.dart
import 'package:flutter/material.dart';

class IntrinsicHeightScreen extends StatelessWidget {
  const IntrinsicHeightScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _customTableRow("会社名", "hogehoge株式会社", isFirst: true),
            _customTableRow("住所", "東京都〇〇〇〇"),
            _customTableRow("電話番号", "03-××××-××××"),
            _customTableRow("沿革", """令和2年4月:hogehoge株式会社設立
令和2年8月:東京に○○店をオープン
令和3年6月:社名を○○へ変更
令和3年5月:売上高1000億円を達成
令和3年12月:マザーズへ株式を上場
          """),
          ],
        ),
      )),
    );
  }

  Widget _customTableRow(String firstText, String secondText, {bool isFirst = false}) {
    return Container(
      decoration: (isFirst)
          ? BoxDecoration(border: Border.all(color: const Color(0XFF000000)))
          : const BoxDecoration(
              border: Border(
                left: BorderSide(color: Color(0XFF000000)),
                right: BorderSide(color: Color(0XFF000000)),
                bottom: BorderSide(color: Color(0XFF000000)),
              ),
            ),
      child: IntrinsicHeight(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              flex: 1,
              child: ColoredBox(
                color: const Color(0XFFD3D3D3),
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0),
                  child: Text(firstText),
                ),
              ),
            ),
            const VerticalDivider(color: Color(0XFF000000), width: 1),
            Expanded(
              flex: 2,
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0),
                child: Text(secondText),
              ),
            )
          ],
        ),
      ),
    );
  }
}
スクリーンショット => IntrinsicHeightで実装

おわりに

今回のいろいろ調べてみて初めてIntrinsicHeightというクラスを知りました。調べてもなかなか出てこず困ったので、誰かの助けになれば幸いです。
もし他の方法で

  1. こっちの方がコストが安いよ!
  2. Tableクラスでもこうすれば実装できるよ!

という方がいらっしゃれば是非ご教授ください!!!

GitHubで編集を提案

Discussion