Flutter 布局(三)- FittedBox扼褪、AspectRatio想幻、ConstrainedBox詳解

本文主要介紹Flutter布局中的FittedBox、AspectRatio话浇、ConstrainedBox脏毯,詳細介紹了其布局行為以及使用場景,并對源碼進行了分析幔崖。

1. FittedBox

Scales and positions its child within itself according to fit.

1.1 簡介

按照其官方的介紹食店,它主要做了兩件事情,縮放(Scale)以及位置調(diào)整(Position)赏寇。

FittedBox會在自己的尺寸范圍內(nèi)縮放并且調(diào)整child位置吉嫩,使得child適合其尺寸。做過移動端的嗅定,可能會聯(lián)想到ImageView控件自娩,它是將圖片在其范圍內(nèi),按照規(guī)則渠退,進行縮放位置調(diào)整忙迁。FittedBox跟ImageView是有些類似的,可以猜測出碎乃,它肯定有一個類似于ScaleType的屬性姊扔。

1.2 布局行為

FittedBox的布局行為還算簡單,官方?jīng)]有給出說明荠锭,我在這里簡單說一下旱眯。由于FittedBox是一個容器,需要讓其child在其范圍內(nèi)縮放证九,因此其布局行為分兩種情況:

  • 如果外部有約束的話删豺,按照外部約束調(diào)整自身尺寸,然后縮放調(diào)整child愧怜,按照指定的條件進行布局呀页;
  • 如果沒有外部約束條件,則跟child尺寸一致拥坛,指定的縮放以及位置屬性將不起作用蓬蝶。

1.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FittedBox

從繼承關(guān)系可以看出尘分,F(xiàn)ittedBox控件是一個基礎(chǔ)控件。

1.4 示例代碼

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"),
    ),
  ),
)

寫了一個很簡單的例子丸氛,加入Container是為了加顏色顯示兩個區(qū)域培愁,讀者可以試著修改fit以及alignment查看其不同的效果。

1.5 源碼解析

const FittedBox({
Key key,
this.fit: BoxFit.contain,
this.alignment: Alignment.center,
Widget child,
})

1.5.1 屬性解析

fit:縮放的方式缓窜,默認的屬性是BoxFit.contain定续,child在FittedBox范圍內(nèi),盡可能的大禾锤,但是不超出其尺寸私股。這里注意一點,contain是保持著child寬高比的大前提下恩掷,盡可能的填滿倡鲸,一般情況下,寬度或者高度達到最大值時黄娘,就會停止縮放峭状。

BoxFit布局表現(xiàn)

alignment:對齊方式,默認的屬性是Alignment.center逼争,居中顯示child宁炫。

1.5.2 源碼

構(gòu)造函數(shù)如下:

@override
RenderFittedBox createRenderObject(BuildContext context) {
return new RenderFittedBox(
  fit: fit,
  alignment: alignment,
  textDirection: Directionality.of(context),
);
}

FittedBox具體實現(xiàn)是由RenderFittedBox進行的。不知道讀者有沒有發(fā)現(xiàn)氮凝,目前的一些基礎(chǔ)控件,繼承自RenderObjectWidget的望忆,widget本身都只是存儲了一些配置信息罩阵,真正的繪制渲染,則是由內(nèi)部的createRenderObject所調(diào)用的RenderObject去實現(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,則按照最小尺寸進行布局
  size = constraints.smallest;
}

1.6 使用場景

FittedBox在目前的項目中還未用到過歉备。對于需要縮放調(diào)整位置處理的傅是,一般都是圖片。筆者一般都是使用Container中的decoration屬性去實現(xiàn)相應(yīng)的效果蕾羊。對于其他控件需要縮放以及調(diào)整位置的喧笔,目前還沒有遇到使用場景,大家只需要知道有這么一個控件龟再,可以實現(xiàn)這個功能即可书闸。

2. AspectRatio

A widget that attempts to size the child to a specific aspect ratio.

2.1 簡介

AspectRatio的作用是調(diào)整child到設(shè)置的寬高比,這種控件在其他移動端平臺上一般都不會提供利凑,F(xiàn)lutter之所以提供浆劲,我想最大的原因嫌术,可能就是自定義起來特別麻煩吧。

2.2 布局行為

AspectRatio的布局行為分為兩種情況:

  • AspectRatio首先會在布局限制條件允許的范圍內(nèi)盡可能的擴展牌借,widget的高度是由寬度和比率決定的度气,類似于BoxFit中的contain,按照固定比率去盡量占滿區(qū)域膨报。
  • 如果在滿足所有限制條件過后無法找到一個可行的尺寸磷籍,AspectRatio最終將會去優(yōu)先適應(yīng)布局限制條件,而忽略所設(shè)置的比率丙躏。

2.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio

從繼承關(guān)系看择示,AspectRatio是基礎(chǔ)的布局控件。

2.4 示例代碼

new Container(
  height: 200.0,
  child: new AspectRatio(
    aspectRatio: 1.5,
    child: new Container(
      color: Colors.red,
    ),
  ),
);

示例代碼是定義了一個高度為200的區(qū)域晒旅,內(nèi)部AspectRatio比率設(shè)置為1.5栅盲,最終AspectRatio的是寬300高200的一個區(qū)域。

2.5 源碼解析

構(gòu)造函數(shù)如下:

const AspectRatio({
Key key,
@required this.aspectRatio,
Widget child
}) 

構(gòu)造函數(shù)只包含了一個aspectRatio屬性废恋,其中aspectRatio不能為null谈秫。

2.5.1 屬性解析

aspectRatio:aspectRatio是寬高比,最終可能不會根據(jù)這個值去布局鱼鼓,具體則要看綜合因素拟烫,外層是否允許按照這種比率進行布局,只是一個參考值迄本。

2.5.2 源碼

@override
  RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);

經(jīng)過前面一些控件的解析硕淑,我想大家對這種構(gòu)造應(yīng)該不會再陌生了,繪制都是交由RenderObject去完成的嘉赎,這里則是由RenderAspectRatio去完成具體的繪制工作置媳。

RenderAspectRatio的構(gòu)造函數(shù)中會對aspectRatio做一些檢測(assert)

  • aspectRatio不能為null;
  • aspectRatio必須大于0公条;
  • aspectRatio必須是有限的拇囊。

接下來我們來看一下RenderAspectRatio的具體尺寸計算函數(shù):

  • 如果限制條件為isTight,則返回最小的尺寸(constraints.smallest)靶橱;
if (constraints.isTight)
  return constraints.smallest;
  • 如果寬度為有限的值寥袭,則將高度設(shè)置為width / _aspectRatio。 如果寬度為無限关霸,則將高度設(shè)為最大高度传黄,寬度設(shè)為height * _aspectRatio;
if (width.isFinite) {
  height = width / _aspectRatio;
} else {
  height = constraints.maxHeight;
  width = height * _aspectRatio;
}
  • 接下來則是在限制范圍內(nèi)調(diào)整寬高谒拴,總體思想則是寬度優(yōu)先尝江,大于最大值則設(shè)為最大值,小于最小值英上,則設(shè)為最小值炭序。

如果寬度大于最大寬度啤覆,則將其設(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;
}

2.6 使用場景

AspectRatio適用于需要固定寬高比的情景下苍苞。筆者最近使用這個控件的場景是相機,相機的預(yù)覽尺寸都是固定的幾個值狼纬,因此不能隨意去設(shè)置相機的顯示區(qū)域羹呵,必須按照比率進行顯示,否則會出現(xiàn)拉伸的情況疗琉。除此之外冈欢,倒是用的不多。

3. ConstrainedBox

A widget that imposes additional constraints on its child.

3.1 簡介

這個控件的作用是添加額外的限制條件(constraints)到child上盈简,本身挺簡單的凑耻,可以被一些控件替換使用。Flutter的布局控件體系柠贤,梳理著發(fā)現(xiàn)確實有點亂拳话,感覺總體思想是缺啥就造啥,哈哈种吸。

3.2 布局行為

ConstrainedBox的布局行為非常簡單,取決于設(shè)置的限制條件呀非,而關(guān)于父子節(jié)點的限制因素生效優(yōu)先級坚俗,可以查看之前的文章,在這里就不做具體敘述了岸裙。

3.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > ConstrainedBox

ConstrainedBox也是一個基礎(chǔ)的布局控件猖败。

3.4 示例代碼

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,
  ),
);

例子也挺簡單的,在一個寬高200.0的Container上添加一個約束最大最小寬高的ConstrainedBox降允,實際的顯示中恩闻,則是一個寬高為150.0的區(qū)域。

3.5 源碼解析

構(gòu)造函數(shù)如下:

ConstrainedBox({
Key key,
@required this.constraints,
Widget child
})

包含了一個constraints屬性剧董,且不能為null幢尚。

3.5.1 屬性解析

constraints:添加到child上的額外限制條件破停,其類型為BoxConstraints。BoxConstraints的作用是干啥的呢尉剩?其實很簡單真慢,就是限制各種最大最小寬高。說到這里插一句理茎,double.infinity在widget布局的時候是合法的黑界,也就說,例如想最大的擴展寬度皂林,可以將寬度值設(shè)為double.infinity朗鸠。

3.5.2 源碼

@override
RenderConstrainedBox createRenderObject(BuildContext context) {
return new RenderConstrainedBox(additionalConstraints: constraints);
}

RenderConstrainedBox實現(xiàn)其繪制。其具體的布局表現(xiàn)分兩種情況:

  • 如果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);
}
}

3.6 使用場景

需要添加額外的約束條件可以使用此控件扰楼,例如設(shè)置最小寬高,盡可能的占用區(qū)域等等美浦。筆者在實際開發(fā)中使用的倒不是很多弦赖,倒不是說這個控件不好使用,而是好多約束因素是綜合的浦辨,例如需要額外的設(shè)置margin蹬竖、padding屬性能,因此單獨再套個這個就顯得很繁瑣了流酬。

3.7 關(guān)于UnconstrainedBox

這個控件不做詳細介紹了币厕,它跟ConstrainedBox相反,是不添加任何約束條件到child上芽腾,讓child按照其原始的尺寸渲染旦装。

很神奇是吧,我也覺得摊滔,其實它的作用就是給child一個盡可能大的空間阴绢,不加約束的讓其顯示。用處我暫時木有想到艰躺。只能說Flutter生產(chǎn)Widget很隨性呻袭。

4. 后話

筆者建的一個Flutter學(xué)習(xí)相關(guān)的項目,Github地址腺兴,里面包含了筆者寫的關(guān)于Flutter學(xué)習(xí)相關(guān)的一些文章左电,會定期更新,也會上傳一些學(xué)習(xí)demo,歡迎大家關(guān)注篓足。

5. 參考

  1. FittedBox class
  2. BoxFit enum
  3. AspectRatio class
  4. ConstrainedBox class
  5. BoxConstraints class
  6. UnconstrainedBox class
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末段誊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纷纫,更是在濱河造成了極大的恐慌枕扫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辱魁,死亡現(xiàn)場離奇詭異烟瞧,居然都是意外死亡,警方通過查閱死者的電腦和手機染簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門参滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锻弓,你說我怎么就攤上這事砾赔。” “怎么了青灼?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵暴心,是天一觀的道長。 經(jīng)常有香客問我杂拨,道長专普,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任弹沽,我火速辦了婚禮檀夹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘策橘。我一直安慰自己炸渡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布丽已。 她就那樣靜靜地躺著蚌堵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沛婴。 梳的紋絲不亂的頭發(fā)上辰斋,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音瘸味,去河邊找鬼。 笑死够挂,一個胖子當(dāng)著我的面吹牛旁仿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼枯冈,長吁一口氣:“原來是場噩夢啊……” “哼毅贮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尘奏,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤滩褥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后炫加,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瑰煎,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年俗孝,在試婚紗的時候發(fā)現(xiàn)自己被綠了酒甸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡赋铝,死狀恐怖插勤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情革骨,我是刑警寧澤农尖,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站良哲,受9級特大地震影響盛卡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜臂外,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一窟扑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漏健,春花似錦嚎货、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓦盛,卻和暖如春洗显,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背原环。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工挠唆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘱吗。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓玄组,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子俄讹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容