今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

Flutterレイアウトの基本——Column縦方向レイアウト

Flutter レイアウトの基本 ——Column 縦レイアウト#

Columnは縦方向に子ビューをレイアウトするウィジェットで、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()
          ],
        ),
      ),
    );
  }
}

効果は以下の通りです:

image image

効果からわかるように、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()
          ],
        ),
      ),
    );
  }
}

効果は以下の通りです:

image image

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()
          ],
        ),
      ),
    );
  }
}

効果は以下の通りです:

image image

同様に、mainAxisAlignmentspaceAroundに設定した場合、mainAxisSizeminmaxに設定した違いが見られます。maxを設定すると全画面に適応され、minを設定すると効果はありません。

mainAxisAlignmentmainAxisSizemaxの場合)の効果:

コードは以下の通りです:


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()
          ],
        ),
      ),
    );
  }
}

効果は以下の通りです:

image image image image image image

図からわかるように、mainAxisSizemaxの場合、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()
          ],
        ),
      ),
    );
  }
}

表示効果は以下の通りです:

image image image image image

上記から、crossAxisAlignmentの各異なる値の表示効果がわかります。

ただし、値が baseline の場合、エラーが発生します。エラーは

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ 次のアサーションがMyApp(dirty)のビルド中にスローされました: crossAxisAlignmentをCrossAxisAlignment.baselineで指定した場合、textBaselineが必要です 'package:flutter/src/widgets/basic.dart': アサーションに失敗しました:行4369 pos 15: 'crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null'

なぜでしょうか?エラーメッセージから推測すると、crossAxisAlignment.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()
          ],
        ),
      ),
    );
  }
}

効果は以下の通りです:

image

textBaseline の異なる値にはどのような違いがあるのでしょうか?参考にしてくださいFlutter の TextBaseline enum における alphabetic と ideographic の違いは何ですか

スクリーンショットは以下の通りです:

image

公式の注意点#

注意点 1#

Columnの子ビューにExpandedまたはFlexibleの子ビューがあり、さらにこのColumnウィジェットが別のColumnウィジェットやListView、または他の不定高さのウィジェットに配置されている場合、エラーが発生します。

検証コードは以下の通りです:


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で包まれた子ビューは親ビューの領域を埋める必要があります。しかし、ColumnListViewなどの可動ビューにネストされている場合、親ビューの高さは固定されていないため、Expandedは埋めることができません。

解決策:

外側がColumnの場合、内側のColumnExpandedで包むことができます。これにより、内側のColumnウィジェットに外側のColumnウィジェットの高さを埋めるように指示することになり、高さが確定します。

コードは以下の通りです:


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ウィジェットの子ビューにExpandedFlexibleを使用することは通常避けるべきです。

注意点 2#

Rowと同様に、子ビューの内容が親ビューの領域を超えた場合、Flutter はデバッグモードで黄色の警告を表示します。効果は以下の通りです:

image

解決策:

Columnの代わりにListViewを使用して、子ビューの内容をスクロール可能にします。

参考#

Column Dev Doc
Flutter 無料動画第 3 シーズン - レイアウト

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。