Code updated

This commit is contained in:
risnaman
2026-09-19 16:21:37 +05:30
parent 012969ae21
commit 212df37f8a
4 changed files with 352 additions and 357 deletions
+3 -1
View File
@@ -1,8 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:label="authsec_flutter_hybrid" android:label="authsec_flutter_hybrid"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
+16 -8
View File
@@ -213,23 +213,32 @@ class _CreateAccountScreenState extends State<CreateAccountScreen> {
onTap: () async { onTap: () async {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
_formKey.currentState!.save(); _formKey.currentState!.save();
{
try { try {
print('form data is $formData'); print('form data is $formData');
final response = final response =
await userService.createAccount(formData); await userService.createAccount(formData);
account_id = response['account_id'].toString(); var parsedId;
if (response is Map) {
parsedId = response['account_id'] ??
response['id'] ??
response['sysAccountId'] ??
response['accountId'] ??
response['item']?['account_id'] ??
response['item']?['id'];
} else {
parsedId = response;
}
account_id = parsedId?.toString();
print( print(
'after create account account id is $account_id'); 'after create account account id is $account_id');
// ignore: use_build_context_synchronously
Navigator.pop(
_context, account_id); // Pop with account_id
// Navigator.pop(context); if (!mounted) return;
Navigator.pop(context, account_id);
} catch (e) { } catch (e) {
// ignore: use_build_context_synchronously if (!mounted) return;
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@@ -249,7 +258,6 @@ class _CreateAccountScreenState extends State<CreateAccountScreen> {
); );
} }
} }
}
}, },
), ),
), ),
+52 -18
View File
@@ -4,7 +4,42 @@ import '../../resources/api_constants.dart';
class SignUpApiService { class SignUpApiService {
final String baseUrl = ApiConstants.baseUrl; final String baseUrl = ApiConstants.baseUrl;
final Dio dio = Dio(); late final Dio dio;
SignUpApiService() {
dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {
'Content-Type': 'application/json',
},
));
}
String _formatError(dynamic e, String defaultMessage) {
if (e is DioException) {
if (e.response != null && e.response?.data != null) {
final data = e.response!.data;
if (data is Map) {
if (data['message'] != null) return data['message'].toString();
if (data['operationMessage'] != null) return data['operationMessage'].toString();
if (data['error'] != null) return data['error'].toString();
} else if (data is String && data.isNotEmpty) {
return data;
}
}
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.sendTimeout ||
e.type == DioExceptionType.receiveTimeout) {
return 'Connection timed out. Please check your network and backend.';
}
if (e.type == DioExceptionType.connectionError) {
return 'Cannot connect to server at $baseUrl. Please verify the backend is running.';
}
return e.message ?? defaultMessage;
}
return defaultMessage.isNotEmpty ? '$defaultMessage: $e' : e.toString();
}
// get all account // get all account
Future<List<Map<String, dynamic>>> getallAccount(String token) async { Future<List<Map<String, dynamic>>> getallAccount(String token) async {
@@ -17,18 +52,15 @@ class SignUpApiService {
print('response data is ... $responseData'); print('response data is ... $responseData');
if (responseData is List) { if (responseData is List) {
// If the response is a list, cast it to the expected type
final entities = responseData.cast<Map<String, dynamic>>(); final entities = responseData.cast<Map<String, dynamic>>();
return entities; return entities;
} else if (responseData is Map<String, dynamic>) { } else if (responseData is Map<String, dynamic>) {
// If the response is a single object, wrap it in a list
return [responseData]; return [responseData];
} else { } else {
// Handle other unexpected response types here
throw Exception('Unexpected response type'); throw Exception('Unexpected response type');
} }
} catch (e) { } catch (e) {
throw Exception('Failed to Account: $e'); throw Exception(_formatError(e, 'Failed to fetch Account'));
} }
} }
@@ -36,14 +68,18 @@ class SignUpApiService {
Future<Map<String, dynamic>> createAccount( Future<Map<String, dynamic>> createAccount(
Map<String, dynamic> entity) async { Map<String, dynamic> entity) async {
try { try {
// dio.options.headers['Authorization'] = 'Bearer $token';
final response = await dio final response = await dio
.post('$baseUrl/token/users/sysaccount/savesysaccount', data: entity); .post('$baseUrl/token/users/sysaccount/savesysaccount', data: entity);
print(' created account is $response'); print('created account is $response');
if (response.data is Map<String, dynamic>) {
return response.data; return response.data;
} else if (response.data is Map) {
return Map<String, dynamic>.from(response.data);
}
return {'account_id': response.data.toString()};
} catch (e) { } catch (e) {
throw Exception('Failed To Create Account: $e'); throw Exception(_formatError(e, 'Failed to Create Account'));
} }
} }
@@ -51,41 +87,39 @@ class SignUpApiService {
Future<void> sendEmail(Map<String, dynamic> entity) async { Future<void> sendEmail(Map<String, dynamic> entity) async {
try { try {
print("in post api...$entity"); print("in post api...$entity");
// dio.options.headers['Authorization'] = 'Bearer $token';
await dio.post('$baseUrl/token/user/send_email', data: entity); await dio.post('$baseUrl/token/user/send_email', data: entity);
print(entity); print(entity);
} catch (e) { } catch (e) {
throw Exception('Failed to Send Email: $e'); throw Exception(_formatError(e, 'Failed to Send Email'));
} }
} }
// RESEND EMAIL FOR OTP // RESEND EMAIL FOR OTP
Future<void> resendEmail(String email) async { Future<void> resendEmail(String email) async {
try { try {
// dio.options.headers['Authorization'] = 'Bearer $token';
await dio.post('$baseUrl/token/user/resend_otp?email=$email'); await dio.post('$baseUrl/token/user/resend_otp?email=$email');
} catch (e) { } catch (e) {
throw Exception('Failed to ReSend Email: $e'); throw Exception(_formatError(e, 'Failed to Resend OTP'));
} }
} }
// OTP VEFICATION // OTP VERIFICATION
Future<void> otpverification(String email, String otp) async { Future<void> otpverification(String email, String otp) async {
try { try {
// dio.options.headers['Authorization'] = 'Bearer $token';
await dio await dio
.post('$baseUrl/token/user/otp_verification?email=$email&otp=$otp'); .post('$baseUrl/token/user/otp_verification?email=$email&otp=$otp');
} catch (e) { } catch (e) {
throw Exception('Failed to Verify Otp: $e'); throw Exception(_formatError(e, 'Failed to Verify OTP'));
} }
} }
Future<void> createuser(Map<String, dynamic> entity) async { Future<void> createuser(Map<String, dynamic> entity) async {
try { try {
print("in post api...$entity"); print("in post api...$entity");
await dio.post('$baseUrl/token/addOneAppUser', data: entity); await dio.post('$baseUrl/token/addOneAppUser', data: entity);
print(entity); print(entity);
} catch (e) { } catch (e) {
throw Exception('Failed to create User: $e'); throw Exception(_formatError(e, 'Failed to Create User'));
} }
} }
@@ -96,7 +130,7 @@ class SignUpApiService {
await dio.put('$baseUrl/api/updateAppUserDto/$entityId', data: entity); await dio.put('$baseUrl/api/updateAppUserDto/$entityId', data: entity);
print(entity); print(entity);
} catch (e) { } catch (e) {
throw Exception('Failed to update Backend: $e'); throw Exception(_formatError(e, 'Failed to update User'));
} }
} }
@@ -105,7 +139,7 @@ class SignUpApiService {
dio.options.headers['Authorization'] = 'Bearer $token'; dio.options.headers['Authorization'] = 'Bearer $token';
await dio.delete('$baseUrl/api/delete_usr/$entityId'); await dio.delete('$baseUrl/api/delete_usr/$entityId');
} catch (e) { } catch (e) {
throw Exception('Failed to delete User: $e'); throw Exception(_formatError(e, 'Failed to delete User'));
} }
} }
} }
+153 -202
View File
@@ -37,22 +37,18 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
final Map<String, dynamic> formData = {}; final Map<String, dynamic> formData = {};
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
var selectedAccount; final TextEditingController emailcontroller = TextEditingController();
TextEditingController emailcontroller = TextEditingController(); String? otp;
var email; bool _isLoading = false;
var otp;
var confirmPassword;
bool _passwordVisible = false;
bool _isPasswordValid = true;
void _validatePassword(String password) {
setState(() {
_isPasswordValid = password.isNotEmpty;
});
}
RegistrationStep currentStep = RegistrationStep.SendOTP; RegistrationStep currentStep = RegistrationStep.SendOTP;
@override
void dispose() {
emailcontroller.dispose();
super.dispose();
}
void moveToNextStep() { void moveToNextStep() {
setState(() { setState(() {
if (currentStep == RegistrationStep.SendOTP) { if (currentStep == RegistrationStep.SendOTP) {
@@ -66,25 +62,96 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
} }
void showSuccessMessage(String message) { void showSuccessMessage(String message) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(message), content: Text(message),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 3),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
); );
} }
void showErrorMessage(String error) { void showErrorMessage(String error) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(error), content: Text(error),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 4),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
); );
} }
Future<void> _handleSendOtp() async {
FocusManager.instance.primaryFocus?.unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
final email = emailcontroller.text.trim();
formData['usrGrpId'] = 41;
formData['email'] = email;
setState(() => _isLoading = true);
try {
print('send email data is $formData');
await userService.sendEmail(formData);
if (!mounted) return;
showSuccessMessage('OTP sent to $email');
moveToNextStep();
} catch (e) {
if (!mounted) return;
showErrorMessage('Failed to send OTP: $e');
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
}
Future<void> _handleVerifyOtp() async {
FocusManager.instance.primaryFocus?.unfocus();
final enteredOtp = otp?.trim();
if (enteredOtp == null || enteredOtp.length < 6) {
showErrorMessage('Please enter the complete 6-digit OTP');
return;
}
final email = emailcontroller.text.trim();
setState(() => _isLoading = true);
try {
print('Verifying email: $email with OTP: $enteredOtp');
await userService.otpverification(email, enteredOtp);
if (!mounted) return;
showSuccessMessage('Email verified successfully');
moveToNextStep();
} catch (e) {
if (!mounted) return;
showErrorMessage('Failed to verify OTP: $e');
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
Future<void> _handleResendOtp() async {
final email = emailcontroller.text.trim();
if (email.isEmpty) {
showErrorMessage('Email address is missing');
return;
}
setState(() => _isLoading = true);
try {
await userService.resendEmail(email);
if (!mounted) return;
showSuccessMessage('OTP resent to $email');
} catch (e) {
if (!mounted) return;
showErrorMessage('Failed to resend OTP: $e');
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -98,24 +165,28 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
svgPath: ImageConstant.imgArrowleft, svgPath: ImageConstant.imgArrowleft,
margin: getMargin(left: 16, top: 13, bottom: 17), margin: getMargin(left: 16, top: 13, bottom: 17),
onTap: () { onTap: () {
if (currentStep == RegistrationStep.VerifyOTP) {
setState(() => currentStep = RegistrationStep.SendOTP);
} else if (currentStep == RegistrationStep.EnterUserInfo) {
setState(() => currentStep = RegistrationStep.VerifyOTP);
} else {
Navigator.pop(context); Navigator.pop(context);
}
}), }),
centerTitle: true, centerTitle: true,
title: AppbarTitle(text: "Create User")), title: AppbarTitle(text: "Create User")),
body: Builder( body: SingleChildScrollView(
builder: (BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Form( child: Form(
key: _formKey, key: _formKey,
child: SizedBox( child: _buildStepContent(),
height: MediaQuery.of(context).size.height * ),
0.5, // Use 50% of the screen height ),
child: ListView.builder( );
itemCount: 1, }
itemBuilder: (BuildContext context, int index) {
if(currentStep == RegistrationStep.SendOTP) { Widget _buildStepContent() {
if (currentStep == RegistrationStep.SendOTP) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@@ -125,12 +196,15 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: AppStyle.txtGilroyMedium16Bluegray900), style: AppStyle.txtGilroyMedium16Bluegray900),
CustomTextFormField( CustomTextFormField(
focusNode: FocusNode(),
controller: emailcontroller, controller: emailcontroller,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.trim().isEmpty) {
return 'Please enter Email'; return 'Please enter Email';
} }
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value.trim())) {
return 'Please enter a valid email address';
}
return null; return null;
}, },
hintText: "Enter Your Email", hintText: "Enter Your Email",
@@ -139,92 +213,26 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
CustomButton( CustomButton(
height: getVerticalSize(50), height: getVerticalSize(50),
width: getHorizontalSize(396), width: getHorizontalSize(396),
text: "Send OTP", text: _isLoading ? "Sending OTP..." : "Send OTP",
margin: getMargin(top: 25), margin: getMargin(top: 25),
onTap: () async { onTap: _isLoading ? null : _handleSendOtp,
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
formData['usrGrpId'] = 41;
formData['email'] = emailcontroller.text;
try {
print('send email data is $formData');
await userService.sendEmail(formData);
await Future.delayed(
const Duration(seconds: 2));
moveToNextStep();
} catch (e) {
showErrorMessage(
'Failed to send OTP: $e');
}
}
},
), ),
Align( ],
alignment: Alignment.center, );
child: Padding( } else if (currentStep == RegistrationStep.VerifyOTP) {
padding: getPadding(top: 28, bottom: 5),
child: RichText(
text: TextSpan(children: [
TextSpan(
text: "",
style: TextStyle(
color: ColorConstant.fromHex(
"#ff12282a"),
fontSize: getFontSize(16),
fontFamily: 'Gilroy',
fontWeight: FontWeight.w400)),
TextSpan(
text: " ",
style: TextStyle(
color: ColorConstant.fromHex(
"#ff12282a"),
fontSize: getFontSize(16),
fontFamily: 'Gilroy',
fontWeight: FontWeight.w700)),
TextSpan(
text: "",
style: TextStyle(
color: ColorConstant.fromHex(
"#ff0061ff"),
fontSize: getFontSize(16),
fontFamily: 'Gilroy',
fontWeight: FontWeight.w700,
decoration:
TextDecoration.underline))
]),
textAlign: TextAlign.left)))
]);
}
else if(currentStep == RegistrationStep.VerifyOTP) {
return Container( return Container(
width: double.maxFinite, width: double.maxFinite,
padding: getPadding( padding: getPadding(left: 16, top: 30, right: 16),
left: 16,
top: 76,
right: 16,
),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
CustomImageView( CustomImageView(
svgPath: ImageConstant.imgMobile, svgPath: ImageConstant.imgMobile,
height: getVerticalSize( height: getVerticalSize(82),
82, width: getHorizontalSize(51),
),
width: getHorizontalSize(
51,
),
), ),
Padding( Padding(
padding: getPadding( padding: getPadding(top: 24),
top: 29,
),
child: Text( child: Text(
"Email Verification", "Email Verification",
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@@ -233,21 +241,16 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
), ),
), ),
Container( Container(
width: getHorizontalSize( width: getHorizontalSize(302),
302, margin: getMargin(left: 16, top: 16, right: 16),
),
margin: getMargin(
left: 46,
top: 19,
right: 46,
),
child: Text( child: Text(
"A mail with a 6-digit verification code was just sent to ${emailcontroller.text}", "A mail with a 6-digit verification code was just sent to ${emailcontroller.text.trim()}",
maxLines: null, maxLines: null,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: AppStyle.txtGilroyMedium16, style: AppStyle.txtGilroyMedium16,
), ),
), ),
const SizedBox(height: 24),
Pinput( Pinput(
length: 6, length: 6,
showCursor: true, showCursor: true,
@@ -265,134 +268,82 @@ class _SignUpUserScreenState extends State<SignUpUserScreenNew> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
onChanged: (value) {
otp = value;
},
onCompleted: (value) { onCompleted: (value) {
setState(() { setState(() {
otp = value; otp = value;
}); });
}, },
), ),
CustomButton( CustomButton(
height: getVerticalSize( height: getVerticalSize(50),
50, text: _isLoading ? "Verifying..." : "Next",
margin: getMargin(top: 30),
onTap: _isLoading ? null : _handleVerifyOtp,
), ),
text: "Next",
margin: getMargin(
top: 40,
),
onTap: () async {
try {
print('email is $email and otp is $otp');
await userService.otpverification(emailcontroller.text, otp);
moveToNextStep();
showSuccessMessage(
'Email verified successfully');
} catch (e) {
showErrorMessage('Failed to verify OTP: $e');
}
},
),
GestureDetector(
onTap: () async {
try {
await userService.resendEmail(email);
await Future.delayed(
const Duration(seconds: 5));
showSuccessMessage('OTP RESEND');
} catch (e) {
showErrorMessage(
'Failed to resend OTP: $e');
}
},
child: Padding(
padding: getPadding(top: 3),
child: Text("Forgot Password?",
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style:
AppStyle.txtGilroyMedium14BlueA700)),
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: getPadding(
top: 18,
bottom: 5,
),
child: Row(
children: [
Padding( Padding(
padding: getPadding( padding: getPadding(top: 24, bottom: 10),
top: 2, child: Row(
), mainAxisAlignment: MainAxisAlignment.center,
child: Text( children: [
Text(
"Didnt get the code?", "Didnt get the code?",
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: AppStyle.txtGilroyMedium16, style: AppStyle.txtGilroyMedium16,
), ),
), const SizedBox(width: 8),
Padding( InkWell(
padding: getPadding( onTap: _isLoading ? null : _handleResendOtp,
left: 12,
bottom: 1,
),
child: Text( child: Text(
"Resend", "Resend",
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: AppStyle.txtGilroySemiBold16BlueA700, style: AppStyle.txtGilroySemiBold16BlueA700,
), ),
), ),
], ],
), ),
), ),
),
], ],
), ),
); );
} } else if (currentStep == RegistrationStep.EnterUserInfo) {
else if (currentStep ==
RegistrationStep.EnterUserInfo) {
return Column( return Column(
children: [ children: [
const SizedBox(height: 40),
CustomImageView(
svgPath: ImageConstant.imgMobile,
height: getVerticalSize(82),
width: getHorizontalSize(51),
),
const SizedBox(height: 20),
Text(
"Email Verified!",
style: AppStyle.txtGilroySemiBold24,
),
const SizedBox(height: 10),
Text(
"Please proceed to enter your registration details.",
textAlign: TextAlign.center,
style: AppStyle.txtGilroyMedium16,
),
CustomButton( CustomButton(
height: getVerticalSize( height: getVerticalSize(50),
50,
),
text: "Go to Registration Details", text: "Go to Registration Details",
margin: getMargin( margin: getMargin(top: 40),
top: 40, onTap: () {
),
onTap: () async {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
RegistrationDetailsScreen(email: emailcontroller.text), RegistrationDetailsScreen(email: emailcontroller.text.trim()),
), ),
); );
}, },
), ),
], ],
); );
} } else {
else{
return Container(); return Container();
} }
},
),
),
),
),
);
},
),
);
} }
} }