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);
}
相关推荐
Monkey-旭5 小时前
Android Bitmap 完全指南:从基础到高级优化
android·java·人工智能·计算机视觉·kotlin·位图·bitmap
Mike_Wuzy10 小时前
【Android】发展历程
android
开酒不喝车10 小时前
安卓Gradle总结
android
喝拿铁写前端11 小时前
Flutter 学习笔记 - 搭建(macOS 版)
前端·flutter
ALLIN11 小时前
Mac Flutter fvm 多版本管理安装与常用指令(详细使用)
flutter
阿华的代码王国11 小时前
【Android】PopupWindow实现长按菜单
android·xml·java·前端·后端
稻草人不怕疼12 小时前
Android 15 全屏模式适配:A15TopView 自定义组件分享
android
静默的小猫12 小时前
LiveDataBus消息事件总线之二-(不含反射和hook)
android
~央千澈~13 小时前
05百融云策略引擎项目交付-laravel实战完整交付定义常量分文件配置-独立建立lib类处理-成功导出pdf-优雅草卓伊凡
android·laravel·软件开发·金融策略
_一条咸鱼_14 小时前
Android Runtime冷启动与热启动差异源码级分析(99)
android·面试·android jetpack