Home > OS >  Flutter sign out won't go to log in after reopening the app
Flutter sign out won't go to log in after reopening the app

Time:02-02

I have the following sign out code which works well as long as a user does not try to sign out after reopening the app. It means if a user is logged in and uses the app, then clicks the sign out, the app goes back to the login page. But if a user closes the app after being signed in, then reopens the app and clicks on the sign out, the app shows the main page and not the login page. The app is freezed in this situation and one should close and reopen it to see the login page again.

signOut(BuildContext context) async {
  SharedPref.saveLoginSharedPreference(false);
  var databasesPath = await getDatabasesPath();
  String path = join(databasesPath, 'patient.db');
  deleteDatabase(path);
  LocationService().dispose();
  Navigator.pushAndRemoveUntil(context,
      MaterialPageRoute(builder: (context) => const LoginScreen()), (route) => false);
  Phoenix.rebirth(context);
}

The main dart:

Future<void> backgroundHandler(RemoteMessage message) async {


await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
}

late AndroidNotificationChannel channel;

late FlutterLocalNotificationsPlugin  flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();


void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  var messaging = FirebaseMessaging.instance;
  String routeFromMessage = '';
  messaging.getInitialMessage().then((message) {
    if (message != null) {
      routeFromMessage = "/"   message.data["route"];
    }
  });
  await messaging.requestPermission(
    alert: true,
    announcement: false,
    badge: true,
    carPlay: false,
    criticalAlert: false,
    provisional: false,
    sound: true,
  );

  LocationService().getListener()?.resume();
  FirebaseMessaging.onBackgroundMessage(backgroundHandler);

  if (!kIsWeb) {
    channel = const AndroidNotificationChannel(
        'high_importance_channel', 'High Importance Notifications',
        importance: Importance.high);

    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    await messaging.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

  if (defaultTargetPlatform == TargetPlatform.android) {
    messaging.getToken().then((newToken) {
      SharedPref.saveFCMToken(newToken);
    });
  } else if (defaultTargetPlatform == TargetPlatform.iOS) {
    messaging.requestPermission(alert: true, sound: true);
    await messaging.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
    messaging.getToken().then((newToken) {
      SharedPref.saveFCMToken(newToken);
    });
  }

  bool? isLoggedIn = await SharedPref.getUserLoginSharedPrefernces();
  isLoggedIn ??= false;
  String route = '';
  if (isLoggedIn && routeFromMessage != '') {
    route = routeFromMessage;
  } else {
    route = isLoggedIn ? '/' : '/loginScreen';
  }

  runApp(Phoenix(
      child: MyApp(
        homepage: route,
        isLoggedIn: isLoggedIn,
      )));
}

class MyApp extends StatelessWidget {
  const MyApp({
    Key? key,
    required this.homepage,
    required this.isLoggedIn,
  }) : super(key: key);
  final bool? isLoggedIn;
  final String homepage;

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Main',
      debugShowCheckedModeBanner: false,
      //theme for all screens
      theme: ThemeData(
        primarySwatch: Colors.blue,
        primaryColor: Colors.blue[100],
      ),
      initialRoute: homepage,
      //all possible routes within the app
      home: PushMessaging(
          isLoggedIn: isLoggedIn,
          homepage: homepage,
          title: "Portal"),
      routes: {
        '/questionnairePushNotificationRoot': (context) =>
        const QuestionnaireRoot(),
        '/singleChoiceNew': (context) => const SelectionWindow(),
        '/exercisePrescriptionPage': (context) =>
        const ExercisePrescriptionHome(),
        '/multipleChoice': (context) => const MultipleChoice(),
        '/scale': (context) => const Scale(),
        '/textfield': (context) => const FreeTextfield(),
        '/endScreen': (context) => const EndScreen(),
        '/loginScreen': (context) => const LoginScreen(),
        '/registerScreen': (context) => const RegisterScreen(),
        '/forgotPassword': (context) => const ForgotPassword(),
        '/training': (context) => const Training(),
        '/riskFactors': (context) => const RiskFactors(),
        '/chat': (context) => const ChatDetailPage(),
      },
    );
  }
}

CodePudding user response:

Instead of manually trying to handle navigation to the login screen, you should have your app automatically redirect to the login screen if the user is not signed in. Since you're already using Firebase, I recommend using firebase_auth for authentication, which comes with it's own authentication state Stream: FirebaseAuth.instance.authStateChanges(). Firebase also automatically persists the authentication state between app launches. If you want to use your own authentication system instead, simply make your own stream using a StreamController.

Note: It looks like you're using Navigator 1.0 with named routes. However, if any readers are using Navigator 2.0, this method wouldn't work.

Wrap your entire app in a StreamBuilder and listen to FirebaseAuth.instance.authStateChanges():

// In the build method of MyApp:
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'My Main',
    home: StreamBuilder<User?>(
      // Here's where you listen to changes in authentication state.
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        // If there's no user data in the stream, show the login screen.
        if (!snapshot.hasData) return const LoginScreen();
        
        // Otherwise show the rest of your app.
        return PushMessaging(
          isLoggedIn: isLoggedIn,
          homepage: homepage,
          title: "Portal",
        );
      },
    ),
  );
}

This will automatically show your app when the user is signed in, and will automatically switch to your login screen when the user signs out. Then for your sign-out function:

signOut(...) {
  // Do whatever else you want to do...
  // Then tell firebase you're signing out.
  FirebaseAuth.instance.signOut();
}

CodePudding user response:

I changed the sign out as follow and it works now:

signOut(BuildContext context) async {
  SharedPref.saveLoginSharedPreference(false);
  var databasesPath = await getDatabasesPath();
  String path = join(databasesPath, 'patient.db');
  deleteDatabase(path);
  LocationService().dispose();
  Navigator.of(context)
      .push(MaterialPageRoute(builder: (context) => const LoginScreen()));
}
  • Related