Flutter Streambuilder on new data - firebase

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){
});

Related

Flutter: I want to get data from firebase realtime database, everytime the application loads

I managed to get the data from the firebase real-time database, but I can't use it inside the app. Based on the data, I want to show different screens (Activities). For example: If the user paid then show the "Paid" screen, otherwise show the "Pay Now" screen. I couldn't use the variable used inside eg: paid_status , outside from the below scope. So, I declared it as a global variable in another file.
package_stream.listen((DatabaseEvent event) {
globals.paid_status = event.snapshot.value;
});
With the above code, it works but every time, the user closes the app and login again, the variable value is changed or kept old (after the database is updated). I want to get the value from the database every time the user opens the app or logout and login again.
I used below code to get the data:
setState(() {
DatabaseReference paid_stat = db_reference.child('Records/${uid}/Paid');
// Get the Stream
Stream<DatabaseEvent> package_stream = paid_stat.onValue;
// Subscribe to the stream!
package_stream.listen((DatabaseEvent event) {
globals.paid_status = event.snapshot.value;
});
});
I solved it myself :
I created an async Future function using get() to return the snapshot.
Using the snapshot.value, I showed different screens/activities.

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 stream not updating

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. ^_^

Stream/Bloc/Repository/Firebase data flow Flutter

I'm starting with flutter as I want to port my swift app to Flutter, but I'm getting stuck understanding the pattern Bloc/Repository/Firebase as I'm following the tutorial https://bloclibrary.dev/#/flutterfirestoretodostutorial dough I use the real time database, not Firestore.
My swift app is basically a map where you can add Alerts at your actual coordinates. The Alert get sent to Firebase and the firebase observer on the map updates the map showing the just added alert.
The above tutorial should help me porting my app. I'm just not sure I do understand the logic behind the code.
My concerns are 2:
First. There is an Entity layer between the model object and the firebase object. It is explained that this will facilitate having different Data providers, but I don't really see it facilitating anything. In the Model class there is a toEntity() and a fromEntity() conversion method, and in the Entity class there is a fromSnapshot() and a toDocument() conversion method. I don't see what's the point here. Is it really necessary? What's wrong with doing the conversion directly in the Model class , having different methods for each Data provider?
Second. Inside the TodoBloc I can't follow the logic.
The first event that is sent to the bloc at AppStart is LoadTodos.
BlocProvider<TodosBloc>(
create: (context) {
return TodosBloc(
todosRepository: FirebaseTodosRepository(),
)..add(LoadTodos());
In the mapEventToState() method of TodoBloc that event gets mapped to this Stream:
Stream<TodosState> _mapLoadTodosToState() async* {
_todosSubscription?.cancel();
_todosSubscription = _todosRepository.todos().listen(
(todos) => add(TodosUpdated(todos)),
);
}
So far so good. As I understand this subscribes to the todos() Stream ()
#override
Stream<List<Todo>> todos() {
return todoCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
.toList();
});
}
and this should be the equivalent of the firebase observer in my swift app. It this part inside the listen closure I'm not sure to understand: (todos) => add(TodosUpdated(todos)) .
This sends to itself (TodoBloc) a TodosUpdated event on which the bloc will map this Stream:
Stream<TodosState> _mapTodosUpdatedToState(TodosUpdated event) async* {
yield TodosLoaded(event.todos);
}
which is this:
class TodosLoaded extends TodosState {
final List<Todo> todos;
const TodosLoaded([this.todos = const []]);
#override
List<Object> get props => [todos];
#override
String toString() => 'TodosLoaded { todos: $todos }';
}
Is this the actual list of Firebase objects? Does the todos() Stream return the entire node every time a new object is added in Firebase?
In my swift app the observer returns only the .childAdded after the first download of the node.
Should I use the firebase_database package that has a FirebaseList class(https://pub.dev/documentation/firebase_database/latest/ui_firebase_list/FirebaseList-class.html) that will just return a List on any change on the node as my observers do in my swift app?
Sorry for this very long and messy question, but I'm quite lost here starting with bloc pattern.
Thank you very much for your time and help.
Ok, I I think I understood the logic behind it, but if you see that I didn't get it right please correct me as at this stage of getting into a new paradigm is very important not to carry any misconceptions.
todos() is the Stream coming from Firebase and returns a List<Todo>.
_mapLoadTodosToState() is the bloc method that attach a bloc listener to todos() and in the .listen(onData) callback, it sends to the bloc an TodosUpdated(todos) event containing the latest list.
TodosUpdated(todos) gets mapped to _mapTodosUpdatedToState, which yields
TodosLoaded(event.todos) , the new state that BlocProvider uses to build the UI.
Thank you and I hope this will help others struggling to master BloC pattern at a more complex level.
Cheers

Firebase React Native fetch data

I am trying to do an app on react-native with a feed. On my main screen, I go fetch the data :
fetchData() {
firebase.database().ref(`/posts/${group}`).on('value', async snapshot => {...}
}
when I want for example to like a comment of the post, I first push the data into firebase with different queries, for example :
export const likeComment = (...) => {
firebase.database().ref(`/posts/${group}/${post}`).update
({
updatedAt: firebase.database.ServerValue.TIMESTAMP
});
firebase.database().ref(`/posts/${group}/${post}/lastComments`).set(...);
But I realized that my first function fetchData was called 3 times.
then I grouped my queries like :
let updates = {}
updates[`/posts/${group}/${post}/lastComments`] = {...};
updates[`/posts/${group}/${post}`] = { ... };
firebase.database().ref().update(updates);
Then, fetchData was called still 2 times.
I was wondering if that was the best way to do it, and why does my function fetchData was still called twice.
Thanks for your help
It's hard to tell from the limited code, but my guess is that fetchData is not actually being called more than once, but instead the snapshot is firing when you make updates to your Firebase database.
The part of your code where you say .on('value', async snapshot => you're setting up a listener to send you updates any time that value changes.
So my guess is that your three invocations are:
The first time you actually call fetchData and set up the
listener
Your .on( snapshot listener firing when you call
update
Your .on( snapshot listener firing again when you
call set
This push-based database workflow is one of the main benefits of Firebase. If you only want to get the data once and not get live updates, you can call .once( instead of .on(
https://firebase.google.com/docs/database/web/read-and-write

Resources