flutter笔记7:flutter页面布局基础,看完这篇就可以用flutter写APP了

不知不觉已经到了第7篇,然而很多萌新玩家可能还是不知道如何堆砌控件,像用CSS一样搭出漂亮的APP界面,我也一样,红红火火恍恍惚惚,直到今天含泪读完Flutter布局基础,仿佛打开了一个全新的世界。

基本概念

在flutter中,一切皆控件!一切皆控件!一切皆控件!牢牢记住这个概念。Text是控件,Image是控件,Icon是控件,布局脚手架也Scaffold也是控件,甚至整个APP也是控件。

用户自定义控件分为有状态控件和无状态控件两种类型,其特性在前面的笔记4中可以感受感受。

flutter的内置控件分为两种:

  • 可视控件(visible widget)。即我们直接看到的控件,如text、Icon、Button,名称理解和html标签相同。
  • 布局控件(layout widget)。可以理解为架子,如Row、Column、Container。布局控件不会直接呈现内容,可看作承载可视控件的容器。所有的布局控件都有承载子控件的属性:childchildrenchild可承载单个子控件,children可承载多个子控件。每个布局控件有默认的布局方式,使其子控件按照自己的样式安放到位子上。布局控件提供了各种样式的参数,可实现子控件的对齐(align)、缩放(size)、包装(pack)和嵌套(Nest)。简单总结为:布局控件是为了实现可视控件的各种视觉效果而做的包装,比如前端的html为了实现sticky、双飞翼、圣杯等布局常常内容区外层添加div包裹层。

布局控件也是可以模拟显示的,通常用于调试布局样式时用到的网格线、标尺、动画帧等。启用方式:
1.在main.dart中,引入包:

import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;

2.在main函数中打开开关:

void main() {
  debugPaintSizeEnabled = true;      //打开视觉调试开关
  runApp(new MyApp());
}

运行代码后APP效果如下:


视觉调试模式下的APP界面

flutter的内置控件已经定义了很多属性,玩家可以参考Widgets Catalog了解每种控件的详细属性和用法。本篇通过几个例子,介绍页面上的控件如何堆砌和布局。

别着急,由于下面的案例中,可能用到图片,先交待一下加载图片的基本配置方法:

  1. 到项目根目录下创建一个文件夹,命名:images,将图片放置到该文件夹中。
  2. 打开根目录下的pubspec.yaml文件,在其下添加注释中的属性:
flutter:
  uses-material-design: true
  assets:                            //如果没有这个属性则添加到flutter标签下
    - images/lake.jpg          //声明图片的路径
  1. 到代码中,以image控件的方式引用图片:
new Image.asset(
              'images/lake.jpg',        //图片的路径
              width: 600.0,              //图片控件的宽度
              height: 240.0,            //图片控件的高度
              fit: BoxFit.cover,        //告诉引用图片的控件,图像应尽可能小,但覆盖整个控件。
            ),

案例

控件的排列

行(Row)和列(Column),是flutter中最常用的两个布局控件,他们只能容纳当前屏幕尺寸大小的内容,如果其内部的子控件超出屏幕尺寸,不但不会自动生成滚动条,还会报错。

案例1-行排列

横向排列

如图上图所示,图中有3个100px宽的图片,通过水平平均间隔的方式居中横向排列显示到一行中,代码示例:

 new Center(                //居中控件
  child: new Row(        //行控件
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,      //对齐方式:平均间隔
    children: [
      new Image.asset('images/pic1.jpg'),        //引入三张图片
      new Image.asset('images/pic2.jpg'),
      new Image.asset('images/pic3.jpg'),
    ]
  )
)

可以看到上图用到了2个布局控件Center和Row,通过Center包裹Row,使行控件保持居中,而ROW控件包裹了3个图片控件Image,通过配置Row的mainAxisAlignment对齐属性,使三个图片空间通过平均间隔的方式进行横向排列。

完整代码:
Dart code: main.dart
Images: images
Pubspec: pubspec.yaml

案例2-列排列

纵向排列

如上图所示,还是那3张图,通过纵向平均间隔的方式显示到一列中,代码如下:

new Center(
  child: new Column(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,        //对齐方式:平均间隔
    children: [
      new Image.asset('images/pic1.jpg'),
      new Image.asset('images/pic2.jpg'),
      new Image.asset('images/pic3.jpg'),
    ]
  )
)

完整代码:
Dart code: main.dart
Images: images
Pubspec: pubspec.yaml

对比案例1和2可以看到,代码结构相同,Row和Column中都用到了mainAxisAlignment属性,除此以外还有crossAxisAlignment属性。值得注意的是,在Row中mainAxisAlignment控制水平方向对齐,crossAxisAlignment控制垂直方向对齐,而在Column中则正好相反。相关参数值请参考MainAxisAlignmentCrossAxisAlignment

布局控件的嵌套

案例3-行列嵌套

行列嵌套

如上图,我们将图中的元素按图中的框线进行分解:

1.最外层的使用Row控件包裹,使其内部的两个子控件:浅蓝色框的菜品介绍和右边的菜品大图横向排列,代码如下:

new Scaffold(                                                                              //脚手架控件
      appBar: new AppBar(                                                            //工具栏控件
        title: new Text(widget.title),
      ),
  body: new Center(
  child: new Container(                                                                 //Container控件用于调整外边距
    margin: new EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),        
    height: 600.0,
    child: new Card(                                                                       //Card控件控制卡片的视觉效果
      child: new Row(                                                                     //Row控件使其子控件横向排列
        crossAxisAlignment: CrossAxisAlignment.start,                  //纵向对齐方式:起始边对齐
        children: [
          new Container(                                                                  //Container控件用于调整宽度
            width: 440.0,   
            child: leftColumn,                                                            //左边的菜品介绍控件
          ),
          mainImage,                                                                        //右边的大图控件
        ],
      ),
    ),
  ),
)
)

2.把浅蓝色框内的信息,用Column包裹,使其内容垂直排列:

new Container(
      padding: new EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0),
      child: new Column(                                                                    //Column控件,使其子控件垂直排列
        children: [
          titleText,        //标题行,包含了可视Text控件
          subTitle,        //副标题行,包含了可视Text控件
          ratings,         //投票信息行
          iconList,        //小图标行
        ],
      ),
    );

3.由于titleText和subTitle都是简单的包装了Text控件,不再贴代码和注释,重点讲下ratings和iconList:


ratings和iconList控件

ratings和iconList控件是垂直排列的两行,而各自内部有自己的布局信息,因此将这两行分别拆解为:

ratings

ratings下包含了两个水平排列:Row控件用于显示星级,Text控件用于显示用户浏览数。而评分星级控件ROW又分解为5个水平排列的Icon控件。

iconList控件结构

iconList横向排列了3个纵向显示的控件,层次一目了然。

对照代码结构:

//ratings控件
new Container(
      padding: new EdgeInsets.all(20.0),
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          new Row(
            mainAxisSize: MainAxisSize.min,       //mainAxisSize,可压缩或伸长行内控件的间距
            children: [
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
            ],
          ),
          new Text(
            '170 Reviews',
            style: new TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w800,
              fontFamily: 'Roboto',
              letterSpacing: 0.5,
              fontSize: 20.0,
            ),
          ),
        ],
      ),
    )

//iconList控件
new Container(
        padding: new EdgeInsets.all(20.0),
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            new Column(
              children: [
                new Icon(Icons.kitchen, color: Colors.green[500]),
                new Text('PREP:'),
                new Text('25 min'),
              ],
            ),
            new Column(
              children: [
                new Icon(Icons.timer, color: Colors.green[500]),
                new Text('COOK:'),
                new Text('1 hr'),
              ],
            ),
            new Column(
              children: [
                new Icon(Icons.restaurant, color: Colors.green[500]),
                new Text('FEEDS:'),
                new Text('4-6'),
              ],
            ),
          ],
        ),
      )

Row和Column可以相互包裹,使页面能够实现整齐的布局,只因其特性总是横向或纵向充满父控件,比如最外层使用时,会自动充满全屏幕。但是当页面内容需要超出屏幕尺寸时,就用ListTileListView代替。

最终显示效果

完整代码(由于手机屏幕尺寸无法适应此案例,运行后长和宽都会报溢出错误,大家最好使用平板虚拟机测试此案例,或者调整代码中的字体大小和控件尺寸,以满足显示要求):
Dart code: main.dart
Images: images
Pubspec: pubspec.yaml

控件的缩放

案例4-缩放

子控件缩放

上图中,三个横向排列的图片控件,中间控件的尺寸比两边的大一倍,代码如下:

new Center(
  child: new Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      new Expanded(
        child: new Image.asset('images/pic1.jpg'),
      ),
      new Expanded(              //使用Expanded控件对Image控件进行包裹
        flex: 2,                 //flex值默认为1,这里改成2之后,其子控件2倍放大
        child: new Image.asset('images/pic2.jpg'),
      ),
     new Expanded(
        child: new Image.asset('images/pic3.jpg'),
      ),
))

完整代码:
Dart code: main.dart
Images: images
Pubspec: pubspec.yaml

由于处理逻辑和布局样式都一起书写到代码中,加上控件的嵌套,flutter的代码会如同html的标签一样嵌套很多层,对前端开发者可能需要时间适应,但对我这种新萌来说降低了从CSS和处理代码之间来回对照的麻烦,并且flutter的内置控件默认的样式已经十分整洁,无需单独再学习类似前端CSS处理页面布局的语法和结构,总体来说降低了不少学习成本,上手更快更简单。能在短时间内掌握生产技能,成就感油然而生,自然有继续学下去的动力。

以上便是最基础的排列布局介绍,相信看到这里的小伙伴已经明白怎么写APP了,今天就到这里~感谢大家支持!
flutter 中文社区(官方QQ群:338252156)

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