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/controllers/auth.dart'; import 'package:nearle/views/onboardscreens/Mpin.dart'; class CreateMpin extends GetResponsiveView { CreateMpin({super.key}); @override Widget builder() { return const _CreateMpinBody(); } } class _CreateMpinBody extends StatefulWidget { const _CreateMpinBody(); @override State<_CreateMpinBody> createState() => _CreateMpinBodyState(); } class _CreateMpinBodyState extends State<_CreateMpinBody> { final AuthController _auth = Get.put(AuthController()); final List _newMpinControllers = List.generate( 4, (_) => TextEditingController(), ); final List _confirmMpinControllers = List.generate( 4, (_) => TextEditingController(), ); final List _newFocusNodes = List.generate(4, (_) => FocusNode()); final List _confirmFocusNodes = List.generate( 4, (_) => FocusNode(), ); bool isLoading = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _newFocusNodes[0].requestFocus(); }); } @override void dispose() { for (var c in [..._newMpinControllers, ..._confirmMpinControllers]) { c.dispose(); } for (var f in [..._newFocusNodes, ..._confirmFocusNodes]) { f.dispose(); } super.dispose(); } void _onMpinChange( String value, int index, List controllers, List nodes, ) { if (value.isNotEmpty && index < 3) { nodes[index + 1].requestFocus(); } else if (value.isEmpty && index > 0) { nodes[index - 1].requestFocus(); } final isGroupFilled = controllers.every((c) => c.text.isNotEmpty); if (controllers == _newMpinControllers && isGroupFilled) { _confirmFocusNodes[0].requestFocus(); } if (controllers == _confirmMpinControllers && isGroupFilled) { FocusScope.of(context).unfocus(); } setState(() {}); } String getMpin(List controllers) => controllers.map((c) => c.text).join(); bool get isMpinMatched => getMpin(_newMpinControllers) == getMpin(_confirmMpinControllers); bool get isAllFilled => [ ..._newMpinControllers, ..._confirmMpinControllers, ].every((c) => c.text.isNotEmpty); @override Widget build(BuildContext context) { final screen = context.width < 600 ? "mobile" : context.width < 1100 ? "tablet" : "desktop"; final height = Get.height; final width = Get.width; // Adjust scale based on device final scale = screen == "mobile" ? 1.0 : screen == "tablet" ? 1.3 : 1.6; return Scaffold( backgroundColor: Colors.white, body: Stack( children: [ SafeArea( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: width * 0.06), child: Column( children: [ SizedBox(height: height * 0.04 * scale), SizedBox( height: height * 0.25 * scale, width: width * 0.6, child: Image.asset( "assets/images/CreateMpin.png", fit: BoxFit.contain, ), ), SizedBox(height: height * 0.03 * scale), Text( "Create Your MPIN", style: TextStyle( fontSize: height * 0.04 * scale, fontWeight: FontWeight.bold, fontFamily: FontConstants.fontFamily, color: ColorConstants.primaryColor, ), ), SizedBox(height: height * 0.015 * scale), Text( "Enter a 4-digit MPIN and confirm it to secure your account.", textAlign: TextAlign.center, style: TextStyle( fontSize: height * 0.02 * scale, color: Colors.black54, fontFamily: FontConstants.fontFamily, ), ), SizedBox(height: height * 0.04 * scale), _buildMpinField( "Enter New MPIN", _newMpinControllers, _newFocusNodes, scale, ), SizedBox(height: height * 0.03 * scale), _buildMpinField( "Confirm MPIN", _confirmMpinControllers, _confirmFocusNodes, scale, ), ], ), ), ), Positioned( top: height * 0.05, left: width * 0.04, child: InkWell( onTap: () => Get.back(), 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, ), ), ), ), ], ), bottomNavigationBar: SafeArea( child: Padding( padding: EdgeInsets.all(width * 0.04), child: SizedBox( width: double.infinity, height: height * 0.065 * scale, child: ElevatedButton( onPressed: isAllFilled && isMpinMatched && !isLoading ? () async { setState(() => isLoading = true); final newPin = getMpin(_newMpinControllers); // Set PIN directly - user ID should already be available from login flow final ok = await _auth.setPin(newPin); setState(() => isLoading = false); if (ok) { // After setting PIN, go to verify PIN page to sign-in with new PIN Get.to(() => Mpin()); } else { Get.snackbar( 'Failed', 'Unable to set PIN. Please try again.', ); } } : null, style: ElevatedButton.styleFrom( backgroundColor: ColorConstants.primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(width * 0.03), ), ), child: isLoading ? SizedBox( height: height * 0.03, width: height * 0.03, child: const CircularProgressIndicator( strokeWidth: 3, color: Colors.white, ), ) : Text( "Continue", style: TextStyle( fontSize: height * 0.024 * scale, fontWeight: FontWeight.bold, color: Colors.white, fontFamily: FontConstants.fontFamily, ), ), ), ), ), ), ); } Widget _buildMpinField( String label, List controllers, List focusNodes, double scale, ) { final height = Get.height; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: height * 0.02 * scale, fontWeight: FontWeight.w600, fontFamily: FontConstants.fontFamily, ), ), SizedBox(height: height * 0.015 * scale), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(4, (index) { return SizedBox( width: 50 * scale, height: 55 * scale, child: TextField( controller: controllers[index], focusNode: focusNodes[index], textAlign: TextAlign.center, obscureText: true, maxLength: 1, keyboardType: TextInputType.number, style: TextStyle( fontSize: height * 0.025 * scale, fontWeight: FontWeight.bold, fontFamily: FontConstants.fontFamily, ), decoration: InputDecoration( counterText: "", enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8 * scale), borderSide: const BorderSide(color: Colors.grey), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8 * scale), borderSide: const BorderSide( color: ColorConstants.primaryColor, width: 2, ), ), ), onChanged: (val) => _onMpinChange(val, index, controllers, focusNodes), ), ); }), ), ], ); } }