Flutter with Riverpod and GoRouter : ConsumerWidget doesn't detect StateProvider update

236 views Asked by At

I'm trying to manage routes in my application using goRouter and Riverpod, and everything is working perfectly except for the navBar. I have a state provider that helps me display which item is currently being shown by the router (just an integer). I'm trying to modify this state in my goRouter provider when calling the redirect function but without success.

Here's the problem: when I call "ref.read(navBarIndexProvider.notifier).state = myTabId;" in my redirect function, the state change is not detected in BottomNavBarItem (it is detected in goRouterProvider). Conversely, when I call "ref.read(navBarIndexProvider.notifier).state = myIntValue" from my BottomNavBarItem widget, the state change is not detected in goRouterProvider. I have the feeling that there are two instances of my navBarIndexProvider, but I'm not sure if that's possible and, if it is, I wonder why.

I'm using Flutter 3.10.5 dart Dart 3.0.5 flutter_riverpod: ^2.3.10 go_router: ^9.0.3

In short, I am completely lost, and if anyone has an idea, I'd appreciate it.

Thanks in advance

My index provider :

final navBarIndexProvider = StateProvider<int>((ref) {
  return 0;
});

My navbar item

class BottomNavBarItem extends ConsumerWidget {
  const BottomNavBarItem({required this.icon, required this.path, required this.index, super.key});

  final IconData icon;
  final String path;
  final int index;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    int navBarIndexState = ref.watch(navBarIndexProvider);

    void onItemTapped() {
      //ref.read(navBarIndexProvider.notifier).state = index;
      context.go(path);
    }

    return
      IconButton(
        enableFeedback: false,
        onPressed: () => onItemTapped(),
        icon: Icon(
          icon,
          color: navBarIndexState == index ? AppColors.primaryDarkest : Colors.white,
          size: 30,
        ),
      );
  }
}

Here my GoRouterProvider

final goRouterProvider = Provider<GoRouter>((ref) {
  final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
  final GlobalKey<NavigatorState> shellNavigatorKey = GlobalKey<NavigatorState>();


  String? redirect(BuildContext context, GoRouterState state) {
    final String destination = state.location;

    print('Before update : ${ref.read(navBarIndexProvider)}');
    print(destination);


    if (destination == '/') {
      ref.read(navBarIndexProvider.notifier).state = 0;
    }
    if (destination == '/page1') {
      ref.read(navBarIndexProvider.notifier).state = 1;
    }
    if (destination == '/page2') {
      ref.read(navBarIndexProvider.notifier).state = 2;
    }
    print('After udpdate : ${ref.read(navBarIndexProvider)}');

    return null;
  }

  return GoRouter(
    initialLocation: '/',
    navigatorKey: rootNavigatorKey,
    redirect: redirect,
    routes: [
      ShellRoute(
        navigatorKey: shellNavigatorKey,
        pageBuilder: (BuildContext context, GoRouterState state, Widget child) {
          return NoTransitionPage(
            child: Scaffold(
              extendBody: true,
              body: child,
              bottomNavigationBar: const MyBottomNavBar(),
            ),
          );
        },
        routes: [
          GoRoute(
            path: '/',
            parentNavigatorKey: shellNavigatorKey,
            pageBuilder: (context, state) => const NoTransitionPage(child: HomePage()),
          ),
          GoRoute(
            path: '/page1',
            parentNavigatorKey: shellNavigatorKey,
            pageBuilder: (context, state) => const NoTransitionPage(child: SwipePage()),
          ),
          //...
          ],),
    ],
  );
});

My main function

Future<void> main() async {

  //...

  //Riverpods
  final container = ProviderContainer();

  runApp(
    ProviderScope(
      parent: container,
      child:
          BlocProvider(
        create: (context) => PaymentBloc(),
        child: p.MultiProvider(
          providers: [
            p.ChangeNotifierProvider(
              create: (_) => AuthProvider(),
            ),
          ],
          child: const MyApp(),
        ),
      ),
    ),
  );
}

I call my app router like this

class MyApp extends ConsumerStatefulWidget {
  const MyApp({super.key});
  @override
  ConsumerState<MyApp> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> {
  @override
  Widget build(BuildContext context) {
    final router = ref.watch(goRouterProvider);

    return MaterialApp.router(

        //Router
        routerConfig: router);
  }
}
0

There are 0 answers