FutureBuilder doesn't work properly with Firestore - flutter

can't create a query using FutureBuilder.
Firestore is at the latest version.
This is the case for both Android and iOS devices, any ideas?
return FutureBuilder<QuerySnapshot>(
future: Firestore.instance
.collection('productlist')
.where('productid', isEqualTo: pid)
.limit(
1), // a previously-obtained Future<String> or null
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
productname = snapshot.data.documents[0]['productname'];
print("productname:" + productname);
}
else{
productname = "0";
}
},
);

I think the issue here is that you are not returning a widget.
Try the code below
FutureBuilder(
future: Firestore.instance
.collection('productlist')
.where('productid', isEqualTo: pid)
.limit(1),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return Text(snapshot.data.documents[0]['productname']);
} else {
return Text("0");
}
},
),

u can use stream builder instead it works pretty well :
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('productlist').where('productid', isEqualTo: pid).limit(1),
builder: (context, snapshot) {
if (snapshot.data != null) {
// Here u will get list of document snapshots
final List<DocumentSnapshot> documents = snapshot.data.documents;
// now u can access each document by simply specifying its number
// u can also use list view to display every one of them
return ListView.builder(
itemCount: documents.length,
itemBuilder: (context, int index) => Text(documents[index]['productname']),
);
} else {
// Show loading indicator here
}
},
);

Related

Listview.builder inside StreamBuilder is rebuilding/flickering

So I used Streambuilder to fetch CartItems from Firestore and it starting to flicker and it always goes to the top. Also can't scroll down, it always goes to the top, I used streambuilder in different parts of the app and it works perfectly but here it causing the problem...
Stream<QuerySnapshot<Map<String, dynamic>>> getCartStream() {
CollectionReference<Map<String, dynamic>> ref = FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection("cart");
Stream<QuerySnapshot<Map<String, dynamic>>> stream = ref.snapshots();
return stream;
}
StreamBuilder(
stream: getCarStream(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container();
} else {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
ProductModel productModel = ProductModel.fromMap(snapshot.data!.docs[index].data());
return CartItem(product: productModel);
},
);
}
},
)),
Rest of the code
Gif of the Problem
just remove the initState(), will work fine. if not then kindly share CARTITEM(..) class.

How to observe Firebase changes in Flutter using StreamBuilder

I am trying to observe Firebase collection changes. In the first run, Listview is not building. After changing data on Firebase, then listview is rendered properly but previous snapshot is used because I see my old data on the screen.
Any idea to find the error?
#override
Widget build(BuildContext context) {
return BlocListener<VoteCubit, VoteState>(
listener: (context, state) {
if (state.status == FormStatus.success) {
print("Success");
} else if (state.status == FormStatus.inprogress) {
print("In progress");
} else if (state.status == FormStatus.fail) {
print("fail");
} else {
print("Empty Area");
}
},
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('liveSurvey').snapshots(),
builder: (context, snapshot) {
final voteCubit = context.read<VoteCubit>();
final voteState = voteCubit.state;
if (snapshot.hasData) {
voteCubit.getAllRooms(snapshot);
print("Here");
return ListView.builder(
itemCount: voteState.rooms.length,
itemBuilder: (context, i) {
print("Inside Listview");
. . .
Use stream builder like this for real time data.
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("travellers/India/details")
.where(
'firestore_id',
isEqualTo: widget.get_firestore_id,
)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
print('play lodader')
}else if (snapshot.hasData) {
print('your_ui_here')
}

Modify Future Builder inside Stream Builder to avoid widget flickering

I am using a FutureBuilder inside a StreamBuilder that updates the UI every time a document is added to the activity collection, to get some aditional data from Firestore. The problem is that the FutureBuilder returns a SizedBox widget while the ConnectionState is waiting causing the all the cards to dissapear for a second. I would like to avoid this flickering since it causes a bad ui experience for users.
Is there a way to query the required user data in the activity stream so it all returns at once that way I can remove the FutureBuilder?
If not ... what would be a solution for this?
activityStream() {
return FirebaseFirestore.instance
.collection('activity')
.orderBy('timestamp', descending: true)
.limit(55)
.snapshots();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
StreamBuilder<QuerySnapshot>(
stream: activityStream(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
final activityContent = snapshot.data?.docs
.map((data) => ActivityModel.fromFirestore(data))
.toList();
return Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: activityContent[i].players)
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
final users = snapshot.data!.docs.map((e) {
return UserModel.fromFirestore(e);
}).toList();
return MyWidget(
users: users,
);
},
);
},
),
);
}
},
);

get a document from firestore with a required phone number

I have a collection of users in have created docs names as per their UID and each doc has a field "phoneNumber" , I want to get a SINGLE Document
In numberArray i have some selected numbers to be searched
ListView.builder(
itemCount: number.length,
itemBuilder: (context, index) => FutureBuilder(
future: FirebaseFirestore.instance
.collection("Users")
.where('phone', isEqualTo: number[index])
.get(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData && snapshot.data != null) {
return ListTile();
else return Container();

Wait a function to be done in ListView

I need to wait my function to be done in a ListView because of a Firestore request. I tried using Future.wait() but it does not work.
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('Statements')
.where('userID',isEqualTo: context.watch<User>().uid)
.get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (snapshot.hasError) {return Text("Erreur");}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");}
return Expanded(
child:ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot document = snapshot.data.docs[index];
Future.wait([getStatementData(document)]);
return StatementCard(statement:selectedStatement,
accommodation : relatedAccommodation,
owner : relatedOwner);
},
)
Here is the function called :
Future getStatementData(document) async {
selectedStatement = Statement.fromFirebase(document);
document.data()["accommodation"].get().then((value) {
relatedAccommodation = Accommodation.fromFirebase(value);});
await FirebaseFirestore.instance
.collection('Owners')
.doc(document["ownerList"][0])
.get().then((value) {
print(value.data());
relatedOwner = Owner.fromFirebase(value);
});
}
Should I use another future builder ?
You should do it like this by returning another Future Builder inside :
ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot document = snapshot.data.docs[index];
return FutureBuilder(
future: getStatementData(document) ,
builder: (context, snapshot) {
return snapshot.connectionState == ConnectionState.done
? StatementCard(statement:selectedStatement,
accommodation : relatedAccommodation,
owner : relatedOwner);
: Container();
);
},
)
I think you just missed a simple thing, FutureBuilder also has a connectionstate.done state which you can access. This waits until your future function is Done.
return FutureBuilder(
future: yourFunction,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text('No status',);
break;
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
case ConnectionState.done:
return Text('Im done!') // Your listviewbuilder comes here
break;
default:
}
},
),
doc: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
example FutureBuilder with ListViewBuilder: https://medium.com/nonstopio/flutter-future-builder-with-list-view-builder-d7212314e8c9
[...]connectionState.done = [future] is not null, and has completed. If the future completed successfully, the [AsyncSnapshot.data] will be set to the value to which the future completed. If it completed with an error, [AsyncSnapshot.hasError] will be true and [AsyncSnapshot.error] will be set to the error object.[...]