import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:local_auth/local_auth.dart'; import 'core/theme/app_theme.dart'; import 'core/theme/theme_provider.dart'; import 'features/auth/presentation/auth_screen.dart'; import 'features/auth/presentation/setup_wizard_screen.dart'; import 'features/dashboard/presentation/dashboard_screen.dart'; import 'features/onboarding/presentation/onboarding_screen.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'core/network/dio_client.dart'; final GlobalKey navigatorKey = GlobalKey(); void main() { WidgetsFlutterBinding.ensureInitialized(); runApp( const ProviderScope( child: KifiApp(), ), ); } class KifiApp extends ConsumerStatefulWidget { const KifiApp({super.key}); @override ConsumerState createState() => _KifiAppState(); } class _KifiAppState extends ConsumerState { bool _isLoading = true; bool _isAuthenticated = false; bool _isSetupCompleted = true; bool _hasSeenOnboarding = false; final LocalAuthentication _localAuth = LocalAuthentication(); String? _startupError; @override void initState() { super.initState(); _checkAuthStatus(); } Future _authenticateWithBiometrics() async { try { final isAvailable = await _localAuth.canCheckBiometrics; final isDeviceSupported = await _localAuth.isDeviceSupported(); if (!isAvailable || !isDeviceSupported) { return true; // If device does not support biometrics, allow access via stored token } return await _localAuth.authenticate( localizedReason: 'Please authenticate to access Kifi', biometricOnly: true, persistAcrossBackgrounding: true, ); } catch (e) { return false; // Fail safe } } Future _checkAuthStatus() async { try { const storage = FlutterSecureStorage(); final token = await storage.read(key: 'jwt_token'); final prefs = await SharedPreferences.getInstance(); final hasSeenOnboarding = prefs.getBool('has_seen_onboarding') ?? false; if (token != null && token.isNotEmpty) { // Prompt for biometrics if returning user final authSuccess = await _authenticateWithBiometrics(); if (!authSuccess) { setState(() { _isAuthenticated = false; _hasSeenOnboarding = hasSeenOnboarding; _isLoading = false; }); return; // User failed biometrics, leave them on login screen } // Validate token and check account setup status with backend try { final res = await DioClient().dio.get('/account/setup/status'); final status = res.data != null ? res.data['status'] : null; setState(() { _isAuthenticated = true; _isSetupCompleted = status == 'COMPLETED'; _hasSeenOnboarding = hasSeenOnboarding; _isLoading = false; }); } catch (_) { // Token is stale / invalid / user was wiped from DB -> clear token and go to AuthScreen await storage.delete(key: 'jwt_token'); await DioClient().clearToken(); setState(() { _isAuthenticated = false; _hasSeenOnboarding = hasSeenOnboarding; _isLoading = false; }); } } else { setState(() { _isAuthenticated = false; _hasSeenOnboarding = hasSeenOnboarding; _isLoading = false; }); } } catch (e) { setState(() { _startupError = e.toString(); _isLoading = false; }); } } @override Widget build(BuildContext context) { final themeMode = ref.watch(themeProvider); return MaterialApp( navigatorKey: navigatorKey, title: 'Kifi', debugShowCheckedModeBanner: false, theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: themeMode, home: _startupError != null ? Scaffold(body: Center(child: Padding(padding: const EdgeInsets.all(20), child: Text("Startup Error: $_startupError", style: const TextStyle(color: Colors.red))))) : _isLoading ? const Scaffold(body: Center(child: CircularProgressIndicator())) : ((!kIsWeb && !_hasSeenOnboarding) ? const OnboardingScreen() : (_isAuthenticated ? (_isSetupCompleted ? const DashboardScreen() : const SetupWizardScreen()) : const AuthScreen())), ); } }