Flutter 状态中最该被写进规范的 5 条底线
《Flutter状态管理的5条团队底线》摘要 本文针对Flutter项目状态管理提出5条核心规范:1) 状态默认必须是局部的,未经证明不得全局化;2) 严格区分UI状态与业务状态;3) 被3个以上页面依赖的状态需重新评估;4) 全局状态不得直接控制Widget结构;5) 临时状态必须预设删除路径。这些规则旨在维护Widget树的可控性、状态依赖的可预测性,避免技术债务累积。作者强调Flutter状


大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:掘金、知乎、CSDN、简书
创作特点:实战导向、源码拆解、少空谈多落地
文章状态:长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
引言
很多 Flutter 项目,状态之所以会失控,并不是因为开发者不懂状态管理。
而是因为团队从一开始就没有明确:哪些事情是绝对不能做的。
Flutter 的状态设计,和代码风格、命名规范不一样——
它一旦走歪,后面几乎没有回头路。
所以,与其指望“后期重构”,不如在一开始就立几条不可突破的底线。
下面这 5 条,是我见过在中大型 Flutter 项目里,最值得写进团队规范、并且真的能救命的规则。
底线一:默认状态必须是「局部的」,而不是「可复用的」
在 Flutter 里,任何状态,在没有被证明需要共享之前,都不允许设计成全局状态。
错误的默认思维
final counterProvider = StateProvider<int>((ref) => 0);
理由往往是:
这个计数以后可能别的地方也会用到
先放全局,方便扩展
但真实情况通常是:
- 这个状态 90% 只在一个页面用
- 后续页面根本不需要
- 但你已经被迫围绕它写 UI 了
正确的团队共识
- 页面内状态 →
StatefulWidget - 页面生命周期状态 → 页面级 Provider
- 跨页面状态 → 必须写清楚「共享原因」
没有明确共享理由的状态,一律不准全局化。
如果团队只立一条状态规范,这一条就够了。
底线二:UI 状态,禁止混入业务状态模型
这是 Flutter 项目中最容易腐烂的一类状态。
常见错误示例
class OrderState {
final List<Order> orders;
final bool isLoading;
final bool showErrorToast;
final int selectedTabIndex;
}
乍一看很“完整”,但这里已经埋雷了:
orders→ 业务状态isLoading→ 业务流程状态showErrorToast→ UI 表现状态selectedTabIndex→ 页面交互状态
一旦混在一起,后果只有一个:
任何 UI 改动,都会牵动业务状态重建。
推荐的拆分方式
class OrderDataState {
final List<Order> orders;
}
class OrderUIState {
final bool isLoading;
final int selectedTabIndex;
}
或者更直接:
- 业务状态 → Provider / Bloc
- UI 状态 → Widget 内部
UI 状态必须允许随时被删除、替换、重写。
一旦它进了全局模型,就等于签了长期合同。
底线三:一个状态,被超过 3 个页面依赖,必须重新评估
这是一个非常实用的量化规则。
为什么是「3 个页面」
经验结论:
- 1 个页面 → 局部状态
- 2 个页面 → 可能是流程状态
- 3 个及以上 → 极有可能是“伪全局”
ref.watch(userProvider);
如果你发现:
- 首页在 watch
- 详情页在 watch
- 设置页也在 watch
那你就要停下来问一句:
这真的是“用户状态”,还是“偷懒的共享”?
团队层面的做法
规范里可以明确写:
-
任意 Provider 被 3 个页面依赖
-
必须在评审中说明:
- 生命周期
- 失效条件
- 是否可以下沉
这条规则,能极大抑制“顺手全局”的冲动。
底线四:状态一旦被用于 build 分支,就要承担结构稳定性责任
高风险写法
Widget build(BuildContext context) {
final mode = ref.watch(modeProvider);
if (mode == Mode.edit) {
return EditView();
} else {
return ReadView();
}
}
问题不在这段代码本身,而在于:
modeProvider一旦变化- Widget 结构整体切换
- 所有子树状态全部重建
如果这个 mode 是全局状态,那影响面会非常大。
团队约定应该是
- 全局状态禁止直接决定 Widget 树结构
- 只能影响「局部表现」
- 结构切换优先在页面内部消化
Widget build(BuildContext context) {
return PageScaffold(
child: ModeSwitcher(),
);
}
状态越靠近 Widget 根部,设计成本越高。
这是 Flutter 项目里最容易被低估的事实。
底线五:任何“临时状态”,必须有删除路径
这是防止状态系统“只增不减”的最后一道防线。
常见问题状态
- 引导弹窗是否展示过
- AB 测试标记
- 临时活动开关
- 灰度逻辑
final featureFlagProvider = StateProvider<bool>((ref) => true);
这种状态最危险的地方在于:
- 上线时很急
- 下线时没人管
- 最后永久留在系统里
团队必须强制的规则
-
所有临时状态:
- 必须标注用途
- 必须标注预期生命周期
- 必须标注删除条件
甚至可以在代码里直接写:
// TODO: remove after v2.3 promotion end
final promoFlagProvider = StateProvider<bool>((ref) => false);
没有删除计划的状态,本质上都是技术债。
这 5 条底线,真正保护的是什么?
它们保护的不是代码优雅,而是:
- Widget 树的可控性
- 状态依赖的可预测性
- 团队协作下的修改安全感
成熟的 Flutter 团队,状态设计看起来往往“很保守”。
不是因为能力不够,而是因为他们知道:
在 Flutter 里,每一个状态提升,都是一次不可逆的结构绑定。
总结
如果你只能在团队 Flutter 规范里写一句话,那应该是:
“状态不是工具,是长期承诺。”
写下这句话,很多灾难在发生之前,就已经被挡在门外了。
更多推荐
所有评论(0)