Flutter(九)之Flutter的佈局Widget

前言一:接下來一段時間我會陸續更新一些列Flutter文字教程前端

更新進度: 因爲Mac檔期問題,可能會暫停一段時間Flutter的更新,可是會更新前端文章;vue

更新地點: 首發於公衆號,次日更新於掘金、思否、開發者頭條等地方;算法

更多交流: 能夠添加個人微信 372623326,關注個人微博:coderwhybash

但願你們能夠 幫忙轉發,點擊在看,給我更多的創做動力。微信

前言二:爲了實現界面內組件的各類排布方式,咱們須要進行佈局,和其餘端不一樣的是,Flutter中由於萬物皆Widget,因此佈局也是使用Widget來完成的。數據結構

Flutter中的佈局組件很是多,有31個用於佈局的組件,Flutter佈局組件app

在學習的過程當中,咱們不必一個個所有掌握,掌握最經常使用的,一些特殊的組件用到時去查文檔便可;less

Flutter將佈局組件分紅了 單子佈局組件(Single-child layout widgets) 和 多子佈局組件(Multi-child layout widgets)ide

一. 單子佈局組件

單子佈局組件的含義是其只有一個子組件,能夠經過設置一些屬性設置該子組件所在的位置信息等。源碼分析

比較經常使用的單子佈局組件有:Align、Center、Padding、Container。

1.1. Align組件

1.1.1. Align介紹

看到Align這個詞,咱們就知道它有咱們的對齊方式有關。

在其餘端的開發中(iOS、Android、前端)Align一般只是一個屬性而已,可是Flutter中Align也是一個組件。

咱們能夠經過源碼來看一下Align有哪些屬性:

const Align({
  Key key,
  this.alignment: Alignment.center, // 對齊方式,默認居中對齊
  this.widthFactor, // 寬度因子,不設置的狀況,會盡量大
  this.heightFactor, // 高度因子,不設置的狀況,會盡量大
  Widget child // 要佈局的子Widget
})
複製代碼

這裏咱們特別解釋一下widthFactorheightFactor做用:

  • 由於子組件在父組件中的對齊方式必須有一個前提,就是父組件得知道本身的範圍(寬度和高度);
  • 若是widthFactorheightFactor不設置,那麼默認Align會盡量的大(儘量佔據本身所在的父組件);
  • 咱們也能夠對他們進行設置,好比widthFactor設置爲3,那麼相對於Align的寬度是子組件跨度的3倍;

1.1.2. Align演練

咱們簡單演練一下Align

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Align(
      child: Icon(Icons.pets, size: 36, color: Colors.red),
      alignment: Alignment.bottomRight,
      widthFactor: 3,
      heightFactor: 3,
    );
  }
}
複製代碼

image-20190903113659914

1.2. Center組件

1.2.1. Center介紹

Center組件咱們在前面已經用過不少次了。

事實上Center組件繼承自Align,只是將alignment設置爲Alignment.center。

源碼分析:

class Center extends Align {
  const Center({ 
    Key key, 
    double widthFactor, 
    double heightFactor, 
    Widget child 
  }) : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
複製代碼

1.2.2. Center演練

咱們將上面的代碼Align換成Center

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Icon(Icons.pets, size: 36, color: Colors.red),
      widthFactor: 3,
      heightFactor: 3,
    );
  }
}
複製代碼

image-20190903114101386

1.3. Padding組件

1.3.1. Padding介紹

Padding組件在其餘端也是一個屬性而已,可是在Flutter中是一個Widget,可是Flutter中沒有Margin這樣一個Widget,這是由於外邊距也能夠經過Padding來完成。

Padding一般用於設置子Widget到父Widget的邊距(你能夠稱之爲是父組件的內邊距或子Widget的外邊距)。

源碼分析:

const Padding({
  Key key,
  @required this.padding, // EdgeInsetsGeometry類型(抽象類),使用EdgeInsets
  Widget child,
})
複製代碼

1.3.2. Padding演練

代碼演練:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(20),
      child: Text(
        "莫聽穿林打葉聲,何妨吟嘯且徐行。竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
        style: TextStyle(
          color: Colors.redAccent,
          fontSize: 18
        ),
      ),
    );
  }
}
複製代碼

image-20190903115027193

1.4. Container組件

Container組件相似於其餘Android中的View,iOS中的UIView。

若是你須要一個視圖,有一個背景顏色、圖像、有固定的尺寸、須要一個邊框、圓角等效果,那麼就可使用Container組件。

14.1. Container介紹

Container在開發中被使用的頻率是很是高的,特別是咱們常常會將其做爲容器組件。

下面咱們來看一下Container有哪些屬性:

Container({
  this.alignment,
  this.padding, //容器內補白,屬於decoration的裝飾範圍
  Color color, // 背景色
  Decoration decoration, // 背景裝飾
  Decoration foregroundDecoration, //前景裝飾
  double width,//容器的寬度
  double height, //容器的高度
  BoxConstraints constraints, //容器大小的限制條件
  this.margin,//容器外補白,不屬於decoration的裝飾範圍
  this.transform, //變換
  this.child,
})
複製代碼

大多數屬性在介紹其它容器時都已經介紹過了,再也不贅述,但有兩點須要說明:

  • 容器的大小能夠經過widthheight屬性來指定,也能夠經過constraints來指定,若是同時存在時,widthheight優先。實際上Container內部會根據widthheight來生成一個constraints
  • colordecoration是互斥的,實際上,當指定color時,Container內會自動建立一個decoration;
  • decoration屬性稍後咱們詳細學習;

1.4.2. Container演練

簡單進行一個演示:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: Color.fromRGBO(3, 3, 255, .5),
        width: 100,
        height: 100,
        child: Icon(Icons.pets, size: 32, color: Colors.white),
      ),
    );
  }
}
複製代碼

image-20190903085602884

1.4.3. BoxDecoration

Container有一個很是重要的屬性 decoration

  • 他對應的類型是Decoration類型,可是它是一個抽象類。
  • 在開發中,咱們常用它的實現類BoxDecoration來進行實例化。

BoxDecoration常見屬性:

const BoxDecoration({
    this.color, // 顏色,會和Container中的color屬性衝突
    this.image, // 背景圖片
    this.border, // 邊框,對應類型是Border類型,裏面每個邊框使用BorderSide
    this.borderRadius, // 圓角效果
    this.boxShadow, // 陰影效果
    this.gradient, // 漸變效果
    this.backgroundBlendMode, // 背景混合
    this.shape = BoxShape.rectangle, // 形變
  })

複製代碼

部分效果演示:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
// color: Color.fromRGBO(3, 3, 255, .5),
        width: 150,
        height: 150,
        child: Icon(Icons.pets, size: 32, color: Colors.white),
        decoration: BoxDecoration(
          color: Colors.amber, // 背景顏色
          border: Border.all(
            color: Colors.redAccent,
            width: 3,
            style: BorderStyle.solid
          ), // 這裏也可使用Border.all統一設置
// top: BorderSide(
// color: Colors.redAccent,
// width: 3,
// style: BorderStyle.solid
// ),
          borderRadius: BorderRadius.circular(20), // 這裏也可使用.only分別設置
          boxShadow: [
            BoxShadow(
              offset: Offset(5, 5),
              color: Colors.purple,
              blurRadius: 5
            )
          ],
// shape: BoxShape.circle, // 會和borderRadius衝突
          gradient: LinearGradient(
            colors: [
              Colors.green,
              Colors.red
            ]
          )
        ),
      ),
    );
  }
}

複製代碼

image-20190903092137469

1.4.4. 實現圓角圖像

上一個章節咱們提到能夠經過 Container+BoxDecoration來實現圓角圖像。

實現代碼以下:

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20),
          image: DecorationImage(
            image: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
          )
        ),
      ),
    );
  }
}

複製代碼

image-20190924100221266

二. 多子佈局組件

在開發中,咱們常常須要將多個Widget放在一塊兒進行佈局,好比水平方向、垂直方向排列,甚至有時候須要他們進行層疊,好比圖片上面放一段文字等;

這個時候咱們須要使用多子佈局組件(Multi-child layout widgets)。

比較經常使用的多子佈局組件是Row、Column、Stack,咱們來學習一下他們的使用。

2.1. Flex組件

事實上,咱們即將學習的Row組件和Column組件都繼承自Flex組件。

  • Flex組件和Row、Column屬性主要的區別就是多一個direction。
  • 當direction的值爲Axis.horizontal的時候,則是Row。
  • 當direction的值爲Axis.vertical的時候,則是Column。

在學習Row和Column以前,咱們先學習主軸交叉軸的概念。

由於Row是一行排布,Column是一列排布,那麼它們都存在兩個方向,而且兩個Widget排列的方向應該是對立的。

它們之中都有主軸(MainAxis)和交叉軸(CrossAxis)的概念:

  • 對於Row來講,主軸(MainAxis)和交叉軸(CrossAxis)分別是下圖

  • 對於Column來講,主軸(MainAxis)和交叉軸(CrossAxis)分別是下圖

2.1. Row組件

2.1.1. Row介紹

Row組件用於將全部的子Widget排成一行,實際上這種佈局應該是借鑑於Web的Flex佈局。

若是熟悉Flex佈局,會發現很是簡單。

從源碼中查看Row的屬性:

Row({
  Key key,
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, // 主軸對齊方式
  MainAxisSize mainAxisSize = MainAxisSize.max, // 水平方向儘量大
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, // 交叉處對齊方式
  TextDirection textDirection, // 水平方向子widget的佈局順序(默認爲系統當前Locale環境的文本方向(如中文、英語都是從左往右,而阿拉伯語是從右往左))
  VerticalDirection verticalDirection = VerticalDirection.down, // 表示Row縱軸(垂直)的對齊方向
  TextBaseline textBaseline, // 若是上面是baseline對齊方式,那麼選擇什麼模式(有兩種可選)
  List<Widget> children = const <Widget>[],
}) 

複製代碼

部分屬性詳細解析:(不過文字是真的難描述,後續推出視頻學習較差)

mainAxisSize:

  • 表示Row在主軸(水平)方向佔用的空間,默認是MainAxisSize.max,表示儘量多的佔用水平方向的空間,此時不管子widgets實際佔用多少水平空間,Row的寬度始終等於水平方向的最大寬度
  • MainAxisSize.min表示儘量少的佔用水平空間,當子widgets沒有佔滿水平剩餘空間,則Row的實際寬度等於全部子widgets佔用的的水平空間;

mainAxisAlignment:表示子Widgets在Row所佔用的水平空間內對齊方式

  • 若是mainAxisSize值爲MainAxisSize.min,則此屬性無心義,由於子widgets的寬度等於Row的寬度
  • 只有當mainAxisSize的值爲MainAxisSize.max時,此屬性纔有意義
  • MainAxisAlignment.start表示沿textDirection的初始方向對齊,
  • 如textDirection取值爲TextDirection.ltr時,則MainAxisAlignment.start表示左對齊,textDirection取值爲TextDirection.rtl時表示從右對齊。
  • MainAxisAlignment.endMainAxisAlignment.start正好相反;
  • MainAxisAlignment.center表示居中對齊。

crossAxisAlignment:表示子Widgets在縱軸方向的對齊方式

  • Row的高度等於子Widgets中最高的子元素高度
  • 它的取值和MainAxisAlignment同樣(包含startendcenter三個值)
  • 不一樣的是crossAxisAlignment的參考系是verticalDirection,即verticalDirection值爲VerticalDirection.downcrossAxisAlignment.start指頂部對齊,verticalDirection值爲VerticalDirection.up時,crossAxisAlignment.start指底部對齊;而crossAxisAlignment.endcrossAxisAlignment.start正好相反;

2.1.2. Row演練

咱們來對部分屬性進行簡單的代碼演練,其餘一些屬性你們本身學習一下

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.end,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        Container(color: Colors.red, width: 60, height: 60),
        Container(color: Colors.blue, width: 80, height: 80),
        Container(color: Colors.green, width: 70, height: 70),
        Container(color: Colors.orange, width: 100, height: 100),
      ],
    );
  }
}

複製代碼

image-20190903154614672

2.1.3. mainAxisSize

默認狀況下,Row會盡量佔據多的寬度,讓子Widget在其中進行排布,這是由於mainAxisSize屬性默認值是MainAxisSize.max

咱們來看一下,若是這個值被修改成MainAxisSize.max會什麼變化:

image-20190903201957558

2.1.4. TextBaseline

關於TextBaseline的取值解析

ideographic alphabetic

2.1.5. Expanded

若是咱們但願紅色和黃色的Container Widget不要設置固定的寬度,而是佔據剩餘的部分,這個時候應該如何處理呢?

這個時候咱們可使用 Expanded 來包裹 Container Widget,而且將它的寬度不設置值;

  • flex屬性,彈性係數,Row會根據兩個Expanded的彈性係數來決定它們佔據剩下空間的比例
class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Container(color: Colors.red, height: 60),
        ),
        Container(color: Colors.blue, width: 80, height: 80),
        Container(color: Colors.green, width: 70, height: 70),
        Expanded(
          flex: 1,
          child: Container(color: Colors.orange, height: 100),
        )
      ],
    );
  }
}

複製代碼

image-20190903202611133

2.2. Column組件

Column組件用於將全部的子Widget排成一列,學會了前面的Row後,Column只是和row的方向不一樣而已。

2.2.1. Column介紹

咱們直接看它的源碼:咱們發現和Row屬性是一致的,再也不解釋

Column({
    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>[],
  })

複製代碼

2.2.2. Column演練

咱們直接將Row的代碼中Row改成Column,查看代碼運行效果:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Container(color: Colors.red, width: 60),
        ),
        Container(color: Colors.blue, width: 80, height: 80),
        Container(color: Colors.green, width: 70, height: 70),
        Expanded(
          flex: 1,
          child: Container(color: Colors.orange, width: 100),
        )
      ],
    );
  }
}

複製代碼

image-20190903203138118

2.3. Stack組件

在開發中,咱們多個組件頗有可能須要重疊顯示,好比在一張圖片上顯示文字或者一個按鈕等。

在Android中可使用Frame來實現,在Web端可使用絕對定位,在Flutter中咱們須要使用層疊佈局Stack。

2.3.1. Stack介紹

咱們仍是經過源碼來看一下Stack有哪些屬性:

Stack({
  Key key,
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.overflow = Overflow.clip,
  List<Widget> children = const <Widget>[],
}) 

複製代碼

參數j解析:

  • alignment:此參數決定如何去對齊沒有定位(沒有使用Positioned)或部分定位的子widget。所謂部分定位,在這裏**特指沒有在某一個軸上定位:**left、right爲橫軸,top、bottom爲縱軸,只要包含某個軸上的一個定位屬性就算在該軸上有定位。
  • textDirection:和Row、Wrap的textDirection功能同樣,都用於決定alignment對齊的參考系即:textDirection的值爲TextDirection.ltr,則alignment的start表明左,end表明右;textDirection的值爲TextDirection.rtl,則alignment的start表明右,end表明左。
  • fit:此參數用於決定沒有定位的子widget如何去適應Stack的大小。StackFit.loose表示使用子widget的大小,StackFit.expand表示擴伸到Stack的大小。
  • overflow:此屬性決定如何顯示超出Stack顯示空間的子widget,值爲Overflow.clip時,超出部分會被剪裁(隱藏),值爲Overflow.visible 時則不會。

2.3.2. Stack演練

Stack會常常和Positioned一塊兒來使用,Positioned能夠決定組件在Stack中的位置,用於實現相似於Web中的絕對定位效果。

咱們來看一個簡單的演練:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Container(
          color: Colors.purple,
          width: 300,
          height: 300,
        ),
        Positioned(
          left: 20,
          top: 20,
          child: Icon(Icons.favorite, size: 50, color: Colors.white)
        ),
        Positioned(
          bottom: 20,
          right: 20,
          child: Text("你好啊,李銀河", style: TextStyle(fontSize: 20, color: Colors.white)),
        )
      ],
    );
  }
}
複製代碼

image-20190903220757633

**注意:**Positioned組件只能在Stack中使用。

備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注

公衆號
相關文章
相關標籤/搜索