Files
umbrix/lib/bootstrap.dart
Umbrix Developer 76a374950f feat: mobile-like window size and always-visible stats
- Changed window size to mobile phone format (400x800)
- Removed width condition for ActiveProxyFooter - now always visible
- Added run-umbrix.sh launch script with icon copying
- Stats cards now display on all screen sizes
2026-01-17 13:09:20 +03:00

211 lines
6.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:umbrix/core/analytics/analytics_controller.dart';
import 'package:umbrix/core/app_info/app_info_provider.dart';
import 'package:umbrix/core/directories/directories_provider.dart';
import 'package:umbrix/core/logger/logger.dart';
import 'package:umbrix/core/logger/logger_controller.dart';
import 'package:umbrix/core/model/environment.dart';
import 'package:umbrix/core/preferences/general_preferences.dart';
import 'package:umbrix/core/preferences/preferences_migration.dart';
import 'package:umbrix/core/preferences/preferences_provider.dart';
import 'package:umbrix/features/app/widget/app.dart';
import 'package:umbrix/features/auto_start/notifier/auto_start_notifier.dart';
import 'package:umbrix/features/common/custom_splash_screen.dart';
import 'package:umbrix/features/deep_link/notifier/deep_link_notifier.dart';
import 'package:umbrix/features/log/data/log_data_providers.dart';
import 'package:umbrix/features/profile/data/profile_data_providers.dart';
import 'package:umbrix/features/profile/notifier/active_profile_notifier.dart';
import 'package:umbrix/features/system_tray/notifier/system_tray_notifier.dart';
import 'package:umbrix/features/window/notifier/window_notifier.dart';
import 'package:umbrix/singbox/service/singbox_service_provider.dart';
import 'package:umbrix/utils/utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> lazyBootstrap(
WidgetsBinding widgetsBinding,
Environment env,
) async {
LoggerController.preInit();
FlutterError.onError = Logger.logFlutterError;
WidgetsBinding.instance.platformDispatcher.onError = Logger.logPlatformDispatcherError;
final stopWatch = Stopwatch()..start();
final container = ProviderContainer(
overrides: [
environmentProvider.overrideWithValue(env),
],
);
// Показываем кастомный splash screen с круговым индикатором
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: CustomSplashScreen(
onInitializationComplete: () async {
await _performBootstrap(container, stopWatch, env);
},
),
),
);
}
Future<void> _performBootstrap(
ProviderContainer container,
Stopwatch stopWatch,
Environment env,
) async {
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).open(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 _safeInit(
"active profile",
() => container.read(activeProfileProvider.future),
timeout: 1000,
);
await _safeInit(
"deep link service",
() => container.read(deepLinkNotifierProvider.future),
timeout: 1000,
);
await _init(
"sing-box",
() => container.read(singboxServiceProvider).init(),
);
if (PlatformUtils.isDesktop) {
await _safeInit(
"system tray",
() => container.read(systemTrayNotifierProvider.future),
timeout: 1000,
);
}
if (Platform.isAndroid) {
await _safeInit(
"android display mode",
() async {
await FlutterDisplayMode.setHighRefreshRate();
},
);
}
Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]");
stopWatch.stop();
runApp(
ProviderScope(
parent: container,
child: SentryUserInteractionWidget(
child: const App(),
),
),
);
}
Future<T> _init<T>(
String name,
Future<T> Function() initializer, {
int? timeout,
}) async {
final stopWatch = Stopwatch()..start();
Logger.bootstrap.info("initializing [$name]");
Future<T> 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<T?> _safeInit<T>(
String name,
Future<T> Function() initializer, {
int? timeout,
}) async {
try {
return await _init(name, initializer, timeout: timeout);
} catch (e) {
return null;
}
}