构建布局实例
这个实例会构建下图所示的页面
配置图片
在项目根目录下创建一个images目录
在百度搜索下载资源图片
在pubspec.yaml文件添加图片资源—images/park.jpg
name: myapp
description: A new flutter project.
dependencies:
flutter:
sdk: flutter
flutter:
uses-material-design: true
assets:
- images/park.jpg
绘制布局图
确定布局的基本要素:
- 确定行和列
- 布局是否包含网格
- 有没有重叠的元素
- 用户界面是否需要标签
- 注意对齐、填充或边框的区域
首先,确定较大的元素,在这个例子中,将四个元素排列成一列,其中包含一个图像、两个行和一个文本块。
接下来,图解两个行。第一行可以称为“标题”部分,有3个子控件,包含一列文本、一个星形图标和一个数字。其中第一个子控件,列,包含2行文本。第一列需要大量空间,因此必须将其包装在Expanded控件(扩展Row,Column或Flex的子控件)中。
第二行可以称为“按钮”部分,第二行也有3个子控件,每个控件都是包含图标和文本的一列。
一旦绘制了布局图,最简单的方法就是采用自下而上的方法来实现。为了避免造成深层嵌套布局代码的视觉混乱,将一些实现放在变量和函数中。
实现标题部分
首先,在标题部分中构建第一列。将列放在Expanded控件中可以延伸列以使用该行中的所有剩余可用空间。将crossAxisAlignment属性设置为CrossAxisAlignment.start将列定位到行的开头。
将第一行文本放在Container(容器)控件中可以添加填充。列中的第二个子控件也是文本,显示为灰色。
标题部分中的最后两个控件是一个星形图标,画成红色,文本显示66。将整行放置在沿着每个边缘的容器和衬垫中,并偏移32个像素。
以下是实现标题部分的代码。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
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(
'红花山公园',
style: new TextStyle(
fontWeight: FontWeight.bold,
)
)
),
new Text(
'深圳市,光明新区,公明镇中心',
style: new TextStyle(
color: Colors.grey[500]
)
)
]
)
),
new Icon(
Icons.star,
color: Colors.red[500],
),
new Text('66')
]
)
);
//...
}
实现按钮部分
按钮部分包含3列使用相同的布局的一列图标和文字,这一行中的列均匀间隔,文本和图标将以主题颜色绘制,在应用程序的build()方法中设置为蓝色。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
//...
}
由于用于构建每一行的代码几乎相同,所以创建一个嵌套函数是非常好的,比如buildButtonColumn(),它接受一个“图标”和“文本”,并返回一个以其主题颜色绘制的控件的列。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
Column buildButtonColumn(IconData icon, String label) {
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(label,
style: new TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
)
)
)
]
);
}
//...
}
构建函数将图标直接添加到列中,将文本放入Container(容器)中以在文本上方添加填充,将其与图标分隔开。
通过调用函数并传递特定于该列的图标和文本来构建包含这些列的行。使用MainAxisAlignment.spaceEvenly沿主轴对齐列,以便在每列之前,之间和之后均匀排列可用空间。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
Widget buttonSection = new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildButtonColumn(Icons.call, '电话'),
buildButtonColumn(Icons.near_me, '导航'),
buildButtonColumn(Icons.call, '分享'),
])
);
//...
}
实现文本部分
将文本部分定义为相当长的变量,将文本放在Container(容器)中,以便在每个边缘添加32个像素的填充。softWrap属性指示文本是否自动换行。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
Widget textSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Text(
'''
红花山公园地处深圳市光明新区公明街道中心,位居松白公路的北端,红花山公园处处是精雕细刻的绿色,整洁、美丽的红花山公园就像一出浴的少女你无法拒绝她的温柔。登上仅百米的红花山极目远眺,楼群与树木花草相互掩映,经过修剪的树木造型别致分列公路两旁。2007年,光明新区公明街道投资30万元建设的红花山公园电子监控系统已完工,该公园各大出入口、环山路和主要休闲景点已设置25个电子监控探头,已经正式投入使用。辖区居民在此活动又多了道安全“保护网”。
''',
softWrap: true,
)
);
//...
}
实现图像部分
列元素中的三个现在完成,只留下图像部分。在前面,我们已经将图像包含在项目中并更新了pubspec文件,现在可以从代码中引用它。
body: new ListView(
children: [
new Image.asset(
'images/park.jpg',
height: 240.0,
fit: BoxFit.cover,
),
//...
]
)
BoxFit.cover告诉框架图像应尽可能小,但覆盖其整个渲染框。
合并在一起
在最后一步中,您将这些部分组合在一起,将这些控件安排在ListView中,而不是列,因为ListView会在移动设备上运行应用程序时自动滚动。
body: new ListView(
children: [
new Image.asset(
'images/park.jpg',
height: 240.0,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection
]
),
//...