Flutter基础控件篇[2]--布局

Container容器控件,可以添加一个子控件,设置宽度高度,padding,margin,背景色、背景图、圆角、阴影 等属性,基本包含了容器控件的常用基本属性。
有些widget 自带padding属性,所以不必多套一层Padding部件。(比如ListView、GridView、Container、ScrollView、Button )
基本使用:

Widget build(BuildContext context) {
    return MaterialApp(
      home: Container(
          width: 200.0,
          height: 150.0,
          margin: const EdgeInsets.fromLTRB(25, 15, 0, 0),
          padding: const EdgeInsets.fromLTRB(10, 15, 10, 15),
          alignment: const Alignment(0, 0),
          decoration: const BoxDecoration(boxShadow: [
            //卡片阴影
            BoxShadow(
                color: Colors.black54,
                offset: Offset(5.0, 5.0),
                blurRadius: 7.0)
          ]),
          child: Image.network(
            "https://gips3.baidu.com/it/u=2498623727,1870741620&fm=3030&app=3030&f=JPEG?w=200&h=133&s=F31330C442402557123E451103005099",
            width: 200.0,
            height: 150.0,
          )),
    );
  }

常用属性:
width:宽度,如果设置具体值则为具体值,不设置则包裹子控件大小,设置为double.infinity一般会充满父控件或者充满屏幕(如果没有其他特别的约束条件);
height:高度,使用方法基本同width;
margin:设置控件与其他控件的外边距;
padding:设置内边距;
decoration:背景装饰;
alignment:设置子控件的方位,用Alignment属性值来表示,具体对应的方位如下:

Alignment topLeft = Alignment(-1.0, -1.0);
Alignment topCenter = Alignment(0.0, -1.0);
Alignment topRight = Alignment(1.0, -1.0);
Alignment centerLeft = Alignment(-1.0, 0.0);
Alignment center = Alignment(0.0, 0.0);
Alignment centerRight = Alignment(1.0, 0.0);
Alignment bottomLeft = Alignment(-1.0, 1.0);
Alignment bottomCenter = Alignment(0.0, 1.0);
Alignment bottomRight = Alignment(1.0, 1.0);

Column(纵向布局) and Row(横向布局)

Column(纵向布局)

类似iOS中的 tableview


WX20230808-094637.png
 Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("学习demo"),
      ),
      body: const Column(
        mainAxisAlignment:  MainAxisAlignment.start, //主轴方向上控件排列规则(居中 靠边 均匀分布等) 主轴是指竖直列表
          crossAxisAlignment: CrossAxisAlignment.start, //纵轴方向上控件排列规则(start左 center居中 end靠右边等)
          mainAxisSize: MainAxisSize.max, //主轴方向(纵向)占空间尽量大,(如果没有其他特别的约束条件则要么填满屏幕,要么填满父布局)
          verticalDirection:VerticalDirection.down, //控件顺序正常顺序,VerticalDirection.up则倒叙
          textBaseline: TextBaseline.alphabetic,
          children: <Widget>[
           Text('verticalDirection:VerticalDirection.up'),
           Text('crossAxisAlignment: CrossAxisAlignment.center'),
            Text('mainAxisAlignment: MainAxisAlignment.spaceEvenly'),
           Text('textBaseline: TextBaseline.ideographic'),
        ],
      ),
      //ZTListTitleView()
    );
  }

Row(横向布局)

和纵向布局属性差不多 要注意元素超出屏幕的控制


1.png
Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("学习demo"),
      ),
      body: Row(
        mainAxisSize:
            MainAxisSize.max, //主轴方向(横向)占空间尽量大,(如果没有其他特别的约束条件则要么填满屏幕,要么填满父布局)
        verticalDirection: VerticalDirection.down, //实测 up和down效果一样
        crossAxisAlignment: CrossAxisAlignment.center, //纵轴方向(纵向)上控件排列规则(居中 靠边等)
        mainAxisAlignment: MainAxisAlignment.spaceBetween, ////主轴方向上控件排列规则(居中 靠边 均匀分布等)
        textBaseline: TextBaseline.ideographic,
        textDirection: TextDirection.ltr, //从左到右排列
        children: <Widget>[
          Container(color: Colors.blue[100], width: 70, height: 70),
          Container(color: Colors.blue[200], width: 70, height: 70),
          Container(color: Colors.blue[300], width: 70, height: 70),
          Container(color: Colors.blue[400], width: 70, height: 70),
        ],
      ),
      //ZTListTitleView()
    );
  }

合并介绍

这里mainAxis和crossAxis代表主轴方向和纵轴方向,Row(横向布局)主轴方向就是横向,纵轴方向就是纵向;Column(纵向布局)主轴方向就是纵向,纵轴方向就是横向;
几个常用属性:
mainAxisSize:

  • MainAxisSize.max:主轴方向占空间尽量大,(如果没有其他特别的约束条件则要么填满屏幕,要么填满父布局);
  • MainAxisSize.min:主轴方向占空间尽量小,基本就是包裹子控件的尺寸了

textDirection

  • TextDirection.ltr,从左到右排列;
  • TextDirection.rtl,从右到左排列;

textBaseline

  • TextBaseline.alphabetic,使用按照排列字母字符的基准线排列方式
  • TextBaseline.ideographic,使用按照表意字字符的基准线排列方式(比如中文);

表意字好像是说可以拆解结构的文字类型,通过各种部首的组合可以形成各种文字,例如中文。(这里有一个视频,简单了解什么是表意字)

verticalDirection

  • VerticalDirection.up,从下向上排列;
  • VerticalDirection.down,从上向下排列;

mainAxisAlignment:

  • start:将children放置在主轴的起点;
  • center:将children放置在主轴的中心;
  • end:将children放置在主轴的末尾;
  • spaceAround:将主轴方向上的空白区域均分,使得children之间的空白区域相等,但是首尾child的空白区域为1/2;
  • spaceBetween:将主轴方向上的空白区域均分,使得children之间的空白区域相等,首尾child都靠近首尾,没有间隙;
  • spaceEvenly:将主轴方向上的空白区域均分,使得children之间的空白区域相等,包括首尾child;

crossAxisAlignment:

  • start:将children放置在纵轴的起点;
  • center:将children放置在纵轴的中心;
  • end:将children放置在纵轴的末尾;

Wrap 流式布局

Wrap包裹子控件,也可以设置横向和纵向,它与Row和Column的区别在于:Row和Column只有一行或一列,子控件尺寸超出了就会显示不全,但是Wrap会自动换行。


WX20230808-095424.png
Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.horizontal,
      //start end值受textDirection具体值的影响
      alignment: WrapAlignment.start,
      runAlignment: WrapAlignment.center, //纵轴方向对齐方式
      spacing: 15, //主轴方向间距15
      runSpacing: 10, //纵轴方向间距10
      textDirection: TextDirection.ltr,
      children: <Widget>[
        Container(
          color: Colors.green[100],
          width: 70,
          height: 70,
        ),
        Container(color: Colors.green[200], width: 70, height: 70),
        Container(color: Colors.green[300], width: 180, height: 70),
        Container(color: Colors.green[400], width: 70, height: 70),
        Container(
          color: Colors.green[100],
          width: 70,
          height: 70,
        ),
        Container(color: Colors.green[200], width: 70, height: 70),
        Container(color: Colors.green[300], width: 70, height: 70),
        Container(color: Colors.green[400], width: 70, height: 70),
      ],
    );
  }

常用属性:direction:排列方向
Axis.horizontal 横向布局(主轴为横向)
Axis.vertical 纵向布局(主轴为纵向)
alignment:使用WrapAlignment的属性值,但是含义和Row、Column基本一致;
spacing:主轴方向子控件间距;
runSpacing:纵轴方向的间距(主轴是横向则纵轴是纵向,主轴是纵向则纵轴是横向);
runAlignment:纵轴方向的对齐方式;

注意 :crossAxisAlignment和runAlignment都表示纵轴方式的对齐方式,实测的结果似乎crossAxisAlignment并没有起作用,只有runAlignment起作用了

Stack 重叠布局

可以把子控件叠在一起来展示。Stack可以搭配Positioned控件一起使用,Positioned控件可以控制子控件在Stack中的具体位置(设置距离左上右下的具体参数)和控件大小(设置宽高),使用场景会非常多

Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.loose,
      alignment: AlignmentDirectional.center,
      textDirection: TextDirection.ltr,
      children: <Widget>[
        Container(
          width: 120,
          height: 120,
          color: Colors.cyan[200],
        ),
        Container(
          width: 90,
          height: 90,
          color: Colors.cyan[400],
        ),
        Container(
          width: 60,
          height: 60,
          color: Colors.cyan[600],
        ),
        Positioned(
          left: 100,
          top: 100,
          child: Container(
            width: 220,
            height: 220,
            color: Colors.cyan[800],
          ),
        ) //指定位置,超出stack范围,测试overflow属性
      ],
    );
  }

常用属性:
alignment:设置子控件的方位,用AlignmentDirectional的值来表示,这个类里面的start和end受textDirection具体值的影响,TextDirection.ltr(左到右)对应start为左边,,TextDirection.rtl(右到左)对应start为右边,属性值具体对应的方位如下
AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0);
AlignmentDirectional topCenter = AlignmentDirectional(0.0, -1.0);
AlignmentDirectional topEnd = AlignmentDirectional(1.0, -1.0);
AlignmentDirectional centerStart = AlignmentDirectional(-1.0, 0.0);
AlignmentDirectional center = AlignmentDirectional(0.0, 0.0);
AlignmentDirectional centerEnd = AlignmentDirectional(1.0, 0.0);
AlignmentDirectional bottomStart = AlignmentDirectional(-1.0, 1.0);
AlignmentDirectional bottomCenter = AlignmentDirectional(0.0, 1.0);
AlignmentDirectional bottomEnd = AlignmentDirectional(1.0, 1.0);
fit:子控件适应Stack控件尺寸的方式,这个属性对于非Positioned控件内的子控件起作用。
StackFit.loose:使用此属性时,对于非Positioned控件内的子控件,它的尺寸会小于Stack控件的尺寸,比如Stack的尺寸是300x600,那么子控件的尺寸宽度允许在0到300之间,高度允许在0到600之间;
StackFit.expand:使用此属性时,对于非Positioned控件内的子控件,它的尺寸会撑满Stack的尺寸。比如Stack的尺寸是300x600,那么子控件的尺寸宽度会是300,高度会是600;
StackFit.passthrough:对子控件的约束条件直接由Stack的父控件传递给Stack的子控件;

GridView

网格布局,类似于iOS的UICollectionView


WX20230808-100723.png
 Widget build(BuildContext context) {
    return GridView(
      scrollDirection: Axis.vertical,
      reverse: false,
      //      primary: true,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3, //横轴三个子widget
          childAspectRatio: 1.3 //宽高比为1
          ),
      children: <Widget>[
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
        Container(
          color: Colors.blue[300],
          margin: EdgeInsets.fromLTRB(5, 5, 5, 5),
          width: 70,
          height: 70,
        ),
      ],
    );
  }

scrollDirection: 滚动方向,Axis.vertical(竖直方向);Axis.horizontal(水平方向);
reverse:子控件排序是否反转;
controller: 滚动控制器,监听和控制滚动用到,比如写下拉加载更多之类的功能就可以用到这个。可以参考ScrollController 滚动监听及控制
shrinkWrap:该属性表示是否根据子组件的总长度来设置GridView的长度,默认值为false 。默认情况下,GridView的会在滚动方向尽可能多的占用空间。当GridView在一个无边界(滚动方向上)的容器中时,shrinkWrap必须为true。
gridDelegate: 控制子控件排列规则的参数,包括配置子控件横纵方向间距,纵轴方向子控件个数,子控件宽高比例等参数,详细文档参考GridView

ListView

1. ListView.builder
QQ20230808-101821.png
ListView.builder(
    itemCount: data.length,
    itemExtent: 50.0, //强制高度为50.0
    itemBuilder: (BuildContext context, int index) {
      return ListTile(title: Text("$index"));
    }
);

itemBuilder作为列表项的布局构建器,具体构建每个位置上的item布局,并展示相应的内容。itemCount配置列表的item条数,可以根据具体的数据来灵活配置。

2. ListView.separated

相对于ListView.builder,ListView.separated增加了一个分割线的构造器separatorBuilder,分割线在实际开发中也是很常用到的。

ListView.separated(
          itemBuilder: (BuildContext context, int index) {
            return Container(
                height: 60,
                child: TextButton(
                    onPressed: () {},
                    child: Text(
                      "哈哈",
                    )));
          },
          separatorBuilder: (BuildContext context, int index) {
            return Container(
              height: 1,
              color: Color(0xffe2e8ed),
            );
          },
          itemCount: 39),

ListView主要属性:
itemExtent: 此参数如果不为null,则会强制children的“长度”为itemExtent的值;这里的“长度”是指滚动方向上子组件的长度,也就是说如果滚动方向是垂直方向,则itemExtent代表子组件的高度;如果滚动方向为水平方向,则itemExtent就代表子组件的宽度。在ListView中,指定itemExtent比让子组件自己决定自身长度会更高效,这是因为指定itemExtent后,滚动系统可以提前知道列表的长度,而无需每次构建子组件时都去再计算一下,尤其是在滚动位置频繁变化时(滚动系统需要频繁去计算列表高度);
itemCount: item条数;
itemBuilder、separatorBuilder的作用在上面代码中都能看得比较清晰;
其他的包括scrollDirection、reverse、controller、shrinkWrap、addRepaintBoundaries、addAutomaticKeepAlives等等这些基本上和GridView是一样的,直接参考上面GridView就可以了。

SingleChildScrollView

单一子控件的滚动布局,跟Android的ScrollView基本一样,Android的ScrollView也是只能有一个子控件,不然就会报错。
基本使用很简单:

Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      reverse:false,
      controller:controller,
      child: Column(
        children: <Widget>[
            //...
        ],
      ),
    );
  } 

InkWell

InkWell有的叫溅墨效果,有的叫水波纹效果。使用场景是给一些无点击事件的部件添加点击事件时使用(也支持长按、双击等事件),同时你也可以去修改它的颜色和形状。

InkWell(
  borderRadius: BorderRadius.circular(8.0), // 圆角
  splashColor: Colors.transparent, // 溅墨色(波纹色)
  highlightColor: Colors.transparent, // 点击时的背景色(高亮色)
  onTap: () {},// 点击事件
  child: Container(),
);

不过有时你会发现并不是包一层InkWell就一定会有溅墨效果。主要原因是溅墨效果是在一个背景效果,并不是覆盖的前景效果。所以InkWell中的child一旦有设置背景图或背景色,那么就会遮住这个溅墨效果。如果你需要这个溅墨效果,有两种方式实现。
1、包一层 Material,将背景色设置在 Material中的color里。

Material(
  color: Colors.white,
  child: InkWell(),
)

2、使用Stack布局,将InkWell放置在上层。这种适用于给图片添加点击效果,比如Banner图的点击。

        Stack(
            children: <Widget>[
              Positioned.fill(
                child: Image(),
              ),
              Positioned.fill(
                child: Material(
                  color: Colors.transparent,
                  child: InkWell(
                    splashColor: Color(0X40FFFFFF),
                    highlightColor: Colors.transparent,
                    onTap: () {},
                  ),
                ),
              )
            ],
          )
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容