Flutter 布局(六)- SizedOverflowBox、Transform、CustomSingleChildLayout詳解

本文主要介紹Flutter布局中的SizedOverflowBox、Transform、CustomSingleChildLayout三種控件,詳細(xì)介紹了其布局行為以及使用場(chǎng)景,并對(duì)源碼進(jìn)行了分析。

1. SizedOverflowBox

A widget that is a specific size but passes its original constraints through to its child, which will probably overflow.

1.1 簡(jiǎn)介

光看名稱,就可以猜出,SizedOverflowBox是SizedBox與OverflowBox的結(jié)合體。

1.2 布局行為

SizedOverflowBox主要的布局行為有兩點(diǎn):

  • 尺寸部分。通過將自身的固定尺寸,傳遞給child,來達(dá)到控制child尺寸的目的;
  • 超出部分??梢酝黄聘腹?jié)點(diǎn)尺寸的限制,超出部分也可以被渲染顯示,與OverflowBox類似。

1.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedOverflowBox

1.4 示例代碼

Container(
  color: Colors.green,
  alignment: Alignment.topRight,
  width: 200.0,
  height: 200.0,
  padding: EdgeInsets.all(5.0),
  child: SizedOverflowBox(
    size: Size(100.0, 200.0),
    child: Container(color: Colors.red, width: 200.0, height: 100.0,),
  ),
);

代碼運(yùn)行的時(shí)候報(bào)出了下面的異常,很神奇。但是同學(xué)們可以自己看看代碼運(yùn)行的效果,可以超出,但是不太好用。

 ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════

1.5 源碼解析

const SizedOverflowBox({
  Key key,
  @required this.size,
  this.alignment = Alignment.center,
  Widget child,
})

1.5.1 屬性解析

size:固定的尺寸。

alignment:對(duì)齊方式。

1.5.2 源碼

直接上布局相關(guān)的代碼:

size = constraints.constrain(_requestedSize);
if (child != null) {
  child.layout(constraints);
  alignChild();
}

如果child存在的話,就將child設(shè)為對(duì)應(yīng)的尺寸,然后按照對(duì)齊方式進(jìn)行對(duì)齊。但是在實(shí)際寫sample的時(shí)候,感覺跟我預(yù)想中的表現(xiàn)不太一致,而且經(jīng)常報(bào)出異常。不知道是我理解錯(cuò)了,還是樣例寫錯(cuò)了,如果有了解的同學(xué),麻煩告知,在此感謝。

1.6 使用場(chǎng)景

代碼的表現(xiàn)跟我預(yù)想中的不太一致,更推薦使用OverflowBox,場(chǎng)景也跟其比較一致。

2. Transform

A widget that applies a transformation before painting its child.

2.1 簡(jiǎn)介

Transform在介紹Container的時(shí)候有提到過,就是做矩陣變換的。Container中矩陣變換就是使用的Transform。

2.2 布局行為

有過其他平臺(tái)經(jīng)驗(yàn)的,對(duì)Transform應(yīng)該不會(huì)陌生??梢詫?duì)child做平移、旋轉(zhuǎn)、縮放等操作。

2.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Transform

2.4 示例代碼

Center(
  child: Transform(
    transform: Matrix4.rotationZ(0.3),
    child: Container(
      color: Colors.blue,
      width: 100.0,
      height: 100.0,
    ),
  ),
)

示例中將Container繞z軸旋轉(zhuǎn)了,代碼很簡(jiǎn)單。Matrix4也提供了很多便捷的構(gòu)造函數(shù)供大家使用,因此寫起來,并不會(huì)有太大的難度。

2.5 源碼解析

const Transform({
  Key key,
  @required this.transform,
  this.origin,
  this.alignment,
  this.transformHitTests = true,
  Widget child,
})

上面是其默認(rèn)的構(gòu)造函數(shù),Transform也提供下面三種構(gòu)造函數(shù):

Transform.rotate
Transform.translate
Transform.scale

2.5.1 屬性解析

transform:一個(gè)4x4的矩陣。不難發(fā)現(xiàn),其他平臺(tái)的變換矩陣也都是四階的。一些復(fù)合操作,僅靠三維是不夠的,必須采用額外的一維來補(bǔ)充,感興趣的同學(xué)可以自行搜索了解。

origin:旋轉(zhuǎn)點(diǎn),相對(duì)于左上角頂點(diǎn)的偏移。默認(rèn)旋轉(zhuǎn)點(diǎn)事左上角頂點(diǎn)。

alignment:對(duì)齊方式。

transformHitTests:點(diǎn)擊區(qū)域是否也做相應(yīng)的改變。

2.5.2 源碼

我們來看看它的繪制代碼:

if (child != null) {
  final Matrix4 transform = _effectiveTransform;
  final Offset childOffset = MatrixUtils.getAsTranslation(transform);
  if (childOffset == null)
    context.pushTransform(needsCompositing, offset, transform, super.paint);
  else
    super.paint(context, offset + childOffset);
}

整個(gè)繪制代碼不復(fù)雜,如果child有偏移的話,則將兩個(gè)偏移相加,進(jìn)行繪制。如果child沒有偏移的話,則按照設(shè)置的offset、transform進(jìn)行繪制。

2.6 使用場(chǎng)景

這個(gè)控件算是較常見的控件,很多平移、旋轉(zhuǎn)、縮放都可以使用的到。如果只是單純的進(jìn)行變換的話,用Transform比用Container效率會(huì)更高。

3. CustomSingleChildLayout

A widget that defers the layout of its single child to a delegate.

3.1 簡(jiǎn)介

一個(gè)通過外部傳入的布局行為,來進(jìn)行布局的控件,不同于其他固定布局的控件,我們自定義一些單節(jié)點(diǎn)布局控件的時(shí)候,可以考慮使用它。

3.2 布局行為

CustomSingleChildLayout提供了一個(gè)控制child布局的delegate,這個(gè)delegate可以控制這些因素:

  • 可以控制child的布局constraints;
  • 可以控制child的位置;
  • 在parent的尺寸不依賴于child的情況下,可以決定parent的尺寸。

3.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > CustomSingleChildLayout

3.4 示例代碼

class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
  FixedSizeLayoutDelegate(this.size);

  final Size size;

  @override
  Size getSize(BoxConstraints constraints) => size;

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    return new BoxConstraints.tight(size);
  }

  @override
  bool shouldRelayout(FixedSizeLayoutDelegate oldDelegate) {
    return size != oldDelegate.size;
  }
}

Container(
  color: Colors.blue,
  padding: const EdgeInsets.all(5.0),
  child: CustomSingleChildLayout(
    delegate: FixedSizeLayoutDelegate(Size(200.0, 200.0)),
    child: Container(
      color: Colors.red,
      width: 100.0,
      height: 300.0,
    ),
  ),
)

由于SingleChildLayoutDelegate是一個(gè)抽象類,我們需要單獨(dú)寫一個(gè)繼承自它的delegate,來進(jìn)行相關(guān)的布局操作。上面例子中是一個(gè)固定尺寸的布局。

3.5 源碼解析

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

const CustomSingleChildLayout({
  Key key,
  @required this.delegate,
  Widget child
})

3.5.1 屬性解析

alignment:一個(gè)抽象類,我們需要自行實(shí)現(xiàn)布局的類。

3.5.2 源碼

我們直接來看其布局函數(shù):

size = _getSize(constraints);
if (child != null) {
  final BoxConstraints childConstraints = delegate.getConstraintsForChild(constraints);
  assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
  child.layout(childConstraints, parentUsesSize: !childConstraints.isTight);
  final BoxParentData childParentData = child.parentData;
  childParentData.offset = delegate.getPositionForChild(size, childConstraints.isTight ? childConstraints.smallest : child.size);
}

其child的constraints是通過delegate傳入的,而這個(gè)delegate則是我們通過外部繼承自SingleChildLayoutDelegate實(shí)現(xiàn)的。

我們接下來看一下SingleChildLayoutDelegate提供了哪些接口:

Size getSize(BoxConstraints constraints) => constraints.biggest;
BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints;
Offset getPositionForChild(Size size, Size childSize) => Offset.zero;
bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate);

其中前三個(gè)都是布局相關(guān)的,包括尺寸、constraints、位置。最后一個(gè)函數(shù)是判斷是否需要重新布局的。我們?cè)诰帉慸elegate的時(shí)候,可以選擇需要進(jìn)行修改的屬性進(jìn)行重寫,并給予相應(yīng)的布局屬性即可。

3.6 使用場(chǎng)景

這種控件用起來可能會(huì)繁瑣一些,但是我們可以通過這個(gè)控件去封裝一些基礎(chǔ)的控件,供他人使用。

4. 階段性小結(jié)

到目前為止,F(xiàn)lutter中單子節(jié)點(diǎn)布局控件,大致上都簡(jiǎn)單的梳理了一遍。如果一直在關(guān)注這個(gè)系列文章的同學(xué),應(yīng)該可以發(fā)現(xiàn)我經(jīng)常吐槽其控件設(shè)計(jì)。

Flutter中總共有18個(gè)單子節(jié)點(diǎn)布局控件,18個(gè)啊,還沒有算多子節(jié)點(diǎn)布局控件,也沒有算可能會(huì)新增的。這樣的學(xué)習(xí)成本非常高。雖然常見的就那么幾種,平時(shí)一直用那些也都沒有問題,但是布局的時(shí)候總是會(huì)遇到那么幾種解決不了的問題。而且梳理的時(shí)候,會(huì)發(fā)現(xiàn),幾種控件都能解決的問題,并不是說把效果實(shí)現(xiàn)出了就完事了,這中間肯定會(huì)涉及到哪種控件效率更高。如果要對(duì)Flutter項(xiàng)目做較深入的性能優(yōu)化,這些控件肯定都得掌握。

Flutter的這種設(shè)計(jì),把一些原本應(yīng)該由它們承擔(dān)的成本,轉(zhuǎn)移到了開發(fā)者身上。很多控件在日常使用中幾乎都用不上,并沒有考慮太多實(shí)際的使用場(chǎng)景。當(dāng)然了,也還是得安慰自己,這畢竟只是初期,亂點(diǎn)就亂點(diǎn),日子肯定會(huì)越來越好的。

后面還會(huì)將多子節(jié)點(diǎn)控件全部梳理一遍,然后來個(gè)大總結(jié),對(duì)什么場(chǎng)景該使用哪種控件,如何進(jìn)行控件級(jí)別的優(yōu)化,做一個(gè)總結(jié)。

5. 后話

筆者建了一個(gè)Flutter學(xué)習(xí)相關(guān)的項(xiàng)目,Github地址,里面包含了筆者寫的關(guān)于Flutter學(xué)習(xí)相關(guān)的一些文章,會(huì)定期更新,也會(huì)上傳一些學(xué)習(xí)Demo,歡迎大家關(guān)注。

6. 參考

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

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

  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,231評(píng)論 3 119
  • 更多信息請(qǐng)查看flutter layout Layouts Sigle-child layout widgets ...
    one_cup閱讀 29,088評(píng)論 1 17
  • 本文主要介紹了Flutter布局相關(guān)的內(nèi)容,對(duì)相關(guān)知識(shí)點(diǎn)進(jìn)行了梳理,并從實(shí)際例子觸發(fā),進(jìn)一步講解該如何去進(jìn)行布局。...
    Q吹個(gè)大氣球Q閱讀 10,207評(píng)論 6 51
  • 本文主要介紹Flutter布局中的FittedBox、AspectRatio、ConstrainedBox,詳細(xì)介...
    Q吹個(gè)大氣球Q閱讀 19,251評(píng)論 2 21
  • 推薦朋友~賀媽咪,寶媽一枚,專心研究教育事業(yè),關(guān)注嬰幼兒成長(zhǎng),性格,教育培養(yǎng),加上自己養(yǎng)娃的一些經(jīng)驗(yàn),家有寶寶的寶...
    賀媽咪閱讀 134評(píng)論 0 0

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