In my project I have a delete button that deletes some data from firebase firestore, what i'm trying to do is showing a snackbar with undo word, when it's pressed the process will be cancelled and no data will be deleted. I haven't found a solution yet. Is it possible?
You can use a Timer to create a callback that can be canceled anytime before the timer runs out.
import 'dart:async';
void main() {
final cancelable = Timer(Duration(seconds: 5), () {
// The delete function should be here
print('canceled after 3 seconds');
});
Timer(Duration(seconds: 3), cancelable.cancel);
}
In this example the print() callback gets canceled after three seconds, but you could pass the cancelable.cancel() function as a callback for the undo button and cancel the deletion on Firestore by clicking it before the Timer runs out
Deletes in Firestore are permanent; once you delete a document it is gone.
If you want to give the option to undo the deletion, you'll want to:
Either create the document in another collection before deleting it from the main one. For example, a common implementation is to have a subcollection with deleted documents, or even the entire history of each document.
Or you mark the documents as deleted, instead of really deleting them. This type of marking is typically referred to as a tombstone.
Related
In my app, I am now using a "refresh function" to update a list in Provider. When the user swipe, I call Refreshlist in my provider and with NotifyListeners() it updates my UI just fine. (The UI is linked to the list _myEleves).
I am afraid that users might use this "refresh" button too many times making unnecessary calls and "reads" on firebase and so increasing artificially the number of reads so the costs of Firebase.
Here is the code :
Future<void> refreshEleveList() async {
final DocumentSnapshot<Map<String, dynamic>> docInfo =
await FirebaseFirestore.instance
.collection('familyAccounts')
.doc(_accountEmail.toLowerCase())
.get();
_mesEleves = (docInfo['mesEleves'] as List<dynamic>)
.map((data) => Eleve.fromMap(data))
.toList();
notifyListeners();
}
I have been reading about STREAMS a lot, but I just can't get it right on how to start this stream, the listening to the changes on Firebase inside my PROVIDER file, so that changes will be made to "_myEleves" list.
What I want to do is that each time a change on firebase happens, it updates my list "_myEleves". Is there a simple way to do this ?
My Provider covers the whole app (I use it in the MAIN file). I thought of adding a StreamProvider, but the thing is I don't want this stream to start until user is authentified etc... and userInfo is first downloaded.
Right now : when user logs in : it downloads from firebase all necessary info, _mesEleves being one of them (This is for a teacher). Whenever a new student joins the group, it modifies firebase and so it should stream down this info into "myEleves" list on the teacher account.
Anybody can help ?
In the flutter firestore codebase you can find a comment about the stream it creates when you run snapshots() on a query.
// It's fine to let the StreamController be garbage collected once all the
// subscribers have cancelled; this analyzer warning is safe to ignore.
StreamController<QuerySnapshotPlatform> controller; // ignore: close_sinks
I want to wrap my resulting snapshot streams with a BehaviorSubject so I can keep track of the latest entry. This is useful when I have one stream that is at the top of a page that I want to be consumed through different widgets farther down in my tree without reloading the stream each time. Without keeping track in a BehaviorSubject or elsewhere if a new widget starts listening to that stream it does not get the most recent information from Firestore as it missed that event.
Can I also not worry about closing the behavior subject I am going to create as it will be garbage collected when there are no more listeners? Or is there another way to achieve what I am wanting?
I'm picturing code like this:
final snapshotStream = _firestore.collection('users').snapshots();
final behaviorSubjectStream = BehaviorSubject();
behaviorSubjectStream.addStream(snapshotStream);
return behaviorSubjectStream;
This will get a complaint that I don't close the behaviorSubjectStream. Is it ok to ignore?
That depends on how you listen to the subject.
From what you describe, it sounds safe to ignore the hint. When the subscriptions that listen to the subject are cancelled, the subject will be cancelled as well (when the garbage collector finds it).
There are situations where you have a subscription that is still listening, but you want the subject to stop emitting. In that case you will need to close() the subject.
You can test that the subject is correctly cancelled by adding
behaviorSubjectStream.onCancel = () {
print("onCancel");
};
Then you can test it by playing around with your app.
I am new to firebase and noSQL databases. I've read the docs and watched instructional videos but there is one concept I haven't fully grasped.
Why is it that querying the database requires a listener such as .on("value")?
Since these listeners are triggered whenever there is a change of sorts (nodes created, edited, children created) shouldn't there be a more direct way of getting the data from the db? Such as
ref.orderBy("age"). equalTo(30).get()
A method to just get what's in there at the time he instruction is executed, without having to listen to some sort of event?
In SQL it's not like you have to wait for something to change in your db to make this query work:
SELECT * FROM TABLE WHERE X == Y
PS: I know .once() also exists, but my question is more about: if my db never changed, how would I be able to query it and always get the same query result/snapshot?
You didn't define a platform so I will use this Swift pseudo-code example. Suppose we want to get a list of all users, one time.
let usersRef = my_firebase.child("users")
usersRef.observeSingleEvent(by: .value, with: { snapshot in
//all users are returned to the app in the snapshot
// .value means 'give me everything in the specified node'
// so then we can iterate over the snapshot to get each users data
}
You can call the above code any time to get the list of users and it does not add an observer (listener) to the node. It's a one-shot.
Whereas, if we want to be notified of users that are added
let usersRef = my_firebase.child("users")
usersRef.observe(by: .childAdded, with: { snapshot in
//upon first calling this, each user will be sent to the app in the snapshot
// and after that, any user that's added
}
The above code attaches an observer (listener) to the users node and whenever a user is added, it's provided to the app via the snapshot.
Note the use of .observeSingleEvent vs .observe and .value to get everything in the node vs .childAdded to get a specific node.
I'm creating the user Follows / Unfollows portion of my app and I'm coming up with several issues. The latest deals with following a fellow user and saving some reference info under a "follows" node. I'm looking at my Firebase console while doing this in my simulator and noticed the information appears correctly in green and 1 second later changes to red in order to delete. Here is how I'm saving things...
Future<Null> follow() async {
await fb
.child('users/${currentUser}/Follows/${friendsUID}')
.update({"name": userName, "photo": userImage});
}
I'm running the following code before my Widget build in order to determine if the user is being followed and which buttons should build accordingly....
void _friends() {
fb.child("users/${userid}/Follows/${id}").onValue.listen((Event event) {
setState(() {
// this will return true or false which is what you want based on your code :)
friends = event.snapshot.value == null;
});
});
}
And occasionally I'm receiving the following error message, I'm not sure if its related to saving the Firebase though...
setState() called after dispose(): FriendsVideoPageState#51581(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
I am coding a basic meteor app and I ask myself how to notify other session of a change.
I have a basic template showing details of a document.
And someone can delete this document,
How can I notify other session that was watching this document, that it was deleted and redirect them?
If your document is in a collection and the delete corresponds to removing the document from the collection, you can use Meteor's observe on the collection to register a callback when the document you're watching is removed:
Documents.find({_id: myDocumentId}).observe({
removed: function () {
console.log('document removed');
}
});
and from there do any DOM/Session manipulation you want to notify clients of the change.
If you display your document by accessing directly your collection client-side (i.e. you don't use a method), your page fields values should reactively disappear since the document does not exist anymore.
What you can do is add a field deleted to your document and when it changes to true, you display your notification. I would advise to use something like a modal, so the user cant dodge it (when he close it, you redirect).
It also means that instead of deleting a document, the other user just change its deleted field to true. Once you set it to true you can also set a time differed function to effectively delete the doc for example 5mn later:
Meteor.setInterval(function () {
Document.remove(yourDocumentID);
}, 300000)