Flutter 状态管理极速版:view_model

在 Flutter 开发领域,状态管理框架层出不穷,Provider、Riverpod、Bloc、Signal 和 Fish Redux 等都是其中的佼佼者。然而,这些框架在实际使用过程中,普遍存在操作复杂的问题,并且过度关注局部状态的更新。

  • Provider:该框架仅支持在 Widget 树的上下层级间共享状态。这就导致处于同级树结构,比如不同路由页面之间,无法直接共享状态。开发者往往只能将状态提升到 Application 层的 Widget 树中进行管理。
  • Bloc 和 Signal:这两个框架的状态共享机制基于 Provider 构建,因此同样存在上述共享范围受限的问题。
  • Riverpod:为解决状态共享问题,Riverpod 舍弃了 Provider 的实现,自行开发了单例管理器,实现了状态的全局创建与销毁管理。但在实际使用中,它仍需依赖一层 Provider 来创建状态,即便官方提供了注解方式简化操作,整体流程依然较为繁琐。

回归初心,一个 ui 的状态管理应该具备哪些功能?

  • 存储 view 的状态
  • 声明式 ui,必须要有数据的监听
  • 可以在 Widget 之间共享
  • 可以跟着 Widget 自动销毁

局部的状态更新真的重要吗?

我们都知道 flutter 3 层树结构, Wiget 就是配置而已。 我们只要恰当的重写 State 的 didUpdate(StatefulWiget old),利用 Widget 自身的 diff 机制就不会有性能开销。每次 setState 不过是多了几个或者几十个 Widget 对象的内存开销而已,并且很快会被 gc 回收掉,实际的性能损坏对于大部分机器来说忽略不计。

基于此,一个轻量版的 view_model 如下:

view_model

  • 简单且轻量级。
  • 无复杂机制,基于StreamControllersetState实现。
  • 自动释放资源,遵循Statedispose方法。
  • 可在任意有状态组件(StatefulWidget)间共享。

ViewModel仅绑定到有状态组件(StatefulWidget)的State上。我们不建议将状态绑定到无状态组件(StatelessWidget)上,因为无状态组件不应有状态。

核心概念

  • ViewModel:存储状态并在状态改变时发出通知。
  • ViewModelFactory:指导如何创建你的ViewModel
  • getViewModel:创建或获取已存在的ViewModel
  • listenViewModelStateChanged:监听Widget.State内的状态变化。

使用方法

添加依赖

yaml 复制代码
view_model:
  git:
    url: https://github.com/lwj1994/flutter_view_model
    ref: 0.0.1

定义ViewModel

dart 复制代码
import "package:view_model/view_model.dart";

class MyViewModel extends ViewModel<String> {
  MyViewModel({
    required super.state,
  }) {
    debugPrint("创建 MyViewModel,状态: $state,哈希码: $hashCode");
  }

  void setNewState() {
    setState((s) {
      return "hi";
    });
  }

  @override
  void dispose() async {
    super.dispose();
    debugPrint("释放 MyViewModel,状态: $state,哈希码: $hashCode");
  }
}

class MyViewModelFactory with ViewModelFactory<MyViewModel> {
  final String arg;

  MyViewModelFactory({this.arg = ""});

  @override
  MyViewModel build() {
    return MyViewModel(state: arg);
  }
}

在组件中使用ViewModel

dart 复制代码
import "package:view_model/view_model.dart";

class _State extends State<Page> with ViewModelStateMixin<Page> {
  // 建议使用getter来获取ViewModel
  MyViewModel get viewModel =>
      getViewModel<MyViewModel>(factory: MyViewModelFactory(arg: "初始参数"));

  // 获取ViewModel的状态
  String get state => viewModel.state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          viewModel.setNewState();
        },
        child: Icon(Icons.add),
      ),
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () {
            appRouter.maybePop();
          },
        ),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text("mainViewModel.state = ${_mainViewModel.state}"),
          Text(
            state,
            style: const TextStyle(color: Colors.red),
          ),
          FilledButton(
              onPressed: () async {
                refreshViewModel(_mainViewModel);
              },
              child: const Text("刷新mainViewModel")),
          FilledButton(
              onPressed: () {
                debugPrint("页面的MyViewModel哈希码 = ${viewModel.hashCode}");
                debugPrint("页面的MyViewModel状态 = ${viewModel.state}");
              },
              child: const Text("打印MyViewModel")),
        ],
      ),
    );
  }
}

共享ViewModel

你可以设置unique() => true来在任意有状态组件(StateWidget)间共享同一个ViewModel实例。

dart 复制代码
import "package:view_model/view_model.dart";

class MyViewModelFactory with ViewModelFactory<MyViewModel> {
  final String arg;

  MyViewModelFactory({this.arg = ""});

  @override
  MyViewModel build() {
    return MyViewModel(state: arg);
  }

  // 如果为true,则会共享同一个viewModel实例。
  @override
  bool unique() => false;
}

监听状态变化

dart 复制代码
@override
void initState() {
  super.initState();
  listenViewModelStateChanged<MainViewModel, String>(
    _mainViewModel,
    onChange: (String? p, String n) {
      print("mainViewModel状态变化: $p -> $n");
    },
  );
}

刷新ViewModel

这将释放旧的ViewModel并创建一个新的。不过,建议使用getter来获取ViewModel,否则你需要手动重置ViewModel

dart 复制代码
// 建议使用getter来获取ViewModel。
MyViewModel get viewModel => getViewModel<MyViewModel>();

void refresh() {
  // 刷新 
  refreshViewModel(viewModel);
}

或者

dart 复制代码
late MyViewModel viewModel = getViewModel<MyViewModel>(factory: factory);

void refresh() {
  // 刷新并重置 
  refreshViewModel(viewModel);
  viewModel = getViewModel<MyViewModel>(factory: factory);
}
相关推荐
小趴菜82277 小时前
安卓接入Kwai广告源
android·kotlin
2501_916013747 小时前
iOS 混淆与 App Store 审核兼容性 避免被拒的策略与实战流程(iOS 混淆、ipa 加固、上架合规)
android·ios·小程序·https·uni-app·iphone·webview
程序员江同学8 小时前
Kotlin 技术月报 | 2025 年 9 月
android·kotlin
码农的小菜园9 小时前
探究ContentProvider(一)
android
时光少年10 小时前
Compose AnnotatedString实现Html样式解析
android·前端
hnlgzb11 小时前
安卓中,kotlin如何写app界面?
android·开发语言·kotlin
jzlhll12312 小时前
deepseek kotlin flow快生产者和慢消费者解决策略
android·kotlin
火柴就是我12 小时前
Android 事件分发之动态的决定某个View来处理事件
android
一直向钱12 小时前
FileProvider 配置必须针对 Android 7.0+(API 24+)做兼容
android
zh_xuan12 小时前
Android 消息循环机制
android