Google authentication problem (Flutter and Firebase) - firebase

I am flutter-newbie and I have one problem: I want to add Google authentication to my flutter app with firebase. This is my code for login screen:
import 'package:PixiCall/resources/firebase_repository.dart';
import 'package:PixiCall/screens/home_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
FirebaseRepository _repository = FirebaseRepository();
#override
Widget build(BuildContext context) {
return Scaffold(
body: loginButton(),
);
}
Widget loginButton() {
return FlatButton(
padding: EdgeInsets.all(35),
child: Text(
'Login',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w900,
letterSpacing: 1.2,
),
),
onPressed: () => performLogin,
);
}
void performLogin() {
_repository.signIn().then((User user) {
if (user != null) {
authenticateUser(user);
} else {
print('There was an error');
}
});
}
void authenticateUser(User user, BuildContext context) {
_repository.authenticateUser(user).then((isNewUser) {
if (isNewUser) {
_repository.addDataToDb(user).then((value) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return HomeScreen();
}));
});
} else {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return HomeScreen();
}));
}
});
}
}
I have this error here:
lib/screens/login_screen.dart:39:25: Error: Too few positional arguments: 2 required, 1 given.
authenticateUser(user);
What is the other parameter which I have to add?
Also I think that I have one more mistake in other file. This is the code from other file:
import 'package:PixiCall/models/user.dart';
import 'package:PixiCall/utils/utilities.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
class FirebaseMethods {
final FirebaseAuth _auth = FirebaseAuth.instance;
GoogleSignIn _googleSignIn = GoogleSignIn();
static final FirebaseFirestore firestore = FirebaseFirestore.instance;
//user class
User1 user = User1();
Future<User> getCurrentUser() async {
User currentUser;
currentUser = await _auth.currentUser;
return currentUser;
}
Future<User> signIn() async {
GoogleSignInAccount _signInAccount = await _googleSignIn.signIn();
GoogleSignInAuthentication _signInAuthentication =
await _signInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: _signInAuthentication.accessToken,
idToken: _signInAuthentication.idToken,
);
User user = await _auth.signInWithCredential(credential);
return user;
}
Future<bool> authenicateUser(User user) async {
QuerySnapshot result = await firestore
.collection('users')
.where('email', isEqualTo: user.email)
.get();
final List<DocumentSnapshot> docs = result.docs;
//if user is registered then length of list > 0 or else less than 0
return docs.length == 0 ? true : false;
}
Future<void> addDataToDb(User currentUser) async {
String username = Utils.getUsername(currentUser.email);
user = User1(
uid: currentUser.uid,
email: currentUser.email,
name: currentUser.displayName,
profilePhoto: currentUser.photoURL,
username: username);
firestore.collection('users').doc(currentUser.uid).set(user.toMap(user));
}
}
This is the mistake in console:
lib/resources/firebase_methods.dart:32:17: Error: A value of type 'UserCredential' can't be assigned to a variable of type 'User'.
Sorry if I confused you, as I said, I am newbie. If you want any other informations please ask here.

For the first mistake , you defined a function void authenticateUser(User user, BuildContext context) so when you use it, it expects 2 arguments a User type object and a BuildContext object
Then you are calling this function in
void performLogin() {
_repository.signIn().then((User user) {
if (user != null) {
authenticateUser(user);
} else {
print('There was an error');
}
});
}
You are passing only the User object, missing the BuildContext

For GoogleSingIn this was my solution:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();
//create user object based on FB json
User _userFromFirebaseUser(FirebaseUser user) {
return user != null
? User(uid: user.uid, emailVerified: user.isEmailVerified)
: null;
}
//auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged.map(_userFromFirebaseUser);
}
Future<User> singInUsingGoogle() async {
int age;
String email;
String name;
String lastname;
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
final result = await _auth.signInWithCredential(credential);
FirebaseUser user = result.user;
print(result.user.providerData);
await DatabaseService(uid: user.uid).createUserData(
//user info
)

Related

Firestore and google auth with role base authorization in Flutter

At the time when user sign in to my app using google sign in with firebase how can I check that if user already have data or not in firestore database if yes then my app will check for user role and redirect user to the screen acc. to the role else it will create a document in firestore database with name, email, role and uid stored. but the problem is we cannot differentiate between signup and sign in with google auth like we can do in email and password auth. when user make purchase in our app his/her role will be changed to expert but due calling the signinwithgoogle in login page the user role is again being set to basic in database.
hope you understood the problem.
Here is my auth.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:gfd_official/User/User.dart';
import 'package:gfd_official/services/database.dart';
import 'package:google_sign_in/google_sign_in.dart';
String photoUrl;
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on firebase user
Userdat _userFromFirebaseUser(User user) {
return user != null ? Userdat(uid: user.uid) : null;
}
// auth change user stream
Stream<Userdat> get user {
return _auth
.authStateChanges()
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
Future signInWithGoogle() async {
GoogleSignIn googleSignIn = GoogleSignIn();
final acc = await googleSignIn.signIn();
final auth = await acc.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: auth.accessToken, idToken: auth.idToken);
try {
final res = await _auth.signInWithCredential(credential);
User user = res.user;
photoUrl = user.photoURL;
//create a new document for the user with the uid
await DatabaseService(uid: user.uid)
.updateUserData(user.displayName, user.email, 'basic', user.uid);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
Future logOut() {
try {
GoogleSignIn().signOut();
return _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
}
class UserHelper {
static FirebaseFirestore _db = FirebaseFirestore.instance;
static saveUser(User user) async {
Map<String, dynamic> userData = {
"name": user.displayName,
"email": user.email,
"role": "basic",
};
try {
final userRef = _db.collection("users").doc(user.uid);
if ((await userRef.get()).exists) {
await userRef.update({});
} else {
await _db.collection("users").doc(user.uid).set(userData);
}
} catch (e) {
print(e.toString());
}
}
}
This is my loginpage.dart
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_auth_buttons/flutter_auth_buttons.dart';
import 'package:gfd_official/Login_data/auth.dart';
class Loginpage extends StatefulWidget {
#override
_LoginpageState createState() => _LoginpageState();
}
class _LoginpageState extends State<Loginpage> {
final AuthService _authService = AuthService();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.red[50],
body: Container(
child: Column(
children: [
SizedBox(
height: 360,
),
Text(
'Yaha par Login Kare',
style: TextStyle(
fontSize: 30,
color: Colors.grey[800],
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 50,
),
Center(
child: GoogleSignInButton(
onPressed: () async {
dynamic result = await _authService.signInWithGoogle();
if (result == null) {
} else {
}
},
darkMode: true,
textStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
fontFamily: "Roboto",
color: Colors.white),
borderRadius: 20,
),
),
],
),
),
);
}
}
this is my database class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:gfd_official/User/User.dart';
class DatabaseService {
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference userCollection =
FirebaseFirestore.instance.collection('users');
Future updateUserData(String name, String email, String role, String
userId)
async {
return await userCollection.doc(uid).set({
'name': name,
'email': email,
'role': role,
'userId': userId,
},
SetOptions(merge: true));
}
//user data from snapshot
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
final usersn = snapshot.data();
return UserData(
uid: uid,
name: usersn['name'],
email: usersn['email'],
role: usersn['role'],
);
}
//get user data
Stream<QuerySnapshot> get userrole {
return userCollection.snapshots();
}
//get user doc stream
Stream<UserData> get userData {
return userCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}

Flutter App stuck at splash screen not moving to login page or homepage

I tried to add splash screen to add splash screen to my app but it gets stuck at splash screen itsalf and does not move to next screens
I have added the code here:-
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserInfo();
}
Future getUserInfo() async {
await getUser();
setState(() {});
print(uid);
navigateUser();
}
navigateUser()
{
if(uid!=null && authSignedIn != false)
{
Timer(Duration(seconds: 2),
()=>Navigator.pushReplacementNamed(context, "/toprofilepage")
);
}
else{
Timer(Duration(seconds: 2),
()=>Navigator.pushReplacementNamed(context, "/tologinpage")
);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Login',
initial route:'/',
routes: {
'/toprofilepage':(context)=>FirstScreen(),
'/tologinpage':(context)=>LoginPage(),
},
home: Scaffold(
body: Center(
child: Text("Saraswat",style: TextStyle(fontSize: 40,fontWeight: FontWeight.bold,fontStyle: FontStyle.italic),),
),
)
);
}
}
I am getting this following message in console also:-
E/flutter ( 5947): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
Code for sign in:-
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
bool authSignedIn;
String uid;
String name;
String imageUrl;
Future getUser() async {
// Initialize Firebase
await Firebase.initializeApp();
SharedPreferences prefs = await SharedPreferences.getInstance();
bool authSignedIn = prefs.getBool('auth') ?? false;
final User user = _auth.currentUser;
if (authSignedIn == true) {
if (user != null) {
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
}
}
}
Future<String> signInWithGoogle() async {
// Initialize Firebase
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential userCredential = await _auth.signInWithCredential(credential);
final User user = userCredential.user;
if (user != null) {
// Checking if email and name is null
assert(user.uid != null);
assert(user.displayName != null);
assert(user.photoURL != null);
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final User currentUser = _auth.currentUser;
assert(user.uid == currentUser.uid);
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', true);
return 'Google sign in successful, User UID: ${user.uid}';
}
return null;
}
void signOutGoogle() async {
await googleSignIn.signOut();
await _auth.signOut();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', false);
uid = null;
name = null;
imageUrl = null;
print("User signed out of Google account");
}
I tried a lot of things but did not get any solution pls help!.Should I place the navigator function anywhere else or is there some other error pls help!.
Try doing the flow conditions in initialRoute only
Since the firebase has updated the way we check the user is logged in or not. Its not a async task so you can use directly in the MyApp class.
initialRoute: FirebaseAuth.instance.currentUser != null
? HomeScreen.route_name
: AuthScreen.route_name
Or you can use the listener for auth change
FirebaseAuth.instance.onAuthStateChanged.listen((firebaseUser) {
// do whatever you want based on the firebaseUser state
});
so when the auth is changed it will re-direct it to the page you want to, like this
home: StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (streamContext, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting)
return SplashScreen();
if (userSnapshot.hasData) {
return HomeScreen();
}
return AuthScreen();
},
),

error: The operator '[]' isn't defined for the type 'DocumentSnapshot'

The code was working fine , but when I updated my dependencies I started getting the following errors:
error: The operator '[]' isn't defined for the type 'DocumentSnapshot'
error: The expression doesn't evaluate to a function, so it can't be invoked. (invocation_of_non_function_expression
here is the code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:za/pages/signup.dart';
import 'home.dart';
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
final GoogleSignIn googleSignIn = new GoogleSignIn();
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController _emailTextController = TextEditingController();
TextEditingController _passwordTextController = TextEditingController();
SharedPreferences preferences;
bool loading = false;
bool islogedin = false;
bool hidepass = true;
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
setState(() {
loading = true;
});
await firebaseAuth.currentUser().then((user){
if (user!=null){
setState(() => islogedin= true);
}
});
if (islogedin == true) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage()));
}
setState(() {
loading = false;
});
}
Future handleSignIn() async {
preferences = await SharedPreferences.getInstance();
setState(() {
loading = true;
});
final GoogleSignInAccount googleUser = await googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
User firebaseUser =
(await firebaseAuth.signInWithCredential(credential)).user;
if (firebaseUser != null) {
final QuerySnapshot result = await Firestore.instance
.collection("users")
.where("id", isEqualTo: firebaseUser.uid)
.get();
final List<DocumentSnapshot> documents = result.docs;
if (documents.length == 0) {
// insert the user to our collection
FirebaseFirestore.instance
.collection("users")
.doc(firebaseUser.uid)
.set({
"id": firebaseUser.uid,
"username": firebaseUser.displayName,
"profilePicture": firebaseUser.photoURL
});
await preferences.setString("id", firebaseUser.uid);
await preferences.setString("username", firebaseUser.displayName);
await preferences.setString("photoUrl", firebaseUser.displayName);
} else {
await preferences.setString("id", documents[0].data['id']);
await preferences.setString("username", documents[0].data['username']);
await preferences.setString("photoUrl", documents[0].data['photoUrl']);
}
Fluttertoast.showToast(msg: "Login was successful");
setState(() {
loading = false;
});
} else {
Fluttertoast.showToast(msg: "Login failed");
}
}
DocumentSnapshot doesn't have [] operator, as error says explicitly. It has data field, which has.
Replace this code:
await preferences.setString("id", documents[0]['id']);
await preferences.setString("username", documents[0]['username']);
await preferences.setString("photoUrl", documents[0]['photoUrl']);
With this:
await preferences.setString("id", documents[0].data['id']);
await preferences.setString("username", documents[0].data['username']);
await preferences.setString("photoUrl", documents[0].data['photoUrl']);
According to the source:
/// Returns the current [User] if they are currently signed-in, or `null` if
/// not.
///
/// You should not use this getter to determine the users current state,
/// instead use [authStateChanges], [idTokenChanges] or [userChanges] to
/// subscribe to updates.
User get currentUser {
if (_delegate.currentUser != null) {
return User._(this, _delegate.currentUser);
}
return null;
}
Try await preferences.setString("username", documents[0].data()['username']);
Btw, did you update your firebase packages? if yes, follow this link to check for the updated changes https://firebase.flutter.dev/docs/migration/
fixed 1, i changed
await preferences.setString("id", documents[0]['id']);
await preferences.setString("username", documents[0]['username']);
await preferences.setString("photoUrl", documents[0]['photoUrl']);
to
await preferences.setString("id", documents[0].data()['id']);
await preferences.setString("username", documents[0].data()['username']);
await preferences.setString("photoUrl", documents[0].data()['photoUrl']);
aparently, Getting a snapshots data via the data getter is now done via the data() method.
I'm only left with error
on
await firebaseAuth.currentUser().then((user){
if (user!=null){
setState(() => islogedin= true);
}
according to the documentation(https://firebase.flutter.dev/docs/migration/), Accessing the current user via currentUser() is now synchronous via the currentUser getter.

How to sign in with different types of user in flutter and firebase?

I am trying to sign in with different types of users, I have firebase authenticator to sign in and made a database with users and userType as a field, so anyone knows a function that shows different interface with different types of user ?
My Code :
class _LoginScreenState extends State<LoginScreen> {
var email;
var password;
var username;
var gender;
var userType;
final usernameController = TextEditingController();
final emailController = TextEditingController();
final passwordController = TextEditingController();
final firestoreInstance = Firestore.instance;
void login() async {
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
firebaseAuth
.signInWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((result) {
{
Navigator.pushReplacementNamed(context, '/homepage');
}
}).catchError((err) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text(err.message),
actions: [
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
}
void getUserData() async {
try {
firestoreInstance
.collection('Users')
.document(usernameController.text)
.get()
.then((value) {
setState(() {
email = (value.data)['email'];
password = (value.data)['password'];
gender = (value.data)['gender'];
username = (value.data)['username'];
userType = (value.data)['userType'];
});
});
} catch (e) {
print(e.toString);
}
}
If you want to navigate to a different page after signing in, that'd be something like:
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
firebaseAuth
.signInWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((result) {
firestoreInstance
.collection('Users')
.document(usernameController.text)
.get()
.then((value) {
var userType = (value.data)['userType'];
if (userType == "firstType") {
Navigator.pushReplacementNamed(context, '/homepage');
}
else if (userType == "secondType") {
Navigator.pushReplacementNamed(context, '/anotherpage');
}
});
}

Save Data to Firebase Realtime Database in Flutter

In my code the user is Sign in through phone authentication.After login i want to get the user id of the user and add it to the Firebase database along with its personal details.
But when i have done this the Path in database is direct rather than it should be via first UserId then under that the personal details.
I have also provide the image with the database output.
Code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:udharibook/Screens/SignInPage.dart';
import 'package:udharibook/Screens/dashboard.dart';
class AuthService {
String UserId ='';
final DBRef = FirebaseDatabase.instance.reference().child('Users');
final FirebaseAuth _auth = FirebaseAuth.instance;
handleAuth(){
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext, snapshot){
if(snapshot.hasData){
getCurrentUser();
writeData();
return DashboardPage();
}
else {
return SignIn();
}
},
);
}
getCurrentUser() async{
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
// Similarly we can get email as well
//final uemail = user.email;
UserId = uid;
print('User ID: '+UserId);
//print(uemail);
}
void writeData(){
DBRef.child(UserId).set({
'id':'ID1',
'Name':'Mehul Jain',
'Phone':'8856061841'
});
}
signOut(){
FirebaseAuth.instance.signOut();
}
signIn(AuthCredential authCreds){
FirebaseAuth.instance.signInWithCredential(authCreds);
}
signInWithOTP(smsCode,verId){
AuthCredential authCreds = PhoneAuthProvider.getCredential(
verificationId: verId,
smsCode: smsCode
);
signIn(authCreds);
}
}
Database Image
Try the following:
void writeData() async{
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
DBRef.child(uid).set({
'id':'ID1',
'Name':'Mehul Jain',
'Phone':'8856061841'
});
}
Remove the getCurrentUser() method and retrieve the uid in writeData()
getCurrentUser() method is an async method, so it not sure that your userId will be having value before the writeData() method is called.
so what you can do is call writeData() after completion of getCurrentUser() method i.e. :
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:udharibook/Screens/SignInPage.dart';
import 'package:udharibook/Screens/dashboard.dart';
class AuthService {
String UserId ='';
final DBRef = FirebaseDatabase.instance.reference().child('Users');
final FirebaseAuth _auth = FirebaseAuth.instance;
handleAuth(){
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext, snapshot){
if(snapshot.hasData){
getCurrentUser();
//call writeData() from inside the above method
return DashboardPage();
}
else {
return SignIn();
}
},
);
}
getCurrentUser() async{
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
// Similarly we can get email as well
//final uemail = user.email;
UserId = uid;
print('User ID: '+UserId);
//print(uemail);
//Here you add the method calling
writeData();
}
void writeData(){
DBRef.child(UserId).set({
'id':'ID1',
'Name':'Mehul Jain',
'Phone':'8856061841'
});
}
signOut(){
FirebaseAuth.instance.signOut();
}
signIn(AuthCredential authCreds){
FirebaseAuth.instance.signInWithCredential(authCreds);
}
signInWithOTP(smsCode,verId){
AuthCredential authCreds = PhoneAuthProvider.getCredential(
verificationId: verId,
smsCode: smsCode
);
signIn(authCreds);
}
}

Resources