api integration
This commit is contained in:
@@ -3,13 +3,28 @@ class ApiEndpoints {
|
||||
static const baseurl =
|
||||
"https://tanami.betadelivery.com/api/development/v1/"; //App Base url
|
||||
|
||||
//Country
|
||||
static const getcountryurl = "${baseurl}country/getAllCountry";
|
||||
|
||||
//Register
|
||||
static const requestotpapi = "${baseurl}auth/public/register";
|
||||
static const registerrequestapi = "${baseurl}auth/public/email-register";
|
||||
|
||||
//OTP
|
||||
static const requestresendotp = "${baseurl}auth/public/resend-otp";
|
||||
static const verifyotp = "${baseurl}auth/public/verify-otp";
|
||||
static const registerrequestapi = "${baseurl}auth/public/email-register";
|
||||
|
||||
//Biometric
|
||||
static const biometricUpdateapi = "${baseurl}auth/public/biometric-update";
|
||||
static const confirmpinapi="${baseurl}auth/public/masterPin";
|
||||
static const loginapi="${baseurl}auth/public/login";
|
||||
static const verifypinapi="${baseurl}auth/public/verify-materPin";
|
||||
|
||||
//PIN
|
||||
static const confirmpinapi = "${baseurl}auth/public/masterPin";
|
||||
static const verifypinapi = "${baseurl}auth/public/verify-materPin";
|
||||
|
||||
//Login
|
||||
static const loginapi = "${baseurl}auth/public/login";
|
||||
|
||||
//Forgot Password
|
||||
static const forgotPasswordApi = "${baseurl}auth/public/forgot-password";
|
||||
static const resetPasswordApi = "${baseurl}auth/public/reset-password";
|
||||
}
|
||||
|
||||
@@ -6,17 +6,22 @@ import 'package:flutter/foundation.dart';
|
||||
import '../../Api_Helper/base_manager.dart';
|
||||
|
||||
class NetworkApiService {
|
||||
final Dio _dio = Dio();
|
||||
final Dio _dio = Dio(BaseOptions(
|
||||
validateStatus: (status) {
|
||||
return status != null &&
|
||||
status < 500; // Allow any status code less than 500
|
||||
},
|
||||
));
|
||||
|
||||
// Common function for GET requests
|
||||
Future<ResponseData> get(String url,
|
||||
{Map<String, dynamic>? queryParameters}) async {
|
||||
if (kDebugMode) {
|
||||
if (kDebugMode) {
|
||||
print("api url is >>> $url");
|
||||
}
|
||||
Response response;
|
||||
try {
|
||||
response = await _dio.get(url);
|
||||
response = await _dio.get(url);
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
return ResponseData<dynamic>("success", ResponseStatus.SUCCESS,
|
||||
data: response.data);
|
||||
@@ -29,10 +34,11 @@ class NetworkApiService {
|
||||
data: response.data,
|
||||
response.statusMessage!,
|
||||
ResponseStatus.FAILED);
|
||||
}}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return ResponseData<dynamic>(
|
||||
"Something went wrong", ResponseStatus.FAILED);
|
||||
"Something went wrong", ResponseStatus.FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,24 +49,48 @@ class NetworkApiService {
|
||||
print("api url is >>> $url");
|
||||
}
|
||||
try {
|
||||
var response= await _dio.post(url, data: data);
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
return ResponseData<dynamic>("success", ResponseStatus.SUCCESS,
|
||||
var response = await _dio.post(
|
||||
url,
|
||||
data: data,
|
||||
);
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
return ResponseData<dynamic>("success", ResponseStatus.SUCCESS,
|
||||
data: response.data);
|
||||
}else {
|
||||
} else if (response.statusCode == 400) {
|
||||
if (response.data['message'] == "Master Pin is not created") {
|
||||
return ResponseData<dynamic>(
|
||||
response.data['message'], ResponseStatus.PRIVATE,
|
||||
data: response.data);
|
||||
} else if (response.data['error']['message'] ==
|
||||
"MASTER PIN NOT MATCH") {
|
||||
return ResponseData<dynamic>(
|
||||
response.data['error']['message'], ResponseStatus.PRIVATE,
|
||||
data: response.data);
|
||||
} else if (response.data['error']['message'] ==
|
||||
"Account already exists. Please Login instead.") {
|
||||
return ResponseData<dynamic>(
|
||||
response.data['error']['message'], ResponseStatus.PRIVATE,
|
||||
data: response.data);
|
||||
} else {
|
||||
return ResponseData<dynamic>(
|
||||
response.data['error']['message'], ResponseStatus.PRIVATE,
|
||||
data: response.data);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return ResponseData<dynamic>(
|
||||
response.data['message'].toString(), ResponseStatus.FAILED);
|
||||
response.data['error']['message'].toString(),
|
||||
ResponseStatus.FAILED);
|
||||
} catch (_) {
|
||||
return ResponseData<dynamic>(
|
||||
data: response.data,
|
||||
response.statusMessage!,
|
||||
ResponseStatus.FAILED);
|
||||
}}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return ResponseData<dynamic>(
|
||||
"Oops something went wrong",
|
||||
ResponseStatus.FAILED);
|
||||
"Oops something went wrong", ResponseStatus.FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/routes/routes.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/core/utils/secure/secure_storage_service.dart';
|
||||
import 'package:tanami_app/features/biometric/presentation/bloc/biometric_bloc.dart';
|
||||
|
||||
import '../../Globalconst.dart';
|
||||
import '../../core/routes/route_name.dart';
|
||||
import '../../core/routes/routes.dart';
|
||||
import '../../core/utils/language/localizations_delegate.dart';
|
||||
import '../../features/biometric/presentation/bloc/biometric_event.dart';
|
||||
import '../../features/biometric/presentation/bloc/biometric_state.dart';
|
||||
import 'text_widget.dart';
|
||||
|
||||
@@ -17,6 +19,7 @@ deviceLockedDialog(
|
||||
context,
|
||||
) {
|
||||
var localizations = AppLocalizations.of(context);
|
||||
final SecureStorageService secureStorageService = SecureStorageService();
|
||||
return showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
@@ -106,11 +109,19 @@ deviceLockedDialog(
|
||||
),
|
||||
Gap(15.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
goRouter.pop();
|
||||
context
|
||||
.read<BiometricBloc>()
|
||||
.add(AuthenticateBiometricEvent());
|
||||
onTap: () async {
|
||||
// goRouter.pop();
|
||||
// context
|
||||
// .read<BiometricBloc>()
|
||||
// .add(AuthenticateBiometricEvent());
|
||||
Globalconst.firstName =
|
||||
await secureStorageService.read("first_name") ??
|
||||
"";
|
||||
|
||||
goRouter
|
||||
.goNamed(RouteName.pinScreen, pathParameters: {
|
||||
"fromScreen": "LoginedInUser",
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/features/login/presentation/bloc/login_bloc.dart';
|
||||
import 'package:tanami_app/shared/components/password_text_form_field.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
import '../../core/routes/route_name.dart';
|
||||
import '../../core/routes/routes.dart';
|
||||
import '../../core/utils/language/localizations_delegate.dart';
|
||||
import '../../features/forgotPassword/presentation/bloc/restore_password_phone_verification_bloc.dart';
|
||||
import '../../features/register/presentation/bloc/register_bloc.dart';
|
||||
import 'text_from_field_widget.dart';
|
||||
|
||||
class FormLabelTextField extends StatelessWidget {
|
||||
@@ -20,6 +24,7 @@ class FormLabelTextField extends StatelessWidget {
|
||||
required this.hintText,
|
||||
this.prefixWidget,
|
||||
this.onChangeFun,
|
||||
this.originalPasswordController,
|
||||
});
|
||||
final String title;
|
||||
final String type;
|
||||
@@ -27,9 +32,23 @@ class FormLabelTextField extends StatelessWidget {
|
||||
final TextEditingController textEditingController;
|
||||
final Widget? prefixWidget;
|
||||
final Function(String)? onChangeFun;
|
||||
final TextEditingController? originalPasswordController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Map of country codes to phone number lengths
|
||||
final Map<String, int> countryPhoneLengths = {
|
||||
"+973": 8, // Bahrain
|
||||
"+965": 8, // Kuwait
|
||||
"+968": 8, // Oman
|
||||
"+974": 8, // Qatar
|
||||
"+966": 9, // Saudi Arabia
|
||||
"+971": 9, // United Arab Emirates
|
||||
};
|
||||
var registerBloc = context.read<RegisterBloc>();
|
||||
var loginBloc = context.read<LoginBloc>();
|
||||
var restorePasswordBloc =
|
||||
context.read<RestorePasswordPhoneVerificationBloc>();
|
||||
var localizations = AppLocalizations.of(context);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -40,10 +59,11 @@ class FormLabelTextField extends StatelessWidget {
|
||||
txtAlign: type == "description" ? TextAlign.start : TextAlign.center,
|
||||
),
|
||||
const Gap(10),
|
||||
type == "password"
|
||||
(type == "password" || type == "repeat-password")
|
||||
? PasswordField(
|
||||
controller: textEditingController,
|
||||
hintText: hintText,
|
||||
originalPasswordController: originalPasswordController,
|
||||
)
|
||||
: textFormField(
|
||||
onInput: onChangeFun,
|
||||
@@ -52,6 +72,47 @@ class FormLabelTextField extends StatelessWidget {
|
||||
if (value != null && value.isEmpty) {
|
||||
return localizations.translate(AppText.enterPhoneNo);
|
||||
}
|
||||
if (registerBloc.isdcode.isNotEmpty) {
|
||||
// Validate phone number length based on selected country
|
||||
if (countryPhoneLengths.containsKey(
|
||||
registerBloc.isdcode,
|
||||
)) {
|
||||
final expectedLength =
|
||||
countryPhoneLengths[registerBloc.isdcode];
|
||||
if (value.length != expectedLength) {
|
||||
return localizations.translate(
|
||||
"Invalid Phone Number",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (loginBloc.isdcode.isNotEmpty) {
|
||||
// Validate phone number length based on selected country
|
||||
if (countryPhoneLengths.containsKey(
|
||||
loginBloc.isdcode,
|
||||
)) {
|
||||
final expectedLength =
|
||||
countryPhoneLengths[loginBloc.isdcode];
|
||||
if (value.length != expectedLength) {
|
||||
return localizations.translate(
|
||||
"Invalid Phone Number",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (restorePasswordBloc.isdcode.isNotEmpty) {
|
||||
// Validate phone number length based on selected country
|
||||
if (countryPhoneLengths.containsKey(
|
||||
restorePasswordBloc.isdcode,
|
||||
)) {
|
||||
final expectedLength =
|
||||
countryPhoneLengths[restorePasswordBloc.isdcode];
|
||||
if (value.length != expectedLength) {
|
||||
return localizations.translate(
|
||||
"Invalid Phone Number",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (type == "country selection") {
|
||||
if (textEditingController.text.isEmpty) {
|
||||
@@ -64,13 +125,38 @@ class FormLabelTextField extends StatelessWidget {
|
||||
.translate(AppText.pleaseEnteraDescription);
|
||||
}
|
||||
return null;
|
||||
} else if (type == "email") {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter an email address.';
|
||||
}
|
||||
// Email validation
|
||||
if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
||||
return 'Please enter a valid email address.';
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(350),
|
||||
],
|
||||
inputFormatters: (type == "phone number")
|
||||
? registerBloc.isdcode.isNotEmpty
|
||||
? [
|
||||
LengthLimitingTextInputFormatter(
|
||||
countryPhoneLengths[registerBloc.isdcode]),
|
||||
]
|
||||
: restorePasswordBloc.isdcode.isNotEmpty
|
||||
? [
|
||||
LengthLimitingTextInputFormatter(
|
||||
countryPhoneLengths[
|
||||
restorePasswordBloc.isdcode]),
|
||||
]
|
||||
: [
|
||||
LengthLimitingTextInputFormatter(
|
||||
countryPhoneLengths[loginBloc.isdcode]),
|
||||
]
|
||||
: [
|
||||
LengthLimitingTextInputFormatter(350),
|
||||
],
|
||||
maxlines: type == "description" ? 6 : 1,
|
||||
texttype: type == "phone number"
|
||||
? TextInputType.phone
|
||||
|
||||
85
lib/shared/components/language_change_bottom_sheet.dart
Normal file
85
lib/shared/components/language_change_bottom_sheet.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/routes/routes.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
void showLanguageBottomSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 1.sw,
|
||||
height: 168.7.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Colors.black87),
|
||||
child: Column(
|
||||
children: [
|
||||
const Gap(10),
|
||||
Center(
|
||||
child: TextWidget()
|
||||
.text17W700("Language", clr: Colors.white60),
|
||||
),
|
||||
const Gap(10),
|
||||
const Divider(
|
||||
color: AppColor.plainWhite,
|
||||
),
|
||||
const Gap(10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
child: Center(
|
||||
child:
|
||||
TextWidget().text15W700("English", clr: Colors.white),
|
||||
),
|
||||
),
|
||||
const Gap(10),
|
||||
const Divider(
|
||||
color: AppColor.plainWhite,
|
||||
),
|
||||
const Gap(10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
child: Center(
|
||||
child: TextWidget().text15W700("عربي", clr: Colors.white),
|
||||
),
|
||||
),
|
||||
const Gap(20),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(18),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
child: Container(
|
||||
width: 1.sw,
|
||||
height: 56.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Colors.black87),
|
||||
child: Center(
|
||||
child:
|
||||
TextWidget().text14W500("Dismiss", clr: Colors.white60),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Gap(18),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -89,6 +89,9 @@ buildprofilelogoutdialog(context) {
|
||||
onTap: () async {
|
||||
context.read<BottomNavigationBloc>().add(TabChanged(2));
|
||||
await secureStorageService.write('isLoginedIn', "false");
|
||||
await secureStorageService.write('accesstoken', '');
|
||||
await secureStorageService.write('refreshtoken', '');
|
||||
|
||||
goRouter.goNamed(RouteName.loginScreen, pathParameters: {
|
||||
"fromScreen": "registerStep",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:control_style/control_style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@@ -15,8 +16,13 @@ import 'bloc/password_field/password_visibility_state.dart';
|
||||
class PasswordField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
const PasswordField(
|
||||
{super.key, required this.controller, required this.hintText});
|
||||
final TextEditingController? originalPasswordController;
|
||||
const PasswordField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.hintText,
|
||||
this.originalPasswordController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -25,9 +31,31 @@ class PasswordField extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return TextFormField(
|
||||
validator: (value) {
|
||||
if (value != null && value.isEmpty) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return localizations.translate(AppText.enterPassword);
|
||||
}
|
||||
|
||||
// Password validation rules
|
||||
if (value.length < 8 || value.length > 20) {
|
||||
return localizations.translate(AppText.passwordLength);
|
||||
}
|
||||
if (!RegExp(r'[a-z]').hasMatch(value)) {
|
||||
return localizations.translate(AppText.passwordLowerCase);
|
||||
}
|
||||
if (!RegExp(r'[A-Z]').hasMatch(value)) {
|
||||
return localizations.translate(AppText.passwordUpperCase);
|
||||
}
|
||||
if (!RegExp(r'\d').hasMatch(value)) {
|
||||
return localizations.translate(AppText.passwordDigit);
|
||||
}
|
||||
if (!RegExp(r'[!@#$%&*()\-+=^]').hasMatch(value)) {
|
||||
return localizations.translate(AppText.passwordSpecialCharacter);
|
||||
}
|
||||
if (originalPasswordController != null &&
|
||||
value != originalPasswordController!.text) {
|
||||
return localizations.translate("Passwords do not match");
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
controller: controller,
|
||||
@@ -38,7 +66,12 @@ class PasswordField extends StatelessWidget {
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(20),
|
||||
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
errorMaxLines: 2,
|
||||
errorStyle: GoogleFonts.dmSans(
|
||||
color: AppColor.txtErrorColor,
|
||||
fontSize: 14,
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/Api_Helper/base_manager.dart';
|
||||
import 'package:tanami_app/Globalconst.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
|
||||
import '../../core/routes/route_name.dart';
|
||||
@@ -85,16 +84,7 @@ permissionDialog(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
Map<String, dynamic> biometricdata = {
|
||||
"token": Globalconst.token,
|
||||
"is_2FA_on": false
|
||||
};
|
||||
|
||||
await RegisterAPIService()
|
||||
.BiometricUpdate(biometricdata);
|
||||
|
||||
successToastMessage(context, "successful !");
|
||||
onTap: () {
|
||||
goRouter.pop();
|
||||
|
||||
goRouter.goNamed(RouteName.pinScreen, pathParameters: {
|
||||
@@ -120,7 +110,8 @@ permissionDialog(
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
Map<String, dynamic> biometricdata = {
|
||||
"token": Globalconst.token,
|
||||
"token":
|
||||
await secureStorageService.read("temp_token"),
|
||||
"is_2FA_on": true
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user