05*:Flutter之基础布局Container:(Container:padding、margins、borders)(Row、Column、Stack【Align、Positioned】)(MainAxisAlignment、CrossAxisAlignment)(Flex、expanded)(Wrap、Flow)

问题

(Container:padding、margins、borders)

(Row、Column、Stack【Align、Positioned】)

(MainAxisAlignment、CrossAxisAlignment) 

目录

1:Container(盒子模型)

2:row、column线性布局

3:stack层叠布局

4:flex弹性布局

5:Expanded: 扩展

预备

 

正文

一:Container(盒子模型)

Container 给一个组件添加padding,margins,边界(borders),背景颜色或者其他装饰 (decorations)和阴影。

import 'package:flutter/material.dart';
class LearnContainer extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return new _LearnContainer();
  }
}
class _LearnContainer extends State<LearnContainer>{
  @override
  Widget build(BuildContext context) {
    return new Container(
      width: 10.0,
      height: 10.0,
      alignment: new Alignment(0.0, 0.0),//设置子控件的位置
//      color: Colors.brown,
      child: new Text('这是一个Container',
        textAlign:TextAlign.center ,//设置文字居中
        style: new TextStyle(
          color: Colors.white,
        ),
      ),//显示的内容
      padding: EdgeInsets.all(0.0),//设置子控件padding
      margin: EdgeInsets.all(50.0),//设置子控件margin
//      foregroundDecoration:,  设置子控件上面的装饰
//      constraints:  设置子控件尺寸约束的条件 比如 宽高
      decoration: new BoxDecoration(//设置子控件背后的装饰
          color: Colors.blue,  //和Container下面的color会冲突 具体原因不详
          border: new Border.all(//添加边框
            width: 10.0,//边框宽度
            color: Colors.green,//边框颜色
          ),
          borderRadius: new BorderRadius.all(Radius.circular(20.0)),//设置圆角
          boxShadow: <BoxShadow>[//设置阴影
//          new BoxShadow(
//            color: Colors.black,
//            blurRadius: 20.0,
//          ),
            new BoxShadow(
              color: Colors.red,//阴影颜色
              blurRadius: 20.0,//阴影大小
            ),
//          new BoxShadow(
//            color: Colors.yellowAccent,
//            blurRadius: 20.0,
//          ),
//          new BoxShadow(
//            color: Colors.blue,
//            blurRadius: 30.0,
//          )
          ]
      ),
    );
  }
}

1:Container

它是一个结合了绘制(painting)、定位(positioning)以及尺寸(sizing)widget的widget。

Container({
    Key key,
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
    this.child,
  })

alignment:控制child的对齐方式
decoration:绘制在child后面的装饰

2:padding/margin

控件加入内/外边距。属性如下:

EdgeInsets.all(12),// 指定四个方向相同的值
EdgeInsets.fromLTRB(10, 20, 30, 40),// 分别指定四个方向的值
EdgeInsets.only(left: 10,right: 30),// 指定任意个方向的值EdgeInsets.symmetric(vertical: 10,horizontal: 50),//指定水平(left right)或垂直方向(top,bottom)的值。

二:线性布局Row、Column

1:row继承自Flex。可将children中的widget在水平方向上进行排列。与css的flex(flex-direction:row;)布局相似

Row和Column分别是Flutter中的水平和垂直布局,对应Android里面的LinearLayout的水平垂直方向布局。

MainAxisAlignment (主轴)属性:

enum MainAxisAlignment {
//将子控件放在主轴的开始位置
start,
//将子控件放在主轴的结束位置
end,
//将子控件放在主轴的中间位置
center,
//将主轴空白位置进行均分,排列子元素,手尾没有空隙
spaceBetween,
//将主轴空白区域均分,使中间各个子控件间距相等,首尾子控件间距为中间子控件间距的一半
spaceAround,
//将主轴空白区域均分,使各个子控件间距相等
spaceEvenly,
}

CrossAxisAlignment(交叉轴)属性:

enum CrossAxisAlignment {
//将子控件放在交叉轴的起始位置
start,
//将子控件放在交叉轴的结束位置
end,
//将子控件放在交叉轴的中间位置
center,
//使子控件填满交叉轴
stretch,
//将子控件放在交叉轴的上,并且与基线相匹配(不常用)
baseline,
}

配合Expanded使用,使Expanded中的child充满空白区域。Expanded组件必须用在Row、Column、Flex内。

Expanded(  
          flex: 1,
          child: Text(""),
        )

属性:

crossAxisAlignment:交叉轴子组件对齐方式
mainAxisAlignment:主轴子组件排列方式
mainAxisSize:Main 轴大小,相当于match_parent,wrap_content
verticalDirection:从下向上或从上向下摆放子组件

import 'package:flutter/material.dart';

class RowDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.max,
      textDirection: TextDirection.ltr,
      verticalDirection: VerticalDirection.down,
      textBaseline: TextBaseline.ideographic,
      children: <Widget>[
        Container(
          width: 80.0,
          height: 200.0,
          color: Colors.cyanAccent,
        ),
        Container(
          width: 80.0,
          color: Colors.red,
        ),
        Container(
          width: 80.0,
          height: 170.0,
          color: Colors.green,
        ),
        Container(
          width: 80.0,
          height: 600.0,
          color: Colors.orange,
        ),
      ],
    );
  }
}

1:MainAxisAlignment.star

从开始位置开始,textDirection若为TextDirection.rtl则为右对齐 

2:同样继承自Flex。children中的widget会在竖直方向上进行排列。相关属性与Row类似,只不过Column的主轴是竖直方向,副轴为水平方向 

mport 'package:flutter/material.dart';

class ColumnDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      mainAxisSize: MainAxisSize.max,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.center,
      textDirection: TextDirection.rtl,
      verticalDirection: VerticalDirection.down,
      children: <Widget>[
        Container(
          height: 100.0,
          width: 90.0,
          color: Colors.redAccent,
        ),
        Container(
          height: 100.0,
          width: 190.0,
          color: Colors.purpleAccent,
        ),
        Container(
          height: 100.0,
          color: Colors.cyanAccent,
        ),
        Container(
          height: 100.0,
          width: 200.0,
          color: Colors.greenAccent,
        ),
        Container(
          height: 100.0,
          width: 290.0,
          color: Colors.yellow,
        ),
      ],
    );
  }
} 

 

三:层叠布局Stack: Align和Positioned结合使用形成相对布局

参数详解

属性 说明
alignment

布局定位 

默认 AlignmentDirectional.topStart

我们也可以直接传入参数,自定义位置(值在1与-1之间),如:alignment: Alignment(0.5,0.5),

textDirection

正反排序

TextDirection.ltr

TextDirection.rtl

fit 默认StackFit.loose
overflow 默认Overflow.clip
children 子元素

 

 

 

 

 

 

 

 

 

 

 

 
 
 
Align
alignment   布局定位
widthFactor  如果为非null,则将其高度设置为子高度乘以此系数。必须为正数
heightFactor  如果为非null,则将其宽度设置为子宽度乘以此系数。必须为正数
child  子元素
 
Positioned
属性 说明
left 左边距
top 上边距
right 右边距
bottom 下边距
width 子元素宽
height 子元素高
child 子元素
 

 

 

 

 

 

 

 

 

 

3.1: 只使用Stack 层叠组件

mport 'package:flutter/material.dart';

class StackDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Stack(
      alignment: AlignmentDirectional.topStart,
      textDirection: TextDirection.ltr,
      fit: StackFit.loose,
      overflow: Overflow.clip,
      children: <Widget>[
        Container(
          width: 120,
          height: 120,
          color: Colors.red,
        ),
        Container(
          width: 100,
          height: 100,
          color: Colors.green,
        ),
        Container(
          width: 80,
          height: 80,
          color: Colors.yellow,
        ),
      ],
    );
  }
}

 

3.2:Stack与Align

Stack中的children都是层层进行叠加的,通过Align或Positioned可将这些层叠组件分开,实现视觉上的分离:

class StackAlignDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ConstrainedBox(
      constraints: BoxConstraints.expand(),
      child: Stack(
        children: <Widget>[
          Align(
            alignment: Alignment.topLeft,
            child: Icon(Icons.settings, size: 30,),
          ),
          Align(
            alignment: Alignment.topRight,
            child: Icon(Icons.add_location, size: 30,),
          ),
          Align(
            alignment: Alignment(-1, 0.9),
            child: Icon(Icons.forward, size: 30,),
          ),
          Align(
            alignment: Alignment(1, 0.9),
            child: Icon(Icons.home, size: 30,),
          ),
          Align(
            alignment: Alignment.center,
            child: Icon(Icons.center_focus_strong, size: 40,),
          )
        ],
      ),
    );
  }
}

3.3: Stack与Positioned

class StackPositionedDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Stack(
      children: <Widget>[
        Positioned(
          top: 0,
          left: 0,
          child: Icon(Icons.settings, size: 30,),
        ),
        Positioned(
          top: 0,
          right: 0,
          child: Icon(Icons.add_location, size: 30,),
        ),
        Positioned(
          left: 0,
          bottom: 20,
          child: Icon(Icons.forward, size: 30,),
        ),
        Positioned(
          right: 0,
          bottom: 20,
          child: Icon(Icons.home, size: 30,),
        ),
        Positioned.fill( // 居中
          child: Icon(Icons.center_focus_strong, size: 40,),
        )
      ],
    );
  }
}

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);

四:弹性布局Flex

弹性布局允许子widget按照一定比例来分配父容器空间,Flutter中的弹性布局主要通过Flex和Expanded来配合实现。

Flex可沿着水平或竖直方向对widget进行布局。

1:构造函数

Flex({
    Key key,
    @required this.direction, // 主轴方向:Axis.horizontal水平方向为主轴,即为Row;Axis.vertical垂直方向为主轴,即为Column
    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>[],
  }) : assert(direction != null),
       assert(mainAxisAlignment != null),
       assert(mainAxisSize != null),
       assert(crossAxisAlignment != null),
       assert(verticalDirection != null),
       assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null),
       super(key: key, children: children);

2:示例代码

import 'package:flutter/material.dart';

class FlexDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    Widget c1 = Container(
      width: 80.0,
      height: 150.0,
      color: Colors.yellow,
      child: Center(
        child: Text(
          'container1',
          style: TextStyle(
              color: Colors.white
          ),
        ),
      ),
    );
    Widget c2 = Container(
      width: 100.0,
      height: 100.0,
      color: Colors.cyanAccent,
      child: Center(
        child: Text(
          'container2',
          style: TextStyle(
              color: Colors.white
          ),
        ),
      ),
    );
    Widget c3 = Container(
      width: 160.0,
      height: 100.0,
      color: Colors.pinkAccent,
      child: Center(
        child: Text(
          'container3',
          style: TextStyle(
              color: Colors.white
          ),
        ),
      ),
    );
    Widget flexForRow = Flex(
      direction: Axis.horizontal,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisSize: MainAxisSize.max,
      textDirection: TextDirection.ltr,
      verticalDirection: VerticalDirection.down,
      textBaseline: TextBaseline.ideographic,
      children: <Widget>[c1, c2, c3],
    );
    Widget flexForColumn = Flex(
      direction: Axis.vertical,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisSize: MainAxisSize.max,
      textDirection: TextDirection.ltr,
      verticalDirection: VerticalDirection.down,
      children: <Widget>[c1, c2, c3],
    );
    // TODO: implement build
    return flexForRow;
  }
}

flexForRow效果:

flexForColumn效果:

五:Expanded:扩展

用在Flex、Column、Row中,用来占满主轴方向上的剩余空间。

利用上面示例,将flexForRow中的c1改为Expanded(child: c1,),你会发现水平方向上的空白没有了,c1将剩余空间占满,效果如下:

 效果

Expanded中还有一个flex属性,该属性大于0时表示会自动占满剩余空间,默认为1。如果多个Expanded同时指定flex,flex的值就是这几个Expanded所占空间的比例。将上面flexForColumn的children改为如下:

children: <Widget>[
        Expanded(
          child: c1,
          flex:1,
        ),
        Expanded(
          child: c2,
          flex:1,
        ),
        Expanded(
          child: c3,
          flex:1,
        ),
      ]

效果 

这三个Container高度被平均分配,这是因为flex默认值为1,所以这三个的高度比为1:1:1,如果设置flex分别为1、2、3,则这三个Container的高度比为1:2:3。

六: Wrap:换行

 熟悉flex布局的同学肯定知道flex布局会有wrap属性,就是换行,而Flex、Column、Row都是不可以换行的。那么如果我们需要换行怎么办呢?Flutte提供了Wrap来解决这一需求,Wrap与Flex具有相似的属性

Wrap({
    Key key,
    this.direction = Axis.horizontal, // 主轴方向
    this.alignment = WrapAlignment.start, //主轴方向上的对齐方式
    this.spacing = 0.0, // 主轴方向上children的间隔
    this.runAlignment = WrapAlignment.start, // children如何放置在交叉轴上
    this.runSpacing = 0.0, // 交叉轴上children的间隔
    this.crossAxisAlignment = WrapCrossAlignment.start, // children之间在交叉轴方向上的对齐方式
    this.textDirection, // 水平开始的方向
    this.verticalDirection = VerticalDirection.down, // 竖直的方向
    List<Widget> children = const <Widget>[],
  }) : super(key: key, children: children);

示例

import 'package:flutter/material.dart';

class WrapDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    Widget c1 = Container(
      height: 130.0,
      width: 130.0,
      color: Colors.redAccent,
    );
    Widget c2 = Container(
      height: 50.0,
      width: 50.0,
      color: Colors.lightBlueAccent,
    );
    Widget c3 = Container(
      height: 80.0,
      width: 80.0,
      color: Colors.yellow,
    );
    Widget c4 =  Container(
      height: 100.0,
      width: 100.0,
      color: Colors.greenAccent,
    );
    Widget c5 = Container(
      height: 170.0,
      width: 170.0,
      color: Colors.grey,
    );
    // TODO: implement build
    return Container(
      width: double.infinity,
      height: double.infinity,
      child: Wrap(
        direction: Axis.horizontal,
        spacing: 10.0,
        runSpacing: 20.0,
        alignment: WrapAlignment.center,
        runAlignment: WrapAlignment.start,
        crossAxisAlignment: WrapCrossAlignment.start,
        verticalDirection: VerticalDirection.down,
        textDirection: TextDirection.ltr,
        children: <Widget>[c1, c2, c3, c4, c5],
      ),
    );
  }
}

Wrap的相关属性大部分与Flex类似,如alignment与Flex的mainAxisAlignment类似,都决定了在主轴方向上的对齐方式;crossAlignment与Flex的crossAxisAlignment相似,都决定了children在交叉轴上的对齐方式。这里主要对runAlignment与crossAlignment属性进行说明,runAlignment属性决定了children整体如何放置在交叉轴上,而crossAlignment属性决定了children之间在交叉轴方向上的对齐方式。详细效果参考如下:

runAlignment值

WrapAlignment.start

七:flow:换行

Flow可自定义children的排列布局,相对Wrap使用比较复杂。Flow中的delegate属性是自定义布局的关键,具体使用参考下面例子。

import 'package:flutter/material.dart';

class FlowDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Flow(
      delegate: TestFlowDelegate(margin:EdgeInsets.only(top: 10.0, left: 10.0)),
      children: <Widget>[
        Container(width: 80.0, height:80.0, color: Colors.red,),
        Container(width: 80.0, height:80.0, color: Colors.green,),
        Container(width: 80.0, height:80.0, color: Colors.blue,),
        Container(width: 80.0, height:80.0,  color: Colors.yellow,),
        Container(width: 80.0, height:80.0, color: Colors.brown,),
        Container(width: 80.0, height:80.0,  color: Colors.purple,),
      ],
    )
    ;
  }
}

class TestFlowDelegate extends FlowDelegate{
  EdgeInsets margin = EdgeInsets.zero;
  TestFlowDelegate({this.margin});
  @override
  void paintChildren(FlowPaintingContext context) {
    double x = margin.left;
    double y = margin.top;
    for (int i = 0; i < context.childCount; i++) {
      double 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;
      }
    }
  }

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

}

 

 

注意

 

引用

1:【Flutter】十五、Flutter中常用的布局容器——层叠组件Stack

2:【Flutter】十三、Flutter中常用的布局容器——弹性布局Flex、Expanded

posted on 2020-12-04 20:40  风zk  阅读(579)  评论(0编辑  收藏  举报

导航