Flutter UI - Wrap、Flow 實現(xiàn)流式布局

流式布局很經(jīng)典了,F(xiàn)lutter 提供了2種方式,Wrap 用著省事,weightTree 里配置就行了,F(xiàn)low 可以自定義顯示,需要自己寫代碼


Wrap

Wrap 好寫,子 view 寫死,不知道有沒有可以代碼動態(tài)添加的實現(xiàn)方式。Wrap 和 Flow 其實和 row、colume 產(chǎn)不多,就是多了可以自動換行的選項,屬性的話大家參照 row、colume 理解

先看屬性:

Wrap({
  ...
  this.direction = Axis.horizontal,
  this.alignment = WrapAlignment.start,
  this.spacing = 0.0,
  this.runAlignment = WrapAlignment.start,
  this.runSpacing = 0.0,
  this.crossAxisAlignment = WrapCrossAlignment.start,
  this.textDirection,
  this.verticalDirection = VerticalDirection.down,
  List<Widget> children = const <Widget>[],
})
  • direction - 這個是方向,一般我們都是用在橫向的多
  • alignment - 主軸對齊方式,參考 row、colume 的
  • spacing - 主軸方向每個 weight 間隔
  • runSpacing - 交叉軸方向間隔,在橫向時這玩意就是行間距
  • runAlignment、crossAxisAlignment - 我試了試沒啥用,有 alignment 就夠用了
class AText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      width: 100,
      height: 40,
      color: Colors.blueAccent,
      child: Text("AAAAAA"),
    );
  }
}
class AA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 25,
      runSpacing: 5,
      direction: Axis.horizontal,
      alignment: WrapAlignment.spaceEvenly,
      children: <Widget>[
        AText(),
        AText(),
        AText(),
        AText(),
        AText(),
        AText(),
        AText(),
      ],
    );
  }
}

Wrap 使用上很方便,一般能用 Wrap 就用 Wrap,雖說 Wrap 能實現(xiàn)的 Flow 也一樣 OK,但是 Flow 需要自己寫代碼實現(xiàn)換行


Flow

我們一般很少會使用Flow,因為其過于復(fù)雜,需要自己實現(xiàn)子widget的位置轉(zhuǎn)換,在很多場景下首先要考慮的是Wrap是否滿足需求。Flow主要用于一些需要自定義布局策略或性能要求較高(如動畫中)的場景

其實我對 Flow 并不熟悉,據(jù)說 Flow 性能比 Wrap 要好,具體的我也沒看懂,下面是原話:

性能好;Flow是一個對child尺寸以及位置調(diào)整非常高效的控件,F(xiàn)low用轉(zhuǎn)換矩陣(transformation matrices)在對child進行位置調(diào)整的時候進行了優(yōu)化:在Flow定位過后,如果child的尺寸或者位置發(fā)生了變化,在FlowDelegate中的paintChildren()方法中調(diào)用context.paintChild 進行重繪,而context.paintChild在重繪時使用了轉(zhuǎn)換矩陣(transformation matrices),并沒有實際調(diào)整Widget位置

flow 抽象了一個接口 FlowDelegate 用來定位子 view 的位置,F(xiàn)lowDelegate 接口里最重要的就是 paintChildren()方法了,在這個方法里我們遍歷所有子 view 計算他們的位置,然后根據(jù)自己的意愿換行還是換列,能自定義效果

但是 flow 非常蛋疼的地方在于無法支持子 view 自定義大小,所有子 view 必須統(tǒng)一設(shè)置寬高值,這在實際中怎么可能啊,getSize 方法返回子view 寬高大小

演示圖

這里我們給 FlowDelegate 接口傳入橫向間距和縱向間距

class AA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Flow(
      // 這里我們給 FlowDelegate 接口傳入橫向間距和縱向間距
      delegate: ADelegate(10, 10),
      children: <Widget>[
          new Container(width: 80.0, height:80.0, color: Colors.red,),
          new Container(width: 80.0, height:80.0, color: Colors.green,),
          new Container(width: 80.0, height:80.0, color: Colors.blue,),
          new Container(width: 80.0, height:80.0,  color: Colors.yellow,),
          new Container(width: 80.0, height:80.0, color: Colors.black,),
          new Container(width: 80.0, height:80.0,  color: Colors.purple,),
        ],
    );
  }
}

FlowDelegate 接口不是照搬網(wǎng)上的,是我自己寫的,看著還算工整,網(wǎng)上的代碼風(fēng)格太亂,沒法看

class ADelegate extends FlowDelegate {
  var wSpacing = 0.0;
  var hSpacing = 0.0;

  ADelegate(this.wSpacing, this.hSpacing);

  @override
  void paintChildren(FlowPaintingContext context) {
    var pWidth = context.size.width;
    var x = 0.0;
    var y = 0.0;
    var childWidth = 0.0;
    var childSize = new Size(0, 0);

    for (int i = 0; i < context.childCount; i++) {
      childSize = context.getChildSize(i);
      childWidth = x + childSize.width;
      // 子 view 的寬度要是比父控件的 width 還大就得換行了
      if (childWidth <= pWidth) {
        // 不換行,定位子 view 位置
        context.paintChild(I,
            transform: new Matrix4.translationValues(x, y, 0.0));
        // 然后把 x 坐標(biāo)偏移量算出來
        x = childWidth + wSpacing;
      } else {
        // 此時換行了,先重置 x 坐標(biāo)偏移量
        x = 0;
        // 因為要顯示在當(dāng)前行的下一行,就得先把下一行起始 Y 坐標(biāo)算出來
        y = y + childSize.height + hSpacing;
        context.paintChild(I,
            transform: new Matrix4.translationValues(x, y, 0.0));
        // 然后別忘了計算 x 坐標(biāo)偏移量
        x = childSize.width + wSpacing;
      }
    }
  }

  getSize(BoxConstraints constraints) {
    //指定Flow的大小
    return Size(double.infinity, 200.0);
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return oldDelegate != this;
  }
}
?著作權(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)容