next Page stream is not update on Flutter - flutter

I used two pages. and I added StreamBuilder in my first page and I passed snapshot.data to next Page. but when value change in 2nd-page value is not changing. How to fix it? I can't call streamBuilder in both pages because it's meaning two read in firebase. Is there any way to create singleton for services and access anywhere?
StreamBuilder(
stream: db.getData(),
builder: (context,snapshot){
return Column(
children: <Widget>[
InkWell(
onTap: ()=> Navigator.pushNamed(context, '/nextPage',arguments: Arguments(
data: snapshot.data
)),
)
],
);
},
)

InkWell(
onTap: ()=> Navigator.pushNamed(context, '/nextPage',arguments: Arguments(
data: snapshot.data
),
),
When using the above code, a data snapshot is only sent when you Tap on the InkWell. Meaning unless tapped on the inkwell it will not provide new data to nextPage.
To resolve this issue, I would suggest the following:
In First page
Create ValueNotifier instance to observe changes in the common reference:
// change Cast to type that you receive from firebase, or you can omit the cast
final ValueNotifier<snapshot_data_type> firebaseDataNotifier = ValueNotifier<snapshot_data_type>();
Update the value of firebaseDataNotifier when you receive data from firebase:
StreamBuilder(
stream: db.getData(),
builder: (context,snapshot){
firebaseDataNotifier.value = snapshot.data;
...
Pass the firebaseDataNotifier as data for the nextPage
In the next Page
Wrap the Next page widgets with ValueListenable
ValueListenableBuilder(
valueListenable: valueNotifier,
builder: (context, firebaseData, child) {
// return your next page widget here and use firebaseData instead of snapshot.data
)
Note: Make sure you keep the ValueNotifier instance outside of both widgets so that they can access it without any dependency.

Related

Need I do pagination for flutter StreamBuilder?

I'm new to flutter, and now I'm creating an app which has a feed page, I'm using StreamBuilder + firestore to do this, the code is like this:
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('posts')
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(
color: primaryColor,
),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) => Container(
child: createPostViewFromSnapShot(snapshot.data!.docs[index]),// it returns a widget
),
);
},
);
You can see from the code I didn't do pagination, I want to know when the code runs, it fetch all the post from firestore ? Or it will fetch data by block or something like pagination ?
I want to know if it's necessary to do pagination for StreamBuilder?
Avoid creating Widgets in Methods. That way you cause your App to
become I'm-performant. Because the Widget is not sat directly in the
Tree, but a method, every build that methods gets called, no matter
if the resulting widget would have to be actually be rebuilt.
That stream will not always emit events where data is not null. You will most likely get an exception for using snapshot.data! bang operator
Take a closer look at the documentation: https://firebase.flutter.dev/docs/firestore/usage/
FlutterFire provides support for dealing with realtime changes to
collections and documents. A new event is provided on the initial
request, and any subsequent changes to collection/document whenever a
change occurs (modification, deleted or added).
For what you are trying to achieve, it would be better to initially fetch a limited set of documents from your collection.
collectionReference.limit(42).get()
You can than fetch the next elements by using https://firebase.flutter.dev/docs/firestore/usage/#start--end-cursors

Using an additional Future inside of a StreamBuilder with Flutter

I am trying to pull favicons dynamically and place them as a leading icon in ListTiles which make up a ListView, which is all contained inside a StreamBuilder. The StreamBuilder 'stream' variable is a FirebaseFirestore instance shown below:
streamBuilder = StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('users').doc(snapshot.data.uid).collection('vault').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapShot){
So the stream is updated every time the snapshot is updated. The snapshot contains docs which are iterated through in a for loop to populate each listtile in the listview. Each doc contains a url. I need to use that 'url' to retrieve that Listtile's respective favicon icon.
import 'package:favicon/favicon.dart' as iconz;
for (var doc in querySnapShot.data.docs) {
String urlIcon = doc['urlIcon'];
iconUse = iconz.Favicon.getBest(urlIcon);
leading: iconUse,
title:...
The problem is that the getBest() function returns a future. i.e. Using 'await' for my favicon getBest() function is not allowed inside the StreamBuilder, and my StreamBuilder is already dependent upon the snapshot stream. How do I use an additional Future inside of a StreamBuilder that is already dependent upon a different stream?
You can use a FutureBuilder for leading: iconUse
import 'package:favicon/favicon.dart' as iconz;
for (var doc in querySnapShot.data.docs) {
String urlIcon = doc['urlIcon'];
//iconUse = iconz.Favicon.getBest(urlIcon);
leading: FutureBuilder(
future: iconz.Favicon.getBest(urlIcon),
builder: (context, snapshot2) {
if (snapshot2.hasData) {
return snapshot2.data; //or whatever object you want to return from your function.
}
),
title:...

Flutter - How to do an infinite card feed?

I want to display one by one each document of a collection from Firestore. A document is a card containing an image and a text with a button to show the next card. Basically, my app should have the same behavior as Tinder: display one card then the next one, and never twice the same.
At the moment, I made a StreamBuilder that call the last 10 cards in a Stack so each card is on top of each other (hiding the next one).
But with this solution I have 2 problems :
I don't know how to display the next item when the user tap on the
next button. The current card has to disappear to let the next one being visible.
The StreamBuilder call only 10 cards, I would like to load 10 more
cards when the user is almost at the end (at the card number 8 for
example) so the user don't even realize the loading in the background.
Maybe the StreamBuilder is not the solution at all. But I have no idea what is the best way to do it.
StreamBuilder(
stream: firestore.collection('Feed').orderBy('date').limit(10).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
var _docs = snapshot.data.docs;
if (_docs.isEmpty)
return Center(
child: Text("There is no more cards"),
);
return Scaffold(
body: Stack(
children: _docs.map((document) {
return Container(
color: Colors.white,
child: CardItem(...),
);
}).toList(),
),
);
});

Two StreamBuilderon on one screen with default empty screen shown when neither has data

I'm trying to create a Split View with two ListViews, one of them showing Tasks which are not completed and the second one containing completed tasks. I managed to make this work with a Column containing two Streambuilder. The problem I don't know how to solve is how to show a single default empty screen when neither of the two Streams have any values, due to the way Flutter works, I can't just return null. So I end up having two Empty default screens in my Column and on my screen.
What would be the right approach to make something like shown in the GIF with Firestore?
If I need to work with a single Stream and filter it inside dart, how can I make it work with ListView ?
I'd highly appreciate an example.
My Job class contains a boolean value jobDone which tells me in which list the Job has to go.
My current code:
return Column(
children: [
StreamBuilder<List<Job>>(
stream: getPendingJobsStream(database),
builder: (context, snapshot) {
return ListItemsBuilder<Job>(
...
);
},
),
ExpansionTile(
title: Text('Completed Tasks'),
children: [
StreamBuilder<List<Job>>(
stream: getCompletedJobsStream(database),
builder: (context, snapshot) {
return ListItemsBuilder<Job>(
...
);
},
),
],
),
],
);
You can check for snapshot state
builder: (context, snapshot) {
if(snapshot.connectionState ==ConnectionState.waiting){
return Center(child:CircularProgressIndicator();
}
if(snapshot.connectionState ==ConnectionState.done){
//check if snapshot has data
if(snapshot.hasData){
return ListItemsBuilder<Job>( ..

Flutter - ReOrder Data Stored in FireStore

As you can see, I am dragging Tasks by using ReorderableListView widget of Flutter.
The onReorder is able to drag the tasks up and down. However, when I close the app, all of the tasks go in the default order as it was arranged.
This is because my data which is being fetched from the CloudFireStore isn't changing its order, the way is it updated in my dragging.
Can anyone help me, how can I update the position of tasks stored as documents in Cloud FireStore, so that when I close the app and open it again, it shows new updated positions and not old positions of the task
The code :
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("Tasks")
.doc(_email)
.collection("User_Tasks_List")
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("Loading!...");
_docs = snapshot.data.documents;
return Theme(
data: ThemeData(canvasColor: Colors.transparent),
child: ReorderableListView(
children: _docs
.map((e) => InkWell(
key: ObjectKey(e),
onTap: () => _popupDialog(context, e),
onDoubleTap: () => FirebaseFirestore
.instance
.runTransaction(
(transaction) async {
transaction.delete(e.reference);
Fluttertoast.showToast(
msg: "Task has been deleted!");
}),
child: ViewHolder(e)))
.toList(),
onReorder: onReorder),
);
}),
You can add a parameter like index:x to items in firestore. Then you need to save the order of the ordered list items and store them in Firestore using the indexes.
When you fetch items you need to sort them by this index and you will get the same order.
objects.sort((a, b) => a.index.compareTo(b.index));