Reputation: 790
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?
Upvotes: 4
Views: 2023
Reputation: 5611
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
Upvotes: 2