harunB10
harunB10

Reputation: 5207

Could not find the correct provider above this widget

I have a problem using Flutter Provider... My flow is like this: After login user id is passed to new widget -> from there it preforms save to db and then it redirects to new widget (Dashboard).

And this is a code of a widget after Login:

return MaterialApp(
      title: title,
      home: Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: 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);
                    },

This works:

user.savePreference(user.user.id, "Something");

But this is causing a problem:

user.navigateToNewPage(Dashboard(), context);

In Dashboard widget I am creating this:

 Widget build(BuildContext context) {
    var user = Provider.of<UserRepository>(context);

And in UserRepository I have this:

class UserRepository with ChangeNotifier {
  User user;
  Status _status = Status.Uninitialized;

  Status get status => _status;
  User get getUser => user;

  UserRepository.instance();

Future<void> navigateToNewPage(Widget page, BuildContext context) {
    Navigator.push(context, MaterialPageRoute(builder: (context) => page));
  }

I know this topic has already solved questions, but could not find anything suitable to my problem.

Upvotes: 44

Views: 87287

Answers (9)

I change ChangeNotifierProvider to the parent context of the material app and my Navigator.pop(context, true); and Navigator.pushNamed(context, "/home"); started to work

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MasterMonitor>(
        create: (context) => MasterMonitor(),
        child: MaterialApp(
            title: 'My APP',
            theme: ThemeData(
              colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
              useMaterial3: true,
            ),
            home: LoginPage(),
            routes: {
              '/login': (BuildContext context) => LoginPage(),
              '/home': (BuildContext context) => HomePage(
                    key: GlobalKey(),
                    //'/about': (BuildContext context) => AboutPage(key: GlobalKey()),
                  )
            }));
  }
}

Upvotes: 0

sachindu srilal
sachindu srilal

Reputation: 21

//My main.dart

void main() {
  final MultiBlocProvider multiBlocProvider = MultiBlocProvider(
    providers: [
      BlocProvider<AuthBloc>(
        create: (context) => AuthBloc(),
      ),
      BlocProvider<LoadCategoriesBloc>(
        create: (context) => LoadCategoriesBloc(),
      ),
      BlocProvider<LoadCustomersBloc>(
        create: (context) => LoadCustomersBloc(),
      ),
    ],
    child: const MyApp(),
  );
  runApp(multiBlocProvider);
}

//Use above bloc providers for load data.
//quotationCreate.dart
BlocConsumer<AuthBloc, AuthState>(
  listener: (context, state) {
    if (state is AuthSuccessState) {
      context.read<LoadCategoriesBloc>().add(LoadCategoriesAction());
      context.read<LoadCustomersBloc>().add(LoadCustomersAction());
    } else if (state is AuthFailureState) {
      Navigator.pushReplacementNamed(context, '/signin');
    }
  },
),

I used MultiBlocProvider and with authorication for my code and, then I used providers like the above.

Upvotes: 0

Anas Mahmood
Anas Mahmood

Reputation: 1

enter image description here As i am using navigator.push widget in my login page when i run the dart file the error n which the message on screen is written as:Error: Could not find the correct Provider«FavoriteModel above this Consumer Widget This happens because you used a "BuildContext, that does not include the provider Eror: Could not find the correct Provider«FavoriteModels above this Consumer«FavoriteModels Widget This happens because you used a 'BuildContext that does not include. If anyone knows about it please suggest me the solution.

void main() {
  SystemChrome.setSystemUIOverlayStyle(
    const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
    ),
  );
  APICallService.getProduct();
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => FavoriteModel(),
        ),
        ChangeNotifierProvider(
          create: (context) => CartProvider(),
        ),
      ],
      child: const App(),
    ),
  );
}

Upvotes: -1

Abbas Jafari
Abbas Jafari

Reputation: 1643

I imported my Provider from the wrong path:

import 'file:///D:/Projects/Flutter/library_app/lib/MyStateManagement/local/temp_management.dart';

And when I change the path's import its work for me:

import 'package:library_app/MyStateManagement/local/temp_management.dart';

And this issue happened to me when I changed the folder of the Provider file.

Upvotes: 5

Eray Hamurlu
Eray Hamurlu

Reputation: 783

This is how we used with multi providers

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (context) => GeneralProvider(),
      ),
    ],
    child: MaterialApp(
      home: InitPage(),
      onGenerateRoute: MyRouter.generateRoute,
      initialRoute: '/InitPage',
      debugShowCheckedModeBanner: false,
      title: 'Eray',
    ),
  );
}
}

Upvotes: 6

Ershat
Ershat

Reputation: 76

maybe answer in DetailScreen.

I don't know whether you resolved this problem.but I just have resolved using a parameter. frist my problem is , I have a list screen, a detail screen, and a listBloc. I provided listBloc in list screen. like this :

class ListScreen extends StatelessWidget {
  static const String routeName = '/list_screen';

  ListScreen({Key key}) : super(key: key);

  static Route route(Map<String, dynamic> args) {
    return MaterialPageRoute(
      builder: (_) => ListScreen(),
      settings: const RouteSettings(name: routeName),
    );
  }


  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
        providers: [
          BlocProvider(
            create: (context) =>ListBloc(),
          )
        ]
        child: DetailScreen(),
    );
  }
}

and , i need to use listBloc in detail screen's BlocBuilder.

class DetailScreen extends StatelessWidget {

  // you need a context
  final BuildContext context;

  // get context
  const DetailScreen({
    Key key,
    @required this.context,
  }) : super(key: key);
  
   @override
  Widget build(BuildContext context) {
    // get bloc using your context
    final bloc = this.context.read<ListBloc>();
    return BlocBuilder<ListBloc, ListState>(
      // provide your bloc
      bloc: bloc,
      builder: (context, state) {}
    );
  }
}

Upvotes: 0

Baker
Baker

Reputation: 28060

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.

Upvotes: 141

BuffK
BuffK

Reputation: 1406

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);
        );
      }),
    );
}

Upvotes: 11

Mikhail Ponkin
Mikhail Ponkin

Reputation: 2711

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);
                    },

...

  }
}

Upvotes: 8

Related Questions