File-Drive-App-Frontend/lib/screens/register_screen.dart

169 lines
6.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../controllers/session_controller.dart';
import '../services/api_client.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({super.key});
@override
State<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
final _form = GlobalKey<FormState>();
final _name = TextEditingController();
final _username = TextEditingController();
final _email = TextEditingController();
final _phone = TextEditingController();
final _password = TextEditingController();
bool _busy = false;
@override
void dispose() {
for (final value in [_name, _username, _email, _phone, _password]) {
value.dispose();
}
super.dispose();
}
Future<void> _submit() async {
if (!_form.currentState!.validate()) return;
setState(() => _busy = true);
try {
await context.read<SessionController>().register({
'name': _name.text.trim(),
'username': _username.text.trim().toLowerCase(),
'email': _email.text.trim().toLowerCase(),
'phone': _phone.text.trim().isEmpty
? 'wd${DateTime.now().microsecondsSinceEpoch}'
: _phone.text.trim(),
'age': 18,
'gender': 'Prefer not to say',
'password': _password.text,
});
if (mounted) Navigator.of(context).popUntil((route) => route.isFirst);
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.read<ApiClient>().messageFrom(error))),
);
}
} finally {
if (mounted) setState(() => _busy = false);
}
}
String? _required(String? value) =>
(value?.trim().isEmpty ?? true) ? 'Required' : null;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Join the digital family')),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: Form(
key: _form,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Create your private workspace',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 8),
const Text(
'We create your personal Google Drive folder automatically.',
),
const SizedBox(height: 24),
TextFormField(
controller: _name,
textCapitalization: TextCapitalization.words,
decoration: const InputDecoration(labelText: 'Full name'),
validator: _required,
),
const SizedBox(height: 12),
TextFormField(
controller: _username,
autocorrect: false,
decoration: const InputDecoration(
labelText: 'Username',
helperText:
'Letters, numbers, dot, dash, or underscore',
),
validator: (value) =>
RegExp(
r'^[a-zA-Z0-9._-]{3,30}$',
).hasMatch(value?.trim() ?? '')
? null
: 'Enter a valid username',
),
const SizedBox(height: 12),
TextFormField(
controller: _email,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: 'Email address',
),
validator: (value) =>
RegExp(r'^.+@.+\..+$').hasMatch(value?.trim() ?? '')
? null
: 'Enter a valid email',
),
const SizedBox(height: 12),
TextFormField(
controller: _phone,
keyboardType: TextInputType.phone,
decoration: const InputDecoration(
labelText: 'Phone number (optional)',
),
validator: (value) {
final phone = value?.trim() ?? '';
return phone.isEmpty || phone.length >= 7
? null
: 'Enter at least 7 characters or leave it blank';
},
),
const SizedBox(height: 12),
TextFormField(
controller: _password,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
helperText: 'At least 8 characters',
),
validator: (value) => (value?.length ?? 0) >= 8
? null
: 'Use at least 8 characters',
),
const SizedBox(height: 24),
FilledButton(
onPressed: _busy ? null : _submit,
child: _busy
? const SizedBox.square(
dimension: 22,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text('Create Metatron-Drive'),
),
],
),
),
),
),
),
),
);
}
}