目 录CONTENT

文章目录

Flutter 布局控件篇-->Wrap、Flow

俊阳IT知识库
2023-03-06 / 0 评论 / 0 点赞 / 511 阅读 / 1,212 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-03-14,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
广告

文章已同步至掘金:https://juejin.cn/post/6844903956825505799
欢迎访问😃,有任何问题都可留言评论哦~

前面的文章,我们说了:

线性布局Flutter 布局控件篇–>Row、Column

弹性布局Flutter 布局控件篇–>Flex、Expanded

接下来我们说一下流式布局

在线性布局Row中,如果子widget超出屏幕范围,则会报溢出错误,如:

Row(
  children: <Widget>[
    Text("xxx"*100)
  ],
);

运行效果:

flutter-wrap-flow-2

那么有人会说了,怎么不用Text组件中的maxLinesoverflow呢?

很显然,Row组件的主轴是水平方向的,是让其内容水平排布的,Row默认只有一行,如果超出屏幕不会折行。这两个属性加上的话,是没有效果的

但是如果把Row换成Column,那么这两个属性就会起作用了

但是我们如果用上面两个属性来指定的话,这样也不好,因为你并不确定有多少内容,所以我们想让他如果超出屏幕显示范围的话,让他自动折行,这种布局称之为流式布局。

Flutter中通过WrapFlow来支持流式布局

Wrap

流式布局组件,如果内容宽度超出屏幕宽度,会自动折行显示

源码示例

Wrap的构造函数:

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>[],
})

属性解释

我们可以看到Wrap的很多属性在RowColumnFlex中也有,

所以重复的属性就不说了,如果不知道的话,请移步:Flutter 布局控件篇–>Row、Column

这里说Wrap特有的几个属性

spacing

主轴方向子组件的间距
相信学过HTML和CSS的话,应该不陌生

在这里就是组件与组件之间的间隙

例如:

Wrap(
  spacing: 20,
  children: <Widget>[
	Text("xxx"),
	Text('yyy')
  ],
)

spacing: 20,表示的是xxxyyy之间的距离

runSpacing

组件纵轴方向的间距,不做过多解释

alignment

纵轴方向的对齐方式

例如:

Wrap(
  spacing: 20,	//主轴(水平)方向间距
  runSpacing: 50,	//纵轴方向间距
  alignment:WrapAlignment.center ,	//沿主轴方向居中
  children: <Widget>[
	Text("xxx"*20),
	Text('yyy')
  ],
)

运行效果:

flutter-wrap-flow-3

Flow

一般很少使用Flow,因为太复杂了。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。

优点:

  • 性能好;Flow是一个对子组件尺寸以及位置调整非常高效的控件,Flow用转换矩阵在对子组件进行位置调整的时候进行了优化:在Flow定位过后,如果子组件的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild进行重绘,而context.paintChild在重绘时使用了转换矩阵,并没有实际调整组件位置。

  • 灵活;由于我们需要自己实现FlowDelegatepaintChildren()方法,所以我们需要自己计算每一个组件的位置,因此,可以自定义布局策略。

缺点:

  • 使用复杂。

  • 不能自适应子组件大小,必须通过指定父容器大小或实现TestFlowDelegategetSize返回固定大小。

例子:

对六个色块进行自定义流式布局:

Flow(
  delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
  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.brown,),
    new Container(width: 80.0, height:80.0,  color: Colors.purple,),
  ],
)

实现TestFlowDelegate

class TestFlowDelegate extends FlowDelegate {
  EdgeInsets margin = EdgeInsets.zero;
  TestFlowDelegate({this.margin});
  @override
  void paintChildren(FlowPaintingContext context) {
    var x = margin.left;
    var y = margin.top;
    //计算每一个子widget的位置  
    for (int i = 0; i < context.childCount; i++) {
      var w = context.getChildSize(i).width + x + margin.right;
      if (w < context.size.width) {
        context.paintChild(i,
            transform: new Matrix4.translationValues(
                x, y, 0.0));
        x = w + margin.left;
      } else {
        x = margin.left;
        y += context.getChildSize(i).height + margin.top + margin.bottom;
        //绘制子widget(有优化)  
        context.paintChild(i,
            transform: new Matrix4.translationValues(
                x, y, 0.0));
         x += context.getChildSize(i).width + margin.left + margin.right;
      }
    }
  }

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

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return oldDelegate != this;
  }
}

运行效果:

flutter-wrap-flow-1

可以看到我们主要的任务就是实现paintChildren,它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小,我们通过在getSize返回一个固定大小来指定Flow的大小。


V_V

0

评论区