I am trying to implement a CircularProgressIndicator when waiting user to finish their authentication process, here's the code, I am using Firebase as the auth backend
Future<void> signIn() async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
how to implement the circularprogressindicator into the code?
CircularProgressIndicator can be implemented in UI with a condition.If you want to implement it in some area of your page you can use boolean to make it work like this:
bool showCircular = false;
Future<void> signIn() async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
setState(() {
showCircular=true;
});
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
setState(() {
showCircular=false;
});
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
Now anywhere in your UI add this widget:
showCircular ? CircularProgressIndicator() : SizedBox(),
Recommended way is using an AlertDialog to show progress indicator. Here is an example with AlertDialog.To open Dialogs you need context so take BuildContext as a parameter and call signIn(context); from sign in button.We will call dialog using openLoadingDialog function and when sign in is completed we will pop it using Navigator.of(context).pop();
Future<void> signIn(BuildContext context) async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
openLoadingDialog(context, 'Signing In...');
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
Navigator.of(context).pop();
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
Paste this dialog anywhere in your app.You can call it by passing context and message you want to show.
openLoadingDialog(BuildContext context, String text) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Row(children: <Widget>[
SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(
strokeWidth: 1,
valueColor: AlwaysStoppedAnimation(Colors.black)
)
),
SizedBox(width: 10),
Text(text)
]),
)
);
}
Related
I built a login page using firebase. When the user clicks the login button, a CircularProgressIndicator is started and if the user gave the correct credentials everything works perfectly but if there is an error (e.g user not found OR wrong password) the user will be not forwarded to the next page but the CircularProgressIndicator will not stop showing.
The aim is, that if there is an error, I want to stop the CircularProgressIndicator and want to show an error message but I don't know how to stop the CircularProgressIndicator.
My code looks the following:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
final navigatorKey = GlobalKey<NavigatorState>();
class LoginUser {
Future loginUser(userEmail, userPassword, context) async {
navigatorKey: navigatorKey;
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Center(child: CircularProgressIndicator())
);
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: userEmail,
password: userPassword
);
// Close Dialog when route changes
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
return "ec-unf";
} else if (e.code == 'wrong-password') {
return "ec-wp";
}
navigatorKey.currentState!.popUntil((route) => route.isFirst);
}
}
}
Does anybody know how to do this?
Thanks!
Chris
maybe you can add isLoading and finaly condition after try catch
isLoading = true
try {
//your code
print("done !");
isLoading
} catch (err) {
print("error");
isLoading = false
} finally {
print("Finish "); //error and success
isLoading = false
}
Just for completeness, here is how i solved it.
I created a separate function in the login file and when i clicked the Sign in buttton, the following function was executed:
Future login() async{
isSignInLoading = true; // START LOADING CYCLE
// --- LOADING CYCLE - START ---
if (isSignInLoading == true) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Center(child: CircularProgressIndicator())
);
}
// --- LOADING CYCLE - END ---
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _controllerEmail.text,
password: _controllerPassword.text
);
} catch (e) {
isSignInLoading = false; // START LOADING CYCLE
isErrorLogin = true; // START LOADING CYCLE
}
Navigator.of(context).pop();
// --- ERROR MESSAGE - START ---
if (isErrorLogin == true) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Center(child:
AlertDialog(
title: const Text('Something went wrong!'),
content: const Text('Please check your entries, either the user does not exist or the password is incorrect.'),
actions: [
TextButton(
child: const Text(
'Close',
style: TextStyle(
color: Color(0xff004494),
fontWeight: FontWeight.w500
),
),
onPressed: () {
Navigator.of(context).pop();
},
),
],
)
)
);
}
// --- ERROR MESSAGE - END ---
//var loginInfo = await LoginUser().loginUser(_controllerEmail.text,_controllerPassword.text, context);
if (FirebaseAuth.instance.currentUser != null) {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("userLoginStatus", true);
Navigator.popAndPushNamed(context, '/overview');
}
}
Pretty sure there is a way better way to do it but this worked for me.
I'm trying to implement redirecting to the login/main page when the login state is changed, but when the login state is changed, nothing happens. Here is the code:
return Scaffold(
body: StreamBuilder<AuthState>(
stream: Auth.instance.stateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
assert(snapshot.connectionState != ConnectionState.done);
if (snapshot.data == AuthState.loggedIn) {
return Navigator(
onGenerateRoute: (_) {
return MaterialPageRoute(
builder: (context) => const MainWidget(),
);
}
);
}
if (snapshot.data == AuthState.loggedOut) {
return Navigator(
onGenerateRoute: (_) {
return MaterialPageRoute(
builder: (context) => const LoginWidget(),
);
}
);
}
if (snapshot.data == AuthState.verifyEmail) {
return const EmailNotVerWidget();
}
throw Exception('Unknown Auth State');
}
),
);
Here, Auth.instance.stateChanges() is just a wrapper for the corresponding FirebaseAuth stream with additional logic.
As the result of debugging, it turned out that, when the login state is changed, the corresponding Navigator widget is returned but its onGenerateRoute() method isn't called, however it's called for the first time when the app is loading. I'm new in Flutter.
Upd. Here is the content of Auth and AuthState:
enum AuthState {
loggedIn,
loggedOut,
verifyEmail,
}
class Auth {
static final Auth _instance = Auth();
static Auth get instance => _instance;
UserObject? user;
String getCurUserId() {
return FirebaseAuth.instance.currentUser!.uid;
}
Future<void> login(String email, String password) async {
await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
}
Future<void> logOut() async {
await FirebaseAuth.instance.signOut();
}
Future<void> register(String email, String password) async {
final credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.sendEmailVerification();
}
Future<void> sendEmailVerEmailCurUser() async {
await FirebaseAuth.instance.currentUser?.sendEmailVerification();
}
Future<void> sendPasswordResetEmail(String email) async {
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
}
Stream<AuthState> stateChanges() async* {
await for (final user in FirebaseAuth.instance.authStateChanges()) {
if (user == null) {
//this.user = null;
yield AuthState.loggedOut;
} else {
/* To Do: implement runtime detection of email verification */
if (!user.emailVerified) {
user.reload();
}
if (user.emailVerified) {
/*try {
this.user = await UserObject.loadData(user.uid);
} catch (e) {
print(e.toString());
}*/
yield AuthState.loggedIn;
} else {
yield AuthState.verifyEmail;
}
}
}
}
}
remove the onGeneratedRuote; Not just it can be called during rebuild
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<AuthState>(
stream: Auth.instance.stateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
assert(snapshot.connectionState != ConnectionState.done);
if (snapshot.data == AuthState.loggedIn) {
return const MainWidget()
}
if (snapshot.data == AuthState.loggedOut) {
return const LoginWidget();
}
if (snapshot.data == AuthState.verifyEmail) {
const EmailNotVerWidget();;
}
throw Exception('Unknown Auth State');
})
// })
);
}
I have a loading spinner that I would like to turn off if the user cancels the purchase using RevenueCat inside of flutter.
I have a try catch block that I use to make the purchase but the error block is never initiated because the try block is always successful.
Here is the code:
purchase(BuildContext ctx, PaymentPlanType selectedPlanType) async {
final offerings = await PurchaseApi.fetchOffers();
try {
if (selectedPlanType == PaymentPlanType.weekly) {
await PurchaseApi.purchasePackage(offerings[0].availablePackages[0]);
} else if (selectedPlanType == PaymentPlanType.monthly) {
await PurchaseApi.purchasePackage(offerings[0].availablePackages[1]);
} else if (selectedPlanType == PaymentPlanType.yearly) {
await PurchaseApi.purchasePackage(offerings[0].availablePackages[2]);
}
} on PlatformException catch (e) {
var errorCode = PurchasesErrorHelper.getErrorCode(e);
if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
showDialog(
context: ctx,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(e.toString()),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'))
],
);
});
}
}
}
How do I just catch that error so I can change an isLoading bool to false?
Currently, I have enabled 'keep logging in' function if the user log in once successfully. However, I still want to make a 'remember me' checkbox to save the success login information for user. Can anyone please help me with this?
Need: a checkbox that enables the user to store email and password if the user logged in once successfully.
Code is shown below:
signIn(String email, pass) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String url = ServerDetails.ip +
':' +
ServerDetails.port +
ServerDetails.api +
'login';
Map<String, String> headers = {"Content-type": "application/json"};
var data = jsonEncode({
'email': email,
'password': pass,
'token': FirebaseNotifications.fcmtoken
});
var jsonResponse = null;
var response = await http.post(url, headers: headers, body: data);
if (response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['token']);
sharedPreferences.setString(
"token_expire_date", jsonResponse['token_expire_date']);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => MainPage()),
(Route<dynamic> route) => false);
}
} else {
setState(() {
_isLoading = false;
});
Widget okButton = FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => MainPage()));
});
setState(() {
AlertDialog alert = AlertDialog(
title: Text("Error message"),
content: Text("Oops! The password is wrong or the email is invalid."),
actions: [
okButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
});
print(response.headers);
print(response.body);
}
}
Of course, you can create a simple checkbox for remember me. In the login button, you can check if this checkbox is checked. If it is, you can set email & password in shared_preferences.
Next time, when the user comes again you can get these fields automatically from shared_preferences.
Here is an example.
Here, I'm authenticated through the phone number with OTP code using firebase but
after login succeeded, it navigated through home page but when I click on back
it drags me login Screen.
here, the code I have tried, but it doesn't work
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
this.setState(() {
isLoading = true;
});
firebaseAuth.currentUser().then((user){
if(user !=null){
Navigator.of(context).pushReplacementNamed('/homepage');
}else{
verifyPhone();
}
});
this.setState(() {
isLoading = false;
});
}
Method for getting OTP code
Future<void> verifyPhone()async{
final PhoneCodeAutoRetrievalTimeout autoRetrieval=(String verId){
this.verificationId=verId;
};
final PhoneCodeSent smsCodeSent=(String verId, [int forceCodeResend]){
this.verificationId=verId;
smsCodeDialog(context).then((value){
print("Signed in");
});
};
final PhoneVerificationCompleted verificationCompleted = (AuthCredential credential) {
print("verified");
};
final PhoneVerificationFailed verfifailed=(AuthException exception){
print("${exception.message}");
};
await firebaseAuth.verifyPhoneNumber(
phoneNumber: this.phoneNo,
codeAutoRetrievalTimeout: autoRetrieval,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 10),
verificationCompleted: verificationCompleted,
verificationFailed: verfifailed
);
}
here the dialog box for sign in with OTP code
Future<bool> smsCodeDialog(BuildContext context){
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextField(
onChanged: (value){
this.smsCode=value;
},
),
contentPadding: const EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text("Done"),
onPressed: (){
firebaseAuth.currentUser().then((user){
if(user !=null){
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/homepage');
}else{
Navigator.of(context).pop();
signIn();
}
});
},
)
],
);
}
);
}
method for Sign in with phone number
signIn()async{
AuthCredential credential= PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode
);
await firebaseAuth.signInWithCredential(credential).then((user){
Navigator.of(context).pushReplacementNamed('/homepage');
print('signed in with phone number successful: user -> $user');
}).catchError((onError){
print(onError);
});
}
`
Welcome Shruti Ramnandan Sharma in Stackoverflow and Flutter dev.
Your code seems to working fine with me, I coded for you a one page dart that can test you the whole code with fixing your problem with going back to Login or VerifyPhone page.
Note: I changed your order of code in verifyPhone() method.
And Changed Navigator.of(context).pushReplacementNamed('/homepage'); to
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => HomeRoute()));
The whole code here
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
void main() => runApp(VerifyPhoneRoute());
class VerifyPhoneRoute extends StatefulWidget {
#override
_VerifyPhoneRouteState createState() {
return _VerifyPhoneRouteState();
}
}
class _VerifyPhoneRouteState extends State<VerifyPhoneRoute> {
bool isLoading = false;
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
String verificationId;
String phoneNo = "Your number here";
String smsCode;
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
this.setState(() {
isLoading = true;
});
firebaseAuth.currentUser().then((user) {
if (user != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
} else {
verifyPhone();
}
});
this.setState(() {
isLoading = false;
});
}
Future<void> verifyPhone() async {
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential credential) {
print("verified");
};
final PhoneVerificationFailed verifyFailed = (AuthException exception) {
print("${exception.message}");
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
smsCodeDialog(context).then((value) {
print("Signed in");
});
};
final PhoneCodeAutoRetrievalTimeout autoRetrieval = (String verId) {
this.verificationId = verId;
};
await firebaseAuth.verifyPhoneNumber(
phoneNumber: this.phoneNo,
codeAutoRetrievalTimeout: autoRetrieval,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 10),
verificationCompleted: verificationCompleted,
verificationFailed: verifyFailed);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Inapp Plugin by dooboolab'),
),
body: Center(
child: RaisedButton(
child: Text("Verify"),
onPressed: () {
verifyPhone();
}),
),
),
);
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextField(
onChanged: (value) {
this.smsCode = value;
},
),
contentPadding: const EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text("Done"),
onPressed: () {
firebaseAuth.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
} else {
Navigator.of(context).pop();
signIn();
}
});
},
)
],
);
});
}
signIn() async {
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId, smsCode: smsCode);
await firebaseAuth.signInWithCredential(credential).then((user) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
print('signed in with phone number successful: user -> $user');
}).catchError((onError) {
print(onError);
});
}
}
class HomeRoute extends StatefulWidget {
#override
_HomeRouteState createState() {
return _HomeRouteState();
}
}
class _HomeRouteState extends State<HomeRoute> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Inapp Plugin by dooboolab'),
),
body: Center(
child: Text("Welcome There."),
),
),
);
}
}
This code works fine with me. So if there's any problem happened with you again, don't be hesitate to comment on this answer. And if this answered your question and solve your problem, please make it as answer.
Use method below by pass verificationID come from API firebase when code sent and code enter by user, so if method return FirebaseUser the code is correct if return null the code enter by user is not correct
Future<FirebaseUser> getUserFromCodePhone(String code, String verificationID) async {
FirebaseAuth mAuth = FirebaseAuth.instance;
AuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(
verificationId: verificationID, smsCode: code);
try {
AuthResult result = await mAuth.signInWithCredential(phoneAuthCredential);
FirebaseUser currentUser = await mAuth.currentUser();
if (currentUser != null && result.user.uid == currentUser.uid) {
return currentUser;
} else {
return null;
}
} on PlatformException catch (_) {}
return null;
}
How it work ? : when use signInWithCredential method if code passed to
AuthCredential is false then the method will throw PlatformException
so out from try block and return null