mobile_adaptive_learning/lib/features/auth/screens/signup/signup_screen.dart

330 lines
14 KiB
Dart

import 'dart:async';
import 'package:english_learning/core/widgets/custom_snackbar.dart';
import 'package:english_learning/features/auth/provider/user_provider.dart';
import 'package:english_learning/features/auth/provider/validator_provider.dart';
import 'package:english_learning/features/auth/screens/signin/signin_screen.dart';
import 'package:english_learning/core/widgets/form_field/custom_field_widget.dart';
import 'package:english_learning/core/widgets/global_button.dart';
import 'package:english_learning/core/utils/styles/theme.dart';
import 'package:english_learning/features/auth/widgets/dialog/signup_verification.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SignupScreen extends StatefulWidget {
const SignupScreen({super.key});
@override
State<SignupScreen> createState() => _SignupScreenState();
}
class _SignupScreenState extends State<SignupScreen> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _nisnController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
bool isLoading = false;
@override
Widget build(BuildContext context) {
final userProvider = Provider.of<UserProvider>(context, listen: false);
Provider.of<ValidatorProvider>(context, listen: false);
final mediaQuery = MediaQuery.of(context);
final screenHeight = mediaQuery.size.height;
Future<void> handleRegistration(
BuildContext context,
ValidatorProvider validatorProvider,
) async {
if (isLoading) return; // Prevent multiple submissions
setState(() {
isLoading = true;
});
try {
// Attempt registration with timeout
final isSuccess = await userProvider
.register(
name: _nameController.text,
email: _emailController.text,
nisn: _nisnController.text,
password: _passwordController.text,
confirmPassword: _passwordController.text,
)
.timeout(
const Duration(seconds: 30),
onTimeout: () {
throw TimeoutException(
'Registration request timed out. Please try again.');
},
);
// Remove loading dialog
Navigator.of(context).pop();
if (isSuccess) {
// Show success verification dialog
if (!context.mounted) return;
await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return PopScope(
canPop: false,
child: SignupVerification(
onSubmit: () {
// Clear the form fields
_emailController.clear();
_passwordController.clear();
_nisnController.clear();
_nameController.clear();
validatorProvider.resetFields();
// Navigate to login screen
Navigator.of(dialogContext).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const SigninScreen(),
),
(Route<dynamic> route) => false,
);
},
),
);
},
);
} else {
if (!context.mounted) return;
CustomSnackBar.show(
context,
message: 'Registration failed. Please try again.',
isError: true,
);
}
} catch (e) {
// Remove loading dialog if it's showing
if (Navigator.canPop(context)) {
Navigator.of(context).pop();
}
if (!context.mounted) return;
CustomSnackBar.show(
context,
message: 'An error occurred: ${e.toString()}',
isError: true,
);
} finally {
setState(() {
isLoading = false;
});
}
}
return Scaffold(
backgroundColor: AppColors.whiteColor,
body: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
child: Consumer<ValidatorProvider>(
builder: (context, validatorProvider, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: screenHeight * 0.05),
Text(
'👋 Hi!, you here!',
style: AppTextStyles.blueTextStyle.copyWith(
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: 4),
Text(
'Let\'s start by creating your account first',
style: AppTextStyles.greyTextStyle.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 49),
CustomFieldWidget(
controller: _nisnController,
fieldName: 'nisn',
isRequired: true,
textInputAction: TextInputAction.next,
labelText: 'NISN',
hintText: 'Enter Your NISN',
keyboardType: TextInputType.number,
validator: validatorProvider.nisnValidator,
onChanged: (value) {
validatorProvider.validateField('nisn', value,
validator: validatorProvider.nisnValidator);
},
errorText: validatorProvider.getError('nisn'),
),
const SizedBox(height: 14),
CustomFieldWidget(
controller: _nameController,
fieldName: 'name',
isRequired: true,
textInputAction: TextInputAction.next,
labelText: 'Full Name',
hintText: 'Enter Your Full Name',
keyboardType: TextInputType.text,
validator: validatorProvider.fullNameValidator,
onChanged: (value) {
validatorProvider.validateField(
'fullname',
value,
validator: validatorProvider.fullNameValidator,
);
},
errorText: validatorProvider.getError('name'),
),
const SizedBox(height: 14),
CustomFieldWidget(
controller: _emailController,
fieldName: 'email',
isRequired: true,
textInputAction: TextInputAction.next,
labelText: 'Email Address',
hintText: 'Enter Your Email Address',
keyboardType: TextInputType.emailAddress,
validator: validatorProvider.emailValidator,
onChanged: (value) {
validatorProvider.validateField(
'email',
value,
validator: validatorProvider.emailValidator,
);
},
errorText: validatorProvider.getError('email'),
),
const SizedBox(height: 14),
CustomFieldWidget(
controller: _passwordController,
fieldName: 'password',
isRequired: true,
textInputAction: TextInputAction.next,
labelText: 'Password',
hintText: 'Create a Strong Password',
obscureText: validatorProvider.isObscure('password'),
keyboardType: TextInputType.visiblePassword,
validator: validatorProvider.passwordValidator,
onChanged: (value) {
validatorProvider.validateField(
'password',
value,
validator: validatorProvider.passwordValidator,
);
},
onSuffixIconTap: () =>
validatorProvider.toggleVisibility('password'),
errorText: validatorProvider.getError('password'),
),
const SizedBox(height: 14),
CustomFieldWidget(
fieldName: 'confirmPassword',
isRequired: true,
textInputAction: TextInputAction.next,
labelText: 'Confirm Password',
hintText: 'Retype Your Password',
obscureText:
validatorProvider.isObscure('confirmPassword'),
keyboardType: TextInputType.visiblePassword,
validator: validatorProvider.confirmPasswordValidator,
onChanged: (value) {
validatorProvider.validateField(
'confirmPassword',
value,
validator: validatorProvider.confirmPasswordValidator,
);
},
onSuffixIconTap: () =>
validatorProvider.toggleVisibility('confirmPassword'),
errorText: validatorProvider.getError('confirmPassword'),
),
const SizedBox(height: 24),
GlobalButton(
text: 'Sign Up',
isLoading: isLoading,
onPressed: isLoading
? null
: () {
validatorProvider.validateField(
'nisn', _nisnController.text,
validator: validatorProvider.nisnValidator);
validatorProvider.validateField(
'name', _nameController.text,
validator:
validatorProvider.fullNameValidator);
validatorProvider.validateField(
'email', _emailController.text,
validator: validatorProvider.emailValidator);
validatorProvider.validateField(
'password', _passwordController.text,
validator:
validatorProvider.passwordValidator);
validatorProvider.validateField(
'confirmPassword', _passwordController.text,
validator: validatorProvider
.confirmPasswordValidator);
// If no error, proceed with registration
if (validatorProvider.getError('nisn') == null &&
validatorProvider.getError('name') == null &&
validatorProvider.getError('email') == null &&
validatorProvider.getError('password') ==
null &&
validatorProvider
.getError('confirmPassword') ==
null) {
handleRegistration(context, validatorProvider);
}
},
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Already have an account?',
style: AppTextStyles.blackTextStyle
.copyWith(fontSize: 14),
),
GestureDetector(
onTap: () {
context.read<ValidatorProvider>().resetFields();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SigninScreen()),
);
},
child: Text(
' Login Here',
style: AppTextStyles.blueTextStyle.copyWith(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
],
),
SizedBox(height: screenHeight * 0.1)
],
);
},
),
),
),
),
);
}
}