Home > other >  Flutter state management error with go_router and riverpod
Flutter state management error with go_router and riverpod

Time:08-31

I realise a Flutter app and I'm a really beginner. I'm using Riverpod for the state management and go_router for the routing. I try to implement a navbar visible only if you are logged. But I think I have a state management issue: when I press a navbar button, nothing happened (no console error neither) but If I logout and login or if I modify my code and save, my Emulator go to the right page. I try to wrap my pages in a bigger Scaffold, to persist the AppBar and NavBar. Here is my main.dart :

Future<void> main() async {

  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends ConsumerWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widgets is the root of your application.
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final router = ref.watch(routerProvider);
    return MaterialApp.router(
      title: 'Ludocal 2',
      theme: ThemeData(
        primarySwatch: Colors.deepOrange,
      ),
      debugShowCheckedModeBanner: false,
      routeInformationProvider: router.routeInformationProvider,
      routeInformationParser: router.routeInformationParser,
      routerDelegate: router.routerDelegate,
    );
  }
}

My router :

  List<GoRoute> get _routes => [
        GoRoute(
            name: 'login',
            builder: (context, state) => const LoginScreen(),
            path: '/login'),
        GoRoute(
            path: '/:screenName(home|game|event|profile)',
            builder: (BuildContext context, GoRouterState state) {
              final String screenName = state.params['screenName']!;
              return LoggedScreen(screenName: screenName);
            })
      ];

My logged_screen.dart wrapping my other screens :

class LoggedScreen extends HookConsumerWidget {
  const LoggedScreen({super.key, required this.screenName});

  final String screenName;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    Future.delayed(Duration.zero, () {
      switch (ref.read(indexProvider.state).state) {
        case 0:
          context.go('/home');
          break;
        case 1:
          context.go('/game');
          break;
        case 2:
          context.go('/event');
          break;
        case 3:
          context.go('/profile');
          break;
      }
    });
    return Scaffold(
      appBar: AppBar(
          title: Text("Ludocal 2"),
          backgroundColor: Colors.deepOrangeAccent,
          actions: [
            TextButton.icon(
              icon: Icon(
                Icons.logout_rounded,
                color: Colors.white,
              ),
              label: Text('', style: TextStyle(color: Colors.white)),
              onPressed: () async {
                ref.read(loginControllerProvider.notifier).signOut();
              },
            ),
          ]),
      body: BodyTab(screenName: screenName),
      bottomNavigationBar: const BottomTab(),
    );
  }
}

class BodyTab extends ConsumerWidget {
  const BodyTab({super.key, required this.screenName});

  final String screenName;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Column(
      children: [
        Expanded(
            child: screenName == 'home'
                ? const HomeScreen()
                : screenName == 'game'
                    ? const GameScreen()
                    : screenName == 'event'
                        ? const EventScreen()
                        : const ProfileScreen()),
      ],
    );
  }
}

class BottomTab extends ConsumerWidget {
  const BottomTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return BottomNavigationBar(
      currentIndex: ref.read(indexProvider.state).state,
      onTap: (int index) => ref.read(indexProvider.state).state = index,
      backgroundColor: Colors.deepOrangeAccent,
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: 'Home',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.emoji_emotions),
          label: 'Game',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.calendar_today_rounded),
          label: 'Event',
        ),
      ],
    );
  }
}

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

The login_controller.dart :

class LoginController extends StateNotifier<LoginState> {
  LoginController(this.ref) : super(const LoginStateInitial());

  final Ref ref;

  void login(String email, String password) async {
    state = const LoginStateLoading();
    try {
      await ref.read(authRepositoryProvider).signInWithEmailAndPassword(
        email,
        password,
      );
      state = const LoginStateSuccess();
    } catch (e) {
      state = LoginStateError(e.toString());
    }
  }

  void signOut() async {
    await ref.read(authRepositoryProvider).signOut();
    state = const LoginStateInitial();
  }
}

final loginControllerProvider =
StateNotifierProvider<LoginController, LoginState>((ref) {
  return LoginController(ref);
});

Thanks for help.

CodePudding user response:

For navigation you need to use listen like below.

    ref.listen(indexProvider, (previous, next) {

      switch (next) {
        case 0:
          context.go('/home');
          break;
        case 1:
          context.go('/game');
          break;
        case 2:
          context.go('/event');
          break;
        case 3:
          context.go('/profile');
          break;
      }

    });
  • Related