River_pod: ProviderScope override "forgets" the StreamProvider value during navigation

Created on 24 Jun 2020  ยท  6Comments  ยท  Source: rrousselGit/river_pod

Describe the bug
When creating a StreamProvider with a value of Stream.value(null) and then override it in another page then this should only be null until it is set. Right now it seems like it resets when navigating with an animation.

Have added a deugPrint(user.name) to easily see the behavior in the console.

To Reproduce
Click the ListTile and look at the log.

class User {
  String id;
  String name;

  User.empty() {
    id = "";
    name = "";
  }

  User.other() {
    id = "12345";
    name = "Tester";
  }

  @override
  bool operator ==(Object other) => identical(this, other) || other is User && runtimeType == other.runtimeType && name == other.name;

  @override
  int get hashCode => name.hashCode;
}

final userProvider = StreamProvider<User>((ref) {
  return Stream.value(User.empty());
});
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: PageOne(),
      ),
    );
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      // This override is causing the issue.
      // If this override would be at the material app
      // then the debugPrint will always print Tester
      overrides: [
        userProvider.overrideAs(
          StreamProvider(
            (ref) => Stream.value(User.other()),
          ),
        ),
      ],
      child: Scaffold(
        body: OpenContainer(
          closedElevation: 4,
          closedBuilder: (BuildContext context, VoidCallback openContainer) {
            return Consumer((context, read) {
              final user = read(userProvider).data == null ? User.empty() : read(userProvider).data.value;
              debugPrint(user.name);
              return Container(
                height: 100,
                child: ListTile(
                  title: Text(user.name),
                ),
              );
            });
          },
          openBuilder: (context, __) {
            return Scaffold();
          },
        ),
      ),
    );
  }
}

Do not forget to add dependencies in .yaml

  animations: ^1.1.0
  flutter_riverpod: ^0.1.0

Expected behavior
When running this the debugPrint(user.name) should not be empty during navigation.

bug

Most helpful comment

Oh, I know

It's the good-old "a provider is scoped to its route"

By using ProviderScope inside a route, the override is not accessible to other routes.

All 6 comments

Oh, I know

It's the good-old "a provider is scoped to its route"

By using ProviderScope inside a route, the override is not accessible to other routes.

In your case, this happens not because of navigation, but because of Overlay

During the transition, your Consumer is rendered above PageOne for a moment.

Ohh I see but then what would be a good solution for that? As that value is used by both and I can not put it above the MaterialApp as let's say I have no access to the value there. (Getting the user after sign in).

It feels weird to wrap both the ClosedBuilder return value and the OpenBuilder return value with a override

Honestly, this is more of an issue with Overlay than riverpod. https://github.com/flutter/flutter/issues/36220 should be fixed.
Ideally by merging https://github.com/flutter/flutter/pull/39104

As for how to fix it... Maybe consume the value above the OpenContainer and re-override ProviderScope inside the builder with the true value

Yeah did that and it works, thanks for the help!

Honestly, this is more of an issue with Overlay than riverpod. flutter/flutter#36220 should be fixed.
Ideally by merging flutter/flutter#39104

As for how to fix it... Maybe consume the value above the OpenContainer and re-override ProviderScope inside the builder with the true value

Hi, I'm trying to implement this solution, however I'm getting this error:
Unsupported operation: ProviderScope was rebuilt with a different ProviderScope ancestor.
along with:

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by widgets library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Updated the list of overrides with providers that were not overriden before
'package:riverpod/src/framework/container.dart':
Failed assertion: line 247 pos 9: 'unusedOverrides.isEmpty'
final Widget closedScreen;

@override
  Widget build(BuildContext context) {
    return Consumer((context, watch) {
      final bool favorited = watch(favoritedProvider);
      return OpenContainer(
        closedBuilder: (_, __) {
          return ProviderScope(
            overrides: [
              favoritedProvider.overrideWithValue(favorited),
            ],
            child: closedScreen,
          );
        },
        openBuilder: (_, __) {
          return Container();
        },
      );
    });



md5-7a91152789ec394bd2bd3dd83b13bf86



flutter_riverpod: ^0.6.1
animations: ^1.1.2
Was this page helpful?
0 / 5 - 0 ratings