Could not find the correct Provider<FirebaseUser> above this HomePage Widget

Viewed 3293

I just started to refactor my apps structure using the provider package. Now I get the error above, even though I'm calling the provider down the widget tree in the next BuildContext. Any idea what could cause the error? As far as I understand, the common error cases explained in the error message shouldn't apply to me. Widget tree

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        Provider(create: (_) => FirebaseAuth.instance.onAuthStateChanged)
      ],
      child: MaterialApp(
          title: "My App",
          home: HomePage()),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return _getLandingPage(Provider.of<FirebaseUser>(context), context);
  }

  Widget _getLandingPage(FirebaseUser firebaseUser, context) {
    if (firebaseUser != null) {
        return CreateProfileFlow();

    } else {
      return PhoneNrInput();
    }
  }
}

Error:

Error: Could not find the correct Provider<FirebaseUser> above this HomePage Widget

This likely happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that HomePage is under your MultiProvider/Provider<FirebaseUser>.
  This usually happen when you are creating a provider and trying to read it immediatly.

  For example, instead of:

  ```
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // Will throw a ProviderNotFoundError, because `context` is associated
      // to the widget that is the parent of `Provider<Example>`
      child: Text(context.watch<Example>()),
    ),
  }
  ```

  consider using `builder` like so:

  ```
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // we use `builder` to obtain a new `BuildContext` that has access to the provider
      builer: (context) {
        // No longer throws
        return Text(context.watch<Example>()),
      }
    ),
  }
  ```

If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
2 Answers

FirebaseAuth.instance.onAuthStateChanged is a Stream, but you used Provider instead of StreamProvider to expose the value.

As such, if you want to read the value, you have two choices:

  • Use StreamProvider instead of Provider:

    StreamProvider(
      create: (_) => FirebaseAuth.instance.onAuthStateChanged,
    ),
    
  • Keep using Provider, and obtain the stream with context.watch<Stream<FirebaseUser>>:

    StreamBuilder(
      stream: context.watch<Stream<FirebaseUser>>(),
      builder: (context, snapshot) {
        ...
      },
    );
    

Use StreamBuilder for checking User data whenever state changes.

class AuthenticationWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          User user = snapshot.data;
          if (user == null) {
            return LoginPage();
          }
          return HomePage();
        } else {
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }
      },
    );
  }
}
Related