I'm using Firebase Authentication in my Flutter app to detect user authentication state changes and navigate accordingly. However, the navigation inside initState
does not work.
./main.dart:
import 'package:app/authentication/email_auth.dart';
import 'package:app/authentication/landing.dart';
import 'package:app/authentication/signup.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'authentication/login.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
User? _user;
@override
void initState() {
super.initState();
FirebaseAuth.instance.authStateChanges().listen((user) {
print("User state changed: ${user?.uid}");
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_user = user;
});
if (user != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false,
);
});
}
});
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarIconBrightness: ThemeMode.system == ThemeMode.light
? Brightness.dark
: Brightness.light));
return MaterialApp(
// app data
title: 'MyApp',
// app navigation
routes: {
Login.routeName: (context) => const Login(),
Signup.routeName: (context) => const Signup(),
EmailAuth.routeName: (context) => const EmailAuth(),
Landing.routeName: (context) => const Landing(),
HomeScreen.routeName: (context) => HomeScreen(),
},
home: Landing(),
themeMode: ThemeMode.dark,
theme: ThemeData(scaffoldBackgroundColor: Color(0xff222531)),
darkTheme: ThemeData(
scaffoldBackgroundColor: Color(0xff222531),
bottomAppBarTheme: BottomAppBarTheme(color: Colors.red),
useMaterial3: true,
textTheme: TextTheme(
labelMedium: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20),
labelSmall: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
bodyLarge: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w500, fontSize: 24),
headlineSmall: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w400, fontSize: 22),
displayMedium: GoogleFonts.montserrat(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 40),
headlineLarge: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w400)),
),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({
super.key,
});
static const routeName = "/home";
final user = FirebaseAuth.instance.currentUser;
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () async {
print("pressed");
await FirebaseAuth.instance.signOut();
},
child: Center(
child: Text(
user?.email ?? "hello",
style: Theme.of(context).textTheme.headlineLarge,
),
),
),
);
}
}
Issue:
print()
logs).Navigator.pushAndRemoveUntil
does not work, and the screen does not change.How can I fix this issue and ensure the navigation happens when the user logs in?
Things I Have Tried:
Navigator.pushAndRemoveUntil
inside WidgetsBinding.instance.addPostFrameCallback
Navigator
Future.delayed(Duration(milliseconds: 100), () {...})
build()
instead of initState
MaterialPageRoute
is correctly definedI was expecting the sign-in to go ahead and the user be navigated to the homePage() page. But in reality the sign in is successful while the navigation is not!
I'm using Firebase Authentication in my Flutter app to detect user authentication state changes and navigate accordingly. However, the navigation inside initState
does not work.
./main.dart:
import 'package:app/authentication/email_auth.dart';
import 'package:app/authentication/landing.dart';
import 'package:app/authentication/signup.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'authentication/login.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
User? _user;
@override
void initState() {
super.initState();
FirebaseAuth.instance.authStateChanges().listen((user) {
print("User state changed: ${user?.uid}");
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_user = user;
});
if (user != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false,
);
});
}
});
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarIconBrightness: ThemeMode.system == ThemeMode.light
? Brightness.dark
: Brightness.light));
return MaterialApp(
// app data
title: 'MyApp',
// app navigation
routes: {
Login.routeName: (context) => const Login(),
Signup.routeName: (context) => const Signup(),
EmailAuth.routeName: (context) => const EmailAuth(),
Landing.routeName: (context) => const Landing(),
HomeScreen.routeName: (context) => HomeScreen(),
},
home: Landing(),
themeMode: ThemeMode.dark,
theme: ThemeData(scaffoldBackgroundColor: Color(0xff222531)),
darkTheme: ThemeData(
scaffoldBackgroundColor: Color(0xff222531),
bottomAppBarTheme: BottomAppBarTheme(color: Colors.red),
useMaterial3: true,
textTheme: TextTheme(
labelMedium: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20),
labelSmall: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
bodyLarge: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w500, fontSize: 24),
headlineSmall: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w400, fontSize: 22),
displayMedium: GoogleFonts.montserrat(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 40),
headlineLarge: GoogleFonts.inter(
color: Colors.white, fontWeight: FontWeight.w400)),
),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({
super.key,
});
static const routeName = "/home";
final user = FirebaseAuth.instance.currentUser;
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () async {
print("pressed");
await FirebaseAuth.instance.signOut();
},
child: Center(
child: Text(
user?.email ?? "hello",
style: Theme.of(context).textTheme.headlineLarge,
),
),
),
);
}
}
Issue:
print()
logs).Navigator.pushAndRemoveUntil
does not work, and the screen does not change.How can I fix this issue and ensure the navigation happens when the user logs in?
Things I Have Tried:
Navigator.pushAndRemoveUntil
inside WidgetsBinding.instance.addPostFrameCallback
Navigator
Future.delayed(Duration(milliseconds: 100), () {...})
build()
instead of initState
MaterialPageRoute
is correctly definedI was expecting the sign-in to go ahead and the user be navigated to the homePage() page. But in reality the sign in is successful while the navigation is not!
Ok so here's how i fixed it:
i moved to contextless navigation, so i defined a GlobalKey<NavigatorState>
globally in main.dart:
./main.dart:
// global navigation
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
then inside initState()
(main.dart
):
FirebaseAuth.instance.authStateChanges().listen((user) {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_user = user;
});
if (user != null) {
navigatorKey.currentState?.pushNamed("/home");
}
});
and in MaterialApp()
(main.dart
) you have to add this line:
navigatorKey: navigatorKey,
however i am yet to investigate the potential drawbacks with this approach (when i find them i will list them down below)