首页 > 解决方案 > 如何在 Flutter 中结合 Riverpod StreamProvider?

问题描述

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

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

这是我共享 FirestoreDatabase 类实例的数据库提供程序:

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);
});

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

标签: fluttergoogle-cloud-firestoreriverpod

解决方案


根据个人经验,我建议远离 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);
      }
    }
  }
});

推荐阅读