Home > Software engineering >  Why Riverpod provider goes into the loading state when there's already value?
Why Riverpod provider goes into the loading state when there's already value?

Time:12-05

Minimum reproducible code:

class FooPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncValue = ref.watch(provider1);
    print('loading: ${asyncValue.isLoading}, value: ${asyncValue.valueOrNull}');
    return Container();
  }
}

final provider1 = StreamProvider<int>((ref) {
  final stream = ref.watch(provider2);
  return stream.maybeWhen(
    orElse: () => Stream.value(0),
    data: (x) {
      print('data = $x');
      return Stream.value(x);
    },
  );
});

final provider2 = StreamProvider<int>((ref) async* {
  await Future.delayed(Duration(seconds: 3));
  yield 1;
});

Output:

flutter: loading: true, value: null // Why value != 0
flutter: loading: false, value: 0
// After 3 seconds...
flutter: data = 1
flutter: loading: true, value: 0 // Why value != 1 
flutter: loading: false, value: 1

There are two questions.

  1. When I have already provided a default value in orElse callback, then why the first line doesn't print that value instead of going in the loading state and printing null.

  2. Once I get the data after 3s, why does the provider print loading: true, value: 0?

CodePudding user response:

When a StreamProvider is first created, it will not have received any data from the stream yet, so its isLoading property will be true and its valueOrNull property will be null. This is why the first line of the output prints loading: loading: true, value: null.

Once the stream emits data, the StreamProvider will update its valueOrNull property with the latest value from the stream. However, it is possible that the ConsumerWidget has not yet rebuilt to reflect this change. When the ConsumerWidget rebuilds, it will receive the latest value from the stream and print it. This is why the fourth line of the output prints loading: false, value: 1.

The orElse callback is used to provide a default value for the StreamProvider when the stream has not emitted any data yet. It does not affect the StreamProvider after it has received data from the stream. This is why the second and fifth lines of the output print loading: false, value: 0.

In summary, the StreamProvider will initially be in the loading state and have a null value until it receives data from the stream. Once it has received data, it will update its value to reflect the latest value from the stream, but this update may not be reflected in the ConsumerWidget until it rebuilds. The orElse callback only provides a default value for the StreamProvider when the stream has not emitted any data.

CodePudding user response:

provider2 changed (it went from loading to having data). And since provider1 depends on it, provider1 got recomputed.

This ends up creating a new stream. The provider then listens to the new stream. Listening to a stream is an async operation, therefore for one frame provider1 does not have access to the new value exposed by the stream. Hence, you got a loading state.

This shouldn't be a problem though

  • Related