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')
}
Related
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.
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,
);
},
);
},
),
);
}
},
);
I want to compound firestore datas. Which widget is useful for multiple data?
For instance, i have user datas and user post's datas. I want to show them in the same Card Widget, but their snapshots are different. How can i compound them ?
I write just like this, but it shows just 1 snapshot, i want to both futurebuilder and streambuilder snapshots:
note: i can't use user datas, in below it has final dataUser = snapshotUser.data;
but i can't use it, it gives an error
class HomePageBodyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collectionGroup('Posts').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot docPost) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('Users')
.doc(docPost.reference.parent.parent.id)
.get(),
builder: (context, snapshotUser) {
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}
return homePageCard(context, dataPost: docPost, dataUser: snapshotUser.data);
},
);
}).toList());
});
}
i solved it, like this:
class HomePageBodyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collectionGroup('Posts').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final dataPost = snapshot.data.docs[index];
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('Users')
.doc(dataPost.reference.parent.parent.id)
.get(),
builder: (context, snapshotUser) {
final dataUser = snapshotUser.data;
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}
return homePageCard(context,
dataPost: dataPost, dataUser: dataUser);
},
);
});
});
}
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.[...]
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
}
},
);