skip to Main Content

When I sign out of my app, I can press the back button to return to the last page I was on before I signed out. However, weirdly, this unintended functionality only occurs if I navigate to another page besides the home page while I’m signed in.

I am using Firebase to manage my user authentication and I have setup a class to manage authentication as follows:

class AuthService {

  // creating a member of class that represents an instance of firebase authentication
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // create user object based on firebase User class
  AppUser? _userFromFirebaseUser(User user) {
    // ternary operator
    return user != null ? AppUser(uid: user.uid) : null;
  }

  // auth change user stream
  Stream<AppUser> get user {
    // mapping firebase User to User 
    return _auth.authStateChanges().map((User? user) => _userFromFirebaseUser(user!)!);
  }

  }
  // sign in with email & password
  Future signIn(String email, String password) async {
    try {
      UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
      User? firebaseUser = result.user;
      return _userFromFirebaseUser(firebaseUser!);
    } catch(e) {
      print(e.toString());
      return null;
    }
  }

  // register with email & password
  Future registerWithEmailAndPassword(String email, String password) async {
    try {
      UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
      User? firebaseUser = result.user;
      return _userFromFirebaseUser(firebaseUser!);
    } catch(e) {
      print(e.toString());
      return null;
    }
  }

  // sign out
  Future signOutFunc() async {
    try {
      _auth.signOut();
    } catch(e) {
      print('Failed to sign out');
      print(e);

    }
  }
}

This class is constantly listening to user auth changes and will show the sign in or register page depending on if the user is logged in.

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

  @override
  Widget build(BuildContext context) {

    final user = Provider.of<AppUser?>(context);
    print(user);
    // return either HomePage or Athenticate Widget
    // listening to auth changes from stream
    if (user == null) {
      return Authenticate();
    } else {
      return MyHomePage();
    }
  }
}

This widget directs the user to the register page when they sign out.

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

  @override
  State<Authenticate> createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {
  @override
  Widget build(BuildContext context) {

      return RegisterPage();
    }
    
  }

This is how I generally manage navigation in my app once the user is logged in:

class RouteGenerator {
  // static function to generate route
  static Route<dynamic> generateRoute(RouteSettings settings) {
    // getting arguments passed in while callling Navigator.pushNamed()
    final args = settings.arguments as Map<String, dynamic>?;

    // checking if the name of the route is the home route
    switch (settings.name) {
      case '/':
        return MaterialPageRoute(builder: (_) => MyHomePage());
      case 'ReceiptExplorer':
        return MaterialPageRoute(builder: (_) => ReceiptExplorer());
      case 'SignInPage':
        return MaterialPageRoute(builder: (_) => SignInPage());
      case 'PicturePreviewPage':
        final imagePath = args!['imagePath'] as String;
        return MaterialPageRoute(
            builder: (_) => PicturePreviewPage(imagePath: imagePath));
      case 'RegisterPage':
          return MaterialPageRoute(builder: (_) => RegisterPage());

      default:
        return _errorRoute();
    }
  }
}

Route<dynamic> _errorRoute() {
  return MaterialPageRoute(builder: (_) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Error"),
        ),
        body: Center(
          child: Text('ERROR'),
        ));
  });
}

Here is my main.dart where I use the stream provider to provide a stream of AppUser (a custom class that abstracts away all the unecessary information returned when I retrieve a firebase User) objects to its child widgets.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

// ignore: use_key_in_widget_constructors
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return StreamProvider<AppUser?>.value
    ( initialData: null,
    catchError: (User, AppUser) => null,
      value: AuthService().user,
      child: const MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Wrapper(),
        initialRoute: "/",
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }
}

I have tried returning the SignInPage in the Authenticate Widget instead of the RegisterPage, however this didn’t change anything. I have also updated the Authenticate Widget with the following code to try to replace the top of the widget stack with a new page however this also didn’t change anything.

class Authenticate extends StatefulWidget {
  const Authenticate({Key? key});

  @override
  State<Authenticate> createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      Navigator.of(context).pushReplacementNamed('SignInPage');
    });
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

I have seen some other posts where people use:

Navigator.of(context).popUntil(ModalRoute.withName('/')); // pops the stack back to the home page

or

Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false)

…but I’m not sure how I can implement these since my home (/) route is the home page of my app and not the sign in or register page so these methods would only return me back to the home page of the app when I sign out of the app which is undesirable.

To summarise, I would like some guidance on why there is even a back button shown on the screen when I sign out of the app and it takes me to the register page and why when I press this back button, it takes me to the picture preview page from when I was signed in.

Here is a video to more easily demonstrate what my issue is:
https://youtu.be/Kl4KOwRgHS0

2

Answers


  1. Chosen as BEST ANSWER

    I extended the initState function of the AuthenticateState widget and used Navigator.pushNamedAndRemoveUntil() which has solved my problem.

    class _AuthenticateState extends State<Authenticate> {
    
      @override
      // initState is called when the Authenticate widget is inserted into the  widget tree
      void initState() {
        super.initState();
        // the addPostFrameCallback method runs at the end of the current frame, which is
        // right when the widget is added to the widget tree so the RegisterPage is shown instantly,
        // after the Authenticate widget is called
        WidgetsBinding.instance.addPostFrameCallback((_) {
          // removing all routes in the stack except RegisterPage
          Navigator.pushNamedAndRemoveUntil(context, 'SignInPage', (route) => false);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        // the Authenticate widget isn't being shown on the screen for any
        // noticeable length of time since the addPostFrameCallback
        // method runs on the first frame. So we return an empty Container
        // just as 'something' to return from the function 
        return Container();
      }
    }
    

  2. To remove the backbutton from your registration page.

    Scaffold(
      appBar: AppBar(automaticallyImplyLeading: false,),
    );
    

    And if you need to show it (in case you want to navigate to register page from another page, by using Navigator.push(…)) then add it manually using (leading:) parameter or AppBar.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search