Flutter 状态管理极速版:view_model

1,436 阅读5分钟

view_model

一切皆 ViewModel。

这是一个为 Flutter 量身定制的状态管理框架。它基于“类型键(Type-keyed)实例注册表”构建,并自带“自动引用计数”的生命周期管理系统。无需繁琐的初始化,真正做到按需创建,自动销毁

dependencies:
  view_model: ^15.0.0

📖 核心目录


🌟 为什么选择 view_model?

在 Flutter 状态管理的丛林里,你可能被 Providercontext 限制搞晕,或者被 Riverpod 复杂的 Provider 依赖图劝退。view_model 的设计哲学是:直觉化、Dart 原生感、零痛苦。

  • 真正的自动生命周期:ViewModel 的存活完全取决于是否有 Widget 在用它。没人用了?自动销毁,一行代码都不用写。
  • 跨越 BuildContext 的自由:不仅仅在 Widget 里,在后台服务、启动逻辑、纯 Dart 类中都能享用同样的 ViewModel 管理逻辑。
  • 自带“防卡顿”光环:当页面进入后台或被上层路由覆盖时,系统会自动暂停通知,仅在页面恢复时触发一次追赶式刷新。
  • 极致的代码生成:配合 @GenSpec 注解,样板代码归零。

🏗️ 三层架构设计

为了实现极致的灵活性,我们将系统拆分为三层:

  1. 消费者层 (Widget/Consumer): 提供 ViewModelStateMixinViewModelBuilder 等贴心的工具。
  2. 绑定层 (ViewModelBinding): 核心桥梁。它负责记录谁(哪个 BindingID)在使用哪个 ViewModel。它还掌管着 Zone 依赖注入和 暂停/恢复 状态。
  3. 实例管理层 (InstanceManager): 一个高效的底盘。它维护着一个实例池,并根据引用计数(BindingIDs 是否为空)决定实例的死活。

🧩 核心武器:两大 Mixin

这是本库的灵魂。只要能掌握这两个 Mixin,你就掌握了全部。

1. with ViewModel — 赋予“生命”

将它混入任意类,这个类就变成了受管实例。它拥有生命周期钩子(onCreate, onDispose 等),能够发射通知,还能通过 viewModelBinding 直接读取其他依赖项。

class UserRepository with ViewModel { /* 业务逻辑 */ }

2. with ViewModelBinding — 获取“力量”

将它混入类(不限 Widget),这个类就变成了管理员。它拥有了访问注册表的能力。你可以用它来 watchread 任何 ViewModel。ViewModelStateMixin 本质上就是它的一个 Widget 封装版。

class AppBootstrap with ViewModelBinding {
  Future<void> init() async {
    // 跨越 context 自由读取
    await viewModelBinding.read(configSpec).load();
  }
}

🚀 3 分钟快速上手

import 'package:view_model/view_model.dart';

// 1. 写逻辑
class CounterViewModel with ViewModel {
  int count = 0;
  void increment() => update(() => count++); // update 会自动帮你触发 UI 刷新
}

// 2. 定规格 (Spec)
final counterSpec = ViewModelSpec<CounterViewModel>(
  builder: () => CounterViewModel(),
);

// 3. 混入 Mixin 即可使用
class CounterPage extends StatefulWidget {
  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> with ViewModelStateMixin {
  // watch 会建立连接:ViewModel 变了,当前 Widget 自动刷新
  late final vm = viewModelBinding.watch(counterSpec);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: vm.increment,
      child: Text('Count: ${vm.count}'),
    );
  }
}

📖 ViewModel 深度探索

StateViewModel(强状态版)

如果你追求不可变状态(配合 Freezed 简直完美),它是你的不二之选。它能记录 previousState,并支持字段级的差异化监听(listenStateSelect)。

class UserViewModel extends StateViewModel<UserState> {
  UserViewModel() : super(state: const UserState());

  void updateName(String name) {
    setState(state.copyWith(name: name)); // 自动触发 equals 比较
  }
}

资源快捷回收

在构造函数里使用 addDispose(),确保资源不遗忘:

StreamViewModel() {
  final sub = stream.listen((_) => notifyListeners());
  addDispose(() => sub.cancel()); // 跟着 VM 一起死,优雅!
}

🔗 viewModelBinding 核心接口

当你拥有了 viewModelBinding 访问器,你就拥有了以下超能力:

方法使用场景特点
watch(spec)在 Widget 的 build 或逻辑中响应式:VM 变化会触发 UI 刷新。若 VM 不存在则创建。
read(spec)事件回调、只需调用方法时非响应式:仅读取,不监听。若 VM 不存在则创建。
watchCached(key/tag)寻找现有的单例或共享 VM如果缓存里没找到,它会抛出异常。
listenStateSelect(...)针对性监听某个字段例如:只有 user.age 变了才弹窗,别的字段变了不理。
recycle(vm)强制销毁重来比如:退出登录时,一键回收所有用户相关的 VM。

💤 智能 暂停 / 恢复 机制

这是 view_model 的独门绝技。

  • 不浪费一分性能:当你的页面处于“不可见”状态(被覆盖、Tab 被切走、应用退后台),哪怕 ViewModel 疯狂更新,你的 Widget 也完全不会 rebuild
  • 丝滑追赶:当你重新看到页面的一瞬间,系统会帮你做一次补报刷新,确保数据是最新的。

提示:为了让路由感知生效,别忘了在 MaterialApp 里加上 ViewModel.routeObserver


🤖 代码自动生成

厌倦了手写 ViewModelSpec?没关系,交给 view_model_generator

@GenSpec(key: 'global_counter', aliveForever: true) // 一键定义单例
class CounterViewModel with ViewModel { ... }

一行命令,生成的 Spec 自动帮你搞定参数注入和单例配置。


🔍 DevTools 视觉化窗口

我们为你准备了强大的 DevTools 扩展。在调试模式下,打开 Flutter DevTools:

  • 可视化依赖图:一眼看清哪个 Widget 绑定了哪个 ViewModel,谁又依赖了谁。
  • 状态实时监控:在不需要打印日志的情况下,直接在浏览器里检视所有存活实例的数据。

🌏 关于本地化支持

本项目提供极其稳定的兼容支持:

  • vef 曾经是我们的核心概念名,虽然现在已更名为 viewModelBinding,但目前的版本依然支持 vef 变量及 with Vef
  • ViewModelProvider 现在改名为 ViewModelSpec,我们依然为你留了兼容别名。

📜 开源协议

本项目采用 Apache License 2.0

如果你觉得好用,请给个 Star 🌟,这是对作者最大的支持!