Flutter构建布局之路
Posted Bliss91
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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不能保存此二进制文件);
更新 pubspec.yaml 文件以包含 assets 标签. 这样才会使您的图片在代码中可用。更新方法如下:
要注意引用位置,不要写错了,不然就会报资源引用错误
引用方法:
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设计方式(第二部分)
首先,构建标题部分左边栏。将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构建布局之路的主要内容,如果未能解决你的问题,请参考以下文章