Riverpod's StreamProvider stuck in loading when reading Hive's box | Flutter
Hey I think I have some solution for you, as far as I understand the watch method of a Box will be empty the first time it runs, it doesn't matter if the box has something because watch only fires when there is a change since the moment it starts listening so it will be in loading state until you change the key 0 value somewhere in your app.
I'm not really a fan of this behavior and it would be better if the watch method returns the initial data the first time
final localUserStream = watch(localUserStreamProvider);
return localUserStream.when(
data: (data) => data == null ? Container(color: Colors.blue) : data.isEmailVerified ? Container(color: Colors.green) : Container(color: Colors.purple),
loading: () => TextButton(
onPressed: () async {
final box = await watch(localUserBoxFutureProvider.future);
await box.put(0, User()) // this is just an example that when you tap the button the stream actually change to data
},
child: Text('Update me'),
),
error: (e, s) => Container(color: Colors.red)
);
UPDATE
This can be a bit tricky (and I haven't tested it) but you could stream an initial value in your StreamProvider
final localUserStreamProvider = StreamProvider<User>((ref) async* {
final usersBox = await ref.watch(localUserBoxFutureProvider.future);
yield* Stream.value(userBox.get(0, defaultValue: User())); //or getAt(0)
yield* usersBox.watch(key: 0).map((boxEvent) => boxEvent as User);
});
This way it will show the value saved in your box at the beggining of your app, and afterwards the change of events related with that key
Florian Leeser
Updated on December 26, 2022Comments
-
Florian Leeser over 1 year
I am trying to stream the users data that I saved into a box called 'users' with Hive. This is for showing a screen based on the information provided from the user. For now, the box contains no data, so I expect the following code to show a blue screen. Otherwise it should be green or purple. It is mandatory for me to know when reading the value finished, so that I know wether the returned value
null
means the data did not load yet or the users box is empty.I am using Riverpod for state management and this approach.
I implemented the following two providers
final localUserBoxFutureProvider = FutureProvider<Box>((ref) async { final usersBox = await Hive.openBox('users'); return usersBox; }); final localUserStreamProvider = StreamProvider<User>((ref) async* { final usersBox = await ref.watch(localUserBoxFutureProvider.future); yield* usersBox.watch(key: 0).map((boxEvent) => boxEvent as User); });
and would like to use them like something like this:
final localUserStream = watch(localUserStreamProvider); return localUserStream.when( data: (data) => data == null ? Container(color: Colors.blue) : data.isEmailVerified ? Container(color: Colors.green) : Container(color: Colors.purple), loading: () => Container(color: Colors.yellow), error: (e, s) => Container(color: Colors.red) );
The problem with this implementation is that it always shows a yellow screen, meaning its stuck in loading. Any ideas?
-
Rémi Rousselet over 3 yearsIs the "yield" reached? And if so, is the "map" called? It is possible that nothing is pushed on your stream
-
Florian Leeser over 3 years@RémiRousselet I removed the "map" and then only had the check
data==null ?
. Same behaviour. How can I check your "yield" part?
-
-
Florian Leeser over 3 yearsThank you! This gives me a good explanation on why it does not work, but not how to implement a solution.
-
EdwynZN over 3 yearstry my update and tell me if it helps you
-
Florian Leeser over 3 yearsThe problem with your updated implementation is the following: If there is no user inside the database, the stream would yield a null value all the time, which means: Loading cycle. I will solve this by yielding an empty User object if the stream value is null. With this approach, null should only be emitted when the ProviderReader is really "loading", I guess.
-
EdwynZN over 3 yearsuserBox.get(0, defaultValue: User()); or some factory constructor of User (User.empty() for example)
-
Florian Leeser over 3 yearsYou were right! It works! This is a very clean workaround for the "initial-null-problem". Nevertheless it needs some kind of different data type (e.g. empty User object).
-
EdwynZN over 3 yearsthat will always depends on what you need to show first when there is no value saved in the box, if this helped you solve the problem you can now accept the answer