I'm using a future to search firebase for users based on user input. The future then returns the results as a list but the future builder is not working to show the data in the UI. How can I build this data in the UI? Also is using future builder the correct way of doing this?
FutureBuilder(
future: userSearch(_userSearchController.text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else {
return new CircularProgressIndicator();
}
},
)
Future userSearch(String userSearch) async {
QuerySnapshot result = await Firestore.instance
.collection("users")
.where("name", isEqualTo: userSearch)
.getDocuments();
final List<DocumentSnapshot> docs = result.documents;
return docs;
}
Edit using Stream
StreamBuilder(
stream: userSearch(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
print(snapshot.data.length);
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return userWidget(snapshot.data[index].data);
});
} else {
return new CircularProgressIndicator();
}
},
)
Stream<dynamic> userSearch() async* {
print("User Search");
QuerySnapshot result = await Firestore.instance
.collection("users")
.where("name", isEqualTo: _userSearchController.text.toLowerCase())
.getDocuments();
yield result.documents;
}
Due to you have catered for search based on userInput, you should be using Streambuilder. Future only return once, but streambuilder will always rebuild your widget whenever the return value is changed.
To build your UI, you can use ListView.builder:
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return BuildYourWidget(snapshot.data[index]);
});
} else {
return CircularProgressionIndicator();
}
BuilYourWidget(dynamic yoursnapshotdata) {
return ListTile(trailing: Text('hello'), title: Text(yoursnapshotdata));
}
Related
I'm trying to search for current chats in firestore by the user name but I get here all the chats I have even when I type a letter that is not included
Stream<QuerySnapshot<Map<String, dynamic>>> chatsSearchService(
String userId, String searchValue) {
return FirebaseFirestore.instance
.collection('users')
.doc(userId)
.collection('chats')
.where('userName', isEqualTo: searchValue)
.snapshots();
I want to only get the chat which contains the letters I type
StreamBuilder(
stream: FirestoreServices()
.chatsSearchService('${cubit.userModel?.id}', cubit.searchValue),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snap) {
if (!snap.hasData) {
return Container();
}
return ListView.separated(
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
LastMessagesModel currentChats =
LastMessagesModel.fromJson(snap.data?.docs[index].data());
return InkWell(
child: searchChatBody(user, context, currentChats),
);
},
separatorBuilder: (context, index) {
return Divider();
},
itemCount: snap.data!.docs.length);
}),
I have 2 streams in my codes the first one is to get the userid from friend list and the second stream is to use the list of ids to search for the userid's document in firebase.
Stream friendIDStream;
Stream friendNameStream;
Widget friendList() {
return StreamBuilder(
stream: friendNameStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return FriendTile(
snapshot.data.docs[index].data()["username"]);
},
)
: Container();
},
);
}
#override
void initState() {
getUserFriend();
getNameByID();
super.initState();
}
getUserFriend() async {
Constant.currentId =
await HelperFunctions.getUserIdSharedPreference(Constant.currentId);
setState(() {
firebaseMethods.getFriend(Constant.currentId).then((value) {
setState(() {
friendIDStream = value;
});
});
});
}
getNameByID() {
setState(() {
firebaseMethods.getFriendName(friendIDStream).then((value) {
setState(() {
friendNameStream = value;
});
});
});
}
This is the firestore code.
Future getFriend(String ownerid) async {
return await FirebaseFirestore.instance
.collection("users")
.doc(ownerid)
.collection("friends")
.snapshots();
}
Future getFriendName(friendid) async {
return await FirebaseFirestore.instance
.collection("users")
.doc(friendid)
.snapshots();
}
I doesn't know why is this happening since I can display the list of ids. I had tried changing docs to doc but is also produce the same error.
Edit:
Added photos of my database structure.
the reason is your function getFriendName is returning a documentsnapshot not a querysnapshot. SO replace your old code with this:-
Widget friendList() {
return StreamBuilder(
stream: friendNameStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: 1,
itemBuilder: (context, index) {
return FriendTile(
//snapshot.data.docs[index].data()["username"] old one
snapshot.data["username"]); //new one
},
)
: Container();
},
);
}
I have an app which I want to display documents inside collection.. the collection reference is the uid of the user.
Is there a way to get current user uid and put this uid inside StreamBuilder in stream.
I have tried like so but it did not work and returned null:
class _MyAdsState extends State<MyAds> {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future getCurrentUser() async {
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
print(uid);
return uid.toString();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("${getCurrentUser()}").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapShot){
if(querySnapShot.hasError){
return Text('Some Error');
}
if(querySnapShot.connectionState == ConnectionState.waiting){
return CircularProgressIndicator();
}else{
final list = querySnapShot.data.documents;
return ListView.builder(
itemBuilder: (context, index){
return ListTile(
title: Text(list[index]["subject"]),
subtitle: Text(list[index]["category"]),
);
},
itemCount: list.length,
);
}
},
)
Getting the UID is an asynchronous operation, so requires a FutureBuilder.
If you want to use the UID to then build a stream, you'll need to have a FutureBuilder for the UID, and then inside of that a StreamBuilder for the stream from the database.
body: FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection(snapshot.data.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapShot){
...
},
)
}
else {
return Text('Loading user data...');
}
THANK YOU GUYS!
I was looking for this for too long now. I had the "problem" that I was recording the senderUID for a sent message only, but of course wanted the Name being displayed in the "sentFrom" field. So I had to query Firestore for the UID and pull out the email. My solution:
FutureBuilder<QuerySnapshot>(
future: _firestore.collection("users").get(),
builder: (context, futureSnapshot) {
if (!futureSnapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
Map<String, String> users = {};
final userData = futureSnapshot.data.docs;
for (var user in userData) {
users[user.id] = user.data()["email"];
}
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection("messages").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
// ignore: missing_return
final messages = snapshot.data.docs;
List<Widget> messageWidgets = [];
for (var message in messages) {
final messageText = message.data()["text"];
final messageEmail = users[message.data()["senderUID"]];
messageWidgets
.add(Text("$messageText from $messageEmail"));
}
return Column(children: messageWidgets);
},
);
},
),
I just created a map from the data and used it inside the stream builder. Is there maybe a better solution?
Future<List<DocumentSnapshot>> getData() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore
.collection("UserTransactions")
.where("AuthUserId", isEqualTo: userId)
.getDocuments();
return qn.documents;
}
Here I am getting all the documents according to the id, I want to display the transactions which is an array in the List View
FutureBuilder(
future: getData(),
builder: (_, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Text(snapshot.data[index].data["transactions"][index]["Mode"])
})
}
);
I am getting the error:
The getter 'length' was called on null.
Receiver: null
Tried calling: length
How to display those values and also display nothing if the array is blank?
You need to check if data is retrieved all not:
FutureBuilder(
future: getData(),
builder: (_, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if(snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Text(snapshot.data[index].data["transactions"][index]["Mode"])
}
return CircularProgressIndicator();
}
)
Use snapshot.hasData to check if the data is retrieved and CircularProgressIndicator which will display a loading icon until data is fully retrieved.
I have created the widget in flutter app and its connected with the google firebase but i got an error on the StreamBuilder while getting the data document.('quick')
Widget _createBody() {
return StreamBuilder(
stream: Firestore.instance
.collection('notes').document.('quick').snapshots(),
builder: (context, snapshot){
if(snapshot.hasData){
var doc = snapshot.data;
if (doc.exists){
return Text(doc['content']);
}
}
return CircularProgressIndicator();
}
);
}
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('notes').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Center(
child: Text('Error: ${snapshot.error}'),
);
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: Text('Loading...'),
);
default:
return new ListView(
children: snapshot.data.documents.map((
DocumentSnapshot document) {
return ListTile(
title: Text(document['content']),
);
}).toList(),
}
}
};
It should be something like this if you're fetching a single document.
Widget _createBody() {
return StreamBuilder(
stream: Firestore.instance
.collection('notes')
.document('quick')
.get()
.snapshots(),
builder: (context, snapshot){
if(snapshot.hasData){
var doc = snapshot.data;
if (doc.exists){
return Text(doc['content']);
}
}
return CircularProgressIndicator();
}
);
}
If this doesn't work, you can always change it like so:
Firestore.instance
.collection('notes')
.document('quick')
.get()
.then((DocumentSnapshot ds) {
// use ds as a snapshot
});