Search code examples
flutterdartflutter-providerflutter-state

Flutter provider does not work with variables


I am trying to abstract my navigation by putting all the logic in a class. I am not using any package for navigation, and I am using Provider for state management. I have realized that the providers don't work if they are in a variable, instead of in the function.

This is roughly what I am trying to do:

class AppRoutes {

  static void push(
    BuildContext context, {
    required Routes route,
  }) {
    Navigator.of(context).push(
      _getRoute(route: route),
    );
  }

  static void replace(
    BuildContext context, {
    required Routes route,
  }) {
    Navigator.of(context).pushReplacement(
      _getRoute(route: route),
    );
  }

  static void pushAndRemoveUntil(
    BuildContext context, {
    required Routes route,
  }) {
    Navigator.of(context).pushAndRemoveUntil(
      _getRoute(route: route),
      (Route<dynamic> routeFn) => false, // Remove all the routes
    );
  }

  static MaterialPageRoute<dynamic> _getRoute({
    required Routes route,
  }) {
    
    bool _isLoggedIn = GetIt.instance.isRegistered<User>();

    return MaterialPageRoute<dynamic>(
      builder: (_) => switch (route) {
        Routes.login => _isLoggedIn ? homePage : loginPage,
        //rest of routes
        _ => _notFoundPage,
      },
    );
  }

static ChangeNotifierProvider get loginPage => ChangeNotifierProvider(
        create: (_) => LoginController(),
        child: LoginView(),
      );

//rest of pages

}

This gives me a problem, the provider is not found in the context:

Error: Could not find the correct Provider above this ProfileView Widget

This happens because you used a BuildContext that does not include the provider of your choice.

However, this does not happen when the provider is placed directly in the function instead of using intermediate functions or variables. So, if I do it like this, it works:

static void push(
    BuildContext context, {
    required Routes route,
  }) {
    Navigator.of(context).push(
      MaterialPageRoute<dynamic>(
        builder: (_) => ChangeNotifierProvider(
          create: (_) => ProfileController(),
          child: ProfileView(),
        ),
      ),
    );
  }

It also works with the previous code but the providers as MultiProviders instead of ChangeNotifierProvider:

static MultiProvider get _profilePage => MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => ProfileController()),
        ],
        child: ProfileView(),
      );

Can anyone tell me why this is happening, and how can I solve it without having to turn all my single providers into MultiProviders?


Solution

  • Provider is working fine in this instance, the issue consists in the following code:

    static ChangeNotifierProvider get loginPage => ChangeNotifierProvider(
        create: (_) => LoginController(),
        child: LoginView(),
      );
    

    This returns the ChangeNotifierProvider with Type dynamic. And when you try to find the provider in the the login page using context.read() or using Consumer(), Provider cannot find it because ChangeNotifierProvider exists with dynamic Type. To fix this, change the code into following:

    static ChangeNotifierProvider<LoginController> get loginPage => ChangeNotifierProvider<LoginController>(
        create: (_) => LoginController(),
        child: LoginView(),
      );
    

    This should work as expected.