update api

This commit is contained in:
Phuoc Nguyen
2025-10-10 17:15:40 +07:00
parent b94c158004
commit 04f7042b8d
24 changed files with 3322 additions and 8 deletions

View File

@@ -0,0 +1,234 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/auth_provider.dart';
/// Register page for new user registration
class RegisterPage extends ConsumerStatefulWidget {
const RegisterPage({super.key});
@override
ConsumerState<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends ConsumerState<RegisterPage> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _obscurePassword = true;
bool _obscureConfirmPassword = true;
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
Future<void> _handleRegister() async {
if (!_formKey.currentState!.validate()) return;
final success = await ref.read(authProvider.notifier).register(
name: _nameController.text.trim(),
email: _emailController.text.trim(),
password: _passwordController.text,
);
if (!mounted) return;
if (success) {
// Navigate to home or show success
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Registration successful!')),
);
// TODO: Navigate to home page
} else {
final errorMessage = ref.read(authProvider).errorMessage;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMessage ?? 'Registration failed'),
backgroundColor: Colors.red,
),
);
}
}
@override
Widget build(BuildContext context) {
final authState = ref.watch(authProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Register'),
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
// Logo or app name
Icon(
Icons.shopping_cart,
size: 80,
color: Theme.of(context).primaryColor,
),
const SizedBox(height: 16),
Text(
'Create Account',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 48),
// Name field
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Full Name',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your name';
}
if (value.length < 2) {
return 'Name must be at least 2 characters';
}
return null;
},
),
const SizedBox(height: 16),
// Email field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
if (!value.contains('@')) {
return 'Please enter a valid email';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: const Icon(Icons.lock),
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
// Check for uppercase, lowercase, and number
if (!RegExp(r'(?=.*[a-z])(?=.*[A-Z])(?=.*\d)').hasMatch(value)) {
return 'Password must contain uppercase, lowercase, and number';
}
return null;
},
),
const SizedBox(height: 16),
// Confirm password field
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirmPassword,
decoration: InputDecoration(
labelText: 'Confirm Password',
prefixIcon: const Icon(Icons.lock_outline),
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
_obscureConfirmPassword = !_obscureConfirmPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please confirm your password';
}
if (value != _passwordController.text) {
return 'Passwords do not match';
}
return null;
},
),
const SizedBox(height: 24),
// Register button
FilledButton(
onPressed: authState.isLoading ? null : _handleRegister,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: authState.isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Text('Register'),
),
),
const SizedBox(height: 16),
// Login link
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Already have an account? Login'),
),
],
),
),
),
),
);
}
}