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);
}
相关推荐
千里马学框架14 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台14 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone14 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc15 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo17 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun00770017 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼18 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone19 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen19 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone19 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui