feat : login api call
This commit is contained in:
53
packages/auth/lib/data/common/dio_error_handler.dart
Normal file
53
packages/auth/lib/data/common/dio_error_handler.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class DioErrorHandler {
|
||||
void handle(DioException error) {
|
||||
switch (error.response?.statusCode) {
|
||||
case 401:
|
||||
_handle401();
|
||||
break;
|
||||
case 403:
|
||||
_handle403();
|
||||
break;
|
||||
default:
|
||||
_handleGeneric(error);
|
||||
}
|
||||
}
|
||||
|
||||
//wrong password/user name => "detail": "No active account found with the given credentials" - 401
|
||||
void _handle401() {
|
||||
Get.showSnackbar(
|
||||
_errorSnackBar('نام کاربری یا رمز عبور اشتباه است'),
|
||||
);
|
||||
}
|
||||
|
||||
//wrong captcha => "detail": "Captcha code is incorrect" - 403
|
||||
void _handle403() {
|
||||
Get.showSnackbar(
|
||||
_errorSnackBar('کد امنیتی اشتباه است'),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleGeneric(DioException error) {
|
||||
// General error handling
|
||||
}
|
||||
|
||||
GetSnackBar _errorSnackBar(String message) {
|
||||
return GetSnackBar(
|
||||
titleText: Text(
|
||||
'خطا',
|
||||
style: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
),
|
||||
messageText: Text(
|
||||
message,
|
||||
style: AppFonts.yekan12.copyWith(color: Colors.white),
|
||||
),
|
||||
backgroundColor: AppColor.error,
|
||||
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
borderRadius: 12,
|
||||
duration: Duration(milliseconds: 3500),
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:rasadyar_auth/data/common/constant.dart';
|
||||
import 'package:rasadyar_auth/data/common/dio_error_handler.dart';
|
||||
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||
import 'package:rasadyar_auth/data/services/auth_service.dart';
|
||||
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
||||
@@ -16,6 +17,7 @@ Future<void> setupAuthDI() async {
|
||||
diAuth.registerCachedFactory<AuthRepositoryImpl>(
|
||||
() => AuthRepositoryImpl(dioRemote),
|
||||
);
|
||||
diAuth.registerLazySingleton(() => AuthService());
|
||||
diAuth.registerLazySingleton(() => TokenStorageService());
|
||||
diAuth.registerLazySingleton<AuthService>(() => AuthService());
|
||||
diAuth.registerLazySingleton<TokenStorageService>(() => TokenStorageService());
|
||||
diAuth.registerLazySingleton<DioErrorHandler>(() => DioErrorHandler());
|
||||
}
|
||||
|
||||
@@ -12,10 +12,23 @@ abstract class LoginRequestModel with _$LoginRequestModel {
|
||||
String? captchaKey,
|
||||
}) = _LoginRequestModel;
|
||||
|
||||
factory LoginRequestModel.createWithCaptcha({
|
||||
required String username,
|
||||
required String password,
|
||||
required String captchaCode,
|
||||
required String captchaKey,
|
||||
}) {
|
||||
return LoginRequestModel(
|
||||
username: username,
|
||||
password: password,
|
||||
captchaCode: captchaCode,
|
||||
captchaKey: 'rest_captcha_$captchaKey.0',
|
||||
);
|
||||
}
|
||||
|
||||
factory LoginRequestModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$LoginRequestModelFromJson(json);
|
||||
|
||||
const LoginRequestModel._();
|
||||
|
||||
String get formattedCaptchaKey => 'rest_captcha_$captchaKey.0';
|
||||
}
|
||||
|
||||
@@ -14,64 +14,34 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
Future<AuthResponseModel?> login({
|
||||
required Map<String, dynamic> authRequest,
|
||||
}) async {
|
||||
final response = await safeCall<DioResponse<AuthResponseModel>>(
|
||||
call:
|
||||
() async => await _httpClient.post<AuthResponseModel>(
|
||||
'$_BASE_URL/login/',
|
||||
data: authRequest,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
),
|
||||
onSuccess: (response) {
|
||||
iLog(response);
|
||||
},
|
||||
onError: (error, trace) {
|
||||
throw Exception('Error during sign in: $error');
|
||||
},
|
||||
var res = await _httpClient.post<AuthResponseModel>(
|
||||
'$_BASE_URL/login/',
|
||||
data: authRequest,
|
||||
fromJson: AuthResponseModel.fromJson,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
return response?.data;
|
||||
return res.data;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CaptchaResponseModel?> captcha() async {
|
||||
final response = await safeCall<CaptchaResponseModel?>(
|
||||
call: () async {
|
||||
var res = await _httpClient.post<CaptchaResponseModel?>(
|
||||
'captcha/',
|
||||
fromJson: CaptchaResponseModel.fromJson,
|
||||
);
|
||||
return res.data;
|
||||
},
|
||||
onSuccess: (response) {
|
||||
return response;
|
||||
},
|
||||
onError: (error, trace) {
|
||||
throw Exception('Error during captcha : $error');
|
||||
},
|
||||
var res = await _httpClient.post<CaptchaResponseModel?>(
|
||||
'captcha/',
|
||||
fromJson: CaptchaResponseModel.fromJson,
|
||||
);
|
||||
return response;
|
||||
return res.data;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AuthResponseModel?> loginWithRefreshToken({
|
||||
required Map<String, dynamic> authRequest,
|
||||
}) async {
|
||||
final response = await safeCall<DioResponse<AuthResponseModel>>(
|
||||
call:
|
||||
() async => await _httpClient.post<AuthResponseModel>(
|
||||
'$_BASE_URL/login/',
|
||||
data: authRequest,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
),
|
||||
onSuccess: (response) {
|
||||
iLog(response);
|
||||
},
|
||||
onError: (error, trace) {
|
||||
throw Exception('Error during sign in: $error');
|
||||
},
|
||||
var res = await _httpClient.post<AuthResponseModel>(
|
||||
'$_BASE_URL/login/',
|
||||
data: authRequest,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
return response?.data;
|
||||
return res.data;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -82,20 +52,11 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
|
||||
@override
|
||||
Future<bool> hasAuthenticated() async {
|
||||
final response = await safeCall<DioResponse<bool>>(
|
||||
call:
|
||||
() async => await _httpClient.get<bool>(
|
||||
'$_BASE_URL/login/',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
),
|
||||
onSuccess: (response) {
|
||||
iLog(response);
|
||||
},
|
||||
onError: (error, trace) {
|
||||
throw Exception('Error during sign in: $error');
|
||||
},
|
||||
final response = await _httpClient.get<bool>(
|
||||
'$_BASE_URL/login/',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
return response?.data ?? false;
|
||||
return response.data ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_core/injection/di.dart';
|
||||
|
||||
|
||||
class TokenStorageService extends GetxService {
|
||||
static const String _boxName = 'secureBox';
|
||||
|
||||
@@ -2,7 +2,12 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_auth/auth.dart';
|
||||
import 'package:rasadyar_auth/data/common/dio_error_handler.dart';
|
||||
import 'package:rasadyar_auth/data/models/request/login_request/login_request_model.dart';
|
||||
import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart';
|
||||
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
||||
import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
enum AuthType { useAndPass, otp }
|
||||
@@ -12,7 +17,8 @@ enum AuthStatus { init }
|
||||
enum OtpStatus { init, sent, verified, reSend }
|
||||
|
||||
class AuthLogic extends GetxController {
|
||||
Rx<GlobalKey<FormState>> formKey = GlobalKey<FormState>().obs;
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
Rx<GlobalKey<FormState>> formKeyOtp = GlobalKey<FormState>().obs;
|
||||
Rx<GlobalKey<FormState>> formKeySentOtp = GlobalKey<FormState>().obs;
|
||||
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
|
||||
@@ -21,11 +27,12 @@ class AuthLogic extends GetxController {
|
||||
TextEditingController().obs;
|
||||
Rx<TextEditingController> otpCodeController = TextEditingController().obs;
|
||||
|
||||
var captchaController = Get.find<CaptchaWidgetLogic>();
|
||||
|
||||
RxnString phoneNumber = RxnString(null);
|
||||
RxnString password = RxnString(null);
|
||||
RxBool isOnError = false.obs;
|
||||
RxBool hidePassword = true.obs;
|
||||
RxBool isLoading = false.obs;
|
||||
TokenStorageService tokenStorageService = diAuth.get<TokenStorageService>();
|
||||
|
||||
Rx<AuthType> authType = AuthType.useAndPass.obs;
|
||||
Rx<AuthStatus> authStatus = AuthStatus.init.obs;
|
||||
Rx<OtpStatus> otpStatus = OtpStatus.init.obs;
|
||||
@@ -61,7 +68,7 @@ class AuthLogic extends GetxController {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
tokenStorageService.init();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -76,5 +83,46 @@ class AuthLogic extends GetxController {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
bool _isFormValid() {
|
||||
final isCaptchaValid =
|
||||
captchaController.formKey.currentState?.validate() ?? false;
|
||||
final isFormValid = formKey.currentState?.validate() ?? false;
|
||||
return isCaptchaValid && isFormValid;
|
||||
}
|
||||
|
||||
LoginRequestModel _buildLoginRequest() {
|
||||
final phone = phoneNumberController.value.text;
|
||||
final pass = passwordController.value.text;
|
||||
final code = captchaController.textController.value.text;
|
||||
final key = captchaController.captchaKey.value;
|
||||
|
||||
return LoginRequestModel.createWithCaptcha(
|
||||
username: phone,
|
||||
password: pass,
|
||||
captchaCode: code,
|
||||
captchaKey: key!,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submitLoginForm() async {
|
||||
if (!_isFormValid()) return;
|
||||
|
||||
final loginRequestModel = _buildLoginRequest();
|
||||
isLoading.value = true;
|
||||
await safeCall<AuthResponseModel?>(
|
||||
call: () => authRepository.login(authRequest: loginRequestModel.toJson()),
|
||||
onSuccess: (result) async{
|
||||
await tokenStorageService.saveRefreshToken(result!.refresh!);
|
||||
await tokenStorageService.saveAccessToken(result!.access!);
|
||||
//Get.offAndToNamed(Routes.home);
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
if (error is DioException) {
|
||||
diAuth.get<DioErrorHandler>().handle(error);
|
||||
}
|
||||
captchaController.getCaptcha();
|
||||
},
|
||||
);
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_auth/presentation/widget/captcha/view.dart';
|
||||
import 'package:rasadyar_auth/presentation/widget/clear_button.dart';
|
||||
import 'package:rasadyar_auth/presentation/widget/logo_widget.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
@@ -17,11 +17,11 @@ class AuthPage extends GetView<AuthLogic> {
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 80),
|
||||
logoWidget(),
|
||||
LogoWidget(),
|
||||
ObxValue((types) {
|
||||
switch (types.value) {
|
||||
case AuthType.otp:
|
||||
//return otpForm();
|
||||
//return otpForm();
|
||||
case AuthType.useAndPass:
|
||||
return useAndPassFrom();
|
||||
}
|
||||
@@ -87,180 +87,114 @@ class AuthPage extends GetView<AuthLogic> {
|
||||
}
|
||||
|
||||
Widget useAndPassFrom() {
|
||||
return ObxValue((data) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50),
|
||||
child: Form(
|
||||
key: data.value,
|
||||
child: Column(
|
||||
children: [
|
||||
ObxValue((phoneController) {
|
||||
return TextFormField(
|
||||
controller: controller.phoneNumberController.value,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
),
|
||||
labelText: 'نام کاربری',
|
||||
labelStyle: AppFonts.yekan13,
|
||||
errorStyle: AppFonts.yekan13.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
|
||||
prefixIconConstraints: BoxConstraints(
|
||||
maxHeight: 40,
|
||||
minHeight: 40,
|
||||
maxWidth: 40,
|
||||
minWidth: 40,
|
||||
),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 6, 8),
|
||||
child: vecWidget(Assets.vecCallSvg),
|
||||
),
|
||||
suffix:
|
||||
phoneController.value.text.trim().isNotEmpty
|
||||
? clearButton(() {
|
||||
phoneController.value.clear();
|
||||
phoneController.refresh();
|
||||
})
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: false,
|
||||
signed: false,
|
||||
),
|
||||
|
||||
maxLines: 1,
|
||||
maxLength: 11,
|
||||
onChanged: (value) {
|
||||
if (controller.isOnError.value) {
|
||||
controller.isOnError.value = !controller.isOnError.value;
|
||||
data.value.currentState?.reset();
|
||||
|
||||
data.refresh();
|
||||
phoneController.value.text = value;
|
||||
}
|
||||
phoneController.refresh();
|
||||
},
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) {
|
||||
if (value == null) {
|
||||
return '⚠️ شماره موبایل را وارد کنید';
|
||||
} else if (value.length < 11) {
|
||||
return '⚠️ شماره موبایل باید 11 رقم باشد';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
);
|
||||
}, controller.phoneNumberController),
|
||||
|
||||
SizedBox(height: 26),
|
||||
|
||||
ObxValue((passwordController) {
|
||||
return TextFormField(
|
||||
controller: passwordController.value,
|
||||
obscureText: controller.hidePassword.value,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
),
|
||||
labelText: 'رمز عبور',
|
||||
labelStyle: AppFonts.yekan13,
|
||||
errorStyle: AppFonts.yekan13.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
|
||||
prefixIconConstraints: BoxConstraints(
|
||||
maxHeight: 34,
|
||||
minHeight: 34,
|
||||
maxWidth: 34,
|
||||
minWidth: 34,
|
||||
),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
|
||||
child: vecWidget(Assets.vecKeySvg),
|
||||
),
|
||||
suffix:
|
||||
passwordController.value.text.trim().isNotEmpty
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
controller.hidePassword.value =
|
||||
!controller.hidePassword.value;
|
||||
},
|
||||
child: Icon(
|
||||
controller.hidePassword.value
|
||||
? CupertinoIcons.eye
|
||||
: CupertinoIcons.eye_slash,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
textInputAction: TextInputAction.done,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
maxLines: 1,
|
||||
onChanged: (value) {
|
||||
if (controller.isOnError.value) {
|
||||
controller.isOnError.value = !controller.isOnError.value;
|
||||
data.value.currentState?.reset();
|
||||
passwordController.value.text = value;
|
||||
}
|
||||
passwordController.refresh();
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '⚠️ رمز عبور را وارد کنید'; // "Please enter the password"
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
);
|
||||
}, controller.passwordController),
|
||||
SizedBox(height: 26),
|
||||
|
||||
CaptchaWidget(),
|
||||
|
||||
SizedBox(height: 23),
|
||||
RElevated(
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50),
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
ObxValue(
|
||||
(phoneController) => RTextField(
|
||||
label: 'نام کاربری',
|
||||
maxLength: 11,
|
||||
maxLines: 1,
|
||||
controller: phoneController.value,
|
||||
keyboardType: TextInputType.number,
|
||||
initText: phoneController.value.text,
|
||||
onChanged: (value) {
|
||||
phoneController.value.text = value;
|
||||
phoneController.refresh();
|
||||
},
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 6, 8),
|
||||
child: vecWidget(Assets.vecCallSvg),
|
||||
),
|
||||
suffixIcon:
|
||||
phoneController.value.text.trim().isNotEmpty
|
||||
? clearButton(() {
|
||||
phoneController.value.clear();
|
||||
phoneController.refresh();
|
||||
})
|
||||
: null,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '⚠️ شماره موبایل را وارد کنید';
|
||||
}
|
||||
/*else if (value.length < 11) {
|
||||
return '⚠️ شماره موبایل باید 11 رقم باشد';
|
||||
}*/
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
errorStyle: AppFonts.yekan13.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
labelStyle: AppFonts.yekan13,
|
||||
boxConstraints: const BoxConstraints(
|
||||
maxHeight: 40,
|
||||
minHeight: 40,
|
||||
maxWidth: 40,
|
||||
minWidth: 40,
|
||||
),
|
||||
),
|
||||
controller.phoneNumberController,
|
||||
),
|
||||
const SizedBox(height: 26),
|
||||
ObxValue(
|
||||
(passwordController) => RTextField(
|
||||
label: 'رمز عبور',
|
||||
filled: false,
|
||||
controller: passwordController.value,
|
||||
variant: RTextFieldVariant.password,
|
||||
initText: passwordController.value.text,
|
||||
onChanged: (value) {
|
||||
passwordController.refresh();
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '⚠️ رمز عبور را وارد کنید';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
errorStyle: AppFonts.yekan13.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
),
|
||||
labelStyle: AppFonts.yekan13,
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
|
||||
child: vecWidget(Assets.vecKeySvg),
|
||||
),
|
||||
boxConstraints: const BoxConstraints(
|
||||
maxHeight: 34,
|
||||
minHeight: 34,
|
||||
maxWidth: 34,
|
||||
minWidth: 34,
|
||||
),
|
||||
),
|
||||
controller.passwordController,
|
||||
),
|
||||
SizedBox(height: 26),
|
||||
CaptchaWidget(),
|
||||
SizedBox(height: 23),
|
||||
ObxValue((data) {
|
||||
return RElevated(
|
||||
text: 'ورود',
|
||||
isLoading: data.value,
|
||||
onPressed: () async {
|
||||
Jalali? picked = await showPersianDatePicker(
|
||||
context: Get.context!,
|
||||
|
||||
initialDate: Jalali.now(),
|
||||
firstDate: Jalali(1385, 8),
|
||||
lastDate: Jalali(1450, 9),
|
||||
initialEntryMode: PersianDatePickerEntryMode.calendarOnly,
|
||||
initialDatePickerMode: PersianDatePickerMode.year,
|
||||
);
|
||||
await controller.submitLoginForm();
|
||||
},
|
||||
width: Get.width,
|
||||
height: 48,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}, controller.isLoading),
|
||||
],
|
||||
),
|
||||
);
|
||||
}, controller.formKey);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/* Widget otpForm() {
|
||||
return ObxValue((status) {
|
||||
switch (status.value) {
|
||||
case OtpStatus.init:
|
||||
return sendCodeForm();
|
||||
case OtpStatus.sent:
|
||||
case OtpStatus.verified:
|
||||
case OtpStatus.reSend:
|
||||
return confirmCodeForm();
|
||||
}
|
||||
}, controller.otpStatus);
|
||||
}*/
|
||||
|
||||
/*
|
||||
Widget sendCodeForm() {
|
||||
return ObxValue((data) {
|
||||
return Form(
|
||||
@@ -494,18 +428,5 @@ class AuthPage extends GetView<AuthLogic> {
|
||||
),
|
||||
);
|
||||
}, controller.formKeySentOtp);
|
||||
}
|
||||
|
||||
Widget logoWidget() {
|
||||
return Column(
|
||||
children: [
|
||||
Row(),
|
||||
Image.asset(Assets.imagesInnerSplash, width: 120, height: 120),
|
||||
Text(
|
||||
'سامانه رصدیار',
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyNormal),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import '../pages/auth/logic.dart';
|
||||
@@ -14,15 +15,16 @@ sealed class AuthPages {
|
||||
page: () => AuthPage(),
|
||||
binding: BindingsBuilder(() {
|
||||
Get.lazyPut(() => AuthLogic());
|
||||
Get.lazyPut(() => CaptchaWidgetLogic());
|
||||
}),
|
||||
),
|
||||
|
||||
|
||||
GetPage(
|
||||
name: AuthPaths.auth,
|
||||
page: () => AuthPage(),
|
||||
binding: BindingsBuilder(() {
|
||||
Get.lazyPut(() => AuthLogic());
|
||||
Get.lazyPut(() => CaptchaWidgetLogic());
|
||||
}),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -6,7 +6,8 @@ import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class CaptchaWidgetLogic extends GetxController
|
||||
with StateMixin<CaptchaResponseModel> {
|
||||
TextEditingController textController = TextEditingController();
|
||||
Rx<TextEditingController> textController = TextEditingController().obs;
|
||||
RxnString captchaKey = RxnString();
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
AuthRepositoryImpl authRepository = diAuth.get<AuthRepositoryImpl>();
|
||||
|
||||
@@ -17,17 +18,19 @@ class CaptchaWidgetLogic extends GetxController
|
||||
getCaptcha();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
textController.value.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getCaptcha() async {
|
||||
change(null, status: RxStatus.loading());
|
||||
textController.value.clear();
|
||||
safeCall(
|
||||
call: () async => await authRepository.captcha(),
|
||||
onSuccess: (value) {
|
||||
captchaKey.value = value?.captchaKey;
|
||||
change(value, status: RxStatus.success());
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
|
||||
@@ -8,9 +8,7 @@ import 'package:rasadyar_core/core.dart';
|
||||
import 'logic.dart';
|
||||
|
||||
class CaptchaWidget extends GetView<CaptchaWidgetLogic> {
|
||||
CaptchaWidget({super.key});
|
||||
|
||||
final CaptchaWidgetLogic logic = Get.put(CaptchaWidgetLogic());
|
||||
const CaptchaWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -19,7 +17,7 @@ class CaptchaWidget extends GetView<CaptchaWidgetLogic> {
|
||||
children: [
|
||||
Container(
|
||||
width: 135,
|
||||
height: 48,
|
||||
height: 50,
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.whiteNormalHover,
|
||||
@@ -27,75 +25,62 @@ class CaptchaWidget extends GetView<CaptchaWidgetLogic> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: controller.obx(
|
||||
(state) => Image.memory(base64Decode(state?.captchaImage??''),fit: BoxFit.cover,),
|
||||
(state) => Image.memory(
|
||||
base64Decode(state?.captchaImage ?? ''),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
onLoading: const Center(
|
||||
child: CupertinoActivityIndicator(
|
||||
color: AppColor.blueNormal,
|
||||
),
|
||||
child: CupertinoActivityIndicator(color: AppColor.blueNormal),
|
||||
),
|
||||
onError: (error) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
'خطا در بارگذاری کد امنیتی',
|
||||
style: AppFonts.yekan13,));
|
||||
child: Text(
|
||||
'خطا در بارگذاری کد امنیتی',
|
||||
style: AppFonts.yekan13,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: controller.getCaptcha,
|
||||
icon: Icon(CupertinoIcons.refresh, size: 16),
|
||||
GestureDetector(
|
||||
onTap: controller.getCaptcha,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Icon(CupertinoIcons.refresh, size: 20),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: TextFormField(
|
||||
controller: controller.textController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gapPadding: 11,
|
||||
autovalidateMode: AutovalidateMode.disabled,
|
||||
child: ObxValue((data) {
|
||||
return RTextField(
|
||||
label: 'کد امنیتی',
|
||||
controller: data.value,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: false,
|
||||
signed: false,
|
||||
),
|
||||
labelText: 'کد امنیتی',
|
||||
labelStyle: AppFonts.yekan13,
|
||||
errorStyle: AppFonts.yekan10.copyWith(
|
||||
color: AppColor.redNormal,
|
||||
fontSize: 8,
|
||||
),
|
||||
suffixIconConstraints: BoxConstraints(
|
||||
maxHeight: 24,
|
||||
minHeight: 24,
|
||||
maxWidth: 24,
|
||||
minWidth: 24,
|
||||
),
|
||||
suffix:
|
||||
controller.textController.text
|
||||
.trim()
|
||||
.isNotEmpty
|
||||
? clearButton(() => controller.textController.clear())
|
||||
: null,
|
||||
counterText: '',
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: false,
|
||||
signed: false,
|
||||
),
|
||||
maxLines: 1,
|
||||
maxLength: 6,
|
||||
onChanged: (value) {},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'کد امنیتی را وارد کنید';
|
||||
}
|
||||
/*if (value != controller.captchaCode.toString()) {
|
||||
return '⚠️کد امنیتی وارد شده اشتباه است';
|
||||
}*/
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
),
|
||||
maxLines: 1,
|
||||
maxLength: 6,
|
||||
suffixIcon:
|
||||
(data.value.text.trim().isNotEmpty ?? false)
|
||||
? clearButton(
|
||||
() => controller.textController.value.clear(),
|
||||
)
|
||||
: null,
|
||||
|
||||
onSubmitted: (data) {},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'کد امنیتی را وارد کنید';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
style: AppFonts.yekan13,
|
||||
);
|
||||
}, controller.textController),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,6 +3,6 @@ import 'package:flutter/cupertino.dart';
|
||||
Widget clearButton(VoidCallback onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Icon(CupertinoIcons.multiply_circle, size: 24),
|
||||
child: Icon(CupertinoIcons.multiply_circle, size: 20),
|
||||
);
|
||||
}
|
||||
|
||||
20
packages/auth/lib/presentation/widget/logo_widget.dart
Normal file
20
packages/auth/lib/presentation/widget/logo_widget.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class LogoWidget extends StatelessWidget {
|
||||
const LogoWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(),
|
||||
Image.asset(Assets.imagesInnerSplash, width: 120, height: 120),
|
||||
Text(
|
||||
'سامانه رصدیار',
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyNormal),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user