外观
生命周期与状态管理
Widget 是配置,State 保存变化
StatelessWidget 和 StatefulWidget 都是不可变配置。前者并不意味着「只构建一次」;父级更新或依赖变化都可能让它重新构建。StatefulWidget 把需要跨多次构建保留的可变信息交给 State。
构建函数负责根据当前配置与状态返回 UI。避免在 build 中发请求、创建需要释放的控制器或修改状态,因为它可能频繁执行。
生命周期速查
| 方法 | 何时执行 | 典型用途 |
|---|---|---|
initState | 每个 State 初始化时一次 | 创建控制器、启动不依赖继承查找的初始化 |
didChangeDependencies | 初始化后及依赖变化时 | 响应通过 context 建立的继承依赖 |
build | 首次展示及后续重建 | 用当前状态描述界面 |
didUpdateWidget | 同位置复用且配置更新时 | 比较新旧参数,更新订阅或数据源 |
deactivate | 暂时从树中移除时 | 处理与树位置相关的连接,之后可能重新插入 |
dispose | State 永久移除时 | 取消订阅并释放控制器 |
异步返回后,使用当前 State 前检查 mounted;使用跨异步间隔保存的 BuildContext 前检查对应的 context.mounted。同时尽可能取消无用任务:挂载检查不会停止网络请求,也不能解决旧请求覆盖新请求的问题。
控制器由谁创建,通常就由谁释放。父级传入的控制器,不应由子组件擅自销毁。生命周期行为参见 State API。
实例:局部搜索状态
下面是独立页面。添加 import 'package:flutter/material.dart';,放入 Flutter 应用中,并设为 MaterialApp 的 home。
dart
class SearchNotesPage extends StatefulWidget {
const SearchNotesPage({super.key});
@override
State<SearchNotesPage> createState() => _SearchNotesPageState();
}
class _SearchNotesPageState extends State<SearchNotesPage> {
final _search = TextEditingController();
static const _titles = ['Flutter 布局', 'Dart 空安全', 'Flutter 状态'];
String _keyword = '';
@override
void dispose() {
_search.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final visible = _titles
.where((title) => title.toLowerCase().contains(_keyword))
.toList();
return Scaffold(
appBar: AppBar(title: const Text('搜索笔记')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: TextField(
controller: _search,
decoration: const InputDecoration(labelText: '关键字'),
onChanged: (value) {
setState(() => _keyword = value.trim().toLowerCase());
},
),
),
Expanded(
child: visible.isEmpty
? const Center(child: Text('没有匹配的笔记'))
: ListView.builder(
itemCount: visible.length,
itemBuilder: (context, index) => ListTile(
title: Text(visible[index]),
),
),
),
],
),
);
}
}setState 的回调应同步完成状态赋值,异步操作放在回调外。只有一个页面关心搜索词时,将状态留在页面内即可;列表可以从原始数据和关键字推导出来,无需再保存第二份可变列表。
共享状态如何选择
| 范围 | 可以从什么方案开始 | 关键边界 |
|---|---|---|
| 单个组件 | setState | 更新范围靠近变化处 |
| 单个可观察值 | ValueNotifier + ValueListenableBuilder | 集合更新要留意通知语义,优先赋予新值 |
| 父子协作 | 构造参数传值、回调传事件 | 父级持有唯一状态来源 |
| 跨层依赖 | InheritedWidget 或状态管理库 | 明确哪些组件订阅哪些数据 |
| 复杂业务 | ViewModel / Controller + Repository | UI 状态和数据访问分开,便于测试 |
ValueListenableBuilder 会监听值的变化并重新构建相关区域,参见 API 文档。是否引入 Provider、Riverpod 或 Bloc,应结合团队已有方案、测试方式与复杂度决定,不必在入门页同时引入所有库。
状态分层可参考 官方应用架构指南:视图呈现状态、处理用户意图,数据访问交给独立对象。分层的目的,是让同一条业务规则只有一个维护位置。
Key 与状态复用
列表会插入、删除、重排时,为有状态的条目提供稳定的 ValueKey(note.id),帮助框架对应逻辑上同一条数据。不要使用会随顺序变化的索引充当业务身份。
GlobalKey 用于确实需要全局身份或访问特定 State 的场景,不是普通状态共享的替代品。三棵树如何配合复用,见 框架原理。
练习与验收
输入 flutter 应命中两条,输入不存在的内容应出现空状态,清空后恢复全部列表。反复进入和退出页面,确认没有控制器或订阅重复创建后未释放的问题。