import 'dart:async'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'providers/auth_provider.dart'; import 'providers/accounts_provider.dart'; import 'providers/transactions_provider.dart'; import 'providers/chat_provider.dart'; import 'screens/backend_config_screen.dart'; import 'screens/login_screen.dart'; import 'screens/main_navigation_screen.dart'; import 'services/api_config.dart'; import 'services/connectivity_service.dart'; import 'services/log_service.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await ApiConfig.initialize(); // Add initial log entry LogService.instance.info('App', 'Sure Finance app starting...'); runApp(const SureApp()); } class SureApp extends StatelessWidget { const SureApp({super.key}); @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => LogService.instance), ChangeNotifierProvider(create: (_) => ConnectivityService()), ChangeNotifierProvider(create: (_) => AuthProvider()), ChangeNotifierProvider(create: (_) => ChatProvider()), ChangeNotifierProxyProvider( create: (_) => AccountsProvider(), update: (_, connectivityService, accountsProvider) { if (accountsProvider == null) { final provider = AccountsProvider(); provider.setConnectivityService(connectivityService); return provider; } else { accountsProvider.setConnectivityService(connectivityService); return accountsProvider; } }, ), ChangeNotifierProxyProvider( create: (_) => TransactionsProvider(), update: (_, connectivityService, transactionsProvider) { if (transactionsProvider == null) { final provider = TransactionsProvider(); provider.setConnectivityService(connectivityService); return provider; } else { transactionsProvider.setConnectivityService(connectivityService); return transactionsProvider; } }, ), ], child: MaterialApp( title: 'Sure Finance', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF6366F1), brightness: Brightness.light, ), useMaterial3: true, appBarTheme: const AppBarTheme( centerTitle: true, elevation: 0, ), cardTheme: CardThemeData( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), filled: true, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), darkTheme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF6366F1), brightness: Brightness.dark, ), useMaterial3: true, appBarTheme: const AppBarTheme( centerTitle: true, elevation: 0, ), cardTheme: CardThemeData( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), filled: true, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), themeMode: ThemeMode.system, routes: { '/config': (context) => const BackendConfigScreen(), '/login': (context) => const LoginScreen(), '/home': (context) => const MainNavigationScreen(), }, home: const AppWrapper(), ), ); } } class AppWrapper extends StatefulWidget { const AppWrapper({super.key}); @override State createState() => _AppWrapperState(); } class _AppWrapperState extends State { bool _isCheckingConfig = true; bool _hasBackendUrl = false; late final AppLinks _appLinks; StreamSubscription? _linkSubscription; @override void initState() { super.initState(); _checkBackendConfig(); _initDeepLinks(); } @override void dispose() { _linkSubscription?.cancel(); super.dispose(); } void _initDeepLinks() { _appLinks = AppLinks(); // Handle deep link that launched the app (cold start) _appLinks.getInitialLink().then((uri) { if (uri != null) _handleDeepLink(uri); }).catchError((e, stackTrace) { LogService.instance.error('DeepLinks', 'Initial link error: $e\n$stackTrace'); }); // Listen for deep links while app is running _linkSubscription = _appLinks.uriLinkStream.listen( (uri) => _handleDeepLink(uri), onError: (e, stackTrace) { LogService.instance.error('DeepLinks', 'Link stream error: $e\n$stackTrace'); }, ); } void _handleDeepLink(Uri uri) { if (uri.scheme == 'sureapp' && uri.host == 'oauth') { final authProvider = Provider.of(context, listen: false); authProvider.handleSsoCallback(uri); } } Future _checkBackendConfig() async { final hasUrl = await ApiConfig.initialize(); if (mounted) { setState(() { _hasBackendUrl = hasUrl; _isCheckingConfig = false; }); } } void _onBackendConfigSaved() { setState(() { _hasBackendUrl = true; }); } void _goToBackendConfig() { setState(() { _hasBackendUrl = false; }); } @override Widget build(BuildContext context) { if (_isCheckingConfig) { return const Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } if (!_hasBackendUrl) { return BackendConfigScreen( onConfigSaved: _onBackendConfigSaved, ); } return Consumer( builder: (context, authProvider, _) { // Only show loading spinner during initial auth check if (authProvider.isInitializing) { return const Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } if (authProvider.isAuthenticated) { return const MainNavigationScreen(); } return LoginScreen( onGoToSettings: _goToBackendConfig, ); }, ); } }