Flutter 佈局(二)- Padding、Align、Center詳解

本文主要介紹Flutter佈局中的Padding、Align以及Center控件,詳細介紹了其布舉行爲以及使用場景,並對源碼進行了分析。html

1. Padding

A widget that insets its child by the given padding.git

1.1 簡介

Padding在Flutter中用的也挺多的,做爲一個基礎的控件,功能很是單一,給子節點設置padding屬性。寫過其餘端的都瞭解這個屬性,就是設置內邊距屬性,內邊距的空白區域,也是widget的一部分。github

Flutter中並無單獨的Margin控件,在Container中有margin屬性,看源碼關於margin的實現。bash

if (margin != null)
  current = new Padding(padding: margin, child: current);
複製代碼

不難看出,Flutter中淡化了margin以及padding的區別,margin實質上也是由Padding實現的。ide

1.2 佈局行爲

Padding的佈局分爲兩種狀況:函數

  • 當child爲空的時候,會產生一個寬爲left+right,高爲top+bottom的區域;
  • 當child不爲空的時候,Padding會將佈局約束傳遞給child,根據設置的padding屬性,縮小child的佈局尺寸。而後Padding將本身調整到child設置了padding屬性的尺寸,在child周圍建立空白區域。

1.3 繼承關係

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Padding
複製代碼

從繼承關係能夠看出,Padding控件是一個基礎控件,不像Container這種組合控件。Container中的margin以及padding屬性都是利用Padding控件去實現的。佈局

1.3.1 關於SingleChildRenderObjectWidget

SingleChildRenderObjectWidget是RenderObjectWidgets的一個子類,用於限制只能有一個子節點。它只提供child的存儲,而不提供實際的更新邏輯。學習

1.3.2 關於RenderObjectWidgets

RenderObjectWidgets爲RenderObjectElement提供配置,而RenderObjectElement包含着(wrap)RenderObject,RenderObject則是在應用中提供實際的繪製(rendering)的元素。ui

1.4 示例代碼

實例代碼直接上官方的例子,很是的簡單:this

new Padding(
  padding: new EdgeInsets.all(8.0),
  child: const Card(child: const Text('Hello World!')),
)
複製代碼

例子中對Card設置了一個寬度爲8的內邊距。

1.5 源碼解析

構造函數以下:

const Padding({
    Key key,
    @required this.padding,
    Widget child,
  })
複製代碼

包含一個padding屬性,至關的簡單。

1.5.1 屬性解析

padding:padding的類型爲EdgeInsetsGeometry,EdgeInsetsGeometry是EdgeInsets以及EdgeInsetsDirectional的基類。在實際使用中不涉及到國際化,例如適配阿拉伯地區等,通常都是使用EdgeInsets。EdgeInsetsDirectional光看命名就知道跟方向相關,所以它的四個邊距不限定上下左右,而是根據方向來定的。

1.5.2 源碼

@override
  RenderPadding createRenderObject(BuildContext context) {
    return new RenderPadding(
      padding: padding,
      textDirection: Directionality.of(context),
   );
}
複製代碼

Padding的建立函數,其實是由RenderPadding來進行的。

關於RenderPadding的實際佈局表現,當child爲null的時候:

if (child == null) {
  size = constraints.constrain(new Size(
    _resolvedPadding.left + _resolvedPadding.right,
    _resolvedPadding.top + _resolvedPadding.bottom
  ));
  return;
}
複製代碼

返回一個寬爲_resolvedPadding.left+_resolvedPadding.right,高爲_resolvedPadding.top+_resolvedPadding.bottom的區域。

當child不爲null的時候,經歷了三個過程,即調整child尺寸、調整child位置以及調整Padding尺寸,最終達到實際的佈局效果。

// 調整child尺寸
final BoxConstraints innerConstraints = constraints.deflate(_resolvedPadding);
child.layout(innerConstraints, parentUsesSize: true);

// 調整child位置
final BoxParentData childParentData = child.parentData;
childParentData.offset = new Offset(_resolvedPadding.left, _resolvedPadding.top);

// 調整Padding尺寸
size = constraints.constrain(new Size(
  _resolvedPadding.left + child.size.width + _resolvedPadding.right,
  _resolvedPadding.top + child.size.height + _resolvedPadding.bottom
));
複製代碼

到此處,上面介紹的padding佈局行爲就解釋的通了。

1.6 使用場景

Padding自己仍是挺簡單的,基本上須要間距的地方,它都可以使用。若是在單一的間距場景,使用Padding比Container的成本要小一些,畢竟Container裏面包含了多個widget。Padding可以實現的,Container都可以實現,只不過,Container更加的複雜。

2. Align

A widget that aligns its child within itself and optionally sizes itself based on the child's size.

2.1 簡介

在其餘端的開發,Align通常都是當作一個控件的屬性,並無拿出來當作一個單獨的控件。Align自己實現的功能並不複雜,設置child的對齊方式,例如居中、居左居右等,並根據child尺寸調節自身尺寸。

2.2 佈局行爲

Align的佈局行爲分爲兩種狀況:

  • 當widthFactor和heightFactor爲null的時候,當其有限制條件的時候,Align會根據限制條件儘可能的擴展本身的尺寸,當沒有限制條件的時候,會調整到child的尺寸;
  • 當widthFactor或者heightFactor不爲null的時候,Aligin會根據factor屬性,擴展本身的尺寸,例如設置widthFactor爲2.0的時候,那麼,Align的寬度將會是child的兩倍。

Align爲何會有這樣的佈局行爲呢?緣由很簡單,設置對齊方式的話,若是外層元素尺寸不肯定的話,內部的對齊就沒法肯定。所以,會有寬高因子、根據外層限制擴大到最大尺寸、外層不肯定時調整到child尺寸這些行爲。

2.3 繼承關係

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Align
複製代碼

能夠看出,Align跟Padding同樣,也是一個很是基礎的組件,Container中的align屬性,也是使用Align去實現的。

2.4 示例代碼

new Align(
  alignment: Alignment.center,
  widthFactor: 2.0,
  heightFactor: 2.0,
  child: new Text("Align"),
)
複製代碼

例子依舊很簡單,設置一個寬高爲child兩倍區域的Align,其child處在正中間。

2.5 源碼解析

const Align({
    Key key,
    this.alignment: Alignment.center,
    this.widthFactor,
    this.heightFactor,
    Widget child
  })
複製代碼

Align的構造函數基本上就是寬高因子、對齊方式屬性。平常使用中,寬高因子屬性基本上用的很少。若是是複雜的佈局,Container內部的align屬性也能夠實現相同的效果。

2.5.1 屬性解析

alignment:對齊方式,通常會使用系統默認提供的9種方式,可是並非說只有這9種,例如以下的定義。系統提供的9種方式只是預先定義好的。

/// The top left corner.
static const Alignment topLeft = const Alignment(-1.0, -1.0);
複製代碼

Alignment其實是包含了兩個屬性的,其中第一個參數,-1.0是左邊對齊,1.0是右邊對齊,第二個參數,-1.0是頂部對齊,1.0是底部對齊。根據這個規則,咱們也能夠自定義咱們須要的對齊方式,例如

/// 居右高於底部1/4處.
static const Alignment rightHalfBottom = alignment: const Alignment(1.0, 0.5),
複製代碼

widthFactor:寬度因子,若是設置的話,Align的寬度就是child的寬度乘以這個值,不能爲負數。

heightFactor:高度因子,若是設置的話,Align的高度就是child的高度乘以這個值,不能爲負數。

2.5.2 源碼

@override
  RenderPositionedBox createRenderObject(BuildContext context) {
    return new RenderPositionedBox(
      alignment: alignment,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      textDirection: Directionality.of(context),
    );
  }
複製代碼

Align的實際構造調用的是RenderPositionedBox

RenderPositionedBox的佈局表現以下:

// 根據_widthFactor、_heightFactor以及限制因素來肯定寬高
final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;

if (child != null) {
  //  若是child不爲null,則根據規則設置Align的寬高,若是須要縮放,則根據_widthFactor是否爲null來進行縮放,若是不須要,則儘可能擴展。
  child.layout(constraints.loosen(), parentUsesSize: true);
  size = constraints.constrain(new Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.infinity,
                                        shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.infinity));
  alignChild();
} else {
  // 若是child爲null,若是須要縮放,則變爲0,不然就儘可能擴展
  size = constraints.constrain(new Size(shrinkWrapWidth ? 0.0 : double.infinity,
                                        shrinkWrapHeight ? 0.0 : double.infinity));
}
複製代碼

2.6 使用場景

通常在對齊場景下使用,例如須要右對齊或者底部對齊之類的。它可以實現的功能,Container都能實現。

3. Center

Center繼承自Align,只不過是將alignment設置爲Alignment.center,其餘屬性例如widthFactor、heightFactor,佈局行爲,都與Align徹底同樣,在這裏就再也不單獨作介紹了。Center源碼以下,沒有設置alignment屬性,是由於Align默認的對齊方式就是居中。

class Center extends Align {
  /// Creates a widget that centers its child.
  const Center({ Key key, double widthFactor, double heightFactor, Widget child })
    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
複製代碼

4. 後話

筆者建了一個flutter學習相關的項目,github地址,裏面包含了筆者寫的關於flutter學習相關的一些文章,會按期更新,也會上傳一些學習demo,歡迎你們關注。

5. 參考

  1. Padding class
  2. EdgeInsetsGeometry class
  3. EdgeInsets class
  4. EdgeInsetsDirectional class
  5. RenderPadding class
  6. Align class
  7. Center class
相關文章
相關標籤/搜索