Flutter Firestore:带有初始数据的 FirestoreBuilder
Flutter Firestore: FirestoreBuilder with initial data
我正在制作我的第一个 Flutter 应用程序,我遇到了一个问题,但没有找到任何解决方案。
我有一个渲染 Firestore 文档的视图,有两种方法可以到达那里:
- 来自我已加载文档的列表
- 来自带有 uid 作为参数附加的动态链接 (args)
因此,为了在从 link 到达时监听文档更改和加载数据,我使用了 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
}
);
当我已经拥有数据但仍会监听更改时,如何避免首次调用 Firebase?主要问题是我的英雄动画因此无法正常工作。
我尝试使用 StreamBuilder
和 initialData
参数,但由于它需要流,所以我不知道如何转换我的数据。
通读cloud_firestore's documentation可以看到,可以通过snapshots()
获取Query中的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(),
);
},
);
好的,经过多次尝试我自己找到了解决方案,所以我添加了可以为 null 的模型对象作为 initialData,但让我纠结的是如何在构建器中获取数据。您必须根据数据的来源调用不同的方法。
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
}
);
这对您没有帮助,但使用 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
}
...
}
您可以在 StreamBuilder.stream
上使用文档快照。您可能希望抽象化对 firebase 的调用并将其映射到您定义的实体。
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());
}
},
);
对于更复杂的数据模型,您可能需要查看 simple state management or patterns such as BLoC。
我正在制作我的第一个 Flutter 应用程序,我遇到了一个问题,但没有找到任何解决方案。
我有一个渲染 Firestore 文档的视图,有两种方法可以到达那里:
- 来自我已加载文档的列表
- 来自带有 uid 作为参数附加的动态链接 (args)
因此,为了在从 link 到达时监听文档更改和加载数据,我使用了 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
}
);
当我已经拥有数据但仍会监听更改时,如何避免首次调用 Firebase?主要问题是我的英雄动画因此无法正常工作。
我尝试使用 StreamBuilder
和 initialData
参数,但由于它需要流,所以我不知道如何转换我的数据。
通读cloud_firestore's documentation可以看到,可以通过snapshots()
获取Query中的StreamStreamBuilder<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(),
);
},
);
好的,经过多次尝试我自己找到了解决方案,所以我添加了可以为 null 的模型对象作为 initialData,但让我纠结的是如何在构建器中获取数据。您必须根据数据的来源调用不同的方法。
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
}
);
这对您没有帮助,但使用 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
}
...
}
您可以在 StreamBuilder.stream
上使用文档快照。您可能希望抽象化对 firebase 的调用并将其映射到您定义的实体。
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());
}
},
);
对于更复杂的数据模型,您可能需要查看 simple state management or patterns such as BLoC。