Building Authentication Screens
Logic is important, but users need a way to interact with that logic. In this chapter, we'll build the Login and Sign-up screens using Flutter's form widgets. We'll also implement validation to ensure users enter valid emails and strong passwords before we even try to talk to Firebase.
Why This Topic Matters
The "Login" and "Sign-up" screens are often the first thing a user sees. A clunky or confusing UI here can drive users away. Learning how to use TextFormField, Form, and GlobalKey allows you to build professional-grade input forms with real-time feedback. This is a skill you'll use in almost every app you build.
How To Study This Chapter
Forms in Flutter use a GlobalKey<FormState> to manage the state of all input fields at once. Pay attention to how the validator function works—it returns a String if there's an error, and null if the input is valid. Also, notice how we use TextEditingController to get the values out of the text fields.
The Login UI
Here's a basic structure for a Login screen.
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
void _submit() {
if (_formKey.currentState!.validate()) {
// Form is valid, proceed with login logic from Chapter 6
print('Logging in with ${_emailController.text}');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty || !value.contains('@')) {
return 'Please enter a valid email';
}
return null;
},
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true, // Hide password
validator: (value) {
if (value == null || value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _submit,
child: Text('Login'),
),
],
),
),
),
);
}
}
Form Validation Tips
- User Feedback: Always provide clear error messages.
- Input Type: Use
keyboardType: TextInputType.emailAddressfor email fields to show the@symbol on the keyboard. - Obscure Text: Always use
obscureText: truefor passwords. - Trim: It's often a good idea to
.trim()the email input to remove accidental spaces.
Navigating Between Auth Screens
You'll usually want a link on the Login screen that says "Don't have an account? Sign up".
TextButton(
onPressed: () {
Navigator.pushReplacementNamed(context, '/signup');
},
child: Text('Don\'t have an account? Sign up'),
)
In the next chapter, we'll learn how to automatically navigate the user to the home screen as soon as they log in!