likes
comments
collection
share

[译][官方文档] Flutter/Dart 状态管理库 Riverpod - 概要 - WebSocket 和同步执行

作者站长头像
站长
· 阅读数 58

!!!译文为作者本人人肉翻译~转载请注明出处!!!


原文链接:Websockets and synchronous execution | Riverpod

pub:riverpod | Dart Package (flutter-io.cn)

译时版本: 2.4.9



WebSocket 和同步执行

迄今为止,只说明了如何创建 Future 。 这是有意为之的,因为 Future 是 Riverpod 应用应当如何构建的核心。 不过,必要时,Riverpod 也支持其它格式。

特别是,除了 Future ,provider 还可自由用于:

  • 同步式返回对象,比如创建 "Repository" 。
  • 返回 Stream (流),比如监听 websocket 。

返回 Future 和返回 Stream 或对象总体上非常相似。 该篇可认为是用于这些场景的细微差别的说明和各种提示。

同步式返回对象

要同步式创建对象,要确保 provider 没有返回 Future :

@riverpod
int synchronousExample(SynchronousExampleRef ref) {
  return 0;
}

当 provider 同步式创建对象时,这会影响对象的消费方式。 特别是同步值不会封装在 "AsyncValue" 里面。

Consumer(
  builder: (context, ref, child) {
    // 该值不会封装在 "AsyncValue" 里面。
    int value = ref.watch(synchronousExampleProvider);

    return Text('$value');
  },
);

该差别的结果就是如果 provider 抛出了错误,尝试读取值会再次抛出错误。 作为替换方案,使用 ref.listen 时,"onError" 回调会被调用。

可监听对象的考量

可监听对象,如 ChangeNotifier 或 StateNotifier 是不支持的。 如果,为了兼容性的原因,需要和该类对象进行交互,一个变通方案是将其通知机制移植到 Riverpod 。

/// provider 创建了 ValueNotifier ,并且在值改变时更新它的 listener (监听器)。
@riverpod
ValueNotifier<int> myListenable(MyListenableRef ref) {
  final notifier = ValueNotifier(0);

  // provider 清理后对 notifier 进行清理。
  ref.onDispose(notifier.dispose);

  // ValueNotifier 更新时通知 provider 的 listener (监听器)。
  notifier.addListener(ref.notifyListeners);

  return notifier;
}

信息

万一需要多次该逻辑,那值得注意一下将逻辑共有! "ref" 对象是被设计成可组合的。这样将清理/监听逻辑放到 provider 之外:

extension on Ref {
  // 可以移除 Ref 扩展前面的逻辑。
  // 这样可以在 provider 之间重用逻辑
  T disposeAndListenChangeNotifier<T extends ChangeNotifier>(T notifier) {
    onDispose(notifier.dispose);
    notifier.addListener(notifyListeners);
    // 返回 notifier 便于使用
    return notifier;
  }
}

@riverpod
ValueNotifier<int> myListenable(MyListenableRef ref) {
  return ref.disposeAndListenChangeNotifier(ValueNotifier(0));
}

@riverpod
ValueNotifier<int> anotherListenable(AnotherListenableRef ref) {
  return ref.disposeAndListenChangeNotifier(ValueNotifier(42));
}

监听 Stream (流)

现代应用的一个常见使用场景是和 websocket 交互,比如和 Firebase 交互或订阅 GraphQL 。 和这些 API 交互通常是通过监听 Stream (流)来完成。

为了助力此类需求,Rvierpod 天然支持 Stream (流)对象。如使用 Future ,对象会被转换成 AsyncValue

@riverpod
Stream<int> streamExample(StreamExampleRef ref) async* {
  // 每 1 秒钟生成 0 到 41 之间的一个数字。
  // 这可替换成源于 Firestore 或 GraphQL 的 Stream (流)或其它任何处理。
  for (var i = 0; i < 42; i++) {
    yield i;
    await Future<void>.delayed(const Duration(seconds: 1));
  }
}

class Consumer extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // Stream 流在这里被监听,并转换成了 AsyncValue 。
    AsyncValue<int> value = ref.watch(streamExampleProvider);

    // 可用 AsyncValue 处理加载中/错误状态和表示数据。
    return switch (value) {
      AsyncValue(:final error?) => Text('Error: $error'),
      AsyncValue(:final valueOrNull?) => Text('$valueOrNull'),
      _ => const CircularProgressIndicator(),
    };
  }
}

信息

Riverpod 没有考虑自定义 Stream (流)的实现,如 RX 的 BehaviorSubject 。因此,返回 BehaviorSubject 不会同步向组件暴露 value (值),即使在创建时已经可用。

禁用 Stream/Future 向 AsyncValue 的转换

Riverpod 默认会把 Stream 和 Future 转换为 AsyncValue 。 尽管不常用,但也可以禁用该行为,将返回类型封装到  Raw 的类型定义中即可禁用。

警告

通常不建议禁用 AsyncValue 的转换。只可在完全了解行为目的时这样做。

@riverpod
Raw<Stream<int>> rawStream(RawStreamRef ref) {
  // "Raw" 是一个类型定义。不需要将返回值封装在 "Raw" 构造函数中。
  return const Stream<int>.empty();
}

class Consumer extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 值不再会转换成 AsyncValue ,创建的 Stream (流)会原样返回。
    Stream<int> stream = ref.watch(rawStreamProvider);
    return StreamBuilder<int>(
      stream: stream,
      builder: (context, snapshot) {
        return Text('${snapshot.data}');
      },
    );
  }
}

转载自:https://juejin.cn/post/7307502467825532955
评论
请登录