简体   繁体   English

Flutter Firestore:具有初始数据的 FirestoreBuilder

[英]Flutter Firestore: FirestoreBuilder with initial data

I'm making my first Flutter app and I encounter a problem and doesn't found any solution for it.我正在制作我的第一个 Flutter 应用程序,我遇到了一个问题,但没有找到任何解决方案。

I have a view where I render a Firestore document, and there is two ways of getting there:我有一个渲染 Firestore 文档的视图,有两种方法可以到达那里:

  • From a list where I already loaded my documents从我已经加载文档的列表中
  • From Dynamic Links with uid attached as arguments (args)来自附加 uid 为 arguments (args) 的动态链接

So in order to listen document changes and loading the data when arriving from the link I used FirestoreBuilder like this:因此,为了在从链接到达时监听文档更改和加载数据,我使用了 FirestoreBuilder,如下所示:

    return FirestoreBuilder<EventDocumentSnapshot>(
        ref: eventsRef.doc(args.uid),
        builder: (context, AsyncSnapshot<EventDocumentSnapshot> snapshot, Widget? child) {
          if (!snapshot.hasData) {
            return Container();
          }
          Event? event = snapshot.requireData.data;
          
          return Scafold(); //Rest of my rendering code
        }
     );
       
 

How I could avoid first call to Firebase when I already have the data but still listen to changes?当我已经有了数据但仍然在听变化时,我如何避免第一次调用 Firebase? The main problem is that my hero animation doesn't work because of this.主要问题是我的英雄 animation 因此无法正常工作。

I tried with a StreamBuilder and initialData param but since it's expecting stream I didn't know how to cast my data.我尝试使用StreamBuilderinitialData参数,但由于它期望 stream 我不知道如何转换我的数据。

Okay, so I found the solution myself after many tries, so I added my Model object that can be null as initialData, but the thing that makes me struggle with is how you get the data in the builder.好吧,经过多次尝试我自己找到了解决方案,所以我添加了我的 Model object 作为 initialData,但让我纠结的是如何在构建器中获取数据。 You have to call different methods depending on where the data is coming from.您必须根据数据的来源调用不同的方法。

    return StreamBuilder(
        initialData: args.event
        ref: eventsRef.doc(args.uid),
        builder: (context, AsyncSnapshot<dynamic> snapshot) {
          // Here is the trick, when data is coming from initialData you only
          // need to call requireData to get your Model
          Event event = snapshot.requireData is EventDocumentSnapshot ? snapshot.requireData.data : snapshot.requireData;
          
          return Scafold(); //Rest of my rendering code
        }
     );

Reading through cloud_firestore's documentation you can see that a Stream from a Query can be obtained via snapshots()阅读cloud_firestore 的文档,您可以看到可以通过 snapshots() 获取查询中的 Stream

StreamBuilder<QuerySnapshot>(
  stream: Firestore.instance.collection('books').snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    if (!snapshot.hasData) return new Text('Loading...');
    return new ListView(
      children: snapshot.data.documents.map((DocumentSnapshot document) {
        return new ListTile(
          title: new Text(document['title']),
          subtitle: new Text(document['author']),
        );
      }).toList(),
    );
  },
);

This won't help you, but with GetX it's simple to implement like this: You don't need StreamBuilder anymore.这对您没有帮助,但是使用 GetX 可以像这样轻松实现:您不再需要 StreamBuilder。

//GetXcontroller
class pageController extends GetXcontroller {

... 

RxList<EventModel> events = RxList<EventModel>([]);


Stream<List<EventModel>> eventStream(User? firebaseUser) =>
      FirebaseFirestore.instance
          .collection('events')
          .snapshots()
          .map((query) =>
              query.docs.map((item) => UserModel.fromMap(item)).toList());


@override
  void onReady() async {
    super.onReady();
    events.bindStream(
        eventStream(controller.firebaseUser)); // subscribe any change of events collection
  }

@override
  onClose() {
    super.onClose();
    events.close(); //close rxObject to stop stream
    
}

...
}

You can use document snapshots on StreamBuilder.stream .您可以在StreamBuilder.stream上使用文档快照。 You might want to abstract the call to firebase and map it to an entity you defined.您可能希望将对 firebase 和 map 的调用抽象为您定义的实体。

  MyEntity fromSnapshot(DocumentSnapshot<Map<String, dynamic>> snap) {
    final data = snap.data()!;
    return MyEntity (
      id: snap.id,
      name: data['name'],
    );
  }

  Stream<MyEntity> streamEntity(String uid) {
    return firebaseCollection
        .doc(uid)
        .snapshots()
        .map((snapshot) => fromSnapshot(snapshot));
  }
  
  return StreamBuilder<MyEntity>(
    // you can use firebaseCollection.doc(uid).snapshots() directly
    stream: streamEntity(args.uid),
    builder: (context, snapshot) {
     if (snapshot.hasData) {
        // do something with snapshot.data
        return Scaffold(...);
      } else {
        // e.g. return progress indicator if there is no data
        return Center(child: CircularProgressIndicator());
      }
    },
  );

For more complex data models you might want to look at simple state management or patterns such as BLoC .对于更复杂的数据模型,您可能需要查看简单的 state 管理BLoC等模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM