I am developing a flutter application following this architecture which can be visualised here. There is a requirement that I need to use a different on-boarding flow when the User signs up for the first time apart from the usual flow.
So, after digging around a bit, I found that Firebase provides a bool variable isNewUser in the additionalUserInfo object of the AuthResult class. So, naturally my first thought was to pass this bool into the User model so that I can then verify whether the user has signed up for the first time and pass the corresponding route in the AuthWidget class like this.
class AuthWidget extends StatelessWidget {
const AuthWidget({Key key, @required this.userSnapshot}) : super(key: key);
final AsyncSnapshot<User> userSnapshot;
@override
Widget build(BuildContext context) {
if (userSnapshot.connectionState == ConnectionState.active) {
if (userSnapshot.hasData) {
return userSnapshot.data.isNew ? OnBoardingPage() : RootWidget();
} else {
return SignInPageBuilder();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
));
}
}
So while passing the boolean variable into the user model from my FirebaseAuthService class, I ran into some trouble. This is what I thought of doing in the FirebaseAuthService class. I have left comments inside the code for better understanding.
class FirebaseAuthService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
User _userFromFirebase(FirebaseUser user, AdditionalUserInfo additionalUserInfo) {
if(user == null){
return null;
}
return User(
uid: user.uid,
email: user.email,
photoUrl: user.photoUrl,
displayName: user.displayName,
isNew: additionalUserInfo.isNewUser // Passing this new variable from the AdditionalUserInfo var we passed as an argument
);
}
Stream<User> get onAuthStateChanged {
// This is where the first trouble is. The linter shows me the error :
// The argument type 'User Function(FirebaseUser, AdditionalUserInfo)' can't be assigned to the parameter type 'User Function(FirebaseUser)'.
// So I guess no other arguments can be passed to the _userFromFirebase function
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
Future<User> signInUsingGoogle() async {
final GoogleSignIn googleSignIn = GoogleSignIn();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken
);
final AuthResult authResult = await _firebaseAuth.signInWithCredential(credential);
// passing the additionalUserInfo variable
return _userFromFirebase(authResult.user, authResult.additionalUserInfo);
}
Future<User> signInUsingFacebook() async {
final FacebookLogin facebookLogin = FacebookLogin();
final FacebookLoginResult facebookLoginResult = await facebookLogin.logIn(['email']);
switch(facebookLoginResult.status) {
case FacebookLoginStatus.cancelledByUser:
print("Cancelled by user");
break;
case FacebookLoginStatus.error:
print("error sigining using facebook");
break;
case FacebookLoginStatus.loggedIn:
print("Logged In with facebook");
break;
}
final accessToken = facebookLoginResult.accessToken.token;
if(facebookLoginResult.status == FacebookLoginStatus.loggedIn) {
final facebookAuthcred = FacebookAuthProvider.getCredential(accessToken: accessToken);
final AuthResult authResult = await _firebaseAuth.signInWithCredential(facebookAuthcred);
// Same as Google login flow
return _userFromFirebase(authResult.user, authResult.additionalUserInfo);
}
return null;
}
Future<User> currentUser() async {
final FirebaseUser user = await _firebaseAuth.currentUser();
// This is the second confusion.
// Since we don't have an AuthResult object, how do I pass that variable.
// I could set the default value for the variable to null/false but I don't think that will be a scalable solution
return _userFromFirebase(user);
}
Future<void> signOut() async {
return _firebaseAuth.signOut();
}
}
Please do suggest if there is a better way to implement this. Thanks!