Search code examples
flutterdartblocflutter-getx

Flutter BloC state executed but the UI didn't change after the state changed


I am using the Bloc and Getx for state management and routing. I have solved the problem of the Bloc is nested with different child with the route which causing the Bloc cannot be found/read if I change the route. Which the solution is wrap the GetMaterialApp inside the BlocProvider.

import ;...

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    BlocProvider<AuthBloc>(
      create: (context) => AuthBloc(FirebaseAuthProvider()),
      child: GetMaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter Demo',
        theme: theme(),
        home: const MainPage(),
        /*BlocProvider<AuthBloc>(
          create: (context) => AuthBloc(FirebaseAuthProvider()),
          child: const MainPage(),
        ),*/
        getPages: RoutesClass.routes,
        // routes: {
        // verifyEmailRoute: (context) => const VerifyEmailView(), // not used
        // },
        // routes: routes, // switch to getx
      ),
    ),
  );
}

But now the Bloc is running well. I use the Bloc for FirebaseAuth. I can do login and logout (the state is correct in logic. I'm sure because it was functioned properly if I didn't click the navigation bar to change route.) if I didn't change the page/view with the bottom navigation bar.

If I change the page/view with the bottom navigation bar and click logout, the Bloc state executed but the UI cannot be changed to logout state. The bottom navigation bar is custom and the routing of changing page is using the Getx to perform routing. using the Get.toNamed() function.

Sorry for my bad explanation. Below is my code for your reference:

import 'package:facerecognitionattendance/services/auth/bloc/auth_bloc.dart';
import 'package:facerecognitionattendance/services/auth/bloc/auth_event.dart';
import 'package:facerecognitionattendance/utilities/dialogs/logout_dialog.dart';
import 'package:facerecognitionattendance/views/profile/components/profile_menu.dart';
import 'package:facerecognitionattendance/views/profile/components/profile_pic.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart' show ReadContext;

class Body extends StatefulWidget {
  const Body({super.key});

  @override
  State<Body> createState() => _BodyState();
}

class _BodyState extends State<Body> {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(vertical: 20),
        child: Column(
          children: [
            const ProfilePic(),
            const SizedBox(height: 20),
            ProfileMenu(
              text: "My Account",
              icon: "assets/icons/User Icon.svg",
              press: () => {},
            ),
            ProfileMenu(
              text: "Notifications",
              icon: "assets/icons/notification.svg",
              press: () {},
            ),
            ProfileMenu(
              text: "Settings",
              icon: "assets/icons/Settings.svg",
              press: () {},
            ),
            ProfileMenu(
              text: "Log Out",
              icon: "assets/icons/Log out.svg",
              press: () async {
                final shouldLogout = await showLogOutDialog(context);
                if (shouldLogout) {
                  if (!mounted) return;
                  context.read<AuthBloc>().add(
                        const AuthEventLogOut(),
                      ); // HERE
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

Hope these will help for the explanation. Thanks in advance.

I tried to add the BlocProvider but does not work. Expecting the logout state executed and change the UI back to the sign in form.


Solution

  • First of all, your Body widget should be a StatelessWidget in this example if you don't make use of the state, also Bloc handles that for you. To solve the redirect you should wrap your code in a BlocListener to listen for changes in the state of AuthBloc. Here's an example:

    return BlocListener<AuthBloc, AuthState>(
        listener: (BuildContext context, state) {
            // Check here if the state is unathenticated
            if (state is Unauthenticated) {
              Get.toNamed(); // Your login or initial page
            }
        },
        child: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.symmetric(vertical: 20),
            child: Column(
              children: [
                // Your code here...
              ],
            ),
          ),
        ),
      );
    

    Also in BlocListener you can just listen to some changes in the state, like for example the authStatus, you can use the property listenWhen for that.

    Hope this helps!