feat : captcha widget.dart
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
enum ApiEnvironment {
|
enum ApiEnvironment {
|
||||||
dam(url: 'https://api.dam.rasadyar.net');
|
dam(url: 'https://api.dam.rasadyar.net/');
|
||||||
|
|
||||||
const ApiEnvironment({required this.url});
|
const ApiEnvironment({required this.url});
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ class DioRemoteManager {
|
|||||||
DioRemote? _currentClient;
|
DioRemote? _currentClient;
|
||||||
ApiEnvironment? _currentEnv;
|
ApiEnvironment? _currentEnv;
|
||||||
|
|
||||||
DioRemote setEnvironment(ApiEnvironment env) {
|
Future<DioRemote> setEnvironment([
|
||||||
|
ApiEnvironment env = ApiEnvironment.dam,
|
||||||
|
]) async {
|
||||||
if (_currentEnv != env) {
|
if (_currentEnv != env) {
|
||||||
_currentClient = DioRemote(env.baseUrl);
|
_currentClient = DioRemote(env.baseUrl);
|
||||||
|
await _currentClient?.init();
|
||||||
_currentEnv = env;
|
_currentEnv = env;
|
||||||
}
|
}
|
||||||
return _currentClient!;
|
return _currentClient!;
|
||||||
@@ -30,10 +33,10 @@ class DioRemoteManager {
|
|||||||
Future<void> switchAuthEnvironment(ApiEnvironment env) async {
|
Future<void> switchAuthEnvironment(ApiEnvironment env) async {
|
||||||
final manager = diAuth.get<DioRemoteManager>();
|
final manager = diAuth.get<DioRemoteManager>();
|
||||||
|
|
||||||
final dioRemote = manager.setEnvironment(env);
|
final dioRemote = await manager.setEnvironment(env);
|
||||||
|
|
||||||
if (diAuth.isRegistered<AuthRepositoryImpl>()) {
|
if (diAuth.isRegistered<AuthRepositoryImpl>()) {
|
||||||
await diAuth.unregister<AuthRepositoryImpl>();
|
await diAuth.unregister<AuthRepositoryImpl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
diAuth.registerLazySingleton<AuthRepositoryImpl>(
|
diAuth.registerLazySingleton<AuthRepositoryImpl>(
|
||||||
|
|||||||
@@ -12,14 +12,10 @@ Future<void> setupAuthDI() async {
|
|||||||
diAuth.registerLazySingleton(() => DioRemoteManager());
|
diAuth.registerLazySingleton(() => DioRemoteManager());
|
||||||
|
|
||||||
final manager = diAuth.get<DioRemoteManager>();
|
final manager = diAuth.get<DioRemoteManager>();
|
||||||
final dioRemote = manager.setEnvironment(ApiEnvironment.dam);
|
final dioRemote = await manager.setEnvironment(ApiEnvironment.dam);
|
||||||
|
diAuth.registerCachedFactory<AuthRepositoryImpl>(
|
||||||
diAuth.registerLazySingleton<AuthRepositoryImpl>(
|
|
||||||
() => AuthRepositoryImpl(dioRemote),
|
() => AuthRepositoryImpl(dioRemote),
|
||||||
);
|
);
|
||||||
diAuth.registerLazySingleton(() => AuthService());
|
diAuth.registerLazySingleton(() => AuthService());
|
||||||
diAuth.registerLazySingleton(() => TokenStorageService());
|
diAuth.registerLazySingleton(() => TokenStorageService());
|
||||||
|
|
||||||
//hive
|
|
||||||
//await diAuth.registerCachedFactoryAsync(() async=>await ,)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,17 +34,18 @@ class AuthRepositoryImpl implements AuthRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CaptchaResponseModel?> captcha() async {
|
Future<CaptchaResponseModel?> captcha() async {
|
||||||
final response = await safeCall<DioResponse<CaptchaResponseModel>>(
|
final response = await safeCall(
|
||||||
call:
|
call:
|
||||||
() async => await _httpClient.post<CaptchaResponseModel>(
|
() async => await _httpClient.post<CaptchaResponseModel>(
|
||||||
'$_BASE_URL/login/',
|
'captcha/',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
fromJson: CaptchaResponseModel.fromJson
|
||||||
),
|
),
|
||||||
onSuccess: (response) {
|
onSuccess: (response) {
|
||||||
iLog(response);
|
iLog(response);
|
||||||
},
|
},
|
||||||
onError: (error, trace) {
|
onError: (error, trace) {
|
||||||
throw Exception('Error during sign in: $error');
|
throw Exception('Error during captcha : $error');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rasadyar_auth/auth.dart';
|
||||||
|
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
enum AuthType { useAndPass, otp }
|
enum AuthType { useAndPass, otp }
|
||||||
@@ -18,8 +20,7 @@ class AuthLogic extends GetxController {
|
|||||||
Rx<TextEditingController> phoneOtpNumberController =
|
Rx<TextEditingController> phoneOtpNumberController =
|
||||||
TextEditingController().obs;
|
TextEditingController().obs;
|
||||||
Rx<TextEditingController> otpCodeController = TextEditingController().obs;
|
Rx<TextEditingController> otpCodeController = TextEditingController().obs;
|
||||||
CaptchaController captchaController = CaptchaController();
|
|
||||||
CaptchaController captchaOtpController = CaptchaController();
|
|
||||||
|
|
||||||
RxnString phoneNumber = RxnString(null);
|
RxnString phoneNumber = RxnString(null);
|
||||||
RxnString password = RxnString(null);
|
RxnString password = RxnString(null);
|
||||||
@@ -32,6 +33,8 @@ class AuthLogic extends GetxController {
|
|||||||
RxInt secondsRemaining = 120.obs;
|
RxInt secondsRemaining = 120.obs;
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
|
AuthRepositoryImpl authRepository = diAuth.get<AuthRepositoryImpl>();
|
||||||
|
|
||||||
void startTimer() {
|
void startTimer() {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
secondsRemaining.value = 120;
|
secondsRemaining.value = 120;
|
||||||
@@ -55,6 +58,12 @@ class AuthLogic extends GetxController {
|
|||||||
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
// TODO: implement onReady
|
// TODO: implement onReady
|
||||||
@@ -66,4 +75,6 @@ class AuthLogic extends GetxController {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.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_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
import 'logic.dart';
|
import 'logic.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AuthPage extends GetView<AuthLogic> {
|
class AuthPage extends GetView<AuthLogic> {
|
||||||
const AuthPage({super.key});
|
const AuthPage({super.key});
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
ObxValue((types) {
|
ObxValue((types) {
|
||||||
switch (types.value) {
|
switch (types.value) {
|
||||||
case AuthType.otp:
|
case AuthType.otp:
|
||||||
return otpForm();
|
//return otpForm();
|
||||||
case AuthType.useAndPass:
|
case AuthType.useAndPass:
|
||||||
return useAndPassFrom();
|
return useAndPassFrom();
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
}, controller.passwordController),
|
}, controller.passwordController),
|
||||||
SizedBox(height: 26),
|
SizedBox(height: 26),
|
||||||
|
|
||||||
CaptchaWidget(controller: controller.captchaController),
|
CaptchaWidget(),
|
||||||
|
|
||||||
SizedBox(height: 23),
|
SizedBox(height: 23),
|
||||||
RElevated(
|
RElevated(
|
||||||
@@ -237,9 +237,6 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
initialEntryMode: PersianDatePickerEntryMode.calendarOnly,
|
initialEntryMode: PersianDatePickerEntryMode.calendarOnly,
|
||||||
initialDatePickerMode: PersianDatePickerMode.year,
|
initialDatePickerMode: PersianDatePickerMode.year,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.value.currentState?.validate() == true &&
|
|
||||||
controller.captchaController.validate()) {}
|
|
||||||
},
|
},
|
||||||
width: Get.width,
|
width: Get.width,
|
||||||
height: 48,
|
height: 48,
|
||||||
@@ -251,7 +248,7 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
}, controller.formKey);
|
}, controller.formKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget otpForm() {
|
/* Widget otpForm() {
|
||||||
return ObxValue((status) {
|
return ObxValue((status) {
|
||||||
switch (status.value) {
|
switch (status.value) {
|
||||||
case OtpStatus.init:
|
case OtpStatus.init:
|
||||||
@@ -262,7 +259,7 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
return confirmCodeForm();
|
return confirmCodeForm();
|
||||||
}
|
}
|
||||||
}, controller.otpStatus);
|
}, controller.otpStatus);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
Widget sendCodeForm() {
|
Widget sendCodeForm() {
|
||||||
return ObxValue((data) {
|
return ObxValue((data) {
|
||||||
@@ -335,14 +332,13 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
|
|
||||||
SizedBox(height: 26),
|
SizedBox(height: 26),
|
||||||
|
|
||||||
CaptchaWidget(controller: controller.captchaOtpController),
|
CaptchaWidget(),
|
||||||
|
|
||||||
SizedBox(height: 23),
|
SizedBox(height: 23),
|
||||||
RElevated(
|
RElevated(
|
||||||
text: 'ارسال رمز یکبار مصرف',
|
text: 'ارسال رمز یکبار مصرف',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (data.value.currentState?.validate() == true &&
|
if (data.value.currentState?.validate() == true) {
|
||||||
controller.captchaOtpController.validate()) {
|
|
||||||
controller.otpStatus.value = OtpStatus.sent;
|
controller.otpStatus.value = OtpStatus.sent;
|
||||||
controller.startTimer();
|
controller.startTimer();
|
||||||
}
|
}
|
||||||
@@ -473,7 +469,6 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
TapGestureRecognizer()
|
TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
controller.otpStatus.value = OtpStatus.init;
|
controller.otpStatus.value = OtpStatus.init;
|
||||||
controller.captchaOtpController.clear();
|
|
||||||
},
|
},
|
||||||
text: ' ویرایش',
|
text: ' ویرایش',
|
||||||
style: AppFonts.yekan14.copyWith(
|
style: AppFonts.yekan14.copyWith(
|
||||||
@@ -489,8 +484,7 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
text: 'ورود',
|
text: 'ورود',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (controller.formKeyOtp.value.currentState?.validate() ==
|
if (controller.formKeyOtp.value.currentState?.validate() ==
|
||||||
true &&
|
true) {}
|
||||||
controller.captchaOtpController.validate()) {}
|
|
||||||
},
|
},
|
||||||
width: Get.width,
|
width: Get.width,
|
||||||
height: 48,
|
height: 48,
|
||||||
@@ -514,11 +508,4 @@ class AuthPage extends GetView<AuthLogic> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget clearButton(VoidCallback onTap) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Icon(CupertinoIcons.multiply_circle, size: 24),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
packages/auth/lib/presentation/widget/clear_button.dart
Normal file
8
packages/auth/lib/presentation/widget/clear_button.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
Widget clearButton(VoidCallback onTap) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Icon(CupertinoIcons.multiply_circle, size: 24),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||||
import 'package:rasadyar_core/infrastructure/remote/interfaces/i_form_data.dart';
|
import 'package:rasadyar_core/infrastructure/remote/interfaces/i_form_data.dart';
|
||||||
|
|
||||||
@@ -15,7 +16,9 @@ class DioRemote implements IHttpClient {
|
|||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
final dio = Dio(BaseOptions(baseUrl: baseUrl));
|
final dio = Dio(BaseOptions(baseUrl: baseUrl));
|
||||||
dio.interceptors.add(PrettyDioLogger());
|
if (kDebugMode) {
|
||||||
|
dio.interceptors.add(PrettyDioLogger());
|
||||||
|
}
|
||||||
_dio = dio;
|
_dio = dio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +43,7 @@ class DioRemote implements IHttpClient {
|
|||||||
String path, {
|
String path, {
|
||||||
dynamic data,
|
dynamic data,
|
||||||
Map<String, dynamic>? queryParameters,
|
Map<String, dynamic>? queryParameters,
|
||||||
|
T Function(Map<String, dynamic> json)? fromJson,
|
||||||
Map<String, String>? headers,
|
Map<String, String>? headers,
|
||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
ProgressCallback? onReceiveProgress,
|
ProgressCallback? onReceiveProgress,
|
||||||
@@ -52,6 +56,15 @@ class DioRemote implements IHttpClient {
|
|||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
onReceiveProgress: onReceiveProgress,
|
onReceiveProgress: onReceiveProgress,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (fromJson != null) {
|
||||||
|
final rawData = response.data;
|
||||||
|
final parsedData =
|
||||||
|
rawData is Map<String, dynamic> ? fromJson(rawData) : null;
|
||||||
|
response.data = parsedData;
|
||||||
|
return DioResponse<T>(response);
|
||||||
|
}
|
||||||
|
|
||||||
return DioResponse<T>(response);
|
return DioResponse<T>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:rasadyar_core/presentation/common/app_color.dart';
|
|
||||||
import 'package:rasadyar_core/presentation/common/app_fonts.dart';
|
|
||||||
|
|
||||||
class CaptchaController {
|
|
||||||
int? captchaCode;
|
|
||||||
TextEditingController textController = TextEditingController();
|
|
||||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
late Function() refreshCaptcha;
|
|
||||||
|
|
||||||
bool validate() {
|
|
||||||
if (formKey.currentState?.validate() == true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get enteredText => textController.text;
|
|
||||||
|
|
||||||
bool isCorrect() {
|
|
||||||
return textController.text == captchaCode.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
textController.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RandomLinePainter extends CustomPainter {
|
|
||||||
final Random random = Random();
|
|
||||||
final Paint linePaint;
|
|
||||||
final List<Offset> points;
|
|
||||||
|
|
||||||
RandomLinePainter({
|
|
||||||
required this.points,
|
|
||||||
required Color lineColor,
|
|
||||||
double strokeWidth = 2.0,
|
|
||||||
}) : linePaint =
|
|
||||||
Paint()
|
|
||||||
..color = lineColor
|
|
||||||
..strokeWidth = strokeWidth
|
|
||||||
..strokeCap = StrokeCap.round
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final path = Path();
|
|
||||||
|
|
||||||
if (points.isNotEmpty) {
|
|
||||||
path.moveTo(points[0].dx, points[0].dy);
|
|
||||||
|
|
||||||
for (int i = 1; i < points.length; i++) {
|
|
||||||
path.lineTo(points[i].dx, points[i].dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.drawPath(path, linePaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(RandomLinePainter oldDelegate) => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CaptchaWidget extends StatefulWidget {
|
|
||||||
final CaptchaController controller;
|
|
||||||
final bool autoValidateMode;
|
|
||||||
|
|
||||||
const CaptchaWidget({
|
|
||||||
required this.controller,
|
|
||||||
this.autoValidateMode = false,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_CaptchaWidgetState createState() => _CaptchaWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CaptchaWidgetState extends State<CaptchaWidget> {
|
|
||||||
late List<Offset> points;
|
|
||||||
late List<Offset> points1;
|
|
||||||
late List<Offset> points2;
|
|
||||||
bool isOnError = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
generateLines();
|
|
||||||
getRandomSixDigitNumber();
|
|
||||||
|
|
||||||
// Set the refresh function in the controller
|
|
||||||
widget.controller.refreshCaptcha = () {
|
|
||||||
getRandomSixDigitNumber();
|
|
||||||
generateLines();
|
|
||||||
setState(() {});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void generateLines() {
|
|
||||||
points = generateRandomLine();
|
|
||||||
points1 = generateRandomLine();
|
|
||||||
points2 = generateRandomLine();
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Offset> generateRandomLine() {
|
|
||||||
final random = Random();
|
|
||||||
int pointCount = random.nextInt(10) + 5;
|
|
||||||
List<Offset> points = [];
|
|
||||||
|
|
||||||
double previousY = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < pointCount; i++) {
|
|
||||||
double x = (i / (pointCount - 1)) * 135;
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
previousY = 24;
|
|
||||||
} else {
|
|
||||||
double change = (random.nextDouble() * 20) - 10;
|
|
||||||
previousY = max(5, min(43, previousY + change));
|
|
||||||
}
|
|
||||||
|
|
||||||
points.add(Offset(x, previousY));
|
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getRandomSixDigitNumber() {
|
|
||||||
final random = Random();
|
|
||||||
widget.controller.captchaCode = random.nextInt(900000) + 100000;
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 135,
|
|
||||||
height: 48,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.whiteNormalHover,
|
|
||||||
border: Border.all(color: Colors.grey.shade300),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
alignment: AlignmentDirectional.center,
|
|
||||||
children: [
|
|
||||||
CustomPaint(
|
|
||||||
painter: RandomLinePainter(
|
|
||||||
points: points,
|
|
||||||
lineColor: Colors.blue,
|
|
||||||
strokeWidth: 1.0,
|
|
||||||
),
|
|
||||||
size: const Size(double.infinity, double.infinity),
|
|
||||||
),
|
|
||||||
CustomPaint(
|
|
||||||
painter: RandomLinePainter(
|
|
||||||
points: points1,
|
|
||||||
lineColor: Colors.green,
|
|
||||||
strokeWidth: 1.0,
|
|
||||||
),
|
|
||||||
size: const Size(double.infinity, double.infinity),
|
|
||||||
),
|
|
||||||
CustomPaint(
|
|
||||||
painter: RandomLinePainter(
|
|
||||||
points: points2,
|
|
||||||
lineColor: Colors.red,
|
|
||||||
strokeWidth: 1.0,
|
|
||||||
),
|
|
||||||
size: const Size(double.infinity, double.infinity),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.controller.captchaCode.toString(),
|
|
||||||
style: AppFonts.yekan24,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
onPressed: widget.controller.refreshCaptcha,
|
|
||||||
icon: Icon(CupertinoIcons.refresh, size: 16),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Form(
|
|
||||||
key: widget.controller.formKey,
|
|
||||||
autovalidateMode:
|
|
||||||
widget.autoValidateMode
|
|
||||||
? AutovalidateMode.onUserInteraction
|
|
||||||
: AutovalidateMode.disabled,
|
|
||||||
child: TextFormField(
|
|
||||||
controller: widget.controller.textController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
gapPadding: 11,
|
|
||||||
),
|
|
||||||
labelText: 'کد امنیتی',
|
|
||||||
labelStyle: AppFonts.yekan13,
|
|
||||||
errorStyle: AppFonts.yekan10.copyWith(
|
|
||||||
color: AppColor.redNormal,
|
|
||||||
fontSize: 8,
|
|
||||||
),
|
|
||||||
suffixIconConstraints: BoxConstraints(
|
|
||||||
maxHeight: 24,
|
|
||||||
minHeight: 24,
|
|
||||||
maxWidth: 24,
|
|
||||||
minWidth: 24,
|
|
||||||
),
|
|
||||||
|
|
||||||
suffix:
|
|
||||||
widget.controller.textController.text.trim().isNotEmpty
|
|
||||||
? clearButton(() {
|
|
||||||
widget.controller.textController.clear();
|
|
||||||
setState(() {});
|
|
||||||
})
|
|
||||||
: null,
|
|
||||||
counterText: '',
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
|
||||||
decimal: false,
|
|
||||||
signed: false,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
maxLength: 6,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (isOnError) {
|
|
||||||
isOnError = !isOnError;
|
|
||||||
widget.controller.formKey.currentState?.reset();
|
|
||||||
widget.controller.textController.text = value;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
isOnError = true;
|
|
||||||
return 'کد امنیتی را وارد کنید';
|
|
||||||
}
|
|
||||||
if (value != widget.controller.captchaCode.toString()) {
|
|
||||||
isOnError = true;
|
|
||||||
return '⚠️کد امنیتی وارد شده اشتباه است';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
style: AppFonts.yekan13,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget clearButton(VoidCallback onTap) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Icon(CupertinoIcons.multiply_circle, size: 18),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ export 'buttons/elevated.dart';
|
|||||||
export 'buttons/outline_elevated.dart';
|
export 'buttons/outline_elevated.dart';
|
||||||
export 'buttons/outline_elevated_icon.dart';
|
export 'buttons/outline_elevated_icon.dart';
|
||||||
export 'buttons/text_button.dart';
|
export 'buttons/text_button.dart';
|
||||||
export 'captcha/captcha_widget.dart';
|
|
||||||
export 'draggable_bottom_sheet/draggable_bottom_sheet.dart';
|
export 'draggable_bottom_sheet/draggable_bottom_sheet.dart';
|
||||||
export 'draggable_bottom_sheet/draggable_bottom_sheet_controller.dart';
|
export 'draggable_bottom_sheet/draggable_bottom_sheet_controller.dart';
|
||||||
export 'draggable_bottom_sheet/bottom_sheet_manger.dart';
|
export 'draggable_bottom_sheet/bottom_sheet_manger.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user