Flutter学习笔记:布局

百般思念 提交于 2020-02-21 22:35:28

3. 布局部分

1. Row水平布局

所谓的水平布局就是单一横向的行内布局, 类似div标签,每次调用一个Row,他会自动占用一行,其高度由内部元素的高度决定,Row组件是一个支持弹性盒布局的组件

代码案例
class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Layout Widget Demo',
            home: Scaffold(
                appBar: new AppBar(
                    title: new Text('Row Layout demo'),
                ),
                body: Row(
                    children: <Widget>[
                        Container(
                            child: Center(
                                child: Text(
                                    '左边的文字',
                                    style: TextStyle(color: Colors.white, fontSize: 15),
                                ),
                            ),
                            width: 300,
                            height: 200,
                            decoration: BoxDecoration(
                                gradient: LinearGradient(colors: [
                                    Colors.lightBlue,
                                    Colors.lightGreen,
                                    Colors.orangeAccent,
                                    Colors.redAccent
                                ])),
                        ),
                        // 启用弹性盒模式
                        Expanded(
                            child: Container(
                                child: Text('右边的文字'),
                                color: Colors.blueAccent,
                                margin: EdgeInsets.fromLTRB(10, 0, 0, 0),
                                height: 200.0,
                                alignment: Alignment.center,
                            ),
                        )
                    ],
                ),
            ),
        );
    }
}
结构说明
  • Row是一个横向布局的组件,其子元素会被横向排列,如果宽度大于屏幕宽度,则会把超出部分顶出屏幕
  • Row 是一个弹性盒模型,因此通过在子组件外包一个Expanded组件可以实现弹性布局
  • 这里适用于的布局情况是一端固定一端长度自适应,如果需要百分比布局的话需要使用FractionallySizedBox
效果

在这里插入图片描述

2. Column纵向布局

***column***是根据children中的最宽的元素作为列宽,做的整一列的布局,也支持弹性盒布局

代码案例
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Layout Widget Demo',
      home: Scaffold(
          appBar: new AppBar(
            title: new Text('Column Layout demo'),
          ),
          body: new Center(
            child: new Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new RaisedButton(
                  onPressed: () {},
                  color: Colors.redAccent,
                  child: new Text('Red Botton'),
                ),
                Expanded(child: new Text('这是一个很长的内容')),
                new RaisedButton(
                  onPressed: () {},
                  color: Colors.yellowAccent,
                  child: new Text(
                    'yellow Button',
                    style: TextStyle(color: Colors.blueGrey),
                  ),
                ),
                new RaisedButton(
                  onPressed: () {},
                  color: Colors.lightBlue,
                  child: new Text(
                    'Blue Button',
                    style: TextStyle(color: Colors.blueGrey),
                  ),
                ),
              ],
            ),
          )),
    );
  }
}
结构说明
  • Column布局是弹性盒布局,通过Expanded来作自适应宽度布局
  • crossAxisAlignment: 横向布局的对齐方式
  • mainAxisAlignment: 纵向布局的对齐方式
效果

在这里插入图片描述

3. Flex弹性盒布局
  • Row和Column继承于Flex
  • Flex盒子里使用Expanded可以实现百分比的布局
Api介绍
  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>[],
  })
  • direction: 类似于Row还是Column
  • crossAxisAlignment: 元素横向对齐方式
  • mainAxisAlignment: 垂直元素对齐方式
  • mainAxisSize: 主轴元素的长度
  • children: List<Widget>
代码示例
// Flex弹性布局
class FlexLayout extends StatelessWidget {
  const FlexLayout({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(title: Text('FlexContainer')),
        body: Column(
          children: <Widget>[
            Flex(
              direction: Axis.horizontal,
              children: <Widget>[
                Expanded(
                    flex: 2,
                    child: Container(
                      child: Text(
                        '2/3',
                        style: TextStyle(color: Colors.white),
                      ),
                      decoration: BoxDecoration(color: Colors.red),
                    )),
                Expanded(
                    child: Container(
                  child: Text(
                    '1/3',
                    style: TextStyle(color: Colors.white),
                  ),
                  decoration: BoxDecoration(color: Colors.purple),
                ))
              ],
            ),
            Text('可以嵌套')
          ],
        ),
      ),
    );
  }
}

效果

在这里插入图片描述

4. Wrap和Flow
  • 如果是Row或者Column布局,超出最大长度会显示溢出,Wrap和Flow就是为了解决这个问题的,使其自动换行
  • Flow是Wrap的升级版,在Flow中的元素布局位置,完全可以自动配置

Wrap

Api介绍
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>[],
})
  • direction: 决定是哪个方向上实现
  • alignment: Wrap元素的排列顺序
  • spacing: 横向间隔
  • runSpacing: 纵向间隔
  • textDirection: 元素的方向,从左到右还是从右到左
代码示例
Wrap(
    spacing: 8,
    runSpacing: 4,
    alignment: WrapAlignment.center,
    textDirection: TextDirection.rtl,
    children: nameList
    .map((item) => (Chip(
        label: Text('$item', style: TextStyle(fontSize: 18.0)),
        avatar: CircleAvatar(
            backgroundColor: Colors.blue,
            child: Text('${item[0]}',
                        style: TextStyle(fontSize: 20.0)),
        ),
    )) as Widget)
    .toList(),
),

Flow

Flow({
    Key key,
    @required this.delegate,
    List<Widget> children = const <Widget>[],
})
  • delegate: 一个FlowDelegate对象
  • children: List<Widget>
Flow(
    delegate: TestFlowDelegate(margin: EdgeInsets.all(10)),
    children: List.generate(
        10,
        (i) => Container(
            child: Text('$i', style: TextStyle(fontSize: 18.0)),
            padding: EdgeInsets.all(30),
            decoration: BoxDecoration(
                border: Border.all(width: 2.0, color: Colors.blue)),
        )).toList(),
)
    
    
class TestFlowDelegate extends FlowDelegate {
    EdgeInsets margin = EdgeInsets.zero;

    TestFlowDelegate({@required this.margin});

    @override
    void paintChildren(FlowPaintingContext context) {
        var x = margin.left;
        var y = margin.top;

        for (var 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 {
                y = y + margin.top + margin.bottom + context.getChildSize(i).height;
                x = margin.left;
                context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
                x = x + context.getChildSize(i).width + margin.right + margin.left;
            }
        }
    }

    @override
    Size getSize(BoxConstraints constraints) {
        // TODO: implement getSize
        return Size(double.infinity, 500);
    }

    @override
    bool shouldRepaint(FlowDelegate oldDelegate) {
        // TODO: implement shouldRepaint
        return oldDelegate != this;
    }
}
效果

在这里插入图片描述

5. 百分比布局

百分比布局主要有两个手段

  • 使用FractionallySizedBox
  • 使用Flexible组件
代码案例
  • 使用FractionallySizedBox
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Layout Widget Demo',
      home: Scaffold(
          appBar: new AppBar(
            title: new Text('Row Layout demo'),
          ),
          body: Column(
            children: <Widget>[
              Row(
                children: <Widget>[
                  Container(
                    width: 300,
                    height: 300,
                    color: Colors.red,
                    alignment: Alignment.bottomRight,
                    child: FractionallySizedBox(
                      widthFactor: 0.7,
                      heightFactor: 0.5,
                      child: Container(
                        height: 200,
                        color: Colors.blue,
                      ),
                    ),
                  )
                ],
              )
            ],
          )),
    );
  }
}
  • 使用Flexible
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Layout Widget Demo',
      home: Scaffold(
          appBar: new AppBar(
            title: new Text('Row Layout demo'),
          ),
          body:
              Container(
                height: 500,
                margin: EdgeInsets.all(10),
                child: 
                Column(
                  children: <Widget>[
                    Flexible(
                      // 设置在所有Flex元素中的百分比占1/5
                      flex: 1,
                      child: Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          border: Border.all(width: 1, color: Colors.black),
                          color: Colors.red,
                        ),
                      ),
                    ),
                    Flexible(
                      // 占 3/5
                      flex: 3,
                      child: Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          border: Border.all(width: 1, color: Colors.black),
                          color: Colors.red,
                        ),
                        margin: EdgeInsets.fromLTRB(0, 10, 0, 10),
                      ),
                    ),
                    Flexible(
                     // 占1/5
                      flex: 1,
                      child: Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          border: Border.all(width: 1, color: Colors.black),
                          color: Colors.red,
                        ),
                      ),
                    ),
                  ],
                ),
              )
            ),
    );
  }
}
结构说明
  • FractionallySizedBox通过设置widthFactor, heightFactor可以将盒子的大小设置为父元素的百分比
  • FractionallySizedBox一般用来设置和父元素之间存在百分比大小的元素的场和宽,一般父元素内只有一个元素的时候用比较好
  • Flexible包裹的元素,通过flex设定其在所有flexible元素中的百分比,比如一共flex的有5份,设为flex:1的元素就是占1/5,和flex布局中的flex属性类似。
6. 层叠布局(绝对定位)
  • Stack: 类似于web中父类元素设置为display: relative
  • Positioned: 类似于子组件的display: absolute,然后通过设置top, left设置偏移距离
Stack
Stack({
    Key key,
    this.alignment = AlignmentDirectional.topStart,
    this.textDirection,
    this.fit = StackFit.loose,
    this.overflow = Overflow.clip,
    List<Widget> children = const <Widget>[],
})
  • alignment: 使用FractionOffset可以按照父类的百分比进行布局,对于没有Positioned的,继承父类的alignment
  • fit: 如果设置为StackFit.expanded就会让子元素默认撑满父元素,使用loose属性将按照原来属性的大小进行渲染
  • children: 后入的元素层级高于前面的元素,会显示在更上层
代码案例
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    var stack = new Stack(
      children: <Widget>[
        new Container(
          height: 300,
          color: Colors.lightGreen,
        ),
        new Container(
          decoration: new BoxDecoration(
              color: Colors.lightBlue,
              border: Border.all(width: 1, color: Colors.pinkAccent)),
          padding: EdgeInsets.all(10.0),
          margin: EdgeInsets.all(20.0),
          child: new Text('Caption'),
        ),
        new Positioned(
          top: 10.0,
          left: 200,
          child: new Container(
            decoration: BoxDecoration(
                color: Colors.red,
                border: Border.all(width: 1, color: Colors.greenAccent)),
            child: new Text('第二个层叠'),
          ),
        ),
      ],
      alignment: const FractionalOffset(0.5, 0.9),
    );

    return new MaterialApp(
      title: '层叠布局',
      home: Scaffold(
        appBar: new AppBar(title: new Text('层叠布局')),
        body: stack,
      ),
    );
  }
}

结构说明
  • 利用stack生成层叠对象,写在越前面的元素放在越下面,类似于z-index值越小
  • 利用Positioned包裹的元素其对齐位置按照top, left设置安排,如果没有包裹的元素继承Stack中的alignment
  • Postiion中设置百分比的定位还没研究过
  • FractionaslOffset可以设置其百分比对齐位置
效果

在这里插入图片描述

7. 相对布局Align
API介绍
const Align({
    Key key,
    this.alignment = Alignment.center,
    this.widthFactor,
    this.heightFactor,
    Widget child,
})
  • alignment: 对齐方式,使用FractionalOffset可以实现百分比定位(相对于父元素)
  • widthFactor: 宽度占父类的百分比宽度设置
  • heightFactor: 高度占父类的百分比高度
  • child: 需要定位的子组件
代码2
class AlignLayout extends StatelessWidget {
    const AlignLayout({Key key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text('Align'),
            ),
            body: Column(children: [
                Container(
                    width: 300,
                    height: 300,
                    child: Align(
                        child: Container(
                            child: Text('右下角'),
                            width: 50,
                            height: 50,
                            alignment: Alignment.center,
                            decoration: BoxDecoration(color: Colors.blue[200]),
                        ),
                        alignment: Alignment.bottomRight,
                    ),
                    decoration: BoxDecoration(color: Colors.red[200]),
                ),
                Container(
                    width: 300,
                    height: 300,
                    decoration: BoxDecoration(color: Colors.blue[100]),
                    child: Align(
                        child: Container(
                            child: Text('另外一种居中方法'),
                            decoration: BoxDecoration(color: Colors.red[100]),
                        ),
                        alignment: FractionalOffset(0.5, 0.5),
                    ),
                )
            ]),
        );
    }
}
效果

在这里插入图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!