140 lines
4.6 KiB
Dart
140 lines
4.6 KiB
Dart
import 'package:finnow_app/api/auth.dart';
|
|
import 'package:finnow_app/auth/model.dart';
|
|
import 'package:finnow_app/components/form_label.dart';
|
|
import 'package:finnow_app/components/title_text.dart';
|
|
import 'package:finnow_app/main.dart';
|
|
import 'package:finnow_app/util/debouncer.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
class RegisterPage extends StatelessWidget {
|
|
const RegisterPage({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Material(
|
|
child: Center(
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 300.0),
|
|
padding: const EdgeInsets.all(10),
|
|
child: const RegisterForm())));
|
|
}
|
|
}
|
|
|
|
class RegisterForm extends StatefulWidget {
|
|
const RegisterForm({super.key});
|
|
|
|
@override
|
|
State<RegisterForm> createState() => _RegisterFormState();
|
|
}
|
|
|
|
class _RegisterFormState extends State<RegisterForm> {
|
|
final formKey = GlobalKey<FormState>();
|
|
final usernameTextController = TextEditingController();
|
|
final passwordTextController = TextEditingController();
|
|
var loading = false;
|
|
final usernameAvailabilityDebouncer = Debouncer();
|
|
bool? usernameAvailable;
|
|
var canCreateAccount = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Form(
|
|
key: formKey,
|
|
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
const TitleText('Create a Finnow Account'),
|
|
const SizedBox(height: 20),
|
|
const FormLabel('Username'),
|
|
TextFormField(
|
|
controller: usernameTextController,
|
|
onChanged: (s) {
|
|
formValuesUpdated();
|
|
usernameAvailabilityDebouncer
|
|
.run(() => checkUsernameAvailability());
|
|
},
|
|
decoration: const InputDecoration(
|
|
hintText: 'Enter username', border: OutlineInputBorder()),
|
|
validator: usernameValidator,
|
|
),
|
|
if (usernameAvailable != null && usernameAvailable == true) ...[
|
|
const Text('Username is available.',
|
|
style: TextStyle(color: Colors.green))
|
|
],
|
|
if (usernameAvailable != null && usernameAvailable == false) ...[
|
|
const Text('Username is taken.',
|
|
style: TextStyle(color: Colors.red))
|
|
],
|
|
const SizedBox(
|
|
height: 10,
|
|
),
|
|
const FormLabel('Password'),
|
|
TextFormField(
|
|
controller: passwordTextController,
|
|
onChanged: (s) => formValuesUpdated(),
|
|
obscureText: true,
|
|
decoration: const InputDecoration(
|
|
hintText: 'Enter password', border: OutlineInputBorder()),
|
|
validator: passwordValidator),
|
|
const SizedBox(height: 10),
|
|
TextButton(
|
|
onPressed: loading || !canCreateAccount ? null : createAccount,
|
|
child: const Text('Create Account'),
|
|
),
|
|
const SizedBox(
|
|
height: 10,
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
getIt<GoRouter>().replace('/login');
|
|
},
|
|
child: const Text('Back to Login'))
|
|
]));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
usernameTextController.dispose();
|
|
passwordTextController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void formValuesUpdated() {
|
|
final usernameValidation = usernameValidator(usernameTextController.text);
|
|
final passwordValidation = passwordValidator(passwordTextController.text);
|
|
setState(() {
|
|
canCreateAccount =
|
|
usernameValidation == null && passwordValidation == null;
|
|
});
|
|
}
|
|
|
|
void checkUsernameAvailability() async {
|
|
final usernameText = usernameTextController.text;
|
|
// Set usernameAvailable to null if the username is invalid.
|
|
if (usernameValidator(usernameText) != null) {
|
|
setState(() => usernameAvailable = null);
|
|
return;
|
|
}
|
|
// Otherwise, there's a valid username, so check if it's available.
|
|
final available = await getUsernameAvailability(usernameText);
|
|
setState(() => usernameAvailable = available);
|
|
}
|
|
|
|
void createAccount() async {
|
|
print('Creating account...');
|
|
if (formKey.currentState!.validate()) {
|
|
setState(() => loading = true);
|
|
final credentials = LoginCredentials(
|
|
usernameTextController.text, passwordTextController.text);
|
|
try {
|
|
final token = await postRegister(credentials);
|
|
getIt<AuthenticationModel>().state =
|
|
Authenticated(token, credentials.username);
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
setState(() => loading = false);
|
|
}
|
|
}
|
|
}
|
|
}
|