Here is my code section in which i am signing in user using firebase, now i wanted to show a loadihng screen while we are fetching data from firebase.
Container(
width: 376,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
gradient: LinearGradient(
colors: <Color>[
Colors.black12,
Colors.blue,
Colors.black12,
]
)
),
child: TextButton(
onPressed: () async{
if(key.currentState!.validate()){
await FirebaseAuth.instance.signInWithEmailAndPassword(email: User_email_id.text, password: user_Passowrd.text)
.then((value) => {
Navigator.push(
context,
MaterialPageRoute(builder: (context)=>Page_First())
)
}).catchError((e){
Fluttertoast.showToast(msg: e!.message);
});
}
},
child: Text(
'Sign-in',
style: TextStyle(
fontSize: 17,
color: Colors.white,
),
),
),
),
Firstly, add boolean isLoading = false to the top of you class. Then move firebase loading code to a separate Future like this:
Future<void> loadFirebase(BuildContext context) async {
await FirebaseAuth.instance.signInWithEmailAndPassword(email: User_email_id.text, password: user_Passowrd.text)
.then((value) => {
Navigator.push(
context,
MaterialPageRoute(builder: (context)=>Page_First())
)
}).catchError((e){
Fluttertoast.showToast(msg: e!.message);
});
}
This function shall be called when the button is pressed. At the same time, isLoading shall be set to true. Finally, when building your widget, add a condition to show loading screen if isLoading is true.
isLoading == true ? LoadingContainer() : Container(content with button);
Related
I've created a login screen and once the email and password got verified user will be pushed to the dashboard. So what I need to do is, show a loading indicator only if there are no exceptions that get thrown otherwise it should show the alert which I've implemented with in a try-catch.
log-in button -
SizedBox(
width: MediaQuery.of(context).size.width,
child: TextButton(
onPressed: signIn,
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(
const EdgeInsets.fromLTRB(0, 20, 0, 20),
),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0))),
backgroundColor:
MaterialStateProperty.all(primaryColor),
),
child: const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontFamily: 'InterBold',
),
),
),
),
Validation class -
Future signIn() async {
if (_key.currentState!.validate()) {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordCrontroller.text.trim());
errorMessage = '';
} on FirebaseAuthException catch (e) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text(
'Error',
style: TextStyle(color: mainText),
),
content: Text(
e.message!,
style: const TextStyle(color: secondaryText),
),
contentPadding:
const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 0.0),
backgroundColor: Colors.white,
actions: [
TextButton(
onPressed: () {
navigatorKey.currentState!.pop();
},
child: const Text(
'Close',
style: TextStyle(color: primaryColor),
))
],
));
}
}
}
String? validateEmail(String? formEmail) {
String pattern = r'\w+#\w+\.\w+';
RegExp regex = RegExp(pattern);
if (formEmail == null || formEmail.isEmpty || !regex.hasMatch(formEmail)) {
return '';
}
return null;
}
String? validatePassword(String? formPassword) {
String pattern =
r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!##\$&*~]).{6,}$';
RegExp regex = RegExp(pattern);
if (formPassword == null ||
formPassword.isEmpty ||
!regex.hasMatch(formPassword)) {
return '';
}
return null;
}
}
Loading indicator -
showLoadingIndicatorDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(
child: CircularProgressIndicator(
color: primaryColor,
),
));
}
So what you can do is the following :
declare the bool value _isLoading =false;
you can use the setstate to change the state.
So what does the below code do:
Initially _isLoading will be false so it will show you the text widget.
when you hit the signin button it will make it true so the circular progress indicator will be appearing.
then when the api call is complete then resetting it back to false and that we can see the text widget.
And if there comes any error en the exception making the _isLoading to false so that it will be the text widget and there will be dialog shown with error message.
I have taken your code and just made the changes check the use of bool value and use it as per you state management needs, I have elaborated with a simple setState.
class App extends StatelessWidget {
bool _isLoading =false;
#override
Widget build(BuildContext context) {
// TODO: implement build
return SizedBox(
width: MediaQuery.of(context).size.width,
child: TextButton(
onPressed: signIn,
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(
const EdgeInsets.fromLTRB(0, 20, 0, 20),
),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0))),
// backgroundColor:
// MaterialStateProperty.all(primaryColor),
),
child:
_isLoading ?
const CircularProgressIndicator():
const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontFamily: 'InterBold',
),
),
),
);
}
Future signIn() async {
if (_key.currentState!.validate()) {
try {
setState({
_isLoading =true;
});
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordCrontroller.text.trim());
errorMessage = '';
setState({
_isLoading =false;
});
} on FirebaseAuthException catch (e) {
setState({
_isLoading =false;
});
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text(
'Error',
style: TextStyle(color: mainText),
),
content: Text(
e.message!,
style: const TextStyle(color: secondaryText),
),
contentPadding:
const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 0.0),
backgroundColor: Colors.white,
actions: [
TextButton(
onPressed: () {
navigatorKey.currentState!.pop();
},
child: const Text(
'Close',
style: TextStyle(color: primaryColor),
))
],
));
}
}
}
}
Let me know if it works
In my app, I have a login page connected to Firebase. I can successfully log in but when logging in, I want to display a CircularProgressIndicator until login is a success.
void signIn(String email, String password) async {
if (_formKey.currentState!.validate()) {
await _auth
.signInWithEmailAndPassword(email: email, password: password)
.then((_userDoc) => {
checkUserType(_userDoc.user!.uid),
})
.catchError((e) {
print('Error');
);
});
}
}
create a isLoading variable, set its state to true at the start of the sign-in, and false after the promise has been fulfilled.
then show the CircularProgressIndicator while isLoading = true
This will replace login button with CircularProgressIndicator while loading.
void signIn() async {
setState(() {
isLoading = true;
});
Future.delayed(Duration(seconds: 1)).then((value) {
/// loginOperationhere
//end of login set false, also include on catch method
setState(() {
isLoading = false;
});
});
}
bool isLoading = false; // on state class level
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () {
signIn();
},
child: Text("login"),
)
],
));
}
}
Try below code hope its helpful to you.
Create bool variable
bool _isLoading = false;
Your Widget:
Center(
child: !_isLoading
? Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 15.0),
margin: EdgeInsets.only(top: 15.0),
// ignore: deprecated_member_use
child: ElevatedButton(
child: Text(
'Sign In',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
textAlign: TextAlign.center,
),
onPressed: () {
// Your Login function call
setState(() {
_isLoading = true;
});
},
),
)
: CircularProgressIndicator(),
),
Your Widget using Center:
!_isLoading
? Center(
child: Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 15.0),
margin: EdgeInsets.only(top: 15.0),
// ignore: deprecated_member_use
child: ElevatedButton(
child: Text(
'Sign In',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
textAlign: TextAlign.center,
),
onPressed: () {
// Your Login function call
setState(() {
_isLoading = true;
});
},
),
),
)
: Center(
child: CircularProgressIndicator(),
),
Your result screen before pressed on button ->
Your result screen after button pressed->
Hy Guys I have connected flutter to my firebase project and used AUTH feature in Firebase but what I would do is to show an error message when a wrong password or Email user is wrong.
this is my code:
Widget submitButton (){
return Container(
child: SizedBox(
width: 220,
height: 40,
child: MaterialButton(
elevation: 5,
onPressed: () async {
setState(() {
showProgress = true;
});
try {
final newUser = await _auth.signInWithEmailAndPassword(email: email, password: password);
print(newUser.toString());
if (newUser != null) {
Navigator.push(context, PageTransition(
type: PageTransitionType.fade,
child: WelcomeScreen(),
));
setState(() {
showProgress = false;
});
}
} catch (e) {}
},
child: Text('Accedi',style: TextStyle(color: Colors.black),),
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.black12,width: 1)),
),
),
);
}
First you need to catch the error, you can do that by using catchError():
final newUser = await _auth.signInWithEmailAndPassword(email: email, password: password)
.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();
},
)
],
);
});
});
Then the showDialog will have err.message which will contain the error received from Firebase Authentication.
You have two options:
1.Use an alert dialog
void _showAlertDialog(String message) async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
full example
Use errorText property in InputDecoration
TextField(
decoration: InputDecoration(
errorText: this.errorText,
),
)
full example
You can use a Form() with TextFormField()
Something like this:
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
key: ValueKey('email'),
validator: (value) {
if (value.isEmpty || !value.contains('#'))
return 'Please enter a valid Email address';
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(labelText: 'Email address'),
onSaved: (value) {
_userEmail = value;
},
),
It's just a dummy, but as you see there's some validation code in the validator attribute.
So what validator does is, if you return null everything is well and good, but if something is wrong, just return the String and it'll be shown below the text form field as an error message.
And to validate you can call a method like this to activate the method while form submission
void _trySubmit() {
final bool isValid = _formKey.currentState.validate();
FocusScope.of(context).unfocus();
if (isValid) {
_formKey.currentState.save();
widget.submitFn(
_userEmail.trim(),
_userName.trim(),
_userPassword.trim(),
_isLogin,
context,
);
}}
The method for getting isValid will invoke validation function of all TextFormFields, if all of them are okay, it'll return true else it'll return false.
All the _name are used to store the values in state variables using the onSaved attribute.
Let me know if you have anymore doubts.
Hope this helpsā
Very new to Flutter. How would I push a new route predicated on a condition invoked by a button press? (asynchronous sign in call).
This sort of thing: if signIn (async) = success -> Navigator.push
Cheers
Widget buildButtons() {
return new Container(
child: new Column(
children: <Widget>[
new MaterialButton(
minWidth: MediaQuery.of(context).size.width - 30, //FULL WIDTH - 30
color: Style.palette3,
padding: EdgeInsets.all(Style.padding1),
child: new Text('Sign in',
style: Style.signInBtn
),
onPressed: () {
if (LoginControl.signIn()) Navigator.push(context, MaterialPageRoute(builder: (context) => ArmDisarm()));
},
),
new FlatButton(
child: Padding(
padding: Style.paddingCreateAcc,
child: new Text(
'Create an account',
style: Style.fontSize1
),
),
),
],
),
);
}
It's better to show us the code of LoginControl.signIn().
Anyway if signIn() is a Future, then you to use async await
...
onPressed: () async {
if (await LoginControl.signIn()) {
Navigator.push(context, MaterialPageRoute(builder: (context) => ArmDisarm()));
}
},
...
Make sure that signIn() method returns true if the user is successfully signed in.
I want to build a Flutter login screen working with Firebase Auth. I posted the Code below. When i run the App, the _switchToHomePage() method is called endless. So the app keeps opening an new HomeScreen until I close the app.
Thanks for help!
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'home_page.dart';
import 'register.dart';
import 'error_dialog.dart';
class LoginForm extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
bool _hidePassword = true;
void _checkUserIsLoggedIn() {
_firebaseAuth.currentUser().then((firebaseUser) {
if (firebaseUser == null) {
print('no user logged in');
} else {
print('User logged in');
_switchToHomePage();
}
});
}
void _switchToHomePage() {
print('switching to home page...');
Navigator.push(
context, MaterialPageRoute(builder: (context) => HomePage()));
this.deactivate();
}
void _login() {
String email = emailController.text;
String password = passwordController.text;
_firebaseAuth
.signInWithEmailAndPassword(email: email, password: password)
.then((firebaseUser) {
if (firebaseUser != null) {
print('Login succesfull');
_switchToHomePage();
} else {
throw Exception('FirebaseUser is null');
}
}).catchError((exception) {
//TODO make better error messages
print(exception.message);
ErrorDialog(
context: this.context,
title: 'Error',
message: exception.message);
});
}
void _showRegisterScreen() {
Navigator.push(
context, MaterialPageRoute(builder: (context) => RegisterForm()));
}
#override
Widget build(BuildContext context) {
_checkUserIsLoggedIn();
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: ListView(
padding: EdgeInsets.fromLTRB(15, 30, 15, 0),
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 60)),
Text(
'placeholder',
style: TextStyle(
color: Colors.teal,
fontSize: 40,
),
),
Padding(padding: EdgeInsets.only(top: 60)),
Padding(padding: EdgeInsets.only(top: 40)),
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email),
labelText: 'E-Mailadresse',
border: UnderlineInputBorder()),
),
Padding(padding: EdgeInsets.only(top: 20)),
TextField(
controller: passwordController,
obscureText: _hidePassword,
decoration: InputDecoration(
labelText: 'Passwort',
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.security),
suffixIcon: IconButton(
icon: Icon(Icons.remove_red_eye),
color: _hidePassword
? Theme.of(context).primaryColor
: Colors.grey,
onPressed: () => this.setState(() {
_hidePassword = !_hidePassword;
}),
)),
),
Padding(padding: EdgeInsets.only(top: 5)),
Container(
child: FlatButton(
onPressed: () => print('Forgot password'),
child: Text(
'Passwort vergessen?',
style: TextStyle(color: Theme.of(context).primaryColor),
),
),
width: double.infinity,
alignment: Alignment.centerRight,
),
Padding(padding: EdgeInsets.only(top: 40)),
RaisedButton(
onPressed: _login,
color: Theme.of(context).primaryColor,
child: Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
Padding(padding: EdgeInsets.only(top: 20)),
FlatButton(
onPressed: _showRegisterScreen,
child: Text(
'Neuen Account erstellen',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).accentColor,
),
),
)
],
));
}
}
The reason you get that, is because _checkUserIsLoggedIn(); which contains navigation to the HomePage is the first method inside build method. Inside the build() method you have setState() on the TextField, therefore everytime the state is changing then the build() method is getting called and _checkUserIsLoggedIn() is also getting called. To solve that try the following:
#override
initState() {
super.initState();
_firebaseAuth.currentUser().then((firebaseUser) {
if (firebaseUser == null) {
print('no user logged in');
} else {
print('User logged in');
_switchToHomePage();
}
});
}
Inside the lifecycle method initState() check if the user is logged in and navigate to the HomePage. Also remove the method _checkUserIsLoggedIn(); from the build method
I just solved it. I replaced:
Navigator.push(
context, MaterialPageRoute(builder: (context) => RegisterForm()));
with
Navigator.pushAndRemoveUntil(
context, MaterialPageRoute(builder: (context) => HomePage()), (e) => false);