选择MacOs
选择MacOs作为虚拟机演示

布局约束规则
让子元素竟可能的大,撑满父元素
void main() {
// 启动Flutter应用,运行build()方法构建UI
runApp(build());
}
Widget build() {
// Container : 在这里创建一个宽200、高200、颜色为琥珀色的容器
// 在Dart语法上,这里是创建了一个匿名对象
return Container(width: 200, height: 200, color: Colors.amber);
}
这个时候,设置的width
和height
并没有起作用

确认位置后,按子元素大小显示
void main() {
// 启动Flutter应用,运行build()方法构建UI
runApp(build());
}
Widget build() {
// Container : 在这里创建一个宽200、高200、颜色为琥珀色的容器
// 在Dart语法上,这里是创建了一个匿名对象
// child: Container : 这里是将Container作为Center的子组件
// Center({super.key, super.widthFactor, super.heightFactor, super.child});
// Center的构造函数是命名参数
return Center(child: Container(width: 200, height: 200, color: Colors.amber));
}
确认好Container
的位置之后,设置的width
和height
才开始起作用

核心规则
核心规则:Constraints go down. Sizes go up. Positions are set by parents.
- 上层 widget 向下层 widget 传递约束条件。
- 下层 widget 向上层 widget 传递大小信息。
- 上层 widget 决定下层 widget 的位置。

Widget build() {
// MaterialApp: 是一个应用程序的顶层容器,提供了许多应用程序所需的功能
// MaterialApp 也是一个Widget
return MaterialApp(
// home:Scaffold: 这里是将Scaffold作为应用的首页
home: Scaffold(
// Scaffold : 在这里创建一个Scaffold组件
body: Column(
// Column : 在这里创建一个垂直排列的文本列表
// Text("aaaaaa") : 这里是第一个文本组件
// const <Widget> : 这里是一个常量列表
children: const <Widget>[Text("aaaaaa"), Text("bbbbb"), Text("cccc")],
),
),
);
}

下面是组件的大小和位置信息:

宽度 0.0 <= w <= 280.0
, 高度 0.0 <= h <= 173.0
,就是上层传下来的约束。Scaffold
组件的大小就是height=173.0
,width=280.0
。

Text
组件的宽度 w=47.8
, 高度 h=20.0
就是组件向上层传递的大小信息
元素左边 w=1.8
,右边 w=1.8
,就是上层决定下层的组件位置

Column 宽度等于子元素最大宽度
Widget build() {
return MaterialApp(
home: Scaffold(
body: Column(
children: const <Widget>[
// 只修改了Text的宽度
Text("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
Text("bbbbb"),
Text("cccc"),
],
),
),

Container 紧包裹子元素
Scaffold 填充了整个屏幕,Container 包裹了 Column
Widget build() {
return MaterialApp(
home: Scaffold(
body: Container( // container 容器
color: Colors.yellow, // 黄色
child: Column(
children: const <Widget>[
Text("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
Text("bbbbb"),
Text("cccc"),
],
),
),
),
);
}

这里container
的width=446.6,等于column
的width。而container
的height=173.0,等于column
的height

Scaffold
的width=603.0,因此会有部分区域不是黄色的。在正常情况下,Flutter 里的 Scaffold
会自动撑满整个可用窗口区域。也就是Scaffold的大小等于窗口的大小。

松约束
当一个 widget 告诉其子级可以比自身更小的话, 我们通常称这个 widget 对其子级使用 宽松约束(loose)。在下面这个例子中,Text的width的取值范围是0.0 <= w <= 280.0
,是一个区间而不是固定的值。这就是松约束。

紧约束
ConstrainedBox 约束组件
constraints
通过 maxWidth
maxHeight
来设置子组件最大约束;通过 minWidth
minHeight
来设置子组件最小约束
Widget build() {
return MaterialApp(
home: Center(
child: Scaffold(
body: Center(
// ConstrainedBox: 用于限制子组件的大小
child: ConstrainedBox(
// constraints: 用于设置子组件的大小限制
constraints: const BoxConstraints(
minWidth: 100, // 最小宽度
minHeight: 100, // 最小高度
maxWidth: 200, // 最大宽度
maxHeight: 200, // 最大高度
),
child: Container(color: Colors.amber, width: 10, height: 10),
),
),
),
),
);
}
由于最小限制是100,即便我们设置了Container
大小,但是我们设置的小于最小值,所以采取了最小值。

紧约束定义
它的最大/最小宽度是一致的,高度也一样。
通过 BoxConstraints.tight
可以设置紧约束
Widget build() {
return MaterialApp(
home: Scaffold(
body: Center(
child: ConstrainedBox(
// constraints: 用于设置子组件的大小限制
// BoxConstraints.tight: 创建一个紧约束,要求子组件必须占据指定的大小
constraints: BoxConstraints.tight(const Size(200, 200)),
child: Container(color: Colors.amber, width: 10, height: 10),
),
),
),
);
}
最后,遵循了BoxConstraints.tight
的设置:

不受约束
UnconstrainedBox 不受约束
Widget build() {
return MaterialApp(
home: Center(
child: Scaffold(
body: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100,
minHeight: 100,
maxWidth: 300,
maxHeight: 300,
),
// UnconstrainedBox: 用于去除父组件的约束,包括宽度和高度的约束
child: UnconstrainedBox(
child: Container(width: 50, height: 50, color: Colors.blue),
),
),
),
),
// 关闭调试模式下的右上角的DEBUG标识
debugShowCheckedModeBanner: false,
);
}

UnconstrainedBox
包裹内部的 Container 50*50
可以不受约束自己控制大小

unbounded 组件
bounded(有界约束) → 父组件明确告诉子组件最大/最小宽高,例如:maxHeight = 500
unbounded(无界约束) → 某个维度上没有最大限制(maxHeight = ∞
或 maxWidth = ∞
)
Row
Column
ListView
这种组件 属于 unbounded
组件 | 主轴方向 | 主轴约束 | 副轴约束 |
---|---|---|---|
Column | 垂直方向 | 高度 unbounded(子组件想多高就多高) | 宽度 bounded(由父约束决定) |
Row | 水平方向 | 宽度 unbounded(子组件想多宽就多宽) | 高度 bounded(由父约束决定) |
ListView | 滚动方向 | 滚动方向 unbounded(无限延伸) | 另一方向 bounded |
Widget build() {
return MaterialApp(
home: Scaffold(
// column: 用于创建一个垂直排列的组件,在垂直方向可以无限扩展
body: Column(
children: [
// FlutterLogo : 用于显示Flutter的Logo
const FlutterLogo(size: 50),
const FlutterLogo(size: 20),
Container(
height: 2000, // 设置了 2000 高
color: Colors.amber,
),
],
),
),
debugShowCheckedModeBanner: false,
);
}
底部有一行小字,提示溢出:

在inspector里面也清楚的显示了你溢出的情况。container最高是731.4,但是现在在2070。

初识生命周期
生命周期就是一个对象从出生到死亡所经历的完整过程,以及在这些过程中的关键阶段。
下面是一个简化版的通用生命周期过程(不特指 Flutter,任何有生命周期的对象都类似):
1. 创建阶段(Create)
- 对象被创建
- 构造函数 / 初始化逻辑执行一次
2. 初始化阶段(Initialize)
- 设置初始数据
- 建立需要的资源(比如网络连接、监听器等)
3. 活跃阶段(Active / Run)
- 执行主要功能
- 根据外部变化更新状态(刷新界面、数据变化等)
4. 更新阶段(Update)
- 外部条件变化时,重新调整自己
- 比如父对象传入的新数据
5. 销毁阶段(Destroy / Dispose)
- 释放资源(关闭文件、断开连接、移除监听)
- 对象彻底销毁,无法再使用
有状态无状态组件
概念理解
首先:Widget 不是“组件”,而是“配置”
- 很多人刚开始会以为:“StatelessWidget 和 StatefulWidget 是组件的两种类型,一个能变,一个不能变。”
- 其实更准确的理解是:
- Widget 在 Flutter 中是UI的配置描述(immutable,不可变对象)
- 真正渲染的是 Element 和 RenderObject
- 所谓“有状态”和“无状态”,是说这个 UI 描述在生命周期内能否保存数据并触发重绘
所谓“有状态”和“无状态”,是说这个 UI 描述在生命周期内能否保存数据并触发重绘
- 生命周期内能否保存数据
- 生命周期内 = 从组件创建到销毁的这段时间。
- 保存数据 = 组件里是否有变量能记住一些值,不会因为一次 UI 重建就丢失。
- StatelessWidget → 没有自己的状态存储空间,它的所有数据都来自外部传入,不能自己记住变化。
- StatefulWidget → 有一个 State 对象,可以在里面存变量,这些变量在组件生命周期内是持久的(直到组件被销毁)。
- 并触发重绘
- 触发重绘 = 当保存的数据发生变化时,是否可以告诉 Flutter “我变了,请重新画我”。在 Flutter 里,这个动作是调用
setState()
完成的。- StatelessWidget → 没有
setState()
,自己没法触发 UI 更新。要更新只能靠外部重新创建它。 - StatefulWidget → 有
setState()
,可以在内部修改数据并要求 Flutter 重建 UI。
- StatelessWidget → 没有
- 触发重绘 = 当保存的数据发生变化时,是否可以告诉 Flutter “我变了,请重新画我”。在 Flutter 里,这个动作是调用
所以这句话的意思就是:
- 有状态组件(StatefulWidget):在它的存在期间,可以记住一些数据(状态),并在这些数据变化时主动让自己重绘。
- 无状态组件(StatelessWidget):生命周期内不保存自己的状态,要变只能靠外部重新创建它。
无状态组件 StatelessWidget
特点:
- 没有可变状态(没有
State
对象) - 数据一旦传进来,就不会自己变
- 需要刷新时只能靠外部传入新的 Widget 实例
- 每次数据变化时,你必须重新 new 一个新的
MyText
,Flutter 会用新的 Widget 替换旧的 - 没有
setState()
方法
适用场景:
- 静态内容(标题、标签、图标等)
- 内容只会随外部数据变化而变化
void main(List<String> args) {
// 1-runApp: Flutter 框架提供的全局函数。参数必须是一个 Widget。
//它会把这个 Widget 挂载到 Flutter 的根部 UI 树,并启动整个应用的渲染流程。
// 2-const MyApp(): 创建一个MyApp的实例
runApp(const MyApp());
}
const img1 = "https://linresource.uk/img/13399635147767705.jpg";
const img2 = "https://linresource.uk/img/13399635169004637.jpg";
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
// AppBar: 用于显示应用程序的标题栏
appBar: AppBar(title: Center(child: const Text('有无状态组件'))),
// body: 用于显示应用程序的主要内容
body: Center(child: const BannerWidget()),
),
debugShowCheckedModeBanner: false,
);
}
}
class BannerWidget extends StatelessWidget {
const BannerWidget({super.key});
@override
Widget build(BuildContext context) {
return Image.network(img1);
}
}

有状态 StatefulWidget
特点:
- 拥有一个单独的 State 对象
- State 对象可以持有可变数据(变量)
- 当状态改变时可以调用
setState()
触发 UI 重建 State
生命周期内变量可以多次改变- 每次调用
setState()
,Flutter 会:- 标记当前 Element 需要重建
- 重新执行
build()
方法 - 刷新界面
适用场景:
- 需要动态变化的 UI(计数器、表单、动画等)
- 需要在组件内部保存数据的情况

void main(List<String> args) {
// 1-runApp: Flutter 框架提供的全局函数。参数必须是一个 Widget。
//它会把这个 Widget 挂载到 Flutter 的根部 UI 树,并启动整个应用的渲染流程。
// 2-const MyApp(): 创建一个MyApp的实例
runApp(const MyApp());
}
const img1 = "https://linresource.uk/img/13399635147767705.jpg";
const img2 = "https://linresource.uk/img/13399635169004637.jpg";
// MyApp: 这是一个无状态组件,继承自 StatelessWidget
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
// AppBar: 用于显示应用程序的标题栏
appBar: AppBar(title: Center(child: const Text('有无状态组件'))),
// body: 用于显示应用程序的主要内容
body: Center(child: const BannerWidget()),
),
debugShowCheckedModeBanner: false,
);
}
}
// BannerWidget: 有状态组件,继承自StatefulWidget
class BannerWidget extends StatefulWidget {
const BannerWidget({super.key});
@override
// createState: 创建状态对象
State<BannerWidget> createState() => _BannerWidgetState();
}
// _BannerWidgetState: BannerWidget的状态类
class _BannerWidgetState extends State<BannerWidget> {
// imgUrl: 用于存储当前显示的图片URL
String? imgUrl;
@override
Widget build(BuildContext context) {
// Column: 用于垂直排列子组件
return Column(
children: [
// ElevatedButton: 用于创建一个可点击的按钮
ElevatedButton(
// onPressed: 当按钮被点击时触发的回调函数
onPressed: () {
// setState: 通知框架状态已改变,从而重新构建 Widget
setState(() {
// imgUrl == img1 ? img2 : img1 : 表示根据当前图片切换到另一张图片
// 如果当前图片是 img1,则切换到 img2;否则切换到 img1
// 如果 imgUrl 为 null,则默认显示 img1,因为null==img1为false
imgUrl = imgUrl == img1 ? img2 : img1;
});
},
// child: 用于定义按钮的子组件
child: const Text("切换图片"),
),
Image.network(imgUrl ?? img1, height: 600),
],
);
}
}
BannerWidget extends StatefulWidget
:继承StatefulWidget
必须实现createState()
方法,返回一个State
类型对象,这个State
对象就是实际维护可变数据的地方。State<BannerWidget> createState()
class _BannerWidgetState extends State<BannerWidget>
这个
State<T>
里面的泛型 T,其实是非常关键的——它必须对应你这个State
类所绑定的StatefulWidget
类型。这里的BannerWidget
是一个StatefulWidget
,而_MyAppState
是它对应的状态类。为什么要指定这个泛型?
让
State
知道自己属于哪个 Widget。Flutter 会把 Widget 实例传给State
,以便在State
中通过widget
属性访问它。比如@override Widget build(BuildContext context) { return Text(widget.title); // widget 就是 BannerWidget 的实例 }
如果你把泛型错写成别的类型,比如
State<OtherWidget>
,widget
的类型就会变成OtherWidget
,而不是BannerWidget
,导致属性和方法对不上。类型安全
- 泛型确保了
State
与它的宿主StatefulWidget
类型一致。 - 如果泛型和实际 Widget 类型不一致,编译器会直接报错,避免运行时才发现问题。
- 泛型确保了
框架自动绑定
- Flutter 在创建
State
时会调用createState()
,并自动把对应的 Widget 传给它,这个过程依赖泛型来做类型匹配。
- Flutter 在创建
_BannerWidgetState
这个类前面有一个下划线_
,在 Dart 里表示库级私有(library private)State
对象是 Flutter 框架内部用的,外部其实不需要直接操作它。- 你只希望外界通过
BannerWidget
这个 Widget 来使用,而不是直接去 new 一个 State。 - 把
State
类私有化,可以防止外部直接访问或依赖它的实现细节,方便以后修改内部逻辑而不影响外部 API。
build()
方法:每次setState()
调用后,Flutter 都会重新调用这个build()
方法来重建 UI。ElevatedButton
→ 点击时执行onPressed
setState()
的作用是:- 更新状态变量(这里是
imgUrl
) - 通知 Flutter:这个 State 对应的 Widget 需要重建
- 更新状态变量(这里是
Image.network(imgUrl ?? img1)
??
运算符:如果imgUrl
是null
,就用img1
作为默认值。
生命周期
StatefulWidget 生命周期
概览
创建阶段
┌──────────────────────────────┐
│ StatefulWidget 被创建 │
└──────────────┬───────────────┘
│
▼
createState() ← 框架调用,生成 State 实例
│
▼
initState() ← 只执行一次,初始化数据
│
▼
didChangeDependencies() ← 首次依赖注入或依赖变更
│
▼
build() ← 构建 UI(可能多次调用)
│
├──────────► addPostFrameCallback()
│ (当前帧构建完成后执行)
▼
────────── 以下是组件更新流程 ──────────
父 Widget 更新(配置变化):
│
▼
didUpdateWidget(oldWidget)
│
▼
build() ← 重建 UI
│
├──────────► addPostFrameCallback()
▼
────────── 以下是移除/销毁流程 ──────────
Widget 从树中临时移除:
│
▼
deactivate()
│
Widget 永久移除:
│
▼
dispose() ← 释放资源(如动画控制器、监听器等)
方法 | 触发时机 | 特点 / 用途 |
---|---|---|
createState() |
Widget 创建时 | 返回一个 State 实例,Flutter 框架调用。 |
initState() |
State 创建后、build 前 |
只调用一次,适合初始化数据、启动动画、监听事件等。 |
didChangeDependencies() |
initState 后、依赖变化时 |
会在依赖的 InheritedWidget 更新时再次调用。 |
build() |
首次渲染 & setState 后 |
构建 Widget 树。可能会频繁调用,要高效。 |
didUpdateWidget() |
父组件重建并传入新配置时 | 适合对比 oldWidget 与 widget 的差异来更新状态。 |
addPostFrameCallback() |
当前帧 build 完成后 |
一般用来在布局完成后获取 context 尺寸、滚动到特定位置等。 |
setState() |
手动调用 | 通知 Flutter 数据变化,触发 build 。 |
deactivate() |
从 widget 树移除时 | 可能会重新插入(比如导航切换 tab 页)。 |
dispose() |
永久移除时 | 释放资源,比如关闭 Stream、动画控制器等。 |

代码
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
runApp(const MyApp());
}
const img1 = "https://linresource.uk/img/13399635147767705.jpg";
const img2 = "https://linresource.uk/img/13399635169004637.jpg";
// 在这里定义一个图片组件
Widget imageWidget({required String imgUrl}) {
return Container(
// 设置内边距和背景色
// padding: 设置内边距, 内边距为 10
padding: const EdgeInsets.all(10),
color: Colors.amber,
child: Center(child: Image.network(imgUrl, height: 600)),
);
}
// BannerWidget: 有状态组件,继承自StatefulWidget
class BannerWidget extends StatefulWidget {
const BannerWidget({super.key});
// 创建 State 只执行1次
@override
State<BannerWidget> createState() {
print("createState:创建状态对象");
return _BannerWidgetState();
}
}
// _BannerWidgetState: BannerWidget的状态类
class _BannerWidgetState extends State<BannerWidget> {
String? imgUrl;
// 初始 State, mounted 等于 true, 只执行1次
@override
void initState() {
super.initState();
print("initState:初始化状态");
// 渲染结束调用,只执行1次
// SchedulerBinding是Flutter框架中的一个类,用于调度帧的绘制和事件的处理
// .instance: 获取SchedulerBinding的实例
// .addPostFrameCallback: 在下一帧绘制结束后执行的回调
//
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
print("addPostFrameCallback:在build完成后");
print(timeStamp);
});
}
// 父或祖先widget中的InheritedWidget改变时会被调用
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies:依赖发生变化");
}
// 父类 setState 后,子类就会触发
@override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget:更新组件");
}
// 从组件树中移除 State 时调用
@override
void deactivate() {
super.deactivate();
print("deactivate:从组件树中移除状态");
}
// 组件被释放时调用
@override
void dispose() {
print("dispose:释放资源");
// 调用父类的dispose方法,目的是释放资源
super.dispose();
}
// UI 被重新渲染的时候多次执行
@override
Widget build(BuildContext context) {
print("build:构建BannerWidget");
return Column(
children: [
ElevatedButton(
onPressed: () {
print("onPressed:按钮被点击");
// mounted 这个值是用来判断当前状态对象是否仍然在树中
// flutter 分配完你的组件树位置,会设置 mounted 为 true。
// 如果 mounted 为 false,说明组件已经被从树中移除
// mounted 是在什么时候被赋值为true的? 是在initState中
if (mounted == true) {
setState(() {
print("setState:更新状态");
imgUrl = imgUrl == img1 ? img2 : img1;
});
}
},
child: const Text("切换图片"),
),
imageWidget(imgUrl: imgUrl ?? img1),
],
);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
print("build:构建MyApp");
return MaterialApp(
home: Scaffold(
body: Column(children: const [Text('有无状态组件'), BannerWidget()]),
),
debugShowCheckedModeBanner: false,
);
}
}
日志如下:
Restarted application in 1,560ms.
I/flutter (11459): build:构建MyApp
I/flutter (11459): createState:创建状态对象
I/flutter (11459): initState:初始化状态
I/flutter (11459): didChangeDependencies:依赖发生变化
I/flutter (11459): build:构建BannerWidget
I/flutter (11459): addPostFrameCallback:在build完成后
I/flutter (11459): 0:00:00.000000
I/flutter (11459): build:构建MyApp
I/flutter (11459): build:构建BannerWidget
I/flutter (11459): onPressed:按钮被点击
I/flutter (11459): setState:更新状态
I/flutter (11459): build:构建BannerWidget
I/flutter (11459): onPressed:按钮被点击
I/flutter (11459): setState:更新状态
I/flutter (11459): build:构建BannerWidget
StatelessWidget 生命周期
无状态组件,不需要处理生命周期,直接显示即可
abstract class StatelessWidget extends Widget {
/// Initializes [key] for subclasses.
const StatelessWidget({ Key? key }) : super(key: key);
/// Creates a [StatelessElement] to manage this widget's location in the tree.
///
/// It is uncommon for subclasses to override this method.
@override
StatelessElement createElement() => StatelessElement(this);
在源码中可见 createElement()
创建组件到组件树,不需要重写去维护
App生命周期
生命周期
名称 | 说明 |
---|---|
resumed | 应用程序可见且响应用户输入。 |
inactive | 应用程序处于非激活状态,无法响应用户输入。 |
pause | 应用程序不可见且无法响应用户输入,运行在后台。 |
detached | 应用程序仍寄存在Flutter引擎上,但与平台 View 分离。 |
suspending | 应用被挂起,此状态IOS永远不会回调 |
假设你正在使用一个时间管理APP
状态 | 场景例子 | 发生时机 |
---|---|---|
resumed | 你正盯着屏幕看着计时器数字往下跳,能点按钮、能滑动页面,App 正常响应你。 | App 在前台、可见、可交互 |
inactive | 你突然从屏幕顶部往下拉通知栏,计时器停留在当前界面,但你没法点击按钮。 | 系统临时打断,比如电话、通知、界面切换过渡 |
pause | 你按 Home 键切回桌面,App 的计时器逻辑暂停(如果开发者没特殊处理),屏幕不显示 App。 | App 进入后台,不可见,不响应输入 |
detached | 你在后台清理了 App(但是引擎资源还没完全释放,比如热重启调试时),UI 和平台视图分离。 | Flutter 引擎还在,但 View 被移除 |
suspending | Android 上,系统为了省电/释放内存,把后台的 App 挂起;iOS 永远不会收到这个状态。 | App 被系统挂起 |
如何监听APP的生命周期
第一步: 创建 StatefulWidget 组件,混入 WidgetsBindingObserver
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
...
WidgetsBindingObserver 是一个 观察者接口,可以用来监听:
App 生命周期(前台/后台切换)
系统设置变化(语言、主题、屏幕旋转等)
内存压力
混入(with)之后,就可以实现它的一系列回调方法。
第二步:添加观察者 addObserver
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this); //添加观察者
}
initState()
是组件初始化时执行的。addObserver(this)
表示当前类要接收生命周期和系统事件的通知。
生命周期变化时回调 didChangeAppLifecycleState(App 前后台切换)
// 生命周期变化时回调
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print("didChangeAppLifecycleState: $state");
}
state
可能的值:
resumed
:应用可见,可交互(进入前台)。inactive
:可见但不可交互(如来电弹窗)。paused
:不可见(进入后台)。detached
:应用与宿主视图分离(极少用)。suspending
:挂起(iOS 永远不会回调)
具体代码
import "package:flutter/material.dart";
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
_MyAppState createState() => _MyAppState();
}
//实现WidgetsBindingObserver观察者
// abstract mixin class WidgetsBindingObserver {
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this); //添加观察者
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("App生命周期")),
body: Column(children: <Widget>[const Text("首页")]),
),
);
}
// 生命周期变化时回调
// resumed:应用可见并可响应用户操作,app进入前台
// inactive:用户可见,但不可响应用户操作,比如来了个电话,前后台切换的过渡状态
// paused:已经暂停了,用户不可见、不可操作,app进入后台
// suspending:应用被挂起,此状态IOS永远不会回调
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print("didChangeAppLifecycleState: $state");
}
//当前系统改变了一些访问性活动的回调: 比如系统开启了“放大文字”“高对比度”等辅助功能时触发。
@override
void didChangeAccessibilityFeatures() {
super.didChangeAccessibilityFeatures();
print("didChangeAccessibilityFeatures");
}
//低内存回调: 当系统内存不足时触发(可以用来释放缓存资源)。
@override
void didHaveMemoryPressure() {
super.didHaveMemoryPressure();
print("didHaveMemoryPressure");
}
//用户本地设置变化时调用,如系统语言改变,比如用户在系统设置里把语言改成英文,会触发。
@override
void didChangeLocales(List<Locale>? locale) {
super.didChangeLocales(locale);
print("didChangeLocales");
}
//应用尺寸改变时回调,比如旋转屏幕(横竖屏切换)时触发。
@override
void didChangeMetrics() {
super.didChangeMetrics();
Size? size = WidgetsBinding.instance.window.physicalSize;
print("didChangeMetrics :宽:${size.width} 高:${size.height}");
}
//系统切换主题时回调:例如用户在系统里切换浅色模式 / 深色模式时触发
@override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
print("didChangePlatformBrightness");
}
///文字系数变化: 当用户调整系统字体大小时触发。
@override
void didChangeTextScaleFactor() {
super.didChangeTextScaleFactor();
print(
"didChangeTextScaleFactor :${WidgetsBinding.instance.window.textScaleFactor}",
);
}
//当应用被销毁时调用:比如用户关闭了应用
@override
void dispose() {
print("dispose:应用被销毁");
super.dispose();
// 组件销毁时必须移除观察者,否则可能造成内存泄漏。
WidgetsBinding.instance.removeObserver(this); //销毁观察者
}
}
输出:
Restarted application in 1,945ms.
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
D/VRI[MainActivity](12299): visibilityChanged oldVisibility=true newVisibility=false
I/flutter (12299): didHaveMemoryPressure
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.hidden
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.paused
D/ViewRootImpl(12299): Skipping stats log for color mode
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.hidden
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.resumed
D/InsetsController(12299): hide(ime(), fromIme=false)
I/ImeTracker(12299): com.example.flutter_quickstart_learn:6f9d52ef: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
D/VRI[MainActivity](12299): visibilityChanged oldVisibility=true newVisibility=false
I/flutter (12299): didHaveMemoryPressure
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.hidden
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.paused
D/ViewRootImpl(12299): Skipping stats log for color mode
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.hidden
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.resumed
D/InsetsController(12299): hide(ime(), fromIme=false)
I/ImeTracker(12299): com.example.flutter_quickstart_learn:58562098: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
I/uickstart_learn(12299): AssetManager2(0x71d909eda778) locale list changing from [] to [en-US]
3
I/flutter (12299): didChangeMetrics :宽:1440.0 高:2560.0
I/flutter (12299): didChangeMetrics :宽:2560.0 高:1440.0
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.resumed
D/InsetsController(12299): hide(ime(), fromIme=false)
I/ImeTracker(12299): com.example.flutter_quickstart_learn:96ce317d: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
2
I/flutter (12299): didChangeMetrics :宽:2560.0 高:1440.0
I/flutter (12299): didChangeMetrics :宽:1440.0 高:2560.0
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.inactive
I/flutter (12299): didChangeAppLifecycleState: AppLifecycleState.resumed
D/InsetsController(12299): hide(ime(), fromIme=false)
I/ImeTracker(12299): com.example.flutter_quickstart_learn:4398f924: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
AppLifecycleState.hidden
是什么
官方定义(Flutter 3.22
及以上版本):hidden
状态是 Flutter 在较新版本中引入的,表示应用程序当前对用户不可见,并且不会接收用户输入。这个状态通常在应用被完全隐藏时触发。它与 AppLifecycleState.inactive
和AppLifecycleState.paused
有所不同:
inactive
:应用处于非活动状态,但可能仍部分可见(例如在多窗口模式下或部分被覆盖)。paused
:应用完全在后台运行,但仍可能保持一些后台任务。hidden
:应用完全不可见,用户无法与之交互。
触发场景:
- 应用被切换到后台(例如用户按下 Home 键)。
- 设备屏幕被锁定。
- 应用被其他全屏应用覆盖(例如来电界面)。
用途:hidden
状态可以用来暂停某些资源密集型任务(例如动画、视频播放)或保存应用状态,以优化性能和电量消耗。
三者的主要区别总结
状态 | 可见性 | 用户交互 | 触发场景示例 | 典型处理 |
---|---|---|---|---|
inactive |
部分可见或不可见 | 不可交互 | 分屏模式、系统对话框、切换到后台过渡 | 暂停用户输入处理,保持部分渲染 |
paused |
完全不可见 | 不可交互 | 应用切换到后台(如按 Home 键) | 保存状态,暂停大部分任务 |
hidden |
完全不可见 | 不可交互 | 屏幕锁定、被全屏界面覆盖 | 暂停几乎所有活动,节省资源 |