Sign Up
Loading preview
Starting the Flutter app
Registration with password strength indicator
import 'package:flutter/material.dart';
final _emailPattern = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$",
);
class SignUpPage extends StatefulWidget {
/// Called with the trimmed email and the password once every field
/// validates. Without it the demo shows a SnackBar.
final void Function(String email, String password)? onSubmit;
const SignUpPage({super.key, this.onSubmit});
@override
State<SignUpPage> createState() => _SignUpPageState();
}
class _SignUpPageState extends State<SignUpPage> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
var _obscurePassword = true;
int _passwordStrength(String password) {
var score = 0;
if (password.length >= 8) score++;
if (RegExp(r'[A-Z]').hasMatch(password)) score++;
if (RegExp(r'[0-9]').hasMatch(password)) score++;
if (RegExp(r'[^A-Za-z0-9]').hasMatch(password)) score++;
return score;
}
void _submit() {
if (!(_formKey.currentState?.validate() ?? false)) return;
final onSubmit = widget.onSubmit;
if (onSubmit != null) {
onSubmit(_emailController.text.trim(), _passwordController.text);
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Details look good')),
);
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sign Up')),
body: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(24),
children: [
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (v) {
if (v == null || v.trim().isEmpty) return 'Required';
if (!_emailPattern.hasMatch(v.trim())) {
return 'Enter a valid email';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
tooltip: _obscurePassword ? 'Show password' : 'Hide password',
icon: Icon(
_obscurePassword ? Icons.visibility : Icons.visibility_off,
),
onPressed: () =>
setState(() => _obscurePassword = !_obscurePassword),
),
),
obscureText: _obscurePassword,
validator: (v) {
if (v == null || v.isEmpty) return 'Required';
if (v.length < 8) return 'Min 8 characters';
return null;
},
),
const SizedBox(height: 8),
ValueListenableBuilder<TextEditingValue>(
valueListenable: _passwordController,
builder: (context, value, child) {
final strength = _passwordStrength(value.text);
return LinearProgressIndicator(
value: strength / 4,
backgroundColor: Colors.grey[300],
);
},
),
const SizedBox(height: 16),
TextFormField(
decoration: const InputDecoration(
labelText: 'Confirm Password',
border: OutlineInputBorder(),
),
obscureText: true,
validator: (v) {
if (v == null || v.isEmpty) return 'Required';
if (v != _passwordController.text) {
return 'Passwords do not match';
}
return null;
},
),
const SizedBox(height: 24),
FilledButton(
onPressed: _submit,
child: const Text('Create Account'),
),
],
),
),
);
}
}
Add it to your project
Copies the Dart source into your app, so you own it. Flutter SDK only, no extra packages.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
_passwordStrength awards a point each for 8+ characters, an uppercase letter, a digit and a symbol, and a ValueListenableBuilder on _passwordController shows strength / 4 as a bar. Email must match _emailPattern, and Confirm Password must equal _passwordController.text. _submit passes the trimmed email and password to onSubmit once validate() passes, or shows a SnackBar without it.
Adapting it
- Call your registration API from
onSubmit, and disable the button while it runs. - The bar is informational: any 8 characters pass, so check
_passwordStrengthin the validator to enforce a policy. _emailPatternonly checks format, so confirm ownership with a verification email.