import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:hiddify/core/analytics/analytics_controller.dart'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/logger/logger.dart'; import 'package:hiddify/core/logger/logger_controller.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/preferences/preferences_migration.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/features/app/widget/app.dart'; import 'package:hiddify/features/auto_start/notifier/auto_start_notifier.dart'; import 'package:hiddify/features/chain/model/chain_enum.dart'; import 'package:hiddify/features/chain/notifier/chain_profile_notifier.dart'; import 'package:hiddify/features/log/data/log_data_providers.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/system_tray/notifier/system_tray_notifier.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hiddify/riverpod_observer.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; Future lazyBootstrap(WidgetsBinding widgetsBinding, Environment env) async { if (!kIsWeb) { FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); } LoggerController.preInit(); FlutterError.onError = Logger.logFlutterError; WidgetsBinding.instance.platformDispatcher.onError = Logger.logPlatformDispatcherError; final stopWatch = Stopwatch()..start(); final container = ProviderContainer(overrides: [environmentProvider.overrideWithValue(env)]); await _init("directories", () => container.read(appDirectoriesProvider.future)); LoggerController.init(container.read(logPathResolverProvider).appFile().path); final appInfo = await _init("app info", () => container.read(appInfoProvider.future)); await _init("preferences", () => container.read(sharedPreferencesProvider.future)); final enableAnalytics = await container.read(analyticsControllerProvider.future); if (enableAnalytics) { await _init("analytics", () => container.read(analyticsControllerProvider.notifier).enableAnalytics()); } await _init("preferences migration", () async { try { await PreferencesMigration(sharedPreferences: container.read(sharedPreferencesProvider).requireValue).migrate(); } catch (e, stackTrace) { Logger.bootstrap.error("preferences migration failed", e, stackTrace); if (env == Environment.dev) rethrow; Logger.bootstrap.info("clearing preferences"); await container.read(sharedPreferencesProvider).requireValue.clear(); } }); final debug = container.read(debugModeNotifierProvider) || kDebugMode; if (PlatformUtils.isDesktop) { await _init("window controller", () => container.read(windowNotifierProvider.future)); final silentStart = container.read(Preferences.silentStart); Logger.bootstrap.debug("silent start [${silentStart ? "Enabled" : "Disabled"}]"); if (!silentStart) { await container.read(windowNotifierProvider.notifier).show(focus: false); } else { Logger.bootstrap.debug("silent start, remain hidden accessible via tray"); } await _init("auto start service", () => container.read(autoStartNotifierProvider.future)); } await _init("logs repository", () => container.read(logRepositoryProvider.future)); await _init("logger controller", () => LoggerController.postInit(debug)); Logger.bootstrap.info(appInfo.format()); await _init("profile repository", () => container.read(profileRepositoryProvider.future)); await _init("translations", () => container.read(translationsProvider.future)); await _safeInit("active profile", () => container.read(activeProfileProvider.future), timeout: 1000); await _init( "chain profile extra security", () => container.read(chainProfileNotifierProvider(ChainType.extraSecurity).future), ); await _init( "chain profile unblocker", () => container.read(chainProfileNotifierProvider(ChainType.unblocker).future), ); await _init("hiddify-core", () => container.read(hiddifyCoreServiceProvider).init()); // Eagerly listen to activeProxyNotifierProvider to force synchronous evaluation in microtasks, // avoiding lazy build-phase flushes and sibling dependency collisions on the Home page. container.listen(activeProxyNotifierProvider, (previous, next) {}); if (!kIsWeb) { // await _safeInit( // "deep link service", // () => container.read(deepLinkNotifierProvider.future), // timeout: 1000, // ); if (PlatformUtils.isDesktop) { await _safeInit("system tray", () => container.read(systemTrayNotifierProvider.future), timeout: 1000); } if (PlatformUtils.isAndroid) { await _safeInit("android display mode", () async { await FlutterDisplayMode.setHighRefreshRate(); }); } } Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]"); stopWatch.stop(); runApp( ProviderScope( parent: container, observers: [RiverpodObserver()], child: SentryUserInteractionWidget(child: const App()), ), ); if (!kIsWeb) { FlutterNativeSplash.remove(); } // SentryFlutter.s(DateTime.now().toUtc()); } Future _init(String name, Future Function() initializer, {int? timeout}) async { final stopWatch = Stopwatch()..start(); Logger.bootstrap.info("initializing [$name]"); Future func() => timeout != null ? initializer().timeout(Duration(milliseconds: timeout)) : initializer(); try { final result = await func(); Logger.bootstrap.debug("[$name] initialized in ${stopWatch.elapsedMilliseconds}ms"); return result; } catch (e, stackTrace) { Logger.bootstrap.error("[$name] error initializing", e, stackTrace); rethrow; } finally { stopWatch.stop(); } } Future _safeInit(String name, Future Function() initializer, {int? timeout}) async { try { return await _init(name, initializer, timeout: timeout); } catch (e) { return null; } }