如何在Flutter中构建漂亮的UI布局小部件
通过一个简单的例子,逐步向大家介绍如何在Flutter
中构建漂亮的布局。通过本文您将了解以下几点:
Flutter
中的布局机制是如何工作的- 如何在垂直和水平方向上布局
Widget
- ❿Flutter❿
Widget
布局
本文档主要介绍如何在Flutter
中进行布局。您最终将构建一个如下所示的页面: The 完成的应用程序
本文将带您逐步构建一个如上所示的 A 页面。 ?运行效果如下图: image-20190610130957545
第一步:分析页面布局逻辑
第一步将整个页面布局分解为以下基本元素,主要是点
- 识别布局中的行 (
Row
) 和列 (Column
) ❀erer )? - 和
Widget
之间是否有重叠? - 整个 UI 布局是否需要
tab
列?按照区域(对齐
)、内边距(内边距
)和框架(边框边框
边框)页面-页页面:栏目元素可以看到,整个页面的主要组成部分就是红框标注的4个部分。这 4 个部分分别放置在同一列(
列
)中:1 个图像
、2 个❓ 和行和❿1 个 Sms。我们来深入分析一下每一行(
Row
):- 第1行,也就是标题部分(TitleSection),有3列:1由文本Column(列)行组成,1行星号图标,1 个数字:
标题部分
您可以看到,由于第一列(
列
)占据了整行的大部分空间,因此应该用 包裹起来扩展小部件。 - 第二行是按钮列(
按钮部分
),也包含3个子元素。每个子元素为一行,该行的内容为图标
和1个文字
:按钮部分
- 第三行没有:一个很复杂的组成,它是一个简单的
文本
块。
经过上面的分析,我们将一个复杂的页面划分为几个简单的组件,这样可以简化整个页面的实现。为了避免布局代码中的混乱,我们应该使用变量和函数来构建布局的子部分。 ,我将在下面的代码中演示。
第2步:实现第一行(标题行)
直接提供代码。具体解释见代码注释。注意注释:
Widget titleSection = Container( //为整个Widget(即这一行)的所有所有方向设置32px的填充 padding: const EdgeInsets.all(32), child: Row( children: [ Expanded( /** 将Column放置在Expanded中,由于Expanded会默认占据所有当前Row的空闲可用空间,所以这个Column也会自然被拉伸的占据完所有当前Row可用的空闲空间。 */ child: Column( /**将Column的crossAxisAlignment属性设置为CrossAxisAlignment.start以保证这个列中的元素(即children属性中的Widget)在水平方向上排列在当前Column的起始位置 */ crossAxisAlignment: CrossAxisAlignment.start, children: [ /** 将这个Text放在Container中的目的是通过Container来添加填充(padding) */ Container( padding: const EdgeInsets.only(bottom: 8), child: Text( 'Oeschinen Lake Campground', style: TextStyle( fontWeight: FontWeight.bold, ), ), ), Text( 'Kandersteg, Switzerland', style: TextStyle( color: Colors.grey[500], ), ), ], ), ), /** 最后的2个元素分别是1个Icon和1个Text,分别用来显示星星和数字 */ Icon( Icons.star, color: Colors.red[500], ), Text('41'), ], ), );
直接编辑上面的变量
titleSection只需将放在
)第1列由1个圆锥体组成,每列:I,1app文本中
:注意这里使用了标题:代码所以不会因为MaterialApp(..)
中的代码太长且令人困惑。如运行的效果如下图所示:图片-20190610133211088
步骤3:实现按钮栏(按钮部分
文本
。该行中的列都是均匀间隔的,并且由于构建每列的代码几乎相同,因此创建一个名为buildbuttoncolumn()
的私有方法,该方法接收颜色 (Color ) 参数、一个图标 (
❿给定'n的列(Icon
) 参数和 1 个文本 (Text
) 参数,并返回一个带有 和 ❓ 的小部件Column
),代码如下:class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // ··· } Column _buildButtonColumn(Color color, IconData icon, String label) { return Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color), Container( margin: const EdgeInsets.only(top: 8), child: Text( label, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w400, color: color, ), ), ), ], ); } }
_buildButtonColumn()
方法将Iconn直接添加到nn 。 和
文本
先用Container
包裹,然后将Container
添加到列。这样做的目的是使用
Container
设置顶部填充 (padding
) 到Textt so 和
icon
相距不太远近。完成函数
_buildButtonColumn()
后,我们只需要在需要构建列时调用该函数(Column
- 第1行,也就是标题部分(TitleSection),有3列:1由文本Column(列)行组成,1行星号图标,1 个数字:
Column)传入相应的参数即可。我们需要的列:
Color color = Theme.of(context).primaryColor;
Widget buttonSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildButtonColumn(color, Icons.call, 'CALL'),
_buildButtonColumn(color, Icons.near_me, 'ROUTE'),
_buildButtonColumn(color, Icons.share, 'SHARE'),
],
),
);
请注意,此处设置的 mainAxisAlignment
属性值是 MainAxisAlignment.spaceEvenly❀❀,❓
中列均匀地填充行的整个可用空间。
然后在app body
中设置变量buttonSection
:
return MaterialApp(
title: 'Flutter layout demo',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Column(
children: [titleSection, buttonSection],
)));
运行效果图如下所示 4053003 8
第4步:实现正文部分
将 Text
部分定义为变量,然后将 Text
放入 ❀♿ create♿ 容器 Container 设置 padding
属性:
Widget textSection = Container(
padding: const EdgeInsets.all(32),
child: 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,
),
);
注意这里设置 softwrap
属性值是
Widget textSection = Container(
padding: const EdgeInsets.all(32),
child: 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,
),
);
注意,这里设置 softwrap
属性值是❀♿,它可以在单词边界换行,而不是在单词中间换行单词。 。
然后将上面的textSection
变量放入app body
中:
return MaterialApp(
title: 'Flutter layout demo',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Column(
children: [titleSection, buttonSection, textSection],
)));
运行效果图如下所示 410091 30
Step 5 :实现图像部分
到目前为止,我们已经实现了 4 行中的 3 行,只有图像行尚未实现。这一步,我们实现图片的显示:
- 在项目A的顶层文件夹中创建
images
文件夹 - 将以下图片放在刚才的❿图片文件夹下创建并命名为
lake.jpg
img
- update
pubspec.yaml
文件,添加Assets
标签,配置图像路径,以便您可以访问代码中保存的图片:flutter: uses-material-design: true assets: - images/lake.jpg
然后我们可以参考代码中的图片:
return MaterialApp(
title: 'Flutter layout demo',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Column(
children: [
Image.asset(
'images/lake.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection
],
)));
驱动效果如下:image-20190610144829576❀第6步:我们来看看完成前几个步骤后的最终代码:
return MaterialApp(
title: 'Flutter layout demo',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Column(
children: [
Image.asset(
'images/lake.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection
],
)));
注意,我们想要将属Body性的属性值设置为❓column,然后我们在 效果如下:Column中实现了一些小部件,现在我们改变使用方式,使用它,使用它,使用它,使用它,使用它,使用它,使用它。
ListView
来替换这个Column
:return MaterialApp(
title: 'Flutter layout demo',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: ListView(
children: [
Image.asset(
'images/lake.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection
],
)));
6114906-210561 可以使用查看
列
并使用 ListView
的静态视觉效果是一样的,那么我们应该使用Column
还是ListView❀呢?两者有什么区别?答案就在于“动态”。两者在动态条件下的效果是不同的。更具体地说,使用
Column
构建的 Widget
不支持滚动,即不支持滚动。上下或左右滚动事件,使用ListView
支持滚动事件如下:
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。