优雅的让 Riverpod 担任 ViewModel

169 阅读1分钟

riverpod 作为状态管理只需要

  • stateprovider
  • ref.read 获取 stateNotifier 和 state
  • ref.listen 监听 state 的变化

作者本人也说 provider 的定位不只是状态管理,这只是它的一个模块。

其他的功能对于状态管理来说,注解,各种 provider,他们很灵活,但是意味着更容易出错,更不知道在什么情况下用哪种 provider 最合适。随地大小注解声明一堆 state,以及 provider 里面还可以 ref 嵌套,这种写法过于黑盒,不稳定性极高,修改一个 provider,你不知道会有其他的 provider 也受到了影响。

riverpod 默认是根据 provider 入参的 == + hashCode 判断 Notifier 的 唯一性,一般入参都会用 freezed 重写 ==和 hashCode. 这样依赖入参其实不太灵活,有的时候我们只想用一个 String id 来标记。

ProviderArg

用 ProviderArg 来包装 key 和 arg,用 key 来重写其 == 和 hascode。key 和 arg 分离,灵活性提高。保证 key 唯一,那么就可以方便的复用 ViewModel 实例。在任何地方通过 key 获取到已经存在的实例

@immutable
class ProviderArg<T extends Object> {
  final String? key;
  final dynamic arg;

  T get requireArg {
    return arg as T;
  }

  final Object _defaultKey = "ProviderKey(${DateTime.now().millisecondsSinceEpoch})_${const Uuid().v6()}";

  static const _uniqueKey = "ProviderArg#uniqueKey";

  Object get _key {
    return key ?? _defaultKey;
  }

  factory ProviderArg.unique({T? arg}) {
    return ProviderArg(key: _uniqueKey, arg: arg);
  }

  ProviderArg({
    this.key,
    this.arg,
  });

  @override
  bool operator ==(Object other) {
    return identical(this, other) ||
        (other is ProviderArg && runtimeType == other.runtimeType && _key == other._key);
  }

  @override
  int get hashCode {
    return _key.hashCode;
  }

  @override
  String toString() {
    return 'ProviderKey{key: $_key}';
  }
}
typedef ProviderBuilder<VM extends ViewModel<S>, S, T extends Object>
    = AutoDisposeStateNotifierProviderFamily<VM, S, ProviderArg<T>>;

ProviderBuilder<VM, S, T> buildProvider<VM extends ViewModel<S>, S, T extends Object>({
  required VM Function(ProviderArg<T> key) viewModel,
}) {
  return StateNotifierProvider.autoDispose.family<VM, S, ProviderArg<T>>((ref, key) {
    return viewModel.call(key);
  });