Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

李鑫鑫發表於2019-09-10

佈局

Flex佈局

Flutter中的Flex佈局和Web的Css類似。

在Flutter 中用於控制Flex佈局的有Row,Column,Expanded,Flexible,Spacer,Flex這些控制元件。

row水平佈局

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

屬性名 型別 預設值 說明
mainAxisAlignment MainAxisAlignment MainAxisAlignment.start 主軸的排列方式
mainAxisSize MainAxisSize MainAxisSize.max 主軸佔空間的大小
crossAxisAlignment CrossAxisAlignment CrossAxisAlignment.center 次軸的排列方式
textDirection TextDirection null 確定children在水平方向的擺放順序
verticalDirection VerticalDirection VerticalDirection.down 確定children在垂直方向的擺放順序
textBaseline TextBaseline null 文字基準線對齊

我們首先建立三個大小不一的Container


class LyoutRowDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("水平佈局"),
        ),
        body: Container(
          child: Row(
            children: <Widget>[
              Container(
                height: 100,
                width: 50,
                color: Colors.redAccent,
              ),
              Container(
                height: 50,
                width: 50,
                color: Colors.blueAccent,
              ),
              Container(
                color: Colors.black,
                height: 75,
                width: 75,
              )
            ],
          ),
        ));
  }
}
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

主軸排列方式MainAxisAlignment

子元素children的排列方式由這兩個屬性決定textDirection和verticalDirection。textDirection決定水平方向的排列方式TextDirection.ltr從左往右排列(把左當作起始位置),TextDirection.rtl從右往左排列(把右當作起始位置)。verticalDirection時水平方向的排列方式。當值為down(向下排列)時起始位置在頂部。當值為up(向上排列)是起始位置在底部。

  • start (預設值) 根據textDirection屬性排列方向。

    將children放置在主軸的起點

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • end

    根據textDirection屬性排列方向。

    將children放置在主軸的末尾

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • center

    根據textDirection屬性排列方向。

    將children放置在主軸的中心

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • spaceBetween

    根據textDirection屬性排列方向。

    將主軸方向上的空白區域均分,使得children之間的空白區域相等,首尾child都靠近首尾,沒有間隙

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • spaceAround

    根據textDirection屬性排列方向。

    將主軸方向上的空白區域均分,使得children之間的空白區域相等,但是首尾空白區域為一半

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • spaceEvenly

    根據textDirection屬性排列方向

    將主軸方向上的空白區域均分,使得children之間的空白區域相等,包括首尾空白區域

    textDirection屬性值為rtl時右圖

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

交叉軸的排列方式crossAxisAlignment

都以 mainAxisAlignment: MainAxisAlignment.start為例

  • start

    將子元素在交叉軸上起點對齊。設定verticalDirection為VerticalDirection.up右圖。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • end

    將子元素在交叉軸上末尾對齊。設定verticalDirection為VerticalDirection.up右圖。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • center

    將子元素在交叉軸上居中對齊。設定verticalDirection無改變。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • strentch

    將子元素在交叉軸上拉伸

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • baseline

    基準線對其適用於文字,我們首先建立文字的start。必須設定textBaseline屬性

    Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      textBaseline: TextBaseline.alphabetic,
      children: [
        Text(
          'Flutter',
          style: TextStyle(
            color: Colors.yellow,
            fontSize: 30.0
          ),
        ),
        Text(
          'Flutter',
          style: TextStyle(
              color: Colors.blue,
              fontSize: 20.0
          ),
        ),
      ],
    );
    複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

設定對齊方式為基準線

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "水平佈局",
      home: Scaffold(
          appBar: AppBar(
            title: Text("水平佈局"),
          ),
          body: Container(
            child: Row(
              children: <Widget>[
                Expanded(
                    child: Container(
                  decoration: BoxDecoration(
                    border: Border.all(
                      color: Colors.orange,
                      width: 8.0,
                    ),
                  ),
                  child: Row(
                    children: <Widget>[
                      Expanded(
                          child: RaisedButton(
                              onPressed: () {},
                              color: Colors.red,
                              child: Container(
                                child: Text(
                                  "紅色按",
                                ),
                              ))),
                      Container(
                        width: 50,
                          child: RaisedButton(
                        onPressed: () {},
                        color: Colors.green,
                        child: Text(
                          "綠",
                        ),
                      )),
                      Container(
                        width: 70,
                          child: RaisedButton(
                        onPressed: () {},
                        color: Colors.yellow,
                        child: Text(
                          "黃",
                        ),
                      )),
                    ],
                  ),
                )),
                RaisedButton(
                    onPressed: () {},
                    color: Colors.blue,
                    child: Container(
                      child: Text(
                        "藍色按鈕",
                      ),
                    )),
              ],
            ),
          )),
    );
  }
}

複製程式碼

橘黃色框和藍色按鈕佔據螢幕整個寬度

紅色按鈕隨意擴充套件 綠色設定50,黃色設定70

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

主軸佔用的空間mainAxisSize

mainAxisSize只有兩個值一個是min一個是max。預設是max。

當值為min時候。主軸縮緊,變為最小。

如圖:即使這時mainAxisAlignment:為MainAxisAlignment.spaceAround。主軸的長度仍為最小狀態

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Column垂直佈局

垂直佈局與水平佈局的屬性和方法一致,唯一需要注意的是textDirection(水平排列方式)和verticalDirection(垂直排列方式)

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "垂直佈局",
        home: Scaffold(
            appBar: AppBar(title: Text("垂直佈局")),
            body: Center(
              child: Column(
			   mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.end,
                children: <Widget>[
                  Text("1",style: TextStyle(fontSize: 100),),
                  Expanded(
                    child: Text("2"),
                  ),
                  Text("33333"),
                  Text("4")
                ],
              ),
            )));
  }
}

複製程式碼

為了便於觀看我們將1擴大

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flex佈局

我們檢視以下原始碼便於理解

可以看到RowColumn都繼承與Flex

class Row extends Flex 
class Column extends Flex   
複製程式碼

Row的建構函式可選命名引數(即{}包裹的引數)有8個。

傳入父級的super建構函式卻有9個,多出了direction: Axis.horizontal

Row({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[],
  }) : super(
    children: children,
    key: key,
    direction: Axis.horizontal,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
  );
}
複製程式碼

再Flex中屬性direction實際上時主軸的方向。且被@required標註。它是必選的。

Flex({
    Key key,
    @required this.direction,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.textBaseline,
    List<Widget> children = const <Widget>[],
  }) : assert(direction != null),
       assert(mainAxisAlignment != null),
       assert(mainAxisSize != null),
       assert(crossAxisAlignment != null),
       assert(verticalDirection != null),
       assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null),
       super(key: key, children: children);
複製程式碼

Expanded

上面的例子我們遇到從未見過的一個widget。很容易就可以看出。Expanded會忽略子元素的大小並強制自動擴充套件使主軸填充父級可用的空白區域。Expanded必須是Row、Column、Flex的children。

下面兩個例子便於更好理解:

//當子元素只有一個Expanded時
class LyoutExpanded extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Expanded"),
      ),
      body: Center(
        child: Container(
          child: Column(
            children: <Widget>[
              Expanded(
                child: Container(
                  color: Colors.blue,
                  width: 100,//這個被忽略掉了
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

class LyoutExpanded extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Expanded"),
      ),
      body: Center(
        child: Container(
          height: 50,//填充使主軸擴充套件至父級高度
          child: Column(
            children: <Widget>[
           Expanded(
                child: Container(
                  color: Colors.blue,
                  height: 100,//
                  width: 100,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Expanden還有一個屬性為flex預設值為1。代表權重。當子元素有多個Expanden。按照權重值佔據父級的高度(row時為水平寬度)

<Widget>[
    Expanded(
        flex: 2,
        child: Container(
            color: Colors.blue,
            width: 100,
        ),
    ),
    Expanded(
        child: Container(
            color: Colors.red,
            width: 100,
        ),
    ),
    Expanded(
        child: Container(
            color: Colors.yellow,
            width: 100,
        ),
    ),
],
複製程式碼

我們可以看到藍色的塊時紅和黃的二倍。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flexible

Flexible元件可以使Row、Column、Flex等子元件在主軸方向有填充可用空間的能力(例如,Row在水平方向,Column在垂直方向),但是它與Expanded元件不同,它不強制子元件填充可用空間。同樣Flexible元件必須是Row、Column、Flex等元件的children。

Flexiible 有屬性fit當屬性值FlexFit.tight時。Flexible和Expanded沒有區別。

從原始碼可以看出Expanded繼承與Flexible

且引用 上級建構函式傳入fit的值為FlexFit.tight。

class Expanded extends Flexible
//省區其他原始碼
    

const Expanded({
    Key key,
    int flex = 1,
    @required Widget child,
  }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
    
複製程式碼

在Column的children中傳入。屬性fit:為FlexFit.tight,按照權值填充空白區域。

<Widget>[
    Flexible(
        fit: FlexFit.tight,
        child: Container(
            color: Colors.blue,
            height: 100,
            width: 100,
        ),
    ),
    Flexible(
        fit: FlexFit.tight,
        flex: 2,
        child: Container(
            color: Colors.yellow,
            height: 100,
            width: 100,
        ),
    ),
],
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

一旦fit的值為FlexFit.loose(預設值)先按flex的值分主軸確定佔主軸的大小,再按child調整元素的高度。不強制拉伸

例如第一個為tight強制擴充套件。第二個為loose不強制擴充套件。

<Widget>[
    Flexible(
        fit: FlexFit.tight,
        child: Container(
            color: Colors.blue,
            height: 100,
            width: 100,
        ),
    ),
    Flexible(
        fit: FlexFit.loose,
        flex: 2,
        child: Container(
            color: Colors.yellow,
            height: 100,
            width: 100,
        ),
    ),
],
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

因為第二個為loose所以黃顏色的Container高度為100。而藍色Container的 fit值為 tight,他的大小和上一個例子一致。

Spacer

Spacer建立一個可以調整的空白區域,可用於調整Flex容器(Row或者Colum)中widget之間的間距。

一旦children裡面包含了Spacer。mainAxisAlignment的屬性值將起不到作用。Spacer已經佔據了所有額外的空間,因此沒有剩餘的空間可以重新分配。

Row(
    mainAxisAlignment: MainAxisAlignment.end,//起不到作用
    children: <Widget>[
        Container(
            height: 100,
            width: 50,
            color: Colors.redAccent,
        ),
        Spacer(),
        Container(
            height: 50,
            width: 50,
            color: Colors.blueAccent,
        ),
        Container(
            color: Colors.black,
            height: 75,
            width: 75,
        )
    ],
),
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

縮放佈局

絕大多數flutter的widget是盒子。可以將他們疊放,堆疊,巢狀。

我們可以層級巢狀盒子,但是如果一個盒子不適合(大小不適合)另一個盒子。該如何解決?

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

為了解決這個問題Flutter提供了FittedBox,這個和移動端的ImageView類似。

它實現的功能是使子元素縮放(fit)或者調整位置(alignment)

  • BoxFit.contain(預設值)等比例擴大或縮小,但內容不會超過容器範圍

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.cover按照比例逐步擴大至充滿容器,內容有能會超過容器範圍。當child比例與容器不同時,要麼高度溢位容器,要麼寬度溢位容器。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.fill不保留比例強制拉伸(縮小)填充容器。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.fitHeight保持比例確保高度在容器中顯示完整。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.fitWidth保持比例確保寬度在容器中顯示完整。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.none將child對齊在目標框內(預設劇中),並丟棄位於框外的部分,原始檔不放大也不縮小。

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  • BoxFit.scaleDown將child對齊在目標框內(預設劇中),當child大於容器,則與contain一致。如果child小於容器,則與none一致

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

為了便於理解我們可以找一個圖片進行測試

class Lyoutfitdemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("data")),
      body: Container(
        height: 250,
        width: 250,
        color: Colors.indigo[200],
        child: FittedBox(
          child: Image.asset('images/fittedbox.png')),
      ),
    );
  }
}
複製程式碼
  1. BoxFit.contain

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  1. BoxFit.cover

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  1. BoxFit.fill

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  1. BoxFit.fitHeight

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

5.BoxFit.fitWidth

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  1. BoxFit.none

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

  1. BoxFit.scaleDown

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】
Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

備註:不用建立FittedBox,Image含有屬性fit。值效果和FittedBox一致。

堆疊佈局

Stack不是Row或者Colum定位的,而是按照特定順序堆疊。可以使用 Positioned作為Stack的wiget來定位。Stack與web絕對定位佈局模型類似。

示例:可以使用層疊佈局實現一個 圖片漸變效果

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "漸變狀態列",
      home: Scaffold(
        body: Container(
          height: 400,
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Image.asset(
                'images/Stack.png',
                fit: BoxFit.cover,
              ),
          
              const DecoratedBox(
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment(0.0, -1.0),
                    end: Alignment(0.0, -0.4),
                    colors: <Color>[Color(0x90000000), Color(0x00000000)],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
複製程式碼

圖片漸變效果

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

這是兩個控制元件堆疊在一起的例子。當然children也可以出現在任何位置。可以通過兩個widget控制。

Align佈局

Align為對齊部件,設定child的在父級的(如container、stack等)對齊方式,並根據child的尺寸調整自身的尺寸。

本文的父級容器選用stack

Alignment

其中有這幾種屬性

topLeft(左上),topCenter(頂部中央),topRight(右上),centerLeft,center,centerRight,bottomLeft,bottomCenter,bottomRight

class LayoutAlignDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("align部件"),
        ),
        body: Stack(
          children: <Widget>[
            Align(
              alignment: Alignment.topLeft,
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
              ),
            ),
          ],
        ));
  }
}
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Alignment.lerp(Alignment a, Alignment b, double t)

lerp方法有三個引數,前兩個引數為Alignment型別,第三個引數為小數。

當t為0時,這個方法返回的值為a。

當t為1時,返回b的值。

當t為0.5時,這個widget位置就位於a和b指定的位置中間。

所以這個t為一個偏移量。指定為a到b之間的偏移。

//在Stack children中新增一個align
Align(
  alignment: Alignment.lerp(Alignment.bottomCenter, Alignment.center,0.5),//位於Alignment.bottomCenter和Alignment.center的中央
  child: Container(
  width: 100,
  height: 100,
  color: Colors.yellow,
   ),
),
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

偏移量對齊

上面介紹了相對於對其方式a和b的偏移對其。

FractionalOffset 是另外一個偏移方法,它是相對於父部件左上角的偏移。

建立偏移量FractionalOffset (dx,dy)。dx和dy的取值都是0~1。左上的位置為dx和dy都為0。

Align(
  alignment: FractionalOffset(0, 0.5),
  child: Container(
    width: 100,
    height: 100,
    color: Colors.red,
  ),
),
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

另外在原始碼中:

class FractionalOffset extends Alignment
複製程式碼

FractionalOffset繼承於Alignment所以Alignment的屬性都可以使用,這樣我們要使得widget位於左上也可以用使用FractionalOffset.topLeft


class LayoutAlignDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("align部件"),
        ),
        body: Stack(
          children: <Widget>[
            Align(
              alignment: FractionalOffset(0, 0),
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
              ),
            ),
            Align(
              alignment: FractionalOffset(0, 0.5),
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
              ),
            ),
            Align(
              alignment: FractionalOffset(0, 1),
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
              ),
            ),
            Align(
              alignment: Alignment.lerp(Alignment.bottomCenter, Alignment.center,0.5),
              child: Container(
                width: 100,
                height: 100,
                color: Colors.yellow,
              ),
            ),
            Align(
              alignment: FractionalOffset.topRight,
              child: Container(
                width: 100,
                height: 100,
                color: Colors.yellow,
              ),
            ),
          ],
        ));
  }
}

複製程式碼

與之相類似的還有Alignment(dx, dy),以父級容器的中心為座標系原點。dx的取值範圍為-1~1,dy的取值範圍為-1~1。設定子元素的位置。

Positioned

Positioned 部件可以控制Stack中子元素的位置。Positioned 與Align不同的是Position必須是Stack的Children。

Position有topbottomleftrightheightwidth

topbottomleftright這些量都是與某一邊界的距離。

當一個大小高度和長度固定容器一旦我們確定其水平方向和垂直方向上各一個量。其位置是固定的。

部分程式碼

Stack(
    children: <Widget>[
        Positioned(
            top: 100,
            right: 100,
            width: 100,
            height: 100,
            child: Container(
                color: Colors.red,
            ),
        ),
    ],
)
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

原始碼分析

 const Positioned({
    Key key,
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
    @required Widget child,
  }) : assert(left == null || right == null || width == null),
       assert(top == null || bottom == null || height == null),
       super(key: key, child: child);
複製程式碼

在水平方向上如果assert(false)會丟擲錯誤

而括號裡的值為left == null || right == null || width == null

left ,right ,width 至少有一個值為null這個程式才不會報錯。

也就是說當width未指定(為空)使left和right是可以同時存在的。這時的容器的寬度會以據邊界的距離(left和right)自動調整。

Stack(
    children: <Widget>[
        Positioned(
            top: 100,
            right: 100,
            left: 100,
            // width: 100,
            height: 100,
            child: Container(
                color: Colors.red,
            ),
        ),
    ],
)
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

IndexedStack

IndexedStack繼承於Stack

class IndexedStack extends Stack
複製程式碼

他和Stack不同的是有一個index的屬性,表示只顯示第幾個元素。

IndexedStack(
    index: 1,//只顯示第二個
    children: <Widget>[
        Positioned(
            top: 100,
            right: 100,
            left: 100,
            height: 100,
            child: Container(
                color: Colors.red,
            ),
        ),  
        Positioned(
            top: 500,
            right: 100,
            left: 100,
            height: 100,
            child: Container(
                color: Colors.red,
            ),
        ),
    ],
)
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

容器佈局

Container可以建立一個矩形元素。可以用BoxDecoration進行裝飾。背景,邊框,陰影。

class LyoutContainerdemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Container容器"),
      ),
      body: Container(
        height: 200,
        width: 200,
        child: Text("這是一個Container容器"),
        decoration: BoxDecoration(
          color: Colors.red[200],
          // shape: BoxShape.circle, //形狀
          border: Border.all(width: 10.0),
          boxShadow: [
            BoxShadow(
              offset: Offset(0.0, 100.0), //模糊偏移量
              color: Color.fromRGBO(16, 20, 188, 1.0), //顏色
              blurRadius: 10, //模糊
              spreadRadius: -9.0, //在應用模糊之前陰影的膨脹量
            ),
          ],
        ),
      ),
    );
  }
}

複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

為了演示功能,這個圖片效果做的比較誇張。

附上一個好看的效果

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

相似於Css的盒子佈局,我們也可以通過Margin給盒子設定外邊距。

Container(
    margin: EdgeInsets.all(50.0),//外邊距50.0
    height: 200,
    width: 200,
    decoration: BoxDecoration(
        color: Colors.red[600],
        border: Border.all(width: 2.0),
        boxShadow: [
            BoxShadow(
                offset: Offset(2.0, 9.0), //偏移量
                color: Colors.red[200], //顏色
                blurRadius: 10, //模糊
                spreadRadius: -1.0, //在應用模糊之前陰影的膨脹量
            ),
        ],
    ),
),
複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

Padding佈局

用於處理容器與子元素之間的距離。與Padding對應的是margin屬性。margin用於處理與其他元件之間的距離。Padding部件和容器內的pading屬性的效果實際上是一致的,當同時出現,真實的padding將是兩者相加。

class LayoutPaddingDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Padding 佈局"),
      ),
      body: Container(
        padding: EdgeInsets.all(20.0),//#1這兩處的屬性效果是一致的
        decoration: BoxDecoration(
            color: Colors.yellow,
            border: Border.all(color: Colors.white, width: 8.0)),//白色邊框
        child: Padding(
          child: Container(
            decoration: BoxDecoration(
            color: Colors.red,
            border: Border.all(color: Colors.white, width: 8.0)),//白色邊框
          ),
          padding: EdgeInsets.all(10.0),//#1這兩處的屬性效果是一致的
        ),
      ),
    );
  }
}

複製程式碼

Flutter佈局——Flex、FittedBox、Stack、Container佈局教程【超詳細】

寫在後面

Flutter中涉及到佈局的Widget有30多種,一樣的效果的ui,實現的途徑有很多中。本篇就重點涉及幾個常用的部件。

參考連結

youtu.be/T4Uehk3_wlY

medium.com/jlouage/flu…

api.flutter.dev/index.html

flutter.dev/docs

相關文章