本文主要介紹Flutter布局中的FittedBox、AspectRatio、ConstrainedBox,詳細(xì)介紹了其布局行為以及使用場(chǎng)景,并對(duì)源碼進(jìn)行了分析。
Scales and positions its child within itself according to fit.
按照其官方的介紹,它主要做了兩件事情,縮放(Scale)以及位置調(diào)整(Position)。
FittedBox會(huì)在自己的尺寸范圍內(nèi)縮放并且調(diào)整child位置,使得child適合其尺寸。做過(guò)移動(dòng)端的,可能會(huì)聯(lián)想到ImageView控件,它是將圖片在其范圍內(nèi),按照規(guī)則,進(jìn)行縮放位置調(diào)整。FittedBox跟ImageView是有些類(lèi)似的,可以猜測(cè)出,它肯定有一個(gè)類(lèi)似于ScaleType的屬性。
FittedBox的布局行為還算簡(jiǎn)單,官方?jīng)]有給出說(shuō)明,我在這里簡(jiǎn)單說(shuō)一下。由于FittedBox是一個(gè)容器,需要讓其child在其范圍內(nèi)縮放,因此其布局行為分兩種情況:
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FittedBox
從繼承關(guān)系可以看出,F(xiàn)ittedBox控件是一個(gè)基礎(chǔ)控件。
new Container( color: Colors.amberAccent, width: 300.0, height: 300.0, child: new FittedBox( fit: BoxFit.contain, alignment: Alignment.topLeft, child: new Container( color: Colors.red, child: new Text("FittedBox"), ), ),)
寫(xiě)了一個(gè)很簡(jiǎn)單的例子,加入Container是為了加顏色顯示兩個(gè)區(qū)域,讀者可以試著修改fit以及alignment查看其不同的效果。
const FittedBox({Key key,this.fit: BoxFit.contain,this.alignment: Alignment.center,Widget child,})
fit:縮放的方式,默認(rèn)的屬性是BoxFit.contain
,child在FittedBox范圍內(nèi),盡可能的大,但是不超出其尺寸。這里注意一點(diǎn),contain是保持著child寬高比的大前提下,盡可能的填滿(mǎn),一般情況下,寬度或者高度達(dá)到最大值時(shí),就會(huì)停止縮放。
alignment:對(duì)齊方式,默認(rèn)的屬性是Alignment.center
,居中顯示child。
構(gòu)造函數(shù)如下:
@overrideRenderFittedBox createRenderObject(BuildContext context) {return new RenderFittedBox( fit: fit, alignment: alignment, textDirection: Directionality.of(context),);}
FittedBox具體實(shí)現(xiàn)是由RenderFittedBox進(jìn)行的。不知道讀者有沒(méi)有發(fā)現(xiàn),目前的一些基礎(chǔ)控件,繼承自RenderObjectWidget的,widget本身都只是存儲(chǔ)了一些配置信息,真正的繪制渲染,則是由內(nèi)部的createRenderObject所調(diào)用的RenderObject去實(shí)現(xiàn)的。
RenderFittedBox具體的布局代碼如下:
if (child != null) { child.layout(const BoxConstraints(), parentUsesSize: true); // 如果child不為null,則按照child的尺寸比率縮放child的尺寸 size = constraints.constrainSizeAndAttemptToPreserveAspectRatio(child.size); _clearPaintData();} else { // 如果child為null,則按照最小尺寸進(jìn)行布局 size = constraints.smallest;}
FittedBox在目前的項(xiàng)目中還未用到過(guò)。對(duì)于需要縮放調(diào)整位置處理的,一般都是圖片。筆者一般都是使用Container中的decoration屬性去實(shí)現(xiàn)相應(yīng)的效果。對(duì)于其他控件需要縮放以及調(diào)整位置的,目前還沒(méi)有遇到使用場(chǎng)景,大家只需要知道有這么一個(gè)控件,可以實(shí)現(xiàn)這個(gè)功能即可。
A widget that attempts to size the child to a specific aspect ratio.
AspectRatio的作用是調(diào)整child到設(shè)置的寬高比,這種控件在其他移動(dòng)端平臺(tái)上一般都不會(huì)提供,F(xiàn)lutter之所以提供,我想最大的原因,可能就是自定義起來(lái)特別麻煩吧。
AspectRatio的布局行為分為兩種情況:
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio
從繼承關(guān)系看,AspectRatio是基礎(chǔ)的布局控件。
new Container( height: 200.0, child: new AspectRatio( aspectRatio: 1.5, child: new Container( color: Colors.red, ), ),);
示例代碼是定義了一個(gè)高度為200的區(qū)域,內(nèi)部AspectRatio比率設(shè)置為1.5,最終AspectRatio的是寬300高200的一個(gè)區(qū)域。
構(gòu)造函數(shù)如下:
const AspectRatio({Key key,@required this.aspectRatio,Widget child})
構(gòu)造函數(shù)只包含了一個(gè)aspectRatio屬性,其中aspectRatio不能為null。
aspectRatio:aspectRatio是寬高比,最終可能不會(huì)根據(jù)這個(gè)值去布局,具體則要看綜合因素,外層是否允許按照這種比率進(jìn)行布局,只是一個(gè)參考值。
@override RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);
經(jīng)過(guò)前面一些控件的解析,我想大家對(duì)這種構(gòu)造應(yīng)該不會(huì)再陌生了,繪制都是交由RenderObject去完成的,這里則是由RenderAspectRatio去完成具體的繪制工作。
RenderAspectRatio的構(gòu)造函數(shù)中會(huì)對(duì)aspectRatio做一些檢測(cè)(assert)
接下來(lái)我們來(lái)看一下RenderAspectRatio的具體尺寸計(jì)算函數(shù):
if (constraints.isTight) return constraints.smallest;
if (width.isFinite) { height = width / _aspectRatio;} else { height = constraints.maxHeight; width = height * _aspectRatio;}
如果寬度大于最大寬度,則將其設(shè)為最大寬度,高度設(shè)為width / _aspectRatio;
if (width > constraints.maxWidth) { width = constraints.maxWidth; height = width / _aspectRatio;}
如果高度大于最大高度,則將其設(shè)為最大高度,寬度設(shè)為height * _aspectRatio;
if (height > constraints.maxHeight) { height = constraints.maxHeight; width = height * _aspectRatio;}
如果寬度小于最小寬度,則將其設(shè)為最小寬度,高度設(shè)為width / _aspectRatio;
if (width < constraints.minWidth) { width = constraints.minWidth; height = width / _aspectRatio;}
如果高度小于最小高度,則將其設(shè)為最小高度,寬度設(shè)為height * _aspectRatio。
if (height < constraints.minHeight) { height = constraints.minHeight; width = height * _aspectRatio;}
AspectRatio適用于需要固定寬高比的情景下。筆者最近使用這個(gè)控件的場(chǎng)景是相機(jī),相機(jī)的預(yù)覽尺寸都是固定的幾個(gè)值,因此不能隨意去設(shè)置相機(jī)的顯示區(qū)域,必須按照比率進(jìn)行顯示,否則會(huì)出現(xiàn)拉伸的情況。除此之外,倒是用的不多。
A widget that imposes additional constraints on its child.
這個(gè)控件的作用是添加額外的限制條件(constraints)到child上,本身挺簡(jiǎn)單的,可以被一些控件替換使用。Flutter的布局控件體系,梳理著發(fā)現(xiàn)確實(shí)有點(diǎn)亂,感覺(jué)總體思想是缺啥就造啥,哈哈。
ConstrainedBox的布局行為非常簡(jiǎn)單,取決于設(shè)置的限制條件,而關(guān)于父子節(jié)點(diǎn)的限制因素生效優(yōu)先級(jí),可以查看之前的文章,在這里就不做具體敘述了。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > ConstrainedBox
ConstrainedBox也是一個(gè)基礎(chǔ)的布局控件。
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, ),);
例子也挺簡(jiǎn)單的,在一個(gè)寬高200.0的Container上添加一個(gè)約束最大最小寬高的ConstrainedBox,實(shí)際的顯示中,則是一個(gè)寬高為150.0的區(qū)域。
構(gòu)造函數(shù)如下:
ConstrainedBox({Key key,@required this.constraints,Widget child})
包含了一個(gè)constraints屬性,且不能為null。
constraints:添加到child上的額外限制條件,其類(lèi)型為BoxConstraints
。BoxConstraints的作用是干啥的呢?其實(shí)很簡(jiǎn)單,就是限制各種最大最小寬高。說(shuō)到這里插一句,double.infinity在widget布局的時(shí)候是合法的
,也就說(shuō),例如想最大的擴(kuò)展寬度,可以將寬度值設(shè)為double.infinity。
@overrideRenderConstrainedBox createRenderObject(BuildContext context) {return new RenderConstrainedBox(additionalConstraints: constraints);}
RenderConstrainedBox實(shí)現(xiàn)其繪制。其具體的布局表現(xiàn)分兩種情況:
具體代碼如下:
@overridevoid performLayout() {if (child != null) { child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true); size = child.size;} else { size = _additionalConstraints.enforce(constraints).constrain(Size.zero);}}
需要添加額外的約束條件可以使用此控件,例如設(shè)置最小寬高,盡可能的占用區(qū)域等等。筆者在實(shí)際開(kāi)發(fā)中使用的倒不是很多,倒不是說(shuō)這個(gè)控件不好使用,而是好多約束因素是綜合的,例如需要額外的設(shè)置margin、padding屬性能,因此單獨(dú)再套個(gè)這個(gè)就顯得很繁瑣了。
這個(gè)控件不做詳細(xì)介紹了,它跟ConstrainedBox相反,是不添加任何約束條件到child上,讓child按照其原始的尺寸渲染。
很神奇是吧,我也覺(jué)得,其實(shí)它的作用就是給child一個(gè)盡可能大的空間,不加約束的讓其顯示。用處我暫時(shí)木有想到。只能說(shuō)Flutter生產(chǎn)Widget很隨性。
筆者建的一個(gè)Flutter學(xué)習(xí)相關(guān)的項(xiàng)目,Github地址,里面包含了筆者寫(xiě)的關(guān)于Flutter學(xué)習(xí)相關(guān)的一些文章,會(huì)定期更新,也會(huì)上傳一些學(xué)習(xí)demo,歡迎大家關(guān)注。
聯(lián)系客服