Firebase check user already logged in and different screens - flutter

So in my main.dart i want to check if the user is logged in already then redirect the user to HomeScreen with his userID and if not redirect the user to SignIn page.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
// Get the firebase user
final firebaseUser = FirebaseAuth.instance.currentUser;
String id = firebaseUser!.uid;
// Assign widget based on availability of currentUser
if (firebaseUser != null) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => HomeScreen(userId: id)));
print('User already logged in');
} else {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => SignIn()));
print('User must SignIn');
}
}
}
I'm getting the error
"The body might complete normally, causing 'null' to be returned, but the return type, 'Widget', is a potentially non-nullable type.
Try adding either a return or a throw statement at the end."

This method should return a widget. In both the cases its only getting navigated, so you can write a return SizedBox.shrink() like
Widget build(BuildContext context) {
// Get the firebase user
final firebaseUser = FirebaseAuth.instance.currentUser;
String id = firebaseUser!.uid;
// Assign widget based on availability of currentUser
if (firebaseUser != null) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => HomeScreen(userId: id)));
print('User already logged in');
} else {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => SignIn()));
print('User must SignIn');
}
return SizedBox.shrink();//<-----here
}

Related

Checking for Firebase uID then routing

So my main.dart looking like this, I just want to check if the user already loggedIn or not. If true then route him directly to Homescreen and passing the UID else to the SignIn screen.
But somehow im getting a black screen without any error. Why? the debug print statements are working...
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
//User logged in?
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
FirebaseAuth.instance.authStateChanges().listen((User? user) {
if (user == null) {
print('User is currently signed out!');
MaterialPageRoute(builder: (context) => const SignIn());
} else {
String myUid = user.uid;
MaterialPageRoute(builder: (context) => HomeScreen(userId: myUid));
print('User is signed in!');
}
});
return const SizedBox.shrink(); //<-----here
}
}
Well my Code looking now like this:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
//User logged in?
final FirebaseAuth auth = FirebaseAuth.instance;
//The stream for auth changee
Future<User?> data() async {
return FirebaseAuth.instance.currentUser;
}
final User? user = auth.currentUser;
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance
.authStateChanges(), //FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
if (snapshot.connectionState == ConnectionState.active) {
if (user == null) {
print('User is currently signed out!');
Navigator.push(context,
MaterialPageRoute(builder: (context) => const SignIn()));
} else {
String myUid = user!.uid;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
userId: myUid,
)));
}
}
return const CircularProgressIndicator();
});
}
}
Navigator operation requested with a context that does not include a Navigator.
The relevant error-causing widget was
StreamBuilder<User?>
You can't just insert a stream listener in the build method like that. The easiest way to do this, is to use a StreamBuilder which handles the stream for you. Similar to the example in the documentation on listening for Firestore updates that'd be something like:
StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
if (user == null) {
print('User is currently signed out!');
return MaterialPageRoute(builder: (context) => const SignIn());
} else {
String myUid = user.uid;
return MaterialPageRoute(builder: (context) => HomeScreen(userId: myUid));
}
},

Onboarding screen does not work correctly with auth Firebase in Flutter app

The logic of the application - onboading screen appears only once, after it goes to the authorization screen with Firebase. If the authorization is successful, we open the home page of the application.
But this doesn't work the first time you run the app. After hot restart app - the login is done and we are right back to the home page.
Is there any way to fix it? (And without onboarding this logic works)
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final pref = await SharedPreferences.getInstance();
final showHome = pref.getBool('showHome') ?? false;
runApp(MyApp(showHome: showHome));
}
class MyApp extends StatelessWidget {
bool showHome;
MyApp({Key? key, required this.showHome}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
...
child: MaterialApp(
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return errorMessage();
} else if (snapshot.hasData) {
return const BottomPage();
} else {
return showHome ? const LoginPage() : const Onboarding();
}
},
),
}
LoginPage()
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _loginController.text.trim(),
password: _passwordContoller.text.trim(),
);
}
Onboarding()
onTap: () async {
final pref = await SharedPreferences.getInstance();
pref.setBool('showHome', true);
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoginPage()));
},

How To Get App To Move To Home Screen If Logged In? (Flutter, Firebase)

I've been trying to get my app to verify whether the user is logged in, in order to have the home screen seen instead of the login screen. How do I do so?
Below is the root screen to check if the user is logged in
` class Root extends StatefulWidget {
Root({Key? key}) : super(key: key);
#override
State<Root> createState() => _RootState();
}
class _RootState extends State<Root> {
AuthStatus _authStatus = AuthStatus.notLoggedIn;
final user = FirebaseAuth.instance.currentUser;
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
switch(_authStatus){
case AuthStatus.notLoggedIn:
return AuthScreen();
break;
case AuthStatus.loggedIn:
return HomePage();
break;
}
}
}
enum AuthStatus{
loggedIn,
notLoggedIn
}
class AuthService extends ChangeNotifier{
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<User?> signIn(String email, String password) async {
var user = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return user.user;
}
signOut() async {
return await _auth.signOut();
}
Future<User?> createPerson(String name, String email, String password) async {
var user = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return user. user;
}
}
Future<String> currentUser() async{
User user = await FirebaseAuth.instance.currentUser!;
return user.uid;
}
void _toNextPage(BuildContext context){
Navigator.of(context).push(MaterialPageRoute(builder: ((context) => SignUp())));
}
sorry I dont know the firebase coding.but I write the code how you handle the condation and remove the switch condation
#override
void initState() {
Timer(
Duration(seconds: 5),
() {
if(AuthStatus.loggedIn){
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}else if(AuthStatus.notLoggedIn){
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => Login()),
(route) => false);
}
});
super.initState();
}

App does not navigate to a different page when user authenticates

The issue is that my app does not navigate to another page automatically when user logs in or out.
class MyApp extends StatelessWidget {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(home: Wrapper()),
);
}
return Center(child: CircularProgressIndicator());
},
);
}
}
class Wrapper extends StatelessWidget {
const Wrapper({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user != null) {
return MaterialApp(initialRoute: '/', routes: {
'/': (context) => Home(),
'/profile': (context) => Profile()
});
}
return MaterialApp(initialRoute: '/', routes: {
'/': (context) => Welcome(),
'/signup': (context) => SignUp(),
'/signin': (context) => SignIn()
});
}
}
When the app starts it does show the Welcome() page. Then i am able to navigate to the signup page by pressing a signup button as such
onPressed: () {Navigator.pushNamed(context, "/signup");}),
but then when the user signs up, the app doesn't automatically navigate to Home()
class AuthService {
FirebaseAuth auth = FirebaseAuth.instance;
User _userFromFirebaseUser(User user) {
return user != null ? User(id: user.uid) : null;
}
Stream<User> get user {
return auth.authStateChanges().map(_userFromFirebaseUser);
}
Future<String> signUp(email, password) async {
try {
UserCredential user = await auth.createUserWithEmailAndPassword(
email: email, password: password);
await FirebaseFirestore.instance
.collection('users')
.doc(user.user.uid)
.set({'name': email, 'email': email});
_userFromFirebaseUser(user.user);
} on FirebaseAuthException catch (e) {
return e.code;
} catch (e) {
return e;
}
return "";
}
}
I am not sure what the issue is. Any help is appreciated.
First of all you need 1 MaterialApp not 3, then try to debug signUp method maybe there is an erorr for instance signUp returns Future<String> but in catch block you are returning an Exception and finally I suggest you to use Cubit whenever you need to listen state changes to navigate.

Creating a Firebase user does not update authStateChanges method

Quick question. Whenever I create a firebase user with email and password, the routing does not take the user to his correct page.
User user = context.watch<User>();
if (user == null) return LoginScreen();
///
rest of the code here where the user type stored in Firestore decides which page to navigate to
///
The createUserWithEmailAndPassword documentation does not say that it updates the authStateChanges method.
On the other hand, signInWithEmailAndPassword and FirebaseAuth.instance.signOut() documentations clearly states that it updates the authStateChanges method. And when I login the user is indeed taken to the correct page.
I assumed that createUserWithEmailAndPassword would do the same.
So should I navigate the user to the correct page manually (while popping all previous routes)?
Thank you in advance.
Routing Code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// final _themeProvider = Provider.of<ThemeProvider>(context);
return MultiProvider(
providers: [
ChangeNotifierProvider<ThemeProvider>(create: (context) => ThemeProvider()),
Provider<CurrentUser>(create: (context) => CurrentUser()),
StreamProvider(create: (context) => context.read<CurrentUser>().authStateChanges),
],
child: MaterialApp(
home: LandingRouting(),
),
);
}
}
class LandingRouting extends StatelessWidget {
const LandingRouting({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
logger.w('Landing Routing ...');
User user = context.watch<User>();
if (user == null) return LoginScreen();
return FutureBuilder(
future: FirebaseFirestore.instance.collection('users').doc(user.uid).get(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return Loading();
if (snapshot.hasError)
return AlertDialog(
title: Text('Error loading user'),
content: Text('${snapshot.error.toString}'),
);
else {
Map<String, dynamic> userData = snapshot.data.data();
assert(userData['userType'] != null, 'User Type cannot be null');
if (userData['userType'] == 'baqala') {
return BaqalaUserScreen();
} else {
return UserHomeScreen();
}
}
},
);
}
}
Sign up code:
class BaqalaAuth {
static void registerBaqala({
#required BuildContext context,
#required String baqalaName,
#required String license,
#required String landlineNumber,
#required String mobileNumber,
#required String email,
#required String password,
#required double lat,
#required double long,
}) async {
try {
FirebaseAuth.instance.createUserWithEmailAndPassword(email: email, password: password).then(
(userCred) async {
BaqalaInit.initBaqala(
context: context,
user: userCred.user,
landlineNumber: landlineNumber,
mobileNumber: mobileNumber,
licenseNumber: license,
baqalaName: baqalaName,
lat: lat,
long: long,
);
await userCred.user.updateProfile(displayName: baqalaName);
},
);
.
.
.
Login Code
class Auth {
static void login({
#required BuildContext context,
#required String email,
#required String password,
}) async {
try {
FocusScope.of(context).unfocus();
await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
} on PlatformException catch (e) {
logger.i('Login: Firebase Auth Exception');
_loginException(context, 'Error: ${e.code}\n\n${e.message}');
} on FirebaseAuthException catch (e) {
logger.i('Login: Firebase Auth Exception');
_loginException(context, 'Error: ${e.code}\n\n${e.message}');
} catch (e) {
_loginException(context, 'Login: Error: ${e.toString}');
}
}
static Future<void> logout() async {
await FirebaseAuth.instance.signOut();
}
}