riverpod icon indicating copy to clipboard operation
riverpod copied to clipboard

Edge case of `Tried to read AutoDisposeProvider from a place where one of its dependencies were overridden but the provider is not.`

Open lcdsmao opened this issue 3 years ago • 2 comments

Describe the bug 2.0.0-dev.9

An edge case that triggers assertion failure:

The following assertion was thrown building Home(dirty, dependencies: [UncontrolledProviderScope],
state: _ConsumerState#53d0d):
Tried to read AutoDisposeProvider<int>#5dc5b(4) from a place where one of its dependencies were
overridden but the provider is not.

To fix this error, you can add add "dependencies" to AutoDisposeProvider<int>#5dc5b(4) such that we
have:

```
final a = Provider(...);
final b = Provider((ref) => ref.watch(a), dependencies: [a]);
```
'package:riverpod/src/framework/container.dart':
Failed assertion: line 492 pos 17: 'targetElement.provider != targetElement.origin ||
                    dependencyElement ==
                        targetElement.container
                            .readProviderElement<Object?>(dependency)'

Sample code:

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() async {
  runApp(
    ProviderScope(
      overrides: [
        parentProvider.overrideWithProvider(
          (arg) =>
              StateNotifierProvider.autoDispose((ref) => StateController(arg)),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ProviderScope(
        // NOTE: override here will have no problem
        /* overrides: [ */
        /*   parentProvider.overrideWithProvider( */
        /*     (arg) => StateNotifierProvider.autoDispose( */
        /*         (ref) => StateController(arg)), */
        /*   ), */
        /* ], */
        child: Home(),
      ),
    );
  }
}

class Home extends HookConsumerWidget {
  const Home({
    super.key,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // NOTE: if you swap the call order of the following two `ref.watch`, then there will be no problem
    ref.watch(parentProvider(4));
    ref.watch(childProvider(4));
    return const SizedBox();
  }
}

// NOTE: seems like Provider has no problem
final parentProvider = StateNotifierProvider.autoDispose
    .family<StateController<int>, int, int>((ref, arg) {
  return StateController(0);
});

final childProvider = Provider.autoDispose.family((ref, int arg) {
  return ref.watch(parentProvider(arg));
}, dependencies: [parentProvider]);

To Reproduce

Run the following code and you should see the assertion failure.

Expected behavior No failure.

The above code is the minimum code I can reproduce. The problem in our production code seems more complex and can not be solved. We have also to override the provider like childProvider in the sample code. I am not sure if there is a relationship between the bug inside the sample code and the problem in our production code.

lcdsmao avatar Sep 02 '22 08:09 lcdsmao

Hello!

I've tried your example on the master branch and failed to reproduce the issue. Either it was fixed implicitly or there's something else going on.

Do you mind maybe trying the master branch?

rrousselGit avatar Sep 11 '22 13:09 rrousselGit

Hello

Yes, the sample code works well when using the master branch code. It will take some time to confirm with our production code, since there seem like some breaking changes, e.g. overrideWithValue.

lcdsmao avatar Sep 11 '22 14:09 lcdsmao

Closing as that was fixed

rrousselGit avatar Oct 23 '22 11:10 rrousselGit