2

在我作为注册过程的一部分构建的应用程序中,为每个“用户”文档创建了一个子集合,其中包含多达 100 个文档。

我试图在StreamBuilder.

我有一个无法解决的奇怪错误。当StreamBuilder用户第一次查看数据时,它不会显示数据。相反,它返回一个空列表。

我可以看到文档已在子集合中正确生成。数据正在使用 StreamBuilder 的页面之前的页面上设置。即使有延迟,我也会认为新文档会刚刚开始出现在StreamBuilder. Firebase 控制台视图

如果应用程序重新启动,或者用户注销并再次登录,StreamBuilder会按预期显示数据。

下面是我正在使用的代码:

Stream<QuerySnapshot> provideActivityStream() {
    return Firestore.instance
        .collection("users")
        .document(widget.userId)
        .collection('activities')
        .orderBy('startDate', descending: true)     
        .snapshots();
  }
...
Widget activityStream() {
  return Container(
      padding: const EdgeInsets.all(20.0),
      child: StreamBuilder<QuerySnapshot>(
      stream: provideActivityStream(),
      builder: (BuildContext context,
          AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        if(snapshot.data == null) {
          return CircularProgressIndicator();
        }
        if(snapshot.data.documents.length < 1) {
          return new Text(
            snapshot.data.documents.toString()
            );
        }
        if (snapshot != null) {
          print('$currentUser.userId');
        }
        if (
          snapshot.hasData && snapshot.data.documents.length > 0
          ) {
          print("I have documents");
          return new ListView(
              children: snapshot.data.documents.map((
                  DocumentSnapshot document) {
                  return new PointCard(
                    title: document['title'],
                    type: document['type'],
                  );
                }).toList(),
            );
        }
      } 
    )
  );
}

编辑:根据评论请求添加主构建

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: Text("Home"),
          actions: <Widget>[
          ],
          bottom: TabBar(
            tabs: [
              Text("Account"),
              Text("Activity"),
              Text("Links"),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            accountStream(),
            activityStream(),
            linksStream()
            ]
          )
        ),
      );
    }
  }

我已经尝试解决

我最初以为是连接错误,所以创建了一系列基于switch (snapshot.connectionState). 我可以看到,ConnectionState.active = true因此认为在 Firestore 中添加新文档可能会产生效果,但什么也没做。

我尝试了以下方法来使初始流构造函数异步。它无法加载任何数据。

Stream<QuerySnapshot> provideActivityStream() async* {
    await Firestore.instance
        .collection("users")
        .document(widget.userId)
        .collection('activities')
        .orderBy('startDate', descending: true)     
        .snapshots();
  }

我试过删除 tabcontroller 元素——例如只有一个页面——但这也无济于事。

我尝试使用 aDocumentSnapshot和 a访问数据QuerySnapshot。我两个都有问题。

我敢肯定这很简单,但坚持下去。非常感谢任何帮助。谢谢!

4

3 回答 3

0

它不是通过使用任何一个查询快照和文档快照来获取的

您应该首先使用 Querysnapshot 进行查询,然后将信息检索到 Documentsnapshot 是的,加载文档可能需要几秒钟 解决方案是正确的,您应该使用 async 和 await 函数

我建议你使用 Direct snapshot 而不是 streamBuilder

当您的类是 statefullWidget 并且问题也与状态有关时,我们可以在 statefullWidget 的 initstate 中加载文档快照

...

 bool isLoading;
 List<DocumentSnapshot> activity =[];
 QuerySnapshot user;
 @override
 void initState() {
  print("in init state");
  super.initState();
  getDocument();
 ``    } 
  getDocument() async{

 setState(() {
   isLoading = true;
 });
 user= await Firestore.instance
    .collection("users")
    .document(widget.userId)
    .collection('activities')
    .orderBy('startDate', descending: true)     
    .getDocuments();

  activity.isEmpty ? activity.addAll(user.documents) : null;

    setState(() {
  isLoading = false;
       });

     }

//inside  Widget build(BuildContext context) { return  Scaffold( in your body 
//section of scaffold in the cointainer
Container(
padding: const EdgeInsets.all(20.0),
child: isLoading ?
         CircularProgressIndicator(),
        :ListView.builder(

                          itemCount: global.category.length,
                          itemBuilder: (context, index) {
                            return  PointCard(
                                    title: activity[index].data['title'],
                                      type: activity[index].data['type'],
                                    );//pointcard
                             }
                            ),//builder
                     ),//container
于 2020-02-26T16:35:14.580 回答
0

我们也可以尝试以下

 QuerySnapshot qs;
 Stream<QuerySnapshot> provideActivityStream() async{
    qs= await Firestore.instance
             .collection("users")
             .document(widget.userId)
             .collection('activities')
            .orderBy('startDate', descending: true)     
            .snapshots();

      return qs;
  }//this should work

但是根据streambuilder的基础知识,如果上面的部分不起作用,那么还有另一个

     QuerySnapshot qs;
 Stream<QuerySnapshot> provideActivityStream() async* {
    qs= await Firestore.instance
             .collection("users")
             .document(widget.userId)
             .collection('activities')
            .orderBy('startDate', descending: true)     
            .snapshots();

      yield qs;
  }//give this a try
于 2020-02-26T17:58:39.307 回答
0

tl;博士

  • 需要用于setState让 Firebase currentUseruid可用于小部件
  • 需要使用AutomaticKeepAliveClientMixin才能正常工作TabBar
  • 我认为使用 Provider 包可能是保持用户状态的更好方法,但没有解决这个问题

解释

我的代码通过 Future 获取 currentUser uid。根据此处的 SO 答案,这是一个问题,因为小部件将在 FirebaseAuth 可以归还uid. 我最初尝试使用initState来获取uid但具有完全相同的同步问题。setState从函数调用以调用允许FirebaseAuth.instance更新的小部件树。

我将此小部件放在 TabBar 小部件中。我的理解是,每次从视图中删除选项卡时,它都会在返回时被重新构建。这导致了进一步的状态问题。AutomaticKeepAlive mixin 的 API 文档在这里

解决方案代码

添加评论,希望它们有助于别人的理解(或者有人可以纠正我的误解)

活动列表.dart

class ActivityList extends StatefulWidget {
// Need a stateful widget to use initState and setState later
  @override
  _ActivityListState createState() => _ActivityListState();
}

class _ActivityListState extends State<ActivityList> 
  with AutomaticKeepAliveClientMixin<ActivityList>{
  // `with AutomaticKeepAliveClientMixin` added for TabBar state issues

  @override
  bool get wantKeepAlive => true;
  // above override required for mixin

  final databaseReference = Firestore.instance;

  @override
    initState() {
      this.getCurrentUser(); // call the void getCurrentUser function
      super.initState();
  }

  FirebaseUser currentUser;

  void getCurrentUser() async {
    currentUser = await FirebaseAuth.instance.currentUser();
    setState(() {
      currentUser.uid;
    });
    // calling setState allows widgets to access uid and access stream
  }

  Stream<QuerySnapshot> provideActivityStream() async* {
    yield* Firestore.instance
        .collection("users")
        .document(currentUser.uid)
        .collection('activities')
        .orderBy('startDate', descending: true)     
        .snapshots();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Container(
                  padding: const EdgeInsets.all(20.0),
                  child: StreamBuilder<QuerySnapshot>(
                  stream: provideActivityStream(),
                  builder: (BuildContext context,
                  AsyncSnapshot<QuerySnapshot> snapshot) {
                    if(snapshot.hasError) return CircularProgressIndicator();
                    if(snapshot.data == null) return CircularProgressIndicator();
                    else if(snapshot.data !=null) {
                      return new ListView(
                          children: snapshot.data.documents.map((
                              DocumentSnapshot document) {
                            return new ActivityCard(
                              title: document['title'],
                              type: document['type'],
                              startDateLocal: document['startDateLocal'],
                            );
                          }).toList(),
                        );
                    }
                  },
                )
            );
  }
}

主页.dart

...
@override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: Text("Home"),
          actions: <Widget>[
          ],
          bottom: TabBar(
            tabs: [
              Text("Account"),
              Text("Activity"),
              Text("Links"),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            accountStream(),
            ActivityList(), // now calling a stateful widget in an external file
            linksStream()
            ]
          )
        ),
      );
    }
  }
于 2020-03-03T10:00:48.177 回答