Flutter中的布局
重点是什么?
· 小部件是用于构建UI的类
· 小部件用于布局和UI元素。
· 组合简单的小部件来构建复杂的小部件。
Flutter布局机制的核心是widgets。在Flutter中,几乎一切都是小部件——甚至布局模型也是小部件。您在Flutter应用程序中看到的图像、图标和文本都是小部件。但是你看不到的东西也是小部件,例如排列、约束和对齐可见小部件的行、列和网格。
您通过组合小部件来创建创建布局以构建更复杂的小部件。例如,下面的第一个屏幕截图显示了3个图标,每个图标下面都有一个标签:
第二个屏幕截图显示了视觉布局 ,显示一行3列,其中每列包含一个图标和标签。
这是此UI的小部件树图:
其中大部分看起来应该与您的预期一样,但您可能想追到容器(以粉红色显示)。Container
是一个小部件类,允许您自定义其子小部件。当您想要添加填充、边距、边框或背景颜色时,请使用Container
来命名它的一些功能。
在此示例中,每个Text
小部件都放置在Container
中以添加 边距。整个行也放置在Container
中以在行中为添加padding。
此示例中的其余UI由属性控制。使用其color
属性设置图标 的颜色。使用Text.style
属性设置字体、颜色、粗细等。列和行的属性允许您指定它们的子元素如何垂直或水平对齐,以及 子元素应占据多少空间。
一、布置一个小部件
如何在Flutter布局单个小部件?本节向您展示如何创建和显示一个简单的小部件。它还显示了一个简单的Hello World应用程序的完整代码。
在Flutter中,只需几个步骤 即可将文本、图标或图像显示在屏幕上。
1、选择一个布局小部件
根据您希望如何对齐或约束可见小部件,从各种layout. widgets
中进行选择,因为这些特征通常会传递给包含的小部件。
此示例使用Center
,其中水平和垂直居中。
2、创建一个可见的小部件
例如,创建一个Text
小部件:
Text('Hello World'),
创建一个Image
小部件:
Image.asset(
'images/lake.jpg',
fit: BoxFit.cover,
),
创建一个Icon
小部件:
Icon(
Icons.star,
color: Colors.red[500],
),
3、在布局widget中添加visible widget
所有布局小部件都具有以下之一:
- 如果他们带一个孩子,使用child属性——例如,
Center
或者Container
- 如果他们采用小部件列表,使用children属性,例如
Row
、Column
、ListView
或Stack
.
Text
将小部件添加到Center
部件:
const Center(
child: Text('Hello World'),
),
4、在页面中添加布局小部件
Flutter应用程序本身就是一个小部件,大多数小部件都有一个build()
方法。在应用程序的方法中实例化并返回一个小部件build()
会显示该小部件。
Material apps
对于Material
应用程序,您可以使用Scaffold
小部件;它提供默认banner、background color,并且有用于添加drawer、snack bars,和bottom sheet的API。然后您可以将Center
小部件直接添加到body
属性中。
class MyApp extends StatelessWidget {
const MyApp(Key? key) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter layout demo'),
),
body: const Center(
child: Text('Hello World'),
),
),
);
}
}
Non-Material apps
对于非Material应用程序,您可以将Center
小部件添加到应用程序的build()
方法中:
class MyApp extends StatelessWidget {
const MyApp(Key? key) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black87,
),
),
);
}
}
默认情况下,非Material应用程序不包含AppBar
、标题或背景颜色。如果您想在非Material应用程序中使用这些功能,则必须自己构建它们。此应用程序将背景颜色更改为白色,将文本更改为深灰色以模仿Material应用程序。
就是这样!运行应用程序时,您应该会看到Hello World
二、垂直和水平布局多个小部件
最常见的布局模式之一是垂直或水平排列小部件。您可以使用Row
小部件水平排列小部件,使用Column
小部件垂直排列小部件。
重点是什么?
· `Row`和`Column`是两种最常用的布局模式。
· `Row`和`Column`每个都有一个子布局列表。
· 子小部件本身可以是`Row`、`Column`或其他复杂小部件。
· 您可以指定行或列如何垂直和水平对齐其子项。
· 您可以拉伸或约束特定的子部件。
· 您可以指定子部件如何使用行或列的可用空间。
要在Flutter中创建行或列,您可以将子部件列表添加到Row
或Column
部件。反过来,每个孩子本身可以是一行或 一列,等等,以下示例显示了如何在行或列内嵌套行或列。
此布局组织为Row
。该行包含两个子项 :左侧的一列和右侧的图像:
左列的小部件树嵌套行和列。
2.1、对齐小部件
mainAxisAlignment
您可以使用和crossAxisAlignment
属性控制行或列如何对齐子项。对于一行,主轴水平运行,横轴垂直运行。对于列,主轴垂直运行,横轴水平运行。
和 枚举提供了多种用于控制对齐的常量MainAxisAlignment
。CrossAxisAlignment
在下面的示例中,3个图像中的每一个都是100像素宽。渲染框(在本例中为整个屏幕)超过300像素宽,因此将主轴对齐设置为spaceEvenly
在每个图像之间、之前和之后平均划分自由水平空间。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
)
列的工作方式与行相同。以下示例显示一列3个图像,每个图像高100像素。渲染框(在本例中为整个屏幕)的高度超过3000像素,因此将主轴对齐设置为spaceEvenly
,在每个图像、上方和下方平均划分 自由垂直空间。
Column(
mainAxisAlignment:MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
]
);
2.2、调整大小的小部件
当布局太大而不合适设备时,受影响的边缘会出现黄色和黑色条纹图案。这是一个太宽的行的示例:
可以使用小部件调整小部件的大小以适应行或列
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 widget flex
属性,它是一个整数,用于确定widget的弹性因子。默认弹性因子为1,以下代码将中间图像的弹性因子设置为2:
2.3、包装小部件
默认情况下,行或列沿其主轴占据尽可能多的空间,但如果您想将子项紧密地打包在一起,请将其设置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.4、嵌套行和列
布局框架允许您根据需要在行和列中嵌套行和列。让我们 看一下以下布局的概述部分的代码:
概述部分实现为两行。评分行包含五颗星和评论数。图表行包含三列图表和文本。 评级行的小部件树:
该ratings
变量创建一行,其中包含较小的一行5个星形图表和文本:
var stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[50]),
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,
),
),
],
),
);
提示: 为了最大限度地减少嵌套布局代码可能导致的视觉混乱,请在变量和函数中实现 UI 的各个部分。
评级行下方的图标行包含3列;每列包含一个图标和两行文本,如您在其小部件树中所见:
该iconList
变量定义图标行:
const descTextStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18,
height: 2,
);
final iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: Container(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.kitchen, color: Colors.green[500]).
const Text('PREP:'),
const Text('25 min'),
],
),
Column(
children: [
Icon(Icons.timer, color: Colors.green[500]),
const Text('COOK:'),
const Text('1 hr'),
],
),
Column(
children: [
Icon(Icons.restuarant, color: Colors.green[500]),
const Text('FEEDS:'),
const Text('4-6'),
],
),
],
),
),
);
该leftColumn
变量包含评级和图标行,以及描述Pavlova的标题和文本:
final leftColumn = Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),
child: Column(
children:[
titleText,
subTitle,
ratings,
iconList,
],
),
);
column左侧放一个限制其宽度的SizedBox
小部件。最后,用一个Card来显示UI。
Pavlova图片来自Pixabay 。您可以使用嵌入来自网络的图像,Image.network()
但对于本示例,图像将保存到项目中的图像目录,添加到pubspec 文件,并使用 访问Images.asset()
body: Center(
child: Container(
margin: const EdgeInsets.fromLTRB(0, 40, 0, 30),
height: 600,
child: Row(
crossAxisAlignment: CrossAxisAlignmeng.start,
children: [
SizedBox(
width: 440,
children: leftColumn,
),
mainImage,
],
),
),
),
转载自:https://juejin.cn/post/7191057584210575416