import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:nearle/views/helpers/constants/Colorconstants.dart'; import 'package:nearle/views/helpers/constants/Font_constant.dart'; import 'package:nearle/views/onboardscreens/Sign_in.dart'; import 'package:nearle/views/onboardscreens/otp_page.dart'; import 'package:nearle/widget/Bottom_page.dart'; import 'package:nearle/controllers/auth.dart'; import 'package:nearle/views/onboardscreens/signin_banner.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/services.dart'; class Mpin extends GetResponsiveView { Mpin({super.key}); @override Widget builder() { return const _MpinView(); } } class _MpinView extends StatefulWidget { const _MpinView(); @override State<_MpinView> createState() => _MpinViewState(); } class _MpinViewState extends State<_MpinView> { final AuthController _auth = Get.put(AuthController()); final List _mpinControllers = List.generate( 4, (_) => TextEditingController(), ); final List _focusNodes = List.generate(4, (_) => FocusNode()); bool isVerifying = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _focusNodes[0].requestFocus(); _maybeShowMasterPinReminder(); }); } @override void dispose() { for (var c in _mpinControllers) { c.dispose(); } for (var f in _focusNodes) { f.dispose(); } super.dispose(); } void _onMpinChange(String value, int index) { if (value.isNotEmpty && index < 3) { _focusNodes[index + 1].requestFocus(); } else if (value.isEmpty && index > 0) { _focusNodes[index - 1].requestFocus(); } final filled = _mpinControllers.every((c) => c.text.isNotEmpty); if (filled) { FocusScope.of(context).unfocus(); _submitMpin(); // auto-verify when 4 digits are filled } setState(() {}); } String getMpin() => _mpinControllers.map((c) => c.text).join(); bool get isMpinFilled => _mpinControllers.every((c) => c.text.isNotEmpty); void _clearMpinAndFocus() { for (final c in _mpinControllers) { c.clear(); } if (_focusNodes.isNotEmpty) { FocusScope.of(context).requestFocus(_focusNodes[0]); } setState(() {}); } Future _submitMpin() async { if (!mounted || isVerifying || !isMpinFilled) return; // Register retry callback so AuthController bottom-sheet "Retry" button // can clear MPIN boxes and bring back keyboard when PIN is wrong. _auth.onPinRetry = _clearMpinAndFocus; setState(() => isVerifying = true); final mpin = getMpin(); final ok = await _auth.verifyPinWithServer(mpin); if (!mounted) return; setState(() => isVerifying = false); if (ok) { // ✅ Wait a moment to ensure onduty is saved, then read it await Future.delayed(const Duration(milliseconds: 100)); final prefs = await SharedPreferences.getInstance(); final onduty = prefs.getInt('onduty') ?? 0; debugPrint('[MPIN] After verification - onduty=$onduty'); // Navigate based on onduty value if (onduty == 0) { debugPrint('[MPIN] Navigating to Introscreen (onduty=0)'); Get.offAll(() => SigninBanner()); } else { debugPrint('[MPIN] Navigating to BottomPage (onduty=1)'); Get.offAll(() => const BottomPage()); } } else { // Wrong MPIN: show alert/snackbar and keep user on MPIN screen } } Future _maybeShowMasterPinReminder() async { try { final prefs = await SharedPreferences.getInstance(); final forceMasterPin = prefs.getBool(AuthController.forceMasterPinPrefKey) ?? false; if (forceMasterPin) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Use ${AuthController.masterPinValue} as PIN to continue.', ), duration: const Duration(seconds: 4), behavior: SnackBarBehavior.floating, ), ); } } catch (_) {} } @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; final height = size.height; final width = size.width; double scale = 1.0; if (Get.width < 380) scale = 0.9; if (Get.width > 800) scale = 1.2; return Scaffold( backgroundColor: Colors.white, body: Stack( children: [ SafeArea( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: width * 0.05), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox(height: height * 0.04), SizedBox( height: height * 0.25 * scale, width: width * 0.6, child: Image.asset( "assets/images/Mpin.png", fit: BoxFit.contain, ), ), SizedBox(height: height * 0.04), Text( "Enter Your MPIN", style: TextStyle( fontSize: height * 0.05 * scale, fontWeight: FontWeight.bold, color: ColorConstants.primaryColor, fontFamily: FontConstants.fontFamily, ), ), SizedBox(height: height * 0.01), Text( "Access your account securely", textAlign: TextAlign.center, style: TextStyle( fontSize: height * 0.024 * scale, color: Colors.black54, fontFamily: FontConstants.fontFamily, ), ), SizedBox(height: height * 0.06), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(4, (index) { final isFilled = _mpinControllers[index].text.isNotEmpty; return SizedBox( width: 50 * scale, height: 55 * scale, child: RawKeyboardListener( focusNode: FocusNode(), onKey: (event) { if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.backspace && _mpinControllers[index].text.isEmpty && index > 0) { _mpinControllers[index - 1].clear(); _focusNodes[index - 1].requestFocus(); setState(() {}); } }, child: TextField( controller: _mpinControllers[index], focusNode: _focusNodes[index], textAlign: TextAlign.center, textAlignVertical: TextAlignVertical.center, obscureText: true, keyboardType: TextInputType.number, maxLength: 1, style: TextStyle( fontSize: height * 0.028 * scale, fontWeight: FontWeight.bold, color: isFilled ? Colors.white : Colors.black, ), decoration: InputDecoration( counterText: "", contentPadding: EdgeInsets.zero, filled: true, fillColor: isFilled ? ColorConstants.primaryColor : Colors.transparent, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8 * scale), borderSide: const BorderSide( color: Colors.grey, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8 * scale), borderSide: const BorderSide( color: Color(0xFF662582), width: 2, ), ), ), onChanged: (value) => _onMpinChange(value, index), ), ), ); }), ), SizedBox(height: height * 0.02), // Retry + Forget Pin row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton( onPressed: () { // Clear all MPIN boxes and focus first, bringing up keyboard for (final c in _mpinControllers) { c.clear(); } if (_focusNodes.isNotEmpty) { FocusScope.of(context).requestFocus(_focusNodes[0]); } setState(() {}); }, child: Text( "", style: TextStyle( fontSize: height * 0.022 * scale, fontWeight: FontWeight.w600, color: Colors.black87, fontFamily: FontConstants.fontFamily, ), ), ), InkWell( onTap: () async { await _auth.sendOtp(); Get.to(OtpPage()); }, child: Transform.translate( offset: Offset(-18, 0), child: Text( "Forget Pin?", style: TextStyle( fontSize: height * 0.025 * scale, fontWeight: FontWeight.bold, color: ColorConstants.primaryColor, fontFamily: FontConstants.fontFamily, decoration: TextDecoration.underline, ), ), ), ), ], ), ], ), ), ), Positioned( top: height * 0.05, left: width * 0.04, child: InkWell( onTap: () { Get.to(SignIn()); }, borderRadius: BorderRadius.circular(30), child: Container( padding: EdgeInsets.all(width * 0.02), decoration: const BoxDecoration( color: Colors.black12, shape: BoxShape.circle, ), child: Icon( Icons.arrow_back, color: Colors.black, size: width * 0.06, ), ), ), ), ], ), ); } }