Creating a polished login interface is one of the first milestones in any mobile app project. In this step‑by‑step guide you will learn how to build a fully functional email + password login form in Dart and Flutter, complete with client‑side validation and beautiful Material styling.
1. Set up a new Flutter project
flutter create login_form_demo
flutter create login_form_demo
cd login_form_demo
flutter create login_form_demo
cd login_form_demo
2. Add the form widget
Inside lib/login_page.dart
, create a StatefulWidget
so you can validate and save the form state.
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
State<LoginPage> createState() => _LoginPageState();
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final _emailCtl = TextEditingController();
final _pwdCtl = TextEditingController();
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Login')),
padding: const EdgeInsets.all(16),
decoration: const InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
value!.contains('@') ? null : 'Enter a valid email',
const SizedBox(height: 16),
decoration: const InputDecoration(labelText: 'Password'),
value!.length >= 6 ? null : 'Minimum 6 characters',
const SizedBox(height: 24),
if (_formKey.currentState!.validate()) {
// TODO: handle submission (e.g., call an API)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Logging in…')),
child: const Text('Login'),
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final _emailCtl = TextEditingController();
final _pwdCtl = TextEditingController();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailCtl,
decoration: const InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: (value) =>
value!.contains('@') ? null : 'Enter a valid email',
),
const SizedBox(height: 16),
TextFormField(
controller: _pwdCtl,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) =>
value!.length >= 6 ? null : 'Minimum 6 characters',
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// TODO: handle submission (e.g., call an API)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Logging in…')),
);
}
},
child: const Text('Login'),
),
],
),
),
),
);
}
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final _emailCtl = TextEditingController();
final _pwdCtl = TextEditingController();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailCtl,
decoration: const InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: (value) =>
value!.contains('@') ? null : 'Enter a valid email',
),
const SizedBox(height: 16),
TextFormField(
controller: _pwdCtl,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) =>
value!.length >= 6 ? null : 'Minimum 6 characters',
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// TODO: handle submission (e.g., call an API)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Logging in…')),
);
}
},
child: const Text('Login'),
),
],
),
),
),
);
}
3. Wire it up in main.dart
import 'package:flutter/material.dart';
import 'login_page.dart';
void main() => runApp(const MyApp());
class MyApp extends <a href="https://aliendro.id/create-ui-with-statelesswidget-in-dart/">StatelessWidget</a> {
const MyApp({super.key});
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
import 'package:flutter/material.dart';
import 'login_page.dart';
void main() => runApp(const MyApp());
class MyApp extends <a href="https://aliendro.id/create-ui-with-statelesswidget-in-dart/">StatelessWidget</a> {
const MyApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Login Demo',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: const LoginPage(),
);
}
import 'package:flutter/material.dart';
import 'login_page.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Login Demo',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: const LoginPage(),
);
}
4. Run and test
With Android Studio or VS Code, press Run (or flutter run
in the terminal). Try invalid inputs to see the real‑time validation in action.
Need more advanced validation? Check Flutter’s official cookbook on form validation: https://docs.flutter.dev/cookbook/forms/validation
By following these steps you now have a clean, reusable login screen that can be expanded with networking logic, password visibility toggles, social‑auth buttons, and more. This foundation will speed up development on future projects while ensuring a consistent user experience.
Post Views: 45