Flutter 状态管理选型:Provider、Riverpod、GetIt 对比

目录

  • 一、先说结论:它们并不是同一类东西
  • 二、Provider:最贴近 Flutter 原生心智
  • 三、Riverpod:更现代的响应式状态管理
  • 四、GetIt:更像依赖注入容器,不是完整状态管理方案
  • 五、核心维度横向对比
  • 六、实际项目怎么选
  • 七、常见误区
  • 八、总结

一、先说结论:它们并不是同一类东西

很多人在 Flutter 里做技术选型时,会把 Provider / Riverpod / GetIt 放在一起比较,但它们其实不完全是同一层的东西。

  • Provider:基于 InheritedWidget 的状态注入与订阅方案,和 Flutter widget tree 绑定很深
  • Riverpod:从 Provider 演化出来的独立响应式状态管理框架,不依赖 BuildContext
  • GetIt:本质是 Service Locator / 依赖注入容器,负责“拿对象”,不天然负责“驱动 UI 刷新”

所以如果一句话概括:

  • Provider 适合简单到中等复杂度应用
  • Riverpod 更适合中大型 Flutter 项目
  • GetIt 更适合做依赖管理,单独拿来做状态管理通常不够完整

二、Provider:最贴近 Flutter 原生心智

Provider 的核心思路其实很朴素:

  1. 把状态放到 widget tree 上层
  2. 通过 Provider 往下提供对象
  3. 通过 Consumer / context.watch 订阅变化
  4. 通过 notifyListeners() 触发刷新

它最大的优点是:非常符合 Flutter“状态上提”的原生思维

class CounterModel extends ChangeNotifier {
  int count = 0;

  void increment() {
    count++;
    notifyListeners();
  }
}
ChangeNotifierProvider(
  create: (_) => CounterModel(),
  child: const MyApp(),
)
Consumer<CounterModel>(
  builder: (_, model, __) {
    return Text('${model.count}');
  },
)

Provider 的优点

  • 上手成本低,概念少
  • 和 Flutter 官方文档教程非常一致
  • 对小项目、Demo、后台配置页很友好
  • 基于 BuildContext,写法比较直观

Provider 的问题

  • 强依赖 widget tree 和 BuildContext
  • 复杂依赖关系会逐渐变乱
  • 异步状态、缓存、错误态需要自己组织
  • ChangeNotifier 是可变状态,规模大时容易失控
  • 测试和模块解耦能力不如 Riverpod

Provider 适合什么场景

  • Flutter 初学者
  • 页面不多、状态关系简单的项目
  • 团队成员都熟悉 ChangeNotifier
  • 更强调“够用”和低心智负担,而不是极致可维护性

三、Riverpod:更现代的响应式状态管理

Riverpod 可以理解成“把 Provider 这套思想,从 widget tree 中解耦出来,再做了一次现代化升级”。

它和 Provider 最大的区别有两个:

  1. 不依赖 BuildContext
  2. 不仅管理状态,还管理依赖、异步、缓存和生命周期

最常见的用法是 ProviderScope + ref.watch/ref.read

final counterProvider = StateProvider<int>((ref) => 0);
class CounterPage extends ConsumerWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      body: Center(child: Text('$count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).state++;
        },
      ),
    );
  }
}

Riverpod 的核心优势

1. 不依赖 BuildContext

这点非常关键。

在 Provider 里,很多读取依赖和状态的动作都发生在 widget tree 中;但在 Riverpod 里,provider 本身就是独立节点。这样做有几个直接收益:

  • 业务逻辑不容易和 UI 强绑定
  • 更容易单元测试
  • 更容易拆模块
  • 更适合纯 Dart 层复用

2. 异步状态是一等公民

Riverpod 对异步场景支持非常自然,FutureProviderStreamProviderAsyncNotifier 都是围绕异步建模的。

final userProvider = FutureProvider<User>((ref) async {
  return api.fetchUser();
});

UI 层天然就是 loading / data / error 三态:

final user = ref.watch(userProvider);

return switch (user) {
  AsyncData(:final value) => Text(value.name),
  AsyncError(:final error) => Text('error: $error'),
  _ => const CircularProgressIndicator(),
};

这比 ChangeNotifier + bool isLoading + Object? error 清晰很多。

3. 依赖关系是显式的

一个 provider 依赖另一个 provider 时,关系写在代码里,而不是散落在构造器、context 或全局单例里。

final apiProvider = Provider<ApiClient>((ref) => ApiClient());

final userRepoProvider = Provider<UserRepository>((ref) {
  return UserRepository(ref.watch(apiProvider));
});

这对中大型项目特别重要,因为状态图会逐渐复杂,显式依赖比“哪里都能拿”更容易维护。

4. 生命周期可控

autoDispose、缓存、重建、监听、失效这些能力,Riverpod 都有比较完整的机制。

它不只是“把状态塞给 UI”,而是把“状态什么时候创建、什么时候销毁、什么时候刷新”也纳入了框架。

Riverpod 的代价

  • 学习曲线高于 Provider
  • provider 种类多,初看会有一点抽象
  • 团队如果不统一规范,写法容易分裂
  • 引入代码生成后,体验更强,但工程复杂度也会上来

Riverpod 适合什么场景

  • 中大型 Flutter 项目
  • 异步请求多、状态关系复杂的应用
  • 想提高可测试性和可维护性
  • 希望业务逻辑和 UI 解耦更干净

四、GetIt:更像依赖注入容器,不是完整状态管理方案

GetIt 的定位和前两个不一样。

它解决的问题不是“状态怎么响应式传播”,而是:

  • 某个 service / repository / use case 从哪里拿
  • 生命周期怎么管理
  • 如何避免层层传参

典型写法如下:

final getIt = GetIt.instance;

void setupDI() {
  getIt.registerLazySingleton<ApiClient>(() => ApiClient());
  getIt.registerLazySingleton<UserRepository>(
    () => UserRepository(getIt<ApiClient>()),
  );
}
final repo = getIt<UserRepository>();

GetIt 的优点

  • 简单直接,注册一次,全局可取
  • 不依赖 BuildContext
  • 依赖注入体验好,适合 service/repository 层
  • 对纯 Dart、CLI、服务端代码也能复用
  • 测试替换依赖相对方便

GetIt 的问题

1. 它不是完整状态管理

这是最容易误解的点。

GetIt 只负责“拿对象”,并不天然解决以下问题:

  • UI 什么时候刷新
  • 加载中/错误态怎么表达
  • 状态依赖关系怎么追踪
  • 页面销毁后资源怎么自动释放

所以你如果直接用 GetIt + 单例对象 管整个 UI 状态,项目一大很容易变成“全局变量升级版”。

2. 全局访问很爽,但边界容易变模糊

任何地方都能 getIt<T>(),短期开发效率很高;但从长期维护看,也意味着:

  • 依赖来源不够显式
  • 模块边界容易被打穿
  • 阅读代码时不容易一眼看出依赖图

3. 响应式能力通常要靠别的东西补

很多团队实际用法不是“只用 GetIt”,而是:

  • GetIt + ValueNotifier
  • GetIt + watch_it
  • GetIt + BLoC
  • GetIt + Riverpod

也就是说,GetIt 更像依赖管理底座,而不是单独扛整个状态体系。

GetIt 适合什么场景

  • 需要做依赖注入
  • 业务层和数据层对象很多
  • 希望摆脱 BuildContext 取依赖
  • 已经有自己的状态管理方案,只是缺一个 DI 容器

五、核心维度横向对比

维度ProviderRiverpodGetIt
定位状态注入 + UI 订阅响应式状态管理 + 依赖管理Service Locator / 依赖注入
是否依赖 BuildContext
是否天然驱动 UI 刷新
异步状态支持一般,需要自己封装很强,原生支持 AsyncValue弱,需要额外方案
依赖关系是否显式一般较弱
学习成本
可测试性中到高
适合项目规模小到中中到大作为 DI 可用于各种规模
常见问题ChangeNotifier 膨胀概念偏多滥用全局单例

如果只给一句话评价:

  • Provider:最容易上手,但上限相对低
  • Riverpod:最均衡,工程化能力最强
  • GetIt:不是状态管理替代品,而是 DI 工具

六、实际项目怎么选

1. 小项目 / 个人项目 / 快速验证

优先考虑 Provider

原因很简单:

  • 学习成本最低
  • 和 Flutter 官方示例一致
  • 对几页表单、列表、详情页完全够用

2. 中大型项目 / 长期维护项目

优先考虑 Riverpod

尤其是下面这些情况:

  • 页面多
  • 接口多
  • 缓存和异步逻辑复杂
  • 团队多人协作
  • 很重视测试

这类项目里,Riverpod 的优势会越来越明显。

3. 你只是想解决依赖注入

那就用 GetIt,但不要指望它一个包解决所有状态问题。

比较合理的定位是:

  • GetIt 负责 service/repository/usecase 注入
  • 状态仍然交给 Provider / Riverpod / BLoC 这类方案

4. 我个人更推荐的组合

如果是今天新开一个 Flutter 中大型项目,我更倾向于:

  • 首选:Riverpod
  • 可选:Riverpod + GetIt(二选一也行)

为什么说 “Riverpod + GetIt 可选”?

因为 Riverpod 本身已经能做依赖管理了,很多项目里其实不一定还需要 GetIt。
如果再叠一层 GetIt,收益未必大,但复杂度会增加。

所以更常见的建议是:

  • 简单点:直接全用 Riverpod
  • 老项目迁移:保留 GetIt 做 DI,上层逐步换 Riverpod

七、常见误区

误区 1:GetIt 比 Provider/Riverpod 更高级

不是。

它们不是“高级版/低级版”的关系,而是不同定位。

GetIt 解决的是对象获取,Provider/Riverpod 解决的是响应式状态传播

误区 2:Provider 已经过时

也不是。

Provider 依然很好用,尤其适合入门和简单项目。
只是当项目复杂度上来后,Riverpod 往往更容易维护。

误区 3:Riverpod 一定要代码生成

不一定。

代码生成会让体验更完整,但不是必须。
只是如果项目已经走向中大型,通常最后都会逐渐用上。

误区 4:一个项目里绝不能混用

也不绝对。

现实里很多项目会是:

  • 历史代码用 Provider
  • 新模块用 Riverpod
  • 基础设施层用 GetIt

混用不是原罪,没有边界和规范才是问题

八、总结

如果你只想记住最核心的选型建议,可以直接记这三句:

  • 新手、小项目:Provider
  • 中大型、长期维护:Riverpod
  • 依赖注入:GetIt,但别把它当完整状态管理

从 2026 年的 Flutter 生态来看,如果你问我“新项目最稳妥的默认答案是什么”,我的答案会是:

优先用 Riverpod。

它在可维护性、异步建模、测试友好、UI 解耦这些方面,整体平衡是最好的。
Provider 依然适合简单项目;GetIt 依然适合做 DI;但如果只能选一个更适合长期演进的状态体系,Riverpod 通常会是更稳的选择。