实现一个App的页面主要由两部分组成,一个是UI页面的构建,一个是与UI的交互。
本次笔者通过实现一个页面来介绍如何实现Flutter布局的构建。这个页面长这个模样
布局构建.png在正式内容看之前,请记住一句话
Flutter一切皆组件(Widget)
很可能你已经听过了,但不是很理解,相信看(-跟着做-)完这篇文章,你会对这句话深有体会的。
解剖布局
解剖布局.png整体来看的话,分为四个部分,也就是按行来分,图片、title、图标文字、大段文本。每个部分又细分几个小部分(具体后面详细介绍)。
整体可以看成一个大列,一个大列分为4行(row),这个列(Column)和行(row)都是控件(Widget)。下面就一个个解剖每一行的内容。
构建页面的框架
重写 build 方法,为了方便(懒),我们接下来的代码都在这个方法里
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: page,
);
}
}
可以看到 home 我使用了一个 page 组件,这个组件用于将刚刚划分的四行内容组合起来
实现标题行
第一行是图片比较简单(最后会提及到),我们先看第二行标题行
标题行解剖.png标题行分为三列,第一列又分为两行,不同样式风格的text文本。第二列是个星星图标,第三列是text文本。
定义一个标题行组件,实现上述的功能
Widget titleSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Row( //标题行的内容
children: [
new Expanded(
child: new Column( //摆放第一列的内容: 标题
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: new Text( //摆放第一列第一行的内容
'Pavlova',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0
),
),
),
new Text( //摆放第一列的第二行内容: 分类
'Food',
style: new TextStyle(
color: Colors.grey[500],
fontSize: 15.0
),
)
],
),
),
new Icon( //摆放第二列的内容:星星图标
Icons.star,
color: Colors.red[500],
),
new Text(
'1k',
style: TextStyle(
fontSize: 13.0,
),
) //摆放第三列的内容: 收藏数
]
),
);
实现按钮行部分
第三行是按钮行,是由三列图标(icon)加 文本(text) 构成
按钮行解剖.png定义按钮行的组件,由于三列UI都是一样的,写三个相同的组件太冗余。我们只需要定义一个方法,把图标和文本作为参数,返回使用这些参数构成的这个组件就好了。
Column buildBottomText(IconData icon,String text){
Color color=Theme.of(context).primaryColor; //主题的主颜色
return new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Icon(icon,color: color),
new Container(
margin: const EdgeInsets.only(top: 8.0),
child: new Text(
text,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color
),
),
),
],
);
}
实现大文本部分
最后一行是一大串文本,这个就比较简单了
定义一个有文本样式的文本组件就ok了
Widget bigText = new Container(
padding: const EdgeInsets.all(32.0),
child: Text(
'Lake Oeschinen lies at the fo....', //文本内容省略
softWrap: true,
style: TextStyle(
fontSize: 15.0
),
),
);
整合组件
按之前说的,我们应该要用 Column (列)的组件将上面实现的几行组件整合起来,但是忽略了一个情况,当我们的文本内容过多,这一个页面是显示不完的,余下的部分就会被遮挡的。
这个时候我们可以用 ListView 的组件,让这个页面可以上下滚动
定义 page 组件
//把这几个组件用listview整合起来
Widget page=new Scaffold(
appBar: new AppBar(
title: new Text(
'Image Introduction'
),
),
body: new ListView(
children: <Widget>[
new Image.asset(
'images/pavlova.jpg',
width: 600.0,
height: 240.0,
fit: BoxFit.cover,
),
titleSection,
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildBottomText(Icons.star, 'start'),
buildBottomText(Icons.share, 'share'),
buildBottomText(Icons.favorite, 'like')
],
),
bigText,
],
),
);
可以看到 ListView 的第一个孩子内容就是图片了,看起来还是比较简单的,只需要把路径图片写上就好了。在项目的最外层新建一个 images 文件夹,把图片放入,但是运行App,会发现图片加载不出来,抛出找不到该图片的异常
这是因为我们少了一个步骤,没有在 pubspec.yaml 这个文件里面把该图片加入进去,所以在 Image.asset 的asset 里面就找不到该图片
把该文件里的 #assets 的 # 去掉,加上这个图片路径
assets:
- images/pavlova.jpg
一些细节问题
1.为什么要用容器 Container 组件?
Container 主要有三个作用:
- 添加内边距,外边距和边框
- 改变背景颜色和背景图像
- 可以容纳一个子控件,这个控件也可以是 Row 或 Column ,甚至控件树的根结点。
比如下面代码,可以把 container 容器与四周添加 32.0 的间距
padding: const EdgeInsets.all(32.0),
2.mainAxisAlignment 和 crossAxisAlignment 属性有什么作用?
这两个属性用于控制行或列的排列方式
对齐控件.png比如在 Column 组件上将主轴对齐设置为 spaceEvenly,这将会在垂直方向上均匀划分剩余的空间
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
3.ListView 组件有什么作用?
- 一个类似 Column 布局的控件
- 可以在水平或垂直方向上进行布局
- 内容超长时可以提供滚动
- 比 Column 更少配置,但更易于使用并支持滚动
4.在实现标题行组件的时候用到了 Expanded 组件,它是干什么的?
假如我想实现下面这张图的效果,中间的图片宽度是另外两张的两倍,这个就可以用到 Expanded 了
expand组件.png比如上面的图片可以这么实现
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
new Expanded(
child: new Image.asset('images/pic1.jpg'),
),
new Expanded(
flex: 2,
child: new Image.asset('images/pic2.jpg'),
),
new Expanded(
child: new Image.asset('images/pic3.jpg'),
)
]
),
Expanded 控件具有 flex 属性,它是一个整数,用于确定控件的弹性因子。Expanded 控件的默认弹性因子是1。
总结
实现完这样一个小清新的页面,相信你知道了如何去构建一些简单的 Flutter 布局。肯定对 Flutter一切皆组件 这句话有了更深刻的理解了吧,真的是哪里都是组件[汗]。
网友评论