Flutter 佈局(四)- Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth詳解

本文主要介紹Flutter佈局中的Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth四種控件,詳細介紹了其佈局行爲以及使用場景,並對源碼進行了分析。html

1. Baseline

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

1.1 簡介

Baseline這個控件,作過移動端開發的都會了解過,通常文字排版的時候,可能會用到它。它的做用很簡單,根據child的baseline,來調整child的位置。例如兩個字號不同的文字,但願底部在一條水平線上,就可使用這個控件,是一個很是基礎的控件。github

關於字符的Baseline,能夠看下下面這張圖,這具體就涉及到了字體排版,感興趣的同窗能夠自行了解。bash

Baseline

1.2 佈局行爲

Baseline控件佈局行爲分爲兩種狀況:app

  • 若是child有baseline,則根據child的baseline屬性,調整child的位置;
  • 若是child沒有baseline,則根據child的bottom,來調整child的位置。

1.3 繼承關係

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,
        ),
      ),
    ),
  ],
)
複製代碼

上述運行結果是左右兩個文本跟中間的Container底部在一個水平線上,這也印證了Baseline的佈局行爲。ide

Baseline樣例

1.5 源碼解析

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

1.5.1 屬性解析

baseline:baseline數值,必需要有,從頂部算。函數

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這個函數是獲取baseline數值的,存在的話,就取這個值,不存在的話,則取其高度。字體

總體的計算過程:

  1. 獲取child的 baseline 值;
  2. 計算出top值,其爲 baseline - childBaseline,這個值有可能爲負數;
  3. 計算出Baseline控件尺寸,寬度爲child的,高度則爲 top + childSize.height。

1.6 使用場景

跟字符對齊相關的會用到,其餘場景暫時沒有想到。

2. FractionallySizedBox

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

2.1 簡介

FractionallySizedBox控件會根據現有空間,來調整child的尺寸,因此說child就算設置了具體的尺寸數值,也不起做用。

2.2 佈局行爲

FractionallySizedBox的佈局行爲主要跟它的寬高因子兩個參數有關,當參數爲null或者有具體數值的時候,佈局表現不同。固然,還有一個輔助參數alignment,做爲對齊方式進行佈局。

  • 當設置了具體的寬高因子,具體的寬高則根據現有空間寬高 * 因子,有可能會超出父控件的範圍,當寬高因子大於1的時候;
  • 當沒有設置寬高因子,則填滿可用區域;

2.3 繼承關係

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,
      ),
    ),
  )
複製代碼

運行效果以下所示

FractionallySizedBox例子

2.5 源碼解析

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

2.5.1 屬性解析

alignment:對齊方式,不能爲null。

widthFactor:寬度因子,跟以前介紹的控件相似,寬度乘以這個值,就是最後的寬度。

heightFactor:高度因子,用做計算最後實際高度的。

其中widthFactor和heightFactor都有一個規則

  • 若是不爲null,那麼實際的最大寬高度則爲child的寬高乘以這個因子;
  • 若是爲null,那麼child的寬高則會盡可能充滿整個區域。

2.5.2 源碼

FractionallySizedBox內部具體渲染是由RenderFractionallySizedOverflowBox來實現的,經過命名就能夠看出,這個控件可能會Overflow。

咱們直接看實際計算尺寸的代碼

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;
}
複製代碼

源代碼中,根據寬高因子是否存在,來進行相對應的尺寸計算。這個過程很是簡單,再也不贅述。

2.6 使用場景

當須要在一個區域裏面取百分比尺寸的時候,可使用這個,比方說,高度40%寬度70%的區域。固然,AspectRatio也能夠達到近似的效果。

3. IntrinsicHeight

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

3.1 簡介

IntrinsicHeight的做用是調整child到固定的高度。這個控件筆者也是看了好久,不知道它的做用是什麼,官方說這個頗有用,可是應該儘可能少用,由於其效率問題。

3.2 佈局行爲

這個控件的做用,是將可能高度不受限制的child,調整到一個合適而且合理的尺寸。

3.3 繼承關係

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例子

當沒有IntrinsicHeight包裹着,能夠看到,第一三個Container高度是不受限制的,當外層套一個IntrinsicHeight,第一三個Container高度就調整到第二個同樣的高度。

3.5 源碼解析

構造函數以下:

const IntrinsicHeight({ Key key, Widget child })
複製代碼

3.5.1 屬性解析

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

3.5.2 源碼

當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;
複製代碼

首先會檢測是否只有一個高度值知足約束條件,若是不是的話,則返回一個最小的高度。而後調整尺寸。

3.6 使用場景

說老實話,不知道在什麼場景使用,能夠替代的控件也有的。谷歌說頗有用,效率會有問題,建議通常的就別用了。

4. IntrinsicWidth

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

4.1 簡介

IntrinsicWidth從描述看,跟IntrinsicHeight相似,一個是調整高度,一個是調整寬度。一樣是會存在效率問題,能別使用就儘可能別使用。

4.2 佈局行爲

IntrinsicWidth不一樣於IntrinsicHeight,它包含了額外的兩個參數,stepHeight以及stepWidth。而IntrinsicWidth的佈局行爲跟這兩個參數相關。

  • 當stepWidth不是null的時候,child的寬度將會是stepWidth的倍數,當stepWidth值比child最小寬度小的時候,這個值不起做用;
  • 當stepWidth爲null的時候,child的寬度是child的最小寬度;
  • 當stepHeight不爲null的時候,效果跟stepWidth相同;
  • 當stepHeight爲null的時候,高度取最大高度。

4.3 繼承關係

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設置不一樣的值,能夠看到不一樣的效果,當step值比最小寬高小的時候,這個值實際上是不起做用的。感興趣的同窗能夠本身試試。

4.5 源碼解析

構造函數

const IntrinsicWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })
複製代碼

4.5.1 屬性解析

stepWidth:能夠爲null,效果參看上面所說的佈局行爲。

stepHeight:能夠爲null,效果參看上面所說的佈局行爲。

4.5.2 源碼

咱們先來看看佈局代碼中_applyStep函數

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

若是存在step數值的話,則會是step的倍數,若是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的額外數值。整體的佈局表現跟上面分析的佈局行爲一致,根據step值是不是null來進行判斷,可是注意其對待高度與寬度的表現略有差別。

4.6 使用場景

這個控件,說老實話,筆者仍是不知道該在什麼場景下使用,可能會有些特殊的場景。可是從IntrinsicWidth與IntrinsicHeight佈局差別看,Flutter基礎控件封的確實很隨性,一些無關緊要甚至是重複的控件,我以爲精簡精簡挺好的,哈哈。

5. 後話

筆者建了一個Flutter學習相關的項目,Github地址,裏面包含了筆者寫的關於Flutter學習相關的一些文章,會按期更新,也會上傳一些學習Demo,歡迎你們關注。

6. 參考

  1. Baseline class
  2. 基線
  3. FractionallySizedBox class
  4. IntrinsicHeight class
  5. IntrinsicWidth class
相關文章
相關標籤/搜索