NANDHOO.

Building Authentication Screens

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.emailAddress for email fields to show the @ symbol on the keyboard.
  • Obscure Text: Always use obscureText: true for 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!