本文主要介紹Flutter佈局中的Padding、Align以及Center控件,詳細介紹了其布舉行爲以及使用場景,並對源碼進行了分析。html
A widget that insets its child by the given padding.git
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
Padding的佈局分爲兩種狀況:函數
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Padding
複製代碼
從繼承關係能夠看出,Padding控件是一個基礎控件,不像Container這種組合控件。Container中的margin以及padding屬性都是利用Padding控件去實現的。佈局
SingleChildRenderObjectWidget是RenderObjectWidgets的一個子類,用於限制只能有一個子節點。它只提供child的存儲,而不提供實際的更新邏輯。學習
RenderObjectWidgets爲RenderObjectElement提供配置,而RenderObjectElement包含着(wrap)RenderObject,RenderObject則是在應用中提供實際的繪製(rendering)的元素。ui
實例代碼直接上官方的例子,很是的簡單:this
new Padding(
padding: new EdgeInsets.all(8.0),
child: const Card(child: const Text('Hello World!')),
)
複製代碼
例子中對Card設置了一個寬度爲8的內邊距。
構造函數以下:
const Padding({
Key key,
@required this.padding,
Widget child,
})
複製代碼
包含一個padding屬性,至關的簡單。
padding:padding的類型爲EdgeInsetsGeometry
,EdgeInsetsGeometry是EdgeInsets以及EdgeInsetsDirectional的基類。在實際使用中不涉及到國際化,例如適配阿拉伯地區等,通常都是使用EdgeInsets。EdgeInsetsDirectional光看命名就知道跟方向相關,所以它的四個邊距不限定上下左右,而是根據方向來定的。
@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佈局行爲就解釋的通了。
Padding自己仍是挺簡單的,基本上須要間距的地方,它都可以使用。若是在單一的間距場景,使用Padding比Container的成本要小一些,畢竟Container裏面包含了多個widget。Padding可以實現的,Container都可以實現,只不過,Container更加的複雜。
A widget that aligns its child within itself and optionally sizes itself based on the child's size.
在其餘端的開發,Align通常都是當作一個控件的屬性,並無拿出來當作一個單獨的控件。Align自己實現的功能並不複雜,設置child的對齊方式,例如居中、居左居右等,並根據child尺寸調節自身尺寸。
Align的佈局行爲分爲兩種狀況:
Align爲何會有這樣的佈局行爲呢?緣由很簡單,設置對齊方式的話,若是外層元素尺寸不肯定的話,內部的對齊就沒法肯定。所以,會有寬高因子、根據外層限制擴大到最大尺寸、外層不肯定時調整到child尺寸這些行爲。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Align
複製代碼
能夠看出,Align跟Padding同樣,也是一個很是基礎的組件,Container中的align屬性,也是使用Align去實現的。
new Align(
alignment: Alignment.center,
widthFactor: 2.0,
heightFactor: 2.0,
child: new Text("Align"),
)
複製代碼
例子依舊很簡單,設置一個寬高爲child兩倍區域的Align,其child處在正中間。
const Align({
Key key,
this.alignment: Alignment.center,
this.widthFactor,
this.heightFactor,
Widget child
})
複製代碼
Align的構造函數基本上就是寬高因子、對齊方式屬性。平常使用中,寬高因子屬性基本上用的很少。若是是複雜的佈局,Container內部的align屬性也能夠實現相同的效果。
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的高度乘以這個值,不能爲負數。
@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));
}
複製代碼
通常在對齊場景下使用,例如須要右對齊或者底部對齊之類的。它可以實現的功能,Container都能實現。
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);
}
複製代碼
筆者建了一個flutter學習相關的項目,github地址,裏面包含了筆者寫的關於flutter學習相關的一些文章,會按期更新,也會上傳一些學習demo,歡迎你們關注。