skip to Main Content

I am new to flutter and this is my first project. I have writter a basic app that uses firebase to login, register, verify emails, and has an as of now empty home page. The code runs fine but i have a warning in two places. In these lines of code Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,); and this Navigator.of(context).pushNamedAndRemoveUntil(mainRoute, (route) => false,); i get this warning: Don't use 'BuildContext's across async gaps. Try rewriting the code to not use the 'BuildContext', or guard the use with a 'mounted' check. I know that using navigator and pushing is not the most optimal technique but as i said it is my first project and this is the most eazy way. I tried using if(!mounted)return; but it didn’t work. My app runs fine and does not have any problems but as i saw this warning can cause problems in specific senarios so i want to know how to fix it in case i encounter it again. This is the code of the main file only. I also have files for the login and register that i don’t think are important.

import 'package:project/constants/routes.dart';
import 'package:project/firebase_options.dart';
import 'package:project/views/loginview.dart';
import 'package:project/views/registerview.dart';
import 'package:project/views/verifyview.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'dart:developer' as devtools show log;

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color.fromARGB(255, 58, 133, 183)),
        useMaterial3: true,
      ),
      home: const HomePage(),
      routes: {
        loginRoute: (context) => const LoginView(),
        registerRoute:(context) => const RegisterView(),
        mainRoute:(context) => const NotesView()
      },
    ),
  );
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Firebase.initializeApp(
          options: DefaultFirebaseOptions.currentPlatform,
        ),
        builder:(context, snapshot) {
          switch (snapshot.connectionState){
            case ConnectionState.done:
            final user = FirebaseAuth.instance.currentUser;
            if(user != null)
            {
              if(user.emailVerified)
              {
                return const NotesView();
              }
              else
              {
                return const VerifyEmailView();
              }
            }
            else 
            {
              return const LoginView();
            }

          default: 
            return const CircularProgressIndicator();
        } 
      },
    );
  }
}

enum MenuAction { logout }

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

  @override
  State<NotesView> createState() => _NotesViewState();
}

class _NotesViewState extends State<NotesView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("My notes"),
        actions: [
          PopupMenuButton<MenuAction>(
            onSelected: (value) async {
              switch (value) {
                
                case MenuAction.logout:
                  final shouldLogOut = await showLogOutDialog(context);
                  if(shouldLogOut)
                  {
                    await FirebaseAuth.instance.signOut();
                    Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
                  }
              }
            },
            itemBuilder: (context) {
              return const [
                PopupMenuItem<MenuAction>(
                value: MenuAction.logout,
                child: Text('Logout'),
                )
              ];            
            },
          )
        ]
      ),
      body: const Text("Hello world"), 
    );
  }
}

Future<bool> showLogOutDialog(BuildContext context)
{
  return showDialog<bool>(
    context: context, 
    builder:  (context)
    {
      return AlertDialog(title: const Text('Sign Out'), 
        content: const Text('Are you sure you want to sign out?'),
        actions: [
            TextButton(onPressed: () {
              Navigator.of(context).pop(false);
            }, child: const Text("Cancel")),
            TextButton(onPressed: () {
              Navigator.of(context).pop(true);
            }, child: const Text("Log Out")),
        ]
      );
    },
  ).then((value) => value ?? false);
}

3

Answers


  1. Move your navigator line to a seperate function and call the handleNavigation() function from inside your onSelected:. This will remove the warning.

    void handleNavigation() {
        Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
    }
    
    Login or Signup to reply.
  2. In Flutter, managing multiple BuildContexts can be tricky. In your case, you are working with different contexts. One from your stateful widget and another from your build function, plus any additional contexts from Builder widgets like LayoutBuilder. The key issue here is correctly using the mounted property to check if the widget is still in the tree.

    Below are three options what you can do now.

    Option 1: Use the widget’s context

    await FirebaseAuth.instance.signOut();
    if (!mounted) return;
    Navigator.of(this.context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
    

    Option 2: Use the local build function’s context

    await FirebaseAuth.instance.signOut();
    if (!context.mounted) return;
    Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
    

    Option 3: Create a helper function outside the build function

    void navigate() {
    if (!mounted) return;
    Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
    }
    
    Login or Signup to reply.
  3. Put your navigations inside a if check like this…

    if(mounted) {
        Navigator.of(context).pushNamedAndRemoveUntil(loginRoute, (_) => false,);
        Navigator.of(context).pushNamedAndRemoveUntil(mainRoute, (route) => false,); 
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search