将用户 uid 传递给 Flutter 中的 Firestore 流查询

Posted

技术标签:

【中文标题】将用户 uid 传递给 Flutter 中的 Firestore 流查询【英文标题】:Pass user uid to Firestore stream query in Flutter 【发布时间】:2020-10-17 05:45:40 【问题描述】:
    这里我使用 main.dart 中的提供程序从文档中流式传输“itemsInUserDocument”数据。

Widget build(BuildContext context) 
    return MultiProvider(
      providers: [
          StreamProvider<DocumentSnapshot>.value(
          value: DatabaseService().itemsInUserDocument,      <--- (1) Providing here
          ),
          StreamProvider<User>.value(
          value: AuthProvider().user,
          ),
      ],

    我的问题是在此处将 userUID 传递给 Stream 查询。我在 Firestore 上的文档 ID 是我的 userUID。

final userUID = "3SlUigYBgsNgzjU8a9GSimhAhuu1";            <-- (2) Need to pass to stream "userID' below??


class DatabaseService 

  Stream<DocumentSnapshot> get itemsInUserDocument 

    final DocumentReference userData = Firestore.instance.collection('userdata').document(userUID);

    return userData.snapshots();
  


    这里我使用的是小部件树中的列表。
  @override
  Widget build(BuildContext context) 
    final userTaskDone = Provider.of<DocumentSnapshot>(context);

    List taskList = [];    <-- (3) Using the list here. 

    taskList = userTaskDone['tasks'].toList();

    print(taskList);


它目前有效,我现在正在第 (3) 节中获取流数据,但我需要将 Firebase 用户 UID 传递到第 (2) 节中的流中。

我可以使用 setState 函数在有状态小部件中获取用户 UID,但我尝试过的一切都不起作用,或者我似乎重复了太多东西,我正在尝试使用 Provider 来正确管理状态。

我也可以通过提供者获取 userUID,但您只能在(上下文)上使用它,其中我的通知程序部分 (2) 只是一个类。

请帮我解决。

编辑:

我已经尝试过修改,结果如下:

当我进入在对 main.dart 进行这些更改之前它曾经工作的屏幕时,从提供者那里得到这个严重错误。

这里是 main.dart 的完整代码,现在进行了编辑:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(),
      title: 'Sendr',
      home: MainScreen(),
      routes: 
        SplashPage.id: (context) => SplashPage(),
        LoginScreen.id: (context) => LoginScreen(),
        NavigatorScreen.id: (context) => NavigatorScreen(),
        CragSelectionScreen.id: (context) => CragSelectionScreen(),
        CragRoutesScreen.id: (context) => CragRoutesScreen(),
        RouteDetailScreen.id: (context) => RouteDetailScreen(),
        MapScreen.id: (context) => MapScreen(),
      ,
    );
  


///Authentication Logic
class MainScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return StreamProvider<User>.value(
      value: AuthProvider().user,
      child: Consumer<User>(
        builder: (context, user, __) 
          if (user == null) 
            return LoginScreen();
           else 
            return MultiProvider(
              providers: [
                Provider<DatabaseService>.value(
                  value: DatabaseService(user.uid),
                ),
              ],
              child: NavigatorScreen(),
            );
          
        ,
      ),
    );
  


这是我的数据库服务:

import 'package:cloud_firestore/cloud_firestore.dart';

class DatabaseService 
  DatabaseService(this.uid);

  final String uid;

  Stream<DocumentSnapshot> get itemsInUserDocument 
    final DocumentReference userData =
        Firestore.instance.collection('userdata').document(uid);
    return userData.snapshots();
  


我没有对我正在使用它的步骤 (3) 进行任何更改,这可能是我现在的问题吗?

我正在尝试对此进行更多研究。通过提供者文档工作。如果我正确理解您的建议,我们将从顶部的 AuthProvider 流式传输 User 值,然后使用其下方的 User 值,然后将 user.uid 值传递给 DatabaseService,该值再次在小部件树下使用。在你的帮助下,我似乎快到了。如果您不介意,请告诉我您对屏幕上的提供程序错误的看法。非常感谢。

【问题讨论】:

【参考方案1】:

您可以尝试使用来自rxdartswitchmap,它会侦听流T 并将其映射到S 之一。

Stream<S> switchMap<S>(Stream<S> Function(T value) mapper) =>
      transform(SwitchMapStreamTransformer<T, S>(mapper));
import 'package:rxdart/transformers.dart';

final userStream = AuthProvider().user;

final itemsStream = userStream.switchMap<DocumentSnapshot>(
  (user) 
    if (user == null) return Stream<DocumentSnapshot>.value(null);

    return itemsCollection.where('uid', isEqualTo: user.uid).snapshots();
  ,
);
StreamProvider<DocumentSnapshot>.value(
  value: itemsStream,
),

暂时写下来,这样可能是错的。

编辑

另一种方法是将您的项目提供者置于用户之下一级。这样您就可以定期使用用户 uid 值。

return StreamProvider<User>.value(
  value: AuthProvider().user,
  child: Consumer<User>(
    builder: (_, user, __) 
      if (!user) 
        // Not logged in
       else 
        return MultiProvider(
          providers: [
            Provider.value<DatabaseService>(
              value: DatabaseService(user.uid),
            ),
            // More services that rely on user.uid
          ],
          child: SizedBox(),
        );
      
    ,
  ),
);

2 编辑

class DatabaseService 
  DatabaseService(this.uid);

  final String uid;

  getData() 
    return MyCollection.where('uid', isEqualTo: uid).snapshots();
  

【讨论】:

我没有使用 rxdart 的经验,但我会调查一下,谢谢您的回答。我认为有一种更简单的方法可以将用户 uid 变量从全局变量或其他东西传递到查询中。我很难理解为什么这很难做到。 是的,您最简单的选择是ProxyStreamProvider,但这并不是真的。详情见this issue 简而言之,我的问题是“如何在您的应用程序中的任何位置提供 userUID?” - 如果有人能回答这个问题,那将非常有帮助。 添加了另一种方法。您不一定需要使用消费者。您还可以使用Builder,然后在其构建器方法中或树的该部分下方的任何位置调用final user = Provider.of&lt;User&gt;(context) 以检索该用户 非常感谢您的建议,我已经坐了好几天了,我想试试这个。但我不确定如何实施。在我的步骤 (2) 中,这是一个单独的文件,其中“DatabaseService”类是我需要该 uid 变量的地方,我将如何在此文件中使用它?【参考方案2】:

这似乎已经使 main.dart 看起来如下:

Main.dart


void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return StreamProvider<User>.value(
      value: AuthProvider().user,
      child: Consumer<User>(
        builder: (context, user, __) 
          if (user == null) 
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              theme: ThemeData.dark(),
              title: 'Sendr',
              home: LoginScreen(),
            );
           else 
            return MultiProvider(
              providers: [
                StreamProvider<DocumentSnapshot>.value(
                  value: DatabaseService(user.uid).userRoutesDone,
                ),
              ],
              child: MaterialApp(
                debugShowCheckedModeBanner: false,
                theme: ThemeData.dark(),
                title: 'Sendr',
                home: NavigatorScreen(),
                routes: 
                  SplashPage.id: (context) => SplashPage(),
                  LoginScreen.id: (context) => LoginScreen(),
                  NavigatorScreen.id: (context) => NavigatorScreen(),
                  MapScreen.id: (context) => MapScreen(),
                ,
              ),
            );
          
        ,
      ),
    );

我的通知者:

import 'package:cloud_firestore/cloud_firestore.dart';

class DatabaseService 
  DatabaseService(this.uid);

  final String uid;

  Stream<DocumentSnapshot> get itemsInUserDocument 
    final DocumentReference userData =
        Firestore.instance.collection('userdata').document(uid);
    return userData.snapshots();
  


我在小部件树中使用它的位置:


  @override
  Widget build(BuildContext context) 
    final userTaskDone = Provider.of<DocumentSnapshot>(context);

    List taskList = [];    <-- (3) Using the list here. 

    taskList = userTaskDone['tasks'].toList();

    print(taskList);


【讨论】:

以上是关于将用户 uid 传递给 Flutter 中的 Firestore 流查询的主要内容,如果未能解决你的问题,请参考以下文章

如何将用户的TextFormField输入传递给flutter中不同类的按钮

Flutter:如何将值从 streamprovider / listview builder 传递给另一个类

从firebase数据库flutter中的uid获取用户数据

从登录用户 Flutter/Firebase 获取文档列表

将 SharedPreferences 值传递给 Flutter 中的另一个方法

如何从 Cloud Firestore 中获取数据,其中 user.uid 等于 Flutter 中的文档 ID?