5eafd7ad38
Changelog: Redesigned theme colors and color styles
194 lines
6.0 KiB
Dart
194 lines
6.0 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/common/window.dart';
|
|
import 'package:fl_clash/bootstrap.dart';
|
|
import 'package:fl_clash/common/system_dns.dart';
|
|
import 'package:fl_clash/l10n/l10n.dart';
|
|
import 'package:fl_clash/manager/hotkey_manager.dart';
|
|
import 'package:fl_clash/manager/manager.dart';
|
|
import 'package:fl_clash/plugins/app.dart';
|
|
import 'package:fl_clash/providers/providers.dart';
|
|
import 'package:fl_clash/state.dart';
|
|
import 'package:fl_clash/ui/ui.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'pages/pages.dart';
|
|
|
|
Widget buildManagerStack({
|
|
required bool isDesktop,
|
|
required Future<void> Function(List<ConnectivityResult> results)
|
|
onConnectivityChanged,
|
|
Stream<ProcessSignal>? terminateSignals,
|
|
required Widget child,
|
|
}) {
|
|
final platformApp = isDesktop
|
|
? WindowHeaderContainer(child: child)
|
|
: VpnManager(child: child);
|
|
final state = AppStateManager(
|
|
child: CoreManager(
|
|
child: ConnectivityManager(
|
|
onConnectivityChanged: onConnectivityChanged,
|
|
child: platformApp,
|
|
),
|
|
),
|
|
);
|
|
final platformState = isDesktop
|
|
? WindowManager(
|
|
terminateSignals: terminateSignals,
|
|
child: TrayManager(
|
|
child: HotKeyManager(child: ProxyManager(child: state)),
|
|
),
|
|
)
|
|
: AndroidManager(child: TileManager(child: state));
|
|
return AppEnvManager(
|
|
child: LocaleManager(
|
|
child: StatusManager(child: ThemeManager(child: platformState)),
|
|
),
|
|
);
|
|
}
|
|
|
|
class Application extends ConsumerStatefulWidget {
|
|
const Application({super.key});
|
|
|
|
@override
|
|
ConsumerState<Application> createState() => ApplicationState();
|
|
}
|
|
|
|
class ApplicationState extends ConsumerState<Application> {
|
|
Timer? _autoUpdateProfilesTaskTimer;
|
|
bool _preHasVpn = false;
|
|
// Windows can only watch SIGINT and SIGHUP.
|
|
final _terminateSignals = system.isDesktop && !system.isWindows
|
|
? ProcessSignal.sigterm.watch()
|
|
: null;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
SystemNavigator.setFrameworkHandlesBack(true);
|
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
|
if (globalState.navigatorKey.currentContext != null) {
|
|
await bootstrap.attach();
|
|
} else {
|
|
exit(0);
|
|
}
|
|
_autoUpdateProfilesTask();
|
|
_initLink();
|
|
if (!safeModeBuild) {
|
|
unawaited(app?.initShortcuts());
|
|
}
|
|
});
|
|
}
|
|
|
|
void _initLink() {
|
|
linkManager.initAppLinksListen((url) async {
|
|
unawaited(window?.show());
|
|
final message = currentAppLocalizations.createProfileFromUrlTip(url);
|
|
final parts = message.split(url);
|
|
final res = await dialogs.showMessage(
|
|
title: currentAppLocalizations.addProfile,
|
|
message: TextSpan(
|
|
children: [
|
|
TextSpan(text: parts.first),
|
|
TextSpan(
|
|
text: url,
|
|
style: TextStyle(
|
|
color: context.colorScheme.primary,
|
|
decoration: TextDecoration.underline,
|
|
decorationColor: context.colorScheme.primary,
|
|
),
|
|
),
|
|
if (parts.length > 1) TextSpan(text: parts.last),
|
|
],
|
|
),
|
|
);
|
|
if (res != true) return;
|
|
unawaited(
|
|
ref.read(profilesActionProvider.notifier).addProfileFromLink(url),
|
|
);
|
|
});
|
|
}
|
|
|
|
void _autoUpdateProfilesTask() {
|
|
_autoUpdateProfilesTaskTimer = Timer(const Duration(minutes: 20), () async {
|
|
await ref.read(profilesActionProvider.notifier).autoUpdateProfiles();
|
|
await ref.read(resourcesActionProvider.notifier).autoUpdate();
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
_autoUpdateProfilesTask();
|
|
});
|
|
}
|
|
|
|
Future<void> _handleConnectivityChanged(
|
|
List<ConnectivityResult> results,
|
|
) async {
|
|
commonPrint.log('connectivityChanged ${results.toString()}');
|
|
unawaited(systemDnsCoordinator?.resync() ?? Future.value());
|
|
unawaited(ref.read(systemActionProvider.notifier).updateLocalIp());
|
|
final hasVpn = results.contains(ConnectivityResult.vpn);
|
|
if (_preHasVpn == hasVpn) {
|
|
ref.read(routeTrackerProvider.notifier).bumpHostEpoch();
|
|
}
|
|
_preHasVpn = hasVpn;
|
|
}
|
|
|
|
@override
|
|
Widget build(context) {
|
|
return Consumer(
|
|
builder: (_, ref, child) {
|
|
final locale = ref.watch(
|
|
appSettingProvider.select((state) => state.locale),
|
|
);
|
|
final themeProps = ref.watch(themeSettingProvider);
|
|
return MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
navigatorKey: globalState.navigatorKey,
|
|
onNavigationNotification: (_) => true,
|
|
localizationsDelegates: const [
|
|
AppLocalizations.delegate,
|
|
AppUiLocalizations.delegate,
|
|
...GlobalMaterialLocalizations.delegates,
|
|
],
|
|
builder: (context, child) {
|
|
return ThemeCrossFade(
|
|
child: buildManagerStack(
|
|
isDesktop: system.isDesktop,
|
|
onConnectivityChanged: _handleConnectivityChanged,
|
|
terminateSignals: _terminateSignals,
|
|
child: RemoteFocusAdapter(
|
|
enabled: system.isTV,
|
|
floatingKeyboard: app?.floatingKeyboard,
|
|
child: child!,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
scrollBehavior: const BaseScrollBehavior(),
|
|
title: appName,
|
|
locale: getLocaleForString(locale),
|
|
supportedLocales: AppLocalizations.delegate.supportedLocales,
|
|
themeMode: themeProps.themeMode,
|
|
theme: watchAppTheme(ref, Brightness.light),
|
|
darkTheme: watchAppTheme(ref, Brightness.dark),
|
|
themeAnimationStyle: AnimationStyle.noAnimation,
|
|
home: KeyboardInsetHold(child: child!),
|
|
);
|
|
},
|
|
child: const HomePage(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
linkManager.destroy();
|
|
_autoUpdateProfilesTaskTimer?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|