Could not find the correct provider above this widget

Provider Scope

MaterialApp
 > provider(Screen A)
 > Screen B

If Provider is instantiated in Screen A, it won't be accessible in Screen B after a Navigator.push from A → B.

Why?

Because Provider is an InheritedWidget and Navigator uses MaterialApp context outside its Screen A context scope. (See Details below.)

Fix

Moving Provider up to a common-parent, MaterialApp context, allows both Screen A and B to inherit its state/context.

provider(MaterialApp)
 > Screen A
 > Screen B

Example

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// wrap MaterialApp in Provider widget
    return ChangeNotifierProvider(
      create: (context) => ColorModel(), // ← create/init your state model
      child: MaterialApp(
          home: ScreenA()
      ),
    );
  }
}

Details

Provider

  • Provider is based on InheritedWidget. Only child widgets can inherit parent widget's state.

    • Provider needs to be the root widget for any widget tree that wants access to your "provided" state object.

Navigator

  • Navigator.push(context) on Screen A doesn't use the context from Screen A.
    • It uses context from MaterialApp.
  • Navigator.push(context) is actually Navigator.of(context).push
  • Navigator.of(context) means: search up this context hierarchy until you find a context that instantiated a Navigator
    • A default Navigator is instantiated in MaterialApp.
    • Unless you explicitly create/use a different Navigator, you're using the default.
    • Navigator's context is that of MaterialApp.
  • Screen B will get that context (of MaterialApp), not the context of Screen A.
    • B is a sibling of A, not its child.
    • B does not inherit from A, even though it appears "instantiated" inside context A.
    • Screen B is a child of MaterialApp context, not of Screen A context.
    • Provider context scope, if defined in Screen A, doesn't cover Screen B

Screen A → B

Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenB()))

is actually:

Navigator.of(context).push(MaterialPageRoute(builder: (context) => ScreenB()))

which is like:

Navigator.of(MaterialApp).push(
  MaterialPageRoute(builder: (MaterialAppContext) => ScreenB())
)

So Screen B is under MaterialApp context, not under Screen A context and therefore has no access to Screen A Provider and its context.

See this answer for a code sample to a similar question about Provider.


Parameter context for Provider.of(context) must be a child of you defined provider.

@override
  Widget build(BuildContext context) {
    // !important here, Scaffold.of(context) returns null
    return Scaffold(
      appBar: AppBar(title: Text('Demo')),
      body: Builder(
        builder: (BuildContext context) {
          return FlatButton(
            child: Text('BUTTON'),
            onPressed: () {
              // here, Scaffold.of(context) returns the locally created Scaffold
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Hello.')
              ));
            }
          );
        }
      )
    );
  }

Official sample: https://api.flutter.dev/flutter/widgets/BuildContext-class.html

This is my code:

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider<SomeModel>(
        create: (context){
          return SomeModel();
        },
      ),],
      child: Builder(builder: (BuildContext context){
        BuildContext rootContext = context;
        return Container(
//Here to use rootContext is safe
//Provider.of<SomeModel>(rootContext, listen: false);
        );
      }),
    );
}

Try extracting part of your widget tree that lies below Scaffold to a separate widget. The context you are using now is used to build your top level widget which does not Navigator yet.

The resulting code should look like that:

return MaterialApp(
      title: title,
      home: Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: LoginWidget()
class LoginWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
            children: <Widget>[
              Container(
                margin: EdgeInsets.all(8.0),
                child: Card(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(8.0))),
                  child: InkWell(
                    onTap: () {
                      var user = Provider.of<UserRepository>(context);
                      user.savePreference(user.user.id, "Something");
                      user.navigateToNewPage(Dashboard(), context);
                      print(user.user.id);
                    },

...

  }
}