今是昨非

今是昨非

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

Flutter佈局基礎——Column豎直佈局

Flutter 佈局基礎 ——Column 豎直佈局#

Column- 是豎直方向佈局子視圖的 Widget,和Row相似,如果想要子視圖充滿,可使用Expanded把子視圖包括起來。

Column不能滑動(通常來說使用Column時,子視圖內容不能超過父視圖的高度),如果真的有很多子視圖,需要滑動的時候,建議使用ListView

如果想要橫向佈局,使用Row

如果只有一個元素,可考慮使用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的情況下,設置mainAxisSizemin
max的不同,設置max可以看到按照全螢幕來適配,設置min則無效果。

mainAxisAlignment(mainAxisSizemax時) 的效果:

代碼如下:


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 ╞═══════════════════════════════════════════════════════════ The following assertion was thrown building MyApp(dirty): textBaseline is required if you specify the crossAxisAlignment with CrossAxisAlignment.baseline 'package:flutter/src/widgets/basic.dart': Failed assertion: 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()
          ],
        ),
      ),
    );
  }
}

效果如下:

image

textBaseline 的不同取值有什麼不同呢?參考What is the difference between alphabetic and ideographic in Flutter's TextBaseline enum

截圖如下:

image

官方提示需要注意的點#

需要注意的:一#

Column的子視圖中,有Expanded或者Flexiable的子視圖,而且這個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 ╞═════════════════════════════════════════════════════════ The following assertion was thrown during performLayout(): RenderFlex children have non-zero flex but incoming height constraints are unbounded.

報錯的原因是:

當使用Expanded時,需要的是父視圖的高度固定,被Expanded包括的子視圖填充父視圖的區域。而如果嵌套了Column或者ListView或其他可滑動視圖時,父視圖的高度是不固定的,此時Expanded也就無法填充了。

解決辦法:

當外層是Column時,可以使用Expanded把內層的Column包括起來,這樣,相當於告訴內層的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 的子視圖還會有ExpandedFlexible這種情況出現,通常解決方法是移除內層子視圖的ExpandedFlexible

需要注意的:二#

Row類似,當子視圖內容超出了父視圖區域時,Flutter 在 Debug 模式下,會顯示黃色的提示。效果如下:

image

解決辦法:

考慮使用ListView來代替Column,使子視圖內容可滑動。

參考#

Column Dev Doc
Flutter 免費視頻第三季 - 佈局

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。