Flutter——布局
xsobi 2024-12-11 17:49 1 浏览
上一节介绍了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树来实现整个界面的绘制。
相关推荐
- 好用的云函数!后端低代码接口开发,零基础编写API接口
-
前言在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作。不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人,通过...
- 快速上手:Windows 平台上 cURL 命令的使用方法
-
在工作流程中,为了快速验证API接口有效性,团队成员经常转向直接执行cURL命令的方法。这种做法不仅节省时间,而且促进了团队效率的提升。对于使用Windows系统的用户来说,这里有一套详细...
- 使用 Golang net/http 包:基础入门与实战
-
简介Go的net/http包是构建HTTP服务的核心库,功能强大且易于使用。它提供了基本的HTTP客户端和服务端支持,可以快速构建RESTAPI、Web应用等服务。本文将介绍ne...
- #小白接口# 使用云函数,人人都能编写和发布自己的API接口
-
你只需编写简单的云函数,就可以实现自己的业务逻辑,发布后就可以生成自己的接口给客户端调用。果创云支持对云函数进行在线接口编程,进入开放平台我的接口-在线接口编程,设计一个新接口,设计和配置好接口参...
- 极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:iN在之前和大家说过,在iN的家里是没有墙面开关的。...
- window使用curl命令的注意事项 curl命令用法
-
cmd-使用curl命令的注意点前言最近在cmd中使用curl命令来测试restapi,发现有不少问题,这里记录一下。在cmd中使用curl命令的注意事项json不能由单引号包括起来json...
- Linux 系统curl命令使用详解 linuxctrl
-
curl是一个强大的命令行工具,用于在Linux系统中进行数据传输。它支持多种协议,包括HTTP、HTTPS、FTP等,用于下载或上传数据,执行Web请求等。curl命令的常见用法和解...
- Tornado 入门:初学者指南 tornados
-
Tornado是一个功能强大的PythonWeb框架和异步网络库。它最初是为了处理实时Web服务中的数千个同时连接而开发的。它独特的Web服务器和框架功能组合使其成为开发高性能Web...
- PHP Curl的简单使用 php curl formdata
-
本文写给刚入PHP坑不久的新手们,作为工具文档,方便用时查阅。CURL是一个非常强大的开源库,它支持很多种协议,例如,HTTP、HTTPS、FTP、TELENT等。日常开发中,我们经常会需要用到cur...
- Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介
-
本章涵盖使用Actix提供静态网页...
- 我给 Apache 顶级项目提了个 Bug apache顶级项目有哪些
-
这篇文章记录了给Apache顶级项目-分库分表中间件ShardingSphere提交Bug的历程。说实话,这是一次比较曲折的Bug跟踪之旅。10月28日,我们在GitHub上提...
- linux文件下载、服务器交互(curl)
-
基础环境curl命令描述...
- curl简单使用 curl sh
-
1.curl--help#查看关键字2.curl-A“(添加user-agent<name>SendUser-Agent<name>toserver)”...
- 常用linux命令:curl 常用linux命令大全
-
//获取网页内容//不加任何选项使用curl时,默认会发送GET请求来获取内容到标准输出$curlhttp://www.baidu.com//输出<!DOCTYPEh...
- 三十七,Web渗透提高班之hack the box在线靶场注册及入门知识
-
一.注册hacktheboxHackTheBox是一个在线平台,允许测试您的渗透技能和代码,并与其他类似兴趣的成员交流想法和方法。它包含一些不断更新的挑战,并且模拟真实场景,其风格更倾向于CT...
- 一周热门
- 最近发表
-
- 好用的云函数!后端低代码接口开发,零基础编写API接口
- 快速上手:Windows 平台上 cURL 命令的使用方法
- 使用 Golang net/http 包:基础入门与实战
- #小白接口# 使用云函数,人人都能编写和发布自己的API接口
- 极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关
- window使用curl命令的注意事项 curl命令用法
- Linux 系统curl命令使用详解 linuxctrl
- Tornado 入门:初学者指南 tornados
- PHP Curl的简单使用 php curl formdata
- Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- strcat (25)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- element style (30)
- dedecms模版 (53)
- vs打不开 (29)
- nmap (30)
- webgl开发 (24)
- parse (24)
- c 视频教程下载 (33)
- paddleocr (28)
- listview排序 (33)
- firebug 使用 (31)
- transactionmanager (30)
- characterencodingfilter (33)
- getmonth (34)
- commandtimeout (30)
- hibernate教程 (31)
- label换行 (33)
- curlpost (31)