Flutter-布局

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter-布局相关的知识,希望对你有一定的参考价值。

参考技术A

flutter布局需要先了解flutter所有布局的widget,首先flutter布局分为Container、RenderObjectWidget和ParentDataWidget。而RenderObject中经常使用的有SingleChildRenderObjectWidget(单节点)和MultiChildRenderObjectWidget(多节点)。

flutter中基础的widget,可以为子节点设置内间距。当padding没有child的时候,它会产生一个宽为left+right,高为top+bottom的区域,当padding的child不为空的时候会将约束传递给child。一般在使用间距的地方使用。

设置child的对齐方式,并根据child的尺寸调整自身的尺寸。

设置透明度

用于矩形圆角裁剪组件

用于圆形裁剪,但是可以添加阴影和Z轴

给组件绘制区域大小

百分比布局,可以通过widthFactor或者heightFactor设置宽高占比

子组件叠加布局,也称绝对布局

mainAxisAlignment: start:顶头 center:居中 end:接尾 spaceAround:中间的孩子均分,两头的孩子空一半 spaceBetween:顶头接尾其他均分 spaceEvenly:均匀分布

crossAxisAlignment: start:顶头 center:居中 end:接尾 stretch:伸展

mainAxisSize: max:父容器没有约束的话,column自身会尽可能延伸 min:不会延伸,只会包裹自己

属性和column属性一致,只是方向不同,一个竖直方向一个水平方向。使用方法也类似。

Wrap可以实现流布局,单行的Wrap跟Row一样,单列的Wrap则跟Colum一样.但是Row和Column都是单行单列的,Wrap可以多行多列。

Wrap能做的事情,flow也能做。但是flow会比较复杂点。flow类似于OC中CollectionLayout,需要自己实现子组件的位置以及大小。但是flow的性能比较好,灵活。

自定义FlowDelegate

运行结果:

绝对定位布局,用于指定组件的具体位置

Expanded组件可以使row、column或者flex子组件在其主轴上展开并填充可用空间。如果多个组件展开的话,会按照比例分割。
Tip:Expanded组件要在row、column或者flex的子组件中使用。具有两个属性,child:子组件,flex:所占比例

Flutter构建布局之路


本文参考链接:在Flutter中构建布局,主要解释一下这里的思路和遇到的坑。

Flutter布局机制简介

Flutter布局机制的核心就是widget。在Flutter中,几乎所有东西都是一个widget - 甚至布局模型都是widget。在Flutter应用中看到的图像、图标和文本都是widget。 甚至你看不到的东西也是widget,例如行(row)、列(column)以及用来排列、约束和对齐这些可见widget的网格(grid)。

要布局的页面

按照文档所述,我们要实现的就是下图页面的效果,将整个widget分割成数值排列的四个子widget,每个widget又可以单独设计。本身少费脑细胞的思路,我们从上向下来看,分别是:一个图片资源widget,一个标题widget,一个按钮widget,一个文本widget,这里概念很清晰,每个子布局都作为一个widget来进行处理。下边就各个击破。

Flutter中图片资源加载方式(第一部分)

为了更有针对性,这里只介绍图片资源的加载方式,关于其他资源的加载方式,请参见资源与图像

图像添加到工程目录中,在project根目录下新建文件夹images,将图片放进去 (注意,wget不能保存此二进制文件);

Flutter构建布局之路

添加图片到项目

更新 pubspec.yaml 文件以包含 assets 标签. 这样才会使您的图片在代码中可用。更新方法如下:

Flutter构建布局之路

更新位置

要注意引用位置,不要写错了,不然就会报资源引用错误

Flutter构建布局之路


  1. 引用方法:

 1body: new ListView(
2  children: [
3    new Image.asset(
4      'images/lake.jpg',
5      height: 240.0,
6      fit: BoxFit.cover,
7    ),
8    // ...
9  ],
10)

至此,图片的问题我们就解决了。

标题栏widget设计方式(第二部分)

Flutter构建布局之路

首先,构建标题部分左边栏。将Column(列)放入Expanded中会拉伸该列以使用该行中的所有剩余空闲空间。 设置crossAxisAlignment属性值为CrossAxisAlignment.start,这会将将列中的子项左对齐。

将第一行文本放入Container中,然后底部添加8像素填充。列中的第二个子项(也是文本)显示为灰色。
标题行中的最后两项是一个红色的星形图标和文字“41”。将整行放在容器中,并沿着每个边缘填充32像素

 1class MyApp extends StatelessWidget {
2  @override
3  Widget build(BuildContext context) {
4    Widget titleSection = new Container(
5      padding: const EdgeInsets.all(32.0),
6      child: new Row(
7        children: [
8          new Expanded(
9            child: new Column(
10              crossAxisAlignment: CrossAxisAlignment.start,
11              children: [
12                new Container(
13                  padding: const EdgeInsets.only(bottom: 8.0),
14                  child: new Text(
15                    'Oeschinen Lake Campground',
16                    style: new TextStyle(
17                      fontWeight: FontWeight.bold,
18                    ),
19                  ),
20                ),
21                new Text(
22                  'Kandersteg, Switzerland',
23                  style: new TextStyle(
24                    color: Colors.grey[500],
25                  ),
26                ),
27              ],
28            ),
29          ),
30          new Icon(
31            Icons.star,
32            color: Colors.red[500],
33          ),
34          new Text('41'),
35        ],
36      ),
37    );
38  //...
39}

代码解析:

标题栏

按钮布局widget设计方式(第三部分)

按钮

按钮部分包含3个使用相同布局的列 - 上面一个图标,下面一行文本。该行中的列平均分布行空间, 文本和图标颜色为主题中的primary color,它在应用程序的build()方法中设置为蓝色:

 1class MyApp extends StatelessWidget {
2  @override
3  Widget build(BuildContext context) {
4    //...
5
6    return new MaterialApp(
7      title: 'Flutter Demo',
8      theme: new ThemeData(
9        primarySwatch: Colors.blue,
10      ),
11
12    //...
13}

.>由于三个按钮布局相同,大小一致,我们这里使用嵌套函数进行处理,如buildButtonColumn,它会创建一个颜色为primary color,包含一个Icon和Text的 Widget 列。

 1 Column buildButtonColumn(IconData icon, String label) {
2      Color color = Theme.of(context).primaryColor;
3      return new Column(
4        mainAxisSize: MainAxisSize.min,
5        mainAxisAlignment: MainAxisAlignment.center,
6        children: [
7          new Icon(icon, color: color),
8          new Container(
9            margin: const EdgeInsets.only(top: 8.0),
10            child: new Text(
11              label,
12              style: new TextStyle(
13                fontSize: 12.0,
14                fontWeight: FontWeight.w400,
15                color: color,
16              ),
17            ),
18          ),
19        ],
20      );
21    }

构建函数将图标直接添加到列(Column)中。将文本放入容器以在文本上方添加填充,将其与图标分开。
通过调用函数并传递icon和文本来构建这些列。然后在行的主轴方向通过 MainAxisAlignment.spaceEvenly 平均的分配每个列占据的行空间。

 1class MyApp extends StatelessWidget {
2  @override
3  Widget build(BuildContext context) {
4    //...
5
6    Widget buttonSection = new Container(
7      child: new Row(
8        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
9        children: [
10          buildButtonColumn(Icons.call, 'CALL'),
11          buildButtonColumn(Icons.near_me, 'ROUTE'),
12          buildButtonColumn(Icons.share, 'SHARE'),
13        ],
14      ),
15    );
16  //...
17}

文本部分

将文本放入容器中,以便沿每条边添加32像素的填充。softwrap属性表示文本是否应在软换行符(例如句点或逗号)之间断开

1  Widget textSection = new Container(
2      padding: const EdgeInsets.all(32.0),
3      child: new Text(
4        '''
5Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese Alps. Situated 1,578 meters above sea level, it is one of the larger Alpine Lakes. A gondola ride from Kandersteg, followed by a half-hour walk through pastures and pine forest, leads you to the lake, which warms to 20 degrees Celsius in the summer. Activities enjoyed here include rowing, and riding the summer toboggan run.
6        ''',
7        softWrap: true,
8      ),
9    );

整合

将上面这些组装在一起。这些widget放置到ListView中,而不是列中,因为在小设备上运行应用程序时,ListView会自动滚动。

 1body: new ListView(
2  children: [
3    new Image.asset(
4      'images/lake.jpg',
5      width: 600.0,
6      height: 240.0,
7      fit: BoxFit.cover,
8    ),
9    titleSection,
10    buttonSection,
11    textSection,
12  ],
13),

通过这一步步走来,我们可以看到,其实关于UI布局,我们的处理的东西其实只有一个,那就是widget,我们这里只是根据官方demo给了图片和文本的基本布局和简单的嵌套的方式,更多widget的介绍当然还是参考Widget目录

其实所有的布局全都是在一个widget中进行的,包括方法在内,dart的结构如下所示:
debugPaintSizeEnabled = true;//开启可视化调试

 1void main() {
2  debugPaintSizeEnabled = true;  runApp(MyApp());
3}
4
5class MyApp extends StatelessWidget {
6  @override
7  Widget build(BuildContext context) {
8  //
9    Widget titleSection = Container(
10      padding: const EdgeInsets.all(32.0),
11      child: Row(
12        children: [
13          Expanded(
14            child: Column(
15              crossAxisAlignment: CrossAxisAlignment.start,
16              children: [
17
18      ),
19    );
20
21    Column buildButtonColumn(IconData icon, String label) {
22    //自定义方法,用来布局那三个按钮
23        }
24
25    Widget buttonSection = Container(
26    //这里是按钮widget
27      child: Row(
28        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
29        children: [
30          buildButtonColumn(Icons.call, 'CALL'),
31          buildButtonColumn(Icons.near_me, 'ROUTE'),
32          buildButtonColumn(Icons.share, 'SHARE'),
33        ],
34      ),
35    );
36
37    Widget textSection = Container(
38    //这里是文本widget
39         );
40
41    return MaterialApp(
42      title: 'Flutter Demo',
43      home: Scaffold(
44        appBar: AppBar(
45          title: Text('Top Lakes'),
46        ),
47        body: ListView(
48          children: [
49          //1、图片
50            Image.asset(
51              'images/lake.jpg',
52              width: 600.0,
53              height: 240.0,
54              fit: BoxFit.cover,
55            ),
56           //2、标题栏 
57            titleSection,
58            //3、按钮
59            buttonSection,
60            //4、文本
61            textSection,
62          ],
63        ),
64      ),
65    );
66  }
67}


以上是关于Flutter-布局的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 多子 Widget 布局之线性布局 RowColumn

flutter学习常见布局组件

flutter学习常见布局组件

Flutter自定义布局套路

Flutter 布局相关知识点

flutter wrap 流式布局