Flutter状态管理那些事儿:一个国企程序员的避坑指南

后端修仙人
2025-12-17 04:41
阅读 1733

上周五下班前,产品经理又在群里@我:“这个页面的状态逻辑能不能再优化一下?用户反馈切换 tab 有点卡。” 我看了眼时间——17:58,离打卡只剩两分钟。心里默默翻了个白眼,但还是回了个“好的,下个版本优化”。毕竟,在我们这种双休不加班(真的不加!)的国企,大家讲究的是“情绪稳定、按时下班、代码整洁”。

我是谁?一个混迹某省属信息集团三年的Flutter开发,VSCode插件装了快50个,最近一边刷LeetCode准备跳槽,一边被安排重构公司主力App的状态管理模块。说白了,就是领导看老项目用setState写得跟意大利面条一样,怕哪天线上崩了背锅,让我“优雅地重构一下”。

于是,就有了这篇技术分享。不为别的,就当给未来的面试官看看:我不仅会写业务逻辑,还能把状态管得明明白白。


事情是怎么开始的?

去年双11期间,我们App搞了个促销活动页。前端用Flutter快速搭了个界面,后端配合上了个简单的爬虫服务(对,你没看错,国企也搞爬虫,只是用来抓公开招标数据而已,合规得很)。结果上线第三天,测试小姐姐跑来跟我说:“用户切换筛选条件时,页面偶尔会闪回默认状态,而且内存占用飙到400MB!”

我打开DevTools一看——好家伙,每个筛选项都绑着setState,一改就全页面重建。更离谱的是,有些FutureBuilder嵌套三层,数据一变就触发多次网络请求。当时真的想砸电脑。

问题根源很明确:状态管理太随意。没人规定用什么方案,有人用Provider,有人直接setState,还有人硬生生用GlobalKey传状态……代码像拼夕夕缝合怪。


选型:别整花活,稳字当头

作为准备跳槽的人,我深知面试官最爱问“你们项目用什么状态管理?为什么选它?” 所以这次重构,我得选个业界主流 + 文档完善 + 团队能快速上手的方案。

对比了几个主流方案:

方案 学习成本 调试体验 适合场景 国企适配度
setState 极低 差(无追踪) 超简单UI ⭐(但别滥用)
Provider 良好 中小型项目 ⭐⭐⭐⭐
Riverpod 中高 优秀 中大型 ⭐⭐⭐
Bloc/Cubit 极佳 复杂状态流 ⭐⭐(团队抗拒写样板代码)

最后我拍板:Provider + 少量Riverpod混合使用。理由很简单:

  • 团队里新人多,Bloc那套event/state转换太重;
  • Provider是官方推荐,生态成熟,连pub.dev下载量都碾压;
  • 我自己也在刷题间隙研究过Riverpod,发现它解决了Provider的“BuildContext依赖”痛点,小模块可以用。

📌 真实心声:其实我想上MobX,但领导说“别整那些冷门的,万一你跳槽了谁维护?”


实战:怎么写才不被后人骂?

1. 分层设计,别把逻辑塞进Widget

以前的代码长这样(脱敏后):

class FilterPage extends StatefulWidget {
  @override
  _FilterPageState createState() => _FilterPageState();
}

class _FilterPageState extends State<FilterPage> {
  String selectedCategory = 'all';
  bool isLoading = false;
  
  void _fetchData() async {
    setState(() { isLoading = true; });
    final data = await http.get('/api/data?category=$selectedCategory');
    // ...处理数据
    setState(() { isLoading = false; });
  }
}

这代码在简历上写“独立负责XX模块”没问题,但实际维护起来想哭。现在我把它拆成三层:

  • Model层:定义数据结构和API调用(纯Dart,无Flutter依赖)
  • ViewModel层:用ChangeNotifier封装状态和业务逻辑
  • View层:只负责UI展示和用户交互

比如爬虫数据的展示模块:

// model/tender_model.dart
class TenderModel {
  final String title;
  final String url;
  TenderModel.fromJson(Map<String, dynamic> json)
      : title = json['title'],
        url = json['url'];
}

// viewmodel/tender_provider.dart
class TenderProvider with ChangeNotifier {
  List<TenderModel> _tenders = [];
  bool _isLoading = false;

  List<TenderModel> get tenders => _tenders;
  bool get isLoading => _isLoading;

  Future<void> fetchTenders(String keyword) async {
    _isLoading = true;
    notifyListeners(); // 先通知loading

    try {
      final response = await http.get('/crawler/tenders?q=$keyword');
      _tenders = (response['data'] as List)
          .map((e) => TenderModel.fromJson(e))
          .toList();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}

然后在main.dart里注册:

void main() {
  runApp(
    ChangeNotifierProvider<TenderProvider>(
      create: (_) => TenderProvider(),
      child: MyApp(),
    ),
  );
}

View层就清爽多了:

class TenderListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final provider = context.watch<TenderProvider>();
    
    return Scaffold(
      body: provider.isLoading 
        ? CircularProgressIndicator() 
        : ListView.builder(
            itemCount: provider.tenders.length,
            itemBuilder: (_, i) => ListTile(title: Text(provider.tenders[i].title)),
          ),
    );
  }
}

2. 处理跨页面状态同步

产品有个需求:在详情页点击“收藏”,首页列表对应项要实时更新图标。以前的做法是用Navigator.pop传值,或者全局变量,特别脆弱。

现在用Provider的selective rebuild特性:

// 在详情页
final tender = context.read<TenderProvider>().tenders[index];
onPressed: () {
  context.read<TenderProvider>().toggleFavorite(tender.id);
  // 不需要pop!状态自动同步
}

首页列表监听特定字段:

// 只在favorite状态变化时重建
final isFavorited = context.select<TenderProvider, bool>(
  (p) => p.isFavorited(tender.id),
);

性能直接起飞,再也不用担心用户狂点收藏导致页面卡顿。


坑与心得

  1. 别在build里创建Provider
    曾经手滑写了Provider(create: (_) => MyProvider())放在build方法里,结果每次setState都新建实例,状态全丢。血泪教训!

  2. 异步操作记得try-finally
    网络请求可能失败,但loading状态必须关掉,否则用户以为卡死了。我们吃过亏——去年有一次爬虫接口超时,页面loading转了30秒,被用户投诉到客服部。

  3. 测试!测试!测试!
    国企虽佛系,但上线前还是要过测试流程。我给Provider写了单元测试,mock API返回,验证状态变更。虽然花了半天,但避免了两次线上事故。

  4. 别过度设计
    有个同事非要在登录状态用Bloc,结果写了200行代码就为了存个token。我直接给他换成了SharedPreferences + Provider,10行搞定。记住:简单即正义


跳槽视角:状态管理是道送分题

最近面了几家大厂,几乎每轮都问状态管理。我直接掏出手机给他们看我们App的重构前后对比——内存占用从400MB降到180MB,帧率稳定60fps。面试官眼睛都亮了。

他们关心的不是你会多少框架,而是是否理解状态的本质

  • 状态应该可预测
  • 变更应该可追踪
  • 错误应该可恢复

这些,Provider+合理架构都能做到。


结语

在国企写代码,最大的好处是不用996,但挑战是如何在“稳定压倒一切”的文化里推动技术改进。这次状态管理重构,没动deadline,没加需求,纯粹是为了代码可维护性——结果产品反而夸我“用户体验提升了”。

所以啊,别觉得国企就是技术洼地。只要你想,哪怕用最朴实的Provider,也能写出让人眼前一亮的代码。

对了,如果你也在准备跳槽,不妨从重构一个小模块开始。面试时聊这个,比背八股文强多了。

(完)

P.S. 我们App已上架各大应用市场,搜“XX省公共资源交易”就能找到。欢迎体验,但别扒代码,爬虫接口有风控 😅

评论 0

最热最新
暂无评论
后端修仙人Lv.1
0
影响力
0
文章
0
粉丝