在Flutter中构建布局
参考官方文档地址: https://flutterchina.club/tutorials/layout/
下面是需要实现的效果,跟着官方说明走,加深理解。

第一步: 绘制布局图
第一步是将布局拆分成基本的元素:
- 找出行和列.
- 布局包含网格吗?
- 有重叠的元素吗?
- 是否需要选项卡?
- 注意需要对齐、填充和边框的区域.
首先,确定更大的元素。在这个例子中,四个元素排列成一列:一个图像,两个行和一个文本块
接下来,绘制每一行。第一行称其为标题部分,有三个子项:一列文字,一个星形图标和一个数字。它的第一个子项,列,包含2行文字。 第一列占用大量空间,所以它必须包装在Expanded widget中。

第二行称其为按钮部分,也有3个子项:每个子项都是一个包含图标和文本的列。

一旦拆分好布局,最简单的就是采取自下而上的方法来实现它。为了最大限度地减少深度嵌套布局代码的视觉混淆,将一些实现放置在变量和函数中。
Step 2: 实现标题行
首先,构建标题部分左边栏。
- 将Column(列)放入Expanded中会拉伸该列以使用该行中的所有剩余空闲空间。 设置crossAxisAlignment属性值为CrossAxisAlignment.start,这会将该列中的子项左对齐。
- 将第一行文本放入Container中,然后底部添加8像素填充。列中的第二个子项(也是文本)显示为灰色。
- 标题行中的最后两项是一个红色的星形图标和文字“41”。将整行放在容器中,并沿着每个边缘填充32像素
这是实现标题行的代码。
Widget titleSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: new Text(
'Oeschinen Lake Campground',
style: new TextStyle(
fontWeight: FontWeight.bold,
),
),
),
new Text(
'Kandersteg, Switzerland',
style: new TextStyle(
color: Colors.grey[500],
),
),
],
),
),
new Icon(
Icons.star,
color: Colors.red[500],
),
new Text('41'),
],
),
);
step 3: 实现按钮行
按钮部分包含3个使用相同布局的列 - 上面一个图标,下面一行文本。该行中的列平均分布行空间, 文本和图标颜色为主题中的primary color,它在应用程序的build()方法中设置为蓝色:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//...
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
//...
}
由于构建每个列的代码几乎是相同的,因此使用一个嵌套函数,如buildButtonColumn,它会创建一个颜色为primary color,包含一个Icon和Text的 Widget 列。
buildButtonColumn(IconData icon, String label) {
Color color = Theme.of(context).primaryColor;
return new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
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,
),
),
),
],
);
}
构建函数将图标直接添加到列(Column)中。将文本放入容器以在文本上方添加填充,将其与图标分开。
通过调用函数并传递icon和文本来构建这些列。然后在行的主轴方向通过 MainAxisAlignment.spaceEvenly
平均的分配每个列占据的行空间。
// 实现按钮行
Widget buttonSection = new Container(
child: new Row(
// 横向分布的三个列
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildButtonColumn(Icons.call, 'CALL'),
buildButtonColumn(Icons.near_me, 'ROUTE'),
buildButtonColumn(Icons.share, 'SHARE'),
],
),
);
step 4:实现文本部分
将文本放入容器中,以便沿每条边添加32像素的填充。softwrap属性表示文本是否应在软换行符(例如句点或逗号)之间断开。
Widget textSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Text(
'''
Lake 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.
''',
softWrap: true,
),
);
step 5:实现图像部分
四列元素中的三个现在已经完成,只剩下图像部分。该图片可以在Creative Commons许可下在线获得, 但是它非常大,且下载缓慢。在步骤0中,您已经将该图像包含在项目中并更新了pubspec文件,所以现在可以从代码中直接引用它:
body: new ListView(
children: [
new Image.asset(
'images/lake.jpg',
height: 240.0,
fit: BoxFit.cover,
),
// ...
],
)
BoxFit.cover 告诉框架,图像应该尽可能小,但覆盖整个渲染框
step 6: 整合
在最后一步,你将上面这些组装在一起。这些widget放置到ListView中,而不是列中,因为在小设备上运行应用程序时,ListView会自动滚动。
body: ListView(
children: <Widget>[
new Image.asset(
'assets/images/lake.jpg',
width: 600.0,
height: 240.0,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection,
],
),
网友评论