Flutter
中擁有30多種預約義的佈局widget
,經常使用的有Container
、Padding
、Center
、Flex
、Row
、Colum
、ListView
、GridView
。按照《Flutter技術入門與實戰》上面來講的話,大概分爲四類git
二,寬高尺寸處理github
SizedBox佈局行爲相對較簡單:less
Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedBox
const SizedBox({
Key key,
this.width, //寬
this.height, //高
Widget child //子組件
})
width:寬度值,若是具體設置了,則強制child寬度爲此值,若是沒設置,則根據child寬度調整自身寬度。ide
height:同上。函數
這個控件的做用是添加額外的限制條件(constraints)到child上,自己挺簡單的,能夠被一些控件替換使用。Flutter的佈局控件體系,梳理着發現確實有點亂,感受整體思想是缺啥就造啥
ConstrainedBox的佈局行爲很是簡單,取決於設置的限制條件,而關於父子節點的限制因素生效優先級,能夠查看以前的文章,在這裏就不作具體敘述了。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > ConstrainedBox
ConstrainedBox({ Key key, @required this.constraints, Widget child })
LimitedBox佈局
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > LimitedBox
const LimitedBox({ Key key, this.maxWidth = double.infinity, this.maxHeight = double.infinity, Widget child, })
maxWidth:限定的最大寬度,默認值是double.infinity,不能爲負數。post
maxHeight:同上。性能
AspectRatio的佈局行爲分爲兩種狀況:學習
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio
從繼承關係看,AspectRatio是基礎的佈局控件。ui
const AspectRatio({ Key key, @required this.aspectRatio, Widget child })
構造函數只包含了一個aspectRatio屬性,其中aspectRatio不能爲null。
FractionallySizedBox的佈局行爲主要跟它的寬高因子兩個參數有關,當參數爲null或者有具體數值的時候,佈局表現不同。固然,還有一個輔助參數alignment,做爲對齊方式進行佈局。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FractionallySizedBox
const FractionallySizedBox({ Key key, this.alignment = Alignment.center, this.widthFactor, this.heightFactor, Widget child, })
alignment:對齊方式,不能爲null。
widthFactor:寬度因子,跟以前介紹的控件相似,寬度乘以這個值,就是最後的寬度。
heightFactor:高度因子,用做計算最後實際高度的。
其中widthFactor和heightFactor都有一個規則
三,經常使用方法
/** * MySizeBox */ class MySizeBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( color: Colors.green, padding: const EdgeInsets.all(5.0), child: SizedBox( width: 200.0, height: 200.0, child: new Container( color: Colors.red, width: 100.0, height: 300.0, ), ), ); } }
效果圖:
源碼解析:
SizedBox內部是經過RenderConstrainedBox來實現的。具體的源碼就不解析了,整體思路是,根據寬高值算好一個constraints,而後強制應用到child上。
/** * ConstrainedBox * 在一個寬高200.0的Container上添加一個約束最大最小寬高的ConstrainedBox,實際的顯示中,則是一個寬高爲150.0的區域。 */ class MyConstrainedBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new ConstrainedBox( constraints: const BoxConstraints( minWidth: 100.0, minHeight: 100.0, maxWidth: 150.0, maxHeight: 150.0, ), child: new Container( width: 200.0, height: 200.0, color: Colors.red, ), ); } }
效果圖:
@override RenderConstrainedBox createRenderObject(BuildContext context) { return new RenderConstrainedBox(additionalConstraints: constraints); }
RenderConstrainedBox實現其繪製。其具體的佈局表現分兩種狀況:
若是child不爲null,則將限制條件加在child上;
若是child爲null,則會盡量的縮小尺寸。
具體代碼以下:
@override void performLayout() { if (child != null) { child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true); size = child.size; } else { size = _additionalConstraints.enforce(constraints).constrain(Size.zero); } }
使用場景:
須要添加額外的約束條件可使用此控件,例如設置最小寬高,儘量的佔用區域等等。筆者在實際開發中使用的倒不是不少,倒不是說這個控件很差使用,而是好多約束因素是綜合的,例如須要額外的設置margin、padding屬性能,所以單獨再套個這個就顯得很繁瑣了。
class MyLimitedBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Row( children: <Widget>[ new Container( color: Colors.red, width: 100.0, ), LimitedBox( maxWidth: 150.0, child: new Container( color: Colors.blue, width: 250.0, ), ) ], ); } }
效果圖:
源碼解析:
先不說其源碼,單純從其做用,前面介紹的SizedBox、ConstrainedBox都相似,都是經過強加到child的constraint,來達到相應的效果。
咱們直接看其計算constraint的代碼
minWidth: constraints.minWidth, maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth), minHeight: constraints.minHeight, maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)
LimitedBox只是改變最大寬高的限定。具體的佈局代碼以下:
if (child != null) { child.layout(_limitConstraints(constraints), parentUsesSize: true); size = constraints.constrain(child.size); } else { size = _limitConstraints(constraints).constrain(Size.zero); }
根據最大尺寸,限制child的佈局,而後將自身調節到child的尺寸。
使用場景:
是不可能清楚了,光是找例子,就花了很多時間。Flutter的一些冷門控件,真的是除了官方的文檔,啥材料都木有。
谷歌說這個頗有用,仍是一臉懵逼。這種控件,也有其餘的替代解決方案,LimitedBox能夠達到的效果,ConstrainedBox均可以實現。
/** * AspectRatio * 定義了一個高度爲200的區域,內部AspectRatio比率設置爲1.5,最終AspectRatio的是寬300高200的一個區域。 */ class MyAspectRatio extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( height: 200.0, child: new AspectRatio( aspectRatio: 1.5, child: new Container( color: Colors.red, ), ), ); } }
效果圖:
@override RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);
通過前面一些控件的解析,我想你們對這種構造應該不會再陌生了,繪製都是交由RenderObject去完成的,這裏則是由RenderAspectRatio去完成具體的繪製工做。
RenderAspectRatio的構造函數中會對aspectRatio作一些檢測(assert)
aspectRatio不能爲null;
aspectRatio必須大於0;
aspectRatio必須是有限的。
接下來咱們來看一下RenderAspectRatio的具體尺寸計算函數:
(1)若是限制條件爲isTight,則返回最小的尺寸(constraints.smallest);
if (constraints.isTight) return constraints.smallest;
(2)若是寬度爲有限的值,則將高度設置爲width / _aspectRatio。 若是寬度爲無限,則將高度設爲最大高度,寬度設爲height * _aspectRatio;
if (width.isFinite) { height = width / _aspectRatio; } else { height = constraints.maxHeight; width = height * _aspectRatio; }
(3)接下來則是在限制範圍內調整寬高,整體思想則是寬度優先,大於最大值則設爲最大值,小於最小值,則設爲最小值。
若是寬度大於最大寬度,則將其設爲最大寬度,高度設爲width / _aspectRatio;
if (width > constraints.maxWidth) { width = constraints.maxWidth; height = width / _aspectRatio; }
若是高度大於最大高度,則將其設爲最大高度,寬度設爲height * _aspectRatio;
if (height > constraints.maxHeight) { height = constraints.maxHeight; width = height * _aspectRatio; }
若是寬度小於最小寬度,則將其設爲最小寬度,高度設爲width / _aspectRatio;
if (width < constraints.minWidth) { width = constraints.minWidth; height = width / _aspectRatio; }
若是高度小於最小高度,則將其設爲最小高度,寬度設爲height * _aspectRatio。
if (height < constraints.minHeight) { height = constraints.minHeight; width = height * _aspectRatio; }
/** * MyFractionallySizeBox */ class MyFractionallySizeBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( color: Colors.blue, height: 150.0, width: 150.0, padding: const EdgeInsets.all(10.0), child: new FractionallySizedBox( alignment: Alignment.topLeft, widthFactor: 1.5, heightFactor: 0.5, child: new Container( color: Colors.red, ), ), ); } }
效果圖:
源碼解析:
FractionallySizedBox內部具體渲染是由RenderFractionallySizedOverflowBox來實現的,經過命名就能夠看出,這個控件可能會Overflow。
咱們直接看實際計算尺寸的代碼
double minWidth = constraints.minWidth; double maxWidth = constraints.maxWidth;
if (_widthFactor != null) { final double width = maxWidth * _widthFactor; minWidth = width; maxWidth = width; }
double minHeight = constraints.minHeight; double maxHeight = constraints.maxHeight;
if (_heightFactor != null) { final double height = maxHeight * _heightFactor; minHeight = height; maxHeight = height; }
源代碼中,根據寬高因子是否存在,來進行相對應的尺寸計算。
使用場景:
當須要在一個區域裏面取百分比尺寸的時候,可使用這個,比方說,高度40%寬度70%的區域。固然,AspectRatio也能夠達到近似的效果。