dramaling-app/apps/mobile/lib/features/auth/screens/register_screen.dart

311 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({super.key});
@override
State<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
final _displayNameController = TextEditingController();
bool _isLoading = false;
bool _obscurePassword = true;
bool _obscureConfirmPassword = true;
bool _acceptTerms = false;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
_displayNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => context.go('/login'),
),
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Title
Text(
'建立新帳號',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'開始您的語言學習之旅',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
// Display Name Field
TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
labelText: '顯示名稱',
hintText: '請輸入您的顯示名稱',
prefixIcon: Icon(Icons.person_outlined),
),
validator: (value) {
if (value == null || value.isEmpty) {
return '請輸入顯示名稱';
}
if (value.length < 2) {
return '顯示名稱至少需要2個字符';
}
return null;
},
),
const SizedBox(height: 16),
// Email Field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: '電子郵件',
hintText: '請輸入您的電子郵件',
prefixIcon: Icon(Icons.email_outlined),
),
validator: (value) {
if (value == null || value.isEmpty) {
return '請輸入電子郵件';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return '請輸入有效的電子郵件格式';
}
return null;
},
),
const SizedBox(height: 16),
// Password Field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
decoration: InputDecoration(
labelText: '密碼',
hintText: '請輸入密碼(至少8個字符)',
prefixIcon: const Icon(Icons.lock_outlined),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return '請輸入密碼';
}
if (value.length < 8) {
return '密碼長度至少需要8個字符';
}
if (!RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)').hasMatch(value)) {
return '密碼需包含大寫字母、小寫字母和數字';
}
return null;
},
),
const SizedBox(height: 16),
// Confirm Password Field
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirmPassword,
decoration: InputDecoration(
labelText: '確認密碼',
hintText: '請再次輸入密碼',
prefixIcon: const Icon(Icons.lock_outlined),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
),
onPressed: () {
setState(() {
_obscureConfirmPassword = !_obscureConfirmPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return '請確認密碼';
}
if (value != _passwordController.text) {
return '密碼不一致';
}
return null;
},
),
const SizedBox(height: 24),
// Terms and Conditions
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
value: _acceptTerms,
onChanged: (value) {
setState(() {
_acceptTerms = value ?? false;
});
},
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_acceptTerms = !_acceptTerms;
});
},
child: Padding(
padding: const EdgeInsets.only(top: 12),
child: RichText(
text: TextSpan(
style: Theme.of(context).textTheme.bodyMedium,
children: [
const TextSpan(text: '我同意'),
TextSpan(
text: '服務條款',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
const TextSpan(text: ''),
TextSpan(
text: '隱私政策',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
],
),
),
),
),
),
],
),
const SizedBox(height: 24),
// Register Button
SizedBox(
height: 48,
child: ElevatedButton(
onPressed: (_isLoading || !_acceptTerms) ? null : _handleRegister,
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('註冊'),
),
),
const SizedBox(height: 16),
// Back to Login
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('已經有帳號了?'),
TextButton(
onPressed: () => context.go('/login'),
child: const Text('立即登入'),
),
],
),
],
),
),
),
),
);
}
Future<void> _handleRegister() async {
if (!_formKey.currentState!.validate()) return;
if (!_acceptTerms) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('請同意服務條款和隱私政策')),
);
return;
}
setState(() {
_isLoading = true;
});
try {
// TODO: 實現實際的註冊邏輯
await Future.delayed(const Duration(seconds: 2)); // 模擬API調用
if (mounted) {
// 註冊成功,導航到首頁
context.go('/home');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('註冊成功歡迎使用Drama Ling')),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('註冊失敗:$e')),
);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
}