Flutter 布局(四)- Baseline舔示、FractionallySizedBox、IntrinsicHeight脑融、IntrinsicWidth詳解

本文主要介紹Flutter布局中的Baseline喻频、FractionallySizedBox、IntrinsicHeight肘迎、IntrinsicWidth四種控件甥温,詳細(xì)介紹了其布局行為以及使用場景,并對源碼進(jìn)行了分析妓布。

1. Baseline

A widget that positions its child according to the child's baseline.

1.1 簡介

Baseline這個控件姻蚓,做過移動端開發(fā)的都會了解過,一般文字排版的時候匣沼,可能會用到它狰挡。它的作用很簡單释涛,根據(jù)child的baseline加叁,來調(diào)整child的位置。例如兩個字號不一樣的文字唇撬,希望底部在一條水平線上它匕,就可以使用這個控件,是一個非辰讶希基礎(chǔ)的控件豫柬。

關(guān)于字符的Baseline,可以看下下面這張圖扑浸,這具體就涉及到了字體排版烧给,感興趣的同學(xué)可以自行了解。

Baseline

1.2 布局行為

Baseline控件布局行為分為兩種情況:

  • 如果child有baseline喝噪,則根據(jù)child的baseline屬性创夜,調(diào)整child的位置;
  • 如果child沒有baseline仙逻,則根據(jù)child的bottom驰吓,來調(diào)整child的位置涧尿。

1.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Baseline

1.4 示例代碼

new Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: <Widget>[
    new Baseline(
      baseline: 50.0,
      baselineType: TextBaseline.alphabetic,
      child: new Text(
        'TjTjTj',
        style: new TextStyle(
          fontSize: 20.0,
          textBaseline: TextBaseline.alphabetic,
        ),
      ),
    ),
    new Baseline(
      baseline: 50.0,
      baselineType: TextBaseline.alphabetic,
      child: new Container(
        width: 30.0,
        height: 30.0,
        color: Colors.red,
      ),
    ),
    new Baseline(
      baseline: 50.0,
      baselineType: TextBaseline.alphabetic,
      child: new Text(
        'RyRyRy',
        style: new TextStyle(
          fontSize: 35.0,
          textBaseline: TextBaseline.alphabetic,
        ),
      ),
    ),
  ],
)

上述運(yùn)行結(jié)果是左右兩個文本跟中間的Container底部在一個水平線上,這也印證了Baseline的布局行為檬贰。

Baseline樣例

1.5 源碼解析

const Baseline({
  Key key,
  @required this.baseline,
  @required this.baselineType,
  Widget child
})

1.5.1 屬性解析

baseline:baseline數(shù)值姑廉,必須要有,從頂部算翁涤。

baselineType:bseline類型桥言,也是必須要有的,目前有兩種類型:

  • alphabetic:對齊字符底部的水平線葵礼;
  • ideographic:對齊表意字符的水平線号阿。

1.5.2 源碼

我們來看看源碼中具體計算尺寸的這段代碼

child.layout(constraints.loosen(), parentUsesSize: true);
final double childBaseline = child.getDistanceToBaseline(baselineType);
final double actualBaseline = baseline;
final double top = actualBaseline - childBaseline;
final BoxParentData childParentData = child.parentData;
childParentData.offset = new Offset(0.0, top);
final Size childSize = child.size;
size = constraints.constrain(new Size(childSize.width, top + childSize.height));

getDistanceToBaseline這個函數(shù)是獲取baseline數(shù)值的,存在的話鸳粉,就取這個值扔涧,不存在的話,則取其高度届谈。

整體的計算過程:

  1. 獲取child的 baseline 值枯夜;
  2. 計算出top值,其為 baseline - childBaseline艰山,這個值有可能為負(fù)數(shù)湖雹;
  3. 計算出Baseline控件尺寸,寬度為child的曙搬,高度則為 top + childSize.height摔吏。

1.6 使用場景

跟字符對齊相關(guān)的會用到,其他場景暫時沒有想到纵装。

2. FractionallySizedBox

A widget that sizes its child to a fraction of the total available space

2.1 簡介

FractionallySizedBox控件會根據(jù)現(xiàn)有空間舔腾,來調(diào)整child的尺寸,所以說child就算設(shè)置了具體的尺寸數(shù)值搂擦,也不起作用稳诚。

2.2 布局行為

FractionallySizedBox的布局行為主要跟它的寬高因子兩個參數(shù)有關(guān),當(dāng)參數(shù)為null或者有具體數(shù)值的時候瀑踢,布局表現(xiàn)不一樣扳还。當(dāng)然,還有一個輔助參數(shù)alignment橱夭,作為對齊方式進(jìn)行布局氨距。

  • 當(dāng)設(shè)置了具體的寬高因子,具體的寬高則根據(jù)現(xiàn)有空間寬高 * 因子棘劣,有可能會超出父控件的范圍俏让,當(dāng)寬高因子大于1的時候;
  • 當(dāng)沒有設(shè)置寬高因子,則填滿可用區(qū)域首昔;

2.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FractionallySizedBox

2.4 示例代碼

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

運(yùn)行效果如下所示

FractionallySizedBox例子

2.5 源碼解析

const FractionallySizedBox({
  Key key,
  this.alignment = Alignment.center,
  this.widthFactor,
  this.heightFactor,
  Widget child,
})

2.5.1 屬性解析

alignment:對齊方式寡喝,不能為null。

widthFactor:寬度因子勒奇,跟之前介紹的控件類似预鬓,寬度乘以這個值,就是最后的寬度赊颠。

heightFactor:高度因子格二,用作計算最后實(shí)際高度的。

其中widthFactor和heightFactor都有一個規(guī)則

  • 如果不為null竣蹦,那么實(shí)際的最大寬高度則為child的寬高乘以這個因子顶猜;
  • 如果為null,那么child的寬高則會盡量充滿整個區(qū)域痘括。

2.5.2 源碼

FractionallySizedBox內(nèi)部具體渲染是由RenderFractionallySizedOverflowBox來實(shí)現(xiàn)的长窄,通過命名就可以看出,這個控件可能會Overflow远寸。

我們直接看實(shí)際計算尺寸的代碼

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;
}

源代碼中抄淑,根據(jù)寬高因子是否存在屠凶,來進(jìn)行相對應(yīng)的尺寸計算驰后。這個過程非常簡單,不再贅述矗愧。

2.6 使用場景

當(dāng)需要在一個區(qū)域里面取百分比尺寸的時候灶芝,可以使用這個,比方說唉韭,高度40%寬度70%的區(qū)域夜涕。當(dāng)然,AspectRatio也可以達(dá)到近似的效果属愤。

3. IntrinsicHeight

A widget that sizes its child to the child's intrinsic height.

3.1 簡介

IntrinsicHeight的作用是調(diào)整child到固定的高度女器。這個控件筆者也是看了很久,不知道它的作用是什么住诸,官方說這個很有用驾胆,但是應(yīng)該盡量少用,因?yàn)槠湫蕟栴}贱呐。

3.2 布局行為

這個控件的作用丧诺,是將可能高度不受限制的child,調(diào)整到一個合適并且合理的尺寸奄薇。

3.3 繼承關(guān)系

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > IntrinsicHeight

3.4 示例代碼

new IntrinsicHeight(
  child: new Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: <Widget>[
      new Container(color: Colors.blue, width: 100.0),
      new Container(color: Colors.red, width: 50.0,height: 50.0,),
      new Container(color: Colors.yellow, width: 150.0),
    ],
  ),
);
IntrinsicHeight例子

當(dāng)沒有IntrinsicHeight包裹著驳阎,可以看到,第一三個Container高度是不受限制的,當(dāng)外層套一個IntrinsicHeight呵晚,第一三個Container高度就調(diào)整到第二個一樣的高度蜘腌。

3.5 源碼解析

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

const IntrinsicHeight({ Key key, Widget child })

3.5.1 屬性解析

除了child,沒有提供額外的屬性劣纲。

3.5.2 源碼

當(dāng)child不為null的時候逢捺,具體的布局代碼如下:

BoxConstraints childConstraints = constraints;
if (!childConstraints.hasTightHeight) {
  final double height = child.getMaxIntrinsicHeight(childConstraints.maxWidth);
  assert(height.isFinite);
  childConstraints = childConstraints.tighten(height: height);
}
child.layout(childConstraints, parentUsesSize: true);
size = child.size;

首先會檢測是否只有一個高度值滿足約束條件,如果不是的話癞季,則返回一個最小的高度劫瞳。然后調(diào)整尺寸。

3.6 使用場景

說老實(shí)話绷柒,不知道在什么場景使用志于,可以替代的控件也有的。谷歌說很有用废睦,效率會有問題伺绽,建議一般的就別用了。

4. IntrinsicWidth

A widget that sizes its child to the child's intrinsic width.

4.1 簡介

IntrinsicWidth從描述看嗜湃,跟IntrinsicHeight類似奈应,一個是調(diào)整高度,一個是調(diào)整寬度购披。同樣是會存在效率問題杖挣,能別使用就盡量別使用。

4.2 布局行為

IntrinsicWidth不同于IntrinsicHeight刚陡,它包含了額外的兩個參數(shù)惩妇,stepHeight以及stepWidth。而IntrinsicWidth的布局行為跟這兩個參數(shù)相關(guān)筐乳。

  • 當(dāng)stepWidth不是null的時候歌殃,child的寬度將會是stepWidth的倍數(shù),當(dāng)stepWidth值比child最小寬度小的時候蝙云,這個值不起作用氓皱;
  • 當(dāng)stepWidth為null的時候,child的寬度是child的最小寬度勃刨;
  • 當(dāng)stepHeight不為null的時候波材,效果跟stepWidth相同;
  • 當(dāng)stepHeight為null的時候朵你,高度取最大高度各聘。

4.3 繼承關(guān)系

Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > IntrinsicWidth

4.4 示例代碼

new Container(
  color: Colors.green,
  padding: const EdgeInsets.all(5.0),
  child: new IntrinsicWidth(
    stepHeight: 450.0,
    stepWidth: 300.0,
    child: new Column(
      children: <Widget>[
        new Container(color: Colors.blue, height: 100.0),
        new Container(color: Colors.red, width: 150.0, height: 100.0),
        new Container(color: Colors.yellow, height: 150.0,),
      ],
    ),
  ),
)
IntrinsicWidth例子

分別對stepWidth以及stepHeight設(shè)置不同的值,可以看到不同的效果抡医,當(dāng)step值比最小寬高小的時候躲因,這個值其實(shí)是不起作用的早敬。感興趣的同學(xué)可以自己試試。

4.5 源碼解析

構(gòu)造函數(shù)

const IntrinsicWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })

4.5.1 屬性解析

stepWidth:可以為null大脉,效果參看上面所說的布局行為搞监。

stepHeight:可以為null,效果參看上面所說的布局行為镰矿。

4.5.2 源碼

我們先來看看布局代碼中_applyStep函數(shù)

static double _applyStep(double input, double step) {
assert(input.isFinite);
if (step == null)
  return input;
return (input / step).ceil() * step;
}

如果存在step數(shù)值的話琐驴,則會是step的倍數(shù),如果step為null秤标,則返回原始的尺寸绝淡。

接下來我們看看child不為null時候的布局代碼

BoxConstraints childConstraints = constraints;
if (!childConstraints.hasTightWidth) {
  final double width = child.getMaxIntrinsicWidth(childConstraints.maxHeight);
  assert(width.isFinite);
  childConstraints = childConstraints.tighten(width: _applyStep(width, _stepWidth));
}
if (_stepHeight != null) {
  final double height = child.getMaxIntrinsicHeight(childConstraints.maxWidth);
  assert(height.isFinite);
  childConstraints = childConstraints.tighten(height: _applyStep(height, _stepHeight));
}
child.layout(childConstraints, parentUsesSize: true);
size = child.size;

寬度方面的布局跟IntrinsicHeight高度部分相似,只是多了一個step的額外數(shù)值苍姜±谓停總體的布局表現(xiàn)跟上面分析的布局行為一致,根據(jù)step值是否是null來進(jìn)行判斷衙猪,但是注意其對待高度與寬度的表現(xiàn)略有差異馍乙。

4.6 使用場景

這個控件,說老實(shí)話垫释,筆者還是不知道該在什么場景下使用丝格,可能會有些特殊的場景。但是從IntrinsicWidth與IntrinsicHeight布局差異看棵譬,F(xiàn)lutter基礎(chǔ)控件封的確實(shí)很隨性显蝌,一些可有可無甚至是重復(fù)的控件,我覺得精簡精簡挺好的茫船,哈哈琅束。

5. 后話

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

6. 參考

  1. Baseline class
  2. 基線
  3. FractionallySizedBox class
  4. IntrinsicHeight class
  5. IntrinsicWidth class
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葵腹,一起剝皮案震驚了整個濱河市高每,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌践宴,老刑警劉巖鲸匿,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異阻肩,居然都是意外死亡带欢,警方通過查閱死者的電腦和手機(jī)运授,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乔煞,“玉大人吁朦,你說我怎么就攤上這事《杉郑” “怎么了逗宜?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長空骚。 經(jīng)常有香客問我纺讲,道長,這世上最難降的妖魔是什么囤屹? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任刻诊,我火速辦了婚禮,結(jié)果婚禮上牺丙,老公的妹妹穿的比我還像新娘则涯。我一直安慰自己,他們只是感情好冲簿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布粟判。 她就那樣靜靜地躺著,像睡著了一般峦剔。 火紅的嫁衣襯著肌膚如雪档礁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天吝沫,我揣著相機(jī)與錄音呻澜,去河邊找鬼。 笑死惨险,一個胖子當(dāng)著我的面吹牛羹幸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辫愉,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栅受,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恭朗?” 一聲冷哼從身側(cè)響起屏镊,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痰腮,沒想到半個月后而芥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膀值,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年棍丐,在試婚紗的時候發(fā)現(xiàn)自己被綠了弟翘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡骄酗,死狀恐怖稀余,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情趋翻,我是刑警寧澤睛琳,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站踏烙,受9級特大地震影響师骗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讨惩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一辟癌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荐捻,春花似錦黍少、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至魂角,卻和暖如春昵济,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背野揪。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工访忿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斯稳。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓海铆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親平挑。 傳聞我的和親對象是個殘疾皇子游添,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354