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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With