美文网首页Flutter
Flutter跨平台移动端开发丨Column、Row、Flex、

Flutter跨平台移动端开发丨Column、Row、Flex、

作者: MobMsg | 来源:发表于2019-05-24 22:10 被阅读11次

    目录

    1. Column Widget(垂直布局)
    2. Row Widget(水平布局)
    3. Flex Widget and Expanded(弹性布局及扩展)
    4. Wrap Widget(流式布局)
    5. Flow Widget(自定义流式布局)
    6. Stack Widget and Positioned(层叠布局及定位)

    Column Widget(垂直布局)

    子 widget 按照垂直方向排列,继承自 flex

      Column({
        Key key,
        MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
        MainAxisSize mainAxisSize = MainAxisSize.max,
        CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
        TextDirection textDirection,
        VerticalDirection verticalDirection = VerticalDirection.down,
        TextBaseline textBaseline,
        List<Widget> children = const <Widget>[],
      }) : super(
        children: children,
        key: key,
        direction: Axis.vertical,
        mainAxisAlignment: mainAxisAlignment,
        mainAxisSize: mainAxisSize,
        crossAxisAlignment: crossAxisAlignment,
        textDirection: textDirection,
        verticalDirection: verticalDirection,
        textBaseline: textBaseline,
      );
    
    • key:当前元素的唯一标识符(类似于 Android 中的 id)
    • mainAxisAlignment :表示子 widget 在 Column 空间内的对齐方式。当值为 mainAxisSize = min 时无意义,因为此时 Column 的宽度 = 所有子 widget 的宽度之和。当 mainAxisSize = max 时有意义,MainAxisAlignment.start表示沿textDirection的初始方向对齐,如textDirection取值为TextDirection.ltr时,则MainAxisAlignment.start表示左对齐,textDirection取值为TextDirection.rtl时表示从右对齐。而MainAxisAlignment.end和MainAxisAlignment.start正好相反;MainAxisAlignment.center表示居中对齐。textDirection是mainAxisAlignment的参考系
    • mainAxisSize :表示 Column 在水平方向占用的空间。max 表示最大宽度,min 表示最小宽度,也就是所有子 widget 的宽度之和
    • crossAxisAlignment:表示子 widgets 在纵轴方向的对齐方式,Column 的高度等于子 widgets 中高度最高子元素的高度。它的取值和MainAxisAlignment一样(包含start、end、 center三个值),verticalDirection 是 crossAxisAlignment 的参考系
    • textDirection:表示水平方向子 widget 的布局顺序,默认为由左向右
    • verticalDirection:表示 Column 的纵轴对齐方式,默认为 VerticalDirection.down 从上到下
    • textBaseline:文本绘制基线(alphabetic/ideographic)
    • children:子 widget 集合
    /**
     * @des Column Widget
     * @author liyongli 20190422
     * */
    class ColumnWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _ColumnState();
    
    }
    
    /**
     * @des Column Widget State
     * @author liyongli 20190422
     * */
    class _ColumnState extends State<ColumnWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Row Widget"),
            ),
    
            body: new Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              verticalDirection: VerticalDirection.up,
    
              children: <Widget>[
                new Text("我是一只小小鸟 ",
                style: new TextStyle(
                  color: Color(0xff2196F3),
                  height: 3.0
                )),
    
                new Text("飞着飞着",
                    style: new TextStyle(
                        color: Color(0xff2196F3),
                        height: 2.0
                )),
    
                new Text("我更有劲了",
                    style: new TextStyle(
                        color: Color(0xff2196F3),
                        height: 4.0
                ))
              ],
            ),
          ),
        );
      }
    }
    

    Row Widget(水平布局)

    子 widget 按照水平方向排列,继承自 flex

      Row({
        Key key,
        MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
        MainAxisSize mainAxisSize = MainAxisSize.max,
        CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
        TextDirection textDirection,
        VerticalDirection verticalDirection = VerticalDirection.down,
        TextBaseline textBaseline,
        List<Widget> children = const <Widget>[],
      }) : super(
        children: children,
        key: key,
        direction: Axis.horizontal,
        mainAxisAlignment: mainAxisAlignment,
        mainAxisSize: mainAxisSize,
        crossAxisAlignment: crossAxisAlignment,
        textDirection: textDirection,
        verticalDirection: verticalDirection,
        textBaseline: textBaseline,
      );
    
    • key:当前元素的唯一标识符(类似于 Android 中的 id)
    • mainAxisAlignment :表示子 widget 在 row 空间内的对齐方式。当值为 mainAxisSize = min 时无意义,因为此时 row 的宽度 = 所有子 widget 的宽度之和。当 mainAxisSize = max 时有意义,MainAxisAlignment.start表示沿textDirection的初始方向对齐,如textDirection取值为TextDirection.ltr时,则MainAxisAlignment.start表示左对齐,textDirection取值为TextDirection.rtl时表示从右对齐。而MainAxisAlignment.end和MainAxisAlignment.start正好相反;MainAxisAlignment.center表示居中对齐。textDirection是mainAxisAlignment的参考系
    • mainAxisSize :表示 row 在水平方向占用的空间。max 表示最大宽度,min 表示最小宽度,也就是所有子 widget 的宽度之和
    • crossAxisAlignment:表示子 widgets 在纵轴方向的对齐方式,row 的高度等于子 widgets 中高度最高子元素的高度。它的取值和MainAxisAlignment一样(包含start、end、 center三个值),verticalDirection 是 crossAxisAlignment 的参考系
    • textDirection:表示水平方向子 widget 的布局顺序,默认为由左向右
    • verticalDirection:表示 row 的纵轴对齐方式,默认为 VerticalDirection.down 从上到下
    • textBaseline:文本绘制基线(alphabetic/ideographic)
    • children:子 widget 集合
    import 'package:flutter/material.dart';
    
    /**
     * @des Row Widget
     * @author liyongli 20190422
     * */
    class RowWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _RowState();
    
    }
    
    /**
     * @des Row Widget State
     * @author liyongli 20190422
     * */
    class _RowState extends State<RowWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Row Widget"),
            ),
    
            body: new Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.end,
              verticalDirection: VerticalDirection.up,
    
              children: <Widget>[
                new Text("我是一只小小鸟 ",
                style: new TextStyle(
                  color: Color(0xff2196F3),
                  height: 3.0
                )),
    
                new Text(" 翅膀硬了",
                    style: new TextStyle(
                        color: Color(0xff2196F3),
                        height: 2.0
                )),
    
                new Text(" 不停的飞",
                    style: new TextStyle(
                        color: Color(0xff2196F3),
                        height: 4.0
                ))
              ],
            ),
          ),
        );
      }
    }
    

    Flex Widget and Expanded(弹性布局及扩展)

    Flex Widget(弹性布局)

    flex 可以按水平或垂直方向排列子 widget,并且允许子 widget 按照比例分配父 widget 的空间,row 和 column 均继承自 flex

      Flex({
        Key key,
        @required this.direction,
        this.mainAxisAlignment = MainAxisAlignment.start,
        this.mainAxisSize = MainAxisSize.max,
        this.crossAxisAlignment = CrossAxisAlignment.center,
        this.textDirection,
        this.verticalDirection = VerticalDirection.down,
        this.textBaseline,
        List<Widget> children = const <Widget>[],
      })
    
    • key:当前元素的唯一标识符(类似于 Android 中的 id)
    • direction:弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
    • mainAxisAlignment :表示子 widget 在 flex 空间内的对齐方式。当值为 mainAxisSize = min 时无意义,因为此时 row 的宽度 = 所有子 widget 的宽度之和。当 mainAxisSize = max 时有意义,MainAxisAlignment.start表示沿textDirection的初始方向对齐,如textDirection取值为TextDirection.ltr时,则MainAxisAlignment.start表示左对齐,textDirection取值为TextDirection.rtl时表示从右对齐。而MainAxisAlignment.end和MainAxisAlignment.start正好相反;MainAxisAlignment.center表示居中对齐。textDirection是mainAxisAlignment的参考系
    • mainAxisSize :表示 flex 在水平方向占用的空间。max 表示最大宽度,min 表示最小宽度,也就是所有子 widget 的宽度之和
    • crossAxisAlignment:表示子 widgets 在纵轴方向的对齐方式,flex 的高度等于子 widgets 中高度最高子元素的高度。它的取值和MainAxisAlignment一样(包含start、end、 center三个值),verticalDirection 是 crossAxisAlignment 的参考系
    • textDirection:表示水平方向子 widget 的布局顺序,默认为由左向右
    • verticalDirection:表示 flex 的纵轴对齐方式,默认为 VerticalDirection.down 从上到下
    • textBaseline:文本绘制基线(alphabetic/ideographic)
    • children:子 widget 集合
    Expanded(扩展)

    可以按照设定的比例值扩展/扩大在 row、column、flex 布局中 widget 的所用空间

    Expanded({
      flex:1, // 当 flex = 0 时为占用空间不扩展
      child: Container(
        height: 30.0
       ),
    })
    
    水平方向扩展
    /**
     * @des Flex Widget
     * @author liyongli 20190422
     * */
    class FlexWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _FlexState();
    
    }
    
    /**
     * @des Flex Widget State
     * @author liyongli 20190422
     * */
    class _FlexState extends State<FlexWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Row Widget"),
            ),
    
            body: new Column(
              children: <Widget>[
    
                new Flex(
                  direction: Axis.horizontal,
                  children: <Widget>[
    
                    Expanded(
                      flex: 1,
                      child: Container(
                        color: Color(0xff333333),
                        height: 20.0,
                      ),
                    ),
    
                    Expanded(
                      flex: 2,
                      child: Container(
                        color: Color(0xff999999),
                        height: 20.0,
                      ),
                    ),
    
                    Expanded(
                      flex: 2,
                      child: Container(
                        color: Color(0xff666666),
                        height: 20.0,
                      ),
                    )
    
                  ],
                )
              ],
            ),
          ),
        );
      }
    }
    
    垂直方向扩展
    /**
     * @des Flex Widget
     * @author liyongli 20190422
     * */
    class FlexWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _FlexState();
    
    }
    
    /**
     * @des Flex Widget State
     * @author liyongli 20190422
     * */
    class _FlexState extends State<FlexWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Flex Widget"),
            ),
    
            body: new Center(
              child: new SizedBox(
                width: 20,
                child: new Flex(
                  direction: Axis.vertical,
                  children: <Widget>[
                    
                    // 第一部分占五分之一
                    Expanded(
                      flex: 1,
                      child: Container(
                        color: Color(0xff333333),
                        height: 20,
                      ),
                    ),
    
                    // 第二部分占五分之二
                    Expanded(
                      flex: 2,
                      child: Container(
                        color: Color(0xff999999),
                        height: 20,
                      ),
                    ),
    
                    // 第三部分占五分之二
                    Expanded(
                      flex: 2,
                      child: Container(
                        color: Color(0xff666666),
                        height: 20,
                      ),
                    ),
                  ],
                ),
              )
            )
          ),
        );
      }
    }
    

    Wrap Widget(流式布局)

    若布局中包含的 widget 超出屏幕范围,且需要自动折行展示,那么你需要使用流式布局 wrap 来实现,wrap 的构成与 flex + row + column 相似

      Wrap({
        Key key,
        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>[],
      }) : super(key: key, children: children);
    

    (key,alignment,crossAxisAlignment,textDirection,verticalDirection,children 参数意义与 flex / row / column 相同)

    • spacing:行间距
    • runAlignment:列间距
    • runSpacing:纵轴方向上对齐方式
    /**
     * @des Wrap Widget
     * @author liyongli 20190423
     * */
    class WrapWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _WrapState();
    
    }
    
    /**
     * @des Wrap Widget State
     * @author liyongli 20190423
     * */
    class _WrapState extends State<WrapWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Wrap Widget"),
            ),
    
            body: new Center(
                child: new Wrap(
                  spacing: 20.0, // 主轴(水平)方向间距
                  runSpacing: 5.0, // 纵轴(垂直)方向间距
                  alignment: WrapAlignment.center, //沿主轴方向居中
                  children: <Widget>[
                    new RaisedButton(
                      child: new Text("A"),
                      color: Colors.blue ,
                      textColor: Colors.white,
                      onPressed: _BtnClick,
                    ),
    
                    new RaisedButton(
                      child: new Text("B"),
                      color: Colors.blue ,
                      textColor: Colors.white,
                      onPressed: _BtnClick,
                    ),
    
                    new RaisedButton(
                      child: new Text("C"),
                      color: Colors.blue ,
                      textColor: Colors.white,
                      onPressed: _BtnClick,
                    ),
    
                    new RaisedButton(
                      child: new Text("D"),
                      color: Colors.blue ,
                      textColor: Colors.white,
                      onPressed: _BtnClick,
                    ),
    
                    new RaisedButton(
                      child: new Text("E"),
                      color: Colors.blue ,
                      textColor: Colors.white,
                      onPressed: _BtnClick,
                    ),
    
                  ],
                )
            )
          ),
        );
      }
    
      // 按钮点击监听
      void _BtnClick(){
        print("不设置点击事件按钮会是灰色的!");
      }
    
    }
    

    Flow Widget(自定义流式布局)

    可灵活实现自定义需求布局,且性能较好,但是使用方式复杂

    flow 官方介绍是一个对 child 尺寸以及位置调整非常高效的控件,主要是得益于其FlowDelegate。另外 flow 在用转换矩阵(transformation matrices)对child进行位置调整的时候进行了优化

    Flow之所以高效,是因为其在定位过后,如果使用FlowDelegate中的paintChildren改变child的尺寸或者位置,只是重绘,并没有实际调整其位置

      Flow({
        Key key,
        @required this.delegate,
        List<Widget> children = const <Widget>[],
      }) : assert(delegate != null),
           super(key: key, children: RepaintBoundary.wrapAll(children));
    
    • key:当前元素的唯一标识符(类似于 Android 中的 id)
    • delegate:影响 flow 具体布局的 flowDelegate
    • children:子 widget 集合
    /**
     * @des Flow Widget
     * @author liyongli 20190423
     * */
    class FlowWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _FlowState();
    
    }
    
    /**
     * @des Flow Widget State
     * @author liyongli 20190423
     * */
    class _FlowState extends State<FlowWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Flow Widget"),
            ),
    
            body: new Flow(
              delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
              children: <Widget>[
                new Container(width: 80.0, height:80.0, color: Colors.blue,),
                new Container(width: 80.0, height:80.0, color: Colors.black,),
                new Container(width: 80.0, height:80.0, color: Colors.amber,),
                new Container(width: 80.0, height:80.0,  color: Colors.brown,),
                new Container(width: 80.0, height:80.0, color: Colors.cyanAccent,),
                new Container(width: 80.0, height:80.0,  color: Colors.deepOrange,),
              ],
            ),
          ),
        );
      }
    }
    
    /**
     * @des Flow Widget Delegate
     * @author liyongli 20190423
     * */
    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;
            context.paintChild(i, transform: new Matrix4.translationValues( x, y, 0.0));
            x += context.getChildSize(i).width + margin.left + margin.right;
          }
        }
      }
    
      getSize(BoxConstraints constraints){
        return Size(double.infinity,200.0);
      }
    
      @override
      bool shouldRepaint(FlowDelegate oldDelegate) {
        return oldDelegate != this;
      }
    }
    

    Stack Widget and Positioned(层叠布局及定位)

    stack 与 Android 中 Frame、Web 中绝对定位类似,子 widget 根据父 widget 的四个顶点确定位置。stack 布局允许子 widget 堆叠绘制

      Stack({
        Key key,
        this.alignment = AlignmentDirectional.topStart,
        this.textDirection,
        this.fit = StackFit.loose,
        this.overflow = Overflow.clip,
        List<Widget> children = const <Widget>[],
      }) : super(key: key, children: children);
    
    • key:当前元素的唯一标识符(类似于 Android 中的 id)
    • alignment:如果子 widget 没有设置定位(无 positioned)或只指定了部分定位,则此参数为子 widget 的定位标准。
    • textDirection:用于决定 alignment 的参考标准,与 row 布局中参数功能一致
    • fit:如果子 widget 没有定位,则此参数将指定子 widget 以怎样的方式适应 statck 的大小。StackFit.loose = 使用子 widget 的大小,StackFit.expand = 扩展至 stack 大小
    • overflow:如果子 widget 超出了 stack 的空间,则此参数将指定如何显示。Overflow.clip = 超出部分隐藏 / 裁剪,Overflow.visible = 产出部分不会隐藏 / 裁剪
    • children:子 widget 集合
    Positioned(定位)
      const Positioned({
        Key key,
        this.left,
        this.top,
        this.right,
        this.bottom,
        this.width,
        this.height,
        @required Widget child,
      }) : assert(left == null || right == null || width == null),
           assert(top == null || bottom == null || height == null),
           super(key: key, child: child);
    
    • left、top 、right、 bottom:代表子 widget 距 statck 左、上、右、下四边的距离
    • width、height:设置定位元素的宽、高(width、height 需配合 left、top 、right、 bottom 并结合实际情况使用,如水平方向时,只可以设置 left、right、width 三个参数中的二个,若同时设置三个参数会产生错误)
    /**
     * @des Stack Widget
     * @author liyongli 20190423
     * */
    class StackWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _StackState();
    
    }
    
    /**
     * @des Stack Widget State
     * @author liyongli 20190423
     * */
    class _StackState extends State<StackWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
    
            appBar: new AppBar(
              title: new Text("Stack Widget"),
            ),
    
            body: ConstrainedBox(
              constraints: BoxConstraints.expand(),
              child: Stack(
                alignment:Alignment.center , //指定无定位或部分定位的子 widget 的对齐方式
                children: <Widget>[
    
                  Container(
                    child: Text("我是一只小小鸟", style: new TextStyle(color: Colors.white),),
                    color: Colors.blue,
                  ),
    
                  Positioned(
                    left: 18.0,
                    child: Text("想飞飞飞"),
                  ),
    
                  Positioned(
                    top: 18.0,
                    child: Text("飞的挺高"),
                  )
    
                ],
              ),
            ),
          ),
        );
      }
    }
    
    • 我是一只小小鸟:没有指定定位(无 positioned),alignment = Alignment.center,所以居中显示
    • 想飞飞飞:指定了 left ,属于部分定位,只指定了水平定位,无垂直定位,所以垂直对齐的方式会按 alignment 的赋值参数,也就是垂直居中显示
    • 飞的挺高:指定了 top,属于部分定位,只制定了垂直定位,无水平定位,所以水平方向会按照 alignment 的赋值参数,也就是水平居中显示

    此时,在原基础上给 stack 设置 fit = StackFit.expand (子 widget 没有指定定位时,此参数将指定子 widget 以怎样的方式适应 stack)

    /**
     * @des Stack Widget
     * @author liyongli 20190423
     * */
    class StackWidget extends StatefulWidget{
    
      @override
      State<StatefulWidget> createState() => new _StackState();
    
    }
    
    /**
     * @des Stack Widget State
     * @author liyongli 20190423
     * */
    class _StackState extends State<StackWidget>{
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
    
            appBar: new AppBar(
              title: new Text("Stack Widget"),
            ),
    
            body: ConstrainedBox(
              constraints: BoxConstraints.expand(),
              child: Stack(
                alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
                fit: StackFit.expand, // expand = 未定位的子 widget 占满 stack 的可用空间
                children: <Widget>[
    
                  Positioned(
                    left: 18.0,
                    child: Text("想飞飞飞"),
                  ),
    
                  Container(
                    child: Text("我是一只小小鸟", style: new TextStyle(color: Colors.white),),
                    color: Colors.blue,
                  ),
    
                  Positioned(
                    top: 18.0,
                    child: Text("飞的挺高"),
                  )
    
                ],
              ),
            ),
          ),
        );
      }
    }
    
    • 我是一只小小鸟:由于它无定位,所以它的绘制方式遵循 fit 指定的值,也就是占满 stack
    • 想飞飞飞:被遮盖 / 隐藏,由于 stack 布局可堆叠的特性,它已被第二个子 widget 遮盖
    • 飞的挺高:正常显示,因为它最后绘制,所以不会被第二个子 widget 遮盖

    本篇到此完结,更多 Flutter 跨平台移动端开发 原创内容持续更新中~

    期待您 关注 / 点赞 / 收藏 向着 大前端工程师 晋级!

    相关文章

      网友评论

        本文标题:Flutter跨平台移动端开发丨Column、Row、Flex、

        本文链接:https://www.haomeiwen.com/subject/uevegqtx.html