Reputation: 84
I am trying to use firebase data to route different pages using Getx. First I have a splash screen and want to automatically go to different pages according to conditions. If the user has already login, it will redirect the Home page, if not the route to the login page. But I can't use initState() on the Stateless widget as I using Getx, I don't want a Stateful widget.
class SplashPage extends StatelessWidget {
RxBool isloading = true.obs;
@override
Widget build(BuildContext context) {
String Uid = "";
return isloading.value
? SpinKitThreeInOut(
color: Colors.red,
)
: Obx(() {
return Get.find<AuthController>().user != null
? homeMethod()
: login();
});
}
Widget homeMethod() {
return Home(AuthController.instance.user.toString());
isloading.value = false;
}
}
But I ain't able to override isloading.value = false;
My Getx Auth Controller:
class AuthController extends GetxController {
static AuthController instance = Get.find();
FirebaseAuth auth = FirebaseAuth.instance;
Rxn<User> _firebaseUser = Rxn<User>();
String? get user => _firebaseUser.value?.uid;
@override
void onReady() {
// TODO: implement onReady
super.onReady();
_firebaseUser.value = auth.currentUser;
_firebaseUser.bindStream(auth.userChanges());
ever(_firebaseUser, _initialScreen);
}
/* @override
void onInit() {
_firebaseUser.bindStream(_auth.authStateChanges());
}*/
_initialScreen(User? user) {
if (user == null) {
Get.offAll(login());
} else {
String userId = user.uid;
Get.offAll(Home(userId));
}
}
Future<User?> LogInAccounts(String Email, String Password) async {
FirebaseAuth auth = FirebaseAuth.instance;
try {
User? user = (await auth.signInWithEmailAndPassword(
email: Email, password: Password))
.user;
if (user != null) {
Fluttertoast.showToast(msg: "Account Create Sucessfully");
return user;
} else {
Fluttertoast.showToast(msg: "Account Create Failed!");
return user;
}
} catch (e) {
return null;
}
}
}
Upvotes: 3
Views: 4723
Reputation: 1
class AuthController extends GetxController {
late Rx<User?> firebaseUser;
@override
void onReady() async {
super.onReady();
firebaseUser = Rx<User?>(FirebaseAuth.instance.currentUser);
firebaseUser.bindStream(firebaseAuth.instance.userChanges());
ever(firebaseUser, _setInitialScreen);
}
_setInitialScreen(user) async{
if (user != null) {
Get.offAllNamed(Routes.home);
} else {
Get.offAllNamed(Routes.login);
}
}
}
Upvotes: 0
Reputation: 1043
You can handle initialRoute
of GetMaterialApp
using isLogin flag
class _MyAppState extends State<MyApp> {
bool isLogin = false;
@override
void initState() {
isLogin = isAlreadyLogin();// Your function to check is user logged in.
super.initState();
}
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Rider App',
translationsKeys: AppTranslation.translationsKeys,
locale: Get.find<CacheManager>().getLocale(),
getPages: AppPages.pages,
initialRoute: isLogin ? Routes.homeScreen : Routes.loginScreen,
initialBinding: InitialBinding(),
);
}
class Routes {
static const homeScreen = '/home-screen';
static const loginScreen = '/login-screen';
}
Upvotes: 0
Reputation: 5595
You can use bindStream
and do it that way, but instead of trying to turn your User
object into a stream this can be done with a simple RxBool
. Firebase already provides a function to listen to auth state changes.
class AuthController extends GetxController {
RxBool loggedIn = false.obs;
@override
void onInit() {
super.onInit();
_subscribe();
}
void _subscribe() {
FirebaseAuth.instance.authStateChanges().listen((User? user) {
if (user == null) {
loggedIn(false);
log('User is currently signed out');
} else {
loggedIn(true);
log('User is signed in');
}
});
}
}
Then you can add another couple methods to your GetX class.
void initNaviationListener() {
/// inital startup naviation
_navigateBasedOnLogin();
/// future navigation based on auth state changes
ever(loggedIn, (value) {
_navigateBasedOnLogin();
});
}
void _navigateBasedOnLogin() {
if (loggedIn.value == false) {
Get.offAndToNamed(LoginPage.id);
} else {
Get.offAndToNamed(HomePage.id);
}
}
Then you can call initNaviationListener
in the onReady
of GetMaterialApp
GetMaterialApp(
/// onReady is called after GetMaterialApp is fully initialized
onReady: () => Get.find<AuthController>().initNaviationListener(),
theme: ThemeData.dark(),
initialRoute: LoginPage.id,
getPages: [
GetPage(
name: SplashPage.id,
page: () => SplashPage(),
),
GetPage(
name: HomePage.id,
page: () => HomePage(),
),
GetPage(
name: LoginPage.id,
page: () => LoginPage(),
),
],
)
That will navigate on app start to the corresponding screen and also respond to any future changes in auth status.
You don't have to navigate from the SplashPage
you can do it from the controller.
Let's say your GetMaterialApp
looks like this. This takes you to SplashPage
first.
GetMaterialApp(
initialRoute: SplashPage.id,
getPages: [
GetPage(
name: SplashPage.id,
page: () => SplashPage(),
),
GetPage(
name: HomePage.id,
page: () => HomePage(),
),
GetPage(
name: LoginPage.id,
page: () => LoginPage(),
),
],
)
Then check logged in status and navigate to the corresponding screen from your AuthController
.
class AuthController extends GetxController {
@override
void onInit() {
super.onInit();
_navigateBasedOnLogin();
}
Future<void> _navigateBasedOnLogin() async {
final loggedIn = await _isLoggedIn();
if (loggedIn) {
Get.offAndToNamed(HomePage.id); // offAndToNamed will remove the SplashScreen from the navigation stack
} else {
Get.offAndToNamed(LoginPage.id);
}
}
Future<bool> _isLoggedIn() async {
/// run your code to check logged in status and return true or false
}
}
Then just init the AuthController
in your main.
void main() async {
Get.put(AuthController());
runApp(MyApp());
}
With this setup, your SplashScreen
can be a generic loading screen with zero logic.
Upvotes: 0