Flutter 布局多個widgets

2020-08-27 14:46 更新

垂直和水平放置多個widget

最常見的布局模式之一是垂直或水平排列widget。您可以使用行(Row)水平排列widget,并使用列(Column)垂直排列widget。

重點是什么?

  • 行和列是兩種最常用的布局模式。
  • 行和列都需要一個子widget列表。
  • 子widget本身可以是行、列或其他復(fù)雜widget。
  • 您可以指定行或列如何在垂直或水平方向上對齊其子項
  • 您可以拉伸或限制特定的子widget.
  • 您可以指定子widget如何使用行或列的可用空間.


要在Flutter中創(chuàng)建行或列,可以將一個widget列表添加到Row 或Column 中。 同時,每個孩子本身可以是一個Row或一個Column,依此類推。以下示例顯示如何在行或列內(nèi)嵌套行或列。

此布局按行組織。該行包含兩個孩子:左側(cè)的一列和右側(cè)的圖片:

screenshot with callouts showing the row containing two children: a column and an image.

左側(cè)的Column widget樹嵌套行和列。

diagram showing a left column broken down to its sub-rows and sub-columns

您將在嵌套行和列中實現(xiàn)一些Pavlova(圖片中的奶油水果蛋白餅)的布局代碼

注意:行和列是水平和垂直布局的基本、低級widget - 這些低級widget允許最大化的自定義。Flutter還提供專門的,更高級別的widget,可能足以滿足您的需求。 例如,您可能更喜歡ListTile而不是Row,ListTile是一個易于使用的小部件,具有前后圖標(biāo)屬性以及最多3行文本。您可能更喜歡ListView而不是列,ListView是一種列狀布局,如果其內(nèi)容太長而無法適應(yīng)可用空間,則會自動滾動。 有關(guān)更多信息,請參閱通用布局widget。


對齊 widgets

您可以控制行或列如何使用mainAxisAlignment和crossAxisAlignment屬性來對齊其子項。 對于行(Row)來說,主軸是水平方向,橫軸垂直方向。對于列(Column)來說,主軸垂直方向,橫軸水平方向。

diagram showing the main axis and cross axis for a rowdiagram showing the main axis and cross axis for a column

MainAxisAlignment 和CrossAxisAlignment 類提供了很多控制對齊的常量.

注意: 將圖片添加到項目時,需要更新pubspec文件才能訪問它們 - 此示例使用Image.asset顯示圖像。 有關(guān)更多信息,請參閱此示例的pubspec.yaml文件, 或在Flutter中添加資源和圖像。如果您使用的是網(wǎng)上的圖片,則不需要執(zhí)行此操作,使用Image.network即可。

在以下示例中,3個圖像中的每一個都是100像素寬。渲染盒(在這種情況下,整個屏幕)寬度超過300個像素, 因此設(shè)置主軸對齊方式為spaceEvenly,它會在每個圖像之間,之前和之后均勻分配空閑的水平空間。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      new Image.asset('images/pic1.jpg'),
a row showing 3 images spaced evenly in the row

Dart code: main.dartImages: imagesPubspec: pubspec.yaml

列的工作方式與行相同。以下示例顯示了一列,包含3個圖片,每個圖片高100個像素。 渲染盒(在這種情況下,整個屏幕)的高度大于300像素,因此設(shè)置主軸對齊方式為spaceEvenly,它會在每個圖像之間,上方和下方均勻分配空閑的垂直空間。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Column(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      new Image.asset('images/pic1.jpg'),

Dart code: main.dartImages: imagesPubspec: pubspec.yaml

a column showing 3 images spaced evenly in the column

*注意: 如果布局太大而不適合設(shè)備,則會在受影響的邊緣出現(xiàn)紅色條紋。例如,以下截圖中的行對于設(shè)備的屏幕來說太寬:

a row that is too wide, showing a red string along the right edge

通過使用Expanded widget,可以將widget的大小設(shè)置為適和行或列,這在下面的調(diào)整 widgets 部分進(jìn)行了描述。


調(diào)整 widget

也許你想要一個widget占據(jù)其兄弟widget兩倍的空間。您可以將行或列的子項放置在Expandedwidget中, 以控制沿著主軸方向的widget大小。Expanded widget具有一個flex屬性,它是一個整數(shù),用于確定widget的彈性系數(shù),默認(rèn)彈性系數(shù)是1。

例如,要創(chuàng)建一個由三個widget組成的行,其中中間widget的寬度是其他兩個widget的兩倍,將中間widget的彈性系數(shù)設(shè)置為2:

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      new Expanded(
        child: new Image.asset('images/pic1.jpg'),
      ),
      new Expanded(
        flex: 2,
        child: new Image.asset('images/pic2.jpg'),
      ),
      new Expanded(

a row of 3 images with the middle image twice as wide as the others

Dart code: main.dartImages: imagesPubspec: pubspec.yaml

要修復(fù)上一節(jié)中的示例:其中一行有3張圖片,行對于其渲染框太寬,并且導(dǎo)致右邊出現(xiàn)紅色條中的問題,可以使用Expanded widget來包裝每個widget。 默認(rèn)情況下,每個widget的彈性系數(shù)為1,將行的三分之一分配給每個小部件。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      new Expanded(
        child: new Image.asset('images/pic1.jpg'),
      ),
      new Expanded(
        child: new Image.asset('images/pic2.jpg'),
      ),
      new Expanded(


聚集 widgets

默認(rèn)情況下,行或列沿著其主軸會盡可能占用盡可能多的空間,但如果要將孩子緊密聚集在一起,可以將mainAxisSize設(shè)置為MainAxisSize.min。 以下示例使用此屬性將星形圖標(biāo)聚集在一起(如果不聚集,五張星形圖標(biāo)會分散開)。

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var packedRow = new Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.black),
        new Icon(Icons.star, color: Colors.black),
      ],
    );

  // ...
}

a row of 5 stars, packed together in the middle of the row


嵌套行和列

布局框架允許您根據(jù)需要在行和列內(nèi)部再嵌套行和列。讓我們看下面紅色邊框圈起來部分的布局代碼:

a screenshot of the pavlova app, with the ratings and icon rows outlined in red

紅色邊框部分的布局通過兩個行來實現(xiàn)。評級行包含五顆星和評論數(shù)量。圖標(biāo)行包含三列圖標(biāo)和文本。

評級行的widget樹:

a node tree showing the widgets in the ratings row

該ratings變量創(chuàng)建一個包含5個星形圖標(biāo)和一個文本的行:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    //...

    var ratings = new Container(
      padding: new EdgeInsets.all(20.0),
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          new Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
            ],
          ),
          new Text(
            '170 Reviews',
            style: new TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w800,
              fontFamily: 'Roboto',
              letterSpacing: 0.5,
              fontSize: 20.0,
            ),
          ),
        ],
      ),
    );
    //...
  }
}

提示: 為了最大限度地減少由嵌套嚴(yán)重的布局代碼導(dǎo)致的視覺混淆,可以在變量和函數(shù)中實現(xiàn)UI的各個部分。

評級行下方的圖標(biāo)行包含3列; 每個列都包含一個圖標(biāo)和兩行文本,您可以在其widget樹中看到:

a node tree for the widets in the icons row

該iconList變量定義了圖標(biāo)行:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    // ...

    var descTextStyle = new TextStyle(
      color: Colors.black,
      fontWeight: FontWeight.w800,
      fontFamily: 'Roboto',
      letterSpacing: 0.5,
      fontSize: 18.0,
      height: 2.0,
    );

    // DefaultTextStyle.merge可以允許您創(chuàng)建一個默認(rèn)的文本樣式,該樣式會被其
    // 所有的子節(jié)點繼承
    var iconList = DefaultTextStyle.merge(
      style: descTextStyle,
      child: new Container(
        padding: new EdgeInsets.all(20.0),
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            new Column(
              children: [
                new Icon(Icons.kitchen, color: Colors.green[500]),
                new Text('PREP:'),
                new Text('25 min'),
              ],
            ),
            new Column(
              children: [
                new Icon(Icons.timer, color: Colors.green[500]),
                new Text('COOK:'),
                new Text('1 hr'),
              ],
            ),
            new Column(
              children: [
                new Icon(Icons.restaurant, color: Colors.green[500]),
                new Text('FEEDS:'),
                new Text('4-6'),
              ],
            ),
          ],
        ),
      ),
    );
    // ...
  }
}

該leftColumn變量包含評分和圖標(biāo)行,以及描述Pavlova的標(biāo)題和文字:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    //...

    var leftColumn = new Container(
      padding: new EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0),
      child: new Column(
        children: [
          titleText,
          subTitle,
          ratings,
          iconList,
        ],
      ),
    );
    //...
  }
}

左列放置在容器中以約束其寬度。最后,用整個行(包含左列和圖像)放置在一個Card內(nèi)構(gòu)建UI:

Pavlova圖片來自 Pixabay ,可以在Creative Commons許可下使用。 您可以使用Image.network直接從網(wǎng)上下載顯示圖片,但對于此示例,圖像保存到項目中的圖像目錄中,添加到pubspec文件, 并使用Images.asset。 有關(guān)更多信息,請參閱在Flutter中添加Asserts和圖片。

body: new Center(
  child: new Container(
    margin: new EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
    height: 600.0,
    child: new Card(
      child: new Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          new Container(
            width: 440.0,
            child: leftColumn,
          ),
          mainImage,
        ],
      ),
    ),
  ),
),

Dart code: main.dartImages: imagesPubspec: pubspec.yaml

提示: Pavlova示例在廣泛的橫屏設(shè)備(如平板電腦)上運行最佳。如果您在iOS模擬器中運行此示例, 則可以使用Hardware > Device菜單選擇其他設(shè)備。對于這個例子,我們推薦iPad Pro。 您可以使用Hardware > Rotate將其方向更改為橫向模式 。您還可以使用Window > Scale更改模擬器窗口的大?。ú桓倪壿嬒袼氐臄?shù)量)

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號