Flutter: StreamBuilder stream not updating - firebase

My StreamBuilder didn't refresh after changes to the stream, and also the stream does not update too, when fetching data from Firebase and store it to local SQLite database;
And here is my code for listen to data changes from Firebase and then write those new chats to local:
/// LISTEN TO THE STREAM FROM FIREBASE AND THEN WRITE THE NEW MESSAGES TO THE LOCAL DATASE
// Fetch normal messages
firestore.fetchMessagesStream(chatRoomId).listen((event) async {
for (QueryDocumentSnapshot message in event.docs) {
_database.insert(
'Messages',
message.data()!,
conflictAlgorithm: sql.ConflictAlgorithm.replace,
);
}
});
// Fetch calls
firestore.fetchCallsStream(chatRoomId).listen((event) async {
for (QueryDocumentSnapshot call in event.docs) {
_database.insert(
'Calls',
call.data()!,
conflictAlgorithm: sql.ConflictAlgorithm.replace,
);
// Fetch posts
firestore.fetchPostsStream(chatRoomId).listen((event) async {
for (QueryDocumentSnapshot post in event.docs) {
_database.insert(
'Posts',
post.data()!,
conflictAlgorithm: sql.ConflictAlgorithm.replace,
);
}
});
And here the code for fetching data from the Local SQLite database:
/// STREAM FOR LISTENING THE CHANGES IN THE LOCAL DATABASE
Rx.combineLatest3(
_database.query(
'Messages',
where: 'chatRoomId = ?',
whereArgs: [chatRoomId],
).asStream(), // Returns the stream of the Messages Table in local databases
_database.query(
'Posts',
where: 'chatRoomId = ?',
whereArgs: [chatRoomId],
).asStream(), // Returns the stream of the Posts Table in local databases
_database.query(
'Calls',
where: 'chatRoomId = ?',
whereArgs: [chatRoomId],
).asStream(), // Returns the stream of the Calls Table in local databases
(List<Map<String, dynamic>> streamingMessages,
List<Map<String, dynamic>> streamingPosts,
List<Map<String, dynamic>> streamingCalls) {
/// VERY IMPORTANT: THE FOLLOWING PRINT STATEMENT WILL BE PRINT OUT EVERYTIME A NEW CHAT ARRIVE
/// VERY IMPORTANT: THE FOLLOWING PRINT STATEMENT WILL BE PRINT OUT EVERYTIME A NEW CHAT ARRIVE
/// VERY IMPORTANT: THE FOLLOWING PRINT STATEMENT WILL BE PRINT OUT EVERYTIME A NEW CHAT ARRIVE
/// VERY IMPORTANT: THE FOLLOWING PRINT STATEMENT WILL BE PRINT OUT EVERYTIME A NEW CHAT ARRIVE
/// VERY IMPORTANT: THE FOLLOWING PRINT STATEMENT WILL BE PRINT OUT EVERYTIME A NEW CHAT ARRIVE
print('MySqlite: chat stream changes!');
final List<dynamic> contents = [...streamingMessages, ...streamingPosts, ...streamingCalls];
return contents;
},
);
}
The expected timeline when sent a new message will be:
User sent a message --> trigger changes in Firebase --> trigger the LOCAL SQLite to add new data --> Because our SQLite have added new chats, so our ChatScreen should refresh & also a new debug message: 'MySqlite: chat stream changes!' should be printed out again in the console since the ChatScreen is listening to the SQLite Database Stream.
But the actual result is:
User sent a message --> trigger changes in Firebase successfully --> but I DIDN'T see the screen being refreshed NOR new debug message in the console...
I've been struggle with this issue for days, and I don't know why the result is not what I want, if I let the ChatScreen to directly listen to Firebase, it works!*
UPDATE:
I just found out that if I rebuild the ChatScreen (pop the screen, and then open it again), or setState when sending a new message, I can see the new message, it proves that the Message did go into the SQLite database after sent, but it just did not trigger the StreamBuilder. So it might be something wrong with the Fetching Stream.
ANSWER:
I just found out that the SQLite.query function cannot be fetched as a Stream, and I thought I can by using "asStream" method, but this does not do anything, it is a missed feature that SQLite package didn't implement yet, so I add the sqlbrite package that works as a wrapper of the original SQLite package, and it has some additional feature such as querying data as a Stream. ^_^

I just found out that the SQLite.query function cannot be fetched as a Stream, and I thought I can by using "asStream" method, but this does not do anything, it is a missed feature that SQLite package didn't implement yet, so I add the sqlbrite package that works as a wrapper of the original SQLite package, and it has some additional feature such as querying data as a Stream. ^_^

Related

How can I listen only to doc that changed in Firestore stream?

I have the following Firestore stream:
FirebaseFirestore.instance.collection("users").orderBy("timestamp", descending: true).snapshots().listen((value) async{
// here i noticed if doc was added or updated so the stream will take action to all docs
in that collection
});
For example if i write .set() to same collection like following:
.set({
name : 'Alex'
});
and I had another doc with the field name 'jack' then I used the following in the previous stream:
stream...{
if(value['name]=='Alex'){
print('ok');
}
if(value['name]=='jack'){
print('ok'); // ok here should print 'ok' if it first snapshot ,
but it will always print 'ok' whatever the new doc equal
my condition or not
}
}
How can I only listen to doc that newly added or updated or changed ?
As #Dharmaraj recommended, docChanges() method informs us of all the changes that have occurred in our database.
docChanges() method should be used on the snapshot parameter. Since that gives an array of document changes, we iterate over with a loop. If you make a console.log of the local variable of the loop, you will see that this returns each change (in the form of an object).
Example:
db.collection('recipes').onSnapshot(snapshot => {
// console.log(snapshot.docChanges());
snapshot.docChanges().forEach(change => {
console.log(change);
});
});
Do not forget that docChanges() should always be a function and never an array.
Additionally, you can add server timestamp. You can set a server timestamp in a field in your document to track when the server receives the update.
Each field receives the same server timestamp value when several timestamp fields are updated during a transaction.

Firebase Firestore Data Not Visible Issue

I ran into issue where Firestore is not reflecting data on client.
Lets say when I create cart manually from Firebase Console it reflects on client side but when I create Cart from client side it does not reflects, although a empty card appears but its null. Assist me on this
Firestore Rules are Public
Data Calling Method
public async Task<ObservableCollection<T>> GetCollection(string collection)
{
var tcs = new TaskCompletionSource<ObservableCollection<T>>();
await DataStore.Collection(collection).Get()
.AddOnCompleteListener(new OnCollectionCompleteListener<T>(tcs));
return await tcs.Task;
}
Thanks
I resolved this issue on my own. While working with Firestore, I understood that if you keep any field null in Firestore, the data inside the document will not be visible. So make sure to not leave any field empty.

Flutter Streambuilder on new data

im currently using a streambuilder to get my order data from firestore for a restaurant app and i want to make something when new order arrives, like a notification or something. How can i know if a new order added to database?
You can subscribe to the stream you give in the "stream" field of your StreamBuilder and use that subscription created to call the onData property of StreamSubscription. It will call the function present inside it whenever new data is added.
Example:
Stream stream = <YOUR STREAM HERE>;
StreamSubscription streamSub;
String actionTaken(dynamic data){
return data;
}
//call the code below in initState if you want to start it with the app.
streamSub = stream.listen(actionTaken);
subscription.onData((data){
});

Fetching data from sqflite and sending it to firebase

I made a local database using sqflite to save data locally and i need to send it to a firebase in need
so far the function i used to get the data is:
Future<List> getNameAndPrice() async{
Database db = await instance.database;
var result = await db.rawQuery('SELECT $columnName , $columnAmount FROM $table');
return result.toList();
}
the other function i use in the other page of flutter to get the data is:
Future sendtofirebase() async {
var orders = await dbHelper.getNameAndPrice();
print(orders);
}
so far the print is just to check the data i get and i get the data in this format:
I/flutter (21542): [{name: Regular burger, amount: 2}, {name: Cheese
burger, amount: 1}]
i just want to find a way to like fetch this data from the variable and then send it to the firebase database i've made firestore collectionreference and everything i just don't know how can i get the name alone and send it then send the amount of the item etc.. for each order (how to get each item individually and send it to the firebase db).
really looking forward for answers cause i've spent time looking and i am stuck there..
ps. i am still a beginner and i even don't know if i should use both sqflite and firebase
other than just saving everything in the firebase itself.. thank you for reading.

Firestore document stream and update

In the init, of my application I register a stream to one of the documents stored in firestore. Later I update a timestamp field in the same document. I should be getting one callback from the stream since there is only 1 update.
However, I am getting 2 callbacks -
Where that updated field is null
Where that updated field has the correct updated value
Any ideas why?
CollectionReference collectionReference = FIRESTORE.collection("users");
if(streamSub == null) {
streamSub = collectionReference.document(documentID).snapshots().listen((onData){
onData.data.forEach((k,v) => debugPrint(k + " = " + v.toString()));
});
}
//Update field
Firestore.instance
.collection("users")
.document(documentID)
.updateData({"Time" : FieldValue.serverTimestamp() })
You're getting two callbacks because of your use of FieldValue.serverTimestamp(). That value is actually a token that gets sent to Firestore servers, where the timestamp is determined and finally written to the database. Locally on the client, the value isn't known at the time of the write, however, a write of the document still happens in the local cache.
Your listener is first getting the local cache write (before the timestamp is known), then again from the server after the timestamp is known. You can look at the snapshot metadata to figure out the source of the data if that's important to you.

Resources