Flutter 为什么走上了和前端一样的“百家争鸣”?

Flutter 为什么走上了和前端一样的“百家争鸣”?
在这里插入图片描述


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

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

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:掘金、知乎、ZEEKLOG、简书
创作特点:实战导向、源码拆解、少空谈多落地
文章状态:长期稳定更新,大量原创输出

我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”

持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱

文章目录

如果你写 Flutter 写到后期,大概率会有一种熟悉又不安的感觉:

“这个项目怎么越来越像前端了?”

State 到处飞、Provider 套 Provider、Riverpod 和 Bloc 各执一词,
社区天天在争:“谁才是 Flutter 最佳状态管理方案?”

而如果你做过前端,甚至 RN,你会发现——
这条路,其实我们已经走过一次了。

Flutter 和前端,为什么会一起“状态爆炸”

先说一个很现实的场景。

一个 Flutter 页面最开始可能只是:

  • 一个列表
  • 一个 loading
  • 一个点击事件
bool loading =false;List<Item> items =[];

一切都很简单。

但很快你会遇到:

  • 下拉刷新
  • 上拉分页
  • 局部 loading
  • 表单校验
  • 异步请求状态
  • 页面间数据共享

于是状态开始分裂:

页面状态 ├─ UI 状态(loading / error / empty) ├─ 业务状态(数据、筛选条件) ├─ 交互状态(是否选中、是否展开) ├─ 全局状态(用户、主题、权限) 

这一步,Flutter 和前端几乎完全一致

原因很简单:

Flutter 本质上是一个声明式 UI 框架,而声明式 UI 一定依赖状态驱动。

React、Vue、Flutter 都遵循同一件事:

UI = f(State)

状态一多,管理方式必然分歧,
于是就出现了——百家争鸣。

Provider / Riverpod / Bloc,本质上在解决什么问题

很多人把注意力放在“API 好不好用”上,其实忽略了核心。

这些方案本质都在回答三个问题:

  1. 状态放在哪里
  2. 谁可以修改状态
  3. UI 如何感知变化

Provider 的选择

Provider 的思路很直接:

  • 状态是对象
  • 通过 InheritedWidget 向下传递
  • notifyListeners 触发 rebuild
classCounterModelextendsChangeNotifier{ int count =0;voidincrement(){ count++;notifyListeners();}}
ChangeNotifierProvider( create:(_)=>CounterModel(), child:CounterPage(),);

好处很明显:

  • 简单
  • 上手快
  • 适合中小项目

但问题也很熟悉:

  • 状态和 UI 容易耦合
  • notifyListeners 粒度太粗
  • 大项目后期 rebuild 不可控

Riverpod、Bloc 为什么会出现

当项目复杂后,大家开始追求:

  • 更强的约束
  • 更明确的数据流
  • 更少的隐式依赖

于是:

  • Riverpod 把依赖关系显式化
  • Bloc 强制事件 → 状态的单向流动
  • Redux 更是走到极端:一切都不可变

这条演化路径,和前端一模一样

RN 项目后期的 Redux 地狱,其实不是 Redux 的错

很多人吐槽:

“Redux 太复杂了”

但真正的问题往往是:

  • 所有状态都进了 global store
  • 页面级状态也被当成全局状态
  • reducer 变成上帝文件
Redux Store ├─ user ├─ theme ├─ homePage ├─ detailPage ├─ modal ├─ tempFlags 

结果就是:

  • 改一个字段,半个项目受影响
  • Action 命名越来越抽象
  • 调试成本指数级上升

Flutter 项目里如果:

  • 所有状态都塞进 Riverpod global provider
  • 或 Bloc 被当成“万能状态容器”

结局是一样的。

Flutter 与前端状态模型的“同构性”

把技术名词全拿掉,其实两边模型几乎完全重合:

维度前端Flutter
UI 更新Virtual DOM DiffWidget rebuild
状态来源props / stateconstructor / provider
全局状态Redux / ZustandProvider / Riverpod
局部状态useStateStatefulWidget

差别只在表现形式,不在问题本质。

所以 Flutter 社区出现多种状态方案,并不是“乱”,
而是必然演化结果

那为什么 iOS 反而看起来更稳定?

再看 iOS。

MVC、MVVM,看起来好像没那么多争论。

原因不是 iOS 更先进,而是——限制更多

iOS 的几个天然约束

  1. UIKit 是命令式
  2. ViewController 生命周期强约束
  3. View 默认不自动响应状态变化
  4. 数据必须手动驱动 UI
funcupdateUI(){ label.text = viewModel.title }

你不调用,它就不变。

这其实强行压制了状态规模。

iOS 状态简单的“代价”

这种稳定是有代价的:

  • UI 更新需要手动维护
  • 容易出现状态不同步
  • 大量胶水代码
  • 重构成本高

所以 SwiftUI 出现后,
iOS 也开始重新面对 Flutter / React 遇到的问题。

状态复杂化,不是 Flutter 的问题,而是声明式 UI 的代价。

一个真正通用的状态分层方法论

不管 Flutter、前端还是 iOS,本质都可以用同一套分层思路。

UI State(页面私有)

  • loading
  • tab index
  • 是否展开

特点:

  • 生命周期 = 页面
  • 不跨页面共享

StatefulWidget / 局部 State 就够了。

Domain State(业务状态)

  • 列表数据
  • 表单内容
  • 筛选条件

特点:

  • 页面核心数据
  • 可测试
  • 与 UI 解耦

用 Provider / Riverpod / Bloc。

App State(全局状态)

  • 登录用户
  • 权限
  • 主题

特点:

  • 生命周期 = App
  • 变化频率低

严格限制数量,能少就少。

Flutter Demo:正确拆分状态的方式

业务状态(Domain State)

classTodoListModelextendsChangeNotifier{finalList<String> todos =[];voidaddTodo(String text){ todos.add(text);notifyListeners();}}

UI 层只关心展示

classTodoPageextendsStatefulWidget{@overrideState<TodoPage>createState()=>_TodoPageState();}class _TodoPageState extendsState<TodoPage>{ bool showInput =false;@overrideWidgetbuild(BuildContext context){final model = context.watch<TodoListModel>();returnColumn( children:[if(showInput)TextField( onSubmitted:(text){ model.addTodo(text);setState(()=> showInput =false);},),Expanded( child:ListView( children: model.todos .map((e)=>ListTile(title:Text(e))).toList(),),),FloatingActionButton( onPressed:(){setState(()=> showInput =true);},)],);}}

这里做对了三件事

  1. UI 状态不进 Provider
  2. 业务状态不关心 UI
  3. rebuild 范围可控

总结

Flutter 走上“百家争鸣”,不是因为它乱,
而是因为它走在了一条所有声明式 UI 都必须面对的路上

前端走过一次
RN 跌过一次
Flutter 正在经历
SwiftUI 也正在路上

真正重要的不是:

“你用的是 Provider 还是 Bloc?”

而是:

“你有没有想清楚,哪些状态该存在,哪些不该存在。”

Read more

Web Components跨框架组件库探索

1. 前言 在网约车业务早期阶段,产品需求迭代迅速,为了支持快速试错与灵活交付, 内部形成了多种技术栈并存的局面:历史项目基于 Vue2,新业务则转向 React。同时,由于早期各项目独立推进,尚未形成统一的设计规范和组件标准,不同项目在组件实现方式、样式规范与交互体验上存在较大差异。 这种多样化在短期内带来了灵活性,使团队能够快速响应业务需求,但随着项目规模和业务复杂度的增加,也逐渐演变成了技术挑战: * 组件复用困难:相同功能组件需要在不同框架中重复实现。 * 维护成本增加:功能或样式的调整须在多套组件库中分别修改。 * 用户体验不一致:不同框架实现可能导致交互和视觉风格不统一。 为解决这些问题,我们移动端前端团队今年开始探索一种能够“一次开发,多处复用”的组件库方案。 2. 目标与场景 2.1. 核心目标 为了解决团队多框架并存、组件重复开发和体验不一致的痛点,我们确定了三大核心目标: * 统一设计规范:建立标准化设计体系和组件规范,确保视觉风格与交互行为在各业务线、各技术栈中保持一致。 * 跨框架复用:构建框架无关的组件实现层,使同一组件可在 Vue

WebUI界面响应慢?优化前端缓存策略,加载速度提升50%

WebUI界面响应慢?优化前端缓存策略,加载速度提升50% 📌 问题背景:语音合成服务的用户体验瓶颈 在部署基于 ModelScope Sambert-Hifigan 的中文多情感语音合成服务后,尽管模型推理质量高、环境稳定,但在实际使用中发现:当用户频繁输入相似或重复文本时,WebUI界面仍会重新发起请求、等待后端合成音频,导致响应延迟明显,尤其在长文本场景下体验较差。 虽然项目本身已对依赖项(如 datasets==2.13.0、numpy==1.23.5、scipy<1.13)进行了深度兼容性修复,并通过 Flask 提供了稳定的 API 与 WebUI 双模式服务,但前端缺乏有效的缓存机制,使得相同内容的语音请求被反复处理,浪费计算资源且拖慢整体响应速度。 本文将围绕该语音合成系统的 WebUI 层面,提出一套轻量级前端缓存优化方案,实现相同文本请求的毫秒级响应,实测页面加载与播放延迟降低 50%以上。

Java Web 拦截机制实战指南:Filter 与 Interceptor 深度解析

一、理解核心概念 在 Java Web 开发中,过滤器(Filter)和拦截器(Interceptor)是两种核心的请求处理机制。它们虽然都能对请求进行拦截和处理,但定位截然不同: * Filter 是 Servlet 容器的"守门人",位于应用最外层 * Interceptor 是 Spring MVC 的"执法官",位于框架内部 二、Filter:Servlet 容器的第一道防线 2.1 本质与特点 Filter 是 Java Servlet 规范 定义的组件,由 Servlet 容器(如 Tomcat)直接管理,不依赖任何框架,

openclaw喂饭教程!在 Linux 环境下快速完成安装、初始化与 Web UI 配置

openclaw喂饭教程!在 Linux 环境下快速完成安装、初始化与 Web UI 配置

前言 OpenClaw 是一款开源的 AI Agent 工具,但对第一次接触的用户来说,完整跑通流程并不直观。本文以 Linux 环境为例,详细记录了 OpenClaw 的安装、初始化流程、模型选择、TUI 使用方式,以及 TUI 与 Web UI 认证不一致导致的常见问题与解决方法,帮助你最快速度把 OpenClaw 真正跑起来 环境准备 1)安装nodejs curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs > node