fix : splash animation
feat : auth with password chore : app Architecture
This commit is contained in:
@@ -36,9 +36,7 @@ class _RElevatedState extends State<RElevated> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {});
|
||||
},
|
||||
onPressed: widget.onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.backgroundColor ?? AppColor.blueNormal,
|
||||
foregroundColor: widget.foregroundColor ?? Colors.white,
|
||||
|
||||
266
lib/presentation/widget/captcha/captcha_widget.dart
Normal file
266
lib/presentation/widget/captcha/captcha_widget.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_app/presentation/common/app_color.dart';
|
||||
import 'package:rasadyar_app/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.yekan24Regular,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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.yekan13Regular,
|
||||
errorStyle: AppFonts.yekan10Regular.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.yekan13Regular,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget clearButton(VoidCallback onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Icon(CupertinoIcons.multiply_circle, size: 18),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,25 @@ SvgPicture vecWidget(
|
||||
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null,
|
||||
);
|
||||
}
|
||||
|
||||
SvgPicture svgWidget(
|
||||
String assets, {
|
||||
double? width,
|
||||
double? height,
|
||||
BoxFit? fit,
|
||||
Color? color,
|
||||
}) {
|
||||
return SvgPicture.asset(
|
||||
assets,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
colorFilter:
|
||||
color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget vecWidget2(
|
||||
String assets, {
|
||||
double? width,
|
||||
|
||||
Reference in New Issue
Block a user