import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'login_view.dart'; import 'jobs_view.dart'; import 'cargo_items_view.dart'; import 'chats_view.dart'; import 'chat_details_view.dart'; import 'settings_view.dart'; import 'models/job.dart'; import 'models/chat.dart'; import 'services/database_service.dart'; import 'services/chat_service.dart'; import 'app_state.dart'; import 'navigation_observer.dart'; import 'services/notification_service.dart'; import 'services/websocket_service.dart'; import 'l10n/app_localizations.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize SQLite database await DatabaseService().initialize(); // Load data from database await AppState().loadLoginFromDatabase(); // Load language preference await AppState().loadLanguagePreference(); // Load jobs from database to trigger message type logging at startup await AppState().refreshJobsFromDatabase(); // Prepare chat service before WebSocket events start flowing await ChatService().initialize(); // Initialize notification service for local notifications with sound await NotificationService().initialize(); // Note: WebSocket connection is initiated from the view that needs it: // - If userId exists: JobsView initiates connection on startup // - If no userId: LoginView initiates connection when login button is clicked runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State with WidgetsBindingObserver { static const Duration _resumeReconnectThreshold = Duration(seconds: 30); final AppState _appState = AppState(); final WebSocketService _webSocketService = WebSocketService(); DateTime? _lastPausedAt; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused) { _lastPausedAt = DateTime.now(); return; } if (state == AppLifecycleState.resumed) { final pausedAt = _lastPausedAt; _lastPausedAt = null; if (pausedAt == null) { return; } final standbyDuration = DateTime.now().difference(pausedAt); if (standbyDuration < _resumeReconnectThreshold || !_appState.isLoggedIn) { return; } _webSocketService.reconnectForAppResume(); } } @override Widget build(BuildContext context) { // Check if user is already logged in final initialRoute = _appState.isLoggedIn ? '/jobs' : '/login'; return ValueListenableBuilder( valueListenable: localeNotifier, builder: (context, locale, child) { return MaterialApp( title: 'VotianLT App', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), // Localization configuration locale: locale, localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: supportedLanguageCodes.map((code) => Locale(code)).toList(), navigatorObservers: [routeObserver], initialRoute: initialRoute, onGenerateRoute: (settings) { switch (settings.name) { case '/login': final arg = settings.arguments; final suppress = (arg is bool) ? arg : false; return MaterialPageRoute( builder: (_) => LoginView(suppressConnectionSnack: suppress), ); case '/jobs': return MaterialPageRoute(builder: (_) => const JobsView()); case '/cargo_items': final job = settings.arguments as Job; return MaterialPageRoute( builder: (_) => CargoItemsView(job: job), ); case '/chats': return MaterialPageRoute(builder: (_) => const ChatsView()); case '/chat_details': final chat = settings.arguments as Chat; return MaterialPageRoute( builder: (_) => ChatDetailsView(chat: chat), ); case '/settings': return MaterialPageRoute(builder: (_) => const SettingsView()); default: return MaterialPageRoute( builder: (_) => const LoginView(suppressConnectionSnack: false), ); } }, ); }, ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('You have pushed the button this many times:'), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }