从零开始的Flutter开发_Day2

  • 时间:
  • 来源:互联网
  • 文章标签:

主要内容为如何在Flutter中构建布局,参照Flutter中文网

在Flutter中构建布局

Flutter的布局方法

重点

- Widget 是用于构建 UI 的类
- Widget 用于布局和 UI 元素
- 通过简单的 Widget 构建复杂的 Widget

Flutter 布局机制的核心就是 Widget
在 Flutter 中,几乎所有东西都是一个 Widget;

您在Flutter应用中看到的图像、图标和文本都是widget。 甚至你看不到的东西也是widget,例如行(row)、列(column)以及用来排列、约束和对齐这些可见widget的网格(grid)

复杂的widget

如下图,有三个图标,每个图标下有一个标签:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pEmT4bv2-1591707683185)(/从零开始的Flutter开发-Day2/icons.png)]
该截图显示布局结构,表示一行包括3列,其中每列包含一个图标(icon)和一个标签

如果希望显示布局情况,需要以下两步:
1.在mian.dart中声明引入包import 'package:flutter/rendering.dart';
2.在主函数中声明debugPaintSizeEnabled = true;

上述 UI 的 widget 树如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25exhKu8-1591707683191)(/从零开始的Flutter开发-Day2/layoutTree.png)]

Container

上图很容易理解,但是 Container 的存在可能令人疑惑

Container是什么

Container 可让您创建矩形视觉元素。Container 可以装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container 可以使用矩阵在三维空间中对其进行变换

为什么要有Container

如果要添加填充,边距,边框或背景色,要使用 Container 来设置,只有 Container 有这些属性

在这个例子中,每个 Text 放置在 Container 中以添加边距。整个行也被放置在容器中以在行的周围添加填充

布局一个Widget

在 Flutter 中,在屏幕上放置文本,图标或图像大致需要以下几个步骤

选择一个widget保存该对象

布局 widget 中进行选择,布局 widget 用于排列其它 widget 的 columns、rows、grids 和其它的 layouts。

创建一个widget来容纳可见对象

例如,创建一个 Text widget

new Text('Hello World', style: new TextStyle(fontSize: 32.0))

创建一个 Text widget

new Image.asset('foo/bar.jpg', fit: BoxFit.cover)

创建一个 Text widget

new Icon(Icons.star, color: Colors.red[500])

将可见widget添加到布局widget

布局 widget 都含有child属性(如Container、Padding、Center)或者children属性(如Row、Column、Stack),子 widget 会继承布局 widget的某些属性以实现布局效果

将Text widget添加到Center widget:

new Center(
	child: new Text('Hello World', style: new TextStyle(fontSize: 32.0))
	)

Center可以将内容水平和垂直居中

将布局widget添加到页面

Flutter 本身就是一个 widget,大部分widget都有一个build()方法。在应用程序的build()方法中创建会在设备上显示的widget。
对于Material应用程序,我们可以将Center widget直接添加到body属性中:

class _MyHomePageState extends State<MyHomePage> {
	@override
	Widget build(BuildContext context) {
		return new Scaffold(
			appBar: new appBar(
				title: new Text(widget.title),
			),
			body: new Center(
				child: new Text('Hello World', style: new TextStyle(fontSize: 32.0)),
			),
		);
	}
}

对于非Material应用程序,我们可以将Center widget添加到应用程序的build()方法中:

void main() {
	runApp(new MyApp());
}

class MyApp extends StatelessWidget {
	@override
	Widget build(BuildContext context) {
		return new Container(
			decoration: new BoxDecoration(color: Colors.white),
			child: new Center(
				'Hello World',
				textDirection: TextDirection.ltr,
				style: new TextStyle(fontSize: 40.0, color: Colors.black87)),
			),
		);
	}
}

注意: 默认情况下,非Material应用程序不包含AppBar,标题或背景颜色。 如果要在非Material应用程序中使用这些功能,则必须自己构建它们

垂直/水平放置多个widget

最常见的布局模式
1.使用行 Row 水平排列widget;
2.使用列 Column 垂直排列widget

将一个 widget 列表添加到Row中以创建行,添加到Column中以创建列

“添加”到Row/Column中,即将 widget 列表添加至children属性中

每个孩子本身可以是一个Row或一个Column,依此类推
以下示例显示如何在行或列内嵌套行或列:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CXVmBnax-1591707683193)(/从零开始的Flutter开发-Day2/pavlova-diagram.png)]

对齐widgets

利用mainAxisAlignmentcrossAxisAlignment属性来对其行/列的子项,这两个属性在Row/Column中的体现如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnS4cpHz-1591707683196)(/从零开始的Flutter开发-Day2/row-diagram.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UaY06L4r-1591707683199)(/从零开始的Flutter开发-Day2/column-diagram.png)]
MainAxisAlignment 和 CrossAxisAlignment 类提供了很多控制对齐的常量

MainAxisAlignment中的常量

  • center:子项居中排列,子项之间没有间隔
  • end:子项靠近 main axis 的末尾排列
  • spaceAround:子项间的间隔相等,且第一个子项之前和最后一个子项之后的间隔是子项间间隔的一半
  • spaceBetween:子项间间隔相等,第一个子项之前和最后一个子项之后没有间隔
  • spaceEvenly:在每个子项之间,之前和之后均匀分配空闲的空间
  • start:子项靠近 main axis 的起始排列

CrossAxisAlignment中的常量

  • center:子项的中线与 cross axis 的中点对齐,这是默认的对齐方式
  • start:子项靠近 cross axis 的起始排列
  • end:子项靠近 cross axis 的末尾排列
  • stretch:要求子项填满整个 cross axis

示例

  1. 一列三个图片以spaceBetween方式对齐
    代码如下:
 Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Test',
      theme: new ThemeData(primarySwatch: Colors.blue),
      home: new Scaffold(
        appBar: new AppBar(title: new Text('Alignment Test'),),
        body: new Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              new Image.asset('images/01.jpg'),
              new Image.asset('images/02.jpg'),
              new Image.asset('images/03.jpg'),
            ],
          ),
        ),
      ),
    );

运行结果如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MU2mPlKA-1591707683201)(/从零开始的Flutter开发-Day2/result1.jpg)]

可以看到:第一张图之前和最后一张图之后没有留下空间

  1. 一行三个图片以spaceEvenly方式对齐
    代码与上方大同小异,运行结果如图:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ByY2Z4UD-1591707683202)(/从零开始的Flutter开发-Day2/result2.jpg)]
    注意:如果布局太大而不适合设备,则会如上图所示,在受影响的边缘出现黄黑条纹;
    解决办法:使用 Expanded widget,将widget的大小设置为适和行或列

后文会详细提及

调整widget

如果想要一个 widget 占据其兄弟 widget 两倍的空间,我们可以将行/列的子项放入Expanded widget中,以控制沿主轴 (main axis) 方向的 widget 大小
Expanded widget有一个flex属性(整数),用于确定 widget 的弹性系数,默认为 1

示例

  1. 创建一个由三个widget组成的行,其中中间widget的宽度是其他两个widget的两倍
    代码:
new Column(
   mainAxisAlignment: MainAxisAlignment.spaceBetween,
   children: <Widget>[
            new Expanded(child: new Image.asset('images/EVA_01.jpg')),
            new Expanded(
                  flex: 2,
                  child: new Image.asset('images/EVA_02.jpg')
            ),
            new Expanded(child: new Image.asset('images/EVA_03.jpg')),
          ],
)

结果如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h5P2UMSZ-1591707683203)(/从零开始的Flutter开发-Day2/result3.jpg)]

  1. 修复上一节中的示例:
    将上一节代码中所有的new Image.asset()new Expanded()包裹,如下:
new Expanded(child: new Image.asset('images/01.jpg')

默认情况下,每个widget的弹性系数为1,将行的三分之一分配给每个小部件

聚集widgets

默认情况下,行或列沿着其主轴会尽可能占用尽可能多的空间,但如果要将孩子紧密聚集在一起,可以将mainAxisSize设置为MainAxisSize.min,如下:

 var packedRow = new Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.black),
        new Icon(Icons.star, color: Colors.black),
      ],
    );

行/列的嵌套

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Su3l8pet-1591707683204)(/从零开始的Flutter开发-Day2/nest-result.png)]
实现图中红框部分的布局

  1. 构造评分行的widget树:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7cSP73yX-1591707683205)(/从零开始的Flutter开发-Day2/nest-widget-tree.png)]
    根据该树编写代码如下:
var ratingRow = new Container(
      padding: new EdgeInsets.all(20.0),
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          new Row(
            mainAxisSize: MainAxisSize.min,
            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(
            '114514 Reviews',
            style: new TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w800,
              fontFamily: 'Roboto',
              letterSpacing: 0.5,
              fontSize: 20.0,
            ),
          ),
        ],
      ),
    );
  1. 构造评分行下方图标行的widget树:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qti56HgL-1591707683206)(/从零开始的Flutter开发-Day2/nest-widget-tree2.png)]
    根据该树编写代码如下:
var descTextStyle = new TextStyle(
      color: Colors.black,
      fontWeight: FontWeight.w800,
      fontFamily: 'Roboto',
      letterSpacing: 0.5,
      fontSize: 18.0,
      height: 2.0,
    );//定义默认Text样式

    var iconRow = DefaultTextStyle.merge(
     // DefaultTextStyle.merge可以允许您创建一个默认的文本样式
     //该样式会被其所有的子节点继承
      style: descTextStyle,
      child: 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'),
              ],
            ),
          ],
        ),
      ),
    );
  1. 整合
    代码如下:
return new MaterialApp(
      title: 'Test',
      theme: new ThemeData(primarySwatch: Colors.blue),
      home: new Scaffold(
        appBar: new AppBar(title: new Text('Alignment Test'),),
        body: new Center(
          child: new Container(
            child: new Column(
              children: <Widget>[
                ratingRow,
                iconRow,
              ],
            ),
          ),
        ),
      ),
    );

效果如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-caK9pCPr-1591707683207)(/从零开始的Flutter开发-Day2/nest-result.jpg)]

本文链接http://www.taodudu.cc/news/show-82929.html