2026-09-15 09:09:11 +05:30
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
|
|
|
|
|
import '../../Utils/image_constant.dart';
|
|
|
|
|
import '../../Utils/size_utils.dart';
|
|
|
|
|
import '../../theme/app_style.dart';
|
|
|
|
|
import '../../widgets/app_bar/appbar_image.dart';
|
|
|
|
|
import '../../widgets/app_bar/appbar_title.dart';
|
|
|
|
|
import '../../widgets/app_bar/custom_app_bar.dart';
|
|
|
|
|
import '../../widgets/custom_button.dart';
|
|
|
|
|
import '../../widgets/custom_text_form_field.dart';
|
|
|
|
|
import '../Login Screen/login_screen.dart';
|
|
|
|
|
import 'CreateAccount.dart';
|
|
|
|
|
import 'SignUpService.dart';
|
|
|
|
|
|
|
|
|
|
class RegistrationDetailsScreen extends StatefulWidget {
|
|
|
|
|
var email;
|
|
|
|
|
RegistrationDetailsScreen({required this.email});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
_RegistrationDetailsScreenState createState() =>
|
|
|
|
|
_RegistrationDetailsScreenState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _RegistrationDetailsScreenState extends State<RegistrationDetailsScreen> {
|
|
|
|
|
final SignUpApiService userService = SignUpApiService();
|
|
|
|
|
|
|
|
|
|
final Map<String, dynamic> formData = {};
|
|
|
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
|
|
|
|
|
|
var account_id = null; // Initialize with null
|
|
|
|
|
var selectedAccount; // Use nullable type
|
|
|
|
|
|
|
|
|
|
var newPassword = ''; // Store the value of the confirm password field
|
|
|
|
|
var confirmPassword = ''; // Store the value of the confirm password field
|
2026-09-18 18:23:17 +00:00
|
|
|
bool _isSubmitting = false;
|
|
|
|
|
|
|
|
|
|
late final FocusNode _firstNameNode;
|
|
|
|
|
late final FocusNode _lastNameNode;
|
|
|
|
|
late final FocusNode _mobileNode;
|
|
|
|
|
late final FocusNode _newPasswordNode;
|
|
|
|
|
late final FocusNode _confirmPasswordNode;
|
2026-09-15 09:09:11 +05:30
|
|
|
// Validate that the passwords match
|
|
|
|
|
String? _validatePasswordMatch(String value) {
|
|
|
|
|
if (value != newPassword) {
|
|
|
|
|
print('value is $value and new is $newPassword');
|
|
|
|
|
return 'Passwords do not match';
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool _newpasswordVisible = false;
|
|
|
|
|
bool _confirmpasswordVisible = false;
|
|
|
|
|
|
|
|
|
|
bool _isPasswordValid = true;
|
|
|
|
|
void _validatePassword(String password) {
|
2026-09-18 18:23:17 +00:00
|
|
|
final valid = password.isNotEmpty;
|
|
|
|
|
if (valid != _isPasswordValid) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_isPasswordValid = valid;
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-09-15 09:09:11 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool _isEmailValid = true;
|
|
|
|
|
void _validateEmail(String email) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_isEmailValid =
|
|
|
|
|
RegExp(r'^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$').hasMatch(email);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void showSuccessMessage(String message) {
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
|
SnackBar(
|
|
|
|
|
content: Text(message),
|
|
|
|
|
duration: const Duration(seconds: 2),
|
|
|
|
|
backgroundColor: Colors.green,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void showErrorMessage(String error) {
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
|
SnackBar(
|
|
|
|
|
content: Text(error),
|
|
|
|
|
duration: const Duration(seconds: 2),
|
|
|
|
|
backgroundColor: Colors.red,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
2026-09-18 18:23:17 +00:00
|
|
|
_firstNameNode = FocusNode();
|
|
|
|
|
_lastNameNode = FocusNode();
|
|
|
|
|
_mobileNode = FocusNode();
|
|
|
|
|
_newPasswordNode = FocusNode();
|
|
|
|
|
_confirmPasswordNode = FocusNode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_firstNameNode.dispose();
|
|
|
|
|
_lastNameNode.dispose();
|
|
|
|
|
_mobileNode.dispose();
|
|
|
|
|
_newPasswordNode.dispose();
|
|
|
|
|
_confirmPasswordNode.dispose();
|
|
|
|
|
super.dispose();
|
2026-09-15 09:09:11 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
|
|
|
|
appBar: CustomAppBar(
|
|
|
|
|
height: getVerticalSize(54),
|
|
|
|
|
leadingWidth: 40,
|
|
|
|
|
leading: AppbarImage(
|
|
|
|
|
height: getSize(24),
|
|
|
|
|
width: getSize(24),
|
|
|
|
|
svgPath: ImageConstant.imgArrowleft,
|
|
|
|
|
margin: getMargin(left: 16, top: 13, bottom: 17),
|
|
|
|
|
onTap: () {
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
}),
|
|
|
|
|
centerTitle: true,
|
|
|
|
|
title: AppbarTitle(text: "Registration")),
|
|
|
|
|
//AppBar(title: const Text('Registration')),
|
|
|
|
|
body: SingleChildScrollView(
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
|
child: Form(
|
|
|
|
|
key: _formKey,
|
2026-09-18 18:23:17 +00:00
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
2026-09-15 09:09:11 +05:30
|
|
|
children: [
|
|
|
|
|
Padding(
|
|
|
|
|
padding: getPadding(top: 19),
|
|
|
|
|
child: Text("First Name",
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
style:
|
|
|
|
|
AppStyle.txtGilroyMedium16Bluegray900)),
|
|
|
|
|
CustomTextFormField(
|
2026-09-18 18:23:17 +00:00
|
|
|
focusNode: _firstNameNode,
|
2026-09-15 09:09:11 +05:30
|
|
|
onsaved: (value) => formData['first_name'] = value,
|
|
|
|
|
validator: (value) {
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
return 'Please enter First Name';
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
hintText: "enter First Name",
|
|
|
|
|
margin: getMargin(top: 6),
|
|
|
|
|
padding: TextFormFieldPadding.PaddingT12,
|
|
|
|
|
textInputType: TextInputType.text
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: getPadding(top: 19),
|
|
|
|
|
child: Text("Last Name",
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
style:
|
|
|
|
|
AppStyle.txtGilroyMedium16Bluegray900)),
|
|
|
|
|
CustomTextFormField(
|
2026-09-18 18:23:17 +00:00
|
|
|
focusNode: _lastNameNode,
|
2026-09-15 09:09:11 +05:30
|
|
|
onsaved: (value) => formData['last_name'] = value,
|
|
|
|
|
validator: (value) {
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
return 'Please enter Last Name';
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
hintText: "enter Last Name",
|
|
|
|
|
margin: getMargin(top: 6),
|
|
|
|
|
padding: TextFormFieldPadding.PaddingT12,
|
|
|
|
|
textInputType: TextInputType.text
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
Padding(
|
|
|
|
|
padding: getPadding(top: 19),
|
|
|
|
|
child: Text("Mobile Number",
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
style:
|
|
|
|
|
AppStyle.txtGilroyMedium16Bluegray900)),
|
|
|
|
|
CustomTextFormField(
|
2026-09-18 18:23:17 +00:00
|
|
|
focusNode: _mobileNode,
|
2026-09-15 09:09:11 +05:30
|
|
|
textInputType: TextInputType.phone,
|
|
|
|
|
inputFormatters: [
|
|
|
|
|
FilteringTextInputFormatter.allow(RegExp(r'[0-9]'))
|
|
|
|
|
],
|
|
|
|
|
onsaved: (value) => formData['mob_no'] = value,
|
|
|
|
|
validator: (value) {
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
return 'Please enter Mobile Number';
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
hintText: "enter Mobile Number",
|
|
|
|
|
margin: getMargin(top: 6),
|
|
|
|
|
padding: TextFormFieldPadding.PaddingT12,
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: getPadding(top: 19),
|
|
|
|
|
child: Text("New Password",
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
style:
|
|
|
|
|
AppStyle.txtGilroyMedium16Bluegray900)),
|
|
|
|
|
CustomTextFormField(
|
2026-09-18 18:23:17 +00:00
|
|
|
focusNode: _newPasswordNode,
|
2026-09-15 09:09:11 +05:30
|
|
|
hintText: "New Password",
|
|
|
|
|
margin: getMargin(top: 6),
|
|
|
|
|
padding: TextFormFieldPadding.PaddingT12,
|
|
|
|
|
textInputAction: TextInputAction.done,
|
|
|
|
|
textInputType:TextInputType.visiblePassword,
|
|
|
|
|
validator: (value) {
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
return 'Please enter Password';
|
|
|
|
|
}else if(!_isPasswordValid){
|
|
|
|
|
return 'Please enter a valid password';
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
suffix: IconButton(
|
|
|
|
|
icon: Icon(
|
|
|
|
|
_newpasswordVisible
|
|
|
|
|
? Icons.visibility
|
|
|
|
|
: Icons.visibility_off,
|
|
|
|
|
),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
setState(() {
|
|
|
|
|
_newpasswordVisible = !_newpasswordVisible;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
onsaved: (value) => formData['new_password'] = value,
|
|
|
|
|
onChanged: (value) {
|
2026-09-18 18:23:17 +00:00
|
|
|
newPassword = value;
|
|
|
|
|
_validatePassword(value);
|
2026-09-15 09:09:11 +05:30
|
|
|
},
|
|
|
|
|
suffixConstraints: BoxConstraints(
|
|
|
|
|
maxHeight: getVerticalSize(44)),
|
|
|
|
|
isObscureText: !_newpasswordVisible),
|
|
|
|
|
|
|
|
|
|
Padding(
|
|
|
|
|
padding: getPadding(top: 19),
|
|
|
|
|
child: Text("Confirm Password",
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
style:
|
|
|
|
|
AppStyle.txtGilroyMedium16Bluegray900)),
|
|
|
|
|
CustomTextFormField(
|
2026-09-18 18:23:17 +00:00
|
|
|
focusNode: _confirmPasswordNode,
|
2026-09-15 09:09:11 +05:30
|
|
|
hintText: "Confirm Password",
|
|
|
|
|
margin: getMargin(top: 6),
|
|
|
|
|
padding: TextFormFieldPadding.PaddingT12,
|
|
|
|
|
textInputAction: TextInputAction.done,
|
|
|
|
|
textInputType:TextInputType.visiblePassword,
|
|
|
|
|
validator: (value) {
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
return 'Please enter Password';
|
|
|
|
|
}
|
2026-09-18 18:23:17 +00:00
|
|
|
return _validatePasswordMatch(value);
|
2026-09-15 09:09:11 +05:30
|
|
|
},
|
|
|
|
|
onsaved: (value) => formData['confirm_password'] = value,
|
|
|
|
|
onChanged: (value) {
|
2026-09-18 18:23:17 +00:00
|
|
|
confirmPassword = value; // Update confirmPassword
|
2026-09-15 09:09:11 +05:30
|
|
|
},
|
|
|
|
|
suffix: IconButton(
|
|
|
|
|
icon: Icon(
|
|
|
|
|
_confirmpasswordVisible
|
|
|
|
|
? Icons.visibility
|
|
|
|
|
: Icons.visibility_off,
|
|
|
|
|
),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
setState(() {
|
|
|
|
|
_confirmpasswordVisible = !_confirmpasswordVisible;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
suffixConstraints: BoxConstraints(
|
|
|
|
|
maxHeight: getVerticalSize(44)),
|
|
|
|
|
isObscureText: !_confirmpasswordVisible,),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
const Expanded(
|
|
|
|
|
child: Text('Add Account'),
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
final accountId = await Navigator.push(
|
|
|
|
|
context,
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
builder: (context) => CreateAccountScreen(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (accountId != null) {
|
|
|
|
|
setState(() {
|
|
|
|
|
selectedAccount = accountId;
|
|
|
|
|
formData['account_id'] = accountId;
|
|
|
|
|
account_id =
|
|
|
|
|
accountId; // Update the account_id here
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
icon: const Icon(Icons.add),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
if (account_id == null)
|
|
|
|
|
const Padding(
|
|
|
|
|
padding: EdgeInsets.only(top: 8),
|
|
|
|
|
child: Text(
|
|
|
|
|
'Tap + to create an account first, then SUBMIT will be enabled.',
|
|
|
|
|
style: TextStyle(fontSize: 12, color: Colors.grey),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
Container(
|
2026-09-15 09:09:11 +05:30
|
|
|
margin:
|
|
|
|
|
const EdgeInsets.symmetric(vertical: 5), // Add margin
|
|
|
|
|
child: CustomButton(
|
|
|
|
|
height: getVerticalSize(50),
|
|
|
|
|
width: getHorizontalSize(396),
|
2026-09-18 18:23:17 +00:00
|
|
|
text: _isSubmitting ? "PLEASE WAIT..." : "SUBMIT",
|
2026-09-15 09:09:11 +05:30
|
|
|
margin: getMargin(top: 25),
|
2026-09-18 18:23:17 +00:00
|
|
|
onTap: (account_id == null || _isSubmitting)
|
|
|
|
|
? null
|
|
|
|
|
: () async {
|
|
|
|
|
final isValid =
|
|
|
|
|
_formKey.currentState!.validate();
|
|
|
|
|
if (!isValid) {
|
|
|
|
|
showErrorMessage(
|
|
|
|
|
'Please fix the red errors (check passwords match).');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_formKey.currentState!.save();
|
2026-09-15 09:09:11 +05:30
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
print('formdata is $formData');
|
2026-09-15 09:09:11 +05:30
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
formData['usrGrpId'] = 46;
|
|
|
|
|
formData['account_id'] = account_id;
|
|
|
|
|
formData['email'] = widget.email;
|
2026-09-15 09:09:11 +05:30
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
setState(() => _isSubmitting = true);
|
|
|
|
|
try {
|
|
|
|
|
print(formData);
|
2026-09-15 09:09:11 +05:30
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
await userService.createuser(formData);
|
2026-09-15 09:09:11 +05:30
|
|
|
|
2026-09-18 18:23:17 +00:00
|
|
|
if (!context.mounted) return;
|
|
|
|
|
showSuccessMessage(
|
|
|
|
|
'User created successfully');
|
|
|
|
|
Navigator.pushAndRemoveUntil(
|
|
|
|
|
context,
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
builder: (context) =>
|
|
|
|
|
const LoginScreen()),
|
|
|
|
|
(route) => false);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (!context.mounted) return;
|
|
|
|
|
showErrorMessage(
|
|
|
|
|
'Failed to create User: $e');
|
|
|
|
|
} finally {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
setState(() => _isSubmitting = false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-09-15 09:09:11 +05:30
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
2026-09-18 18:23:17 +00:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|