
子玥酱
(掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、CSDN、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨ 👋 如果你正在做前端,或准备长期走前端这条路 📚 关注我,第一时间获取前端行业趋势与实践总结 🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构) 💡 一起把技术学“明白”,也用“到位”
持续写作,持续进阶。 愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 引言
- 全局状态不是“方便”,而是“承诺”
- 页面 UI 状态,被误判为“业务状态”
-
- 常见表现
- 为什么容易被误判?
- 实际问题在哪?
- 更合理的写法
- 网络请求状态,被当成“共享数据源”
-
- 常见写法
- 为什么看起来合理?
- 真正的问题是什么?
- 更合理的拆分方式
-
- 数据能力层(可全局)
- 页面状态层(页面私有)
- 表单状态,被错误抽象成全局模型
-
- 常见写法
- 实际后果
- 更健康的方式
- 权限 / 可见性状态,被无限放大
-
- 常见写法
- 问题不在权限,而在粒度
- 更健康的方式
- 临时业务中间态,被当成长期状态
-
- 常见例子
- 为什么危险?
- 一个非常实用的判断清单
- 总结
引言
在 Flutter 项目里,很多“状态失控”的问题,并不是一开始就选错了状态管理方案,而是第一步就把状态放错了位置。
你回头看那些变复杂的项目,往往会发现一个共同点:
一些本该只活在页面里的状态,被过早地、理直气壮地放进了全局。
而一旦进了全局,就几乎再也回不去了。
这篇文章就来拆一拆: 在 Flutter 项目中,哪些状态最容易被误判为“全局状态”,以及为什么它们迟早会成为维护成本的源头。
全局状态不是“方便”,而是“承诺”
在开始列清单之前,先明确一件事。
当你把一个状态放进全局(Provider / Riverpod / Bloc / Redux),你其实是在做一个长期承诺:
- 这个状态可能被很多地方依赖
- 生命周期长于页面
- 每一次修改,都要考虑历史使用者
也就是说:
全局状态不是“好不好用”的问题,而是“值不值得长期背”的问题。
如果一个状态不值得你为它承担这些代价,它就不应该是全局的。
页面 UI 状态,被误判为“业务状态”
这是最常见、也是最隐蔽的一类。
常见表现
- 当前 Tab 索引
- 列表选中项
- 是否展开 / 折叠
- 是否展示某个浮层
你很可能见过这样的代码:
final selectedTabProvider = StateProvider<int>((ref) => 0);
然后在多个 Widget 中:
final tab = ref.watch(selectedTabProvider);
为什么容易被误判?
因为它们看起来像是:
- 多个 Widget 都要用
- 切换时要同步
- 不想一层层传参数
于是很自然地进了全局。
实际问题在哪?
这类状态的真实生命周期其实是:
页面存在 → 状态有意义 页面销毁 → 状态应该消失
但一旦进了全局,就会出现:
- 页面退出,状态还在
- 再次进入页面,状态被“继承”
- 用户看到的不是初始 UI,而是上次残留状态
这类问题往往被描述成:
“偶现 UI 错乱” “复现不了的展示问题”
问题不是 bug,而是状态语义错位。
更合理的写法
UI 行为状态,默认就应该是页面私有:
class _PageState extends State<Page> {
int selectedTab = 0;
bool expanded = false;
}
或者如果你使用 ViewModel:
class PageViewModel extends ChangeNotifier {
int selectedTab = 0;
bool expanded = false;
}
只有当你非常确定:
- 它跨页面
- 有明确业务含义
- 生命周期需要延长
才考虑把它提升为全局状态。
网络请求状态,被当成“共享数据源”
这一类在 Flutter 项目中非常高发。
常见写法
final userListProvider =
StateNotifierProvider<UserListNotifier, AsyncValue<List<User>>>(
(ref) => UserListNotifier(),
);
然后:
- A 页面触发加载
- B 页面直接 watch
- C 页面修改列表
看起来非常“干净”。
为什么看起来合理?
因为从接口视角看:
- 数据来自同一个 API
- 多个页面确实要展示
但这里有一个非常关键的误区:
接口一致 ≠ 状态一致
真正的问题是什么?
网络请求状态,通常是:
- 被页面驱动
- 和当前 UI 上下文强绑定
- 和加载时机、错误处理强相关
一旦把它做成全局,就会出现这些问题:
- A 页面 loading,B 页面也 loading
- A 页面失败,B 页面跟着 error
- 谁刷新了数据,完全不可控
尤其在分页、筛选、多条件场景下,问题会指数级放大。
更合理的拆分方式
应该拆成两层:
数据能力层(可全局)
class UserRepository {
Future<List<User>> fetchUsers() async {
...
}
}
页面状态层(页面私有)
class UserListViewModel extends ChangeNotifier {
bool loading = false;
List<User> users = [];
Future<void> load() async {
loading = true;
notifyListeners();
users = await repository.fetchUsers();
loading = false;
notifyListeners();
}
}
全局共享的是“能力”,不是“状态结果”。
表单状态,被错误抽象成全局模型
表单是状态膨胀的重灾区。
常见写法
final formProvider =
StateProvider<FormData>((ref) => FormData());
理由通常是:
- 字段多
- 步骤复杂
- 后面页面还要用
实际后果
你会慢慢遇到这些问题:
- 页面返回,表单没清空
- 再进流程,数据是旧的
- 调试时完全不知道数据从哪来的
表单状态,本质上是:
一次用户操作过程的临时状态
它不具备长期存在的价值。
更健康的方式
- 表单状态跟随流程对象
- 流程结束立即销毁
- 最终结果再转换为业务模型
class FormFlowState {
final String name;
final int age;
}
全局状态不应该保存“草稿”。
权限 / 可见性状态,被无限放大
这一类通常在项目中后期出现。
常见写法
final permissionProvider =
Provider<PermissionState>((ref) => ...);
然后 UI 中到处是:
if (ref.watch(permissionProvider).canEdit) ...
问题不在权限,而在粒度
权限本身没问题,问题在于:
- 权限是用户维度
- 可见性是页面语义
当你直接用全局权限控制 UI:
- 页面逻辑被“外部条件”劫持
- 组件复用性下降
- UI 规则散落各处
更健康的方式
- 全局只保留原始权限数据
- 页面内做 UI 决策
final canEdit = permission.canEdit && pageState.isEditable;
让 UI 感知权限,但不依赖权限。
临时业务中间态,被当成长期状态
这是最危险、也最隐蔽的一类。
常见例子
- 当前流程第几步
- 是否已确认
- 某弹窗是否展示过
一开始只是临时需求,后来却演化成:
final flowStateProvider =
StateProvider<FlowState>((ref) => FlowState());
为什么危险?
因为这类状态:
- 强依赖具体流程
- 生命周期不稳定
- 复用价值极低
但一旦全局化,就会:
- 被其他流程“顺手复用”
- 成为隐藏耦合点
- 很难安全删除
一个非常实用的判断清单
在你准备把一个状态放进全局前,可以问自己这 5 个问题:
只要有 2 个问题答不上来, 它大概率就不该是全局状态。
总结
Flutter 项目中的状态失控,很少是突然发生的,而是:
一个个“看起来合理”的全局状态,慢慢堆出来的。
真正拉开项目差距的从来不是:
- 用 Provider 还是 Riverpod
- 用 Bloc 还是 Redux
而是你能不能在一开始就分清:
哪些状态值得被长期承担, 哪些状态应该尽快销毁。
网硕互联帮助中心






评论前必须登录!
注册