跳到主要内容

Websocket 和同步执行

到目前为止,我们只介绍了如何创建一个 Future
这是有意为之的,因为 Future 是 Riverpod 应用程序构建方式的核心。 但是,如有必要,Riverpod 还支持其他格式。

特别是,除了 Future 类型的提供者程序,还存在灵活的类型:

  • 同步返回一个对象,例如创建“存储库”。
  • 返回一个 Stream,例如监听 websockets。

返回 Future 和返回 Stream 或 object 总体上非常相似。 本页将解释这些用例的细微差别和各种提示。

同步返回对象

若要同步创建对象,请确保提供者程序不返回 Future:


int synchronousExample(Ref ref) {
return 0;
}

当提供者程序同步创建对象时,这会影响对象的使用方式。 具体而言,同步值不会被包装在“AsyncValue”中:

  Consumer(
builder: (context, ref, child) {
// The value is not wrapped in an "AsyncValue"
int value = ref.watch(synchronousExampleProvider);

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

这种差异的后果是,如果提供者程序抛出异常,尝试读取该值会重新抛出错误。 或者,当使用 ref.listen 时,将调用 “onError” 回调。

可监听对象的注意事项

可监听对象,例如 ChangeNotifierStateNotifier 是不受支持的。
如果出于兼容性原因,您需要与其中一个对象进行交互, 一种解决方法是将其通知机制通过管道传递给 Riverpod。

/// A provider which creates a ValueNotifier and update its listeners
/// whenever the value changes.

ValueNotifier<int> myListenable(Ref ref) {
final notifier = ValueNotifier(0);

// Dispose of the notifier when the provider is destroyed
ref.onDispose(notifier.dispose);

// Notify listeners of this provider whenever the ValueNotifier updates.
notifier.addListener(ref.notifyListeners);

return notifier;
}
信息

如果你多次需要这样的逻辑,值得注意的是, 逻辑是共享的!"ref" 对象被设计为可组合的。 这样就可以从提供者程序中提取处置/监听逻辑:

extension on Ref {
// We can move the previous logic to a Ref extension.
// This enables reusing the logic between providers
T disposeAndListenChangeNotifier<T extends ChangeNotifier>(T notifier) {
onDispose(notifier.dispose);
notifier.addListener(notifyListeners);
// We return the notifier to ease the usage a bit
return notifier;
}
}


ValueNotifier<int> myListenable(Ref ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(0));
}


ValueNotifier<int> anotherListenable(Ref ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(42));
}

监听一个流

现代应用程序的一个常见用例是与 Websocket 交互, 例如与 Firebase 或 GraphQL 订阅进行交互。
与这些 API 的交互通常是通过监听一个 Stream

为了帮助实现这一点,Riverpod 自然地支持 Stream 对象。 与 Future 一样,该对象将转换为 AsyncValue


Stream<int> streamExample(Ref ref) async* {
// Every 1 second, yield a number from 0 to 41.
// This could be replaced with a Stream from Firestore or GraphQL or anything else.
for (var i = 0; i < 42; i++) {
yield i;
await Future<void>.delayed(const Duration(seconds: 1));
}
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// The stream is listened to and converted to an AsyncValue.
AsyncValue<int> value = ref.watch(streamExampleProvider);

// We can use the AsyncValue to handle loading/error states and show the data.
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 会将 StreamFuture 转换为 AsyncValue。 尽管很少需要,但可以通过将返回类型包装在 Raw 泛型中来禁用此行为。

警告

通常不建议禁用转换 AsyncValue。 只有当您知道自己在做什么时才这样做。


Raw<Stream<int>> rawStream(Ref ref) {
// "Raw" is a typedef. No need to wrap the return
// value in a "Raw" constructor.
return const Stream<int>.empty();
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// The value is no-longer converted to AsyncValue,
// and the created stream is returned as is.
Stream<int> stream = ref.watch(rawStreamProvider);
return StreamBuilder<int>(
stream: stream,
builder: (context, snapshot) {
return Text('${snapshot.data}');
},
);
}
}