Search code examples
flutterstreamprovider

StreamProvider: Error: Could not find the correct Provider<User> above this App Widget


I'm using StreamProvider from the provider package for auth functionality in my flutter-firebase app, just like it is explained in this tutorial https://www.youtube.com/watch?v=j_SJ7XmT2MM&list=PL4cUxeGkcC9j--TKIdkb3ISfRbJeJYQwC&index=9.

When trying to run my app, I get an error message, with a suggestion how to do it correctly, but my code IS written in the way that is suggested.

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(FirebaseWrapper());
  runApp(App());
}

class FirebaseWrapper extends StatelessWidget {
  // Create the initialization Future outside of build():
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();
  // final Future<void> _initSharedPrefs = SharedPrefsHelper().initSharedPrefsInstance();

  @override
  Widget build(BuildContext context) {
    SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
    return FutureBuilder(
      // from: https://firebase.flutter.dev/docs/overview/#initializing-flutterfire
      future: _initialization,
      // future: Future.wait([_initialization, _initSharedPrefs]),
      builder: (context, snapshot) {
        if (snapshot.hasError) return ErrorPage(); //TODO better error pages
        if (snapshot.connectionState == ConnectionState.done) return FirebaseAuthWrapper();
        return Loading(); //waiting
      },
    );
  }
}

class FirebaseAuthWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      value: Auth().userStream,
      initialData: null,
      child: App(),
    );
  }
}

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);
    print('yeet');

    return MaterialApp(
      key: UniqueKey(),
      title: 'Wanderapp',
      theme: ThemeData(primarySwatch: Colors.blue),
      initialRoute: (user == null) ? '/signIn' : '/',
      routes: (user == null)
          ? {
              '/signIn': (context) => SignIn(),
              '/register': (context) => Register(),
              // '/forgotPassword': (context) => ForgotPassword(),
            }
          : {
              '/': (context) => Home(),
              //...
            },
    );
  }
}

the error message:

Error: Could not find the correct Provider<User> above this App Widget

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

- You added a new provider in your `main.dart` and performed a hot-reload.
  To fix, perform a hot-restart.

- 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 App is under your MultiProvider/Provider<User>.
  This usually happens when you are creating a provider and trying to read it immediately.

  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
      builder: (context) {
        // No longer throws
        return Text(context.watch<Example>()),
      }
    ),
  }
  ```

I'm user the same "User" class from Firebase for StreamProvider and Provider.of, the hierarchy/scope also seems to be correct in my code, but it doesn't work. Does anyone know what my mistake is? Thank you very much.


Solution

  • In this link about runApp it says:

    Calling runApp again will detach the previous root widget from the screen and attach the given widget in its place.

    So, you just need to remove the second runApp, as App is being called anyway from the StreamProvider: child: App(),.

    Solution:

    void main() {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(FirebaseWrapper());
      runApp(App()); //*** Remove this line ***///
    }