import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:workmanager/workmanager.dart'; import 'app/app_bootstrap.dart'; import 'features/homes/services/geofence_notifications_service.dart'; import 'screens/homes_screen.dart'; import 'screens/remote_screen.dart'; import 'services/geofence_worker.dart'; /// Top-level callback для workmanager (выполняется в отдельном изоляте). @pragma('vm:entry-point') void callbackDispatcher() { DartPluginRegistrant.ensureInitialized(); Workmanager().executeTask((taskName, inputData) async { if (taskName == geofenceTaskName) { return await executeGeofenceCheck(); } return true; }); } Future main() async { WidgetsFlutterBinding.ensureInitialized(); await GeofenceNotificationsService().initialize(); // Инициализация workmanager Workmanager().initialize(callbackDispatcher); runApp(const ProviderScope(child: IgnisApp())); } class IgnisApp extends StatelessWidget { const IgnisApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Ignis', debugShowCheckedModeBanner: false, theme: ThemeData.dark(useMaterial3: true).copyWith( scaffoldBackgroundColor: const Color(0xFF0E0E0E), colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepOrange, brightness: Brightness.dark, ), appBarTheme: const AppBarTheme( backgroundColor: Color(0xFF1A1A1A), elevation: 0, centerTitle: true, ), cardTheme: CardThemeData( color: const Color(0xFF1E1E1E), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), sliderTheme: const SliderThemeData( trackHeight: 4, thumbShape: RoundSliderThumbShape(enabledThumbRadius: 8), ), ), home: const MainGate(), ); } } /// Стартовый экран -- проверяет наличие домов и перенаправляет class MainGate extends ConsumerStatefulWidget { const MainGate({super.key}); @override ConsumerState createState() => _MainGateState(); } class _MainGateState extends ConsumerState { @override void initState() { super.initState(); Future.microtask(_bootstrap); } Future _bootstrap() async { await ref.read(appBootstrapProvider.notifier).bootstrap(); if (!mounted) return; final bootstrap = ref.read(appBootstrapProvider); switch (bootstrap.status) { case AppBootstrapStatus.ready: Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const RemoteScreen()), ); break; case AppBootstrapStatus.noHomes: Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const HomesScreen()), ); break; case AppBootstrapStatus.bootstrapping: case AppBootstrapStatus.authFailed: case AppBootstrapStatus.networkFailed: case AppBootstrapStatus.invalidConfig: case AppBootstrapStatus.failed: break; } } @override Widget build(BuildContext context) { final bootstrap = ref.watch(appBootstrapProvider); final message = bootstrap.message; if (!bootstrap.isBootstrapping && message != null) { return Scaffold( body: Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.warning_amber_rounded, color: Colors.deepOrange, size: 56, ), const SizedBox(height: 16), const Text( 'Не удалось запустить приложение', textAlign: TextAlign.center, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white54), ), const SizedBox(height: 20), FilledButton.icon( onPressed: _bootstrap, icon: const Icon(Icons.refresh), label: const Text('Повторить'), ), TextButton( onPressed: () => Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const HomesScreen()), ), child: const Text('Открыть список домов'), ), ], ), ), ), ); } return const Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(color: Colors.deepOrange), SizedBox(height: 20), Text("Загрузка...", style: TextStyle(color: Colors.white54)), ], ), ), ); } }