new auth page
This commit is contained in:
2025-08-02 12:19:25 +03:30
parent 6040ca9f86
commit fae6703d8d
7 changed files with 120 additions and 79 deletions

View File

@@ -49,6 +49,7 @@ class RTextField extends StatefulWidget {
final TextInputAction? textInputAction; final TextInputAction? textInputAction;
final double? height; final double? height;
final Iterable<String>? autofillHints; final Iterable<String>? autofillHints;
final InputBorder? focusedBorder;
const RTextField({ const RTextField({
super.key, super.key,
@@ -84,6 +85,7 @@ class RTextField extends StatefulWidget {
this.hintStyle, this.hintStyle,
this.labelStyle, this.labelStyle,
this.errorStyle, this.errorStyle,
this.focusedBorder,
// 🎨 Decorations // 🎨 Decorations
this.suffixIcon, this.suffixIcon,
@@ -243,7 +245,7 @@ class _RTextFieldState extends State<RTextField> {
counter: widget.showCounter ? null : const SizedBox(), counter: widget.showCounter ? null : const SizedBox(),
hintStyle: widget.hintStyle, hintStyle: widget.hintStyle,
enabledBorder: widget._inputBorder, enabledBorder: widget._inputBorder,
focusedBorder: widget._inputBorder, focusedBorder: widget.focusedBorder ?? widget._inputBorder,
border: widget._inputBorder, border: widget._inputBorder,
), ),
), ),

View File

@@ -16,9 +16,13 @@ enum AuthStatus { init }
enum OtpStatus { init, sent, verified, reSend } enum OtpStatus { init, sent, verified, reSend }
class AuthLogic extends GetxController { class AuthLogic extends GetxController with GetTickerProviderStateMixin {
GlobalKey<FormState> formKey = GlobalKey<FormState>(); GlobalKey<FormState> formKey = GlobalKey<FormState>();
late AnimationController _textAnimationController;
late Animation<double> textAnimation;
RxBool showCard = false.obs;
Rx<GlobalKey<FormState>> formKeyOtp = GlobalKey<FormState>().obs; Rx<GlobalKey<FormState>> formKeyOtp = GlobalKey<FormState>().obs;
Rx<GlobalKey<FormState>> formKeySentOtp = GlobalKey<FormState>().obs; Rx<GlobalKey<FormState>> formKeySentOtp = GlobalKey<FormState>().obs;
Rx<TextEditingController> usernameController = TextEditingController().obs; Rx<TextEditingController> usernameController = TextEditingController().obs;
@@ -44,6 +48,31 @@ class AuthLogic extends GetxController {
final Module _module = Get.arguments; final Module _module = Get.arguments;
@override
void onInit() {
super.onInit();
_textAnimationController =
AnimationController(vsync: this, duration: const Duration(milliseconds: 1200))
..repeat(reverse: true, count: 2).whenComplete(() {
showCard.value = true;
});
textAnimation = CurvedAnimation(parent: _textAnimationController, curve: Curves.easeInOut);
}
@override
void onReady() {
super.onReady();
//_textAnimationController.forward();
}
@override
void onClose() {
_timer?.cancel();
super.onClose();
}
void startTimer() { void startTimer() {
_timer?.cancel(); _timer?.cancel();
secondsRemaining.value = 120; secondsRemaining.value = 120;
@@ -67,18 +96,6 @@ 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 onReady() {
super.onReady();
iLog('module selected : ${_module.toString()}');
}
@override
void onClose() {
_timer?.cancel();
super.onClose();
}
bool _isFormValid() { bool _isFormValid() {
final isCaptchaValid = captchaController.formKey.currentState?.validate() ?? false; final isCaptchaValid = captchaController.formKey.currentState?.validate() ?? false;
final isFormValid = formKey.currentState?.validate() ?? false; final isFormValid = formKey.currentState?.validate() ?? false;

View File

@@ -13,86 +13,97 @@ class AuthPage extends GetView<AuthLogic> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: SingleChildScrollView( body: Stack(
child: Column( alignment: Alignment.center,
children: [
SizedBox(height: 80),
LogoWidget(),
ObxValue((types) {
switch (types.value) {
case AuthType.otp:
//return otpForm();
case AuthType.useAndPass:
return useAndPassFrom();
}
}, controller.authType),
SizedBox(height: 20), children: [
RichText( Assets.vec.bgAuthSvg.svg(fit: BoxFit.fill),
text: TextSpan(
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.r),
child: FadeTransition(
opacity: controller.textAnimation,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 12,
children: [ children: [
TextSpan( Text(
text: 'مطالعه بیانیه ', 'به سامانه رصدیار خوش آمدید!',
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDark), textAlign: TextAlign.right,
style: AppFonts.yekan25Bold.copyWith(color: Colors.white),
), ),
TextSpan( Text(
recognizer: TapGestureRecognizer() 'سامانه رصد و پایش زنجیره تامین، تولید و توزیع کالا های اساسی',
..onTap = () { textAlign: TextAlign.center,
Get.bottomSheet( style: AppFonts.yekan16.copyWith(color: Colors.white),
privacyPolicyWidget(),
isScrollControlled: true,
enableDrag: true,
ignoreSafeArea: false,
);
},
text: 'حریم خصوصی',
style: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
), ),
], ],
), ),
), ),
/* SizedBox(height: 18), ),
ObxValue((types) { Obx(() {
return RichText( final screenHeight = MediaQuery.of(context).size.height;
text: TextSpan( final targetTop = (screenHeight - 676) / 2;
return AnimatedPositioned(
duration: const Duration(milliseconds: 1200),
curve: Curves.linear,
top: controller.showCard.value ? targetTop : screenHeight,
left: 10.r,
right: 10.r,
child: Container(
width: 381.w,
height: 676.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
child: Column(
children: [ children: [
TextSpan( SizedBox(height: 50.h),
recognizer: LogoWidget(),
TapGestureRecognizer() SizedBox(height: 20.h),
..onTap = () { useAndPassFrom(),
if (controller.authType.value == AuthType.otp) { SizedBox(height: 24.h),
controller.authType.value = AuthType.useAndPass; RichText(
if (controller.otpStatus.value != text: TextSpan(
OtpStatus.init) { children: [
controller.otpStatus.value = OtpStatus.init; TextSpan(
} text: 'مطالعه بیانیه ',
} else { style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDark),
controller.authType.value = AuthType.otp; ),
} TextSpan(
}, recognizer: TapGestureRecognizer()
text: ..onTap = () {
controller.authType.value == AuthType.otp Get.bottomSheet(
? 'ورود با رمز ثابت' privacyPolicyWidget(),
: 'ورود با رمز یکبار مصرف', isScrollControlled: true,
enableDrag: true,
style: AppFonts.yekan14.copyWith( ignoreSafeArea: false,
color: AppColor.blueNormal, );
},
text: 'حریم خصوصی',
style: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
),
],
), ),
), ),
], ],
), ),
); ),
}, controller.authType),*/ );
], }),
), ],
), ),
); );
} }
Widget useAndPassFrom() { Widget useAndPassFrom() {
return Padding( return Padding(
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 50), padding: EdgeInsets.symmetric(horizontal: 30.r),
child: Form( child: Form(
key: controller.formKey, key: controller.formKey,
child: AutofillGroup( child: AutofillGroup(
@@ -106,6 +117,10 @@ class AuthPage extends GetView<AuthLogic> {
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
initText: controller.usernameController.value.text, initText: controller.usernameController.value.text,
autofillHints: [AutofillHints.username], autofillHints: [AutofillHints.username],
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: AppColor.textColor, width: 1),
),
onChanged: (value) async { onChanged: (value) async {
controller.usernameController.value.text = value; controller.usernameController.value.text = value;
controller.usernameController.refresh(); controller.usernameController.refresh();
@@ -144,6 +159,10 @@ class AuthPage extends GetView<AuthLogic> {
label: 'رمز عبور', label: 'رمز عبور',
filled: false, filled: false,
obscure: true, obscure: true,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: AppColor.textColor, width: 1),
),
controller: passwordController.value, controller: passwordController.value,
autofillHints: [AutofillHints.password], autofillHints: [AutofillHints.password],
variant: RTextFieldVariant.password, variant: RTextFieldVariant.password,

View File

@@ -177,7 +177,6 @@ class InspectionMapPage extends GetView<InspectionMapLogic> {
} }
/* /*
Widget selectedLocationWidget2({ Widget selectedLocationWidget2({
required bool showHint, required bool showHint,
required SlidableController sliderController, required SlidableController sliderController,

View File

@@ -97,7 +97,7 @@ class MapLogic extends GetxController with GetTickerProviderStateMixin {
isLoading.value = false; isLoading.value = false;
} }
/* /*
void debouncedUpdateVisibleMarkers({required LatLng center, required double zoom}) { void debouncedUpdateVisibleMarkers({required LatLng center, required double zoom}) {
_debounceTimer?.cancel(); _debounceTimer?.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 300), () { _debounceTimer = Timer(const Duration(milliseconds: 300), () {

View File

@@ -50,6 +50,10 @@ class CaptchaWidget extends GetView<CaptchaWidgetLogic> {
child: RTextField( child: RTextField(
label: 'کد امنیتی', label: 'کد امنیتی',
controller: controller.textController, controller: controller.textController,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: AppColor.textColor, width: 1),
),
keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false), keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false),
maxLines: 1, maxLines: 1,
maxLength: 6, maxLength: 6,

View File

@@ -10,8 +10,8 @@ class LogoWidget extends StatelessWidget {
children: [ children: [
Row(), Row(),
Assets.images.innerSplash.image( Assets.images.innerSplash.image(
width: 150, width: 120.w,
height: 150, height: 120.h,
), ),
Text( Text(
'سامانه رصدیار', 'سامانه رصدیار',