How to show alert on firebase auth errors flutter - firebase

I want to show an alert dialog when there is an error in the firebase auth.
Firebase already prints the error in the UI but i want to show a dialog to the user.
Heres my createUser and signInUser Funtion and my signup button function
Future registerWithEmailAndPassword({String email,password,username,image,phoneNumber}) async {
try {
UserCredential userCredential = await _firebaseAuth
.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
}
Future signInWithEmailAndPassword({String email, String password}) async {
try {
UserCredential userCredential = await _firebaseAuth
.signInWithEmailAndPassword(
email: email,
password: password
);
User user = userCredential.user;
assert(user.uid != null);
email = user.email;
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
}
}
press: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
context
.read<Authentication>()
.signInWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.whenComplete(() => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen())));
}
},

You can set up an AlertDialog widget similar to this. The Yes/No buttons are probably overkill in your situation and if so, just convert to an ok button and then you don't have to check the return result.
Future<String> showYesNoAlertDialog({
#required BuildContext context,
#required String titleText,
#required String messageText,
}) async {
// set up the buttons
final Widget yesButton = FlatButton(
onPressed: () => Navigator.pop(context, 'yes'),
child: const Text('Yes'),
);
final Widget noButton = FlatButton(
onPressed: () => Navigator.pop(context, 'no'),
child: const Text('No'),
);
// set up the AlertDialog
final alert = AlertDialog(
title: Text(titleText),
content: Text(messageText),
actions: [
yesButton,
noButton,
],
);
// show the dialog
return showDialog(
context: context,
builder: (context) => alert,
);
}
Then where you have your print statements outputting the errors, you'd call the above widget like this
final dr = await showYesNoAlertDialog(
context: context,
titleText: 'Authentication Error',
messageText:
'There has been an error during authentication. Would you like to retry?',
);
if (dr == 'yes') {
// Yes button clicked
}
else {
// No button clicked
}

Related

I have some problems in Flutter Firebase Login

I am coding an app for my company and I tried to add firebase authentication for login and registration to my app. The app shows no error and runs successfully.
But when a user tries to login with the wrong email and password, it is showing an internal flutter error instead of the toast I have programmed. And also I have used shared preferences to make users stay logged in.
So when a user tried to log in with the wrong credential it is showing an internal flutter error and when the app is re-opened, instead of going to the login screen, it is using the wrong credential and navigates user to Home Screen which is ridiculous.
These are the declared variables:
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController _emailcontroller = TextEditingController();
TextEditingController _passwordcontroller = TextEditingController();
bool passvis = true;
bool loading = false;
And this is the function for login:
Future loginForm() async {
FormState formSate = _formKey.currentState;
if (formSate.validate()) {
final User firebaseUser = (await firebaseAuth
.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text)
.catchError((errMsg) {
displayToast("Error: " + errMsg.toString(), context);
}))
.user;
if (firebaseUser != null) {
setState(() {
loading = true;
});
usersRef.child(firebaseUser.uid).once().then((DataSnapshot snap) {
if (snap.value != null) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return LocationHome();
}));
displayToast("Succesfully LoggedIn!", context);
} else {
firebaseAuth.signOut();
displayToast("No user found! Please try SignUp", context);
}
});
} else {
displayToast("Error Occured! Cannot log you in", context);
}
}
}
}
And for Registration the code is below:
Future validateForm() async {
FormState formSate = _formKey.currentState;
if (formSate.validate()) {
final User firebaseUser = (await firebaseAuth
.createUserWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text)
.catchError((errMsg) {
displayToast("Error: " + errMsg.toString(), context);
}))
.user;
if (firebaseUser != null) {
Map userDataMap = {
"name": _namecontroller.text.trim(),
"email": _emailcontroller.text.trim(),
"phone": _phonecontroller.text.trim(),
};
usersRef.child(firebaseUser.uid).set(userDataMap);
displayToast("Succesfully Registered!", context);
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return LocationHome();
}));
} else {
displayToast("User was unable to create", context);
}
}
}
}
The main.dart file is also coded correctly:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
SharedPreferences preferences = await SharedPreferences.getInstance();
var circle = preferences.getString("circle");
runApp(MaterialApp(
title: 'TaakStore',
home: circle == null ? Login() : Home(),
));
}
DatabaseReference usersRef =
FirebaseDatabase.instance.reference().child("users");
Dont worry about the displayToast function. It is a function manually created with flutter toast.
To display a toast, try the following:
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text
);
} on FirebaseAuthException catch (e) {
displayToast("Error: " + e.message.toString(), context);
print(e.message);
}
To check if the user is logged in or not use the following:
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
authStateChanges() is of type Stream<User> which will listen for any changes on the state of a user. So if user is logged in, it will return a valid user object and you can navigate to the home screen. Therefore no need to use shared preferences.
To display a toast
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text
);
} on FirebaseAuthException catch (e) {
displayToast("Error: " + e.message.toString(), context);
print(e.message);
}
To check if the user is logged in
//inside the main.dart in the "MaterialApp" widget
MaterialApp(home:buildHome(),)
buildHome(){return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (snapshot.hasData) {
print(snapshot);
//if the user is logged in return what you want
return "";
} else {
//else return what you want also
return"";
}
},
);}

Check if phone exists before signup/signin - Flutter Phone Authentication [duplicate]

This question already exists:
Firebase Authentication using Phone number (Error: Missing Session Info)
Closed 2 years ago.
I want to check if phone exists before signing in or signing up a user. With email registration, I used the following and I was able to tell if an email exists or not.
final url =
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/$verifyPassword?key={API_KEY}';
Similarly, for phone numbers, I used the following:
final url ='https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key={API_KEY}';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
However, I am getting the following error message:
Extracted data is {error: {code: 400, message: MISSING_SESSION_INFO, errors: [{message: MISSING_SESSION_INFO, domain: global, reason: invalid}]}}
I want to know why does it work for email but not for phone registration?
Also, is this the right way to check if a phone exists?
Here's my full code:
enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }
class AuthProvider with ChangeNotifier {
FirebaseAuth _auth = FirebaseAuth.instance;
User _user;
Status _status = Status.Uninitialized;
TextEditingController phoneNo;
String smsOTP;
String verificationId;
String errorMessage = '';
bool logedIn = false;
bool loading = false;
Status get status => _status;
TextEditingController address = TextEditingController();
AuthProvider.initialize() {
readPrefs();
}
Future signOut() async {
_auth.signOut();
_status = Status.Unauthenticated;
notifyListeners();
return Future.delayed(Duration.zero);
}
Future<void> readPrefs() async {
await Future.delayed(Duration(seconds: 3)).then((v) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
logedIn = prefs.getBool('logedIn') ?? false;
if (!logedIn) {
print('User is not logged in');
_status = Status.Unauthenticated;
} else {
print('User is logged in');
_user = _auth.currentUser;
_status = Status.Authenticated;
}
notifyListeners();
});
}
Future<void> verifyPhone(BuildContext context, String number,String password) async {
//To be used in the verifyPhone method
final PhoneCodeSent smsOTPSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
smsOTPDialog(context, number,password).then((value) {
_status = Status.Authenticated;
});
};
try {
await _auth.verifyPhoneNumber(
phoneNumber: number.trim(),
codeAutoRetrievalTimeout: (String verId) {
//Starts the phone number verification process for the given phone number.
//Either sends an SMS with a 6 digit code to the phone number specified, or sign's the user in and [verificationCompleted] is called.
this.verificationId = verId;
},
codeSent: smsOTPSent,
// timeout: const Duration(seconds: 20),
//If user is automatically verified (without having to type the code)
verificationCompleted: (AuthCredential credential) async {
Navigator.of(context).pop();
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
//TO DO:// Here you need to save the phone and password to DB
print('Adding user to DB');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
_status = Status.Authenticated;
} catch (error) {
print(error);
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
} else {
print("Error");
}
},
verificationFailed: (FirebaseAuthException exceptio) {
print('${exceptio.message} + something is wrong');
});
} catch (e) {
handleError(e, context,number,password);
errorMessage = e.toString();
notifyListeners();
}
notifyListeners();
}
Future<bool> smsOTPDialog(BuildContext context,String number,String password) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Enter SMS Code'),
content: Container(
height: 85,
child: Column(children: [
TextField(
onChanged: (value) {
this.smsOTP = value;
},
),
(errorMessage != ''
? Text(
errorMessage,
style: TextStyle(color: Colors.red),
)
: Container())
]),
),
contentPadding: EdgeInsets.all(10),
actions: <Widget>[
FlatButton(
child: Text("Confirm"),
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
final code = this.smsOTP.trim();
AuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: code);
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
print('user already exist');
// //TO DO:// Save the phone number and password to DB
print('Adding user to Db in the manual OTP route');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
} catch (error) {
print('INSIDE ERROR');
print(error);
}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("logedIn", true);
logedIn = true;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
loading = false;
notifyListeners();
} else {
print("No OTP was added");
loading = true;
notifyListeners();
Navigator.of(context).pop();
}
},
)
],
);
});
}
//Sign-In Method checks to see if a phone exists.
signIn(BuildContext context, String number, String password,AuthMode authMode) async {
try {
//Check to see if the phone number is available
final url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key=';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
print('Extracted data is ' + extractedData.toString());
//Register and send OTP if new user
if (extractedData == null && authMode == AuthMode.Signup) {
print('Inside NULL no errors');
// //Verify phone
verifyPhone(context, number, password);
}
//If tries to login but phone not available
else if(extractedData == null && authMode == AuthMode.Login)
{
_showErrorDialog('Phone number does not exist. Please Sign Up', context);
}
else if (extractedData['error'] != null) {
_showErrorDialog('Something went wrong! Please try again!', context);
}
//If someone signup but their phone already exist
else if(extractedData != null && authMode == AuthMode.Signup)
{
_showErrorDialog('Your phone already exists. Please Login!', context);
}
//If available, proceed to homepage
else {
print('User found');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
}
} catch (e) {
handleError(e, context,number,password);
}
}
handleError(error, BuildContext context,String number,String password) {
errorMessage = error.toString();
print('ERROR IS ' + errorMessage);
notifyListeners();
}
Future<bool> _showErrorDialog(String message,BuildContext context) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An Error Occurred!'),
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}
}

Flutter : Save user id from firestore using Shared Preferences and retrieve the value into class

So first of all I'm new in Flutter. I want to use current sign in user id from firebase as filter from data that i want to show in apps.Please Help Me.
here is the user id from wrapper.dart I want to use.
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final User user = Provider.of<User>(context);
print(user.uid);
and here i want to pass the value as uid from database.dart
Stream<List<InitialRPP>> get dataInitRPP {
return dbRPP
.where('uid', isEqualTo: uid)
.snapshots()
.map(_initRPPFromSnapshot);
}
Here is the full source code
wrapper.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rppapps/models/user.dart';
import 'package:rppapps/screens/authenticate/authenticate.dart';
import 'package:rppapps/screens/home/home.dart';
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final User user = Provider.of<User>(context);
print(user.uid);
// Login or Home
if (user == null) {
return Authenticate();
} else {
return Home();
}
}
}
database.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:rppapps/models/model.dart';
class DatabaseService {
// the code should be in here "String uid;" like that
//collection reference
final CollectionReference dbRPP = Firestore.instance.collection('rpp');
Future addinitialRPP(String uid, String nama, String tahun, String kelas,
String semester, String mapel, String materi) async {
return await dbRPP.add({
'uid': uid,
'nama': nama,
'tahun': tahun,
'kelas': kelas,
'semester': semester,
'mapel': mapel,
'materi': materi
});
}
List<InitialRPP> _initRPPFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return InitialRPP(
uid: doc.data['uid'] ?? '',
nama: doc.data['nama'] ?? '',
tahun: doc.data['tahun'] ?? '',
kelas: doc.data['kelas'] ?? '',
semester: doc.data['semester'] ?? '',
mapel: doc.data['mapel'] ?? '',
materi: doc.data['materi'] ?? '');
}).toList();
}
Stream<List<InitialRPP>> get dataInitRPP {
return dbRPP
.where('uid', isEqualTo: uid)
.snapshots()
.map(_initRPPFromSnapshot);
}
}
EDIT: (sign in sign out method and firebase_auth)
auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:rppapps/models/user.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// created user object based on FirebaseUser
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged.map(_userFromFirebaseUser);
}
// sign anon
Future signInAnon() async {
try {
AuthResult result = await _auth.signInAnonymously();
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign with email and pass
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// register with email and pass
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
}
pubspec.yaml
firebase_auth: ^0.14.0+5
cloud_firestore: ^0.12.9+4
provider: ^3.1.0
Try using onAuthStateChanged() instead. You can check if the user is logged in by adding this Stream to a Streambuilder. Any time the user logs out or in, the Stream automatically updates. Then wrap your Home widgets with a FutureBuilder and pass the currentUser() future. This will return a snapshot containing the user information, such as email and uid.
Finally, you can filter widgets by checking if the uid is the same as the given one. For example, if a user is admin or not.
wrapper.dart
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<FirebaseUser>(
stream: AuthService().authStateChanges(),
builder: (context, AsyncSnapshot snapshot) {
// if the stream has data, the user is logged in
if (snapshot.hasData) {
// isLoggedIn
return Home();
} else if (snapshot.hasData == false &&
snapshot.connectionState == ConnectionState.active) {
// isLoggedOut
return Authenticate();
} else {
return CircularProgressIndicator();
}
},
),
);
}
}
auth.dart
class AuthService {
final FirebaseAuth _firebaseInstance = FirebaseAuth.instance;
final CollectionReference _usersCollection =
Firestore.instance.collection("users");
// User State
Stream<FirebaseUser> authStateChanges() {
FirebaseAuth _firebaseInstance = FirebaseAuth.instance;
return _firebaseInstance.onAuthStateChanged;
}
// Current User
Future<FirebaseUser> currentUser() async {
FirebaseAuth _firebaseInstance = FirebaseAuth.instance;
return _firebaseInstance.currentUser();
}
// Sign Out
Future<void> signOut() async {
FirebaseAuth _firebaseInstance = FirebaseAuth.instance;
return _firebaseInstance.signOut();
}
// Sign In Anonymously
Future<AuthResult> signInAnon() async {
return await _firebaseInstance.signInAnonymously().catchError((error) {
print(error);
});
}
// Sign In With Email And Password
Future<AuthResult> signIn(String email, String password) async {
return await _firebaseInstance
.signInWithEmailAndPassword(email: email, password: password)
.catchError((error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
print("ERROR_INVALID_EMAIL");
break;
case "ERROR_WRONG_PASSWORD":
print("ERROR_WRONG_PASSWORD");
break;
case "ERROR_USER_NOT_FOUND":
print("ERROR_USER_NOT_FOUND");
break;
case "ERROR_USER_DISABLED":
print("ERROR_USER_DISABLED");
break;
case "ERROR_TOO_MANY_REQUESTS":
print("ERROR_TOO_MANY_REQUESTS");
break;
case "ERROR_NETWORK_REQUEST_FAILED":
print("ERROR_NETWORK_REQUEST_FAILED");
break;
}
});
}
// Create User With Email And Password
Future<AuthResult> signUp(String email, String password) async {
return await _firebaseInstance
.createUserWithEmailAndPassword(email: email, password: password)
.catchError(
(error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
print("ERROR_INVALID_EMAIL");
break;
case "ERROR_WEAK_PASSWORD":
print("ERROR_WEAK_PASSWORD");
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
print("ERROR_EMAIL_ALREADY_IN_USE");
break;
case "ERROR_NETWORK_REQUEST_FAILED":
print("ERROR_NETWORK_REQUEST_FAILED");
break;
}
},
).then((user) {
if (user != null) {
_usersCollection.document(user.user.uid).setData(
{
"email": user.user.email,
"uid": user.user.uid,
},
);
return null;
} else {
return null;
}
});
}
}
authenticate.dart
class Authenticate extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(child: Text("Authenticate")),
// Sign In Button
RaisedButton(
onPressed: () => AuthService().signIn("testemail01#gmail.com", "password"),
child: Text("Sign In as user 01"),
),
RaisedButton(
onPressed: () => AuthService().signIn("testemail02#gmail.com", "password"),
child: Text("Sign In as user 02"),
)
],
);
}
}
home.dart
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<FirebaseUser>(
future: AuthService().currentUser(),
builder: (context, snapshot) {
if (snapshot.hasData) {
String userEmail = snapshot.data.email;
String userUid = snapshot.data.uid;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(child: Text("Home")),
// Get Current User Email
Center(child: Text(userEmail)),
// Get Current User UID
Center(child: Text(userUid)),
// Filter By UID
Builder(
builder: (context) {
if (userUid == "X6Ibch8OwmZWrYIB1F3IPpbBQbk2") {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.admin_panel_settings),
Text("Admin"),
],
);
}
return Container();
},
),
// Sign Out Button
RaisedButton(
onPressed: () => AuthService().signOut(),
child: Text("Sign Out"),
)
],
);
} else {
return CircularProgressIndicator();
}
},
);
}
}

How to edit a user in flutter firestore

I have been learning how does flutter work with firestore and now I am working in user auth with password, email and username, when a user is created the email and password are saved with an uid but the username and the email(again) are saved in firestore with a different uid, by the way I have tried a lot of things to make it have the same id but I currently cant find the way. in addition to this, there is also a function that is supposed to edit the username and save those changes. The problem comes when trying to implement the edit functinality because the edit form doesnt return anything as an output except the loading screen, I think this error is happening because of the uids. How can I fix this problem?
models/user.dart
class CustomUser {
final String uid;
CustomUser({this.uid});
}
class UserData {
final String uid;
final String name;
UserData({this.uid, this.name});
}
models/username.dart
class Username {
final String name;
Username({this.name});
}
services/auth.dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on fb user
CustomUser _userFromFirebaseUser(User user) {
return user != null ? CustomUser(uid: user.uid) : null;
}
Stream<CustomUser> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
//signin email password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signup
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signout
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
services/database.dart
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection = FirebaseFirestore.instance.collection('usernames');
Future updateUserData(String name) async { // this is the function that has to edit the username
return await userCollection.doc(uid).set({
'name': name,
});
}
Future uploadUserInfo(userMap) async { // this function adds username and email to firestore
return await userCollection.doc(uid).set(userMap);
}
List<Username> _usernameListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return Username(
name: doc.data()['name'] ?? '',
);
}).toList();
}
// userData from snapshot
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
name: snapshot.data()['name'],
);
}
Stream<List<Username>> get usernames {
return userCollection.snapshots().map(_usernameListFromSnapshot);
}
Stream<UserData> get userData {
return userCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}
register.dart(code that registers the user with a username)
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
databaseService.uploadUserInfo(userInfoMap);
});
if (result == null) {
setState(() {
error = 'please suply a valid email';
loading = false;
});
}
}
}),
editForm.dart
final _formKey = GlobalKey<FormState>();
String _currentName;
final user = Provider.of<CustomUser>(context);
StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Text('edit username!'),
SizedBox(
height: 30,
),
TextFormField(
// initialValue: userData.user gives a initial text to the input
validator: (val) => val.isEmpty ? 'Please enter a name' : null,
onChanged: (val) => setState(() => _currentName = val),
),
RaisedButton(
child: Text('Save'),
onPressed: () async {
if (_formKey.currentState.validate()) {
print('update if good');
await DatabaseService(uid: user.uid).updateUserData(
_currentName ?? userData.name,
);
}
Navigator.pop(context);
})
],
));
} else {
return Loading();
}
},
);
If you have any questions please let me know;)
In your register.dart, the registerWithEmailAndPassword method returns a User object which contains the uid internally created by FirebaseAuth however, it doesn't seem like you took used this uid to update your Firestore user document. I've implemented a sample of what should have been done below.
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
DatabaseService(uid: val.uid).uploadUserInfo(userInfoMap);
});
I just realized that your registerWithEmailAndPassword function returns a CustomUser instead of a Firebase User. I just modified it to make it work.
//signup
Future<User> registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return result.user;
} catch (e) {
print(e.toString());
return null;
}
}
//editFrom.dart
//form validation function
Map<String, String> userMap = {'name': currentName};
await DatabaseService(uid: user.uid).uploadUserInfo(userMap);
Side note: when working with Futures, it helps if you specify the expected return type as this will help you with debugging. I've done it for the function above

Failed to return to login page/screen after signing out (Not sure if sign out is successful or not)

I'm new in flutter and dealing with backend. I faced problem in signing out and I don't know how to fix it due to lack of experience. When I sign out, it would not return to the login screen (Authenticate). I'm not quite sure if the problem lies at the function I wrote at the wrapper or somewhere in auth.
The wrapper widget will be executed everytime the app runs:
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: new MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.redAccent,
),
home: Wrapper(),
),
);
}
}
The wrapper code is as below:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
print(user);
if (user == null) {
return Authenticate();
} else {
return MyBottomNavigationBar();
}
}
}
Sign out button function:
onPressed: () async {
await _auth.signOut();
print('signing out');
},
Auth code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mudah_replica/models/user.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
//Create user object based on FirebaseUser
User _userFromFirebaseUser(FirebaseUser user){
return user != null ? User(uid: user.uid) : null;
}
//Auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
//Sign in anonymously
Future signInAnon() async {
try {
AuthResult result = await _auth.signInAnonymously();
FirebaseUser user = result.user;
return user;
} catch(e) {
print(e.toString());
return null;
}
}
//Sign in with Email & Password
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch(e){
print(e.toString());
return null;
}
}
//Register with Email & Password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch(e){
print(e.toString());
return null;
}
}
//Sign out
Future signOut() async {
try{
return await _auth.signOut();
} catch(e){
print('Fail to sign out');
print(e.toString());
return null;
}
}
}
in your wrapper class, you are returning auth widget instead of navigate to the right screen, so it doesn't route. Instead, you might wanna navigate to the Auth screen. Do something like this:
onPressed: () async {
await _auth.signOut();
Navigator.push(context,
MaterialPageRoute(builder: (context) => Authenticate())
},

Resources