Flutter 中的Widgets
Widgets
是用于构建UI
的类。Widgets
可以用于布局和展示UI
元素。- 通过组合简单的
widgets
来构建复杂的widgets
。
1、创建一个简单的Widget
创建一个Text
Widget,
Text('Hello world')
创建一个IconButton
Widget,
IconButton(icon: Icon(Icons.search), onPressed: null)
2、将可见 widget 添加到布局 widget
两个widget
相互组合成一个复合widget
:例如:
将 Text widget
添加进 Center widget
:
Center(
child: Text('Hello World'),
),
关于
child
和children
child
属性,只包含一个子项 —— 一般用于Center
和Container
children
属性,如果它们包含多个子项 —— 一般用于Row
、Column
、ListView
和Stack
3、将布局 widget 添加到页面
一个 Flutter
app 本身就是一个 widget
,大多数 widgets
都有一个build()
方法,在 app
的 build()
方法中实例化和返回一个 widget
会让它显示出来
Widget
布局一般有两种情况Material
和 非 Material
- 使用
Material
布局app
Material
库 实现了一些遵循Material Design
原则的widgets
。在设计UI
时,你可以只使用标准widgets
库 中的widgets
,也可以使用Material library
中的widgets
。你可以混合来自两个库的widgets
,可以自定义现有widgets
,也可以构建自己的一组自定义widgets
- 可以使用
Scaffold
widget
,它提供默认的banner、背景颜色,还有用于添加抽屉、提示条和底部列表弹窗的 API
。你可以将Center widget
直接添加到主页body
的属性中
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
- 非
Material
布局
非Material
布局 app,你可以将Center widget
添加到app
的build()
方法里,默认不包含AppBar、标题和背景颜色
。如果你希望在非 Material app
中使用这些功能,则必须自己构建它们
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.white),
child: Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black87,
),
),
),
);
}
}
4、横向或纵向布局多个 widgets
在Flutter里面我们常用Row 和 Column来进行 水平和 垂直排列。
- Row 水平排列
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('assets/pic1.jpg'),
Image.asset('assets/pic2.jpg'),
],
),
- Column 垂直排列
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('assets/pic1.jpg'),
Image.asset('assets/pic2.jpg'),
],
),
Widgets
对齐
在水平
和垂直
的布局中,我么可以使用mainAxisAlignment
和crossAxisAlignment
属性控制行
或列
如何对齐其子项。
对于行
来说,主轴水平延伸,交叉轴垂直延伸。
对于列
来说,主轴垂直延伸,交叉轴水平延伸。
MainAxisAlignment
和 CrossAxisAlignment
类为控制对齐提供了各种常量
。
enum MainAxisAlignment {
// start end
// 将子级放置在尽可能靠近主轴起点的位置
// Row:用于确定起点是左侧还是右侧
// Column: 用于确定起点是顶部还是底部
start,
end,
// 将子对象尽可能靠近主轴中间
center,
//将空白区域放在里两个子视图之间
spaceBetween,
//在子视图之间以及第一个和最后一个子视图之前和之后的一半空间之间均匀地放置可用空间。
spaceAround,
///将空闲空间均匀地放在子视图之间以及第一个和最后一个子视图之前和之后
spaceEvenly,
}
enum CrossAxisAlignment {
// start end
//将子项的起始边缘与交叉轴的起始侧对齐
// Row:用于确定起点是左侧还是右侧
// Column: 用于确定起点是顶部还是底部
start,
end,
// 放置子项,使其中心与十字轴的中心对齐
// 默认的
center,
// 将子对象填充交叉轴
// 这会导致传递给子级的约束在交叉轴上很紧。
stretch,
// 将子项沿着横轴放置,使其基线匹配
baseline,
}
使用列子:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),,
],
);
Widgets
调整大小
我么可以通过使用Expanded
widget ,可以调整widgets
的大小以适合行或列,Expanded
里面有个flex
属性,这是一个用来确定widget
的弹性系数
的整数。默认的弹性系数为1
,我们可以更改这个属性来调整每个widgets
的大小
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'),
),
],
);
上面的例子,第二个视图的
flex
为2 ,显示出来的图就是其他两个的两倍的大小
- 组合
widgets
默认情况下,行
或列
沿其主轴会占用尽可能多的空间,但如果要将子项紧密组合在一起,可以使用mainAxisSize
设置
num MainAxisSize {
// 根据传入的布局约束,最小化主轴上的可用空间。
min,
// 根据传入的布局约束,最大化主轴上的可用空间。
max,
}
例如将五个星星紧挨在一起,我们就可以使用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]),
Icon(Icons.star, color: Colors.black),
Icon(Icons.star, color: Colors.black),
],
)
注
:布局框架允许你根据需要在行和列内嵌套
行和列
通用布局 widgets
Flutter
中的widget
会分为两类:
widgets 库
中的标准 widgets
: 任何app
都可以使用widget 库
Material 库
中的widgets
:Material
库中的组件只能
在Material app
中使用。
widgets
标准库
Container
:向widget
增加padding
、margins
、borders
、background color
或者其他的装饰
。GridView
:将widget
展示为一个可滚动
的网格
。ListView
:将widget
展示为一个可滚动
的列表
。Stack
:将widget
覆盖在另一个的上面。
Material
库
Card
:将相关信息整理到一个有圆角
和阴影
的盒子中。ListTile
:将最多三行的文本、可选的导语以及后面的图标组织在一行中
Container
许多布局都可以随意的用 Container
,它可以将使用了padding
或者增加了 borders/margins
的 widget
分开。
也可以通过将整个布局放到一个 Container
中,并且改变它的背景色或者图片,来改变设备的背景。
- 增加
padding
、margins
、borders
- 改变背景色或者图片
- 只包含一个子
widget
,但是这个子widget
可以是行、列或者是widget
树的根 widget
更改背景色示例:
Widget _buildImageColumn() => Container(
decoration: 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(const Radius.circular(8)),
),
margin: const EdgeInsets.all(4),
child: Image.asset('images/pic$imageIndex.jpg'),
),
);
GridView (类似于iOS 中的UICollectionView)
- 使用
GridView
将widget
作为二维列表
展示。类似于iOS
中常用的UICollectionView
GridView
提供两个预制的列表,或者你可以自定义网格。- 当
GridView
检测到内容太长而无法适应渲染盒时,它就会自动支持滚动
。
自定义网格的时候,可以使用
GridView.count
:允许你制定列的数量GridView.extent
:允许你制定单元格的最大宽度
我们看个GridView.extent
的使用列子:
// GridView
Widget _buildGridView() => GridView.extent(
maxCrossAxisExtent: 100,// item最大的宽度
padding: const EdgeInsets.all(10),// View 距左右屏幕的间隔
mainAxisSpacing: 10, // item 之间的上下间隔
crossAxisSpacing: 10,// item 之间的左右间隔
children: _buildGridTileList(30),
);
// 创建图片列表
// 当对象具有可预测的命名模式时,使用List.generate()构造函数可以轻松创建列表。
List<Container> _buildGridTileList(int count) => List.generate(
count, (i) => Container(child: Image.asset('assets/pic$i.jpg')));
ListView
ListView
,一个和列
很相似的widget
,当内容长于自己的渲染盒时,就会自动支持滚动.- 类似于
iOS
中的UITableView
- 一个用来组织盒子中列表的专用
Column
(特殊的Column
) - 可以水平或者垂直布局
- 比
Column
的配置少,使用更容易,并且支持滚动
使用列子:
// 创建列表
Widget _buildList() => ListView(
children: [
_title('List_View_1', 'Hello', Icons.theaters),
_title('List_View_2', 'Hello', Icons.theaters),
_title('List_View_3', 'Hello', Icons.theaters),
_title('List_View_4', 'Hello', Icons.theaters),
_title('List_View_5', 'Hello', Icons.theaters),
_title('List_View_6', 'Hello', Icons.theaters),
_title('List_View_7', 'Hello', Icons.theaters),
_title('List_View_8', 'Hello', Icons.theaters),
_title('List_View_9', 'Hello', Icons.theaters),
_title('List_View_10', 'Hello', Icons.theaters),
_title('List_View_11', 'Hello', Icons.theaters),
_title('List_View_12', 'Hello', Icons.theaters),
_title('List_View_13', 'Hello', Icons.theaters),
],
);
// 创建单个的Cell 布局
ListTile _title(String title, String subtitle, IconData icon) => ListTile (
title: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
)),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Colors.blue,
),
);
Stack
可以使用 Stack
Stack
主要用于一个widget
去覆盖另一个widget
- 可以完全或者部分覆盖基础
widget
- 通常 在
图片 widget
上排列widget
使用例子:
// Stack Widget
Widget _BuildStackView() => Stack(
// 设置文字的位置
alignment: const Alignment(0.6,0.6),
children: [
// 圆形图片
CircleAvatar(
backgroundImage: AssetImage('assets/pic2.jpg'),
radius: 100,
),
Container(
child: Text(
'Jadekirin',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.red,
),
),
),
],
);
ListTile
ListTile
是Material 库
中专用的行widget
,- 包含
最多三行文本
以及可选的行前
和行尾
图标的行。 ListTile
在Card
或者ListView
中最常用,但是也可以在别处使用。- 比
Row
更少的配置,更容易使用
我看下在上年ListView
中使用到的ListTile
的例子
// 创建单个的Cell 布局
ListTile _title(String title, String subtitle, IconData icon) => ListTile (
title: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
)),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Colors.blue,
),
);
Card
Card
来自Material 库
中的,几乎可以由任何widget
组成,但是通常和 ListTile 一起使用。Card
只有一个子项,这个子项可以是列
、行
、列表
、网格
或者其他支持多个子项的widget
。- 默认情况下,
Card
的大小是0x0 像素
。你可以使用SizedBox
控制card
的大小。 Card
有轻微的圆角
和阴影
来使它具有3D 效果
。改变Card
的elevation
属性可以控制阴影效果Card
的内容无法滚动
// Card 一般结合 SizeBox使用
// SizeBox 控制Card 的大小
Widget _buildCard() => SizedBox(
height: 240,
width: 300,
child: Card(
child: Column(
children: [
// _title('Name', 'Good Bye', Icons.account_circle_rounded),
// _title('90388283', null, Icons.phone),
// _title('costa@example.com', null, Icons.email),
ListTile(
title: Text('1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('My City, CA 99984'),
leading: Icon(
Icons.account_circle_rounded,
color: Colors.blue[500],
),
),
Divider(),
ListTile(
title: Text('(408) 555-1212',
style: TextStyle(fontWeight: FontWeight.w500)),
leading: Icon(
Icons.contact_phone,
color: Colors.blue[500],
),
),
ListTile(
title: Text('costa@example.com'),
leading: Icon(
Icons.contact_mail,
color: Colors.blue[500],
),
),
],
),
),
);