I'm working with Sembast nowadays and was wondering If there's any way to create a stream of data that could get me all the values inside the DB. My requirement is to setup a listener on that stream so that whenever the data change is triggered, I could do something with it.
Documentation on Sembast is pretty limited and I'm now sure how I can do this. Usually I use the .find method to fetch all the values from within my db. I've been using a stringMapFactory to store my records.
Can we do this ? Any help would be really appreciated.
Sorry for the poor documentation.
It is quite similar to firestore. You can listen to all changes in a store
// Track every store changes
var query = store.query();
var subscription = query.onSnapshots(db).listen((snapshots) {
// snapshots always contains the list of all records
// ...
});
Basically you have a query on the store (with or without filter) that you can query or listen for changes.
See https://pub.dev/documentation/sembast/latest/sembast/QueryRef/onSnapshots.html
https://github.com/tekartik/sembast.dart/blob/master/sembast/doc/change_listener.md
If you use Hive as db, may use Hive Box as listenable.
ValueListenableBuilder<Box<YOUR_BOX_MODEL>>(
valueListenable: box.listenable(),
builder: (context,value,child){}
)
Related
I'm creating a fitness app, and so far I came with the following structure:
Workout
difficulty (String)
duration (String)
exerciseSets (Firestore ref)
ExerciseSet
repNumber (int)
exercise (Firestore Ref)
and the Exercise object has a few fields describing the exercise.
So right now if i want to retrieve a whole workout, i need to do at least 3 calls to firestore, one for the Workout, then i get the ExerciseSets by ref (and there are usually a few in each workout) , and then the Exercise by ref as well..
ExerciseSet and Exercise objects are shared between workouts, thats why i have them in a different doc.
Also after retrieving all 3 or more snapshots from Firestore, i need to iterate through them to map it to my model.. i do something like this currently:
for (var exerciseSet in fsWorkout.exerciseSets) {
var fsExerciseSet = await _getFsExerciseSet(exerciseSet.ref);
var set = ExerciseSet.fromFirstoreObject(fsExerciseSet);
var fsExercise = await _getFsExercise(fsExerciseSet.exerciseRef.ref);
set.exercise = Exercise.fromFirestoreObject(fsExercise);
exerciseSets.add(set);
}
return Workout(fsWorkout.difficulty, fsWorkout.duration, exerciseSets);
Does this make sense? or is there a more efficient/easy way to achieve this? It feels like I over complicated stuff..
And is there any advantage to using firestore reference instead of just a String field with the ID?
Thanks!
EDIT: I would like to mention that in my case all the data is added once by me, and the client reads the data and needs to retrieve a Workout object that contains all the ExerciseSet and Exercise objects.
You are actually applying an SQL normalization data-modelling strategy to a NonSQL database. This is not the most efficient approach...
In the NoSQL world, you should not be afraid to duplicate data and denormalize your data model. I would suggest your read this "famous" post about NoSQL data-modelling approaches.
So, instead of designing your data-model according to SQL normalization you should, in the NoSQL world, think about it from a query perspective, trying to minimize the number of queries for a given screen/use case.
In your case a common approach would be to use a set of Cloud Functions (which are executed in the back-end) to duplicate your data and have all the ExerciceSets and corresponding Exercises in your Workout Firestore document. And to keep all these data in sync, you would also use also use Cloud Functions.
You could also go for an intermediate approach where you only add the ExerciceSets data to a Workout and when the user wants to see an ExerciceSet details (e.g. by clicking on the ExerciceSet link) you query the corresponding Exercises.
I have been using onSnapshot successfully to alert my code to changes in underlying data, as in
// Set up to listen for changes to the "figures" collection, that is,
// someone has created a new figure that we will want to list on the screen.
setFiguresListener: function () {
// `figuresCR` is a collection reference defined elsewhere
return this.figuresCR.onSnapshot((iFigs) => {
iFigs.forEach((fSnap) => {
const aFigure = figureConverter.fromFirestore(fSnap, null);
const dbid = aFigure.guts.dbid; // ID of the "figure" in the database
nos2.theFigures[dbid] = aFigure; // update the local copy of the data
});
nos2.ui.update();
console.log(` Listener gets ${iFigs.size} figures`);
});
But I now read about on in the docs. It explains:
[The on() function] Listens for data changes at a particular location.
This is the primary way to read data from a Database. Your callback
will be triggered for the initial data and again whenever the data
changes. Use off( )to stop receiving updates. See Retrieve Data on
the Web for more details.
The syntax is a bit different, and on() seems to do much the same as onSnapshot().
So what is the real difference? Should we be using on() instead of onSnapshot()?
on() is an operation for reading from Firebase Realtime Database. That's a completely different database with different APIs than Firestore. They have essentially no overlap. There is no on() operation with Firestore.
If you're working with Firestore, ignore all the documentation about Realtime Database, and stick to using onSnapshot() for getting realtime updates.
Other tyros who fall into this tar pit: in the API doc pages, you might think that since firestore is a database under firebase, you could look for help under firebase.database. But no: look only in the next section, firebase.firestore.
I'm new to dart/flutter and am building an app right now that keeps track of some data on each user in a firstore database. I have a 'Users' collection which contains a document for each user, and one of the fields in the user document is the "UID" received through firebase_auth.
That being said, to make sure I have access to the latest copy of a user document, I hold a Stream. I want to somehow access the "UID" field from the latest snapshot in the stream and then do some other operations with it.
Is there anyway to do this? Am I using/understanding streams incorrectly?
If you only ever need the UID to build other widgets, you can simply use a StreamBuilder which will rebuild its children whenever a new value is emitted from the stream (which you get a copy of). However, if you need to access the latest UID at some arbitrary point of time, check out RxDart's BehaviorSubject which provides constant-time synchronous access to the latest emitted value, if one exists. It is very helpful for handling state in general.
I want to get all documents from firestore collection without using stream builder, otherwise I will have nested streams and in the last stream I am getting a null. Initial data is not solving my problem.
Now I make a stream builder for that collection, when a snapshot is chosen I jump on other widget and there I also make a stream builder and here is the problem.
I want to make stream builder only in the last widget of the scenario, because only there realtime data update is needed.
You can use FutureBuilder with the following method
await Firestore.instance.collection("books").getDocuments()
Get Realtime data using this:
Firestore.instance
.collection('books').snapshots().listen((querySnapshot){
});
I got a setup with a BehaviorSubject. Whenever I load data from an API I call Add to push the data and use it from the stream in a page. Now I want to format some of the data before it is consumed from the stream. As far as I can see Map seems to be the thing to use for a transformation of data, but I'm not sure if it should be set up once or I need to ensure Map is called when data is when data is loaded from an. Currently, I expose a behaviorSubject but if I use map should I then Expose a new stream based on the map?
This is my current setup. From a StreamBuilder in the page I'm using myRides. I can see the applyUnits gets called but its like data is not on the stream anymore
final BehaviorSubject<FeedLoad> ridesSubject = new BehaviorSubject<FeedLoad>();
Observable<FeedLoad> get myRides => ridesSubject.stream.map((item)=> item.applyUnits());