visit
Welcome to another exciting blog, this is the 8th part of the series Flutter App Development Tutorial. So, far we made Splash Screen, some global widgets like the app bar and bottom nav bar, and also implemented a global theme. In the last two sections, we created UI and wrote the backend for registration and sign-in methods to use with the Firebase project. In this section, we'll use flutters' features to give users visual feedback.
Head over to the auth_state_provider class and make some changes.
Create an enum outside of class, to toggle the Application Process State State.
// Outside of any class or function
// Make an enum to togggle progrss indicator
enum ProcessingState {
done,
waiting,
}
Inside of Provider class let's create a field of type ProcessingState and a function that switches these values/states.
ProcessingState _processingState = ProcessingState.done;
// getter
ProcessingState get processingState => _processingState;
void setPrcState(ProcessingState prcsState) {
_processingState = prcsState;
notifyListeners();
}
Update Processing State In Register Function
// Our Function will take email,password, username and buildcontext
void register(String email, String password, String username,
BuildContext context) async {
// Start loading progress indicator once submit button is hit
// #1
setPrcState(ProcessingState.waiting);
try {
// Get back usercredential future from createUserWithEmailAndPassword method
UserCredential userCred = await authInstance
.createUserWithEmailAndPassword(email: email, password: password);
// Save username name
await userCred.user!.updateDisplayName(username);
// After that access "users" Firestore in firestore and save username, email and userLocation
await FirebaseFirestore.instance
.collection('users')
.doc(userCred.user!.uid)
.set(
{
'username': username,
'email': email,
'userLocation': null,
},
);
// if everything goes well user will be registered and logged in
// now go to the homepage
GoRouter.of(context).goNamed(APP_PAGE.home.routeName);
} on FirebaseAuthException catch (e) {
// In case of error
// if email already exists
if (e.code == "email-already-in-use") {
print("The account with this email already exists.");
}
if (e.code == 'weak-password') {
// If password is too weak
print("Password is too weak.");
}
// #2
setPrcState(ProcessingState.done);
} catch (e) {
// For anything else
print("Something went wrong please try again.");
// #3
setPrcState(ProcessingState.done);
}
// notify listeneres
notifyListeners();
}
Now, we'll need to tell the application, that when the processing state is in waiting, display a progress indicator, and then remove the indicator once the processing state is done. To do so let's head over to auth_form_widget. Right after we instantiate an AuthStateProvider, create a new variable of the type ProcessState whose value is equal to that of AuthStateProvider's process state.
Widget build(BuildContext context) {
final AuthStateProvider authStateProvider =
Provider.of<AuthStateProvider>(context);
// make new ProcessState var
ProcessingState prcState = authStateProvider.processingState;
if (prcState == ProcessingState.waiting) const CircularProgressIndicator(),
if (prcState == ProcessingState.done)
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('Cancel'),
),
const SizedBox(
width: 20,
),
ElevatedButton(
onPressed: () {
// call _submitForm on tap
_submitForm(authStateProvider, context);
},
child: Text(registerAuthMode ? 'Register' : 'Sign In'),
style: ButtonStyle(
elevation: MaterialStateProperty.all(8.0),
),
),
],
),
// Right after setPrcState function
// create function to handle popups
SnackBar msgPopUp(msg) {
return SnackBar(
content: Text(
msg,
textAlign: TextAlign.center,
));
}
This function will take a custom message and return a SnackBar to display on the screen. Snackbar works in conjunction with class. So, we'll pass this msgPopUp method in at the end of the try block operations, and just before we call GoRouter.
Inside Registration Function
ScaffoldMessenger.of(context)
.showSnackBar(msgPopUp("The account has been registered."));
// Before GoRouter
GoRouter.of(context).goNamed(APP_PAGE.home.routeName);
Inside Login Function
ScaffoldMessenger.of(context).showSnackBar(msgPopUp("Welcome Back"));
With this when the authentication operation succeeds user will see a snack bar at the bottom of the application.
// #1
AlertDialog errorDialog(BuildContext context, String errMsg) {
return AlertDialog(
title: Text("Error",
style: TextStyle(
//text color will be red
// #2
color: Theme.of(context).colorScheme.error,
)),
content: Text(errMsg,
style: TextStyle(
//text color will be red
// #3
color: Theme.of(context).colorScheme.error,
)),
actions: [
TextButton(
// On button click remove the dialog box
// #2
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
);
}
Inside Register Function's Catch Blocks
on FirebaseAuthException catch (e) {
// In case of error
// if email already exists
if (e.code == "email-already-in-use") {
showDialog(
context: context,
builder: (context) => errorDialog(
context, "The account with this email already exists."));
}
if (e.code == 'weak-password') {
// If password is too weak
showDialog(
context: context,
builder: (context) =>
errorDialog(context, "Password is too weak."));
}
setPrcState(ProcessingState.done);
} catch (e) {
// For anything else
showDialog(
context: context,
builder: (context) =>
errorDialog(context, "Something went wrong please try again."));
setPrcState(ProcessingState.done);
}
Please, add these alerts to your sign-in method by yourself.
Was this an informative blog? If so, hit the like button, ask us some questions and share this with your colleagues. The next part is going to be on App Permissions and Google Places API. So, subscribe and follow us to get notifications.