import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:kmobile/app.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/security/secure_storage.dart'; import 'package:local_auth/local_auth.dart'; enum MPinMode { enter, set, confirm } class MPinScreen extends StatefulWidget { final MPinMode mode; final String? initialPin; final void Function(String pin)? onCompleted; const MPinScreen({ super.key, required this.mode, this.initialPin, this.onCompleted, }); @override State createState() => _MPinScreenState(); } class _MPinScreenState extends State { List mPin = []; String? errorText; @override void initState() { super.initState(); if (widget.mode == MPinMode.enter) { _tryBiometricBeforePin(); } } Future _tryBiometricBeforePin() async { final storage = getIt(); final enabled = await storage.read('biometric_enabled'); log('biometric_enabled: $enabled'); if (enabled != null && enabled) { final auth = LocalAuthentication(); if (await auth.canCheckBiometrics) { final didAuth = await auth.authenticate( localizedReason: 'Authenticate to access kMobile', options: const AuthenticationOptions(biometricOnly: true), ); if (didAuth && mounted) { // success → directly “complete” your flow widget.onCompleted?.call(''); // or navigate yourself: // Navigator.of(context).pushReplacement( // MaterialPageRoute(builder: (_) => const NavigationScaffold())); } } } } void addDigit(String digit) { if (mPin.length < 4) { setState(() { mPin.add(digit); errorText = null; }); if (mPin.length == 4) { _handleComplete(); } } } void deleteDigit() { if (mPin.isNotEmpty) { setState(() { mPin.removeLast(); errorText = null; }); } } Future _handleComplete() async { final pin = mPin.join(); final storage = SecureStorage(); switch (widget.mode) { case MPinMode.enter: final storedPin = await storage.read('mpin'); log('storedPin: $storedPin'); if (storedPin == int.tryParse(pin)) { widget.onCompleted?.call(pin); } else { setState(() { errorText = "Incorrect mPIN. Try again."; mPin.clear(); }); } break; case MPinMode.set: // propagate parent onCompleted into confirm step Navigator.push( context, MaterialPageRoute( builder: (_) => MPinScreen( mode: MPinMode.confirm, initialPin: pin, onCompleted: widget.onCompleted, // <-- use parent callback ), ), ); break; case MPinMode.confirm: if (widget.initialPin == pin) { // 1) persist the pin await storage.write('mpin', pin); // 3) now clear the entire navigation stack and go to your main scaffold if (mounted) { Navigator.of(context, rootNavigator: true).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => const NavigationScaffold()), (route) => false, ); } } else { setState(() { errorText = "Pins do not match. Try again."; mPin.clear(); }); } break; } } Widget buildMPinDots() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(4, (index) { return Container( margin: const EdgeInsets.all(8), width: 15, height: 15, decoration: BoxDecoration( shape: BoxShape.circle, color: index < mPin.length ? Colors.black : Colors.grey[400], ), ); }), ); } Widget buildNumberPad() { List> keys = [ ['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], ['Enter', '0', '<'] ]; return Column( children: keys.map((row) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: row.map((key) { return Padding( padding: const EdgeInsets.all(8.0), child: GestureDetector( onTap: () { if (key == '<') { deleteDigit(); } else if (key == 'Enter') { if (mPin.length == 4) { _handleComplete(); } else { setState(() { errorText = "Please enter 4 digits"; }); } } else if (key.isNotEmpty) { addDigit(key); } }, child: Container( width: 70, height: 70, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.grey[200], ), alignment: Alignment.center, child: key == 'Enter' ? const Icon(Icons.check) : Text( key == '<' ? '⌫' : key, style: TextStyle( fontSize: 20, color: key == 'Enter' ? Colors.blue : Colors.black, ), ), ), ), ); }).toList(), ); }).toList(), ); } String getTitle() { switch (widget.mode) { case MPinMode.enter: return "Enter your mPIN"; case MPinMode.set: return "Set your new mPIN"; case MPinMode.confirm: return "Confirm your mPIN"; } } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ const Spacer(), // Logo Image.asset('assets/images/logo.png', height: 100), const SizedBox(height: 20), Text( getTitle(), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500), ), const SizedBox(height: 20), buildMPinDots(), if (errorText != null) Padding( padding: const EdgeInsets.only(top: 8.0), child: Text(errorText!, style: const TextStyle(color: Colors.red)), ), const Spacer(), buildNumberPad(), const Spacer(), ], ), ), ); } }