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 formKey = GlobalKey(); 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 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 { late List points; late List points1; late List 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 generateRandomLine() { final random = Random(); int pointCount = random.nextInt(10) + 5; List 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), ); } }