如何在Flutter中结合Riverpod StreamProvider?

How to combine Riverpod StreamProvider in Flutter?

我一直在尝试合并两个 Riverpod StreamProvider。 目标是根据来自 Firestore 的其他数据对 Firestore 执行查询以获取数据,如下所示:

  1. 从 firestore 文档中获取用户数据
  2. 使用该数据执行另一个查询

这是我共享 FirestoreDatabase class 实例的数据库提供商:

final databaseProvider = Provider<FirestoreDatabase>((ref) {
  final auth = ref.watch(authStateChangesProvider);

  if (auth.data?.value?.uid != null) {
    return FirestoreDatabase(uid: auth.data!.value!.uid);
  }
  throw UnimplementedError();
});

然后我创建了一个 StreamProvider 来获取用户的数据

final userProvider = StreamProvider<UserModel>((ref) {
  final database = ref.watch(databaseProvider);
  return database.getUser();
});

现在我需要创建最后一个提供程序,但我无法让它工作。 这是我从现在开始写的:

final nodesProvider = StreamProvider<List<Node>>((ref) {
  final database = ref.watch(databaseProvider);
  final user = ref.watch(userProvider);

  //... some code to await user data fetching

  // user.nodes contains the list I need to pass in order to perform the query
  return database.getNodes(user.nodes);
});

有人可以帮助我或给我提示吗? 提前致谢

根据个人经验,我建议远离 StreamProviders,除非它只有一层深度(没有依赖其他流的流)。

相反,Riverpod 有一个 .last 提供程序公开流中的最后一个值。调用 ref.watch 将让您收听它和任何更新。

final databaseProvider = FutureProvider<FirestoreDatabase>((ref) async {
  final auth = await ref.watch(authStateChangesProvider.last);

  if (auth.data?.value?.uid != null) {
    return FirestoreDatabase(uid: auth.data!.value!.uid);
  }
  throw StateError('auth uid is null so no database could be created');
});

final nodesProvider = FutureProvider<List<Node>>((ref) async {
  // you can Future.wait these if you want
  final database = await ref.watch(databaseProvider);
  final user = await ref.watch(userProvider.last);

  return database.getNodes(user.nodes);
});

我认为这会起作用,尽管我的语法可能有点错误(在 SO 文本区域中编写)。另一方面,有很多方法可以在流中收听流,但我从来没有运气过。这是我认为的样子。

final databaseProvider = StreamProvider<FirestoreDatabase?>((ref) async* {
  // Or any loading value really... Idk if I'd actually do this
  yield null;

  for await (final authState in ref.watch(authStateChangesProvider.stream)) {
    if (auth.data?.value?.uid != null) {
      yield FirestoreDatabase(uid: auth.data!.value!.uid);
    }
  }
});

final nodesProvider = StreamProvider<List<Node>?>((ref) async* {
  // [Edit] You could probably use combineStream from rxDart here
  for await (final database in await ref.watch(databaseProvider.stream)) {
    if (database != null) {
      for await (final user in ref.watch(userProvider.stream)) {
        yield database.getNodes(user.nodes);
      }
    }
  }
});