After Login Button
This commit is contained in:
33
lib/app.dart
33
lib/app.dart
@@ -4,7 +4,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:kmobile/features/auth/controllers/theme_mode_cubit.dart';
|
||||
import 'package:kmobile/features/auth/controllers/theme_mode_state.dart';
|
||||
import 'package:kmobile/features/auth/screens/sms_verification_screen.dart';
|
||||
import 'package:kmobile/features/auth/screens/login_screen.dart';
|
||||
//import 'package:kmobile/features/auth/screens/sms_verification_screen.dart';
|
||||
import 'package:kmobile/security/secure_storage.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import './l10n/app_localizations.dart';
|
||||
@@ -14,7 +15,6 @@ import 'config/routes.dart';
|
||||
import 'di/injection.dart';
|
||||
import 'features/auth/controllers/auth_cubit.dart';
|
||||
import 'features/card/screens/card_management_screen.dart';
|
||||
import 'features/auth/screens/splash_screen.dart';
|
||||
import 'features/service/screens/service_screen.dart';
|
||||
import 'features/dashboard/screens/dashboard_screen.dart';
|
||||
import 'features/auth/screens/mpin_screen.dart';
|
||||
@@ -37,7 +37,6 @@ class KMobile extends StatefulWidget {
|
||||
|
||||
class _KMobileState extends State<KMobile> with WidgetsBindingObserver {
|
||||
Timer? _backgroundTimer;
|
||||
bool showSplash = true;
|
||||
Locale? _locale;
|
||||
|
||||
@override
|
||||
@@ -45,11 +44,6 @@ class _KMobileState extends State<KMobile> with WidgetsBindingObserver {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
loadPreferences();
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
setState(() {
|
||||
showSplash = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -132,8 +126,7 @@ class _KMobileState extends State<KMobile> with WidgetsBindingObserver {
|
||||
darkTheme: themeState.getDarkThemeData(),
|
||||
themeMode: context.watch<ThemeModeCubit>().state.mode,
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute: AppRoutes.splash,
|
||||
home: showSplash ? const SplashScreen() : const AuthGate(),
|
||||
home: const AuthGate(),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -205,7 +198,11 @@ class _AuthGateState extends State<AuthGate> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_checking) {
|
||||
return const SplashScreen();
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_isLoggedIn) {
|
||||
if (_hasMPin) {
|
||||
@@ -214,7 +211,11 @@ class _AuthGateState extends State<AuthGate> {
|
||||
future: _tryBiometric(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const SplashScreen();
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.data == true) {
|
||||
return const NavigationScaffold();
|
||||
@@ -301,7 +302,7 @@ class _AuthGateState extends State<AuthGate> {
|
||||
);
|
||||
}
|
||||
}
|
||||
return const SmsVerificationScreen();
|
||||
return const LoginScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +423,11 @@ class BiometricPromptScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Future.microtask(() => _showDialog(context));
|
||||
return const SplashScreen();
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showDialog(BuildContext context) async {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
||||
import 'package:kmobile/features/auth/screens/splash_screen.dart';
|
||||
import '../app.dart';
|
||||
import '../features/auth/screens/login_screen.dart';
|
||||
// import '../features/auth/screens/forgot_password_screen.dart';
|
||||
@@ -30,8 +29,6 @@ class AppRoutes {
|
||||
// Route generator
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
switch (settings.name) {
|
||||
case splash:
|
||||
return MaterialPageRoute(builder: (_) => const SplashScreen());
|
||||
case login:
|
||||
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ Dio _createDioClient() {
|
||||
final dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl:
|
||||
'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //test
|
||||
// 'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //test
|
||||
//'http://lb-kccb-mobile-banking-app-848675342.ap-south-1.elb.amazonaws.com', //prod
|
||||
//'https://kccbmbnk.net', //prod small
|
||||
'https://kccbmbnk.net', //prod small
|
||||
connectTimeout: const Duration(seconds: 60),
|
||||
receiveTimeout: const Duration(seconds: 60),
|
||||
headers: {
|
||||
|
||||
@@ -12,6 +12,10 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
checkAuthStatus();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
emit(AuthInitial());
|
||||
}
|
||||
|
||||
Future<void> checkAuthStatus() async {
|
||||
emit(AuthLoading());
|
||||
try {
|
||||
@@ -27,6 +31,10 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
}
|
||||
}
|
||||
|
||||
void startVerification() {
|
||||
emit(AuthVerificationInProgress());
|
||||
}
|
||||
|
||||
Future<void> refreshUserData() async {
|
||||
try {
|
||||
// emit(AuthLoading());
|
||||
|
||||
@@ -10,6 +10,8 @@ class AuthInitial extends AuthState {}
|
||||
|
||||
class AuthLoading extends AuthState {}
|
||||
|
||||
class AuthVerificationInProgress extends AuthState {}
|
||||
|
||||
class Authenticated extends AuthState {
|
||||
final List<User> users;
|
||||
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:kmobile/di/injection.dart';
|
||||
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
||||
import 'package:kmobile/features/auth/screens/set_password_screen.dart';
|
||||
import 'package:kmobile/security/secure_storage.dart';
|
||||
import '../../../app.dart';
|
||||
import '../controllers/auth_cubit.dart';
|
||||
import '../controllers/auth_state.dart';
|
||||
import 'package:kmobile/features/auth/controllers/auth_cubit.dart';
|
||||
import 'package:kmobile/features/auth/screens/verification_screen.dart';
|
||||
import 'package:kmobile/l10n/app_localizations.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@@ -23,7 +17,12 @@ class LoginScreenState extends State<LoginScreen>
|
||||
final _customerNumberController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
bool _obscurePassword = true;
|
||||
//bool _showWelcome = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
context.read<AuthCubit>().reset();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -34,10 +33,14 @@ class LoginScreenState extends State<LoginScreen>
|
||||
|
||||
void _submitForm() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
context.read<AuthCubit>().login(
|
||||
_customerNumberController.text.trim(),
|
||||
_passwordController.text,
|
||||
);
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => VerificationScreen(
|
||||
customerNo: _customerNumberController.text.trim(),
|
||||
password: _passwordController.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,217 +48,141 @@ class LoginScreenState extends State<LoginScreen>
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
// appBar: AppBar(title: const Text('Login')),
|
||||
body: BlocConsumer<AuthCubit, AuthState>(
|
||||
listener: (context, state) async {
|
||||
if (state is Authenticated) {
|
||||
final storage = getIt<SecureStorage>();
|
||||
final mpin = await storage.read('mpin');
|
||||
if (!context.mounted) return;
|
||||
if (mpin == null) {
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MPinScreen(
|
||||
mode: MPinMode.set,
|
||||
onCompleted: (_) {
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const NavigationScaffold(),
|
||||
),
|
||||
);
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
Icons.account_balance,
|
||||
size: 100,
|
||||
color: Theme.of(context).primaryColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Title
|
||||
Text(
|
||||
AppLocalizations.of(context).kccb,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
|
||||
TextFormField(
|
||||
controller: _customerNumberController,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).customerNumber,
|
||||
// prefixIcon: Icon(Icons.person),
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2),
|
||||
),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return AppLocalizations.of(context).pleaseEnterUsername;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// Password
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: _obscurePassword,
|
||||
textInputAction: TextInputAction.done,
|
||||
onFieldSubmitted: (_) => _submitForm(),
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).password,
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(builder: (_) => const NavigationScaffold()),
|
||||
);
|
||||
}
|
||||
} else if (state is AuthError) {
|
||||
if (state.message == 'MIGRATED_USER_HAS_NO_PASSWORD') {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => SetPasswordScreen(
|
||||
customerNo: _customerNumberController.text.trim(),
|
||||
)));
|
||||
} else {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text(state.message)));
|
||||
}
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
Icons.account_balance,
|
||||
size: 100,
|
||||
color: Theme.of(context).primaryColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Title
|
||||
Text(
|
||||
AppLocalizations.of(context).kccb,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
|
||||
TextFormField(
|
||||
controller: _customerNumberController,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).customerNumber,
|
||||
// prefixIcon: Icon(Icons.person),
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2),
|
||||
),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return AppLocalizations.of(context).pleaseEnterUsername;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// Password
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: _obscurePassword,
|
||||
textInputAction: TextInputAction.done,
|
||||
onFieldSubmitted: (_) => _submitForm(),
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).password,
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return AppLocalizations.of(context).pleaseEnterPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
//Login Button
|
||||
SizedBox(
|
||||
width: 250,
|
||||
child: ElevatedButton(
|
||||
onPressed: state is AuthLoading ? null : _submitForm,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Theme.of(context).primaryColorDark,
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 1),
|
||||
elevation: 0,
|
||||
),
|
||||
child: state is AuthLoading
|
||||
? const CircularProgressIndicator()
|
||||
: Text(
|
||||
AppLocalizations.of(context).login,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const Expanded(child: Divider()),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
// child: Text(AppLocalizations.of(context).or),
|
||||
// ),
|
||||
// //const Expanded(child: Divider()),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
|
||||
const SizedBox(height: 25),
|
||||
|
||||
// Register Button
|
||||
// SizedBox(
|
||||
// width: 250,
|
||||
// child: ElevatedButton(
|
||||
// //disable until registration is implemented
|
||||
// onPressed: null,
|
||||
// style: OutlinedButton.styleFrom(
|
||||
// shape: const StadiumBorder(),
|
||||
// padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
// backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
// foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
// ),
|
||||
// child: Text(AppLocalizations.of(context).register,
|
||||
// style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return AppLocalizations.of(context).pleaseEnterPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
const SizedBox(height: 24),
|
||||
//Login Button
|
||||
SizedBox(
|
||||
width: 250,
|
||||
child: ElevatedButton(
|
||||
onPressed: _submitForm,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Theme.of(context).primaryColorDark,
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
width: 1),
|
||||
elevation: 0,
|
||||
),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).login,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
const SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
131
lib/features/auth/screens/sms_verification_helper.dart
Normal file
131
lib/features/auth/screens/sms_verification_helper.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
// lib/features/auth/screens/sms_verification_helper.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kmobile/api/services/send_sms_service.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class SmsVerificationHelper {
|
||||
final SmsService _smsService = SmsService();
|
||||
|
||||
Future<void> _showRestrictedSmsDialog(BuildContext context) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text("SMS Permission Restricted"),
|
||||
content: const SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("It seems your device is restricting this app from sending SMS messages, which is required for verification. Please follow these steps to enable it:\n"),
|
||||
Text("1. Open your device Settings.", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text("2. Go to 'Apps' or 'Apps & notifications'."),
|
||||
Text("3. Find and tap on this app ('KMobile')."),
|
||||
Text("4. Tap on the three dots (⋮) in the top right corner."),
|
||||
Text("5. Select 'Allow restricted settings' and confirm. This is crucial to allow SMS permission."),
|
||||
Text("6. Now you have two options to allow SMS permission:"),
|
||||
Text(" a. Tap on 'Permissions', then find 'SMS' is set to 'Allow'."),
|
||||
Text(" b. Alternatively, you can return to the KMobile app, and the SMS permission pop-up should appear again, allowing you to grant it directly."),
|
||||
Text("\nSome devices have an additional setting for 'Premium SMS'. If the above doesn't work, look for a 'Premium SMS access' setting (you can search for it in your Settings app) and set it to 'Always Allow' for this app.\n"),
|
||||
Text("After you've enabled the permission, please come back to the app."),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("I've Enabled It"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("Open Settings"),
|
||||
onPressed: () {
|
||||
openAppSettings();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSnackBar(BuildContext context, String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 3),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> initiateSmsSequence({
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
bool hasPermission = false;
|
||||
|
||||
// --- PERMISSION LOOP ---
|
||||
while (!hasPermission) {
|
||||
final status = await _smsService.handleSmsPermission();
|
||||
|
||||
switch (status) {
|
||||
case PermissionStatusResult.granted:
|
||||
_showSnackBar(context, "Permissions Granted! Proceeding...");
|
||||
hasPermission = true; // This will break the loop
|
||||
break;
|
||||
case PermissionStatusResult.denied:
|
||||
_showSnackBar(context, "SMS and Phone permissions are required. Please try again.");
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
break;
|
||||
case PermissionStatusResult.permanentlyDenied:
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text("Permission Required"),
|
||||
content: const Text("SMS and Phone permissions are required for device verification. Please enable them in your app settings to continue."),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("Open Settings"),
|
||||
onPressed: () {
|
||||
openAppSettings(); // Opens the phone's settings screen for this app
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
// Wait for user to return from settings
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
break;
|
||||
case PermissionStatusResult.restricted:
|
||||
await _showRestrictedSmsDialog(context);
|
||||
// Wait for user to return from settings
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- SMS SENDING LOOP ---
|
||||
bool isSmsSent = false;
|
||||
while (!isSmsSent) {
|
||||
var uuid = const Uuid();
|
||||
String uniqueId = uuid.v4();
|
||||
String smsMessage = uniqueId;
|
||||
_showSnackBar(context, "Attempting to send verification SMS...");
|
||||
isSmsSent = await _smsService.sendVerificationSms(
|
||||
context: context,
|
||||
destinationNumber: '9580079717', // Replace with your number
|
||||
message: smsMessage,
|
||||
);
|
||||
if (isSmsSent) {
|
||||
_showSnackBar(context, "SMS sent successfully! Proceeding to login.");
|
||||
break;
|
||||
} else {
|
||||
_showSnackBar(context, "SMS failed to send. Retrying in 5 seconds...");
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
// lib/features/auth/screens/sms_verification_screen.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:kmobile/api/services/send_sms_service.dart';
|
||||
import 'package:kmobile/l10n/app_localizations.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
class SmsVerificationScreen extends StatefulWidget {
|
||||
const SmsVerificationScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SmsVerificationScreen> createState() => _SmsVerificationScreenState();
|
||||
}
|
||||
|
||||
class _SmsVerificationScreenState extends State<SmsVerificationScreen> {
|
||||
String _version = '';
|
||||
final SmsService _smsService = SmsService();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadVersion();
|
||||
_initiateSmsSequence();
|
||||
}
|
||||
|
||||
Future<void> _showRestrictedSmsDialog() async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text("SMS Permission Restricted"),
|
||||
content: const SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("It seems your device is restricting this app from sending SMS messages, which is required for verification. Please follow these steps to enable it:\n"),
|
||||
Text("1. Open your device Settings.", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text("2. Go to 'Apps' or 'Apps & notifications'."),
|
||||
Text("3. Find and tap on this app ('KMobile')."),
|
||||
Text("4. Tap on the three dots (⋮) in the top right corner."),
|
||||
Text("5. Select 'Allow restricted settings' and confirm. This is crucial to allow SMS permission."),
|
||||
Text("6. Now you have two options to allow SMS permission:"),
|
||||
Text(" a. Tap on 'Permissions', then find 'SMS' and ensure it's set to 'Allow'."),
|
||||
Text(" b. Alternatively, you can return to the KMobile app, and the SMS permission pop-up should appear again, allowing you to grant it directly."),
|
||||
Text("\nSome devices have an additional setting for 'Premium SMS'. If the above doesn't work, look for a 'Premium SMS access' setting (you can search for it in your Settings app) and set it to 'Always Allow' for this app.\n"),
|
||||
Text("After you've enabled the permission, please come back to the app."),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("I've Enabled It"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("Open Settings"),
|
||||
onPressed: () {
|
||||
openAppSettings();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSnackBar(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 3),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _initiateSmsSequence() async {
|
||||
bool hasPermission = false;
|
||||
|
||||
// --- PERMISSION LOOP ---
|
||||
while (!hasPermission) {
|
||||
final status = await _smsService.handleSmsPermission();
|
||||
|
||||
switch (status) {
|
||||
case PermissionStatusResult.granted:
|
||||
_showSnackBar("Permissions Granted! Proceeding...");
|
||||
hasPermission = true; // This will break the loop
|
||||
break;
|
||||
case PermissionStatusResult.denied:
|
||||
_showSnackBar("SMS and Phone permissions are required. Please try again.");
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
break;
|
||||
case PermissionStatusResult.permanentlyDenied:
|
||||
if (mounted) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text("Permission Required"),
|
||||
content: const Text("SMS and Phone permissions are required for device verification. Please enable them in your app settings to continue."),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("Open Settings"),
|
||||
onPressed: () {
|
||||
openAppSettings(); // Opens the phone's settings screen for this app
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// Wait for user to return from settings
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
break;
|
||||
case PermissionStatusResult.restricted:
|
||||
if (mounted) {
|
||||
await _showRestrictedSmsDialog();
|
||||
}
|
||||
// Wait for user to return from settings
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- SMS SENDING LOOP ---
|
||||
bool isSmsSent = false;
|
||||
while (!isSmsSent) {
|
||||
var uuid = const Uuid();
|
||||
String uniqueId = uuid.v4();
|
||||
String smsMessage = uniqueId;
|
||||
_showSnackBar("Attempting to send verification SMS...");
|
||||
isSmsSent = await _smsService.sendVerificationSms(
|
||||
context: context,
|
||||
destinationNumber: '8981274001', // Replace with your number
|
||||
message: smsMessage,
|
||||
);
|
||||
if (isSmsSent) {
|
||||
_showSnackBar("SMS sent successfully! Proceeding to login.");
|
||||
break;
|
||||
} else {
|
||||
_showSnackBar("SMS failed to send. Retrying in 5 seconds...");
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pushReplacementNamed(context, '/login');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadVersion() async {
|
||||
final PackageInfo info = await PackageInfo.fromPlatform();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_version = 'Version ${info.version} (${info.buildNumber})';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
'assets/images/kconnect2.webp',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context).kccbMobile,
|
||||
style: const TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
AppLocalizations.of(context).kccBankFull,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Color(0xFFFFFFFF),
|
||||
letterSpacing: 1.2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Positioned(
|
||||
bottom: 40,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(color: Color(0xFFFFFFFF)),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 90,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Text(
|
||||
_version,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFFFFFFF),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
// // import 'package:package_info_plus/package_info_plus.dart';
|
||||
// // import '../../../l10n/app_localizations.dart';
|
||||
// // import 'package:kmobile/api/services/send_sms_service.dart';
|
||||
// // import 'package:flutter/material.dart';
|
||||
|
||||
// // class SplashScreen extends StatefulWidget {
|
||||
// // const SplashScreen({super.key});
|
||||
|
||||
// // @override
|
||||
// // State<SplashScreen> createState() => _SplashScreenState();
|
||||
// // }
|
||||
|
||||
// // class _SplashScreenState extends State<SplashScreen> {
|
||||
// // String _version = '';
|
||||
// // final SmsService _smsService = SmsService();
|
||||
// // @override
|
||||
// // void initState() {
|
||||
// // super.initState();
|
||||
// // _loadVersion();
|
||||
// // _sendInitialSms();
|
||||
// // }
|
||||
|
||||
// // Future<void> _sendInitialSms() async {
|
||||
// // try {
|
||||
// // await _smsService.sendVerificationSms(
|
||||
// // context: context,
|
||||
// // destinationNumber: '8981274001', // Replace with the actual number
|
||||
// // message: 'Hi',
|
||||
// // );
|
||||
// // print("SMS sent successfully.");
|
||||
// // } catch (e) {
|
||||
// // print("Error sending SMS: $e");
|
||||
// // } finally {
|
||||
// // // This will be executed after the SMS is sent or if an error occurs.
|
||||
// // // Replace with your actual navigation logic
|
||||
// // Navigator.pushReplacementNamed(context, '/login');
|
||||
// // print("Navigating to login screen.");
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // Future<void> _loadVersion() async {
|
||||
// // final PackageInfo info = await PackageInfo.fromPlatform();
|
||||
// // if (mounted) {
|
||||
// // // Check if the widget is still in the tree
|
||||
// // setState(() {
|
||||
// // _version = 'Version ${info.version} (${info.buildNumber})';
|
||||
// // });
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // @override
|
||||
// // Widget build(BuildContext context) {
|
||||
// // return Scaffold(
|
||||
// // body: Stack(
|
||||
// // fit: StackFit.expand,
|
||||
// // children: <Widget>[
|
||||
// // Positioned.fill(
|
||||
// // child: Image.asset(
|
||||
// // 'assets/images/kconnect2.webp',
|
||||
// // fit: BoxFit.cover,
|
||||
// // ),
|
||||
// // ),
|
||||
// // Center(
|
||||
// // child: Column(
|
||||
// // mainAxisSize: MainAxisSize.min,
|
||||
// // children: [
|
||||
// // Text(
|
||||
// // AppLocalizations.of(context).kccbMobile,
|
||||
// // style: const TextStyle(
|
||||
// // fontSize: 36,
|
||||
// // fontWeight: FontWeight.bold,
|
||||
// // color: Color(0xFFFFFFFF),
|
||||
// // ),
|
||||
// // ),
|
||||
// // const SizedBox(height: 12),
|
||||
// // Text(
|
||||
// // AppLocalizations.of(context).kccBankFull,
|
||||
// // textAlign: TextAlign.center,
|
||||
// // style: const TextStyle(
|
||||
// // fontSize: 18,
|
||||
// // color: Color(0xFFFFFFFF),
|
||||
// // letterSpacing: 1.2,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// // ),
|
||||
// // const Positioned(
|
||||
// // bottom: 40,
|
||||
// // left: 0,
|
||||
// // right: 0,
|
||||
// // child: Center(
|
||||
// // child: CircularProgressIndicator(color: Color(0xFFFFFFFF)),
|
||||
// // ),
|
||||
// // ),
|
||||
// // Positioned(
|
||||
// // bottom: 90,
|
||||
// // left: 0,
|
||||
// // right: 0,
|
||||
// // child: Text(
|
||||
// // _version,
|
||||
// // textAlign: TextAlign.center,
|
||||
// // style: const TextStyle(
|
||||
// // color: Color(0xFFFFFFFF),
|
||||
// // fontSize: 14,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// // );
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// import 'package:package_info_plus/package_info_plus.dart';
|
||||
// import 'package:kmobile/l10n/app_localizations.dart';
|
||||
// import 'package:kmobile/api/services/send_sms_service.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// class SplashScreen extends StatefulWidget {
|
||||
// const SplashScreen({super.key});
|
||||
|
||||
// @override
|
||||
// State<SplashScreen> createState() => _SplashScreenState();
|
||||
// }
|
||||
|
||||
// class _SplashScreenState extends State<SplashScreen> {
|
||||
// String _version = '';
|
||||
// final SmsService _smsService = SmsService();
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _loadVersion();
|
||||
// // Start the full permission and SMS sending sequence
|
||||
// _initiateSmsSequence();
|
||||
// }
|
||||
|
||||
// /// Manages the entire sequence from getting permission to sending the SMS.
|
||||
// Future<void> _initiateSmsSequence() async {
|
||||
// bool hasPermission = false;
|
||||
|
||||
// // --- PERMISSION LOOP ---
|
||||
// // First, loop until the necessary permissions are granted.
|
||||
// while (!hasPermission) {
|
||||
// print("Checking for SMS permission...");
|
||||
// hasPermission = await _smsService.handleSmsPermission();
|
||||
|
||||
// if (hasPermission) {
|
||||
// print("Permission granted! Proceeding to send SMS.");
|
||||
// break; // Exit the permission loop
|
||||
// } else {
|
||||
// print("Permission not granted. Will re-check in 5 seconds. Please grant permission in settings if prompted.");
|
||||
// // Wait for 5 seconds. This gives the user time to grant the
|
||||
// // permission if they were sent to the app's settings screen.
|
||||
// await Future.delayed(const Duration(seconds: 5));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // --- SMS SENDING LOOP ---
|
||||
// // Second, loop until the SMS is successfully sent.
|
||||
// bool isSmsSent = false;
|
||||
// while (!isSmsSent) {
|
||||
// print("Attempting to send SMS...");
|
||||
// isSmsSent = await _smsService.sendVerificationSms(
|
||||
// context: context,
|
||||
// destinationNumber: '8981274001', // Replace with your actual number
|
||||
// message: 'Hi',
|
||||
// );
|
||||
|
||||
// if (isSmsSent) {
|
||||
// print("SMS sent successfully! Proceeding to login.");
|
||||
// break; // Exit the SMS sending loop
|
||||
// } else {
|
||||
// print("SMS failed to send. Retrying in 5 seconds...");
|
||||
// await Future.delayed(const Duration(seconds: 5));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // --- NAVIGATION ---
|
||||
// // Once both loops are broken, navigate to the login screen.
|
||||
// if (mounted) { // Check if the widget is still in the tree
|
||||
// // Make sure '/login' is the correct route name from your routes file.
|
||||
// Navigator.pushReplacementNamed(context, '/login');
|
||||
// }
|
||||
// }
|
||||
|
||||
// Future<void> _loadVersion() async {
|
||||
// final PackageInfo info = await PackageInfo.fromPlatform();
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// _version = 'Version ${info.version} (${info.buildNumber})';
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// body: Stack(
|
||||
// fit: StackFit.expand,
|
||||
// children: <Widget>[
|
||||
// Positioned.fill(
|
||||
// child: Image.asset(
|
||||
// 'assets/images/kconnect2.webp',
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
// ),
|
||||
// Center(
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Text(
|
||||
// AppLocalizations.of(context).kccbMobile,
|
||||
// style: const TextStyle(
|
||||
// fontSize: 36,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Color(0xFFFFFFFF),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 12),
|
||||
// Text(
|
||||
// AppLocalizations.of(context).kccBankFull,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: const TextStyle(
|
||||
// fontSize: 18,
|
||||
// color: Color(0xFFFFFFFF),
|
||||
// letterSpacing: 1.2,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const Positioned(
|
||||
// bottom: 40,
|
||||
// left: 0,
|
||||
// right: 0,
|
||||
// child: Center(
|
||||
// child: CircularProgressIndicator(color: Color(0xFFFFFFFF)),
|
||||
// ),
|
||||
// ),
|
||||
// Positioned(
|
||||
// bottom: 90,
|
||||
// left: 0,
|
||||
// right: 0,
|
||||
// child: Text(
|
||||
// _version,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: const TextStyle(
|
||||
// color: Color(0xFFFFFFFF),
|
||||
// fontSize: 14,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:kmobile/l10n/app_localizations.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SplashScreen> createState() => _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
String _version = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadVersion();
|
||||
}
|
||||
|
||||
Future<void> _loadVersion() async {
|
||||
final PackageInfo info = await PackageInfo.fromPlatform();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_version = 'Version ${info.version} (${info.buildNumber})';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This build method is the same, but all the SMS logic is gone.
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
'assets/images/kconnect2.webp',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context).kccbMobile,
|
||||
style: const TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
AppLocalizations.of(context).kccBankFull,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Color(0xFFFFFFFF),
|
||||
letterSpacing: 1.2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Positioned(
|
||||
bottom: 40,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(color: Color(0xFFFFFFFF)),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 90,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Text(
|
||||
_version,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFFFFFFF),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
146
lib/features/auth/screens/verification_screen.dart
Normal file
146
lib/features/auth/screens/verification_screen.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
// lib/features/auth/screens/verification_screen.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:kmobile/features/auth/controllers/auth_cubit.dart';
|
||||
import 'package:kmobile/features/auth/controllers/auth_state.dart';
|
||||
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
||||
import 'package:kmobile/features/auth/screens/sms_verification_helper.dart';
|
||||
import '../../../app.dart';
|
||||
|
||||
class VerificationScreen extends StatefulWidget {
|
||||
final String customerNo;
|
||||
final String password;
|
||||
|
||||
const VerificationScreen({
|
||||
super.key,
|
||||
required this.customerNo,
|
||||
required this.password,
|
||||
});
|
||||
|
||||
@override
|
||||
State<VerificationScreen> createState() => _VerificationScreenState();
|
||||
}
|
||||
|
||||
class _VerificationScreenState extends State<VerificationScreen> {
|
||||
final SmsVerificationHelper _smsVerificationHelper = SmsVerificationHelper();
|
||||
late Timer _timer;
|
||||
int _start = 120;
|
||||
String _message = "Attempting verification...";
|
||||
String? _error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
context.read<AuthCubit>().startVerification();
|
||||
startTimer();
|
||||
_verifySmsAndLogin();
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
const oneSec = Duration(seconds: 1);
|
||||
_timer = Timer.periodic(
|
||||
oneSec,
|
||||
(Timer timer) {
|
||||
if (_start == 0) {
|
||||
timer.cancel();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_error = "Verification timed out.";
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setState(() {
|
||||
_start--;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _verifySmsAndLogin() async {
|
||||
await _smsVerificationHelper.initiateSmsSequence(context: context);
|
||||
// After SMS sequence completes, proceed with login
|
||||
_timer.cancel(); // Stop the timer
|
||||
if (mounted) {
|
||||
context.read<AuthCubit>().login(widget.customerNo, widget.password);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: BlocListener<AuthCubit, AuthState>(
|
||||
listenWhen: (previous, current) {
|
||||
return current is! AuthVerificationInProgress && current is! AuthInitial;
|
||||
},
|
||||
listener: (context, state) {
|
||||
if (state is Authenticated) {
|
||||
_timer.cancel();
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MPinScreen(
|
||||
mode: MPinMode.set,
|
||||
onCompleted: (_) {
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const NavigationScaffold(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (state is AuthError) {
|
||||
_timer.cancel();
|
||||
setState(() {
|
||||
_error = state.message;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Center(
|
||||
child: _error != null
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.red, size: 80),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_error!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.red, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Back to Login"),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
const SizedBox(height: 16),
|
||||
Text(_message),
|
||||
const SizedBox(height: 16),
|
||||
Text("Time remaining: $_start seconds"),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user