Search code examples
flutterblocflutter-bloc

Flutter Bloc: log out from any place when using Navigator + Bloc


I am new to Fluter and trying to tackle the navigation part using Bloc.

I would like to add the possibility to log out from any place in the application.

Details:

I am working on the Notes application (just for learning), and have wrapped MaterialApp with BlocProvider, so I can access AuthBloc from any place. MaterialApp 'home' widget is HomePage, which returns BlocConsumer, thus it can return a state depending on the event. So, if the user has logged in - I emit AuthStateLoggedIn and return a ViewNotePage. From the ViewNotePage (by clicking on a concrete Note) I go to NotePage, using MaterialPageRoute. And if I emit AuthEventLogOut (by press on the LogOut button) from the NotesListPage - nothing happens, until I invoke Navigator.of(context).popUntil(ModalRoute.withName('/')). Here is the code

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

// App
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider<AuthBloc>(
      create: (context) => AuthBloc(FirebaseAuthProvider()),
      child: MaterialApp(
        title: 'Notes',
        theme: Colors.blue,
        home: const HomePage(),
        routes: {
          viewNotePage: (context) => const ViewNotePage(),
        }
      ),
    );
  }
}

// HomePage

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    context
        .read<AuthBloc>()
        .add(const AuthEventInitialize());
    return BlocConsumer<AuthBloc, AuthState>(
      listener: (context, state) {
        if (state.isLoading) {
          // loading is handled here
        }
      },
      builder: (context, state) {
        if (state is AuthStateLoggedOut) {
          return const LoginView();
        } else if (state is AuthStateRegistering) {
          return const RegisterView();
        } else if (state is AuthStateNeedsVerification) {
          return const VerifyEmailView();
        } else if (state is AuthStateLoggedIn) {
          return const NotesPage();
        } else if (state is AuthStateForgotPassword) {
          return const ForgotPasswordView();
        }
        return const Scaffold(body: CircularProgressIndicator());
      },
    );
  }
}

// NotesPage

To shorten the code, basically, I do here:

await Navigator.of(context).pushNamed(viewNotePage, arguments: someArgs);

// ViewNotePage

context.read<AuthBloc>().add(const AuthEventLogOut());
Navigator.of(context).popUntil(ModalRoute.withName('/'));  // without popUntil - I stay on the ViewNotePage

So from what I understand, when I use MaterialPageRoute - it creates a new tree under the MaterialApp and now it can not access BlocConsumer.

Can someone please point out, what is the correct way to handle this case? Is it a good idea to mix Bloc + Navigator features?


Solution

  • you can listen for the logout event using bloc listener and then call the navigator function