1
votes

I just started using provider as a state management for flutter. I have a value in a provider to be used for the initial login. isLogin will return true or false and depending on that it will redirect the user to home or login page. I'm getting the error below:

Could not find the correct Provider above this MyApp Widget

Plus, is this a good way of doing authentication or is there a better way. I'm using laravel API for authentication.

Main.dart

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

@override
Widget build(BuildContext context) {
  return ChangeNotifierProvider<UserProvider>(
      create: (context) => UserProvider(),
      child: MaterialApp(
      initialRoute:Provider.of<UserProvider>(context,listen:false).isLogin?'/':'/login',
      routes: {
        '/':(_)=>HomePage(),
        '/donation-history':(_)=>DonationHistoryPage(),
        '/login':(_)=>LoginPage(),
        '/new-donation':(_)=>NewDonation()
      },
      debugShowCheckedModeBanner:false,
      title: 'RedHero',
      theme: ThemeData(
        primaryColor: kPrimaryColor,
        accentColor: Colors.white,
        scaffoldBackgroundColor: kBackgroundColor,
        fontFamily: "Poppins",
        textTheme: TextTheme(
          bodyText1: TextStyle(color: kBodyTextColor)
        )
      ),
    ),
  );
}
1
It's ok, but how about my initial problem? I cant find the error, I check the widget tree and the provider is on top of all my routes @Uni - Grey
the widget above belongs to MyApp @Uni - Grey

1 Answers

1
votes

The broken pattern is:

class OuterWidget : StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Something>(
      ... Provider.of<Something>(context) ...
    );
  }
}

The problem is that Provider.of<Something> receives the same context that was passed to the build function. On that context, the ChangeNotifierProvider does not exist; the provider is only added to descendant build contexts, which are created while building child widgets.

If that isn't clear, imagine extracting the provided object into a variable (which does not change the meaning of the code):

class OuterWidget : StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final something = Provider.of<Something>(context);
    return ChangeNotifierProvider<Something>(
      ... something ...
    );
  }
}

Now it's quite obvious that we're trying to consume an object that hasn't been provided yet.

One solution, then, is to split the inner part into a separate widget:

class OuterWidget : StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Something>(
      ... InnerWidget() ...
    );
  }
}

class InnerWidget : StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ... Provider.of<Something>(context) ...;
  }
}

This widget will receive a new child context, on which the provider does exist.

Another solution is to use a Consumer widget, which exists exactly for this scenario:

class OuterWidget : StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Something>(
      ...
      Consumer<Something>(builder: (_, something, __) => ... something ...)
      ...
    );
  }
}

Now, the build of the children of Consumer is delayed until the builder method, which therefore also receives a build context on which the provider exists.