zoukankan      html  css  js  c++  java
  • Flutter 布局类组件:流式布局(Wrap和Flow)

    前言

    把超出屏幕显示范围会自动折行的布局称为流式布局。Flutter中通过Wrap和Flow来支持流式布局,将Row换成Wrap后溢出部分则会自动折行。

    Wrap

    接口描述

    Wrap({
        Key key,
        this.direction = Axis.horizontal,
        this.alignment = WrapAlignment.start,
        // 主轴方向子widget的间距
        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>[],
      })
    
    

    代码示例

    class WrapTest extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('流式布局-Wrap'),
          ),
          body: Container(
            child: Wrap(
              // 主轴(水平)方向间距
              spacing: 8.0,
              // 纵轴(垂直)方向间距
              runSpacing: 4.0,
              // 沿主轴方向居中
              alignment: WrapAlignment.center,
              children: <Widget>[
                Chip(
                  avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text('A'),),
                  label: Text('HelloWorld'),
                ),
    
                Chip(
                  avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text('B'),),
                  label: Text('WorldHello'),
                ),
    
                Chip(
                  avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text('T'),),
                  label: Text('FlowWorld'),
                ),
    
                Chip(
                  avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text('J'),),
                  label: Text('WarpHello'),
                ),
    
                Chip(
                  avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text('F'),),
                  label: Text('Yes i do'),
                ),
    
              ],
            ),
          ),
        );
      }
    }
    
    

    FLow

    一般很少会使用Flow,因为其过于复杂,需要自己实现子widget的位置转换,在很多场景下首先要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。
    Flow有如下优点

    1. 性能好;Flow是一个对子组件尺寸以及位置调整非常高效的控件,Flow用转换矩阵在对子组件进行位置调整的时候进行了优化:在Flow定位过后,如果子组件的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild 进行重绘,而context.paintChild在重绘时使用了转换矩阵,并没有实际调整组件位置。
    2. 灵活;由于我们需要自己实现FlowDelegate的paintChildren()方法,所以我们需要自己计算每一个组件的位置,因此,可以自定义布局策略。

    缺点

    1. 使用复杂。
    2. 不能自适应子组件大小,必须通过指定父容器大小或实现TestFlowDelegate的getSize返回固定大小。

    接口描述

     Flow({
        Key key,
        @required this.delegate,
        List<Widget> children = const <Widget>[],
      })
    
    

    代码示例

    class FlowTest extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('流式布局-Flow'),
          ),
          body: Container(
            child: Flow(
              delegate: FlowDelegateTest(margin: EdgeInsets.all(10.0)),
              children: <Widget>[
                Container( 80.0, height: 80.0, color: Colors.red,),
                Container( 80.0, height: 80.0, color: Colors.green,),
                Container( 80.0, height: 80.0, color: Colors.blue,),
                Container( 80.0, height: 80.0, color: Colors.yellow,),
                Container( 80.0, height: 80.0, color: Colors.brown,),
                Container( 80.0, height: 80.0, color: Colors.purple,),
              ],
    
            ),
          ),
        );
    
      }
    }
    
    class FlowDelegateTest extends FlowDelegate {
      EdgeInsets margin = EdgeInsets.zero;
      FlowDelegateTest({this.margin});
    
      // Flow主要要重载这个函数,它的主要任务是确定每个子widget位置。
      // 由于Flow不能自适应子widget的大小,通过在getSize返回一个固定大小来指定Flow的大小。
      @override
      void paintChildren(FlowPaintingContext context) {
        var x = margin.left;
        var y = margin.right;
        // 计算每一个子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: 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: Matrix4.translationValues(x, y, 0.0)
            );
            x += context.getChildSize(i).width + margin.left + margin.right;
          }
        }
      }
    
      @override
      Size getSize(BoxConstraints constraints) {
        // 指定Flow的大小
        return Size(double.infinity, 200.0);
      }
    
      @override
      bool shouldRepaint(FlowDelegate oldDelegate) {
        return oldDelegate != this;
      }
    }
    
    
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 奇偶判断
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Qt: 访问容器(三种方法,加上for循环就四种了)good
  • 原文地址:https://www.cnblogs.com/parzulpan/p/12071664.html
Copyright © 2011-2022 走看看