Flutter 入门指北(Part 4)之容器部件

该文已授权公众号 「码个蛋」,转载请指明出处

上节填完了 Scaffold 留下的坑,这节继续填坑,之前留下关于 Layout 的坑,又是一堆部件袭来

Container

为了让我们的界面更容易被扩展,通常会在最外层包裹一层 Container,其构造函数也不是很难理解

Container({
    Key key,
    this.alignment, // child 的对齐方式,包括左对齐,居中,右对齐,左上对齐..等等
    this.padding, // child 和 Container 的边距
    Color color, // Container 的背景色
    Decoration decoration, // 样式,可以设置背景图,圆角等属性
    this.foregroundDecoration, // child 的样式
    double width, // 宽度
    double height, // 高度
    BoxConstraints constraints, // 默认使用 BoxConstraints.tightFor,可以手动传入
    this.margin, // Container 同上层容器的边距
    this.transform, // 是个 Matrix4 矩阵,(嗯..这个参数基本很少用,没怎么了解 /捂脸)
    this.child, // 需要展示的内容
  })

// ...
const BoxConstraints.tightFor({
    double width,
    double height
  }): minWidth = width != null ? width : 0.0,
      maxWidth = width != null ? width : double.infinity,
      minHeight = height != null ? height : 0.0,
      maxHeight = height != null ? height : double.infinity;

让我们写个圆角矩形的外层,内层值显示白色文字

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: Container(
        alignment: Alignment.center,
        // 宽,高度同上层容器相同
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        margin: const EdgeInsets.all(8.0),
        padding: const EdgeInsets.all(20.0),
        // Container 的样式
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(20.0),
            color: Colors.red,
//            shape: BoxShape.circle, // 该属性不可同 borderRadius 一起使用
            backgroundBlendMode: BlendMode.colorDodge, // 背景图片和颜色混合模式
            image: DecorationImage(image: AssetImage('images/ali.jpg'), fit: BoxFit.cover)),
        child: Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)),
//        color: Theme.of(context).primaryColor, // 该属性不可和 decoration 一起使用
      )),
    );
  }
}

效果图如下:

container 展示图.png

该部分代码查看 column_main.dart 文件

看到这,应该很多小伙伴注意到 marginpadding 属性用来和别的部件保持间距,那...那我就是不用 Container 呢(专门来挑事的...),当然没问题,有个专门用来设置间距的部件 Padding,看名字就可以看出来作用了,修改下 child 部分代码,这边先提前用下接下来会讲的部件

child: Column(
          children: <Widget>[
            Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)),
            Padding(
                // 需要传入一个间隔值,`Flutter` 提供了很多 EdgeInsets 来设置间隔,
                // 参数也很明确,可以一一尝试
                padding: const EdgeInsets.symmetric(vertical: 12.0),
                // 传入需要间隔的部件
                child: Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)))
          ],
        ),

效果就不展示了,接下来就要开始我的填坑之旅了....

Flex,Row,Column

Android 的小伙伴应该比较常用 LinearLayout,在 Flutter 中用两个部件,Row Column来代替 Android 中的 LinearLayout,其中 Row 是横向布局,Column 是垂直布局,因为 RowColumn 都是继承于 Flex 部件,Flex 比他们多了 direction 属性用来指定方向,所以主要拿 Column 来讲解,FlexRow 用法相同

Column({
    Key key,
    // 对齐方式,对于 `Column` start 为顶部,对于 `Row` 需要分语言,和语言同向
    // 3 种比较特殊的对齐方式,前端的小伙伴会了解,
    // spaceAround 两个部件之间的间隔是部件和上层容器间隔的两倍
    // spaceBetween 两侧部件同上层容器间隔为 0,部件之间的间隔相等
    // spaceEvenly 部件之间的间隔同两侧部件与上层容器间隔
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 
    MainAxisSize mainAxisSize = MainAxisSize.max, // 主轴的大小
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, // 副轴对齐方式
    TextDirection textDirection, // 文字方向,决定 start
    VerticalDirection verticalDirection = VerticalDirection.down, // 垂直方向
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[], // 内部子部件
  })

RowColumn 都有主轴和副轴,如何区分呢,布局平行方向为主轴,垂直方向为副轴,我们把 Containerchild 修改成 Column,然后把 Text 放到 Column 中,多放几个,然后自己设置 mainAxisAlignment 属性,查看布局的变化

// ... 省略相同代码
child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            Text('Container Text 1', style: TextStyle(color: Colors.white, fontSize: 30.0)),
            Text('Container Text 2', style: TextStyle(color: Colors.white, fontSize: 30.0)),
            Text('Container Text 3', style: TextStyle(color: Colors.white, fontSize: 30.0)),
            Text('Container Text 4', style: TextStyle(color: Colors.white, fontSize: 30.0)),
            Text('Container Text 5', style: TextStyle(color: Colors.white, fontSize: 30.0)),
          ],
        )

最后的效果图如下:

column 展示图.png

这边 Column 内部的子部件因为高度相同,如果不同还需要等分空间的话,就不可以通过设置 mainAxisAlignment 属性来实现了,这里介绍一个等分的部件 Expanded

const Expanded({
    Key key,
    int flex = 1, // 所占比例
    @required Widget child, // 子部件
  })

直接给 Text 外层加一个 Expanded 即可实现效果,当然可以按照需求来设置 flex 来修改比例值。

当然,在使用过程中也会遇到那么些坑,我们修改下代码,把 child 的代码修改成如下

child: Row(
          children: <Widget>[
            Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
            Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
            Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
            Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
            Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0))
          ],
        )

然后运行下,你的屏幕就提示你 RIGHT OVERFLOWED BY XXX PIXELS 「**, *****」我猜你内心肯定这样的,冷静冷静

既然遇到问题,当然要解决,不然和产品去撕逼吗..?这边,我们把 Row 换成另一个布局 Wrap 然后再运行,Prefect,WrapRow 的参数基本类似

Wrap

Wrap({
    Key key,
    this.direction = Axis.horizontal,
    this.alignment = WrapAlignment.start,
    this.spacing = 0.0, // 两个子部件之间的间隔,默认 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>[],
  })

当然,很多时候只有以上的布局是不行的,比如我们需要实现一个圆形头像,然后一段文字在其上面 ,例如下面的效果

stack 展示1.png

接下来介绍一个堆叠的部件 Stack,源码比较简单,就不贴了,直接上效果代码

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Stack(
        // 内部子部件的对齐方式
        alignment: Alignment.center,
        children: <Widget>[
          // 圆形头像,指定半径,指定背景图为头像即可
          CircleAvatar(backgroundImage: AssetImage('images/ali.jpg'), radius: 100.0),
          Text(
            'Kuky', style: TextStyle(color: Colors.white, fontSize: 34.0)),
        ],
      )),
    );
  }
}

如果我们需要第三个部件,底部距离圆形头像10px,那么只靠 alignment 是不可能实现了

所以,另外一个灰常流弊的部件就出来了 Positioned,其源码也比较简单,我还是不贴了吧~,还是直接上代码,直接修改

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Stack(
        alignment: Alignment.center,
        children: <Widget>[
          CircleAvatar(backgroundImage: AssetImage('images/ali.jpg'), radius: 100.0),
          Text(
            'Kuky',
            style: TextStyle(color: Colors.white, fontSize: 34.0),
          ),
          Positioned(child: Text('另外一段文字', style: TextStyle(color: Colors.white, fontSize: 20.0)), bottom: 10.0), // left, right, top, bottom 分别表示和 stack 的间距
        ],
      )),
    );
  }
}

最后的效果图如下:

stack 展示2.png

此处代码查看 stack_main.dart 文件

当然,Flutter 还提供别的布局类的部件,例如 Flow 这个鬼,但是呢......这个鬼会用到「转换矩阵」来对子部件进行布局(一般听到需要用矩阵...emmmm,算...算你牛逼),而且不能自适应子部件的大小等等一些问题,所以还是放弃吧,实际上,前面介绍的布局完全够用了,是在不够用了,再看 Flow

很好,今天填了布局的这个大坑,而且讲的部件貌似还挺多的,虽然还是比较简单,剩下的就给小伙伴们慢慢消化今天的内容。下节,除了有常用的部件外,我会尽量加上实战内容

最后代码的地址还是要的:

  1. 文章中涉及的代码:demos

  2. 基于郭神 cool weather 接口的一个项目,实现 BLoC 模式,实现状态管理:flutter_weather

  3. 一个课程(当时买了想看下代码规范的,代码更新会比较慢,虽然是跟着课上的一些写代码,但是还是做了自己的修改,很多地方看着不舒服,然后就改成自己的实现方式了):flutter_shop

如果对你有帮助的话,记得给个 Star,先谢过,你的认可就是支持我继续写下去的动力~

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

推荐阅读更多精彩内容