在这里插入图片描述
在这里插入图片描述

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、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 规范里写一句话,那应该是:

“状态不是工具,是长期承诺。”

写下这句话,很多灾难在发生之前,就已经被挡在门外了。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐