Flutter——布局
liuian 2025-05-21 14:57 46 浏览
上一节介绍了Flutter构建界面的一些要素,并通过一些代码和运行结果让大家对Flutter能有一些基本了解。从这一节开始,将对Flutter构建界面的要素进行逐一展开。
不管我们之前是做web开发、桌面端开发、移动客户端开发,其实都知道布局,它是我们构建界面的基础。那么在Flutter中学习布局,需要理解核心要点有哪些呢?
- 部件是用来构建UI的类。
- 部件用于布局和UI元素。
- 组合简单的部件来构建复杂的部件。
Flutter布局机制的核心是widget,即部件。在Flutter中,几乎所有东西都是部件——甚至布局模型也是部件。在Flutter应用程序中看到的图像、图标和文本都是部件。还有看不到的东西也是部件,例如排列、约束和对齐可见部件的行、列和网格。
通过组合部件来创建布局,从而构建更复杂的部件。例如,下图显示了可视化布局,显示了一行3列,每列包含一个图标和一个标签。
下面是这个UI的部件树:
上图中大部分应该与我们所期望的一样,但是可能会对Container(粉红色显示)感到疑惑。Container是一个部件类,允许自定义它的子部件。当需要添加填充、边距、边框或背景颜色时,可以使用Container来命名它的一些功能。
在本例中,每个Text小部件都放置在一个Container中,并添加边距,整个Row也被放置在一个Container中,以实现在行周围添加填充。
本例中UI的其余部分由属性控制。使用color属性设置图标的颜色,使用Text.style属性设置字体、颜色、粗细等。列和行也具有一些属性,这些属性允许您指定它们的子元素如何垂直或水平对齐,以及子元素应该占用多少空间。
实现一个简单的布局
如何在Flutter中布局一个部件?后续将展示如何创建和显示一个简单的部件,它还显示了一个简单的Hello World应用程序的完整代码。在Flutter中,只需要几个步骤就可以在屏幕上放置文本、图标或图像。
1.1 选择一个布局部件
根据希望如何对齐或约束可见小部件的方式,从各种布局小部件中进行选择,因为这些特征通常会传递给所包含的部件。这个例子使用Center来水平和垂直地居中它的内容。
1.2 创建一个可见部件
例如,创建一个Text部件:
Text('Hello World'),创建一个icon部件‘
Icon(
Icons.star,
color: Colors.red[500],
),1.3 将可见部件添加到布局部件
所有布局部件都有以下两种:
- 一个子属性,如果它们有一个子属性,例如Center、Container。
- 子属性接受一个部件列表,例如Row、Column、ListView、Stack。
将Text部件添加到Center部件:
const Center(
child: Text('Hello World'),
),1.4 将布局部件添加到页面
Flutter应用程序本身就是一个部件,大多数部件都有一个build()方法。在应用程序的build()方法中实例化并返回一个部件,并将显示该部件。
对于Material应用,可以使用Scaffold部件,它提供了一个默认的banner、背景色,并提供了用于添加drawers、snack bars和bottom sheets的API,然后可以将Center小部件直接添加到主页的body属性中。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
const String appTitle = 'Flutter layout demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const Center(
child: Text('Hello World'),
),
),
);
}
}如果想要构建苹果风格的界面,可以使用CupertinoApp和CupertinoPageScaffold,具体代码就不详细展示了。
如果以上两种风格我们都不需要,我们可以直接使用Container即可,但是它不包括AppBar、标题或背景颜色等待。如果需要使用这些功能,就必须自己构建它们。下面的代码实现的功能是:将背景颜色更改为白色,文本更改为深灰色。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: const Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black87,
),
),
),
);
}
}运行结果:
垂直和水平布局多个部件
最常见的布局模式之一是垂直或水平排列部件。可以使Row部件水平排列部件,使用Column部件垂直排列部件。以下是这两个布局的
- Row和Column各有一个子部件列表。
- 子部件本身可以是行、列或其他复杂部件。
- 可以指定行或列如何垂直和水平对齐其子元素。
- 可以拉伸或约束特定的部件。
- 可以指定子部件如何使用行或列的可用空间。
可以使用mainAxisAlignment和crossAxisAlignment属性控制行或列如何对齐其子行或列。
对于Row,主轴水平运行,交叉轴垂直运行。
对于Column,主轴垂直运行,十字轴水平运行。
在下面的例子中,这3张图片的宽度都是100px。渲染框(在本例中是整个屏幕)的宽度超过300px,因此将主轴对齐方式设置为spaceEvenly将每个图像前后的自由水平空间均匀划分。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);效果:
垂直方向代码如下:
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);效果:
2.1 组件大小
在Flutter中当布局太大而不适合设备时,受影响的边缘会出现黄黑条纹图案。下面是一个行太宽的例子:
通过使用Expanded部件,可以调整小部件的大小,使其适合一行或一列。要修复前面的示例,即图像行对于呈现框来说太宽,请使用Expanded部件包装每个图像。
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);效果:
如果我们希望一个部件占用的空间是同类部件的两倍。为此,请使用Expanded部件的flex属性,这是一个决定小部件伸缩系数的整数。默认的伸缩因子是1。下面的代码将中间图像的伸缩系数设置为2:
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
flex: 2,
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);效果:
2.2 组件填充方式
默认情况下,一行或列沿其主轴占用尽可能多的空间,但如果你想让子组件挤在一起,可通过设置mainAxisSize为MainAxisSize.min。
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
)效果:
2.3 嵌套行和列
下面一段代码:
final stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
);
final ratings = Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
const Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),
);部件树:
效果:
其他常用布局部件
Flutter有一个丰富的布局部件库。这里有一些最常用的部件,以达到快速实现各种效果的目的,而不用我们去重复的造轮子。当然Flutter还有更多其他的部件部件,后续章节会单独列出来进行介绍。
标准部件
- Container: 为部件添加填充、边距、边框、背景色或其他装饰。
- GridView: 将部件作为一个可滚动的网格展开。
- ListView: 将部件以可滚动列表的形式展开。
- Stack: 将一个部件重叠在另一个部件的顶部。
Material部件
- Card: 将相关信息组织到圆角和阴影框中。
- ListTile: 将最多3行文本,以及可选的前后图标组织成一行。
3.1 Container
许多布局可以自由地使用Container来使用填充来分隔部件,或者添加边框或边距。可以通过将整个布局放入Container中并更改其背景颜色或图像来更改设备的背景。以下是Container的概要:
- 添加padding,margin,border
- 更改背景颜色或图像
- 包含单个子部件,但该子部件可以是行、列,甚至是部件树的根
通过以下代码,进一步理解Container部件:
Widget _buildImageColumn() {
return Container(
decoration: const BoxDecoration(
color: Colors.black26,
),
child: Column(
children: [
_buildImageRow(1),
_buildImageRow(3),
],
),
);
}
Widget _buildDecoratedImage(int imageIndex) => Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 10, color: Colors.black38),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
margin: const EdgeInsets.all(4),
child: Image.asset('images/pic$imageIndex.jpg'),
),
);
Widget _buildImageRow(int imageIndex) => Row(
children: [
_buildDecoratedImage(imageIndex),
_buildDecoratedImage(imageIndex + 1),
],
);效果:
3.2 GridView
使用GridView将部件布置为二维列表。GridView提供了两个预制列表,或者您可以构建自己的自定义网格。当GridView检测到它的内容太长而不适合渲染框时,它会自动滚动。以下是GridView的概要:
- 在网格中布局部件
- 检测列内容何时超过呈现框并自动提供滚动
- 构建自定义网格,或使用提供的网格之一:
GridView.count 允许指定列的数量
GridView.extent 允许指定贴图的最大像素宽度
Widget _buildGrid() => GridView.extent(
maxCrossAxisExtent: 150,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: _buildGridTileList(30));
List<Container> _buildGridTileList(int count) => List.generate(
count, (i) => Container(child: Image.asset('images/pic$i.jpg')));使用GridView.extent创建一个最大150像素宽的网格,效果如下:
3.3 ListView
ListView是一个类似列的部件,当它的内容对于呈现框来说太长时,它会自动提供滚动。
- 一个特殊的Column,并以特定的规则组织内部的部件
- 可以水平布局,也可以垂直布局
- 检测其内容,并决定何时提供滚动
Widget _buildList() {
return ListView(
children: [
_tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
_tile('The Castro Theater', '429 Castro St', Icons.theaters),
_tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
_tile('Roxie Theater', '3117 16th St', Icons.theaters),
_tile('United Artists Stonestown Twin', '501 Buckingham Way',
Icons.theaters),
_tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
const Divider(),
_tile('K\'s Kitchen', '757 Monterey Blvd', Icons.restaurant),
_tile('Emmy\'s Restaurant', '1923 Ocean Ave', Icons.restaurant),
_tile('Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
_tile('La Ciccia', '291 30th St', Icons.restaurant),
],
);
}
ListTile _tile(String title, String subtitle, IconData icon) {
return ListTile(
title: Text(title,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
)),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Colors.blue[500],
),
);
}运行效果:
3.4 Stack
使用Stack将部件排列在基本部件之上,部件可以完全或部分地与基本部件重叠。概要如下:
- 用于将一个部件叠加到另一个部件
- 子部件列表中的第一个部件是基本部件,随后的子部件被覆盖在该基本部件的顶部
- Stack的内容不能滚动
Widget _buildStack() {
return Stack(
alignment: const Alignment(0.6, 0.6),
children: [
const CircleAvatar(
backgroundImage: AssetImage('images/pic.jpg'),
radius: 100,
),
Container(
decoration: const BoxDecoration(
color: Colors.black45,
),
child: const Text(
'Mia B',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
}运行效果:
3.5 Card
Material库中的Card包含相关的信息,几乎可以由任何小部件组成,但通常与ListTile一起使用。Card有一个子节点,但它的子节点可以是列、行、列表、网格或其他支持多个子节点的小部件。默认情况下,Card将其大小缩小为0 × 0像素。您可以使用SizedBox来限制卡片的大小。
在Flutter中,Card具有圆角和阴影,使其具有3D效果。改变Card的elevation属性可以让你控制器阴影效果。
Widget _buildCard() {
return SizedBox(
height: 210,
child: Card(
child: Column(
children: [
ListTile(
title: const Text(
'1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500),
),
subtitle: const Text('My City, CA 99984'),
leading: Icon(
Icons.restaurant_menu,
color: Colors.blue[500],
),
),
const Divider(),
ListTile(
title: const Text(
'(408) 555-1212',
style: TextStyle(fontWeight: FontWeight.w500),
),
leading: Icon(
Icons.contact_phone,
color: Colors.blue[500],
),
),
ListTile(
title: const Text('costa@example.com'),
leading: Icon(
Icons.contact_mail,
color: Colors.blue[500],
),
),
],
),
),
);
}效果:
3.6 ListTile
ListTile 是Material库中的一个专门的行部件,可以轻松地创建包含最多3行文本和可选的前后图标的行。ListTile最常用于Card或ListView,但也可以用于其他地方。具体代码和效果,可参考上面的Card代码。
布局约束
在Flutter中,"Constraints"(约束)通常指的是一个描述 Widget 可以使用的空间大小的对象。布局系统使用约束来决定 Widget 的大小以及其在屏幕上的位置。
BoxConstraints(盒子约束): BoxConstraints 是描述 Widget 可以使用的空间范围的对象。它包含了最小宽度、最大宽度、最小高度、最大高度等属性。这些属性用于定义 Widget 可以占用的空间的范围。
ConstrainedBox: ConstrainedBox 是一个 Widget,它允许您为其子 Widget 指定额外的约束条件,强制子 Widget 在给定的约束条件下进行布局。
IntrinsicHeight 和 IntrinsicWidth: 这两个 Widget 允许其子 Widget 自适应其固有的高度或宽度。它们在包含的 Widget 可以根据其内容自动调整大小时非常有用。
MediaQuery: MediaQuery 允许您查询当前应用程序的屏幕大小和其他屏幕相关的信息,从而确定约束条件。
LayoutBuilder: LayoutBuilder 是一个用于构建依赖于父 Widget 约束的 Widget 的常见工具。它接受一个构建函数,该函数会接收约束参数并返回一个 Widget。
这一小节主要列举了一些布局约束组件和基本概念,布局约束涉及的内容比较多,后续会专门使用一个章节进行详细介绍。
小结
这一节,主要介绍了Flutter的布局部件,也是我们构建Flutter UI的基础。用户界面通过Widget树的层次结构构建,常用的布局Widget包括Container、Row、Column、Stack、Expanded等,它们可以用于灵活排列和组织子Widget。Padding和Margin通过EdgeInsets类用于定义内边距和外边距,调整Widget的间距。ListView和GridView分别用于垂直和二维网格排列子Widget。Flutter提供丰富的布局工具和技术,适用于各种应用场景,通过递归构建Widget树来实现整个界面的绘制。
- 上一篇:Flutter 如何实现性能优化
- 下一篇:Flutter 之 ListView
相关推荐
- 搭建一个20人的办公网络(适用于20多人的小型办公网络环境)
-
楼主有5台机上网,则需要一个8口路由器,组网方法如下:设备:1、8口路由器一台,其中8口为LAN(局域网)端口,一个WAN(广域网)端口,价格100--400元2、网线N米,这个你自己会看了:)...
- 笔记本电脑各种参数介绍(笔记本电脑各项参数新手普及知识)
-
1、CPU:这个主要取决于频率和二级缓存,频率越高、二级缓存越大,速度越快,现在的CPU有三级缓存、四级缓存等,都影响相应速度。2、内存:内存的存取速度取决于接口、颗粒数量多少与储存大小,一般来说,内...
- 汉字上面带拼音输入法下载(字上面带拼音的输入法是哪个)
-
使用手机上的拼音输入法打成汉字的方法如下:1.打开手机上的拼音输入法,在输入框中输入汉字的拼音,例如“nihao”。2.根据输入法提示的候选词,选择正确的汉字。例如,如果输入“nihao”,输...
- xpsp3安装版系统下载(windowsxpsp3安装教程)
-
xpsp3纯净版在采用微软封装部署技术的基础上,结合作者的实际工作经验,融合了许多实用的功能。它通过一键分区、一键装系统、自动装驱动、一键设定分辨率,一键填IP,一键Ghost备份(恢复)等一系列...
- 没有备份的手机数据怎么恢复
-
手机没有备份恢复数据方法如下1、使用数据线将手机与电脑连接好,在“我的电脑”中可以看到手机的盘符。 2、将手机开启USB调试模式。在手机设置中找到开发者选项,然后点击“开启USB调试模式”。 3、...
- 电脑怎么激活windows11专业版
-
win11专业版激活方法有多种,以下提供两种常用的激活方式:方法一:使用激活密钥激活。在win11桌面上右键点击“此电脑”,选择“属性”选项。进入属性页面后,点击“更改产品密钥或升级windows”。...
- 华为手机助手下载官网(华为手机助手app下载专区)
-
华为手机助手策略调整,已不支持从应用市场下载手机助手,目前华为手机助手是需要在电脑上下载或更新手机助手到最新版本,https://consumer.huawei.com/cn/support/his...
- 光纤线断了怎么接(宽带光纤线断了怎么接)
-
宽带光纤线断了可以重接,具体操作方法如下:1、光纤连接的时候要根据束管内,同色相连,同芯相连,按顺序进行连接,由大到小。一般有三种连接方法,分别是熔接、活动连接和机械连接。2、连接的时候要开剥光缆,抛...
- win7旗舰版和专业版区别(win7旗舰版跟专业版)
-
1、功能区别:Win7旗舰版比专业版多了三个功能,分别是Bitlocker、BitlockerToGo和多语言界面; 2、用途区别:旗舰版的功能是所有版本中最全最强大的,占用的系统资源,...
- 万能连接钥匙(万能wifi连接钥匙下载)
-
1、首先打开wifi万能钥匙软件,若手机没有开启WLAN,就根据软件提示打开WLAN开关;2、打开WLAN开关后,会显示附近的WiFi,如果知道密码,可点击相应WiFi后点击‘输入密码’连接;3、若不...
- 雨林木风音乐叫什么(雨林木风是啥)
-
雨林木风的创始人是陈年鑫先生。陈年鑫先生于1999年创立了雨林木风公司,其初衷是为满足中国市场对高品质、高性能电脑的需求。在陈年鑫先生的领导下,雨林木风以技术创新、产品质量和客户服务为核心价值,不断推...
- aics6序列号永久序列号(aics6破解序列号)
-
关于AICS6这个版本,虽然是比较久远的版本,但是在功能上也是十分全面和强大的,作为一名平面设计师的话,AICS6的现有的功能已经能够应付几乎所有的设计工作了……到底AICC2019的功能是不是...
- 手机可以装电脑系统吗(手机可以装电脑系统吗怎么装)
-
答题公式1:手机可以通过数据线或无线连接的方式给电脑装系统。手机安装系统需要一定的技巧和软件支持,一般需要通过数据线或无线连接的方式与电脑连接,并下载相应的软件和系统文件进行安装。对于大部分手机用户来...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
