double.infinity 到底有啥用?

做 Android 的朋友都知道,在 Android 中,限定子元素大小的方法有三種,分別是 match_parentwrap_content、fixed size。如果使用 ConstraintLayout,還會有 match_constraint。

這些值被設(shè)置到子元素的 layout_width 和 layout_height,由父元素解析生成 LayoutParams 設(shè)置給子元素,并在父元素的 measure 過程中使用它們以及父元素得到的 measureSpec 來確定子元素的大小。雖然子元素的大小最終由父元素決定,但父元素幾乎不會違背子元素對自身大小的期望。因此你可以認(rèn)為在 Android 中子元素的大小可以由子元素自己決定。子元素想要什么大小,給自身的 layout_width 和 layout_height 指定不同的值即可。

而在 Flutter 中,就完全不一樣了。Flutter 積極組合的設(shè)計,希望一切布局都盡量通過組合的方式來實現(xiàn)。組合優(yōu)于繼承這是真理,但 Flutter 卻走向了極端。連指定子元素的大小、padding、margin、translate、background 等等都需要通過嵌套來實現(xiàn),這就是導(dǎo)致嵌套地獄的根源。

以指定子元素的大小為例,你需要給子元素套一層 SizedBox,通過它來限定子元素的大小。基于此,你可以理解為在一般情況下,Flutter 中子元素的大小無法由子元素自己決定,始終由其父元素決定,這和 Android 有本質(zhì)的區(qū)別。

Flutter 也沒有提供 match_parent 來讓子元素?fù)螡M父元素,wrap_content 來讓子元素內(nèi)容有多大就撐多大。但是它提供了 double.infinity。我們一般使用它來讓子元素?fù)螡M父元素。但其實 double.infinity 也用來表示 wrap_content。這得從 Flutter 的布局過程說起。

Flutter 布局過程

Flutter 的布局過程總結(jié)起來就三句話:

  1. 父元素向子元素傳遞約束(BoxConstraint)
  2. 子元素根據(jù)父元素傳遞來的約束以及自身的內(nèi)容屬性測量自身,并向父元素反饋自身大?。⊿ize)
  3. 父元素根據(jù)子元素的大小以及自身的布局屬性決定自身的大小以及子元素的 offset

這里有一個很重要的點是,子元素的大小永遠(yuǎn)不能違背父元素的約束,否則就會拋異常。子元素為自身的 size 賦值時,會觸發(fā)以下檢測:

if (!constraints.isSatisfiedBy(_size!)) {
  throw FlutterError.fromParts(<DiagnosticsNode>[
    ErrorSummary('$runtimeType does not meet its constraints.'),
    DiagnosticsProperty<BoxConstraints>('Constraints', constraints, style: DiagnosticsTreeStyle.errorProperty),
    DiagnosticsProperty<Size>('Size', _size, style: DiagnosticsTreeStyle.errorProperty),
    ErrorHint(
      'If you are not writing your own RenderBox subclass, then this is not '
      'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.md',
    ),
  ]);
}

為了避免這個異常,子元素在設(shè)置自身 size 之前,會先嘗試滿足父元素的約束:

double contentWidth;

// calculate content width

double realWidth = constraints.constrainWidth(contentWidth);

這樣就能保證設(shè)置的 size 永遠(yuǎn)滿足父元素的約束,不引發(fā)異常。

當(dāng)你使用 double.infinity 嘗試撐滿父元素時,這里的 contentWidth 值為 double.infinity,但 constraints.constrainWidth 會返回父元素的寬度,這就起到了 match_parent 的作用。這里的隱含意思是子元素的大小不可能真的為無限大,因為沒有任何一個 UI 框架能渲染無限大的東西,因為那意味著無限大的資源消耗。

wrap_content 又是咋回事呢?其實也是同樣的道理。

當(dāng)父元素對子元素沒有大小要求時,會向子元素傳遞寬松約束,即只限制最大的大小,一般為父元素的大小。意思是子元素想要多大就給多大,但不能超過自身大小。但有些 Widget 就沒有這個限制,它們允許子元素的大小超過自己的大小,因此它們給子元素傳遞的最大大小為 double.infinity。但子元素測量自己時,發(fā)現(xiàn)父元素給的最大大小為 double.infinity,不可能真的將自身大小設(shè)置為 double.infinity,因為 Flutter 沒法渲染無限大的東西。如果你嘗試著這么做,同樣會拋異常。所以一般情況下,子元素會直接將 double.infinity 當(dāng)作 wrap_content 處理,向父元素反饋自己的內(nèi)容大小。

所以總結(jié)下來就是:

  1. 自下而上的 double.infinity 代表 match_parent
  2. 自上而下的 double.infinity 代表 wrap_content

當(dāng)然一切的原因都在于 Flutter 設(shè)計之初沒有考慮像 Android 那樣用 -1 來表示 match_parent,-2 來表示 wrap_content。如果這么做的話,就沒有 double.infinity 什么事了。

多數(shù)時候你都可以使用 double.infinity 來達(dá)到 match_parent 的效果,但也不總是這樣。你永遠(yuǎn)如法用它來自下而上的達(dá)到 wrap_content 的效果。因為它只能自上而下。真想讓子元素自身大小為 wrap_content,那就結(jié)合支持 wrap_content 的 Widget 比如 Center 使用吧?;蛘呤褂?Flutter ConstraintLayout。它自帶了 matchParent、wrapContentfixedSize、matchConstraint 的支持。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容