Flutter 组件
在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget 前期我们都继承 StatelessWidget。
- StatelessWidget 是无状态组件,状态不可变的 widget
- StatefulWidget 是有状态组件,持有的状态可能在 widget 生命周期改变
MaterialApp
MaterialApp 是一个方便的 Widget,它封装了应用程序实现 Material Design 所需要的一些 Widget。一般作为顶层 widget 使用。
常用属性
属性 | 含义 |
---|---|
home | 主页 |
title | 标题 |
theme | 主题 |
Routes | 路由 |
去掉 debug 图标
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
);
}
Scaffold 布局组件
Scaffold 是 Material Design 布局结构的基本实现。此类提供了用于显示 drawer、snackbar 和底部 sheet 的 API
常用属性
属性 | 含义 |
---|---|
appBar | 显示在界面顶部的一个 AppBar |
body | 当前界面所显示的主要内容 Widget |
drawer | 抽屉菜单控件 |
BottomNavigationBar | 底部导航条 |
FloatingActionButton | FloatingActionButton 简称 FAB ,可以实现浮动按钮,也可以实现类似闲鱼 app 的底部凸起导航 |
BottomNavigationBar
BottomNavigationBar 是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar 是 Scaffold 组件的参数。
属性名 | 说明 |
---|---|
items | List 底部导航条按钮集合 |
iconSize | icon |
currentIndex | 默认选中第几个 |
onTap | 选中变化回调函数 |
fixedColor | 选中的颜色 |
type | BottomNavigationBarType.fixed BottomNavigationBarType.shifting |
底部菜单选中
FloatingActionButton
FloatingActionButton 简称 FAB ,可以实现浮动按钮,也可以实现类似闲鱼 app 的底部凸起导航
属性名称 | 属性值 |
---|---|
child | 子视图,一般为 Icon,不推荐使用文字 |
tooltip | FAB 被长按时显示,也是无障碍功能 |
backgroundColor | 背景颜色 |
elevation | 未点击的时候的阴影 |
hignlightElevation | 点击时阴影值,默认 12.0 |
onPressed | 点击事件回调 |
shape | 可以定义 FAB 的形状等 |
mini | 是否是 mini 类型默认 false |
Drawer 抽屉菜单
在 Scaffold 组件里面传入 drawer 参数可以定义左侧边栏,传入 endDrawer 可以定义右侧边栏。侧边栏默认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧边栏。
DrawerHeader
常见属性
属性 | 描述 |
---|---|
decoration | 设置顶部背景颜色 |
child | 配置子元素 |
padding | 内边距 |
margin | 外边距 |
UserAccountsDrawerHeader
属性 | 描述 |
---|---|
decoration | 设置顶部背景颜色 |
accountName | 账户名称 |
accountEmail | 账户邮箱 |
currentAccountPicture | 用户头像 |
otherAccountsPictures | 用来设置当前账户其他账户头像 |
margin |
AppBar
AppBar 自定义顶部按钮图标、颜色
属性 | 描述 |
---|---|
leading | 在标题前面显示的一个控件,在首页通常显示应用的 logo;在其他界面通常显示为返回按钮 |
title | 标题,通常显示为当前界面的标题文字,可以放组件 |
actions | 通常使用 IconButton 来表示,可以放按钮组 |
bottom | 通常放 tabBar,标题下面显示一个 Tab 导航栏 |
backgroundColor | 导航背景颜色 |
iconTheme | 图标样式 |
centerTitle | 标题是否居中显示 |
TabBar
属性 | 描述 |
---|---|
tabs | 显示的标签内容,一般使用 Tab 对象,也可以是其他的 Widget |
controller | TabController 对象 |
isScrollable | 是否可滚动 |
indicatorColor | 指示器颜色 |
indicatorWeight | 指示器高度 |
indicatorPadding | 底部指示器的 Padding |
indicator | 指示器 decoration,例如边框等 |
indicatorSize | 指示器大小计算方式,TabBarIndicatorSize.label 跟文字等宽,TabBarIndicatorSize.tab 跟每个 tab 等宽 |
labelColor | 选中 label 颜色 |
labelStyle | 选中 label 的 Style |
labelPadding | 每个 label 的 padding 值 |
unselectedLabelColor | 未选中 label 颜色 |
unselectedLabelStyle | 未选中 label 的 Style |
AppBar 结合 TabBar 实现顶部 Tab 切换
Container 容器组件
名称 | 功能 |
---|---|
alignment | topCenter:顶部居中对齐 topLeft:顶部左对齐 topRight:顶部右对齐 center:水平垂直居中对齐 centerLeft:垂直居中水平居左对齐 centerRight:垂直居中水平居右对齐 bottomCenter 底部居中对齐 bottomLeft:底部居左对齐 bottomRight:底部居右对齐 |
decoration | decoration: BoxDecoration( color: Colors.blue, border: Border.all( color: Colors.red, width: 2.0), borderRadius:BorderRadius.circular((8)),// 圆角 , boxShadow: [ BoxShadow( color: Colors.blue, offset: Offset(2.0, 2.0), blurRadius: 10.0, ) ], ) //LinearGradient 背景线性渐变 RadialGradient 径向渐变 gradient: LinearGradient( colors: [Colors.red, Colors.orange], ), |
margin | margin 属性是表示 Container 与外部其他组件的距离。 EdgeInsets.all(20.0), |
padding | padding 就是 Container 的内边距,指 Container 边缘与 Child 之间的距离 padding:EdgeInsets.all(10.0) |
transform | 让 Container 容易进行一些旋转之类的 transform: Matrix4.rotationZ(0.2) |
height | 容器高度 |
width | 容器宽度 |
child | 容器子元素 |
import 'dart:ffi';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: Center(
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
transform: Matrix4.rotationZ(0.1),
width: 300,box
height: 300,
decoration: BoxDecoration(
color: Colors.red[100],
border: Border.all(color: Colors.black45, width: 10),
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(color: Colors.blue, offset: Offset(2, 2)),
],
gradient: const RadialGradient(
colors: [Colors.pink, Colors.orange])),
child: const Text(
'Container使用',
style: TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w900),
),
),
)),
);
}
}
Text 组件详解
名称 | 功能 |
---|---|
textAlign | 文本对齐方式(center 居中,left 左对齐,right 右对齐,justfy 两端对齐) |
textDirection | 文本方向(ltr 从左至右,rtl 从右至左) |
overflow | 文字超出屏幕之后的处理方式(clip 裁剪,fade 渐隐,ellipsis 省略号) |
textScaleFactor | 字体显示倍率 |
maxLines | 文字显示最大行数 |
style | 字体的样式设置 |
TextStyle 的参数
名称 | 功能 |
---|---|
decoration | 文字装饰线(none 没有线,lineThrough 删除线,overline 上划线, underline 下划线) |
decorationColor | 文字装饰线颜色 |
decorationStyle | 文字装饰线风格([dashed,dotted]虚线,double 两根线,solid 一根实线, wavy 波浪线) |
wordSpacing | 单词间隙(如果是负值,会让单词变得更紧凑 |
letterSpacing | 字母间隙(如果是负值,会让字母变得更紧凑) |
fontStyle | 文字样式(italic 斜体,normal 正常体) |
fontSize | 文字大小 |
color | 文字颜色 |
fontWeight | 字体粗细(bold 粗体,normal 正常体) |
Image 图片组件
Flutter 中,我们可以通过 Image 组件来加载并显示图片 Image 的数据源可以是 asset、文件、内存以及网络
- Image.asset, 本地图片
- Image.network 远程图片
Image 组件的常用属性
名称 | 类型 | 说明 |
---|---|---|
alignment | Alignment | 图片的对齐方式 |
color 和 colorBlendMode | 设置图片的背景颜色,通常和 colorBlendMode 配合一起使用,这样可以是图片颜色和背景色混合。上面的图片就是进行了颜色的混合,绿色背景和图片红色的混合 | |
fit | BoxFit | fit 属性用来控制图片的拉伸和挤压,这都是根据父容器来 的。 BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。 BoxFit.contain:全图显示,显示原比例,可能会有空隙。 BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。 BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。 BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。 BoxFit.scaleDown:效果和 contain 差不多,但是此属性不允许显示超过源图片大小,可小不可大。 |
repeat | 平铺 | ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。ImageRepeat.repeatX: 横向重复,纵向不重复。 ImageRepeat.repeatY:纵向重复,横向不重复。 |
width | 宽度 一般结合 ClipOval 才能看到效果 | |
height | 高度 一般结合 ClipOval 才能看到效果 |
加载远程图片
Container 实现圆形图片
ClipOval 实现圆形图片
CircleAvatar 实现圆形图片
加载本地图片
CircleAvatar
import 'package:flutter/material.dart';
class User extends StatelessWidget {
const User({super.key});
@override
Widget build(BuildContext context) {
return ListView(
children: const [
Text('个人中心'),
CircleAvatar(
radius: 200,
backgroundImage: NetworkImage('https://doc.ronhai.com/bg.png'),
)
],
);
}
}
基本上,CircleAvatar 不提供设置边框的属性。但是,可以将其包裹在具有更大半径和不同背景颜色的不同 CircleAvatar 中,以创建类似于边框的内容。
图标组件
使用 Flutter 官方 Icons 图标
Material Design 所有图标可以在其官网查看:https://material.io/tools/icons/
阿里巴巴图标库
Flutter 中借助阿里巴巴图标库自定义字体图标
我们也可以使用自定义字体图标。阿里巴巴图标库官网 iconfont.cn 上有很多字体图标素材,我们可以选择自己需要的图标打包下载后,会生成一些不同格式的字体文件,在 Flutter 中,我们使用 ttf 格式即可。
假设我们项目中需要使用一套图标,我们打包下载后导入
也可以配置多个字体文件
fonts:
- family: iconfont
fonts:
- asset: assets/fonts/iconfont.ttf
- family: myicon
fonts:
- asset: assets/fonts/myicon.ttf
为了使用方便,我们定义一个 MyIcons 类,功能和 Icons 类一样:将字体文件中的所有图标都定义成静态变量
import 'package:flutter/material.dart';
class IconFont {
static const IconData view = IconData(
0xe633,
fontFamily: 'iconfont',
matchTextDirection: true,
);
static const IconData message = IconData(
0xe635,
fontFamily: 'iconfont',
matchTextDirection: true,
);
}
使用
import 'package:flutter/material.dart';
import 'common/iconfont.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: Center(
child: Container(
alignment: Alignment.center,
width: 300,
height: 300,
child: const Column(
children: [
Icon(
IconFont.view,
size: 30,
color: Colors.amber,
),
Icon(
IconFont.message,
size: 30,
color: Colors.amber,
)
],
)),
)),
);
}
}
脚本生成 iconfont.dart 文件
新建generate_icons.dart
文件放入 common 文件夹中,增加以下内容:
使用Code Runner
运行脚本即可
import 'dart:convert';
import 'dart:io';
String toCamelCase(String name) {
List<String> parts = name.split(RegExp(r'[-_]'));
String camelCaseName = parts[0];
for (int i = 1; i < parts.length; i++) {
camelCaseName += parts[i][0].toUpperCase() + parts[i].substring(1);
}
return camelCaseName;
}
void generateIconFontClass(String jsonFilePath, String outputFilePath) {
File jsonFile = File(jsonFilePath);
if (!jsonFile.existsSync()) {
print('JSON file not found: $jsonFilePath');
return;
}
String jsonString = jsonFile.readAsStringSync();
Map<String, dynamic> jsonData = jsonDecode(jsonString);
StringBuffer buffer = StringBuffer();
buffer.writeln("import 'package:flutter/material.dart';\n");
buffer.writeln("class IconFont {");
if (jsonData.containsKey('glyphs')) {
List<dynamic> glyphs = jsonData['glyphs'];
glyphs.forEach((glyph) {
if (glyph.containsKey('unicode_decimal') && glyph.containsKey('name')) {
String name = glyph['name'];
int unicode = glyph['unicode_decimal'];
// String fontClass = glyph['font_class'];
String fieldName = toCamelCase(name);
buffer.writeln(" static const IconData $fieldName = IconData(");
buffer.writeln(" 0x${unicode.toRadixString(16)},");
buffer.writeln(" fontFamily: 'iconfont',");
buffer.writeln(" matchTextDirection: true,");
buffer.writeln(" );");
}
});
}
buffer.writeln("}");
// Write generated code to output file
File outputFile = File(outputFilePath);
outputFile.writeAsStringSync(buffer.toString());
print('IconFont class generated successfully at: $outputFilePath');
}
void main() {
String jsonFilePath = 'assets/fonts/iconfont.json';
String outputFilePath = 'lib/common/iconfont.dart';
generateIconFontClass(jsonFilePath, outputFilePath);
}
引用
const Icon(
IconFont.view,
color: Colors.red,
size: 30,
)
ListView 列表组件
列表布局是我们项目开发中最常用的一种布局方式。Flutter 中我们可以通过 ListView 来定义列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有以下分类
- 垂直列表
- 垂直图文列表
- 水平列表
- 动态列表
列表组件常用参数
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | Axis.horizontal 水平列表 Axis.vertical 垂直列表 |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
children | List | 列表元素 |
Divider 横线, ListTile 一行内容
垂直列表
垂直图文列表
水平列表
可以左右滑动
动态列表
GridView 网格布局组件
GridView 网格布局在实际项目中用的也是非常多的,当我们想让可以滚动的元素使用矩阵方式排列的时候。此时我们可以用网格列表组件 GridView 实现布局。
GridView 创建网格列表主要有下面三种方式
- 可以通过 GridView.count 实现网格布局
- 可以通过 GridView.extent 实现网格布局
- 通过 GridView.builder 实现动态网格布局
常用属性
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滚动方法 |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
crossAxisSpacing | double | 水平子 Widget 之间间距 |
mainAxisSpacing | double | 垂直子 Widget 之间间距 |
crossAxisCount | int 用在 GridView.count | 一行的 Widget 数量 |
maxCrossAxisExtent | double 用在 GridView.extent | 横轴子元素的最大长度 |
childAspectRatio | double | 子 Widget 宽高比例 |
children | [ ] | |
gridDelegate | SliverGridDelegateWithFixedCrossAxisCount SliverGridDelegateWithMaxCrossAxisExtent | 控制布局主要用在 GridView.builder 里面 |
实现网格布局
GridView.count 构造函数内部使用了 SliverGridDelegateWithFixedCrossAxisCount,我们通过它可以快速的创建横轴固定数量子元素的 GridView
实现网格布局
GridView.extent 构造函数内部使用了 SliverGridDelegateWithMaxCrossAxisExtent,我们通过它可以快速的创建横轴子元素为固定最大长度的的 GridView。
Paddiing 组件
在 html 中常见的布局标签都有 padding 属性,但是 Flutter 中很多 Widget 是没有 padding 属性。这个时候我们可以用 Padding 组件处理容器与子元素之间的间距。
属性 | 说明 |
---|---|
padding | padding 值, EdgeInsetss 设置填充的值 |
child | 子组件 |
Row 和 Column 线性布局
Row 水平布局组件
属性 | 说明 |
---|---|
mainAxisAlignment | 主轴的排序方式 |
crossAxisAlignment | 次轴的排序方式 |
children | 组件子元素 |
Column 垂直布局组件
属性 | 说明 |
---|---|
mainAxisAlignment | 主轴的排序方式 |
crossAxisAlignment | 次轴的排序方式 |
children | 组件子元素 |
double
double.infinity 和 double.maxFinite 可以让当前元素的 width 或者 height 达到父元素的尺寸
Flex Expanded 弹性布局
Flex 组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用 Row 或 Column 会方便一些,因为 Row 和 Column 都继承自 Flex ,参数基本相同,所以能使用 Flex 的地方基本上都可以使用 Row 或 Column 。 Flex 本身功能是很强大的,它也可以和 Expanded 组件配合实现弹性布局 。
水平弹性布局
垂直弹性布局
使用Row 或Column 结合 Expanded 实现下面示例
Stack、Align、Positioned 层叠布局
Stack 组件
Stack 表示堆的意思,我们可以用 Stack 或者 Stack 结合 Align 或者 Stack 结合 Positiond 来实现页面的定位布局
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
children | 子组件 |
Stack Align
Align 组件可以调整子组件的位置 , Stack 组件中结合 Align 组件也可以控制每个子元素的显示位置
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
child | 子组件 |
Stack Positioned
Stack 组件中结合 Positioned 组件也可以控制每个子元素的显示位置
属性 | 说明 |
---|---|
top | 子元素距离顶部的距离 |
bottom | 子元素距离底部的距离 |
left | 子元素距离左侧距离 |
right | 子元素距离右侧距离 |
child | 子组件 |
width | 组件的高度 (注意:宽度和高度必须是固定值,没法使用 double.infinity) |
height | 子组件的高度 |
MediaQuery 获取屏幕宽度和高度
需要在 build 中使用
final size=MediaQuery.of(context).size;
Stack Positioned 固定导航案例
AspectRatio
AspectRatio的作用是根据设置调整子元素 child 的宽高比。
AspectRatio首先会在布局限制条件允许的范围内尽可能的扩展,widget 的高度是由宽度和比率决定的,类似于 BoxFit 中的 contain,按照固定比率去尽量占满区域。
如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio 最终将会去优先适应布局限制条件,而忽略所设置的比率。
属性 | 说明 |
---|---|
aspectRatio | 宽高比,最终可能不会根据这个值去布局,具体则要看综合因素,外层是否允许按照这种比率进行布局,这只是一个参考值 |
child | 子组件 |
Card 组件
Card 是卡片组件块,内容可以由大多数类型的 Widget 构成,Card 具有圆角和阴影,这让它看起来有立体感。
属性 | 说明 |
---|---|
margin | 外边距 |
child | 子组件 |
elevation | 阴影值的深度 |
color | 背景颜色 |
shadowColor | 阴影颜色 |
margin | 外边距 |
clipBehavior | clipBehavior 内容溢出的剪切方式 Clip.none 不剪切 Clip.hardEdge 裁剪但不应用抗锯齿 Clip.antiAlias 裁剪而且抗锯齿 Clip.antiAliasWithSaveLayer 带有抗锯齿的剪辑,并在剪辑之后立即保存 saveLayer |
Shape | Card 的阴影效果,默认的阴影效果为圆角的长方形边。 shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)) ), |
通讯录列表
图文列表
CircleAvatar 实现一个圆形图片
按钮组件
常用属性
属性 | 说明 |
---|---|
onPressed | 必填参数,按下按钮时触发的回调,接收一个方法,传 null 表示按钮禁用,会显示禁用相关样式 |
child | 子组件 |
style | 通过 ButtonStyle 装饰 |
ButtonStylee 组件属性
常用的参数
属性名称 | 值类型 | 属性值 |
---|---|---|
foregroundColor | Color | 文本颜色 |
backgroundColor | Color | 按钮的颜色 |
shadowColor | Color | 阴影颜色 |
elevation | double | 阴影的范围,值越大阴影范围越大 |
padding | 内边距 | |
shape | 设置按钮的形状 shape: MaterialStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)) ) | |
side | 设置边框 | MaterialStateProperty.all(BorderSide(width:1,color: Colors.red)) |
ElevatedButton
ElevatedButton 即"凸起"按钮,它默认带有阴影和灰色背景。按下后,阴影会变大
TextButton
TextButton 即文本按钮,默认背景透明并不带阴影。按下后,会有背景色
OutlinedButton
OutlineButton 默认有一个边框,不带阴影且背景透明。按下后,边框颜色会变亮、同时出现背景和阴影
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
ElevatedButton(onPressed: () {}, child: const Text('普通按钮')),
TextButton(onPressed: () {}, child: const Text('文本按钮')),
OutlinedButton(onPressed: () {}, child: const Text('边框按钮')),
]),
IconButton
IconButton 是一个可点击的 Icon,不包括文字,默认没有背景,点击后会出现背景
带图标的按钮
ElevatedButton 、 TextButton 、 OutlineButton 都有一个 icon 构造函数,通过它可以轻松创建带图标的按钮
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
onPressed: () {}, icon: const Icon(Icons.theater_comedy)),
ElevatedButton.icon(
onPressed: () {},
label: const Text('图标按钮'),
icon: const Icon(
Icons.send,
color: Colors.red,
),
),
OutlinedButton.icon(
onPressed: () {},
label: const Text('边框带图标'),
icon: const Icon(Icons.access_time_filled),
)
],
)
设置按钮颜色和背景色
ElevatedButton.icon(
onPressed: () {},
label: const Text('边框带图标'),
icon: const Icon(Icons.access_time_filled),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.pink),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
),
//side改变边框颜色,width边框粗细
OutlinedButton.icon(
onPressed: () {},
label: const Text('边框带图标'),
icon: const Icon(Icons.access_time_filled),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.pink),
foregroundColor: WidgetStateProperty.all(Colors.white),
side: WidgetStateProperty.all(
const BorderSide(color: Colors.blue, width: 2.0),
),
),
),
设置按钮宽度高度
自适应按钮
圆形圆角按钮
OutlinedButton 边框
Wrap 组件
Wrap 可以实现流布局,单行的 Wrap 跟 Row 表现几乎一致,单列的 Wrap 则跟 Column 表现几乎一致。但 Row 与 Column 都是单行单列的,Wrap 则突破了这个限制,mainAxis 上空间不足时,则向 crossAxis 上去扩展显示。
常用属性
属性 | 说明 |
---|---|
direction | 主轴的方向,默认水平 |
alignment | 主轴的对其方式 |
spacing | 主轴方向上的间距 |
textDirection | 文本方向 |
verticalDirection | 定义了 children 摆放顺序,默认是 down,见 Flex 相关属性介绍。 |
runAlignment | run 的对齐方式。run 可以理解为新的行或者列,如果是水平方向布局的话, run 可以理解为新的一行 |
runSpacing | run 的间距 |
preferredSize 组件
PreferredSize 可以改变 appBar 的高度
KeepAliveWrapper 缓存页面
AutomaticKeepAliveClientMixin 可以快速的实现页面缓存功能,但是通过混入的方式实现不是很优雅, 所以我们有必要对 AutomaticKeepAliveClientMixin 混入进行封装
监听 TabController 改变事件
Dialog
AlertDialog
SimpleDialog SimpleDialogOption
showModalBottomSheet
Toast
fluttertoast 的使用:https://pub.dev/packages/fluttertoast
ftoast 的使用
支持 LINUX MACOS WEB WINDOWS 的另一个插件https://pub.dev/packages/ftoast
PageView
Flutter 中的轮动图以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView 轻松实现
PageView 常见属性
属性 | 描述 |
---|---|
scrollDirection | Axis.horizonta 水平方向 Axis.vertical 锤子方向 |
children | 配置子元素 |
allowImplicitScrolling | 缓存当前页面的前后两页 |
onPageChanged | page 改变的时候触发 |
Widget
在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget。
StatelessWidget是无状态组件,状态不可变的 widget
StatefulWidget是有状态组件,持有的状态可能在 widget 生命周期改变。
通俗的讲:如果我们想改变页面中的数据的话这个时候就需要用到 StatefulWidget
实现一个计数器的功能
实现一个动态列表
路由
Flutter 中的路由通俗的讲就是页面跳转。在 Flutter 中通过 Navigator 组件管理路由导航。并提供了管理堆栈的方法。如:Navigator.push 和 Navigator.pop
**Flutter****中给我们提供了两种配置路由跳转的方式**:1、基本路由 2、命名路由
普通路由
比如我们现在想从 HomePage 组件跳转到 SearchPage 组件。
需要在 HomPage 中引入 SearchPage.dart
在 HomePage 中通过下面方法跳转
普通路由跳转传值
跳转传值和调用组件传值的实现方法是一样的
命名路由
命名路由传值
替换路由
比如我们从用户中心页面跳转到了 registerFirst 页面,然后从 registerFirst 页面通过 pushReplacementNamed 跳转到了 registerSecond 页面。这个时候当我们点击 registerSecond 的返回按钮的时候它会直接返回到用户中心。
返回到根路由
比如我们从用户中心跳转到 registerFirst 页面,然后从 registerFirst 页面跳转到 registerSecond 页面,然后从 registerSecond 跳转到了 registerThird 页面。这个时候我们想的是 registerThird 注册成功后返回到用户中心。 这个时候就用到了返回到根路由的方法。
全局配置主题
Key 详解
我们平时一定接触过很多的 Widget,比如 Container、Row、Column 等,它们在我们绘制界面的过程中发挥着重要的作用。但是不知道你有没有注意到,在几乎每个 Widget 的构造函数中,都有一个共同的参数,它们通常在参数列表的第一个,那就是 Key。
在 Flutter 中,Key 是不能重复使用的,所以 Key 一般用来做唯一标识。组件在更新的时候,其状态的保存主要是通过判断组件的类型或者key 值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用 key。但是如果同时存在多个同一类型的控件的时候,此时类型已经无法作为区分的条件了,我们就需要使用到 key
LocalKey、GlobalKeyFlutter key 子类包含 LocalKey 和 GlobalKey 。
- 局部键(LocalKey):ValueKey、ObjectKey、UniqueKey
- 全局键(GlobalKey): GlobalKey、GlobalObjectKey
ValueKey (值 key)把一个值作为 key ,UniqueKey(唯一 key)程序生成唯一的 Key,当我们不知道如何指定 ValueKey 的时候就可以使用 UniqueKey,ObjectKey(对象 key)把一个对象实例作为 key。
GlobalKey(全局 key),GlobalObjectKey(全局 Objec key,和 ObjectKey 有点类似)
获取子组件状态
- globalKey.currentState 可以获取子组件的状态,执行子组件的方法
- globalKey.currentWidget 可以获取子组件的属
- _globalKey.currentContext!.findRenderobject()可以获取渲染的属性。
Flutter 动画
动画原理
动画基本原理以及 Flutter 动画简介
在任何系统的 UI 框架中,动画实现的原理都是相同的,即:在一段时间内,快速地多次改变 UI 外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画,这和电影的原理是一样的。我们将 UI 的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率 FPS(Frame Per Second),即每秒的动画帧数。很明显,帧率越高则动画就会越流畅!一般情况下,对于人眼来说,动画帧率超过 16 FPS,就基本能看了,超过 32 FPS 就会感觉相对平滑,而超过 32 FPS,大多数人基本上就感受不到差别了。由于动画的每一帧都是要改变 UI 输出,所以在一个时间段内连续的改变 UI 输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在 UI 系统中,动画的平均帧率是重要的性能指标,而在 Flutter 中,理想情况下是可以实现 60FPS 的,这和原生应用能达到的帧率是基本是持平的。
Flutter 动画简介
FLutter 中的动画主要分为:隐式动画、显式动画、自定义隐式动画、自定义显式动画、和 Hero 动画
隐式动画
通过几行代码就可以实现隐式动画,由于隐式动画背后的实现原理和繁琐的操作细节都被隐去了,所以叫隐式动画,FLutter 中提供的 AnimatedContainer、AnimatedPadding、AnimatedPositioned、 AnimatedOpacity、AnimatedDefaultTextStyle、AnimatedSwitcher 都属于隐式动画。隐式动画中可以通过 duration 配置动画时长、可以通过 Curve (曲线)来配置动画过程
AnimatedContainer
AnimatedContainer 的属性和 Container 属性基本是一样的,当 AnimatedContainer 属性改变的时候就会触发动画
AnimatedPadding 以及 curve 属性
Curves 曲线值
曲线名 | 动画过程 |
---|---|
linear | 匀速的 |
decelerate | 匀减速 |
ease | 开始加速,后面减速 |
easeIn | 开始慢,后面快 |
easeOut | 开始快,后面慢 |
easeInOut | 开始慢,然后加速,最后再减速 |
更多曲线 | https://docs.flutter.io/flutter/animation/Curves-class.html 官方文档打不开也可以参考教程目录中提供的 gif 截图 |