Add android separates the core process

Support core status check and force restart

Optimize proxies page and access page

Update flutter and pub dependencies

Update go version

Optimize more details
This commit is contained in:
chen08209
2025-07-31 17:09:18 +08:00
parent e956373ef4
commit ed7868282a
276 changed files with 85260 additions and 80090 deletions

View File

@@ -1,8 +1,8 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/l10n/l10n.dart';
import 'package:fl_clash/manager/hotkey_manager.dart';
import 'package:fl_clash/manager/manager.dart';
@@ -17,9 +17,7 @@ import 'controller.dart';
import 'pages/pages.dart';
class Application extends ConsumerStatefulWidget {
const Application({
super.key,
});
const Application({super.key});
@override
ConsumerState<Application> createState() => ApplicationState();
@@ -48,7 +46,6 @@ class ApplicationState extends ConsumerState<Application> {
@override
void initState() {
super.initState();
_autoUpdateGroupTask();
_autoUpdateProfilesTask();
globalState.appController = AppController(context, ref);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@@ -62,15 +59,6 @@ class ApplicationState extends ConsumerState<Application> {
});
}
void _autoUpdateGroupTask() {
_autoUpdateGroupTaskTimer = Timer(const Duration(milliseconds: 20000), () {
WidgetsBinding.instance.addPostFrameCallback((_) {
globalState.appController.updateGroupsDebounce();
_autoUpdateGroupTask();
});
});
}
void _autoUpdateProfilesTask() {
_autoUpdateProfilesTaskTimer = Timer(const Duration(minutes: 20), () async {
await globalState.appController.autoUpdateProfiles();
@@ -78,32 +66,24 @@ class ApplicationState extends ConsumerState<Application> {
});
}
Widget _buildPlatformState(Widget child) {
Widget _buildPlatformState({required Widget child}) {
if (system.isDesktop) {
return WindowManager(
child: TrayManager(
child: HotKeyManager(
child: ProxyManager(
child: child,
),
),
child: HotKeyManager(child: ProxyManager(child: child)),
),
);
}
return AndroidManager(
child: TileManager(
child: child,
),
);
return AndroidManager(child: TileManager(child: child));
}
Widget _buildState(Widget child) {
Widget _buildState({required Widget child}) {
return AppStateManager(
child: ClashManager(
child: CoreManager(
child: ConnectivityManager(
onConnectivityChanged: (results) async {
if (!results.contains(ConnectivityResult.vpn)) {
clashCore.closeConnections();
coreController.closeConnections();
}
globalState.appController.updateLocalIp();
globalState.appController.addCheckIpNumDebounce();
@@ -114,81 +94,68 @@ class ApplicationState extends ConsumerState<Application> {
);
}
Widget _buildPlatformApp(Widget child) {
Widget _buildPlatformApp({required Widget child}) {
if (system.isDesktop) {
return WindowHeaderContainer(
child: child,
);
return WindowHeaderContainer(child: child);
}
return VpnManager(
child: child,
);
return VpnManager(child: child);
}
Widget _buildApp(Widget child) {
return MessageManager(
child: ThemeManager(
child: child,
),
);
Widget _buildApp({required Widget child}) {
return MessageManager(child: ThemeManager(child: child));
}
@override
Widget build(context) {
return _buildPlatformState(
_buildState(
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,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
builder: (_, child) {
return AppEnvManager(
child: _buildApp(
AppSidebarContainer(
child: _buildPlatformApp(
child!,
),
),
),
);
},
scrollBehavior: BaseScrollBehavior(),
title: appName,
locale: utils.getLocaleForString(locale),
supportedLocales: AppLocalizations.delegate.supportedLocales,
themeMode: themeProps.themeMode,
theme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.light,
primaryColor: themeProps.primaryColor,
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,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
builder: (_, child) {
return AppEnvManager(
child: _buildApp(
child: _buildPlatformState(
child: _buildState(child: _buildPlatformApp(child: child!)),
),
),
darkTheme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.dark,
primaryColor: themeProps.primaryColor,
).toPureBlack(themeProps.pureBlack),
),
home: child!,
);
},
child: const HomePage(),
),
),
scrollBehavior: BaseScrollBehavior(),
title: appName,
locale: utils.getLocaleForString(locale),
supportedLocales: AppLocalizations.delegate.supportedLocales,
themeMode: themeProps.themeMode,
theme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.light,
primaryColor: themeProps.primaryColor,
),
),
darkTheme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.dark,
primaryColor: themeProps.primaryColor,
).toPureBlack(themeProps.pureBlack),
),
home: child!,
);
},
child: const HomePage(),
);
}
@@ -197,7 +164,7 @@ class ApplicationState extends ConsumerState<Application> {
linkManager.destroy();
_autoUpdateGroupTaskTimer?.cancel();
_autoUpdateProfilesTaskTimer?.cancel();
await clashCore.destroy();
await coreController.destroy();
await globalState.appController.savePreferences();
await globalState.appController.handleExit();
super.dispose();

View File

@@ -1,284 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/clash/interface.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
class ClashCore {
static ClashCore? _instance;
late ClashHandlerInterface clashInterface;
ClashCore._internal() {
if (system.isAndroid) {
clashInterface = clashLib!;
} else {
clashInterface = clashService!;
}
}
factory ClashCore() {
_instance ??= ClashCore._internal();
return _instance!;
}
Future<bool> preload() {
return clashInterface.preload();
}
static Future<void> initGeo() async {
final homePath = await appPath.homeDirPath;
final homeDir = Directory(homePath);
final isExists = await homeDir.exists();
if (!isExists) {
await homeDir.create(recursive: true);
}
const geoFileNameList = [
mmdbFileName,
geoIpFileName,
geoSiteFileName,
asnFileName,
];
try {
for (final geoFileName in geoFileNameList) {
final geoFile = File(
join(homePath, geoFileName),
);
final isExists = await geoFile.exists();
if (isExists) {
continue;
}
final data = await rootBundle.load('assets/data/$geoFileName');
List<int> bytes = data.buffer.asUint8List();
await geoFile.writeAsBytes(bytes, flush: true);
}
} catch (e) {
exit(0);
}
}
Future<bool> init() async {
await initGeo();
if (globalState.config.appSetting.openLogs) {
clashCore.startLog();
} else {
clashCore.stopLog();
}
final homeDirPath = await appPath.homeDirPath;
return await clashInterface.init(
InitParams(
homeDir: homeDirPath,
version: globalState.appState.version,
),
);
}
Future<bool> setState(CoreState state) async {
return await clashInterface.setState(state);
}
Future<void> shutdown() async {
await clashInterface.shutdown();
}
FutureOr<bool> get isInit => clashInterface.isInit;
FutureOr<String> validateConfig(String data) {
return clashInterface.validateConfig(data);
}
Future<String> updateConfig(UpdateParams updateParams) async {
return await clashInterface.updateConfig(updateParams);
}
Future<String> setupConfig(SetupParams setupParams) async {
return await clashInterface.setupConfig(setupParams);
}
Future<List<Group>> getProxiesGroups() async {
final proxies = await clashInterface.getProxies();
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
})
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group['all'] = ((group['all'] ?? []) as List)
.map(
(name) => proxies[name],
)
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
return groupsRaw
.map(
(e) => Group.fromJson(e),
)
.toList();
}
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
return await clashInterface.changeProxy(changeProxyParams);
}
Future<List<TrackerInfo>> getConnections() async {
final res = await clashInterface.getConnections();
final connectionsData = json.decode(res) as Map;
final connectionsRaw = connectionsData['connections'] as List? ?? [];
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
}
void closeConnection(String id) {
clashInterface.closeConnection(id);
}
void closeConnections() {
clashInterface.closeConnections();
}
void resetConnections() {
clashInterface.resetConnections();
}
Future<List<ExternalProvider>> getExternalProviders() async {
final externalProvidersRawString =
await clashInterface.getExternalProviders();
if (externalProvidersRawString.isEmpty) {
return [];
}
return Isolate.run<List<ExternalProvider>>(
() {
final externalProviders =
(json.decode(externalProvidersRawString) as List<dynamic>)
.map(
(item) => ExternalProvider.fromJson(item),
)
.toList();
return externalProviders;
},
);
}
Future<ExternalProvider?> getExternalProvider(
String externalProviderName) async {
final externalProvidersRawString =
await clashInterface.getExternalProvider(externalProviderName);
if (externalProvidersRawString.isEmpty) {
return null;
}
if (externalProvidersRawString.isEmpty) {
return null;
}
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
}
Future<String> updateGeoData(UpdateGeoDataParams params) {
return clashInterface.updateGeoData(params);
}
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
}) {
return clashInterface.sideLoadExternalProvider(
providerName: providerName, data: data);
}
Future<String> updateExternalProvider({
required String providerName,
}) async {
return clashInterface.updateExternalProvider(providerName);
}
Future<void> startListener() async {
await clashInterface.startListener();
}
Future<void> stopListener() async {
await clashInterface.stopListener();
}
Future<Delay> getDelay(String url, String proxyName) async {
final data = await clashInterface.asyncTestDelay(url, proxyName);
return Delay.fromJson(json.decode(data));
}
Future<Map<String, dynamic>> getConfig(String id) async {
final profilePath = await appPath.getProfilePath(id);
final res = await clashInterface.getConfig(profilePath);
if (res.isSuccess) {
return res.data as Map<String, dynamic>;
} else {
throw res.message;
}
}
Future<Traffic> getTraffic() async {
final trafficString = await clashInterface.getTraffic();
if (trafficString.isEmpty) {
return Traffic();
}
return Traffic.fromMap(json.decode(trafficString));
}
Future<IpInfo?> getCountryCode(String ip) async {
final countryCode = await clashInterface.getCountryCode(ip);
if (countryCode.isEmpty) {
return null;
}
return IpInfo(
ip: ip,
countryCode: countryCode,
);
}
Future<Traffic> getTotalTraffic() async {
final totalTrafficString = await clashInterface.getTotalTraffic();
if (totalTrafficString.isEmpty) {
return Traffic();
}
return Traffic.fromMap(json.decode(totalTrafficString));
}
Future<int> getMemory() async {
final value = await clashInterface.getMemory();
if (value.isEmpty) {
return 0;
}
return int.parse(value);
}
void resetTraffic() {
clashInterface.resetTraffic();
}
void startLog() {
clashInterface.startLog();
}
void stopLog() {
clashInterface.stopLog();
}
Future<void> requestGc() async {
await clashInterface.forceGc();
}
Future<void> destroy() async {
await clashInterface.destroy();
}
}
final clashCore = ClashCore();

File diff suppressed because it is too large Load Diff

View File

@@ -1,421 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:fl_clash/clash/message.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
mixin ClashInterface {
Future<bool> init(InitParams params);
Future<bool> preload();
Future<bool> shutdown();
Future<bool> get isInit;
Future<bool> forceGc();
FutureOr<String> validateConfig(String data);
FutureOr<Result> getConfig(String path);
Future<String> asyncTestDelay(String url, String proxyName);
FutureOr<String> updateConfig(UpdateParams updateParams);
FutureOr<String> setupConfig(SetupParams setupParams);
FutureOr<Map> getProxies();
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams);
Future<bool> startListener();
Future<bool> stopListener();
FutureOr<String> getExternalProviders();
FutureOr<String>? getExternalProvider(String externalProviderName);
Future<String> updateGeoData(UpdateGeoDataParams params);
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
});
Future<String> updateExternalProvider(String providerName);
FutureOr<String> getTraffic();
FutureOr<String> getTotalTraffic();
FutureOr<String> getCountryCode(String ip);
FutureOr<String> getMemory();
FutureOr<void> resetTraffic();
FutureOr<void> startLog();
FutureOr<void> stopLog();
Future<bool> crash();
FutureOr<String> getConnections();
FutureOr<bool> closeConnection(String id);
FutureOr<bool> closeConnections();
FutureOr<bool> resetConnections();
Future<bool> setState(CoreState state);
}
mixin AndroidClashInterface {
Future<bool> updateDns(String value);
Future<AndroidVpnOptions?> getAndroidVpnOptions();
Future<String> getCurrentProfileName();
Future<DateTime?> getRunTime();
}
abstract class ClashHandlerInterface with ClashInterface {
Map<String, Completer> callbackCompleterMap = {};
Future<void> handleResult(ActionResult result) async {
final completer = callbackCompleterMap[result.id];
try {
switch (result.method) {
case ActionMethod.message:
clashMessage.controller.add(result.data);
completer?.complete(true);
return;
case ActionMethod.getConfig:
completer?.complete(result.toResult);
return;
default:
completer?.complete(result.data);
return;
}
} catch (e) {
commonPrint.log('${result.id} error $e');
}
}
void sendMessage(String message);
FutureOr<void> reStart();
FutureOr<bool> destroy();
Future<T> invoke<T>({
required ActionMethod method,
dynamic data,
Duration? timeout,
FutureOr<T> Function()? onTimeout,
T? defaultValue,
}) async {
final id = '${method.name}#${utils.id}';
callbackCompleterMap[id] = Completer<T>();
dynamic mDefaultValue = defaultValue;
if (mDefaultValue == null) {
if (T == String) {
mDefaultValue = '';
} else if (T == bool) {
mDefaultValue = false;
} else if (T == Map) {
mDefaultValue = {};
}
}
sendMessage(
json.encode(
Action(
id: id,
method: method,
data: data,
),
),
);
return (callbackCompleterMap[id] as Completer<T>).safeFuture(
timeout: timeout,
onLast: () {
callbackCompleterMap.remove(id);
},
onTimeout: onTimeout ??
() {
return mDefaultValue;
},
functionName: id,
);
}
@override
Future<bool> init(InitParams params) {
return invoke<bool>(
method: ActionMethod.initClash,
data: json.encode(params),
);
}
@override
Future<bool> setState(CoreState state) {
return invoke<bool>(
method: ActionMethod.setState,
data: json.encode(state),
);
}
@override
shutdown() async {
return await invoke<bool>(
method: ActionMethod.shutdown,
timeout: Duration(seconds: 1),
);
}
@override
Future<bool> get isInit {
return invoke<bool>(
method: ActionMethod.getIsInit,
);
}
@override
Future<bool> forceGc() {
return invoke<bool>(
method: ActionMethod.forceGc,
);
}
@override
FutureOr<String> validateConfig(String data) {
return invoke<String>(
method: ActionMethod.validateConfig,
data: data,
);
}
@override
Future<String> updateConfig(UpdateParams updateParams) async {
return await invoke<String>(
method: ActionMethod.updateConfig,
data: json.encode(updateParams),
timeout: Duration(minutes: 2),
);
}
@override
Future<Result> getConfig(String path) async {
final res = await invoke<Result>(
method: ActionMethod.getConfig,
data: path,
timeout: Duration(minutes: 2),
defaultValue: Result.success({}),
);
return res;
}
@override
Future<String> setupConfig(SetupParams setupParams) async {
final data = await Isolate.run(() => json.encode(setupParams));
return await invoke<String>(
method: ActionMethod.setupConfig,
data: data,
timeout: Duration(minutes: 2),
);
}
@override
Future<bool> crash() {
return invoke<bool>(
method: ActionMethod.crash,
);
}
@override
Future<Map> getProxies() {
return invoke<Map>(
method: ActionMethod.getProxies,
timeout: Duration(seconds: 5),
);
}
@override
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) {
return invoke<String>(
method: ActionMethod.changeProxy,
data: json.encode(changeProxyParams),
);
}
@override
FutureOr<String> getExternalProviders() {
return invoke<String>(
method: ActionMethod.getExternalProviders,
);
}
@override
FutureOr<String> getExternalProvider(String externalProviderName) {
return invoke<String>(
method: ActionMethod.getExternalProvider,
data: externalProviderName,
);
}
@override
Future<String> updateGeoData(UpdateGeoDataParams params) {
return invoke<String>(
method: ActionMethod.updateGeoData,
data: json.encode(params),
timeout: Duration(minutes: 1));
}
@override
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
}) {
return invoke<String>(
method: ActionMethod.sideLoadExternalProvider,
data: json.encode({
'providerName': providerName,
'data': data,
}),
);
}
@override
Future<String> updateExternalProvider(String providerName) {
return invoke<String>(
method: ActionMethod.updateExternalProvider,
data: providerName,
timeout: Duration(minutes: 1),
);
}
@override
FutureOr<String> getConnections() {
return invoke<String>(
method: ActionMethod.getConnections,
);
}
@override
Future<bool> closeConnections() {
return invoke<bool>(
method: ActionMethod.closeConnections,
);
}
@override
Future<bool> resetConnections() {
return invoke<bool>(
method: ActionMethod.resetConnections,
);
}
@override
Future<bool> closeConnection(String id) {
return invoke<bool>(
method: ActionMethod.closeConnection,
data: id,
);
}
@override
FutureOr<String> getTotalTraffic() {
return invoke<String>(
method: ActionMethod.getTotalTraffic,
);
}
@override
FutureOr<String> getTraffic() {
return invoke<String>(
method: ActionMethod.getTraffic,
);
}
@override
resetTraffic() {
invoke(method: ActionMethod.resetTraffic);
}
@override
startLog() {
invoke(method: ActionMethod.startLog);
}
@override
stopLog() {
invoke<bool>(
method: ActionMethod.stopLog,
);
}
@override
Future<bool> startListener() {
return invoke<bool>(
method: ActionMethod.startListener,
);
}
@override
stopListener() {
return invoke<bool>(
method: ActionMethod.stopListener,
);
}
@override
Future<String> asyncTestDelay(String url, String proxyName) {
final delayParams = {
'proxy-name': proxyName,
'timeout': httpTimeoutDuration.inMilliseconds,
'test-url': url,
};
return invoke<String>(
method: ActionMethod.asyncTestDelay,
data: json.encode(delayParams),
timeout: Duration(
milliseconds: 6000,
),
onTimeout: () {
return json.encode(
Delay(
name: proxyName,
value: -1,
url: url,
),
);
},
);
}
@override
FutureOr<String> getCountryCode(String ip) {
return invoke<String>(
method: ActionMethod.getCountryCode,
data: ip,
);
}
@override
FutureOr<String> getMemory() {
return invoke<String>(
method: ActionMethod.getMemory,
);
}
}

View File

@@ -1,292 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:isolate';
import 'dart:ui';
import 'package:ffi/ffi.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/state.dart';
import 'generated/clash_ffi.dart';
import 'interface.dart';
class ClashLib extends ClashHandlerInterface with AndroidClashInterface {
static ClashLib? _instance;
Completer<bool> _canSendCompleter = Completer();
SendPort? sendPort;
final receiverPort = ReceivePort();
ClashLib._internal() {
_initService();
}
@override
preload() {
return _canSendCompleter.future;
}
Future<void> _initService() async {
await service?.destroy();
_registerMainPort(receiverPort.sendPort);
receiverPort.listen((message) {
if (message is SendPort) {
if (_canSendCompleter.isCompleted) {
sendPort = null;
_canSendCompleter = Completer();
}
sendPort = message;
_canSendCompleter.complete(true);
} else {
handleResult(
ActionResult.fromJson(json.decode(
message,
)),
);
}
});
await service?.init();
}
void _registerMainPort(SendPort sendPort) {
IsolateNameServer.removePortNameMapping(mainIsolate);
IsolateNameServer.registerPortWithName(sendPort, mainIsolate);
}
factory ClashLib() {
_instance ??= ClashLib._internal();
return _instance!;
}
@override
destroy() async {
await service?.destroy();
return true;
}
@override
reStart() {
_initService();
}
@override
Future<bool> shutdown() async {
await super.shutdown();
destroy();
return true;
}
@override
sendMessage(String message) async {
await _canSendCompleter.future;
sendPort?.send(message);
}
// @override
// Future<bool> stopTun() {
// return invoke<bool>(
// method: ActionMethod.stopTun,
// );
// }
@override
Future<AndroidVpnOptions?> getAndroidVpnOptions() async {
final res = await invoke<String>(
method: ActionMethod.getAndroidVpnOptions,
);
if (res.isEmpty) {
return null;
}
return AndroidVpnOptions.fromJson(json.decode(res));
}
@override
Future<bool> updateDns(String value) {
return invoke<bool>(
method: ActionMethod.updateDns,
data: value,
);
}
@override
Future<DateTime?> getRunTime() async {
final runTimeString = await invoke<String>(
method: ActionMethod.getRunTime,
);
if (runTimeString.isEmpty) {
return null;
}
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
}
@override
Future<String> getCurrentProfileName() {
return invoke<String>(
method: ActionMethod.getCurrentProfileName,
);
}
}
class ClashLibHandler {
static ClashLibHandler? _instance;
late final ClashFFI clashFFI;
late final DynamicLibrary lib;
ClashLibHandler._internal() {
lib = DynamicLibrary.open('libclash.so');
clashFFI = ClashFFI(lib);
clashFFI.initNativeApiBridge(
NativeApi.initializeApiDLData,
);
}
factory ClashLibHandler() {
_instance ??= ClashLibHandler._internal();
return _instance!;
}
Future<String> invokeAction(String actionParams) {
final completer = Completer<String>();
final receiver = ReceivePort();
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(message);
receiver.close();
}
});
final actionParamsChar = actionParams.toNativeUtf8().cast<Char>();
clashFFI.invokeAction(
actionParamsChar,
receiver.sendPort.nativePort,
);
malloc.free(actionParamsChar);
return completer.future;
}
void attachMessagePort(int messagePort) {
clashFFI.attachMessagePort(
messagePort,
);
}
void updateDns(String dns) {
final dnsChar = dns.toNativeUtf8().cast<Char>();
clashFFI.updateDns(dnsChar);
malloc.free(dnsChar);
}
void setState(CoreState state) {
final stateChar = json.encode(state).toNativeUtf8().cast<Char>();
clashFFI.setState(stateChar);
malloc.free(stateChar);
}
String getCurrentProfileName() {
final currentProfileRaw = clashFFI.getCurrentProfileName();
final currentProfile = currentProfileRaw.cast<Utf8>().toDartString();
clashFFI.freeCString(currentProfileRaw);
return currentProfile;
}
AndroidVpnOptions getAndroidVpnOptions() {
final vpnOptionsRaw = clashFFI.getAndroidVpnOptions();
final vpnOptions = json.decode(vpnOptionsRaw.cast<Utf8>().toDartString());
clashFFI.freeCString(vpnOptionsRaw);
return AndroidVpnOptions.fromJson(vpnOptions);
}
Traffic getTraffic() {
final trafficRaw = clashFFI.getTraffic();
final trafficString = trafficRaw.cast<Utf8>().toDartString();
clashFFI.freeCString(trafficRaw);
if (trafficString.isEmpty) {
return Traffic();
}
return Traffic.fromMap(json.decode(trafficString));
}
Traffic getTotalTraffic(bool value) {
final trafficRaw = clashFFI.getTotalTraffic();
final trafficString = trafficRaw.cast<Utf8>().toDartString();
clashFFI.freeCString(trafficRaw);
if (trafficString.isEmpty) {
return Traffic();
}
return Traffic.fromMap(json.decode(trafficString));
}
Future<bool> startListener() async {
clashFFI.startListener();
return true;
}
Future<bool> stopListener() async {
clashFFI.stopListener();
return true;
}
DateTime? getRunTime() {
final runTimeRaw = clashFFI.getRunTime();
final runTimeString = runTimeRaw.cast<Utf8>().toDartString();
if (runTimeString.isEmpty) {
return null;
}
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
}
Future<Map<String, dynamic>> getConfig(String id) async {
final path = await appPath.getProfilePath(id);
final pathChar = path.toNativeUtf8().cast<Char>();
final configRaw = clashFFI.getConfig(pathChar);
final configString = configRaw.cast<Utf8>().toDartString();
if (configString.isEmpty) {
return {};
}
final config = json.decode(configString);
malloc.free(pathChar);
clashFFI.freeCString(configRaw);
return config;
}
Future<String> quickStart(
InitParams initParams,
SetupParams setupParams,
CoreState state,
) {
final completer = Completer<String>();
final receiver = ReceivePort();
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(message);
receiver.close();
}
});
final params = json.encode(setupParams);
final initValue = json.encode(initParams);
final stateParams = json.encode(state);
final initParamsChar = initValue.toNativeUtf8().cast<Char>();
final paramsChar = params.toNativeUtf8().cast<Char>();
final stateParamsChar = stateParams.toNativeUtf8().cast<Char>();
clashFFI.quickStart(
initParamsChar,
paramsChar,
stateParamsChar,
receiver.sendPort.nativePort,
);
malloc.free(initParamsChar);
malloc.free(paramsChar);
malloc.free(stateParamsChar);
return completer.future;
}
}
ClashLib? get clashLib =>
system.isAndroid && !globalState.isService ? ClashLib() : null;
ClashLibHandler? get clashLibHandler =>
system.isAndroid && globalState.isService ? ClashLibHandler() : null;

View File

@@ -1,55 +0,0 @@
import 'dart:async';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart';
class ClashMessage {
final controller = StreamController<Map<String, Object?>>();
ClashMessage._() {
controller.stream.listen(
(message) {
if (message.isEmpty) {
return;
}
final m = AppMessage.fromJson(message);
for (final AppMessageListener listener in _listeners) {
switch (m.type) {
case AppMessageType.log:
listener.onLog(Log.fromJson(m.data));
break;
case AppMessageType.delay:
listener.onDelay(Delay.fromJson(m.data));
break;
case AppMessageType.request:
listener.onRequest(TrackerInfo.fromJson(m.data));
break;
case AppMessageType.loaded:
listener.onLoaded(m.data);
break;
}
}
},
);
}
static final ClashMessage instance = ClashMessage._();
final ObserverList<AppMessageListener> _listeners =
ObserverList<AppMessageListener>();
bool get hasListeners {
return _listeners.isNotEmpty;
}
void addListener(AppMessageListener listener) {
_listeners.add(listener);
}
void removeListener(AppMessageListener listener) {
_listeners.remove(listener);
}
}
final clashMessage = ClashMessage.instance;

View File

@@ -1,160 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:fl_clash/clash/interface.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/state.dart';
class ClashService extends ClashHandlerInterface {
static ClashService? _instance;
Completer<ServerSocket> serverCompleter = Completer();
Completer<Socket> socketCompleter = Completer();
bool isStarting = false;
Process? process;
factory ClashService() {
_instance ??= ClashService._internal();
return _instance!;
}
ClashService._internal() {
_initServer();
reStart();
}
Future<void> _initServer() async {
runZonedGuarded(() async {
final address = !system.isWindows
? InternetAddress(
unixSocketPath,
type: InternetAddressType.unix,
)
: InternetAddress(
localhost,
type: InternetAddressType.IPv4,
);
await _deleteSocketFile();
final server = await ServerSocket.bind(
address,
0,
shared: true,
);
serverCompleter.complete(server);
await for (final socket in server) {
await _destroySocket();
socketCompleter.complete(socket);
socket
.transform(uint8ListToListIntConverter)
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(data) {
handleResult(
ActionResult.fromJson(
json.decode(data.trim()),
),
);
},
);
}
}, (error, stack) {
commonPrint.log(error.toString());
if (error is SocketException) {
globalState.showNotifier(error.toString());
// globalState.appController.restartCore();
}
});
}
@override
reStart() async {
if (isStarting == true) {
return;
}
isStarting = true;
socketCompleter = Completer();
if (process != null) {
await shutdown();
}
final serverSocket = await serverCompleter.future;
final arg = system.isWindows
? '${serverSocket.port}'
: serverSocket.address.address;
if (system.isWindows && await system.checkIsAdmin()) {
final isSuccess = await request.startCoreByHelper(arg);
if (isSuccess) {
return;
}
}
process = await Process.start(
appPath.corePath,
[
arg,
],
);
process?.stdout.listen((_) {});
process?.stderr.listen((e) {
final error = utf8.decode(e);
if (error.isNotEmpty) {
commonPrint.log(error);
}
});
isStarting = false;
}
@override
destroy() async {
final server = await serverCompleter.future;
await server.close();
await _deleteSocketFile();
return true;
}
@override
sendMessage(String message) async {
final socket = await socketCompleter.future;
socket.writeln(message);
}
Future<void> _deleteSocketFile() async {
if (!system.isWindows) {
final file = File(unixSocketPath);
if (await file.exists()) {
await file.delete();
}
}
}
Future<void> _destroySocket() async {
if (socketCompleter.isCompleted) {
final lastSocket = await socketCompleter.future;
await lastSocket.close();
socketCompleter = Completer();
}
}
@override
shutdown() async {
if (system.isWindows) {
await request.stopCoreByHelper();
}
await _destroySocket();
process?.kill();
process = null;
return true;
}
@override
Future<bool> preload() async {
await serverCompleter.future;
return true;
}
}
final clashService = system.isDesktop ? ClashService() : null;

View File

@@ -1,14 +0,0 @@
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/state.dart';
import 'system.dart';
class Android {
Future<void> init() async {
app?.onExit = () async {
await globalState.appController.savePreferences();
};
}
}
final android = system.isAndroid ? Android() : null;

View File

@@ -14,16 +14,15 @@ extension ArchiveExt on Archive {
final data = entity.readAsBytesSync();
final archiveFile = ArchiveFile(relativePath, data.length, data);
addFile(archiveFile);
} else if (entity is Directory) {
addDirectoryToArchive(entity.path, parentPath);
}
// else if (entity is Directory) {
// addDirectoryToArchive(entity.path, parentPath);
// }
}
}
void add<T>(String name, T raw) {
void addTextFile<T>(String name, T raw) {
final data = json.encode(raw);
addFile(
ArchiveFile(name, data.length, data),
);
addFile(ArchiveFile.string(name, data));
}
}

185
lib/common/cache.dart Normal file
View File

@@ -0,0 +1,185 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:fl_clash/common/common.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
class LocalImageCacheManager extends CacheManager {
static const key = 'ImageCaches';
static final LocalImageCacheManager _instance = LocalImageCacheManager._();
factory LocalImageCacheManager() {
return _instance;
}
LocalImageCacheManager._()
: super(Config(key, fileService: _LocalImageCacheFileService()));
}
class _LocalImageCacheFileService extends FileService {
_LocalImageCacheFileService();
@override
Future<FileServiceResponse> get(
String url, {
Map<String, String>? headers,
}) async {
final response = await request.dio.get<ResponseBody>(
url,
options: Options(headers: headers, responseType: ResponseType.stream),
);
return _LocalImageResponse(response);
}
}
class _LocalImageResponse implements FileServiceResponse {
_LocalImageResponse(this._response);
final DateTime _receivedTime = DateTime.now();
final Response<ResponseBody> _response;
String? _header(String name) {
return _response.headers.value(name);
}
@override
int get statusCode => _response.statusCode ?? 0;
@override
Stream<List<int>> get content =>
_response.data!.stream.transform(uint8ListToListIntConverter);
@override
int? get contentLength => _response.data?.contentLength;
@override
DateTime get validTill {
var ageDuration = const Duration(days: 7);
final controlHeader = _header(HttpHeaders.cacheControlHeader);
if (controlHeader != null) {
final controlSettings = controlHeader.split(',');
for (final setting in controlSettings) {
final sanitizedSetting = setting.trim().toLowerCase();
if (sanitizedSetting == 'no-cache') {
ageDuration = Duration.zero;
}
if (sanitizedSetting.startsWith('max-age=')) {
final validSeconds =
int.tryParse(sanitizedSetting.split('=')[1]) ?? 0;
if (validSeconds > 0) {
ageDuration = Duration(seconds: validSeconds);
}
}
}
}
if (ageDuration > const Duration(days: 7)) {
return _receivedTime.add(ageDuration);
}
return _receivedTime.add(const Duration(days: 7));
}
@override
String? get eTag => _header(HttpHeaders.etagHeader);
@override
String get fileExtension {
var fileExtension = '';
final contentTypeHeader = _header(HttpHeaders.contentTypeHeader);
if (contentTypeHeader != null) {
final contentType = ContentType.parse(contentTypeHeader);
fileExtension = contentType.fileExtension;
}
return fileExtension;
}
}
extension ContentTypeConverter on ContentType {
String get fileExtension => mimeTypes[mimeType] ?? '.$subType';
}
const mimeTypes = {
'application/vnd.android.package-archive': '.apk',
'application/epub+zip': '.epub',
'application/gzip': '.gz',
'application/java-archive': '.jar',
'application/json': '.json',
'application/ld+json': '.jsonld',
'application/msword': '.doc',
'application/octet-stream': '.bin',
'application/ogg': '.ogx',
'application/pdf': '.pdf',
'application/php': '.php',
'application/rtf': '.rtf',
'application/vnd.amazon.ebook': '.azw',
'application/vnd.apple.installer+xml': '.mpkg',
'application/vnd.mozilla.xul+xml': '.xul',
'application/vnd.ms-excel': '.xls',
'application/vnd.ms-fontobject': '.eot',
'application/vnd.ms-powerpoint': '.ppt',
'application/vnd.oasis.opendocument.presentation': '.odp',
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
'.pptx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
'.docx',
'application/vnd.rar': '.rar',
'application/vnd.visio': '.vsd',
'application/x-7z-compressed': '.7z',
'application/x-abiword': '.abw',
'application/x-bzip': '.bz',
'application/x-bzip2': '.bz2',
'application/x-csh': '.csh',
'application/x-freearc': '.arc',
'application/x-sh': '.sh',
'application/x-shockwave-flash': '.swf',
'application/x-tar': '.tar',
'application/xhtml+xml': '.xhtml',
'application/xml': '.xml',
'application/zip': '.zip',
'audio/3gpp': '.3gp',
'audio/3gpp2': '.3g2',
'audio/aac': '.aac',
'audio/x-aac': '.aac',
'audio/midi': '.midi',
'audio/x-midi': '.midi',
'audio/x-m4a': '.m4a',
'audio/m4a': '.m4a',
'audio/mpeg': '.mp3',
'audio/ogg': '.oga',
'audio/opus': '.opus',
'audio/wav': '.wav',
'audio/x-wav': '.wav',
'audio/webm': '.weba',
'font/otf': '.otf',
'font/ttf': '.ttf',
'font/woff': '.woff',
'font/woff2': '.woff2',
'image/bmp': '.bmp',
'image/gif': '.gif',
'image/jpeg': '.jpg',
'image/png': '.png',
'image/svg+xml': '.svg',
'image/tiff': '.tiff',
'image/vnd.microsoft.icon': '.ico',
'image/webp': '.webp',
'text/calendar': '.ics',
'text/css': '.css',
'text/csv': '.csv',
'text/html': '.html',
'text/javascript': '.js',
'text/plain': '.txt',
'text/xml': '.xml',
'video/3gpp': '.3gp',
'video/3gpp2': '.3g2',
'video/mp2t': '.ts',
'video/mpeg': '.mpeg',
'video/ogg': '.ogv',
'video/webm': '.webm',
'video/x-msvideo': '.avi',
'video/quicktime': '.mov',
};

View File

@@ -1,6 +1,7 @@
export 'android.dart';
export 'app_localizations.dart';
export 'cache.dart';
export 'color.dart';
export 'compute.dart';
export 'constant.dart';
export 'context.dart';
export 'converter.dart';

110
lib/common/compute.dart Normal file
View File

@@ -0,0 +1,110 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'string.dart';
List<Group> computeSort({
required List<Group> groups,
required ProxiesSortType sortType,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String defaultTestUrl,
}) {
return groups.map((group) {
final proxies = group.all;
final newProxies = switch (sortType) {
ProxiesSortType.none => proxies,
ProxiesSortType.delay => _sortOfDelay(
groups: groups,
proxies: proxies,
delayMap: delayMap,
selectedMap: selectedMap,
testUrl: group.testUrl.getSafeValue(defaultTestUrl),
),
ProxiesSortType.name => _sortOfName(proxies),
};
return group.copyWith(all: newProxies);
}).toList();
}
DelayState computeProxyDelayState({
required String proxyName,
required String testUrl,
required List<Group> groups,
required SelectedMap selectedMap,
required DelayMap delayMap,
}) {
final state = computeRealSelectedProxyState(
proxyName,
groups: groups,
selectedMap: selectedMap,
);
final currentDelayMap = delayMap[state.testUrl.getSafeValue(testUrl)] ?? {};
final delay = currentDelayMap[state.proxyName];
return DelayState(delay: delay ?? 0, group: state.group);
}
SelectedProxyState computeRealSelectedProxyState(
String proxyName, {
required List<Group> groups,
required SelectedMap selectedMap,
}) {
return _getRealSelectedProxyState(
SelectedProxyState(proxyName: proxyName),
groups: groups,
selectedMap: selectedMap,
);
}
SelectedProxyState _getRealSelectedProxyState(
SelectedProxyState state, {
required List<Group> groups,
required SelectedMap selectedMap,
}) {
if (state.proxyName.isEmpty) return state;
final index = groups.indexWhere((element) => element.name == state.proxyName);
final newState = state.copyWith(group: true);
if (index == -1) return newState;
final group = groups[index];
final currentSelectedName = group.getCurrentSelectedName(
selectedMap[newState.proxyName] ?? '',
);
if (currentSelectedName.isEmpty) {
return newState;
}
return _getRealSelectedProxyState(
newState.copyWith(proxyName: currentSelectedName, testUrl: group.testUrl),
groups: groups,
selectedMap: selectedMap,
);
}
List<Proxy> _sortOfDelay({
required List<Group> groups,
required List<Proxy> proxies,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String testUrl,
}) {
return List.from(proxies)..sort((a, b) {
final aDelayState = computeProxyDelayState(
proxyName: a.name,
testUrl: testUrl,
groups: groups,
selectedMap: selectedMap,
delayMap: delayMap,
);
final bDelayState = computeProxyDelayState(
proxyName: b.name,
testUrl: testUrl,
groups: groups,
selectedMap: selectedMap,
delayMap: delayMap,
);
return aDelayState.compareTo(bDelayState);
});
}
List<Proxy> _sortOfName(List<Proxy> proxies) {
return List.of(proxies)..sort((a, b) => a.name.compareTo(b.name));
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: constant_identifier_names
import 'dart:math';
import 'dart:ui';
@@ -30,14 +32,14 @@ const animateDuration = Duration(milliseconds: 100);
const midDuration = Duration(milliseconds: 200);
const commonDuration = Duration(milliseconds: 300);
const defaultUpdateDuration = Duration(days: 1);
const mmdbFileName = 'geoip.metadb';
const asnFileName = 'ASN.mmdb';
const geoIpFileName = 'GeoIP.dat';
const geoSiteFileName = 'GeoSite.dat';
const MMDB = 'GEOIP.metadb';
const ASN = 'ASN.mmdb';
const GEOIP = 'GEOIP.dat';
const GEOSITE = 'GEOSITE.dat';
final double kHeaderHeight = system.isDesktop
? !system.isMacOS
? 40
: 28
? 40
: 28
: 0;
const profilesDirectoryName = 'profiles';
const localhost = '127.0.0.1';
@@ -84,7 +86,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
const defaultPrimaryColor = 0XFFD8C0C3;
double getWidgetHeight(num lines) {
return max(lines * 84 + (lines - 1) * 16, 0).ap;
return max(lines * 80 + (lines - 1) * 16, 0).ap;
}
const maxLength = 1000;

View File

@@ -7,28 +7,17 @@ extension BuildContextExtension on BuildContext {
return findAncestorStateOfType<CommonScaffoldState>();
}
Future<void>? showNotifier(String text) {
void showNotifier(String text) {
return findAncestorStateOfType<MessageManagerState>()?.message(text);
}
void showSnackBar(
String message, {
SnackBarAction? action,
}) {
void showSnackBar(String message, {SnackBarAction? action}) {
final width = viewWidth;
EdgeInsets margin;
if (width < 600) {
margin = const EdgeInsets.only(
bottom: 16,
right: 16,
left: 16,
);
margin = const EdgeInsets.only(bottom: 16, right: 16, left: 16);
} else {
margin = EdgeInsets.only(
bottom: 16,
left: 16,
right: width - 316,
);
margin = EdgeInsets.only(bottom: 16, left: 16, right: width - 316);
}
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(
@@ -76,8 +65,11 @@ extension BuildContextExtension on BuildContext {
class BackHandleInherited extends InheritedWidget {
final Function handleBack;
const BackHandleInherited(
{super.key, required this.handleBack, required super.child});
const BackHandleInherited({
super.key,
required this.handleBack,
required super.child,
});
static BackHandleInherited? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<BackHandleInherited>();

View File

@@ -9,23 +9,17 @@ class Debouncer {
FunctionTag tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
Duration? duration,
}) {
final timer = _operations[tag];
if (timer != null) {
timer.cancel();
}
_operations[tag] = Timer(
duration,
() {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(
func,
args,
);
},
);
_operations[tag] = Timer(duration ?? const Duration(milliseconds: 600), () {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(func, args);
});
}
void cancel(dynamic tag) {
@@ -42,22 +36,25 @@ class Throttler {
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
bool fire = false,
}) {
final timer = _operations[tag];
if (timer != null) {
return true;
}
_operations[tag] = Timer(
duration,
() {
if (fire) {
Function.apply(func, args);
_operations[tag] = Timer(duration, () {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(
func,
args,
);
},
);
});
} else {
_operations[tag] = Timer(duration, () {
Function.apply(func, args);
_operations[tag]?.cancel();
_operations.remove(tag);
});
}
return false;
}

View File

@@ -3,42 +3,37 @@ import 'dart:ui';
import 'package:fl_clash/common/common.dart';
extension CompleterExt<T> on Completer<T> {
Future<T> safeFuture({
extension FutureExt<T> on Future<T> {
Future<T> withTimeout({
Duration? timeout,
String? tag,
VoidCallback? onLast,
FutureOr<T> Function()? onTimeout,
required String functionName,
}) {
final realTimeout = timeout ?? const Duration(seconds: 30);
Timer(realTimeout + commonDuration, () {
final realTimout = timeout ?? const Duration(minutes: 3);
Timer(realTimout + commonDuration, () {
if (onLast != null) {
onLast();
}
});
return future.withTimeout(
timeout: realTimeout,
functionName: functionName,
onTimeout: onTimeout,
);
}
}
extension FutureExt<T> on Future<T> {
Future<T> withTimeout({
required Duration timeout,
required String functionName,
FutureOr<T> Function()? onTimeout,
}) {
return this.timeout(
timeout,
realTimout,
onTimeout: () async {
if (onTimeout != null) {
return onTimeout();
} else {
throw TimeoutException('$functionName timeout');
throw TimeoutException('${tag ?? runtimeType} timeout');
}
},
);
}
}
extension CompleterExt<T> on Completer<T> {
void safeCompleter(T value) {
if (isCompleted) {
return;
}
complete(value);
}
}

View File

@@ -18,7 +18,7 @@ class FlClashHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = super.createHttpClient(context);
client.badCertificateCallback = (_, __, ___) => true;
client.badCertificateCallback = (_, _, _) => true;
client.findProxy = handleFindProxy;
return client;
}

View File

@@ -1,8 +1,13 @@
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
mixin AutoDisposeNotifierMixin<T> on AutoDisposeNotifier<T> {
mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
set value(T value) {
state = value;
if (ref.mounted) {
state = value;
} else {
onUpdate(value);
}
}
@override
@@ -17,28 +22,25 @@ mixin AutoDisposeNotifierMixin<T> on AutoDisposeNotifier<T> {
void onUpdate(T value) {}
}
// mixin PageMixin<T extends StatefulWidget> on State<T> {
// initPageState() {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final commonScaffoldState = context.commonScaffoldState;
// commonScaffoldState?.actions = actions;
// commonScaffoldState?.floatingActionButton = floatingActionButton;
// commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate;
// commonScaffoldState?.updateSearchState(
// (_) => onSearch != null
// ? AppBarSearchState(
// onSearch: onSearch!,
// )
// : null,
// );
// });
// }
//
// List<Widget> get actions => [];
//
// Widget? get floatingActionButton => null;
//
// Function(String)? get onSearch => null;
//
// Function(List<String>)? get onKeywordsUpdate => null;
// }
mixin AnyNotifierMixin<T> on AnyNotifier<T, T> {
T get value;
set value(T value) {
if (ref.mounted) {
state = value;
} else {
onUpdate(value);
}
}
@override
bool updateShouldNotify(previous, next) {
final res = super.updateShouldNotify(previous, next);
if (res) {
onUpdate(next);
}
return res;
}
void onUpdate(T value) {}
}

View File

@@ -1,9 +1,7 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/views/views.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Navigation {
static Navigation? _instance;
@@ -17,25 +15,14 @@ class Navigation {
keep: false,
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
builder: (_) => const DashboardView(
key: GlobalObjectKey(PageLabel.dashboard),
),
builder: (_) =>
const DashboardView(key: GlobalObjectKey(PageLabel.dashboard)),
),
NavigationItem(
icon: const Icon(Icons.article),
label: PageLabel.proxies,
builder: (_) => ProviderScope(
overrides: [
queryProvider.overrideWith(
() => Query(),
),
],
child: const ProxiesView(
key: GlobalObjectKey(
PageLabel.proxies,
),
),
),
builder: (_) =>
const ProxiesView(key: GlobalObjectKey(PageLabel.proxies)),
modes: hasProxies
? [NavigationItemMode.mobile, NavigationItemMode.desktop]
: [],
@@ -43,31 +30,22 @@ class Navigation {
NavigationItem(
icon: Icon(Icons.folder),
label: PageLabel.profiles,
builder: (_) => const ProfilesView(
key: GlobalObjectKey(
PageLabel.profiles,
),
),
builder: (_) =>
const ProfilesView(key: GlobalObjectKey(PageLabel.profiles)),
),
NavigationItem(
icon: Icon(Icons.view_timeline),
label: PageLabel.requests,
builder: (_) => const RequestsView(
key: GlobalObjectKey(
PageLabel.requests,
),
),
builder: (_) =>
const RequestsView(key: GlobalObjectKey(PageLabel.requests)),
description: 'requestsDesc',
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
NavigationItem(
icon: Icon(Icons.ballot),
label: PageLabel.connections,
builder: (_) => const ConnectionsView(
key: GlobalObjectKey(
PageLabel.connections,
),
),
builder: (_) =>
const ConnectionsView(key: GlobalObjectKey(PageLabel.connections)),
description: 'connectionsDesc',
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
@@ -75,21 +53,14 @@ class Navigation {
icon: Icon(Icons.storage),
label: PageLabel.resources,
description: 'resourcesDesc',
builder: (_) => const ResourcesView(
key: GlobalObjectKey(
PageLabel.resources,
),
),
builder: (_) =>
const ResourcesView(key: GlobalObjectKey(PageLabel.resources)),
modes: [NavigationItemMode.more],
),
NavigationItem(
icon: const Icon(Icons.adb),
label: PageLabel.logs,
builder: (_) => const LogsView(
key: GlobalObjectKey(
PageLabel.logs,
),
),
builder: (_) => const LogsView(key: GlobalObjectKey(PageLabel.logs)),
description: 'logsDesc',
modes: openLogs
? [NavigationItemMode.desktop, NavigationItemMode.more]
@@ -98,11 +69,7 @@ class Navigation {
NavigationItem(
icon: Icon(Icons.construction),
label: PageLabel.tools,
builder: (_) => const ToolsView(
key: GlobalObjectKey(
PageLabel.tools,
),
),
builder: (_) => const ToolsView(key: GlobalObjectKey(PageLabel.tools)),
modes: [NavigationItemMode.desktop, NavigationItemMode.mobile],
),
];

View File

@@ -7,17 +7,13 @@ import 'package:flutter/material.dart';
class BaseNavigator {
static Future<T?> push<T>(BuildContext context, Widget child) async {
if (globalState.appState.viewMode != ViewMode.mobile) {
return await Navigator.of(context).push<T>(
CommonDesktopRoute(
builder: (context) => child,
),
);
return await Navigator.of(
context,
).push<T>(CommonDesktopRoute(builder: (context) => child));
}
return await Navigator.of(context).push<T>(
CommonRoute(
builder: (context) => child,
),
);
return await Navigator.of(
context,
).push<T>(CommonRoute(builder: (context) => child));
}
// static Future<T?> modal<T>(BuildContext context, Widget child) async {
@@ -39,9 +35,7 @@ class BaseNavigator {
class CommonDesktopRoute<T> extends PageRoute<T> {
final Widget Function(BuildContext context) builder;
CommonDesktopRoute({
required this.builder,
});
CommonDesktopRoute({required this.builder});
@override
Color? get barrierColor => null;
@@ -59,10 +53,7 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
return Semantics(
scopesRoute: true,
explicitChildNodes: true,
child: FadeTransition(
opacity: animation,
child: result,
),
child: FadeTransition(opacity: animation, child: result),
);
}
@@ -77,9 +68,7 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
}
class CommonRoute<T> extends MaterialPageRoute<T> {
CommonRoute({
required super.builder,
});
CommonRoute({required super.builder});
@override
Duration get transitionDuration => const Duration(milliseconds: 500);
@@ -139,16 +128,21 @@ class CommonPageTransition extends StatefulWidget {
final bool linearTransition;
static Widget? delegatedTransition(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
bool allowSnapshotting,
Widget? child) {
final Animation<Offset> delegatedPositionAnimation = CurvedAnimation(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
bool allowSnapshotting,
Widget? child,
) {
final CurvedAnimation animation = CurvedAnimation(
parent: secondaryAnimation,
curve: Curves.linearToEaseOut,
reverseCurve: Curves.easeInToLinear,
).drive(_kMiddleLeftTween);
);
final Animation<Offset> delegatedPositionAnimation = animation.drive(
_kMiddleLeftTween,
);
animation.dispose();
assert(debugCheckHasDirectionality(context));
final TextDirection textDirection = Directionality.of(context);
@@ -222,23 +216,23 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
);
}
_primaryPositionAnimation =
(_primaryPositionCurve ?? widget.primaryRouteAnimation)
.drive(_kRightMiddleTween);
(_primaryPositionCurve ?? widget.primaryRouteAnimation).drive(
_kRightMiddleTween,
);
_secondaryPositionAnimation =
(_secondaryPositionCurve ?? widget.secondaryRouteAnimation)
.drive(_kMiddleLeftTween);
(_secondaryPositionCurve ?? widget.secondaryRouteAnimation).drive(
_kMiddleLeftTween,
);
_primaryShadowAnimation =
(_primaryShadowCurve ?? widget.primaryRouteAnimation).drive(
DecorationTween(
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(
<Color>[
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
Colors.transparent,
],
),
),
);
DecorationTween(
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(<Color>[
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
Colors.transparent,
]),
),
);
}
@override
@@ -273,10 +267,8 @@ class _CommonEdgeShadowDecoration extends Decoration {
}
class _CommonEdgeShadowPainter extends BoxPainter {
_CommonEdgeShadowPainter(
this._decoration,
super.onChanged,
) : assert(_decoration._colors == null || _decoration._colors!.length > 1);
_CommonEdgeShadowPainter(this._decoration, super.onChanged)
: assert(_decoration._colors == null || _decoration._colors.length > 1);
final _CommonEdgeShadowDecoration _decoration;
@@ -304,11 +296,16 @@ class _CommonEdgeShadowPainter extends BoxPainter {
bandColorIndex += 1;
}
final Paint paint = Paint()
..color = Color.lerp(colors[bandColorIndex], colors[bandColorIndex + 1],
(dx % bandWidth) / bandWidth)!;
..color = Color.lerp(
colors[bandColorIndex],
colors[bandColorIndex + 1],
(dx % bandWidth) / bandWidth,
)!;
final double x = start + shadowDirection * dx;
canvas.drawRect(
Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight), paint);
Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight),
paint,
);
}
}
}

View File

@@ -1,3 +1,5 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -17,6 +19,20 @@ extension NumExt on num {
double get ap {
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
}
TrafficShow get traffic {
final units = TrafficUnit.values;
var size = toDouble();
var unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return TrafficShow(
value: size.fixed(decimals: 1),
unit: units[unitIndex].name,
);
}
}
extension DoubleExt on double {

View File

@@ -10,6 +10,7 @@ class AppPath {
Completer<Directory> dataDir = Completer();
Completer<Directory> downloadDir = Completer();
Completer<Directory> tempDir = Completer();
Completer<Directory> cacheDir = Completer();
late String appDirPath;
AppPath._internal() {
@@ -23,6 +24,9 @@ class AppPath {
getDownloadsDirectory().then((value) {
downloadDir.complete(value);
});
getApplicationCacheDirectory().then((value) {
cacheDir.complete(value);
});
}
factory AppPath() {
@@ -58,8 +62,13 @@ class AppPath {
}
Future<String> get lockFilePath async {
final directory = await dataDir.future;
return join(directory.path, 'FlClash.lock');
final homeDirPath = await appPath.homeDirPath;
return join(homeDirPath, 'FlClash.lock');
}
Future<String> get configFilePath async {
final homeDirPath = await appPath.homeDirPath;
return join(homeDirPath, 'config.json');
}
Future<String> get sharedPreferencesPath async {
@@ -77,13 +86,19 @@ class AppPath {
return join(directory, '$id.yaml');
}
Future<String> getIconsCacheDir() async {
final directory = await cacheDir.future;
return join(directory.path, 'icons');
}
Future<String> getProvidersRootPath() async {
final directory = await profilesPath;
return join(directory, 'providers');
}
Future<String> getProvidersDirPath(String id) async {
final directory = await profilesPath;
return join(
directory,
'providers',
id,
);
return join(directory, 'providers', id);
}
Future<String> getProvidersFilePath(
@@ -92,13 +107,7 @@ class AppPath {
String url,
) async {
final directory = await profilesPath;
return join(
directory,
'providers',
id,
type,
url.toMd5(),
);
return join(directory, 'providers', id, type, url.toMd5());
}
Future<String> get tempPath async {

View File

@@ -35,9 +35,10 @@ class Picker {
return null;
}
final controller = MobileScannerController();
final capture = await controller.analyzeImage(xFile.path, formats: [
BarcodeFormat.qrCode,
]);
final capture = await controller.analyzeImage(
xFile.path,
formats: [BarcodeFormat.qrCode],
);
final result = capture?.barcodes.first.rawValue;
if (result == null || !result.isUrl) {
throw appLocalizations.pleaseUploadValidQrcode;

View File

@@ -16,7 +16,7 @@ class Preferences {
Preferences._internal() {
SharedPreferences.getInstance()
.then((value) => sharedPreferencesCompleter.complete(value))
.onError((_, __) => sharedPreferencesCompleter.complete(null));
.onError((_, _) => sharedPreferencesCompleter.complete(null));
}
factory Preferences() {
@@ -42,10 +42,7 @@ class Preferences {
Future<bool> saveConfig(Config config) async {
final preferences = await sharedPreferencesCompleter.future;
return await preferences?.setString(
configKey,
json.encode(config),
) ??
return await preferences?.setString(configKey, json.encode(config)) ??
false;
}

View File

@@ -1,3 +1,4 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/cupertino.dart';
@@ -12,14 +13,14 @@ class CommonPrint {
return _instance!;
}
void log(String? text) {
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
final payload = '[APP] $text';
debugPrint(payload);
if (!globalState.isInit) {
return;
}
globalState.appController.addLog(
Log.app(payload),
Log.app(payload).copyWith(logLevel: logLevel),
);
}
}

View File

@@ -11,35 +11,29 @@ import 'package:fl_clash/state.dart';
import 'package:flutter/cupertino.dart';
class Request {
late final Dio _dio;
late final Dio dio;
late final Dio _clashDio;
String? userAgent;
Request() {
_dio = Dio(
BaseOptions(
headers: {
'User-Agent': browserUa,
},
),
);
dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));
_clashDio = Dio();
_clashDio.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
final client = HttpClient();
client.findProxy = (Uri uri) {
client.userAgent = globalState.ua;
return FlClashHttpOverrides.handleFindProxy(uri);
};
return client;
});
_clashDio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.findProxy = (Uri uri) {
client.userAgent = globalState.ua;
return FlClashHttpOverrides.handleFindProxy(uri);
};
return client;
},
);
}
Future<Response> getFileResponseForUrl(String url) async {
final response = await _clashDio.get(
url,
options: Options(
responseType: ResponseType.bytes,
),
options: Options(responseType: ResponseType.bytes),
);
return response;
}
@@ -47,20 +41,16 @@ class Request {
Future<Response> getTextResponseForUrl(String url) async {
final response = await _clashDio.get(
url,
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
);
return response;
}
Future<MemoryImage?> getImage(String url) async {
if (url.isEmpty) return null;
final response = await _dio.get<Uint8List>(
final response = await dio.get<Uint8List>(
url,
options: Options(
responseType: ResponseType.bytes,
),
options: Options(responseType: ResponseType.bytes),
);
final data = response.data;
if (data == null) return null;
@@ -68,11 +58,9 @@ class Request {
}
Future<Map<String, dynamic>?> checkForUpdate() async {
final response = await _dio.get(
final response = await dio.get(
'https://api.github.com/repos/$repository/releases/latest',
options: Options(
responseType: ResponseType.json,
),
options: Options(responseType: ResponseType.json),
);
if (response.statusCode != 200) return null;
final data = response.data as Map<String, dynamic>;
@@ -85,10 +73,13 @@ class Request {
}
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
'https://ipwho.is/': IpInfo.fromIpwhoIsJson,
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
'https://api.myip.com': IpInfo.fromMyIpJson,
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
'https://ident.me/json': IpInfo.fromIdentMeJson,
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
};
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
@@ -101,27 +92,29 @@ class Request {
}
}
final future = Dio().get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(
responseType: ResponseType.json,
),
);
future.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
} else {
failureCount++;
handleFailRes();
}
}).catchError((e) {
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
}
handleFailRes();
});
final future = dio
.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(responseType: ResponseType.json),
)
.timeout(const Duration(seconds: 10));
future
.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
failureCount++;
handleFailRes();
})
.catchError((e) {
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
}
handleFailRes();
});
return completer.future;
});
final res = await Future.any(futures);
@@ -131,18 +124,12 @@ class Request {
Future<bool> pingHelper() async {
try {
final response = await _dio
final response = await dio
.get(
'http://$localhost:$helperPort/ping',
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}
@@ -154,22 +141,13 @@ class Request {
Future<bool> startCoreByHelper(String arg) async {
try {
final response = await _dio
final response = await dio
.post(
'http://$localhost:$helperPort/start',
data: json.encode({
'path': appPath.corePath,
'arg': arg,
}),
options: Options(
responseType: ResponseType.plain,
),
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}
@@ -182,18 +160,12 @@ class Request {
Future<bool> stopCoreByHelper() async {
try {
final response = await _dio
final response = await dio
.post(
'http://$localhost:$helperPort/stop',
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}

View File

@@ -8,13 +8,40 @@ import 'package:flutter/material.dart';
class BaseScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
PointerDeviceKind.trackpad,
if (system.isDesktop) PointerDeviceKind.mouse,
PointerDeviceKind.unknown,
};
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
PointerDeviceKind.trackpad,
if (system.isDesktop) PointerDeviceKind.mouse,
PointerDeviceKind.unknown,
};
@override
Widget buildScrollbar(
BuildContext context,
Widget child,
ScrollableDetails details,
) {
switch (axisDirectionToAxis(details.direction)) {
case Axis.horizontal:
return child;
case Axis.vertical:
switch (getPlatform(context)) {
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
assert(details.controller != null);
return CommonScrollBar(
controller: details.controller,
child: child,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
return child;
}
}
}
}
class HiddenBarScrollBehavior extends BaseScrollBehavior {
@@ -35,10 +62,7 @@ class ShowBarScrollBehavior extends BaseScrollBehavior {
Widget child,
ScrollableDetails details,
) {
return CommonScrollBar(
controller: details.controller,
child: child,
);
return CommonScrollBar(controller: details.controller, child: child);
}
}
@@ -52,7 +76,9 @@ class NextClampingScrollPhysics extends ClampingScrollPhysics {
@override
Simulation? createBallisticSimulation(
ScrollMetrics position, double velocity) {
ScrollMetrics position,
double velocity,
) {
final Tolerance tolerance = toleranceFor(position);
if (position.outOfRange) {
double? end;

View File

@@ -11,16 +11,15 @@ extension StringExtension on String {
}
dynamic get splitByMultipleSeparators {
final parts =
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
final parts = split(
RegExp(r'[, ;]+'),
).where((part) => part.isNotEmpty).toList();
return parts.length > 1 ? parts : this;
}
int compareToLower(String other) {
return toLowerCase().compareTo(
other.toLowerCase(),
);
return toLowerCase().compareTo(other.toLowerCase());
}
List<int> get encodeUtf16LeWithBom {
@@ -66,9 +65,9 @@ extension StringExtension on String {
return md5.convert(bytes).toString();
}
// bool containsToLower(String target) {
// return toLowerCase().contains(target);
// }
// bool containsToLower(String target) {
// return toLowerCase().contains(target);
// }
}
extension StringExtensionSafe on String? {

View File

@@ -37,7 +37,7 @@ class System {
'macos' => (deviceInfo as MacOsDeviceInfo).majorVersion,
'android' => (deviceInfo as AndroidDeviceInfo).version.sdkInt,
'windows' => (deviceInfo as WindowsDeviceInfo).majorVersion,
String() => 0
String() => 0,
};
}
@@ -104,7 +104,7 @@ class System {
);
final arguments = [
'-c',
'echo "$password" | sudo -S chown root:root "$corePath" && echo "$password" | sudo -S chmod +sx "$corePath"'
'echo "$password" | sudo -S chown root:root "$corePath" && echo "$password" | sudo -S chmod +sx "$corePath"',
];
final result = await Process.run(shell, arguments);
if (result.exitCode != 0) {
@@ -148,21 +148,25 @@ class Windows {
final argumentsPtr = arguments.toNativeUtf16();
final operationPtr = 'runas'.toNativeUtf16();
final shellExecute = _shell32.lookupFunction<
Int32 Function(
final shellExecute = _shell32
.lookupFunction<
Int32 Function(
Pointer<Utf16> hwnd,
Pointer<Utf16> lpOperation,
Pointer<Utf16> lpFile,
Pointer<Utf16> lpParameters,
Pointer<Utf16> lpDirectory,
Int32 nShowCmd),
int Function(
Int32 nShowCmd,
),
int Function(
Pointer<Utf16> hwnd,
Pointer<Utf16> lpOperation,
Pointer<Utf16> lpFile,
Pointer<Utf16> lpParameters,
Pointer<Utf16> lpDirectory,
int nShowCmd)>('ShellExecuteW');
int nShowCmd,
)
>('ShellExecuteW');
final result = shellExecute(
nullptr,
@@ -177,7 +181,10 @@ class Windows {
calloc.free(argumentsPtr);
calloc.free(operationPtr);
commonPrint.log('windows runas: $command $arguments resultCode:$result');
commonPrint.log(
'windows runas: $command $arguments resultCode:$result',
logLevel: LogLevel.warning,
);
if (result < 42) {
return false;
@@ -248,15 +255,14 @@ class Windows {
final res = runas('cmd.exe', command);
await Future.delayed(
Duration(milliseconds: 300),
);
await Future.delayed(Duration(milliseconds: 300));
return res;
}
Future<bool> registerTask(String appName) async {
final taskXml = '''
final taskXml =
'''
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Principals>
@@ -295,8 +301,9 @@ class Windows {
</Task>''';
final taskPath = join(await appPath.tempPath, 'task.xml');
await File(taskPath).create(recursive: true);
await File(taskPath)
.writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);
await File(
taskPath,
).writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);
final commandLine = [
'/Create',
'/TN',
@@ -305,10 +312,7 @@ class Windows {
'%s',
'/F',
].join(' ');
return runas(
'schtasks',
commandLine.replaceFirst('%s', taskPath),
);
return runas('schtasks', commandLine.replaceFirst('%s', taskPath));
}
}
@@ -337,23 +341,25 @@ class MacOS {
return null;
}
final device = lineSplits[1];
final serviceResult = await Process.run(
'networksetup',
['-listnetworkserviceorder'],
);
final serviceResult = await Process.run('networksetup', [
'-listnetworkserviceorder',
]);
final serviceResultOutput = serviceResult.stdout.toString();
final currentService = serviceResultOutput.split('\n\n').firstWhere(
(s) => s.contains('Device: $device'),
orElse: () => '',
);
final currentService = serviceResultOutput
.split('\n\n')
.firstWhere((s) => s.contains('Device: $device'), orElse: () => '');
if (currentService.isEmpty) {
return null;
}
final currentServiceNameLine = currentService.split('\n').firstWhere(
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
orElse: () => '');
final currentServiceNameLineSplits =
currentServiceNameLine.trim().split(' ');
final currentServiceNameLine = currentService
.split('\n')
.firstWhere(
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
orElse: () => '',
);
final currentServiceNameLineSplits = currentServiceNameLine.trim().split(
' ',
);
if (currentServiceNameLineSplits.length < 2) {
return null;
}
@@ -365,10 +371,10 @@ class MacOS {
if (deviceServiceName == null) {
return null;
}
final result = await Process.run(
'networksetup',
['-getdnsservers', deviceServiceName],
);
final result = await Process.run('networksetup', [
'-getdnsservers',
deviceServiceName,
]);
final output = result.stdout.toString().trim();
if (output.startsWith("There aren't any DNS Servers set on")) {
originDns = [];
@@ -400,15 +406,12 @@ class MacOS {
if (nextDns == null) {
return;
}
await Process.run(
'networksetup',
[
'-setdnsservers',
serviceName,
if (nextDns.isNotEmpty) ...nextDns,
if (nextDns.isEmpty) 'Empty',
],
);
await Process.run('networksetup', [
'-setdnsservers',
serviceName,
if (nextDns.isNotEmpty) ...nextDns,
if (nextDns.isEmpty) 'Empty',
]);
}
}

View File

@@ -19,9 +19,6 @@ class Tray {
required Brightness? brightness,
bool force = false,
}) async {
if (system.isAndroid) {
return;
}
if (Platform.isLinux || force) {
await trayManager.destroy();
}
@@ -197,4 +194,4 @@ class Tray {
}
}
final tray = Tray();
final tray = system.isDesktop ? Tray() : null;

View File

@@ -8,7 +8,6 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lpinyin/lpinyin.dart';
class Utils {
Color? getDelayColor(int? delay) {
@@ -21,16 +20,15 @@ class Utils {
String get id {
final timestamp = DateTime.now().microsecondsSinceEpoch;
final random = Random();
final randomStr =
String.fromCharCodes(List.generate(8, (_) => random.nextInt(26) + 97));
final randomStr = String.fromCharCodes(
List.generate(8, (_) => random.nextInt(26) + 97),
);
return '$timestamp$randomStr';
}
String getDateStringLast2(int value) {
var valueRaw = '0$value';
return valueRaw.substring(
valueRaw.length - 2,
);
return valueRaw.substring(valueRaw.length - 2);
}
String generateRandomString({int minLength = 10, int maxLength = 100}) {
@@ -43,8 +41,9 @@ class Utils {
String result = '';
for (int i = 0; i < length; i++) {
if (random.nextBool()) {
result +=
String.fromCharCode(0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1));
result += String.fromCharCode(
0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1),
);
} else {
result += latinChars[random.nextInt(latinChars.length)];
}
@@ -60,8 +59,9 @@ class Utils {
bytes[6] = (bytes[6] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
final hex =
bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
final hex = bytes
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join();
return '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20, 32)}';
}
@@ -102,9 +102,10 @@ class Utils {
}
if (localSplit.length == 3) {
return Locale.fromSubtags(
languageCode: localSplit[0],
scriptCode: localSplit[1],
countryCode: localSplit[2]);
languageCode: localSplit[0],
scriptCode: localSplit[1],
countryCode: localSplit[2],
);
}
return null;
}
@@ -141,9 +142,7 @@ class Utils {
}
}
String getTrayIconPath({
required Brightness brightness,
}) {
String getTrayIconPath({required Brightness brightness}) {
if (system.isMacOS) {
return 'assets/images/icon_white.png';
}
@@ -178,26 +177,30 @@ class Utils {
return build1.compareTo(build2);
}
String getPinyin(String value) {
return value.isNotEmpty
? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
: '';
}
// String getPinyin(String value) {
// return value.isNotEmpty
// ? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
// : '';
// }
String? getFileNameForDisposition(String? disposition) {
if (disposition == null) return null;
final parseValue = HeaderValue.parse(disposition);
final parameters = parseValue.parameters;
final fileNamePointKey = parameters.keys
.firstWhere((key) => key == 'filename*', orElse: () => '');
final fileNamePointKey = parameters.keys.firstWhere(
(key) => key == 'filename*',
orElse: () => '',
);
if (fileNamePointKey.isNotEmpty) {
final res = parameters[fileNamePointKey]?.split("''") ?? [];
if (res.length >= 2) {
return Uri.decodeComponent(res[1]);
}
}
final fileNameKey = parameters.keys
.firstWhere((key) => key == 'filename', orElse: () => '');
final fileNameKey = parameters.keys.firstWhere(
(key) => key == 'filename',
orElse: () => '',
);
if (fileNameKey.isEmpty) return null;
return parameters[fileNameKey];
}
@@ -224,7 +227,7 @@ class Utils {
}
int getProxiesColumns(double viewWidth, ProxiesLayout proxiesLayout) {
final columns = max((viewWidth / 300).ceil(), 2);
final columns = max((viewWidth / 250).ceil(), 2);
return switch (proxiesLayout) {
ProxiesLayout.tight => columns + 1,
ProxiesLayout.standard => columns,
@@ -233,22 +236,10 @@ class Utils {
}
int getProfilesColumns(double viewWidth) {
return max((viewWidth / 320).floor(), 1);
return max((viewWidth / 280).floor(), 1);
}
final _indexPrimary = [
50,
100,
200,
300,
400,
500,
600,
700,
800,
850,
900,
];
final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900];
MaterialColor _createPrimarySwatch(Color color) {
final Map<int, Color> swatch = <int, Color>{};
@@ -302,16 +293,15 @@ class Utils {
}
Future<String?> getLocalIpAddress() async {
List<NetworkInterface> interfaces = await NetworkInterface.list(
includeLoopback: false,
)
..sort((a, b) {
if (a.isWifi && !b.isWifi) return -1;
if (!a.isWifi && b.isWifi) return 1;
if (a.includesIPv4 && !b.includesIPv4) return -1;
if (!a.includesIPv4 && b.includesIPv4) return 1;
return 0;
});
List<NetworkInterface> interfaces =
await NetworkInterface.list(includeLoopback: false)
..sort((a, b) {
if (a.isWifi && !b.isWifi) return -1;
if (!a.isWifi && b.isWifi) return 1;
if (a.includesIPv4 && !b.includesIPv4) return -1;
if (!a.includesIPv4 && b.includesIPv4) return 1;
return 0;
});
for (final interface in interfaces) {
final addresses = interface.addresses;
if (addresses.isEmpty) {
@@ -329,59 +319,9 @@ class Utils {
SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) {
final control = system.isMacOS ? false : true;
return SingleActivator(
trigger,
control: control,
meta: !control,
);
return SingleActivator(trigger, control: control, meta: !control);
}
// dynamic convertYamlNode(dynamic node) {
// if (node is YamlMap) {
// final map = <String, dynamic>{};
// YamlNode? mergeKeyNode;
// for (final entry in node.nodes.entries) {
// if (entry.key is YamlScalar &&
// (entry.key as YamlScalar).value == '<<') {
// mergeKeyNode = entry.value;
// break;
// }
// }
// if (mergeKeyNode != null) {
// final mergeValue = mergeKeyNode.value;
// if (mergeValue is YamlMap) {
// map.addAll(convertYamlNode(mergeValue) as Map<String, dynamic>);
// } else if (mergeValue is YamlList) {
// for (final node in mergeValue.nodes) {
// if (node.value is YamlMap) {
// map.addAll(convertYamlNode(node.value) as Map<String, dynamic>);
// }
// }
// }
// }
//
// node.nodes.forEach((key, value) {
// String stringKey;
// if (key is YamlScalar) {
// stringKey = key.value.toString();
// } else {
// stringKey = key.toString();
// }
// map[stringKey] = convertYamlNode(value.value);
// });
// return map;
// } else if (node is YamlList) {
// final list = <dynamic>[];
// for (final item in node.nodes) {
// list.add(convertYamlNode(item.value));
// }
// return list;
// } else if (node is YamlScalar) {
// return node.value;
// }
// return node;
// }
FutureOr<T> handleWatch<T>(Function function) async {
if (kDebugMode) {
final stopwatch = Stopwatch()..start();

View File

@@ -3,7 +3,6 @@ import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as acrylic;
import 'package:screen_retriever/screen_retriever.dart';
import 'package:window_manager/window_manager.dart';
@@ -19,9 +18,6 @@ class Window {
protocol.register('clashmeta');
protocol.register('flclash');
}
if ((version > 10 && system.isMacOS)) {
await acrylic.Window.initialize();
}
await windowManager.ensureInitialized();
WindowOptions windowOptions = WindowOptions(
size: Size(props.width, props.height),
@@ -39,25 +35,18 @@ class Window {
await windowManager.setAlignment(Alignment.center);
} else {
final displays = await screenRetriever.getAllDisplays();
final isPositionValid = displays.any(
(display) {
final displayBounds = Rect.fromLTWH(
display.visiblePosition!.dx,
display.visiblePosition!.dy,
display.size.width,
display.size.height,
);
return displayBounds.contains(Offset(left, top)) ||
displayBounds.contains(Offset(right, bottom));
},
);
if (isPositionValid) {
await windowManager.setPosition(
Offset(
left,
top,
),
final isPositionValid = displays.any((display) {
final displayBounds = Rect.fromLTWH(
display.visiblePosition!.dx,
display.visiblePosition!.dy,
display.size.width,
display.size.height,
);
return displayBounds.contains(Offset(left, top)) ||
displayBounds.contains(Offset(right, bottom));
});
if (isPositionValid) {
await windowManager.setPosition(Offset(left, top));
}
}
}
@@ -66,13 +55,6 @@ class Window {
});
}
void updateMacOSBrightness(Brightness brightness) {
if (!system.isMacOS) {
return;
}
acrylic.Window.overrideMacOSBrightness(dark: brightness == Brightness.dark);
}
Future<void> show() async {
render?.resume();
await windowManager.show();

View File

@@ -4,8 +4,8 @@ import 'dart:io';
import 'dart:isolate';
import 'package:archive/archive.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/archive.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/providers/providers.dart';
@@ -20,7 +20,6 @@ import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
import 'models/models.dart';
import 'views/profiles/override_profile.dart';
class AppController {
int? lastProfileModified;
@@ -42,8 +41,8 @@ class AppController {
});
}
void updateGroupsDebounce() {
debouncer.call(FunctionTag.updateGroups, updateGroups);
void updateGroupsDebounce([Duration? duration]) {
debouncer.call(FunctionTag.updateGroups, updateGroups, duration: duration);
}
void addCheckIpNumDebounce() {
@@ -52,9 +51,7 @@ class AppController {
});
}
void applyProfileDebounce({
bool silence = false,
}) {
void applyProfileDebounce({bool silence = false}) {
debouncer.call(FunctionTag.applyProfile, (silence) {
applyProfile(silence: silence);
}, args: [silence]);
@@ -65,33 +62,32 @@ class AppController {
}
void changeProxyDebounce(String groupName, String proxyName) {
debouncer.call(FunctionTag.changeProxy,
(String groupName, String proxyName) async {
await changeProxy(
groupName: groupName,
proxyName: proxyName,
);
debouncer.call(FunctionTag.changeProxy, (
String groupName,
String proxyName,
) async {
await changeProxy(groupName: groupName, proxyName: proxyName);
await updateGroups();
}, args: [groupName, proxyName]);
}
Future<void> restartCore() async {
commonPrint.log('restart core');
await clashService?.reStart();
globalState.isUserDisconnected = true;
await coreController.shutdown();
await _connectCore();
await _initCore();
if (_ref.read(runTimeProvider.notifier).isStart) {
_ref.read(initProvider.notifier).value = true;
if (_ref.read(isStartProvider)) {
await globalState.handleStart();
}
}
Future<void> updateStatus(bool isStart) async {
if (isStart) {
await globalState.handleStart([
updateRunTime,
updateTraffic,
]);
final currentLastModified =
await _ref.read(currentProfileProvider)?.profileLastModified;
await globalState.handleStart([updateRunTime, updateTraffic]);
final currentLastModified = await _ref
.read(currentProfileProvider)
?.profileLastModified;
if (currentLastModified == null || lastProfileModified == null) {
addCheckIpNumDebounce();
return;
@@ -103,7 +99,7 @@ class AppController {
applyProfileDebounce();
} else {
await globalState.handleStop();
clashCore.resetTraffic();
coreController.resetTraffic();
_ref.read(trafficsProvider.notifier).clear();
_ref.read(totalTrafficProvider.notifier).value = Traffic();
_ref.read(runTimeProvider.notifier).value = null;
@@ -123,10 +119,13 @@ class AppController {
}
Future<void> updateTraffic() async {
final traffic = await clashCore.getTraffic();
final onlyStatisticsProxy = _ref.read(
appSettingProvider.select((state) => state.onlyStatisticsProxy),
);
final traffic = await coreController.getTraffic(onlyStatisticsProxy);
_ref.read(trafficsProvider.notifier).addTraffic(traffic);
_ref.read(totalTrafficProvider.notifier).value =
await clashCore.getTotalTraffic();
_ref.read(totalTrafficProvider.notifier).value = await coreController
.getTotalTraffic(onlyStatisticsProxy);
}
Future<void> addProfile(Profile profile) async {
@@ -152,8 +151,8 @@ class AppController {
}
Future<void> updateProviders() async {
_ref.read(providersProvider.notifier).value =
await clashCore.getExternalProviders();
_ref.read(providersProvider.notifier).value = await coreController
.getExternalProviders();
}
Future<void> updateLocalIp() async {
@@ -193,8 +192,9 @@ class AppController {
void updateOrAddHotKeyAction(HotKeyAction hotKeyAction) {
final hotKeyActions = _ref.read(hotKeyActionsProvider);
final index =
hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action);
final index = hotKeyActions.indexWhere(
(item) => item.action == hotKeyAction.action,
);
if (index == -1) {
_ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions)
..add(hotKeyAction);
@@ -225,16 +225,12 @@ class AppController {
}
String? getCurrentGroupName() {
final currentGroupName = _ref.read(currentProfileProvider.select(
(state) => state?.currentGroupName,
));
final currentGroupName = _ref.read(
currentProfileProvider.select((state) => state?.currentGroupName),
);
return currentGroupName;
}
ProxyCardState getProxyCardState(String proxyName) {
return _ref.read(getProxyCardStateProvider(proxyName));
}
String? getSelectedProxyName(String groupName) {
return _ref.read(getSelectedProxyNameProvider(groupName));
}
@@ -244,18 +240,13 @@ class AppController {
if (profile == null || profile.currentGroupName == groupName) {
return;
}
setProfile(
profile.copyWith(currentGroupName: groupName),
);
setProfile(profile.copyWith(currentGroupName: groupName));
}
Future<void> updateClashConfig() async {
await safeRun(
() async {
await _updateClashConfig();
},
needLoading: true,
);
await safeRun(() async {
await _updateClashConfig();
}, needLoading: true);
}
Future<void> _updateClashConfig() async {
@@ -265,16 +256,14 @@ class AppController {
return;
}
final realTunEnable = _ref.read(realTunEnableProvider);
final message = await clashCore.updateConfig(
updateParams.copyWith.tun(
enable: realTunEnable,
),
final message = await coreController.updateConfig(
updateParams.copyWith.tun(enable: realTunEnable),
);
if (message.isNotEmpty) throw message;
}
Future<Result<bool>> _requestAdmin(bool enableTun) async {
if(system.isWindows && kDebugMode){
if (system.isWindows && kDebugMode) {
return Result.success(false);
}
final realTunEnable = _ref.read(realTunEnableProvider);
@@ -296,12 +285,9 @@ class AppController {
}
Future<void> setupClashConfig() async {
await safeRun(
() async {
await _setupClashConfig();
},
needLoading: true,
);
await safeRun(() async {
await _setupClashConfig();
}, needLoading: true);
}
Future<void> _setupClashConfig() async {
@@ -313,14 +299,9 @@ class AppController {
}
final realTunEnable = _ref.read(realTunEnableProvider);
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
final params = await globalState.getSetupParams(
pathConfig: realPatchConfig,
);
final message = await clashCore.setupConfig(params);
final message = await coreController.setupConfig(realPatchConfig);
lastProfileModified = await _ref.read(
currentProfileProvider.select(
(state) => state?.profileLastModified,
),
currentProfileProvider.select((state) => state?.profileLastModified),
);
if (message.isNotEmpty) {
throw message;
@@ -328,7 +309,7 @@ class AppController {
}
Future _applyProfile() async {
await clashCore.requestGc();
await coreController.requestGc();
await setupClashConfig();
await updateGroups();
await updateProviders();
@@ -338,12 +319,9 @@ class AppController {
if (silence) {
await _applyProfile();
} else {
await safeRun(
() async {
await _applyProfile();
},
needLoading: true,
);
await safeRun(() async {
await _applyProfile();
}, needLoading: true);
}
addCheckIpNumDebounce();
}
@@ -367,9 +345,7 @@ class AppController {
for (final profile in _ref.read(profilesProvider)) {
if (!profile.autoUpdate) continue;
final isNotNeedUpdate = profile.lastUpdateDate
?.add(
profile.autoUpdateDuration,
)
?.add(profile.autoUpdateDuration)
.isBeforeNow;
if (isNotNeedUpdate == false || profile.type == ProfileType.file) {
continue;
@@ -377,7 +353,7 @@ class AppController {
try {
await updateProfile(profile);
} catch (e) {
commonPrint.log(e.toString());
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
}
}
}
@@ -386,7 +362,22 @@ class AppController {
try {
_ref.read(groupsProvider.notifier).value = await retry(
task: () async {
return await clashCore.getProxiesGroups();
final sortType = _ref.read(
proxiesStyleSettingProvider.select((state) => state.sortType),
);
final delayMap = _ref.read(delayDataSourceProvider);
final testUrl = _ref.read(
appSettingProvider.select((state) => state.testUrl),
);
final selectedMap = _ref.read(
currentProfileProvider.select((state) => state?.selectedMap ?? {}),
);
return await coreController.getProxiesGroups(
selectedMap: selectedMap,
sortType: sortType,
delayMap: delayMap,
defaultTestUrl: testUrl,
);
},
retryIf: (res) => res.isEmpty,
);
@@ -413,14 +404,11 @@ class AppController {
required String groupName,
required String proxyName,
}) async {
await clashCore.changeProxy(
ChangeProxyParams(
groupName: groupName,
proxyName: proxyName,
),
await coreController.changeProxy(
ChangeProxyParams(groupName: groupName, proxyName: proxyName),
);
if (_ref.read(appSettingProvider).closeConnections) {
clashCore.closeConnections();
coreController.closeConnections();
}
addCheckIpNumDebounce();
}
@@ -455,8 +443,8 @@ class AppController {
await savePreferences();
await macOS?.updateDns(true);
await proxy?.stopProxy();
await clashCore.shutdown();
await clashService?.destroy();
await coreController.shutdown();
await coreController.destroy();
} finally {
system.exit();
}
@@ -465,9 +453,7 @@ class AppController {
Future handleClear() async {
await preferences.clearPreferences();
commonPrint.log('clear preferences');
globalState.config = Config(
themeProps: defaultThemeProps,
);
globalState.config = Config(themeProps: defaultThemeProps);
}
Future<void> autoCheckUpdate() async {
@@ -494,15 +480,9 @@ class AppController {
text: '$tagName \n',
style: textTheme.headlineSmall,
children: [
TextSpan(
text: '\n',
style: textTheme.bodyMedium,
),
TextSpan(text: '\n', style: textTheme.bodyMedium),
for (final submit in submits)
TextSpan(
text: '- $submit \n',
style: textTheme.bodyMedium,
),
TextSpan(text: '- $submit \n', style: textTheme.bodyMedium),
],
),
confirmText: appLocalizations.goDownload,
@@ -510,15 +490,11 @@ class AppController {
if (res != true) {
return;
}
launchUrl(
Uri.parse('https://github.com/$repository/releases/latest'),
);
launchUrl(Uri.parse('https://github.com/$repository/releases/latest'));
} else if (handleError) {
globalState.showMessage(
title: appLocalizations.checkUpdate,
message: TextSpan(
text: appLocalizations.checkUpdateError,
),
message: TextSpan(text: appLocalizations.checkUpdateError),
);
}
}
@@ -542,30 +518,24 @@ class AppController {
}
Future<void> _initCore() async {
final isInit = await clashCore.isInit;
final isInit = await coreController.isInit;
if (!isInit) {
await clashCore.init();
await clashCore.setState(
globalState.getCoreState(),
);
await coreController.init(globalState.appState.version);
}
await applyProfile();
}
Future<void> init() async {
FlutterError.onError = (details) {
if (kDebugMode) {
commonPrint.log(details.stack.toString());
}
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
updateTray(true);
await _initCore();
await _initStatus();
autoLaunch?.updateStatus(
_ref.read(appSettingProvider).autoLaunch,
);
autoUpdateProfiles();
autoCheckUpdate();
autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);
if (!_ref.read(appSettingProvider).silentLaunch) {
window?.show();
} else {
@@ -573,9 +543,31 @@ class AppController {
}
await _handlePreference();
await _handlerDisclaimer();
await _showCrashlyticsTip();
await _connectCore();
await _initCore();
await _initStatus();
_ref.read(initProvider.notifier).value = true;
}
Future<void> _connectCore() async {
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting;
final result = await Future.wait([
coreController.preload(),
if (!globalState.isService) Future.delayed(Duration(milliseconds: 300)),
]);
final String message = result[0];
await Future.delayed(commonDuration);
if (message.isNotEmpty) {
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
if (context.mounted) {
context.showNotifier(message);
}
return;
}
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connected;
}
Future<void> _initStatus() async {
if (system.isAndroid) {
await globalState.updateStartTime();
@@ -603,34 +595,32 @@ class AppController {
}
void initLink() {
linkManager.initAppLinksListen(
(url) async {
final res = await globalState.showMessage(
title: '${appLocalizations.add}${appLocalizations.profile}',
message: TextSpan(
children: [
TextSpan(text: appLocalizations.doYouWantToPass),
TextSpan(
text: ' $url ',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
decorationColor: Theme.of(context).colorScheme.primary,
),
linkManager.initAppLinksListen((url) async {
final res = await globalState.showMessage(
title: '${appLocalizations.add}${appLocalizations.profile}',
message: TextSpan(
children: [
TextSpan(text: appLocalizations.doYouWantToPass),
TextSpan(
text: ' $url ',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
decorationColor: Theme.of(context).colorScheme.primary,
),
TextSpan(
text:
'${appLocalizations.create}${appLocalizations.profile}'),
],
),
);
),
TextSpan(
text: '${appLocalizations.create}${appLocalizations.profile}',
),
],
),
);
if (res != true) {
return;
}
addProfileFormURL(url);
},
);
if (res != true) {
return;
}
addProfileFormURL(url);
});
}
Future<bool> showDisclaimer() async {
@@ -647,30 +637,47 @@ class AppController {
),
TextButton(
onPressed: () {
_ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(disclaimerAccepted: true),
);
Navigator.of(context).pop<bool>(true);
},
child: Text(appLocalizations.agree),
)
),
],
child: SelectableText(
appLocalizations.disclaimerDesc,
),
child: Text(appLocalizations.disclaimerDesc),
),
) ??
false;
}
Future<void> _showCrashlyticsTip() async {
if (!system.isAndroid) {
return;
}
if (_ref.read(appSettingProvider.select((state) => state.crashlyticsTip))) {
return;
}
await globalState.showMessage(
title: appLocalizations.dataCollectionTip,
cancelable: false,
message: TextSpan(text: appLocalizations.dataCollectionContent),
);
_ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(crashlyticsTip: true));
}
Future<void> _handlerDisclaimer() async {
if (_ref.read(appSettingProvider).disclaimerAccepted) {
if (_ref.read(
appSettingProvider.select((state) => state.disclaimerAccepted),
)) {
return;
}
final isDisclaimerAccepted = await showDisclaimer();
if (!isDisclaimerAccepted) {
await handleExit();
}
_ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(disclaimerAccepted: true));
return;
}
@@ -682,9 +689,7 @@ class AppController {
final profile = await safeRun(
() async {
return await Profile.normal(
url: url,
).update();
return await Profile.normal(url: url).update();
},
needLoading: true,
title: '${appLocalizations.add}${appLocalizations.profile}',
@@ -718,9 +723,7 @@ class AppController {
}
Future<void> addProfileFormQrCode() async {
final url = await safeRun(
picker.pickerConfigQRCode,
);
final url = await safeRun(picker.pickerConfigQRCode);
if (url == null) return;
addProfileFormURL(url);
}
@@ -735,63 +738,7 @@ class AppController {
_ref.read(providersProvider.notifier).setProvider(provider);
}
List<Proxy> _sortOfName(List<Proxy> proxies) {
return List.of(proxies)
..sort(
(a, b) => utils.sortByChar(
utils.getPinyin(a.name),
utils.getPinyin(b.name),
),
);
}
List<Proxy> _sortOfDelay({
required List<Proxy> proxies,
String? testUrl,
}) {
return List.of(proxies)
..sort(
(a, b) {
final aDelay = _ref.read(getDelayProvider(
proxyName: a.name,
testUrl: testUrl,
));
final bDelay = _ref.read(
getDelayProvider(
proxyName: b.name,
testUrl: testUrl,
),
);
if (aDelay == null && bDelay == null) {
return 0;
}
if (aDelay == null || aDelay == -1) {
return 1;
}
if (bDelay == null || bDelay == -1) {
return -1;
}
return aDelay.compareTo(bDelay);
},
);
}
List<Proxy> getSortProxies({
required List<Proxy> proxies,
required ProxiesSortType sortType,
String? testUrl,
}) {
return switch (sortType) {
ProxiesSortType.none => proxies,
ProxiesSortType.delay => _sortOfDelay(
proxies: proxies,
testUrl: testUrl,
),
ProxiesSortType.name => _sortOfName(proxies),
};
}
Future<Null> clearEffect(String profileId) async {
Future<void> clearEffect(String profileId) async {
final profilePath = await appPath.getProfilePath(profileId);
final providersDirPath = await appPath.getProvidersDirPath(profileId);
return await Isolate.run(() async {
@@ -800,28 +747,28 @@ class AppController {
if (isExists) {
profileFile.delete(recursive: true);
}
final providersFileDir = File(providersDirPath);
final providersFileIsExists = await providersFileDir.exists();
if (providersFileIsExists) {
providersFileDir.delete(recursive: true);
}
await coreController.deleteFile(providersDirPath);
});
}
void updateTun() {
_ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.tun(enable: !state.tun.enable),
);
_ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.tun(enable: !state.tun.enable));
}
void updateSystemProxy() {
_ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
systemProxy: !state.systemProxy,
),
_ref
.read(networkSettingProvider.notifier)
.updateState(
(state) => state.copyWith(systemProxy: !state.systemProxy),
);
}
void handleCoreDisconnected() {
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
}
Future<List<Package>> getPackages() async {
if (_ref.read(isMobileViewProvider)) {
await Future.delayed(commonDuration);
@@ -834,21 +781,18 @@ class AppController {
}
void updateStart() {
updateStatus(!_ref.read(runTimeProvider.notifier).isStart);
updateStatus(!_ref.read(isStartProvider));
}
void updateCurrentSelectedMap(String groupName, String proxyName) {
final currentProfile = _ref.read(currentProfileProvider);
if (currentProfile != null &&
currentProfile.selectedMap[groupName] != proxyName) {
final SelectedMap selectedMap = Map.from(
currentProfile.selectedMap,
)..[groupName] = proxyName;
_ref.read(profilesProvider.notifier).setProfile(
currentProfile.copyWith(
selectedMap: selectedMap,
),
);
final SelectedMap selectedMap = Map.from(currentProfile.selectedMap)
..[groupName] = proxyName;
_ref
.read(profilesProvider.notifier)
.setProfile(currentProfile.copyWith(selectedMap: selectedMap));
}
}
@@ -857,17 +801,15 @@ class AppController {
if (currentProfile == null) {
return;
}
_ref.read(profilesProvider.notifier).setProfile(
currentProfile.copyWith(
unfoldSet: value,
),
);
_ref
.read(profilesProvider.notifier)
.setProfile(currentProfile.copyWith(unfoldSet: value));
}
void changeMode(Mode mode) {
_ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(mode: mode),
);
_ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(mode: mode));
if (mode == Mode.global) {
updateCurrentGroupName(GroupName.GLOBAL.name);
}
@@ -875,11 +817,9 @@ class AppController {
}
void updateAutoLaunch() {
_ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoLaunch: !state.autoLaunch,
),
);
_ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(autoLaunch: !state.autoLaunch));
}
Future<void> updateVisible() async {
@@ -892,64 +832,23 @@ class AppController {
}
void updateMode() {
_ref.read(patchClashConfigProvider.notifier).updateState(
(state) {
final index = Mode.values.indexWhere((item) => item == state.mode);
if (index == -1) {
return null;
}
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
return state.copyWith(
mode: Mode.values[nextIndex],
);
},
);
}
Future<void> handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {
final res = await globalState.showCommonDialog<Rule>(
child: AddRuleDialog(
rule: rule,
snippet: ref.read(
profileOverrideStateProvider.select(
(state) => state.snippet!,
),
),
),
);
if (res == null) {
return;
}
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) {
final model = state.copyWith.overrideData!(
rule: state.overrideData!.rule.updateRules(
(rules) {
final index = rules.indexWhere((item) => item.id == res.id);
if (index == -1) {
return List.from([res, ...rules]);
}
return List.from(rules)..[index] = res;
},
),
);
return model;
},
);
_ref.read(patchClashConfigProvider.notifier).updateState((state) {
final index = Mode.values.indexWhere((item) => item == state.mode);
if (index == -1) {
return null;
}
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
return state.copyWith(mode: Mode.values[nextIndex]);
});
}
Future<bool> exportLogs() async {
final logsRaw = _ref.read(logsProvider).list.map(
(item) => item.toString(),
);
final logsRaw = _ref.read(logsProvider).list.map((item) => item.toString());
final data = await Isolate.run<List<int>>(() async {
final logsRawString = logsRaw.join('\n');
return utf8.encode(logsRawString);
});
return await picker.saveFile(
utils.logFile,
Uint8List.fromList(data),
) !=
return await picker.saveFile(utils.logFile, Uint8List.fromList(data)) !=
null;
}
@@ -959,17 +858,15 @@ class AppController {
final configJson = globalState.config.toJson();
return Isolate.run<List<int>>(() async {
final archive = Archive();
archive.add('config.json', configJson);
archive.addTextFile('config.json', configJson);
archive.addDirectoryToArchive(profilesPath, homeDirPath);
final zipEncoder = ZipEncoder();
return zipEncoder.encode(archive) ?? [];
return zipEncoder.encode(archive);
});
}
Future<void> updateTray([bool focus = false]) async {
tray.update(
trayState: _ref.read(trayStateProvider),
);
tray?.update(trayState: _ref.read(trayStateProvider));
}
Future<void> recoveryData(
@@ -981,18 +878,19 @@ class AppController {
return zipDecoder.decodeBytes(data);
});
final homeDirPath = await appPath.homeDirPath;
final configs =
archive.files.where((item) => item.name.endsWith('.json')).toList();
final profiles =
archive.files.where((item) => !item.name.endsWith('.json'));
final configIndex =
configs.indexWhere((config) => config.name == 'config.json');
final configs = archive.files
.where((item) => item.name.endsWith('.json'))
.toList();
final profiles = archive.files.where(
(item) => !item.name.endsWith('.json'),
);
final configIndex = configs.indexWhere(
(config) => config.name == 'config.json',
);
if (configIndex == -1) throw 'invalid backup file';
final configFile = configs[configIndex];
var tempConfig = Config.compatibleFromJson(
json.decode(
utf8.decode(configFile.content),
),
json.decode(utf8.decode(configFile.content)),
);
for (final profile in profiles) {
final filePath = join(homeDirPath, profile.name);
@@ -1000,38 +898,30 @@ class AppController {
await file.create(recursive: true);
await file.writeAsBytes(profile.content);
}
final clashConfigIndex =
configs.indexWhere((config) => config.name == 'clashConfig.json');
final clashConfigIndex = configs.indexWhere(
(config) => config.name == 'clashConfig.json',
);
if (clashConfigIndex != -1) {
final clashConfigFile = configs[clashConfigIndex];
tempConfig = tempConfig.copyWith(
patchClashConfig: ClashConfig.fromJson(
json.decode(
utf8.decode(
clashConfigFile.content,
),
),
json.decode(utf8.decode(clashConfigFile.content)),
),
);
}
_recovery(
tempConfig,
recoveryOption,
);
_recovery(tempConfig, recoveryOption);
}
void _recovery(Config config, RecoveryOption recoveryOption) {
final recoveryStrategy = _ref.read(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
final recoveryStrategy = _ref.read(
appSettingProvider.select((state) => state.recoveryStrategy),
);
final profiles = config.profiles;
if (recoveryStrategy == RecoveryStrategy.override) {
_ref.read(profilesProvider.notifier).value = profiles;
} else {
for (final profile in profiles) {
_ref.read(profilesProvider.notifier).setProfile(
profile,
);
_ref.read(profilesProvider.notifier).setProfile(profile);
}
}
final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
@@ -1072,15 +962,13 @@ class AppController {
final res = await futureFunction();
return res;
} catch (e) {
commonPrint.log('$e');
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
if (realSilence) {
globalState.showNotifier(e.toString());
} else {
globalState.showMessage(
title: title ?? appLocalizations.tip,
message: TextSpan(
text: e.toString(),
),
message: TextSpan(text: e.toString()),
);
}
return null;

276
lib/core/controller.dart Normal file
View File

@@ -0,0 +1,276 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/core/interface.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
class CoreController {
static CoreController? _instance;
late CoreHandlerInterface _interface;
CoreController._internal() {
if (system.isAndroid) {
_interface = coreLib!;
} else {
_interface = coreService!;
}
}
factory CoreController() {
_instance ??= CoreController._internal();
return _instance!;
}
Future<String> preload() {
return _interface.preload();
}
static Future<void> initGeo() async {
final homePath = await appPath.homeDirPath;
final homeDir = Directory(homePath);
final isExists = await homeDir.exists();
if (!isExists) {
await homeDir.create(recursive: true);
}
const geoFileNameList = [MMDB, GEOIP, GEOSITE, ASN];
try {
for (final geoFileName in geoFileNameList) {
final geoFile = File(join(homePath, geoFileName));
final isExists = await geoFile.exists();
if (isExists) {
continue;
}
final data = await rootBundle.load('assets/data/$geoFileName');
List<int> bytes = data.buffer.asUint8List();
await geoFile.writeAsBytes(bytes, flush: true);
}
} catch (e) {
exit(0);
}
}
Future<bool> init(int version) async {
await initGeo();
final homeDirPath = await appPath.homeDirPath;
return await _interface.init(
InitParams(homeDir: homeDirPath, version: version),
);
}
Future<void> shutdown() async {
await _interface.shutdown();
}
FutureOr<bool> get isInit => _interface.isInit;
FutureOr<String> validateConfig(String data) {
return _interface.validateConfig(data);
}
Future<String> updateConfig(UpdateParams updateParams) async {
return await _interface.updateConfig(updateParams);
}
Future<String> setupConfig(ClashConfig clashConfig) async {
await globalState.genConfigFile(clashConfig);
final params = await globalState.getSetupParams();
return await _interface.setupConfig(params);
}
Future<List<Group>> getProxiesGroups({
required ProxiesSortType sortType,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String defaultTestUrl,
}) async {
final proxies = await _interface.getProxies();
return Isolate.run<List<Group>>(() {
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
}),
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group['all'] = ((group['all'] ?? []) as List)
.map((name) => proxies[name])
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
final groups = groupsRaw.map((e) => Group.fromJson(e)).toList();
return computeSort(
groups: groups,
sortType: sortType,
delayMap: delayMap,
selectedMap: selectedMap,
defaultTestUrl: defaultTestUrl,
);
});
}
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
return await _interface.changeProxy(changeProxyParams);
}
Future<List<TrackerInfo>> getConnections() async {
final res = await _interface.getConnections();
final connectionsData = json.decode(res) as Map;
final connectionsRaw = connectionsData['connections'] as List? ?? [];
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
}
void closeConnection(String id) {
_interface.closeConnection(id);
}
void closeConnections() {
_interface.closeConnections();
}
void resetConnections() {
_interface.resetConnections();
}
Future<List<ExternalProvider>> getExternalProviders() async {
final externalProvidersRawString = await _interface.getExternalProviders();
if (externalProvidersRawString.isEmpty) {
return [];
}
return Isolate.run<List<ExternalProvider>>(() {
final externalProviders =
(json.decode(externalProvidersRawString) as List<dynamic>)
.map((item) => ExternalProvider.fromJson(item))
.toList();
return externalProviders;
});
}
Future<ExternalProvider?> getExternalProvider(
String externalProviderName,
) async {
final externalProvidersRawString = await _interface.getExternalProvider(
externalProviderName,
);
if (externalProvidersRawString.isEmpty) {
return null;
}
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
}
Future<String> updateGeoData(UpdateGeoDataParams params) {
return _interface.updateGeoData(params);
}
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
}) {
return _interface.sideLoadExternalProvider(
providerName: providerName,
data: data,
);
}
Future<String> updateExternalProvider({required String providerName}) async {
return _interface.updateExternalProvider(providerName);
}
Future<bool> startListener() async {
return await _interface.startListener();
}
Future<bool> stopListener() async {
return await _interface.stopListener();
}
Future<Delay> getDelay(String url, String proxyName) async {
final data = await _interface.asyncTestDelay(url, proxyName);
return Delay.fromJson(json.decode(data));
}
Future<Map<String, dynamic>> getConfig(String id) async {
final profilePath = await appPath.getProfilePath(id);
final res = await _interface.getConfig(profilePath);
if (res.isSuccess) {
return res.data;
} else {
throw res.message;
}
}
Future<Traffic> getTraffic(bool onlyStatisticsProxy) async {
final trafficString = await _interface.getTraffic(onlyStatisticsProxy);
if (trafficString.isEmpty) {
return Traffic();
}
return Traffic.fromJson(json.decode(trafficString));
}
Future<IpInfo?> getCountryCode(String ip) async {
final countryCode = await _interface.getCountryCode(ip);
if (countryCode.isEmpty) {
return null;
}
return IpInfo(ip: ip, countryCode: countryCode);
}
Future<Traffic> getTotalTraffic(bool onlyStatisticsProxy) async {
final totalTrafficString = await _interface.getTotalTraffic(
onlyStatisticsProxy,
);
if (totalTrafficString.isEmpty) {
return Traffic();
}
return Traffic.fromJson(json.decode(totalTrafficString));
}
Future<int> getMemory() async {
final value = await _interface.getMemory();
if (value.isEmpty) {
return 0;
}
return int.parse(value);
}
void resetTraffic() {
_interface.resetTraffic();
}
void startLog() {
_interface.startLog();
}
void stopLog() {
_interface.stopLog();
}
Future<void> requestGc() async {
await _interface.forceGc();
}
Future<void> destroy() async {
await _interface.destroy();
}
Future<void> crash() async {
await _interface.crash();
}
Future<String> deleteFile(String path) async {
return await _interface.deleteFile(path);
}
}
final coreController = CoreController();

View File

@@ -1,4 +1,5 @@
export 'controller.dart';
export 'core.dart';
export 'event.dart';
export 'lib.dart';
export 'message.dart';
export 'service.dart';

68
lib/core/event.dart Normal file
View File

@@ -0,0 +1,68 @@
import 'dart:async';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart';
abstract mixin class CoreEventListener {
void onLog(Log log) {}
void onDelay(Delay delay) {}
void onRequest(TrackerInfo connection) {}
void onLoaded(String providerName) {}
void onCrash(String message) {}
}
class CoreEventManager {
final _controller = StreamController<CoreEvent>();
CoreEventManager._() {
_controller.stream.listen((event) {
for (final CoreEventListener listener in _listeners) {
switch (event.type) {
case CoreEventType.log:
listener.onLog(Log.fromJson(event.data));
break;
case CoreEventType.delay:
listener.onDelay(Delay.fromJson(event.data));
break;
case CoreEventType.request:
listener.onRequest(TrackerInfo.fromJson(event.data));
break;
case CoreEventType.loaded:
listener.onLoaded(event.data);
break;
case CoreEventType.crash:
listener.onCrash(event.data);
break;
}
}
});
}
static final CoreEventManager instance = CoreEventManager._();
final ObserverList<CoreEventListener> _listeners =
ObserverList<CoreEventListener>();
bool get hasListeners {
return _listeners.isNotEmpty;
}
void sendEvent(CoreEvent event) {
_controller.add(event);
}
void addListener(CoreEventListener listener) {
_listeners.add(listener);
}
void removeListener(CoreEventListener listener) {
_listeners.remove(listener);
}
}
final coreEventManager = CoreEventManager.instance;

327
lib/core/interface.dart Normal file
View File

@@ -0,0 +1,327 @@
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
mixin CoreInterface {
Future<bool> init(InitParams params);
Future<String> preload();
Future<bool> shutdown();
Future<bool> get isInit;
Future<bool> forceGc();
Future<String> validateConfig(String data);
Future<Result> getConfig(String path);
Future<String> asyncTestDelay(String url, String proxyName);
Future<String> updateConfig(UpdateParams updateParams);
Future<String> setupConfig(SetupParams setupParams);
Future<Map> getProxies();
Future<String> changeProxy(ChangeProxyParams changeProxyParams);
Future<bool> startListener();
Future<bool> stopListener();
Future<String> getExternalProviders();
Future<String>? getExternalProvider(String externalProviderName);
Future<String> updateGeoData(UpdateGeoDataParams params);
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
});
Future<String> updateExternalProvider(String providerName);
FutureOr<String> getTraffic(bool onlyStatisticsProxy);
FutureOr<String> getTotalTraffic(bool onlyStatisticsProxy);
FutureOr<String> getCountryCode(String ip);
FutureOr<String> getMemory();
FutureOr<void> resetTraffic();
FutureOr<void> startLog();
FutureOr<void> stopLog();
Future<bool> crash();
FutureOr<String> getConnections();
FutureOr<bool> closeConnection(String id);
FutureOr<String> deleteFile(String path);
FutureOr<bool> closeConnections();
FutureOr<bool> resetConnections();
}
abstract class CoreHandlerInterface with CoreInterface {
Future get connected;
FutureOr<bool> destroy();
Future<T?> _invoke<T>({
required ActionMethod method,
dynamic data,
Duration? timeout,
}) async {
await connected;
return invoke(method: method, data: data, timeout: timeout);
}
Future<T?> invoke<T>({
required ActionMethod method,
dynamic data,
Duration? timeout,
});
Future<T> parasResult<T>(ActionResult result) async {
return switch (result.method) {
ActionMethod.getConfig => result.toResult as T,
_ => result.data as T,
};
}
@override
Future<bool> init(InitParams params) async {
return await _invoke<bool>(
method: ActionMethod.initClash,
data: json.encode(params),
) ??
false;
}
@override
Future<bool> shutdown();
@override
Future<bool> get isInit async {
return await _invoke<bool>(method: ActionMethod.getIsInit) ?? false;
}
@override
Future<bool> forceGc() async {
return await _invoke<bool>(method: ActionMethod.forceGc) ?? false;
}
@override
Future<String> validateConfig(String data) async {
return await _invoke<String>(
method: ActionMethod.validateConfig,
data: data,
) ??
'';
}
@override
Future<String> updateConfig(UpdateParams updateParams) async {
return await _invoke<String>(
method: ActionMethod.updateConfig,
data: json.encode(updateParams),
) ??
'';
}
@override
Future<Result> getConfig(String path) async {
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
Result<Map<String, dynamic>>.success({});
}
@override
Future<String> setupConfig(SetupParams setupParams) async {
final data = await Isolate.run(() => json.encode(setupParams));
return await _invoke<String>(
method: ActionMethod.setupConfig,
data: data,
) ??
'';
}
@override
Future<bool> crash() async {
return await _invoke<bool>(method: ActionMethod.crash) ?? false;
}
@override
Future<Map> getProxies() async {
final map = await _invoke<Map>(method: ActionMethod.getProxies);
return map ?? {};
}
@override
Future<String> changeProxy(ChangeProxyParams changeProxyParams) async {
return await _invoke<String>(
method: ActionMethod.changeProxy,
data: json.encode(changeProxyParams),
) ??
'';
}
@override
Future<String> getExternalProviders() async {
return await _invoke<String>(method: ActionMethod.getExternalProviders) ??
'';
}
@override
Future<String> getExternalProvider(String externalProviderName) async {
return await _invoke<String>(
method: ActionMethod.getExternalProvider,
data: externalProviderName,
) ??
'';
}
@override
Future<String> updateGeoData(UpdateGeoDataParams params) async {
return await _invoke<String>(
method: ActionMethod.updateGeoData,
data: json.encode(params),
) ??
'';
}
@override
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
}) async {
return await _invoke<String>(
method: ActionMethod.sideLoadExternalProvider,
data: json.encode({'providerName': providerName, 'data': data}),
) ??
'';
}
@override
Future<String> updateExternalProvider(String providerName) async {
return await _invoke<String>(
method: ActionMethod.updateExternalProvider,
data: providerName,
) ??
'';
}
@override
Future<String> getConnections() async {
return await _invoke<String>(method: ActionMethod.getConnections) ?? '';
}
@override
Future<bool> closeConnections() async {
return await _invoke<bool>(method: ActionMethod.closeConnections) ?? false;
}
@override
Future<bool> resetConnections() async {
return await _invoke<bool>(method: ActionMethod.resetConnections) ?? false;
}
@override
Future<bool> closeConnection(String id) async {
return await _invoke<bool>(
method: ActionMethod.closeConnection,
data: id,
) ??
false;
}
@override
Future<String> getTotalTraffic(bool onlyStatisticsProxy) async {
return await _invoke<String>(
method: ActionMethod.getTotalTraffic,
data: onlyStatisticsProxy,
) ??
'';
}
@override
Future<String> getTraffic(bool onlyStatisticsProxy) async {
return await _invoke<String>(
method: ActionMethod.getTraffic,
data: onlyStatisticsProxy,
) ??
'';
}
@override
Future<String> deleteFile(String path) async {
return await _invoke<String>(method: ActionMethod.deleteFile, data: path) ??
'';
}
@override
resetTraffic() {
_invoke(method: ActionMethod.resetTraffic);
}
@override
startLog() {
_invoke(method: ActionMethod.startLog);
}
@override
stopLog() {
_invoke<bool>(method: ActionMethod.stopLog);
}
@override
Future<bool> startListener() async {
return await _invoke<bool>(method: ActionMethod.startListener) ?? false;
}
@override
Future<bool> stopListener() async {
return await _invoke<bool>(method: ActionMethod.stopListener) ?? false;
}
@override
Future<String> asyncTestDelay(String url, String proxyName) async {
final delayParams = {
'proxy-name': proxyName,
'timeout': httpTimeoutDuration.inMilliseconds,
'test-url': url,
};
return await _invoke<String>(
method: ActionMethod.asyncTestDelay,
data: json.encode(delayParams),
timeout: Duration(seconds: 6),
) ??
json.encode(Delay(name: proxyName, value: -1, url: url));
}
@override
Future<String> getCountryCode(String ip) async {
return await _invoke<String>(
method: ActionMethod.getCountryCode,
data: ip,
) ??
'';
}
@override
Future<String> getMemory() async {
return await _invoke<String>(method: ActionMethod.getMemory) ?? '';
}
}

68
lib/core/lib.dart Normal file
View File

@@ -0,0 +1,68 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/state.dart';
import 'interface.dart';
class CoreLib extends CoreHandlerInterface {
static CoreLib? _instance;
Completer<bool> _connectedCompleter = Completer();
CoreLib._internal();
@override
Future<String> preload() async {
final res = await service?.init();
if (res?.isEmpty != true) {
return res ?? '';
}
_connectedCompleter.complete(true);
final syncRes = await service?.syncAndroidState(
globalState.getAndroidState(),
);
return syncRes ?? '';
}
factory CoreLib() {
_instance ??= CoreLib._internal();
return _instance!;
}
@override
destroy() async {
return true;
}
@override
Future<bool> shutdown() async {
await service?.shutdown();
_connectedCompleter = Completer();
return true;
}
@override
Future<T?> invoke<T>({
required ActionMethod method,
dynamic data,
Duration? timeout,
}) async {
final id = '${method.name}#${utils.id}';
final result = await service
?.invokeAction(Action(id: id, method: method, data: data))
.withTimeout(onTimeout: () => null);
if (result == null) {
return null;
}
return parasResult<T>(result);
}
@override
Future get connected => _connectedCompleter.future;
}
CoreLib? get coreLib => system.isAndroid ? CoreLib() : null;

188
lib/core/service.dart Normal file
View File

@@ -0,0 +1,188 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/core.dart';
import 'interface.dart';
class CoreService extends CoreHandlerInterface {
static CoreService? _instance;
final Completer<ServerSocket> _serverCompleter = Completer();
Completer<Socket> _socketCompleter = Completer();
final Map<String, Completer> _callbackCompleterMap = {};
Process? _process;
factory CoreService() {
_instance ??= CoreService._internal();
return _instance!;
}
CoreService._internal() {
_initServer();
}
Future<void> handleResult(ActionResult result) async {
final completer = _callbackCompleterMap[result.id];
final data = await parasResult(result);
if (result.id?.isEmpty == true) {
coreEventManager.sendEvent(CoreEvent.fromJson(result.data));
}
completer?.complete(data);
}
void _initServer() {
runZonedGuarded(
() async {
final address = !system.isWindows
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
: InternetAddress(localhost, type: InternetAddressType.IPv4);
await _deleteSocketFile();
final server = await ServerSocket.bind(address, 0, shared: true);
_serverCompleter.complete(server);
await for (final socket in server) {
await _attachSocket(socket);
}
},
(error, stack) async {
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
},
);
}
Future<void> _attachSocket(Socket socket) async {
await _destroySocket();
_socketCompleter.complete(socket);
socket
.transform(uint8ListToListIntConverter)
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((data) {
handleResult(ActionResult.fromJson(json.decode(data.trim())));
})
.onDone(() {
_handleInvokeCrashEvent();
});
}
void _handleInvokeCrashEvent() {
coreEventManager.sendEvent(
CoreEvent(type: CoreEventType.crash, data: 'socket done'),
);
}
Future<void> start() async {
if (_process != null) {
await shutdown();
}
final serverSocket = await _serverCompleter.future;
final arg = system.isWindows
? '${serverSocket.port}'
: serverSocket.address.address;
if (system.isWindows && await system.checkIsAdmin()) {
final isSuccess = await request.startCoreByHelper(arg);
if (isSuccess) {
return;
}
}
_process = await Process.start(appPath.corePath, [arg]);
_process?.stdout.listen((_) {});
_process?.stderr.listen((e) {
final error = utf8.decode(e);
if (error.isNotEmpty) {
commonPrint.log(error, logLevel: LogLevel.warning);
}
});
await _socketCompleter.future;
}
@override
destroy() async {
final server = await _serverCompleter.future;
await server.close();
await _deleteSocketFile();
return true;
}
Future<void> sendMessage(String message) async {
final socket = await _socketCompleter.future;
socket.writeln(message);
}
Future<void> _deleteSocketFile() async {
if (!system.isWindows) {
final file = File(unixSocketPath);
if (await file.exists()) {
await file.delete();
}
}
}
Future<void> _destroySocket() async {
if (_socketCompleter.isCompleted) {
final socket = await _socketCompleter.future;
_socketCompleter = Completer();
socket.close();
}
}
@override
shutdown() async {
await _destroySocket();
_clearCompleter();
if (system.isWindows) {
await request.stopCoreByHelper();
}
_process?.kill();
_process = null;
return true;
}
void _clearCompleter() {
for (final completer in _callbackCompleterMap.values) {
completer.safeCompleter(null);
}
}
@override
Future<String> preload() async {
await _serverCompleter.future;
await start();
return '';
}
@override
Future<T?> invoke<T>({
required ActionMethod method,
dynamic data,
Duration? timeout,
}) async {
final id = '${method.name}#${utils.id}';
_callbackCompleterMap[id] = Completer<T?>();
sendMessage(json.encode(Action(id: id, method: method, data: data)));
return (_callbackCompleterMap[id] as Completer<T?>).future.withTimeout(
timeout: timeout,
onLast: () {
final completer = _callbackCompleterMap[id];
completer?.safeCompleter(null);
_callbackCompleterMap.remove(id);
},
tag: id,
onTimeout: () => null,
);
}
@override
Future get connected {
return _socketCompleter.future;
}
}
final coreService = system.isDesktop ? CoreService() : null;

View File

@@ -2,6 +2,7 @@
import 'dart:io';
import 'package:fl_clash/common/color.dart';
import 'package:fl_clash/common/system.dart';
import 'package:fl_clash/views/dashboard/widgets/widgets.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -58,11 +59,8 @@ enum GroupType {
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
extension GroupTypeExtension on GroupType {
static List<String> get valueList => GroupType.values
.map(
(e) => e.toString().split('.').last,
)
.toList();
static List<String> get valueList =>
GroupType.values.map((e) => e.toString().split('.').last).toList();
bool get isComputedSelected {
return [GroupType.URLTest, GroupType.Fallback].contains(this);
@@ -80,11 +78,8 @@ extension GroupTypeExtension on GroupType {
enum UsedProxy { GLOBAL, DIRECT, REJECT }
extension UsedProxyExtension on UsedProxy {
static List<String> get valueList => UsedProxy.values
.map(
(e) => e.toString().split('.').last,
)
.toList();
static List<String> get valueList =>
UsedProxy.values.map((e) => e.toString().split('.').last).toList();
String get value => UsedProxyExtension.valueList[index];
}
@@ -93,13 +88,7 @@ enum Mode { rule, global, direct }
enum ViewMode { mobile, laptop, desktop }
enum LogLevel {
debug,
info,
warning,
error,
silent,
}
enum LogLevel { debug, info, warning, error, silent }
extension LogLevelExt on LogLevel {
Color? get color {
@@ -107,7 +96,7 @@ extension LogLevelExt on LogLevel {
LogLevel.silent => Colors.grey.shade700,
LogLevel.debug => Colors.grey.shade400,
LogLevel.info => null,
LogLevel.warning => Colors.yellowAccent,
LogLevel.warning => Colors.orangeAccent.darken(),
LogLevel.error => Colors.redAccent,
};
}
@@ -138,24 +127,13 @@ enum ResultType {
error,
}
enum AppMessageType {
log,
delay,
request,
loaded,
}
enum CoreEventType { log, delay, request, loaded, crash }
enum InvokeMessageType {
protect,
process,
}
enum InvokeMessageType { protect, process }
enum FindProcessMode { always, off }
enum RecoveryOption {
all,
onlyProfiles,
}
enum RecoveryOption { all, onlyProfiles }
enum ChipType { action, delete }
@@ -177,7 +155,7 @@ enum DnsMode {
fakeIp,
@JsonValue('redir-host')
redirHost,
hosts
hosts,
}
enum ExternalControllerStatus {
@@ -192,28 +170,12 @@ enum ExternalControllerStatus {
}
enum KeyboardModifier {
alt([
PhysicalKeyboardKey.altLeft,
PhysicalKeyboardKey.altRight,
]),
capsLock([
PhysicalKeyboardKey.capsLock,
]),
control([
PhysicalKeyboardKey.controlLeft,
PhysicalKeyboardKey.controlRight,
]),
fn([
PhysicalKeyboardKey.fn,
]),
meta([
PhysicalKeyboardKey.metaLeft,
PhysicalKeyboardKey.metaRight,
]),
shift([
PhysicalKeyboardKey.shiftLeft,
PhysicalKeyboardKey.shiftRight,
]);
alt([PhysicalKeyboardKey.altLeft, PhysicalKeyboardKey.altRight]),
capsLock([PhysicalKeyboardKey.capsLock]),
control([PhysicalKeyboardKey.controlLeft, PhysicalKeyboardKey.controlRight]),
fn([PhysicalKeyboardKey.fn]),
meta([PhysicalKeyboardKey.metaLeft, PhysicalKeyboardKey.metaRight]),
shift([PhysicalKeyboardKey.shiftLeft, PhysicalKeyboardKey.shiftRight]);
final List<PhysicalKeyboardKey> physicalKeys;
@@ -233,19 +195,9 @@ extension KeyboardModifierExt on KeyboardModifier {
}
}
enum HotAction {
start,
view,
mode,
proxy,
tun,
}
enum HotAction { start, view, mode, proxy, tun }
enum ProxiesIconStyle {
standard,
none,
icon,
}
enum ProxiesIconStyle { standard, none, icon }
enum FontFamily {
twEmoji('Twemoji'),
@@ -257,10 +209,7 @@ enum FontFamily {
const FontFamily(this.value);
}
enum RouteMode {
bypassPrivate,
config,
}
enum RouteMode { bypassPrivate, config }
enum ActionMethod {
message,
@@ -294,6 +243,7 @@ enum ActionMethod {
getMemory,
crash,
setupConfig,
deleteFile,
///Android,
setState,
@@ -307,11 +257,7 @@ enum ActionMethod {
enum AuthorizeCode { none, success, error }
enum WindowsHelperServiceStatus {
none,
presence,
running,
}
enum WindowsHelperServiceStatus { none, presence, running }
enum FunctionTag {
updateClashConfig,
@@ -337,79 +283,30 @@ enum FunctionTag {
}
enum DashboardWidget {
networkSpeed(
GridItem(
crossAxisCellCount: 8,
child: NetworkSpeed(),
),
),
outboundModeV2(
GridItem(
crossAxisCellCount: 8,
child: OutboundModeV2(),
),
),
outboundMode(
GridItem(
crossAxisCellCount: 4,
child: OutboundMode(),
),
),
trafficUsage(
GridItem(
crossAxisCellCount: 4,
child: TrafficUsage(),
),
),
networkDetection(
GridItem(
crossAxisCellCount: 4,
child: NetworkDetection(),
),
),
networkSpeed(GridItem(crossAxisCellCount: 8, child: NetworkSpeed())),
outboundModeV2(GridItem(crossAxisCellCount: 8, child: OutboundModeV2())),
outboundMode(GridItem(crossAxisCellCount: 4, child: OutboundMode())),
trafficUsage(GridItem(crossAxisCellCount: 4, child: TrafficUsage())),
networkDetection(GridItem(crossAxisCellCount: 4, child: NetworkDetection())),
tunButton(
GridItem(
crossAxisCellCount: 4,
child: TUNButton(),
),
GridItem(crossAxisCellCount: 4, child: TUNButton()),
platforms: desktopPlatforms,
),
vpnButton(
GridItem(
crossAxisCellCount: 4,
child: VpnButton(),
),
platforms: [
SupportPlatform.Android,
],
GridItem(crossAxisCellCount: 4, child: VpnButton()),
platforms: [SupportPlatform.Android],
),
systemProxyButton(
GridItem(
crossAxisCellCount: 4,
child: SystemProxyButton(),
),
GridItem(crossAxisCellCount: 4, child: SystemProxyButton()),
platforms: desktopPlatforms,
),
intranetIp(
GridItem(
crossAxisCellCount: 4,
child: IntranetIP(),
),
),
memoryInfo(
GridItem(
crossAxisCellCount: 4,
child: MemoryInfo(),
),
);
intranetIp(GridItem(crossAxisCellCount: 4, child: IntranetIP())),
memoryInfo(GridItem(crossAxisCellCount: 4, child: MemoryInfo()));
final GridItem widget;
final List<SupportPlatform> platforms;
const DashboardWidget(
this.widget, {
this.platforms = SupportPlatform.values,
});
const DashboardWidget(this.widget, {this.platforms = SupportPlatform.values});
static DashboardWidget getDashboardWidget(GridItem gridItem) {
final dashboardWidgets = DashboardWidget.values;
@@ -420,10 +317,7 @@ enum DashboardWidget {
}
}
enum GeodataLoader {
standard,
memconservative,
}
enum GeodataLoader { standard, memconservative }
enum PageLabel {
dashboard,
@@ -478,51 +372,30 @@ enum RuleAction {
extension RuleActionExt on RuleAction {
bool get hasParams => [
RuleAction.GEOIP,
RuleAction.IP_ASN,
RuleAction.SRC_IP_ASN,
RuleAction.IP_CIDR,
RuleAction.IP_CIDR6,
RuleAction.IP_SUFFIX,
RuleAction.RULE_SET,
].contains(this);
RuleAction.GEOIP,
RuleAction.IP_ASN,
RuleAction.SRC_IP_ASN,
RuleAction.IP_CIDR,
RuleAction.IP_CIDR6,
RuleAction.IP_SUFFIX,
RuleAction.RULE_SET,
].contains(this);
}
enum OverrideRuleType {
override,
added,
}
enum OverrideRuleType { override, added }
enum RuleTarget {
DIRECT,
REJECT,
}
enum RuleTarget { DIRECT, REJECT }
enum RecoveryStrategy {
compatible,
override,
}
enum RecoveryStrategy { compatible, override }
enum CacheTag {
logs,
rules,
requests,
proxiesList,
}
enum CacheTag { logs, rules, requests, proxiesList }
enum Language {
yaml,
javaScript,
}
enum Language { yaml, javaScript }
enum ImportOption {
file,
url,
}
enum ImportOption { file, url }
enum ScrollPositionCacheKeys {
tools,
profiles,
proxiesList,
proxiesTabList,
}
enum ScrollPositionCacheKey { tools, profiles, proxiesList, proxiesTabList }
enum QueryTag { proxies }
enum CoreStatus { connecting, connected, disconnected }

View File

@@ -177,6 +177,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Opening it will lose part of its application ability and gain the support of full amount of Clash.",
),
"confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
"connected": MessageLookupByLibrary.simpleMessage("Connected"),
"connecting": MessageLookupByLibrary.simpleMessage("Connecting..."),
"connection": MessageLookupByLibrary.simpleMessage("Connection"),
"connections": MessageLookupByLibrary.simpleMessage("Connections"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage(
@@ -194,13 +196,24 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"),
"core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Core status"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
"crashlytics": MessageLookupByLibrary.simpleMessage("Crash Analysis"),
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
"When enabled, automatically uploads crash logs without sensitive information when the app crashes",
),
"create": MessageLookupByLibrary.simpleMessage("Create"),
"creationTime": MessageLookupByLibrary.simpleMessage("Creation time"),
"cut": MessageLookupByLibrary.simpleMessage("Cut"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
"Data Collection Notice",
),
"days": MessageLookupByLibrary.simpleMessage("Days"),
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
"Default nameserver",
@@ -238,6 +251,7 @@ class MessageLookup extends MessageLookupByLibrary {
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.",
),
"disconnected": MessageLookupByLibrary.simpleMessage("Disconnected"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
"Discover the new version",
),
@@ -247,6 +261,7 @@ class MessageLookup extends MessageLookupByLibrary {
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Update DNS related settings",
),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS hijacking"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
"Do you want to pass",
@@ -301,6 +316,9 @@ class MessageLookup extends MessageLookupByLibrary {
"There is a certain performance loss after opening",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"),
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to force restart the core?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("FruitSalad"),
"general": MessageLookupByLibrary.simpleMessage("General"),
@@ -513,6 +531,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Please press the keyboard.",
),
"preview": MessageLookupByLibrary.simpleMessage("Preview"),
"process": MessageLookupByLibrary.simpleMessage("Process"),
"profile": MessageLookupByLibrary.simpleMessage("Profile"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage(
@@ -539,7 +558,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
"profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"),
"progress": MessageLookupByLibrary.simpleMessage("Progress"),
"project": MessageLookupByLibrary.simpleMessage("Project"),
"providers": MessageLookupByLibrary.simpleMessage("Providers"),
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
@@ -606,6 +624,9 @@ class MessageLookup extends MessageLookupByLibrary {
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS connection following rules, need to configure proxy-server-nameserver",
),
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to restart the core?",
),
"routeAddress": MessageLookupByLibrary.simpleMessage("Route address"),
"routeAddressDesc": MessageLookupByLibrary.simpleMessage(
"Config listen route address",

View File

@@ -133,6 +133,8 @@ class MessageLookup extends MessageLookupByLibrary {
"有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
),
"confirm": MessageLookupByLibrary.simpleMessage("確認"),
"connected": MessageLookupByLibrary.simpleMessage("接続済み"),
"connecting": MessageLookupByLibrary.simpleMessage("接続中..."),
"connection": MessageLookupByLibrary.simpleMessage("接続"),
"connections": MessageLookupByLibrary.simpleMessage("接続"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
@@ -146,13 +148,22 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"),
"core": MessageLookupByLibrary.simpleMessage("コア"),
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
"coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"),
"country": MessageLookupByLibrary.simpleMessage(""),
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
"crashlytics": MessageLookupByLibrary.simpleMessage("クラッシュ分析"),
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
"有効にすると、アプリがクラッシュした際に機密情報を含まないクラッシュログを自動的にアップロードします",
),
"create": MessageLookupByLibrary.simpleMessage("作成"),
"creationTime": MessageLookupByLibrary.simpleMessage("作成時間"),
"cut": MessageLookupByLibrary.simpleMessage("切り取り"),
"dark": MessageLookupByLibrary.simpleMessage("ダーク"),
"dashboard": MessageLookupByLibrary.simpleMessage("ダッシュボード"),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"本アプリはFirebase Crashlyticsを使用してクラッシュ情報を収集し、アプリの安定性を向上させます。\n収集されるデータにはデバイス情報とクラッシュ詳細が含まれますが、個人の機密データは含まれません。\n設定でこの機能を無効にすることができます。",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("データ収集説明"),
"days": MessageLookupByLibrary.simpleMessage(""),
"defaultNameserver": MessageLookupByLibrary.simpleMessage("デフォルトネームサーバー"),
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
@@ -182,9 +193,11 @@ class MessageLookup extends MessageLookupByLibrary {
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"本ソフトウェアは学習交流や科学研究などの非営利目的でのみ使用されます。商用利用は厳禁です。いかなる商用活動も本ソフトウェアとは無関係です。",
),
"disconnected": MessageLookupByLibrary.simpleMessage("切断済み"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("新バージョンを発見"),
"discovery": MessageLookupByLibrary.simpleMessage("新しいバージョンを発見"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("DNS関連設定の更新"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNSハイジャッキング"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNSモード"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("通過させますか?"),
"domain": MessageLookupByLibrary.simpleMessage("ドメイン"),
@@ -229,6 +242,9 @@ class MessageLookup extends MessageLookupByLibrary {
"有効化するとパフォーマンスが若干低下します",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("フォントファミリー"),
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"コアを強制再起動してもよろしいですか?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("4列"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("フルーツサラダ"),
"general": MessageLookupByLibrary.simpleMessage("一般"),
@@ -387,6 +403,7 @@ class MessageLookup extends MessageLookupByLibrary {
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
"preview": MessageLookupByLibrary.simpleMessage("プレビュー"),
"process": MessageLookupByLibrary.simpleMessage("プロセス"),
"profile": MessageLookupByLibrary.simpleMessage("プロファイル"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage("有効な間隔形式を入力してください"),
@@ -409,7 +426,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("プロファイル一覧"),
"profilesSort": MessageLookupByLibrary.simpleMessage("プロファイルの並び替え"),
"progress": MessageLookupByLibrary.simpleMessage("進捗"),
"project": MessageLookupByLibrary.simpleMessage("プロジェクト"),
"providers": MessageLookupByLibrary.simpleMessage("プロバイダー"),
"proxies": MessageLookupByLibrary.simpleMessage("プロキシ"),
@@ -460,6 +476,7 @@ class MessageLookup extends MessageLookupByLibrary {
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS接続がルールに従うproxy-server-nameserverの設定が必要",
),
"restartCoreTip": MessageLookupByLibrary.simpleMessage("コアを再起動してもよろしいですか?"),
"routeAddress": MessageLookupByLibrary.simpleMessage("ルートアドレス"),
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("ルートアドレスを設定"),
"routeMode": MessageLookupByLibrary.simpleMessage("ルートモード"),

View File

@@ -182,6 +182,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
),
"confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"),
"connected": MessageLookupByLibrary.simpleMessage("Подключено"),
"connecting": MessageLookupByLibrary.simpleMessage("Подключение..."),
"connection": MessageLookupByLibrary.simpleMessage("Соединение"),
"connections": MessageLookupByLibrary.simpleMessage("Соединения"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage(
@@ -199,13 +201,24 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"),
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
"crashlytics": MessageLookupByLibrary.simpleMessage("Анализ сбоев"),
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
"При включении автоматически загружает журналы сбоев без конфиденциальной информации, когда приложение выходит из строя",
),
"create": MessageLookupByLibrary.simpleMessage("Создать"),
"creationTime": MessageLookupByLibrary.simpleMessage("Время создания"),
"cut": MessageLookupByLibrary.simpleMessage("Вырезать"),
"dark": MessageLookupByLibrary.simpleMessage("Темный"),
"dashboard": MessageLookupByLibrary.simpleMessage("Панель управления"),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"Это приложение использует Firebase Crashlytics для сбора информации о сбоях nhằm улучшения стабильности приложения.\nСобираемые данные включают информацию об устройстве и подробности о сбоях, но не содержат персональных конфиденциальных данных.\nВы можете отключить эту функцию в настройках.",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
"Уведомление о сборе данных",
),
"days": MessageLookupByLibrary.simpleMessage("Дней"),
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
"Сервер имен по умолчанию",
@@ -245,6 +258,7 @@ class MessageLookup extends MessageLookupByLibrary {
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"Это программное обеспечение используется только в некоммерческих целях, таких как учебные обмены и научные исследования. Запрещено использовать это программное обеспечение в коммерческих целях. Любая коммерческая деятельность, если таковая имеется, не имеет отношения к этому программному обеспечению.",
),
"disconnected": MessageLookupByLibrary.simpleMessage("Отключено"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
"Обнаружена новая версия",
),
@@ -254,6 +268,7 @@ class MessageLookup extends MessageLookupByLibrary {
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Обновление настроек, связанных с DNS",
),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS-перехват"),
"dnsMode": MessageLookupByLibrary.simpleMessage("Режим DNS"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
"Вы хотите пропустить",
@@ -316,6 +331,9 @@ class MessageLookup extends MessageLookupByLibrary {
"При включении возможны небольшие потери производительности",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("Семейство шрифтов"),
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите принудительно перезапустить ядро?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("Фруктовый микс"),
"general": MessageLookupByLibrary.simpleMessage("Общие"),
@@ -540,6 +558,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Пожалуйста, нажмите клавишу.",
),
"preview": MessageLookupByLibrary.simpleMessage("Предпросмотр"),
"process": MessageLookupByLibrary.simpleMessage("процесс"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage(
@@ -566,7 +585,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("Профили"),
"profilesSort": MessageLookupByLibrary.simpleMessage("Сортировка профилей"),
"progress": MessageLookupByLibrary.simpleMessage("Прогресс"),
"project": MessageLookupByLibrary.simpleMessage("Проект"),
"providers": MessageLookupByLibrary.simpleMessage("Провайдеры"),
"proxies": MessageLookupByLibrary.simpleMessage("Прокси"),
@@ -641,6 +659,9 @@ class MessageLookup extends MessageLookupByLibrary {
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS-соединение следует правилам, необходимо настроить proxy-server-nameserver",
),
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите перезапустить ядро?",
),
"routeAddress": MessageLookupByLibrary.simpleMessage("Адрес маршрутизации"),
"routeAddressDesc": MessageLookupByLibrary.simpleMessage(
"Настройка адреса прослушивания маршрутизации",

View File

@@ -123,6 +123,8 @@ class MessageLookup extends MessageLookupByLibrary {
"开启将失去部分应用能力获得全量的Clash的支持",
),
"confirm": MessageLookupByLibrary.simpleMessage("确定"),
"connected": MessageLookupByLibrary.simpleMessage("已连接"),
"connecting": MessageLookupByLibrary.simpleMessage("连接中..."),
"connection": MessageLookupByLibrary.simpleMessage("连接"),
"connections": MessageLookupByLibrary.simpleMessage("连接"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
@@ -136,13 +138,22 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"),
"core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
"crashlytics": MessageLookupByLibrary.simpleMessage("崩溃分析"),
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
"开启后,应用崩溃时自动上传不包含敏感信息的崩溃日志",
),
"create": MessageLookupByLibrary.simpleMessage("创建"),
"creationTime": MessageLookupByLibrary.simpleMessage("创建时间"),
"cut": MessageLookupByLibrary.simpleMessage("剪切"),
"dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"本应用使用 Firebase Crashlytics 收集崩溃信息以改进应用稳定性。\n收集的数据包括设备信息和崩溃详情,不包含个人敏感数据。\n您可以在设置中关闭此功能。",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("数据收集说明"),
"days": MessageLookupByLibrary.simpleMessage(""),
"defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"),
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"),
@@ -168,9 +179,11 @@ class MessageLookup extends MessageLookupByLibrary {
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。",
),
"disconnected": MessageLookupByLibrary.simpleMessage("已断开"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS劫持"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"),
"domain": MessageLookupByLibrary.simpleMessage("域名"),
@@ -209,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary {
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"),
"fontFamily": MessageLookupByLibrary.simpleMessage("字体"),
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要强制重启核心吗?"),
"fourColumns": MessageLookupByLibrary.simpleMessage("四列"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("果缤纷"),
"general": MessageLookupByLibrary.simpleMessage("常规"),
@@ -341,6 +355,7 @@ class MessageLookup extends MessageLookupByLibrary {
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
"preview": MessageLookupByLibrary.simpleMessage("预览"),
"process": MessageLookupByLibrary.simpleMessage("进程"),
"profile": MessageLookupByLibrary.simpleMessage("配置"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage("请输入有效间隔时间格式"),
@@ -361,7 +376,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("配置"),
"profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"),
"progress": MessageLookupByLibrary.simpleMessage("进度"),
"project": MessageLookupByLibrary.simpleMessage("项目"),
"providers": MessageLookupByLibrary.simpleMessage("提供者"),
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
@@ -404,6 +418,7 @@ class MessageLookup extends MessageLookupByLibrary {
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS连接跟随rules,需配置proxy-server-nameserver",
),
"restartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要重启核心吗?"),
"routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"),
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"),
"routeMode": MessageLookupByLibrary.simpleMessage("路由模式"),

View File

@@ -28,10 +28,9 @@ class AppLocalizations {
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future<AppLocalizations> load(Locale locale) {
final name =
(locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final name = (locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
@@ -3160,9 +3159,9 @@ class AppLocalizations {
);
}
/// `Progress`
String get progress {
return Intl.message('Progress', name: 'progress', desc: '', args: []);
/// `Process`
String get process {
return Intl.message('Process', name: 'process', desc: '', args: []);
}
/// `Host`
@@ -3259,6 +3258,106 @@ class AppLocalizations {
String get request {
return Intl.message('Request', name: 'request', desc: '', args: []);
}
/// `Connected`
String get connected {
return Intl.message('Connected', name: 'connected', desc: '', args: []);
}
/// `Disconnected`
String get disconnected {
return Intl.message(
'Disconnected',
name: 'disconnected',
desc: '',
args: [],
);
}
/// `Connecting...`
String get connecting {
return Intl.message(
'Connecting...',
name: 'connecting',
desc: '',
args: [],
);
}
/// `Are you sure you want to restart the core?`
String get restartCoreTip {
return Intl.message(
'Are you sure you want to restart the core?',
name: 'restartCoreTip',
desc: '',
args: [],
);
}
/// `Are you sure you want to force restart the core?`
String get forceRestartCoreTip {
return Intl.message(
'Are you sure you want to force restart the core?',
name: 'forceRestartCoreTip',
desc: '',
args: [],
);
}
/// `DNS hijacking`
String get dnsHijacking {
return Intl.message(
'DNS hijacking',
name: 'dnsHijacking',
desc: '',
args: [],
);
}
/// `Core status`
String get coreStatus {
return Intl.message('Core status', name: 'coreStatus', desc: '', args: []);
}
/// `Data Collection Notice`
String get dataCollectionTip {
return Intl.message(
'Data Collection Notice',
name: 'dataCollectionTip',
desc: '',
args: [],
);
}
/// `This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.`
String get dataCollectionContent {
return Intl.message(
'This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.',
name: 'dataCollectionContent',
desc: '',
args: [],
);
}
/// `Crash Analysis`
String get crashlytics {
return Intl.message(
'Crash Analysis',
name: 'crashlytics',
desc: '',
args: [],
);
}
/// `When enabled, automatically uploads crash logs without sensitive information when the app crashes`
String get crashlyticsTip {
return Intl.message(
'When enabled, automatically uploads crash logs without sensitive information when the app crashes',
name: 'crashlyticsTip',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -1,128 +1,47 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/plugins/tile.dart';
import 'package:fl_clash/plugins/vpn.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'application.dart';
import 'clash/core.dart';
import 'clash/lib.dart';
import 'common/common.dart';
import 'models/models.dart';
import 'core/controller.dart';
Future<void> main() async {
globalState.isService = false;
WidgetsFlutterBinding.ensureInitialized();
final version = await system.version;
await clashCore.preload();
await globalState.initApp(version);
await android?.init();
await window?.init(version);
HttpOverrides.global = FlClashHttpOverrides();
runApp(ProviderScope(
child: const Application(),
));
runApp(ProviderScope(child: const Application()));
}
@pragma('vm:entry-point')
Future<void> _service(List<String> flags) async {
globalState.isService = true;
WidgetsFlutterBinding.ensureInitialized();
final quickStart = flags.contains('quick');
final clashLibHandler = ClashLibHandler();
globalState.isService = true;
await globalState.init();
await coreController.preload();
tile?.addListener(
_TileListenerWithService(
onStop: () async {
await app?.tip(appLocalizations.stopVpn);
clashLibHandler.stopListener();
await vpn?.stop();
exit(0);
await globalState.handleStop();
},
),
);
vpn?.handleGetStartForegroundParams = () {
final traffic = clashLibHandler.getTraffic();
return json.encode({
'title': clashLibHandler.getCurrentProfileName(),
'content': '$traffic'
});
};
vpn?.addListener(
_VpnListenerWithService(
onDnsChanged: (String dns) {
clashLibHandler.updateDns(dns);
},
),
);
if (!quickStart) {
_handleMainIpc(clashLibHandler);
} else {
commonPrint.log('quick start');
await ClashCore.initGeo();
Future(() async {
app?.tip(appLocalizations.startVpn);
final homeDirPath = await appPath.homeDirPath;
final version = await system.version;
await coreController.init(version);
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
enable: false,
);
Future(() async {
final profileId = globalState.config.currentProfileId;
if (profileId == null) {
return;
}
final params = await globalState.getSetupParams(
pathConfig: clashConfig,
);
final res = await clashLibHandler.quickStart(
InitParams(
homeDir: homeDirPath,
version: version,
),
params,
globalState.getCoreState(),
);
debugPrint(res);
if (res.isNotEmpty) {
await vpn?.stop();
exit(0);
}
await vpn?.start(
clashLibHandler.getAndroidVpnOptions(),
);
clashLibHandler.startListener();
});
}
}
void _handleMainIpc(ClashLibHandler clashLibHandler) {
final sendPort = IsolateNameServer.lookupPortByName(mainIsolate);
if (sendPort == null) {
return;
}
final serviceReceiverPort = ReceivePort();
serviceReceiverPort.listen((message) async {
final res = await clashLibHandler.invokeAction(message);
sendPort.send(res);
});
sendPort.send(serviceReceiverPort.sendPort);
final messageReceiverPort = ReceivePort();
clashLibHandler.attachMessagePort(
messageReceiverPort.sendPort.nativePort,
);
messageReceiverPort.listen((message) {
sendPort.send(message);
await globalState.handleStart();
await coreController.setupConfig(clashConfig);
});
}
@@ -130,27 +49,11 @@ void _handleMainIpc(ClashLibHandler clashLibHandler) {
class _TileListenerWithService with TileListener {
final Function() _onStop;
const _TileListenerWithService({
required Function() onStop,
}) : _onStop = onStop;
const _TileListenerWithService({required Function() onStop})
: _onStop = onStop;
@override
void onStop() {
_onStop();
}
}
@immutable
class _VpnListenerWithService with VpnListener {
final Function(String dns) _onDnsChanged;
const _VpnListenerWithService({
required Function(String dns) onDnsChanged,
}) : _onDnsChanged = onDnsChanged;
@override
void onDnsChanged(String dns) {
super.onDnsChanged(dns);
_onDnsChanged(dns);
}
}

View File

@@ -1,4 +1,8 @@
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -6,23 +10,49 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class AndroidManager extends ConsumerStatefulWidget {
final Widget child;
const AndroidManager({
super.key,
required this.child,
});
const AndroidManager({super.key, required this.child});
@override
ConsumerState<AndroidManager> createState() => _AndroidContainerState();
}
class _AndroidContainerState extends ConsumerState<AndroidManager> {
class _AndroidContainerState extends ConsumerState<AndroidManager>
with ServiceListener {
@override
void initState() {
super.initState();
ref.listenManual(appSettingProvider.select((state) => state.hidden),
(prev, next) {
ref.listenManual(appSettingProvider.select((state) => state.hidden), (
prev,
next,
) {
app?.updateExcludeFromRecents(next);
}, fireImmediately: true);
ref.listenManual(androidStateProvider, (prev, next) {
if (prev != next) {
service?.syncAndroidState(next);
}
});
service?.addListener(this);
}
@override
Future<void> dispose() async {
service?.removeListener(this);
super.dispose();
}
@override
void onServiceEvent(CoreEvent event) {
coreEventManager.sendEvent(event);
super.onServiceEvent(event);
}
@override
void onServiceCrash(String message) {
coreEventManager.sendEvent(
CoreEvent(type: CoreEventType.crash, data: message),
);
super.onServiceCrash(message);
}
@override

View File

@@ -7,17 +7,13 @@ import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/widgets/transparent_macos_sidebar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
class AppStateManager extends ConsumerStatefulWidget {
final Widget child;
const AppStateManager({
super.key,
required this.child,
});
const AppStateManager({super.key, required this.child});
@override
ConsumerState<AppStateManager> createState() => _AppStateManagerState();
@@ -36,46 +32,34 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
}
});
});
ref.listenManual(
checkIpProvider,
(prev, next) {
if (prev != next && next.b) {
detectionState.startCheck();
}
},
fireImmediately: true,
);
ref.listenManual(checkIpProvider, (prev, next) {
if (prev != next && next.b) {
detectionState.startCheck();
}
}, fireImmediately: true);
ref.listenManual(configStateProvider, (prev, next) {
if (prev != next) {
globalState.appController.savePreferencesDebounce();
}
});
ref.listenManual(needUpdateGroupsProvider, (prev, next) {
if (prev != next) {
globalState.appController.updateGroupsDebounce();
}
});
if (window == null) {
return;
}
ref.listenManual(
autoSetSystemDnsStateProvider,
(prev, next) async {
if (prev == next) {
return;
}
if (next.a == true && next.b == true) {
macOS?.updateDns(false);
} else {
macOS?.updateDns(true);
}
},
);
ref.listenManual(
currentBrightnessProvider,
(prev, next) {
if (prev == next) {
return;
}
window?.updateMacOSBrightness(next);
},
fireImmediately: true,
);
ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async {
if (prev == next) {
return;
}
if (next.a == true && next.b == true) {
macOS?.updateDns(false);
} else {
macOS?.updateDns(true);
}
});
}
@override
@@ -119,10 +103,7 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
class AppEnvManager extends StatelessWidget {
final Widget child;
const AppEnvManager({
super.key,
required this.child,
});
const AppEnvManager({super.key, required this.child});
@override
Widget build(BuildContext context) {
@@ -149,14 +130,11 @@ class AppEnvManager extends StatelessWidget {
class AppSidebarContainer extends ConsumerWidget {
final Widget child;
const AppSidebarContainer({
super.key,
required this.child,
});
const AppSidebarContainer({super.key, required this.child});
Widget _buildLoading() {
return Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final loading = ref.watch(loadingProvider);
final isMobileView = ref.watch(isMobileViewProvider);
return loading && !isMobileView
@@ -173,18 +151,25 @@ class AppSidebarContainer extends ConsumerWidget {
required BuildContext context,
required Widget child,
}) {
if (!system.isMacOS) {
return Material(
color: context.colorScheme.surfaceContainer,
child: child,
);
}
return TransparentMacOSSidebar(
child: Material(
color: Colors.transparent,
child: child,
),
);
return Material(color: context.colorScheme.surfaceContainer, child: child);
// if (!system.isMacOS) {
// return Material(
// color: context.colorScheme.surfaceContainer,
// child: child,
// );
// }
// return child;
// return TransparentMacOSSidebar(
// child: Material(color: Colors.transparent, child: child),
// );
}
void _updateSideBarWidth(WidgetRef ref, double contentWidth) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(sideWidthProvider.notifier).value =
ref.read(viewSizeProvider.select((state) => state.width)) -
contentWidth;
});
}
@override
@@ -199,92 +184,102 @@ class AppSidebarContainer extends ConsumerWidget {
final showLabel = ref.watch(appSettingProvider).showLabel;
return Row(
children: [
Stack(
alignment: Alignment.topRight,
children: [
_buildBackground(
context: context,
child: Column(
children: [
SizedBox(
height: 32,
),
if (!system.isMacOS) ...[
AppIcon(),
SizedBox(
height: 12,
),
],
Expanded(
child: ScrollConfiguration(
behavior: HiddenBarScrollBehavior(),
child: SingleChildScrollView(
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: Colors.transparent,
selectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
unselectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label.name),
_buildBackground(
context: context,
child: SafeArea(
child: Stack(
alignment: Alignment.topRight,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (system.isMacOS) SizedBox(height: 22),
SizedBox(height: 10),
if (!system.isMacOS) ...[
ClipRect(child: AppIcon()),
SizedBox(height: 12),
],
Expanded(
child: ScrollConfiguration(
behavior: HiddenBarScrollBehavior(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: NavigationRail(
scrollable: true,
minExtendedWidth: 200,
backgroundColor: Colors.transparent,
selectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(
color: context.colorScheme.onSurface,
),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController
.toPage(navigationItems[index].label);
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
),
unselectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(
color: context.colorScheme.onSurface,
),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(Intl.message(e.label.name)),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
),
),
],
),
),
),
),
const SizedBox(
height: 16,
),
IconButton(
onPressed: () {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
showLabel: !state.showLabel,
),
);
},
icon: Icon(
Icons.menu,
color: context.colorScheme.onSurfaceVariant,
const SizedBox(height: 16),
IconButton(
onPressed: () {
ref
.read(appSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith(showLabel: !state.showLabel),
);
},
icon: Icon(
Icons.menu,
color: context.colorScheme.onSurfaceVariant,
),
),
),
const SizedBox(
height: 16,
),
],
),
const SizedBox(height: 16),
],
),
_buildLoading(),
],
),
_buildLoading(),
],
),
),
Expanded(
flex: 1,
child: ClipRect(
child: child,
child: LayoutBuilder(
builder: (_, constraints) {
_updateSideBarWidth(ref, constraints.maxWidth);
return child;
},
),
),
)
),
],
);
}

View File

@@ -1,5 +1,5 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/app.dart';
@@ -9,20 +9,17 @@ import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ClashManager extends ConsumerStatefulWidget {
class CoreManager extends ConsumerStatefulWidget {
final Widget child;
const ClashManager({
super.key,
required this.child,
});
const CoreManager({super.key, required this.child});
@override
ConsumerState<ClashManager> createState() => _ClashContainerState();
ConsumerState<CoreManager> createState() => _CoreContainerState();
}
class _ClashContainerState extends ConsumerState<ClashManager>
with AppMessageListener {
class _CoreContainerState extends ConsumerState<CoreManager>
with CoreEventListener {
@override
Widget build(BuildContext context) {
return widget.child;
@@ -31,38 +28,32 @@ class _ClashContainerState extends ConsumerState<ClashManager>
@override
void initState() {
super.initState();
clashMessage.addListener(this);
coreEventManager.addListener(this);
ref.listenManual(needSetupProvider, (prev, next) {
if (prev != next) {
globalState.appController.handleChangeProfile();
}
});
ref.listenManual(coreStateProvider, (prev, next) async {
if (prev != next) {
await clashCore.setState(next);
}
});
ref.listenManual(updateParamsProvider, (prev, next) {
if (prev != next) {
globalState.appController.updateClashConfigDebounce();
}
});
ref.listenManual(
appSettingProvider.select((state) => state.openLogs),
(prev, next) {
if (next) {
clashCore.startLog();
} else {
clashCore.stopLog();
}
},
);
ref.listenManual(appSettingProvider.select((state) => state.openLogs), (
prev,
next,
) {
if (next) {
coreController.startLog();
} else {
coreController.stopLog();
}
}, fireImmediately: true);
}
@override
Future<void> dispose() async {
clashMessage.removeListener(this);
coreEventManager.removeListener(this);
super.dispose();
}
@@ -71,13 +62,9 @@ class _ClashContainerState extends ConsumerState<ClashManager>
super.onDelay(delay);
final appController = globalState.appController;
appController.setDelay(delay);
debouncer.call(
FunctionTag.updateDelay,
() async {
appController.updateGroupsDebounce();
},
duration: const Duration(milliseconds: 5000),
);
debouncer.call(FunctionTag.updateDelay, () async {
appController.updateGroupsDebounce();
}, duration: const Duration(milliseconds: 5000));
}
@override
@@ -97,12 +84,24 @@ class _ClashContainerState extends ConsumerState<ClashManager>
@override
Future<void> onLoaded(String providerName) async {
ref.read(providersProvider.notifier).setProvider(
await clashCore.getExternalProvider(
providerName,
),
);
ref
.read(providersProvider.notifier)
.setProvider(await coreController.getExternalProvider(providerName));
globalState.appController.updateGroupsDebounce();
super.onLoaded(providerName);
}
@override
Future<void> onCrash(String message) async {
if (!globalState.isUserDisconnected) {
context.showNotifier(message);
}
globalState.isUserDisconnected = false;
if (ref.read(coreStatusProvider) != CoreStatus.connected) {
return;
}
ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
await coreController.shutdown();
super.onCrash(message);
}
}

View File

@@ -1,7 +1,7 @@
export 'android_manager.dart';
export 'app_manager.dart';
export 'clash_manager.dart';
export 'connectivity_manager.dart';
export 'core_manager.dart';
export 'message_manager.dart';
export 'proxy_manager.dart';
export 'theme_manager.dart';

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:collection';
import 'dart:math';
import 'package:fl_clash/common/common.dart';
@@ -9,10 +10,7 @@ import 'package:flutter/material.dart';
class MessageManager extends StatefulWidget {
final Widget child;
const MessageManager({
super.key,
required this.child,
});
const MessageManager({super.key, required this.child});
@override
State<MessageManager> createState() => MessageManagerState();
@@ -20,8 +18,9 @@ class MessageManager extends StatefulWidget {
class MessageManagerState extends State<MessageManager> {
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
final List<CommonMessage> _bufferMessages = [];
bool _pushing = false;
final _bufferMessages = Queue<CommonMessage>();
final _activeTimers = <String, Timer>{};
bool _isDisplayingMessage = false;
@override
void initState() {
@@ -31,43 +30,48 @@ class MessageManagerState extends State<MessageManager> {
@override
void dispose() {
_messagesNotifier.dispose();
for (final timer in _activeTimers.values) {
timer.cancel();
}
_activeTimers.clear();
_bufferMessages.clear();
super.dispose();
}
Future<void> message(String text) async {
final commonMessage = CommonMessage(
id: utils.uuidV4,
text: text,
);
commonPrint.log(text);
void message(String text) {
final commonMessage = CommonMessage(id: utils.uuidV4, text: text);
_bufferMessages.add(commonMessage);
await _showMessage();
commonPrint.log('message: $text');
_processQueue();
}
Future<void> _showMessage() async {
if (_pushing == true) {
void _cancelMessage(String id) {
_bufferMessages.removeWhere((msg) => msg.id == id);
if (_activeTimers.containsKey(id)) {
_removeMessage(id);
}
}
void _processQueue() {
if (_isDisplayingMessage || _bufferMessages.isEmpty) {
return;
}
_pushing = true;
while (_bufferMessages.isNotEmpty) {
final commonMessage = _bufferMessages.removeAt(0);
_messagesNotifier.value = List.from(_messagesNotifier.value)
..add(
commonMessage,
);
await Future.delayed(Duration(seconds: 1));
Future.delayed(commonMessage.duration, () {
_handleRemove(commonMessage);
});
if (_bufferMessages.isEmpty) {
_pushing = false;
}
}
_isDisplayingMessage = true;
final message = _bufferMessages.removeFirst();
_messagesNotifier.value = List.from(_messagesNotifier.value)..add(message);
final timer = Timer(message.duration, () {
_removeMessage(message.id);
});
_activeTimers[message.id] = timer;
}
Future<void> _handleRemove(CommonMessage commonMessage) async {
_messagesNotifier.value = List<CommonMessage>.from(_messagesNotifier.value)
..remove(commonMessage);
void _removeMessage(String id) {
_activeTimers.remove(id)?.cancel();
final currentMessages = List<CommonMessage>.from(_messagesNotifier.value);
currentMessages.removeWhere((msg) => msg.id == id);
_messagesNotifier.value = currentMessages;
_isDisplayingMessage = false;
_processQueue();
}
@override
@@ -77,43 +81,67 @@ class MessageManagerState extends State<MessageManager> {
widget.child,
ValueListenableBuilder(
valueListenable: _messagesNotifier,
builder: (_, messages, __) {
return FadeThroughBox(
builder: (_, messages, _) {
return Container(
margin: EdgeInsets.only(
top: kToolbarHeight + 8,
top: kToolbarHeight + 12,
left: 12,
right: 12,
),
alignment: Alignment.topRight,
child: messages.isEmpty
? SizedBox()
: LayoutBuilder(
key: Key(messages.last.id),
builder: (_, constraints) {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12.0),
child: FadeThroughBox(
alignment: Alignment.topRight,
child: messages.isEmpty
? SizedBox()
: LayoutBuilder(
key: Key(messages.last.id),
builder: (_, constraints) {
return Dismissible(
key: ValueKey(messages.last.id),
onDismissed: (_) {
_cancelMessage(messages.last.id);
},
child: Card(
shape: const RoundedSuperellipseBorder(
borderRadius: BorderRadius.all(
Radius.circular(14),
),
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(constraints.maxWidth, 500),
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
messages.last.text,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 16),
IconButton(
padding: EdgeInsets.all(2),
visualDensity: VisualDensity.compact,
onPressed: () {
_cancelMessage(messages.last.id);
},
icon: Icon(Icons.close),
),
],
),
),
),
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(
constraints.maxWidth,
500,
),
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Text(
messages.last.text,
),
),
);
},
),
);
},
),
),
);
},
),

View File

@@ -13,10 +13,7 @@ import '../providers/state.dart';
class ThemeManager extends ConsumerWidget {
final Widget child;
const ThemeManager({
super.key,
required this.child,
});
const ThemeManager({super.key, required this.child});
Widget _buildSystemUi(Widget child) {
if (!system.isAndroid) {
@@ -85,23 +82,28 @@ class ThemeManager extends ConsumerWidget {
final height = MediaQuery.of(context).size.height;
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(
textScaleFactor,
),
textScaler: TextScaler.linear(textScaleFactor),
padding: padding.copyWith(
top: padding.top > height * 0.3 ? 20.0 : padding.top,
),
),
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewSize(
Size(
container.maxWidth,
container.maxHeight,
),
);
return _buildSystemUi(child);
},
child: Theme(
data: Theme.of(context).copyWith(
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
.copyWith(
shape: const RoundedSuperellipseBorder(
borderRadius: BorderRadius.all(Radius.circular(16.0)),
),
),
),
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewSize(
Size(container.maxWidth, container.maxHeight),
);
return _buildSystemUi(child);
},
),
),
);
}

View File

@@ -1,3 +1,4 @@
import 'package:fl_clash/models/app.dart';
import 'package:fl_clash/plugins/tile.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -5,10 +6,7 @@ import 'package:flutter/material.dart';
class TileManager extends StatefulWidget {
final Widget child;
const TileManager({
super.key,
required this.child,
});
const TileManager({super.key, required this.child});
@override
State<TileManager> createState() => _TileContainerState();
@@ -22,12 +20,18 @@ class _TileContainerState extends State<TileManager> with TileListener {
@override
void onStart() {
if (globalState.appState.isStart) {
return;
}
globalState.appController.updateStatus(true);
super.onStart();
}
@override
Future<void> onStop() async {
if (!globalState.appState.isStart) {
return;
}
globalState.appController.updateStatus(false);
super.onStop();
}

View File

@@ -39,6 +39,7 @@ class _TrayContainerState extends ConsumerState<TrayManager> with TrayListener {
@override
void onTrayIconRightMouseDown() {
// ignore: deprecated_member_use
trayManager.popUpContextMenu(bringAppToFront: true);
}

View File

@@ -1,6 +1,5 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/providers/state.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -9,10 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class VpnManager extends ConsumerStatefulWidget {
final Widget child;
const VpnManager({
super.key,
required this.child,
});
const VpnManager({super.key, required this.child});
@override
ConsumerState<VpnManager> createState() => _VpnContainerState();
@@ -28,15 +24,15 @@ class _VpnContainerState extends ConsumerState<VpnManager> {
}
void showTip() {
debouncer.call(
throttler.call(
FunctionTag.vpnTip,
() {
if (ref.read(runTimeProvider.notifier).isStart) {
globalState.showNotifier(
appLocalizations.vpnTip,
);
if (ref.read(isStartProvider)) {
globalState.showNotifier(appLocalizations.vpnTip);
}
},
duration: const Duration(seconds: 6),
fire: true,
);
}

View File

@@ -12,10 +12,7 @@ import 'package:window_manager/window_manager.dart';
class WindowManager extends ConsumerStatefulWidget {
final Widget child;
const WindowManager({
super.key,
required this.child,
});
const WindowManager({super.key, required this.child});
@override
ConsumerState<WindowManager> createState() => _WindowContainerState();
@@ -31,19 +28,16 @@ class _WindowContainerState extends ConsumerState<WindowManager>
@override
void initState() {
super.initState();
ref.listenManual(
appSettingProvider.select((state) => state.autoLaunch),
(prev, next) {
if (prev != next) {
debouncer.call(
FunctionTag.autoLaunch,
() {
autoLaunch?.updateStatus(next);
},
);
}
},
);
ref.listenManual(appSettingProvider.select((state) => state.autoLaunch), (
prev,
next,
) {
if (prev != next) {
debouncer.call(FunctionTag.autoLaunch, () {
autoLaunch?.updateStatus(next);
});
}
});
windowExtManager.addListener(this);
windowManager.addListener(this);
}
@@ -71,11 +65,10 @@ class _WindowContainerState extends ConsumerState<WindowManager>
Future<void> onWindowMoved() async {
super.onWindowMoved();
final offset = await windowManager.getPosition();
ref.read(windowSettingProvider.notifier).updateState(
(state) => state.copyWith(
top: offset.dy,
left: offset.dx,
),
ref
.read(windowSettingProvider.notifier)
.updateState(
(state) => state.copyWith(top: offset.dy, left: offset.dx),
);
}
@@ -83,11 +76,10 @@ class _WindowContainerState extends ConsumerState<WindowManager>
Future<void> onWindowResized() async {
super.onWindowResized();
final size = await windowManager.getSize();
ref.read(windowSettingProvider.notifier).updateState(
(state) => state.copyWith(
width: size.width,
height: size.height,
),
ref
.read(windowSettingProvider.notifier)
.updateState(
(state) => state.copyWith(width: size.width, height: size.height),
);
}
@@ -117,10 +109,7 @@ class _WindowContainerState extends ConsumerState<WindowManager>
class WindowHeaderContainer extends StatelessWidget {
final Widget child;
const WindowHeaderContainer({
super.key,
required this.child,
});
const WindowHeaderContainer({super.key, required this.child});
@override
Widget build(BuildContext context) {
@@ -135,13 +124,8 @@ class WindowHeaderContainer extends StatelessWidget {
children: [
Column(
children: [
SizedBox(
height: kHeaderHeight,
),
Expanded(
flex: 1,
child: child!,
),
SizedBox(height: kHeaderHeight),
Expanded(flex: 1, child: child!),
],
),
const WindowHeader(),
@@ -210,14 +194,10 @@ class _WindowHeaderState extends State<WindowHeader> {
},
icon: ValueListenableBuilder(
valueListenable: isPinNotifier,
builder: (_, value, ___) {
builder: (_, value, _) {
return value
? const Icon(
Icons.push_pin,
)
: const Icon(
Icons.push_pin_outlined,
);
? const Icon(Icons.push_pin)
: const Icon(Icons.push_pin_outlined);
},
),
),
@@ -233,15 +213,10 @@ class _WindowHeaderState extends State<WindowHeader> {
},
icon: ValueListenableBuilder(
valueListenable: isMaximizedNotifier,
builder: (_, value, ___) {
builder: (_, value, _) {
return value
? const Icon(
Icons.filter_none,
size: 20,
)
: const Icon(
Icons.crop_square,
);
? const Icon(Icons.filter_none, size: 20)
: const Icon(Icons.crop_square);
},
),
),
@@ -280,15 +255,10 @@ class _WindowHeaderState extends State<WindowHeader> {
),
),
if (system.isMacOS)
const Text(
appName,
)
const Text(appName)
else ...[
Positioned(
right: 0,
child: _buildActions(),
),
]
Positioned(right: 0, child: _buildActions()),
],
],
),
);
@@ -301,19 +271,17 @@ class AppIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: context.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.all(6),
child: SizedBox(
width: 28,
height: 28,
child: CircleAvatar(
foregroundImage: AssetImage('assets/images/icon.png'),
backgroundColor: Colors.transparent,
decoration: ShapeDecoration(
color: context.colorScheme.surfaceContainerHighest,
shape: RoundedSuperellipseBorder(
borderRadius: BorderRadius.circular(14),
),
),
padding: EdgeInsets.all(8),
child: Transform.translate(
offset: Offset(0, -1),
child: Image.asset('assets/images/icon.png', width: 34, height: 34),
),
);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/selector.dart';
import 'package:flutter/services.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -11,7 +12,7 @@ part 'generated/app.freezed.dart';
typedef DelayMap = Map<String, Map<String, int?>>;
@freezed
class AppState with _$AppState {
abstract class AppState with _$AppState {
const factory AppState({
@Default(false) bool isInit,
@Default(false) bool backBlock,
@@ -19,6 +20,7 @@ class AppState with _$AppState {
@Default([]) List<Package> packages,
@Default(0) int sortNum,
required Size viewSize,
@Default(0) double sideWidth,
@Default({}) DelayMap delayMap,
@Default([]) List<Group> groups,
@Default(0) int checkIpNum,
@@ -34,6 +36,9 @@ class AppState with _$AppState {
@Default(false) bool realTunEnable,
@Default(false) bool loading,
required SystemUiOverlayStyle systemUiOverlayStyle,
ProfileOverrideModel? profileOverrideModel,
@Default({}) Map<QueryTag, String> queryMap,
@Default(CoreStatus.connecting) CoreStatus coreStatus,
}) = _AppState;
}

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -100,17 +98,14 @@ const defaultBypassPrivateRouteAddress = [
'f000::/5',
'f800::/6',
'fe00::/9',
'fec0::/10'
'fec0::/10',
];
@freezed
class ProxyGroup with _$ProxyGroup {
abstract class ProxyGroup with _$ProxyGroup {
const factory ProxyGroup({
required String name,
@JsonKey(
fromJson: GroupType.parseProfileType,
)
required GroupType type,
@JsonKey(fromJson: GroupType.parseProfileType) required GroupType type,
List<String>? proxies,
List<String>? use,
int? interval,
@@ -131,17 +126,15 @@ class ProxyGroup with _$ProxyGroup {
}
@freezed
class RuleProvider with _$RuleProvider {
const factory RuleProvider({
required String name,
}) = _RuleProvider;
abstract class RuleProvider with _$RuleProvider {
const factory RuleProvider({required String name}) = _RuleProvider;
factory RuleProvider.fromJson(Map<String, Object?> json) =>
_$RuleProviderFromJson(json);
}
@freezed
class Sniffer with _$Sniffer {
abstract class Sniffer with _$Sniffer {
const factory Sniffer({
@Default(false) bool enable,
@Default(true) @JsonKey(name: 'override-destination') bool overrideDest,
@@ -165,7 +158,7 @@ List<String> _formJsonPorts(List? ports) {
}
@freezed
class SnifferConfig with _$SnifferConfig {
abstract class SnifferConfig with _$SnifferConfig {
const factory SnifferConfig({
@Default([]) @JsonKey(fromJson: _formJsonPorts) List<String> ports,
@JsonKey(name: 'override-destination') bool? overrideDest,
@@ -176,7 +169,7 @@ class SnifferConfig with _$SnifferConfig {
}
@freezed
class Tun with _$Tun {
abstract class Tun with _$Tun {
const factory Tun({
@Default(false) bool enable,
@Default(appName) String device,
@@ -206,30 +199,23 @@ extension TunExt on Tun {
? defaultBypassPrivateRouteAddress
: routeAddress;
return switch (system.isDesktop) {
true => copyWith(
autoRoute: true,
routeAddress: [],
),
true => copyWith(autoRoute: true, routeAddress: []),
false => copyWith(
autoRoute: mRouteAddress.isEmpty ? true : false,
routeAddress: mRouteAddress,
),
autoRoute: mRouteAddress.isEmpty ? true : false,
routeAddress: mRouteAddress,
),
};
}
}
@freezed
class FallbackFilter with _$FallbackFilter {
abstract class FallbackFilter with _$FallbackFilter {
const factory FallbackFilter({
@Default(true) bool geoip,
@Default('CN') @JsonKey(name: 'geoip-code') String geoipCode,
@Default(['gfw']) List<String> geosite,
@Default(['240.0.0.0/4']) List<String> ipcidr,
@Default([
'+.google.com',
'+.facebook.com',
'+.youtube.com',
])
@Default(['+.google.com', '+.facebook.com', '+.youtube.com'])
List<String> domain,
}) = _FallbackFilter;
@@ -238,7 +224,7 @@ class FallbackFilter with _$FallbackFilter {
}
@freezed
class Dns with _$Dns {
abstract class Dns with _$Dns {
const factory Dns({
@Default(true) bool enable,
@Default('0.0.0.0:1053') String listen,
@@ -256,32 +242,20 @@ class Dns with _$Dns {
@Default('198.18.0.1/16')
@JsonKey(name: 'fake-ip-range')
String fakeIpRange,
@Default([
'*.lan',
'localhost.ptlogin2.qq.com',
])
@Default(['*.lan', 'localhost.ptlogin2.qq.com'])
@JsonKey(name: 'fake-ip-filter')
List<String> fakeIpFilter,
@Default({
'www.baidu.com': '114.114.114.114',
'+.internal.crop.com': '10.0.0.1',
'geosite:cn': 'https://doh.pub/dns-query'
'geosite:cn': 'https://doh.pub/dns-query',
})
@JsonKey(name: 'nameserver-policy')
Map<String, String> nameserverPolicy,
@Default([
'https://doh.pub/dns-query',
'https://dns.alidns.com/dns-query',
])
@Default(['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'])
List<String> nameserver,
@Default([
'tls://8.8.4.4',
'tls://1.1.1.1',
])
List<String> fallback,
@Default([
'https://doh.pub/dns-query',
])
@Default(['tls://8.8.4.4', 'tls://1.1.1.1']) List<String> fallback,
@Default(['https://doh.pub/dns-query'])
@JsonKey(name: 'proxy-server-nameserver')
List<String> proxyServerNameserver,
@Default(FallbackFilter())
@@ -301,7 +275,7 @@ class Dns with _$Dns {
}
@freezed
class GeoXUrl with _$GeoXUrl {
abstract class GeoXUrl with _$GeoXUrl {
const factory GeoXUrl({
@Default(
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
@@ -337,7 +311,7 @@ class GeoXUrl with _$GeoXUrl {
}
@freezed
class ParsedRule with _$ParsedRule {
abstract class ParsedRule with _$ParsedRule {
const factory ParsedRule({
required RuleAction ruleAction,
String? content,
@@ -351,9 +325,7 @@ class ParsedRule with _$ParsedRule {
factory ParsedRule.parseString(String value) {
final splits = value.split(',');
final shortSplits = splits
.where(
(item) => !item.contains('src') && !item.contains('no-resolve'),
)
.where((item) => !item.contains('src') && !item.contains('no-resolve'))
.toList();
final ruleAction = RuleAction.values.firstWhere(
(item) => item.value == shortSplits.first,
@@ -398,33 +370,25 @@ extension ParsedRuleExt on ParsedRule {
if (ruleAction.hasParams) ...[
if (src) 'src',
if (noResolve) 'no-resolve',
]
],
].join(',');
}
}
@freezed
class Rule with _$Rule {
const factory Rule({
required String id,
required String value,
}) = _Rule;
abstract class Rule with _$Rule {
const factory Rule({required String id, required String value}) = _Rule;
factory Rule.value(String value) {
return Rule(
value: value,
id: utils.uuidV4,
);
return Rule(value: value, id: utils.uuidV4);
}
factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);
}
@freezed
class SubRule with _$SubRule {
const factory SubRule({
required String name,
}) = _SubRule;
abstract class SubRule with _$SubRule {
const factory SubRule({required String name}) = _SubRule;
factory SubRule.fromJson(Map<String, Object?> json) =>
_$SubRuleFromJson(json);
@@ -434,11 +398,7 @@ List<Rule> _genRule(List<dynamic>? rules) {
if (rules == null) {
return [];
}
return rules
.map(
(item) => Rule.value(item),
)
.toList();
return rules.map((item) => Rule.value(item)).toList();
}
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
@@ -446,17 +406,11 @@ List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
}
List<SubRule> _genSubRules(Map<String, dynamic> json) {
return json.entries
.map(
(entry) => SubRule(
name: entry.key,
),
)
.toList();
return json.entries.map((entry) => SubRule(name: entry.key)).toList();
}
@freezed
class ClashConfigSnippet with _$ClashConfigSnippet {
abstract class ClashConfigSnippet with _$ClashConfigSnippet {
const factory ClashConfigSnippet({
@Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule, name: 'rules') @Default([]) List<Rule> rule,
@@ -473,7 +427,7 @@ class ClashConfigSnippet with _$ClashConfigSnippet {
}
@freezed
class ClashConfig with _$ClashConfig {
abstract class ClashConfig with _$ClashConfig {
const factory ClashConfig({
@Default(defaultMixedPort) @JsonKey(name: 'mixed-port') int mixedPort,
@Default(0) @JsonKey(name: 'socks-port') int socksPort,
@@ -484,7 +438,7 @@ class ClashConfig with _$ClashConfig {
@Default(false) @JsonKey(name: 'allow-lan') bool allowLan,
@Default(LogLevel.error) @JsonKey(name: 'log-level') LogLevel logLevel,
@Default(false) bool ipv6,
@Default(FindProcessMode.off)
@Default(FindProcessMode.always)
@JsonKey(
name: 'find-process-mode',
unknownEnumValue: FindProcessMode.always,

View File

@@ -1,7 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'dart:math';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
@@ -11,7 +7,7 @@ part 'generated/common.freezed.dart';
part 'generated/common.g.dart';
@freezed
class NavigationItem with _$NavigationItem {
abstract class NavigationItem with _$NavigationItem {
const factory NavigationItem({
required Icon icon,
required PageLabel label,
@@ -25,7 +21,7 @@ class NavigationItem with _$NavigationItem {
}
@freezed
class Package with _$Package {
abstract class Package with _$Package {
const factory Package({
required String packageName,
required String label,
@@ -39,7 +35,7 @@ class Package with _$Package {
}
@freezed
class Metadata with _$Metadata {
abstract class Metadata with _$Metadata {
const factory Metadata({
@Default(0) int uid,
@Default('') String network,
@@ -65,7 +61,7 @@ class Metadata with _$Metadata {
}
@freezed
class TrackerInfo with _$TrackerInfo {
abstract class TrackerInfo with _$TrackerInfo {
const factory TrackerInfo({
required String id,
@Default(0) int upload,
@@ -99,9 +95,9 @@ extension TrackerInfoExt on TrackerInfo {
final process = metadata.process;
final uid = metadata.uid;
if (uid != 0) {
return '$process($uid)';
return '$process($uid)'.trim();
}
return process;
return process.trim();
}
}
@@ -114,7 +110,7 @@ String _logDateTime(dynamic _) {
// }
@freezed
class Log with _$Log {
abstract class Log with _$Log {
const factory Log({
// @JsonKey(fromJson: _logId) required String id,
@JsonKey(name: 'LogLevel') @Default(LogLevel.info) LogLevel logLevel,
@@ -122,9 +118,7 @@ class Log with _$Log {
@JsonKey(fromJson: _logDateTime) required String dateTime,
}) = _Log;
factory Log.app(
String payload,
) {
factory Log.app(String payload) {
return Log(
payload: payload,
dateTime: _logDateTime(null),
@@ -136,7 +130,7 @@ class Log with _$Log {
}
@freezed
class LogsState with _$LogsState {
abstract class LogsState with _$LogsState {
const factory LogsState({
@Default([]) List<Log> logs,
@Default([]) List<String> keywords,
@@ -148,19 +142,17 @@ class LogsState with _$LogsState {
extension LogsStateExt on LogsState {
List<Log> get list {
final lowQuery = query.toLowerCase();
return logs.where(
(log) {
final logLevelName = log.logLevel.name;
return {logLevelName}.containsAll(keywords) &&
((log.payload.toLowerCase().contains(lowQuery)) ||
logLevelName.contains(lowQuery));
},
).toList();
return logs.where((log) {
final logLevelName = log.logLevel.name;
return {logLevelName}.containsAll(keywords) &&
((log.payload.toLowerCase().contains(lowQuery)) ||
logLevelName.contains(lowQuery));
}).toList();
}
}
@freezed
class TrackerInfosState with _$TrackerInfosState {
abstract class TrackerInfosState with _$TrackerInfosState {
const factory TrackerInfosState({
@Default([]) List<TrackerInfo> trackerInfos,
@Default([]) List<String> keywords,
@@ -178,8 +170,8 @@ extension TrackerInfosStateExt on TrackerInfosState {
final process = trackerInfo.metadata.process;
final networkText = trackerInfo.metadata.network.toLowerCase();
final hostText = trackerInfo.metadata.host.toLowerCase();
final destinationIPText =
trackerInfo.metadata.destinationIP.toLowerCase();
final destinationIPText = trackerInfo.metadata.destinationIP
.toLowerCase();
final processText = trackerInfo.metadata.process.toLowerCase();
final chainsText = chains.join('').toLowerCase();
return {...chains, process}.containsAll(keywords) &&
@@ -195,7 +187,7 @@ extension TrackerInfosStateExt on TrackerInfosState {
const defaultDavFileName = 'backup.zip';
@freezed
class DAV with _$DAV {
abstract class DAV with _$DAV {
const factory DAV({
required String uri,
required String user,
@@ -207,20 +199,18 @@ class DAV with _$DAV {
}
@freezed
class FileInfo with _$FileInfo {
const factory FileInfo({
required int size,
required DateTime lastModified,
}) = _FileInfo;
abstract class FileInfo with _$FileInfo {
const factory FileInfo({required int size, required DateTime lastModified}) =
_FileInfo;
}
extension FileInfoExt on FileInfo {
String get desc =>
'${TrafficValue(value: size).show} · ${lastModified.lastUpdateTimeDesc}';
'${size.traffic.show} · ${lastModified.lastUpdateTimeDesc}';
}
@freezed
class VersionInfo with _$VersionInfo {
abstract class VersionInfo with _$VersionInfo {
const factory VersionInfo({
@Default('') String clashName,
@Default('') String version,
@@ -230,60 +220,38 @@ class VersionInfo with _$VersionInfo {
_$VersionInfoFromJson(json);
}
class Traffic {
int id;
TrafficValue up;
TrafficValue down;
@freezed
abstract class Traffic with _$Traffic {
const factory Traffic({@Default(0) num up, @Default(0) num down}) = _Traffic;
Traffic({int? up, int? down})
: id = DateTime.now().millisecondsSinceEpoch,
up = TrafficValue(value: up),
down = TrafficValue(value: down);
num get speed => up.value + down.value;
factory Traffic.fromMap(Map<String, dynamic> map) {
return Traffic(
up: map['up'],
down: map['down'],
);
}
String toSpeedText() {
return '$up/s ↓ $down/s';
}
@override
String toString() {
return '$up$down';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Traffic &&
runtimeType == other.runtimeType &&
id == other.id &&
up == other.up &&
down == other.down;
@override
int get hashCode => id.hashCode ^ up.hashCode ^ down.hashCode;
factory Traffic.fromJson(Map<String, Object?> json) =>
_$TrafficFromJson(json);
}
@immutable
class TrafficValueShow {
final double value;
final TrafficUnit unit;
extension TrafficExt on Traffic {
String get speedText {
return '${up.traffic.show}/s ↓ ${down.traffic.show}/s';
}
const TrafficValueShow({
required this.value,
required this.unit,
});
String get desc {
return '${up.traffic.show}${down.traffic.show}';
}
num get speed => up + down;
}
@freezed
class Proxy with _$Proxy {
abstract class TrafficShow with _$TrafficShow {
const factory TrafficShow({required String value, required String unit}) =
_TrafficShow;
}
extension TrafficShowExt on TrafficShow {
String get show => '$value$unit';
}
@freezed
abstract class Proxy with _$Proxy {
const factory Proxy({
required String name,
required String type,
@@ -294,7 +262,7 @@ class Proxy with _$Proxy {
}
@freezed
class Group with _$Group {
abstract class Group with _$Group {
const factory Group({
required GroupType type,
@Default([]) List<Proxy> all,
@@ -326,70 +294,8 @@ extension GroupExt on Group {
}
}
@immutable
class TrafficValue {
final int _value;
const TrafficValue({int? value}) : _value = value ?? 0;
int get value => _value;
String get show => '$showValue $showUnit';
String get shortShow =>
'${trafficValueShow.value.fixed(decimals: 1)} $showUnit';
String get showValue => trafficValueShow.value.fixed();
String get showUnit => trafficValueShow.unit.name;
TrafficValueShow get trafficValueShow {
if (_value > pow(1024, 4)) {
return TrafficValueShow(
value: _value / pow(1024, 4),
unit: TrafficUnit.TB,
);
}
if (_value > pow(1024, 3)) {
return TrafficValueShow(
value: _value / pow(1024, 3),
unit: TrafficUnit.GB,
);
}
if (_value > pow(1024, 2)) {
return TrafficValueShow(
value: _value / pow(1024, 2), unit: TrafficUnit.MB);
}
if (_value > pow(1024, 1)) {
return TrafficValueShow(
value: _value / pow(1024, 1),
unit: TrafficUnit.KB,
);
}
return TrafficValueShow(
value: _value.toDouble(),
unit: TrafficUnit.B,
);
}
@override
String toString() {
return '$showValue$showUnit';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TrafficValue &&
runtimeType == other.runtimeType &&
_value == other._value;
@override
int get hashCode => _value.hashCode;
}
@freezed
class ColorSchemes with _$ColorSchemes {
abstract class ColorSchemes with _$ColorSchemes {
const factory ColorSchemes({
ColorScheme? lightColorScheme,
ColorScheme? darkColorScheme,
@@ -426,79 +332,76 @@ extension ColorSchemesExt on ColorSchemes {
}
}
class IpInfo {
final String ip;
final String countryCode;
const IpInfo({
required this.ip,
required this.countryCode,
});
@freezed
abstract class IpInfo with _$IpInfo {
const factory IpInfo({required String ip, required String countryCode}) =
_IpInfo;
static IpInfo fromIpInfoIoJson(Map<String, dynamic> json) {
return switch (json) {
{
'ip': final String ip,
'country': final String country,
} =>
IpInfo(
ip: ip,
countryCode: country,
),
{'ip': final String ip, 'country': final String country} => IpInfo(
ip: ip,
countryCode: country,
),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIpApiCoJson(Map<String, dynamic> json) {
return switch (json) {
{
'ip': final String ip,
'country_code': final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
{'ip': final String ip, 'country_code': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIpSbJson(Map<String, dynamic> json) {
return switch (json) {
{
'ip': final String ip,
'country_code': final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
{'ip': final String ip, 'country_code': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
static IpInfo fromIpWhoIsJson(Map<String, dynamic> json) {
return switch (json) {
{
'ip': final String ip,
'country_code': final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
{'ip': final String ip, 'country_code': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
@override
String toString() {
return 'IpInfo{ip: $ip, countryCode: $countryCode}';
static IpInfo fromMyIpJson(Map<String, dynamic> json) {
return switch (json) {
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIpAPIJson(Map<String, dynamic> json) {
return switch (json) {
{'query': final String ip, 'countryCode': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIdentMeJson(Map<String, dynamic> json) {
return switch (json) {
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException('invalid json'),
};
}
}
@freezed
class HotKeyAction with _$HotKeyAction {
abstract class HotKeyAction with _$HotKeyAction {
const factory HotKeyAction({
required HotAction action,
int? key,
@@ -512,7 +415,7 @@ class HotKeyAction with _$HotKeyAction {
typedef Validator = String? Function(String? value);
@freezed
class Field with _$Field {
abstract class Field with _$Field {
const factory Field({
required String label,
required String value,
@@ -520,35 +423,33 @@ class Field with _$Field {
}) = _Field;
}
enum PopupMenuItemType {
primary,
danger,
}
enum PopupMenuItemType { primary, danger }
class PopupMenuItemData {
const PopupMenuItemData({
this.icon,
required this.label,
required this.onPressed,
this.danger = false,
});
final String label;
final VoidCallback? onPressed;
final IconData? icon;
final bool danger;
}
@freezed
class TextPainterParams with _$TextPainterParams {
const factory TextPainterParams({
required String? text,
required double? fontSize,
required double textScaleFactor,
@Default(double.infinity) double maxWidth,
int? maxLines,
}) = _TextPainterParams;
abstract class AndroidState with _$AndroidState {
const factory AndroidState({
required String currentProfileName,
required String stopText,
required bool onlyStatisticsProxy,
required bool crashlytics,
}) = _AndroidState;
factory TextPainterParams.fromJson(Map<String, Object?> json) =>
_$TextPainterParamsFromJson(json);
factory AndroidState.fromJson(Map<String, Object?> json) =>
_$AndroidStateFromJson(json);
}
class CloseWindowIntent extends Intent {
@@ -556,24 +457,18 @@ class CloseWindowIntent extends Intent {
}
@freezed
class Result<T> with _$Result<T> {
abstract class Result<T> with _$Result<T> {
const factory Result({
required T? data,
required ResultType type,
required String message,
}) = _Result;
factory Result.success(T data) => Result(
data: data,
type: ResultType.success,
message: '',
);
factory Result.success(T data) =>
Result(data: data, type: ResultType.success, message: '');
factory Result.error(String message) => Result(
data: null,
type: ResultType.error,
message: message,
);
factory Result.error(String message) =>
Result(data: null, type: ResultType.error, message: message);
}
extension ResultExt on Result {
@@ -583,23 +478,42 @@ extension ResultExt on Result {
}
@freezed
class Script with _$Script {
abstract class Script with _$Script {
const factory Script({
required String id,
required String label,
required String content,
}) = _Script;
factory Script.create({
required String label,
required String content,
}) {
return Script(
id: utils.uuidV4,
label: label,
content: content,
);
factory Script.create({required String label, required String content}) {
return Script(id: utils.uuidV4, label: label, content: content);
}
factory Script.fromJson(Map<String, Object?> json) => _$ScriptFromJson(json);
}
@freezed
abstract class DelayState with _$DelayState {
const factory DelayState({required int delay, required bool group}) =
_DelayState;
}
extension DelayStateExt on DelayState {
int get priority {
if (delay > 0) return 0;
if (delay == 0) return 1;
return 2;
}
int compareTo(DelayState other) {
if (priority != other.priority) {
return priority.compareTo(other.priority);
}
if (delay != other.delay) {
return delay.compareTo(other.delay);
}
if (group && !group) return -1;
if (!group && group) return 1;
return 0;
}
}

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
@@ -27,7 +25,7 @@ const defaultBypassDomain = [
'172.2*',
'172.30.*',
'172.31.*',
'192.168.*'
'192.168.*',
];
const defaultAppSettingProps = AppSettingProps();
@@ -36,9 +34,7 @@ const defaultNetworkProps = NetworkProps();
const defaultProxiesStyle = ProxiesStyle();
const defaultWindowProps = WindowProps();
const defaultAccessControl = AccessControl();
final defaultThemeProps = ThemeProps(
primaryColor: defaultPrimaryColor,
);
final defaultThemeProps = ThemeProps(primaryColor: defaultPrimaryColor);
const List<DashboardWidget> defaultDashboardWidgets = [
DashboardWidget.networkSpeed,
@@ -64,7 +60,7 @@ List<DashboardWidget> dashboardWidgetsSafeFormJson(
}
@freezed
class AppSettingProps with _$AppSettingProps {
abstract class AppSettingProps with _$AppSettingProps {
const factory AppSettingProps({
String? locale,
@Default(defaultDashboardWidgets)
@@ -81,6 +77,8 @@ class AppSettingProps with _$AppSettingProps {
@Default(true) bool autoCheckUpdate,
@Default(false) bool showLabel,
@Default(false) bool disclaimerAccepted,
@Default(false) bool crashlyticsTip,
@Default(false) bool crashlytics,
@Default(true) bool minimizeOnExit,
@Default(false) bool hidden,
@Default(false) bool developerMode,
@@ -98,7 +96,7 @@ class AppSettingProps with _$AppSettingProps {
}
@freezed
class AccessControl with _$AccessControl {
abstract class AccessControl with _$AccessControl {
const factory AccessControl({
@Default(false) bool enable,
@Default(AccessControlMode.rejectSelected) AccessControlMode mode,
@@ -115,13 +113,13 @@ class AccessControl with _$AccessControl {
extension AccessControlExt on AccessControl {
List<String> get currentList => switch (mode) {
AccessControlMode.acceptSelected => acceptList,
AccessControlMode.rejectSelected => rejectList,
};
AccessControlMode.acceptSelected => acceptList,
AccessControlMode.rejectSelected => rejectList,
};
}
@freezed
class WindowProps with _$WindowProps {
abstract class WindowProps with _$WindowProps {
const factory WindowProps({
@Default(750) double width,
@Default(600) double height,
@@ -134,12 +132,13 @@ class WindowProps with _$WindowProps {
}
@freezed
class VpnProps with _$VpnProps {
abstract class VpnProps with _$VpnProps {
const factory VpnProps({
@Default(true) bool enable,
@Default(true) bool systemProxy,
@Default(false) bool ipv6,
@Default(true) bool allowBypass,
@Default(false) bool dnsHijacking,
@Default(defaultAccessControl) AccessControl accessControl,
}) = _VpnProps;
@@ -148,7 +147,7 @@ class VpnProps with _$VpnProps {
}
@freezed
class NetworkProps with _$NetworkProps {
abstract class NetworkProps with _$NetworkProps {
const factory NetworkProps({
@Default(true) bool systemProxy,
@Default(defaultBypassDomain) List<String> bypassDomain,
@@ -161,7 +160,7 @@ class NetworkProps with _$NetworkProps {
}
@freezed
class ProxiesStyle with _$ProxiesStyle {
abstract class ProxiesStyle with _$ProxiesStyle {
const factory ProxiesStyle({
@Default(ProxiesType.tab) ProxiesType type,
@Default(ProxiesSortType.none) ProxiesSortType sortType,
@@ -176,7 +175,7 @@ class ProxiesStyle with _$ProxiesStyle {
}
@freezed
class TextScale with _$TextScale {
abstract class TextScale with _$TextScale {
const factory TextScale({
@Default(false) bool enable,
@Default(1.0) double scale,
@@ -187,7 +186,7 @@ class TextScale with _$TextScale {
}
@freezed
class ThemeProps with _$ThemeProps {
abstract class ThemeProps with _$ThemeProps {
const factory ThemeProps({
int? primaryColor,
@Default(defaultPrimaryColors) List<int> primaryColors,
@@ -213,7 +212,7 @@ class ThemeProps with _$ThemeProps {
}
@freezed
class ScriptProps with _$ScriptProps {
abstract class ScriptProps with _$ScriptProps {
const factory ScriptProps({
String? currentId,
@Default([]) List<Script> scripts,
@@ -242,7 +241,7 @@ extension ScriptPropsExt on ScriptProps {
}
@freezed
class Config with _$Config {
abstract class Config with _$Config {
const factory Config({
@JsonKey(fromJson: AppSettingProps.safeFromJson)
@Default(defaultAppSettingProps)

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -7,26 +5,9 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/core.freezed.dart';
part 'generated/core.g.dart';
abstract mixin class AppMessageListener {
void onLog(Log log) {}
void onDelay(Delay delay) {}
void onRequest(TrackerInfo connection) {}
void onLoaded(String providerName) {}
}
// abstract mixin class ServiceMessageListener {
// onProtect(Fd fd) {}
//
// onProcess(ProcessData process) {}
// }
@freezed
class SetupParams with _$SetupParams {
abstract class SetupParams with _$SetupParams {
const factory SetupParams({
@JsonKey(name: 'config') required Map<String, dynamic> config,
@JsonKey(name: 'selected-map') required Map<String, String> selectedMap,
@JsonKey(name: 'test-url') required String testUrl,
}) = _SetupParams;
@@ -35,17 +16,8 @@ class SetupParams with _$SetupParams {
_$SetupParamsFromJson(json);
}
// extension SetupParamsExt on SetupParams {
// Map<String, dynamic> get json {
// final json = Map<String, dynamic>.from(config);
// json["selected-map"] = selectedMap;
// json["test-url"] = testUrl;
// return json;
// }
// }
@freezed
class UpdateParams with _$UpdateParams {
abstract class UpdateParams with _$UpdateParams {
const factory UpdateParams({
required Tun tun,
@JsonKey(name: 'mixed-port') required int mixedPort,
@@ -66,39 +38,26 @@ class UpdateParams with _$UpdateParams {
}
@freezed
class CoreState with _$CoreState {
const factory CoreState({
@JsonKey(name: 'vpn-props') required VpnProps vpnProps,
@JsonKey(name: 'only-statistics-proxy') required bool onlyStatisticsProxy,
@JsonKey(name: 'current-profile-name') required String currentProfileName,
@JsonKey(name: 'bypass-domain') @Default([]) List<String> bypassDomain,
}) = _CoreState;
factory CoreState.fromJson(Map<String, Object?> json) =>
_$CoreStateFromJson(json);
}
@freezed
class AndroidVpnOptions with _$AndroidVpnOptions {
const factory AndroidVpnOptions({
abstract class VpnOptions with _$VpnOptions {
const factory VpnOptions({
required bool enable,
required int port,
required AccessControl? accessControl,
required bool ipv6,
required bool dnsHijacking,
required AccessControl accessControl,
required bool allowBypass,
required bool systemProxy,
required List<String> bypassDomain,
required String ipv4Address,
required String ipv6Address,
required String stack,
@Default([]) List<String> routeAddress,
required String dnsServerAddress,
}) = _AndroidVpnOptions;
}) = _VpnOptions;
factory AndroidVpnOptions.fromJson(Map<String, Object?> json) =>
_$AndroidVpnOptionsFromJson(json);
factory VpnOptions.fromJson(Map<String, Object?> json) =>
_$VpnOptionsFromJson(json);
}
@freezed
class InitParams with _$InitParams {
abstract class InitParams with _$InitParams {
const factory InitParams({
@JsonKey(name: 'home-dir') required String homeDir,
required int version,
@@ -109,7 +68,7 @@ class InitParams with _$InitParams {
}
@freezed
class ChangeProxyParams with _$ChangeProxyParams {
abstract class ChangeProxyParams with _$ChangeProxyParams {
const factory ChangeProxyParams({
@JsonKey(name: 'group-name') required String groupName,
@JsonKey(name: 'proxy-name') required String proxyName,
@@ -120,7 +79,7 @@ class ChangeProxyParams with _$ChangeProxyParams {
}
@freezed
class UpdateGeoDataParams with _$UpdateGeoDataParams {
abstract class UpdateGeoDataParams with _$UpdateGeoDataParams {
const factory UpdateGeoDataParams({
@JsonKey(name: 'geo-type') required String geoType,
@JsonKey(name: 'geo-name') required String geoName,
@@ -131,71 +90,40 @@ class UpdateGeoDataParams with _$UpdateGeoDataParams {
}
@freezed
class AppMessage with _$AppMessage {
const factory AppMessage({
required AppMessageType type,
dynamic data,
}) = _AppMessage;
abstract class CoreEvent with _$CoreEvent {
const factory CoreEvent({required CoreEventType type, dynamic data}) =
_CoreEvent;
factory AppMessage.fromJson(Map<String, Object?> json) =>
_$AppMessageFromJson(json);
factory CoreEvent.fromJson(Map<String, Object?> json) =>
_$CoreEventFromJson(json);
}
@freezed
class InvokeMessage with _$InvokeMessage {
const factory InvokeMessage({
required InvokeMessageType type,
dynamic data,
}) = _InvokeMessage;
abstract class InvokeMessage with _$InvokeMessage {
const factory InvokeMessage({required InvokeMessageType type, dynamic data}) =
_InvokeMessage;
factory InvokeMessage.fromJson(Map<String, Object?> json) =>
_$InvokeMessageFromJson(json);
}
@freezed
class Delay with _$Delay {
const factory Delay({
required String name,
required String url,
int? value,
}) = _Delay;
abstract class Delay with _$Delay {
const factory Delay({required String name, required String url, int? value}) =
_Delay;
factory Delay.fromJson(Map<String, Object?> json) => _$DelayFromJson(json);
}
@freezed
class Now with _$Now {
const factory Now({
required String name,
required String value,
}) = _Now;
abstract class Now with _$Now {
const factory Now({required String name, required String value}) = _Now;
factory Now.fromJson(Map<String, Object?> json) => _$NowFromJson(json);
}
// @freezed
// class ProcessData with _$ProcessData {
// const factory ProcessData({
// required String id,
// required Metadata metadata,
// }) = _ProcessData;
//
// factory ProcessData.fromJson(Map<String, Object?> json) =>
// _$ProcessDataFromJson(json);
// }
//
// @freezed
// class Fd with _$Fd {
// const factory Fd({
// required String id,
// required int value,
// }) = _Fd;
//
// factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);
// }
@freezed
class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
abstract class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
const factory ProviderSubscriptionInfo({
@JsonKey(name: 'UPLOAD') @Default(0) int upload,
@JsonKey(name: 'DOWNLOAD') @Default(0) int download,
@@ -218,7 +146,7 @@ SubscriptionInfo? subscriptionInfoFormCore(Map<String, Object?>? json) {
}
@freezed
class ExternalProvider with _$ExternalProvider {
abstract class ExternalProvider with _$ExternalProvider {
const factory ExternalProvider({
required String name,
required String type,
@@ -236,7 +164,7 @@ class ExternalProvider with _$ExternalProvider {
}
@freezed
class Action with _$Action {
abstract class Action with _$Action {
const factory Action({
required ActionMethod method,
required dynamic data,
@@ -247,7 +175,7 @@ class Action with _$Action {
}
@freezed
class ActionResult with _$ActionResult {
abstract class ActionResult with _$ActionResult {
const factory ActionResult({
required ActionMethod method,
required dynamic data,

View File

@@ -1,5 +1,5 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
@@ -9,614 +9,407 @@ part of '../app.dart';
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$AppState {
bool get isInit => throw _privateConstructorUsedError;
bool get backBlock => throw _privateConstructorUsedError;
PageLabel get pageLabel => throw _privateConstructorUsedError;
List<Package> get packages => throw _privateConstructorUsedError;
int get sortNum => throw _privateConstructorUsedError;
Size get viewSize => throw _privateConstructorUsedError;
Map<String, Map<String, int?>> get delayMap =>
throw _privateConstructorUsedError;
List<Group> get groups => throw _privateConstructorUsedError;
int get checkIpNum => throw _privateConstructorUsedError;
Brightness get brightness => throw _privateConstructorUsedError;
int? get runTime => throw _privateConstructorUsedError;
List<ExternalProvider> get providers => throw _privateConstructorUsedError;
String? get localIp => throw _privateConstructorUsedError;
FixedList<TrackerInfo> get requests => throw _privateConstructorUsedError;
int get version => throw _privateConstructorUsedError;
FixedList<Log> get logs => throw _privateConstructorUsedError;
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
Traffic get totalTraffic => throw _privateConstructorUsedError;
bool get realTunEnable => throw _privateConstructorUsedError;
bool get loading => throw _privateConstructorUsedError;
SystemUiOverlayStyle get systemUiOverlayStyle =>
throw _privateConstructorUsedError;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$AppStateCopyWith<AppState> get copyWith =>
throw _privateConstructorUsedError;
bool get isInit; bool get backBlock; PageLabel get pageLabel; List<Package> get packages; int get sortNum; Size get viewSize; double get sideWidth; DelayMap get delayMap; List<Group> get groups; int get checkIpNum; Brightness get brightness; int? get runTime; List<ExternalProvider> get providers; String? get localIp; FixedList<TrackerInfo> get requests; int get version; FixedList<Log> get logs; FixedList<Traffic> get traffics; Traffic get totalTraffic; bool get realTunEnable; bool get loading; SystemUiOverlayStyle get systemUiOverlayStyle; ProfileOverrideModel? get profileOverrideModel; Map<QueryTag, String> get queryMap; CoreStatus get coreStatus;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AppStateCopyWith<AppState> get copyWith => _$AppStateCopyWithImpl<AppState>(this as AppState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other.packages, packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other.delayMap, delayMap)&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other.providers, providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other.queryMap, queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
}
@override
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(delayMap),const DeepCollectionEquality().hash(groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(queryMap),coreStatus]);
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
}
}
/// @nodoc
abstract class $AppStateCopyWith<$Res> {
factory $AppStateCopyWith(AppState value, $Res Function(AppState) then) =
_$AppStateCopyWithImpl<$Res, AppState>;
@useResult
$Res call(
{bool isInit,
bool backBlock,
PageLabel pageLabel,
List<Package> packages,
int sortNum,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
List<Group> groups,
int checkIpNum,
Brightness brightness,
int? runTime,
List<ExternalProvider> providers,
String? localIp,
FixedList<TrackerInfo> requests,
int version,
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
bool realTunEnable,
bool loading,
SystemUiOverlayStyle systemUiOverlayStyle});
}
abstract mixin class $AppStateCopyWith<$Res> {
factory $AppStateCopyWith(AppState value, $Res Function(AppState) _then) = _$AppStateCopyWithImpl;
@useResult
$Res call({
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
});
$TrafficCopyWith<$Res> get totalTraffic;$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel;
}
/// @nodoc
class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
class _$AppStateCopyWithImpl<$Res>
implements $AppStateCopyWith<$Res> {
_$AppStateCopyWithImpl(this._value, this._then);
_$AppStateCopyWithImpl(this._self, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
final AppState _self;
final $Res Function(AppState) _then;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isInit = null,
Object? backBlock = null,
Object? pageLabel = null,
Object? packages = null,
Object? sortNum = null,
Object? viewSize = null,
Object? delayMap = null,
Object? groups = null,
Object? checkIpNum = null,
Object? brightness = null,
Object? runTime = freezed,
Object? providers = null,
Object? localIp = freezed,
Object? requests = null,
Object? version = null,
Object? logs = null,
Object? traffics = null,
Object? totalTraffic = null,
Object? realTunEnable = null,
Object? loading = null,
Object? systemUiOverlayStyle = null,
}) {
return _then(_value.copyWith(
isInit: null == isInit
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
backBlock: null == backBlock
? _value.backBlock
: backBlock // ignore: cast_nullable_to_non_nullable
as bool,
pageLabel: null == pageLabel
? _value.pageLabel
: pageLabel // ignore: cast_nullable_to_non_nullable
as PageLabel,
packages: null == packages
? _value.packages
: packages // ignore: cast_nullable_to_non_nullable
as List<Package>,
sortNum: null == sortNum
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
as int,
viewSize: null == viewSize
? _value.viewSize
: viewSize // ignore: cast_nullable_to_non_nullable
as Size,
delayMap: null == delayMap
? _value.delayMap
: delayMap // ignore: cast_nullable_to_non_nullable
as Map<String, Map<String, int?>>,
groups: null == groups
? _value.groups
: groups // ignore: cast_nullable_to_non_nullable
as List<Group>,
checkIpNum: null == checkIpNum
? _value.checkIpNum
: checkIpNum // ignore: cast_nullable_to_non_nullable
as int,
brightness: null == brightness
? _value.brightness
: brightness // ignore: cast_nullable_to_non_nullable
as Brightness,
runTime: freezed == runTime
? _value.runTime
: runTime // ignore: cast_nullable_to_non_nullable
as int?,
providers: null == providers
? _value.providers
: providers // ignore: cast_nullable_to_non_nullable
as List<ExternalProvider>,
localIp: freezed == localIp
? _value.localIp
: localIp // ignore: cast_nullable_to_non_nullable
as String?,
requests: null == requests
? _value.requests
: requests // ignore: cast_nullable_to_non_nullable
as FixedList<TrackerInfo>,
version: null == version
? _value.version
: version // ignore: cast_nullable_to_non_nullable
as int,
logs: null == logs
? _value.logs
: logs // ignore: cast_nullable_to_non_nullable
as FixedList<Log>,
traffics: null == traffics
? _value.traffics
: traffics // ignore: cast_nullable_to_non_nullable
as FixedList<Traffic>,
totalTraffic: null == totalTraffic
? _value.totalTraffic
: totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,
realTunEnable: null == realTunEnable
? _value.realTunEnable
: realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
systemUiOverlayStyle: null == systemUiOverlayStyle
? _value.systemUiOverlayStyle
: systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
as SystemUiOverlayStyle,
) as $Val);
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
return _then(_self.copyWith(
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable
as PageLabel,packages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
as double,delayMap: null == delayMap ? _self.delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as DelayMap,groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable
as Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable
as int?,providers: null == providers ? _self.providers : providers // ignore: cast_nullable_to_non_nullable
as List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable
as String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable
as FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
as int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable
as FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable
as FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable
as bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
as SystemUiOverlayStyle,profileOverrideModel: freezed == profileOverrideModel ? _self.profileOverrideModel : profileOverrideModel // ignore: cast_nullable_to_non_nullable
as ProfileOverrideModel?,queryMap: null == queryMap ? _self.queryMap : queryMap // ignore: cast_nullable_to_non_nullable
as Map<QueryTag, String>,coreStatus: null == coreStatus ? _self.coreStatus : coreStatus // ignore: cast_nullable_to_non_nullable
as CoreStatus,
));
}
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TrafficCopyWith<$Res> get totalTraffic {
return $TrafficCopyWith<$Res>(_self.totalTraffic, (value) {
return _then(_self.copyWith(totalTraffic: value));
});
}/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel {
if (_self.profileOverrideModel == null) {
return null;
}
return $ProfileOverrideModelCopyWith<$Res>(_self.profileOverrideModel!, (value) {
return _then(_self.copyWith(profileOverrideModel: value));
});
}
}
/// @nodoc
abstract class _$$AppStateImplCopyWith<$Res>
implements $AppStateCopyWith<$Res> {
factory _$$AppStateImplCopyWith(
_$AppStateImpl value, $Res Function(_$AppStateImpl) then) =
__$$AppStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool isInit,
bool backBlock,
PageLabel pageLabel,
List<Package> packages,
int sortNum,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
List<Group> groups,
int checkIpNum,
Brightness brightness,
int? runTime,
List<ExternalProvider> providers,
String? localIp,
FixedList<TrackerInfo> requests,
int version,
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
bool realTunEnable,
bool loading,
SystemUiOverlayStyle systemUiOverlayStyle});
/// Adds pattern-matching-related methods to [AppState].
extension AppStatePatterns on AppState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppState value) $default,){
final _that = this;
switch (_that) {
case _AppState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppState value)? $default,){
final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus) $default,) {final _that = this;
switch (_that) {
case _AppState():
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,) {final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return null;
}
}
/// @nodoc
class __$$AppStateImplCopyWithImpl<$Res>
extends _$AppStateCopyWithImpl<$Res, _$AppStateImpl>
implements _$$AppStateImplCopyWith<$Res> {
__$$AppStateImplCopyWithImpl(
_$AppStateImpl _value, $Res Function(_$AppStateImpl) _then)
: super(_value, _then);
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isInit = null,
Object? backBlock = null,
Object? pageLabel = null,
Object? packages = null,
Object? sortNum = null,
Object? viewSize = null,
Object? delayMap = null,
Object? groups = null,
Object? checkIpNum = null,
Object? brightness = null,
Object? runTime = freezed,
Object? providers = null,
Object? localIp = freezed,
Object? requests = null,
Object? version = null,
Object? logs = null,
Object? traffics = null,
Object? totalTraffic = null,
Object? realTunEnable = null,
Object? loading = null,
Object? systemUiOverlayStyle = null,
}) {
return _then(_$AppStateImpl(
isInit: null == isInit
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
backBlock: null == backBlock
? _value.backBlock
: backBlock // ignore: cast_nullable_to_non_nullable
as bool,
pageLabel: null == pageLabel
? _value.pageLabel
: pageLabel // ignore: cast_nullable_to_non_nullable
as PageLabel,
packages: null == packages
? _value._packages
: packages // ignore: cast_nullable_to_non_nullable
as List<Package>,
sortNum: null == sortNum
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
as int,
viewSize: null == viewSize
? _value.viewSize
: viewSize // ignore: cast_nullable_to_non_nullable
as Size,
delayMap: null == delayMap
? _value._delayMap
: delayMap // ignore: cast_nullable_to_non_nullable
as Map<String, Map<String, int?>>,
groups: null == groups
? _value._groups
: groups // ignore: cast_nullable_to_non_nullable
as List<Group>,
checkIpNum: null == checkIpNum
? _value.checkIpNum
: checkIpNum // ignore: cast_nullable_to_non_nullable
as int,
brightness: null == brightness
? _value.brightness
: brightness // ignore: cast_nullable_to_non_nullable
as Brightness,
runTime: freezed == runTime
? _value.runTime
: runTime // ignore: cast_nullable_to_non_nullable
as int?,
providers: null == providers
? _value._providers
: providers // ignore: cast_nullable_to_non_nullable
as List<ExternalProvider>,
localIp: freezed == localIp
? _value.localIp
: localIp // ignore: cast_nullable_to_non_nullable
as String?,
requests: null == requests
? _value.requests
: requests // ignore: cast_nullable_to_non_nullable
as FixedList<TrackerInfo>,
version: null == version
? _value.version
: version // ignore: cast_nullable_to_non_nullable
as int,
logs: null == logs
? _value.logs
: logs // ignore: cast_nullable_to_non_nullable
as FixedList<Log>,
traffics: null == traffics
? _value.traffics
: traffics // ignore: cast_nullable_to_non_nullable
as FixedList<Traffic>,
totalTraffic: null == totalTraffic
? _value.totalTraffic
: totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,
realTunEnable: null == realTunEnable
? _value.realTunEnable
: realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
systemUiOverlayStyle: null == systemUiOverlayStyle
? _value.systemUiOverlayStyle
: systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
as SystemUiOverlayStyle,
));
}
}
/// @nodoc
class _$AppStateImpl implements _AppState {
const _$AppStateImpl(
{this.isInit = false,
this.backBlock = false,
this.pageLabel = PageLabel.dashboard,
final List<Package> packages = const [],
this.sortNum = 0,
required this.viewSize,
final Map<String, Map<String, int?>> delayMap = const {},
final List<Group> groups = const [],
this.checkIpNum = 0,
required this.brightness,
this.runTime,
final List<ExternalProvider> providers = const [],
this.localIp,
required this.requests,
required this.version,
required this.logs,
required this.traffics,
required this.totalTraffic,
this.realTunEnable = false,
this.loading = false,
required this.systemUiOverlayStyle})
: _packages = packages,
_delayMap = delayMap,
_groups = groups,
_providers = providers;
@override
@JsonKey()
final bool isInit;
@override
@JsonKey()
final bool backBlock;
@override
@JsonKey()
final PageLabel pageLabel;
final List<Package> _packages;
@override
@JsonKey()
List<Package> get packages {
if (_packages is EqualUnmodifiableListView) return _packages;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_packages);
}
class _AppState implements AppState {
const _AppState({this.isInit = false, this.backBlock = false, this.pageLabel = PageLabel.dashboard, final List<Package> packages = const [], this.sortNum = 0, required this.viewSize, this.sideWidth = 0, final DelayMap delayMap = const {}, final List<Group> groups = const [], this.checkIpNum = 0, required this.brightness, this.runTime, final List<ExternalProvider> providers = const [], this.localIp, required this.requests, required this.version, required this.logs, required this.traffics, required this.totalTraffic, this.realTunEnable = false, this.loading = false, required this.systemUiOverlayStyle, this.profileOverrideModel, final Map<QueryTag, String> queryMap = const {}, this.coreStatus = CoreStatus.connecting}): _packages = packages,_delayMap = delayMap,_groups = groups,_providers = providers,_queryMap = queryMap;
@override
@JsonKey()
final int sortNum;
@override
final Size viewSize;
final Map<String, Map<String, int?>> _delayMap;
@override
@JsonKey()
Map<String, Map<String, int?>> get delayMap {
if (_delayMap is EqualUnmodifiableMapView) return _delayMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_delayMap);
}
final List<Group> _groups;
@override
@JsonKey()
List<Group> get groups {
if (_groups is EqualUnmodifiableListView) return _groups;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_groups);
}
@override
@JsonKey()
final int checkIpNum;
@override
final Brightness brightness;
@override
final int? runTime;
final List<ExternalProvider> _providers;
@override
@JsonKey()
List<ExternalProvider> get providers {
if (_providers is EqualUnmodifiableListView) return _providers;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_providers);
}
@override
final String? localIp;
@override
final FixedList<TrackerInfo> requests;
@override
final int version;
@override
final FixedList<Log> logs;
@override
final FixedList<Traffic> traffics;
@override
final Traffic totalTraffic;
@override
@JsonKey()
final bool realTunEnable;
@override
@JsonKey()
final bool loading;
@override
final SystemUiOverlayStyle systemUiOverlayStyle;
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$AppStateImpl &&
(identical(other.isInit, isInit) || other.isInit == isInit) &&
(identical(other.backBlock, backBlock) ||
other.backBlock == backBlock) &&
(identical(other.pageLabel, pageLabel) ||
other.pageLabel == pageLabel) &&
const DeepCollectionEquality().equals(other._packages, _packages) &&
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
(identical(other.viewSize, viewSize) ||
other.viewSize == viewSize) &&
const DeepCollectionEquality().equals(other._delayMap, _delayMap) &&
const DeepCollectionEquality().equals(other._groups, _groups) &&
(identical(other.checkIpNum, checkIpNum) ||
other.checkIpNum == checkIpNum) &&
(identical(other.brightness, brightness) ||
other.brightness == brightness) &&
(identical(other.runTime, runTime) || other.runTime == runTime) &&
const DeepCollectionEquality()
.equals(other._providers, _providers) &&
(identical(other.localIp, localIp) || other.localIp == localIp) &&
(identical(other.requests, requests) ||
other.requests == requests) &&
(identical(other.version, version) || other.version == version) &&
(identical(other.logs, logs) || other.logs == logs) &&
(identical(other.traffics, traffics) ||
other.traffics == traffics) &&
(identical(other.totalTraffic, totalTraffic) ||
other.totalTraffic == totalTraffic) &&
(identical(other.realTunEnable, realTunEnable) ||
other.realTunEnable == realTunEnable) &&
(identical(other.loading, loading) || other.loading == loading) &&
(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) ||
other.systemUiOverlayStyle == systemUiOverlayStyle));
}
@override
int get hashCode => Object.hashAll([
runtimeType,
isInit,
backBlock,
pageLabel,
const DeepCollectionEquality().hash(_packages),
sortNum,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
brightness,
runTime,
const DeepCollectionEquality().hash(_providers),
localIp,
requests,
version,
logs,
traffics,
totalTraffic,
realTunEnable,
loading,
systemUiOverlayStyle
]);
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$AppStateImplCopyWith<_$AppStateImpl> get copyWith =>
__$$AppStateImplCopyWithImpl<_$AppStateImpl>(this, _$identity);
@override@JsonKey() final bool isInit;
@override@JsonKey() final bool backBlock;
@override@JsonKey() final PageLabel pageLabel;
final List<Package> _packages;
@override@JsonKey() List<Package> get packages {
if (_packages is EqualUnmodifiableListView) return _packages;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_packages);
}
abstract class _AppState implements AppState {
const factory _AppState(
{final bool isInit,
final bool backBlock,
final PageLabel pageLabel,
final List<Package> packages,
final int sortNum,
required final Size viewSize,
final Map<String, Map<String, int?>> delayMap,
final List<Group> groups,
final int checkIpNum,
required final Brightness brightness,
final int? runTime,
final List<ExternalProvider> providers,
final String? localIp,
required final FixedList<TrackerInfo> requests,
required final int version,
required final FixedList<Log> logs,
required final FixedList<Traffic> traffics,
required final Traffic totalTraffic,
final bool realTunEnable,
final bool loading,
required final SystemUiOverlayStyle systemUiOverlayStyle}) =
_$AppStateImpl;
@override
bool get isInit;
@override
bool get backBlock;
@override
PageLabel get pageLabel;
@override
List<Package> get packages;
@override
int get sortNum;
@override
Size get viewSize;
@override
Map<String, Map<String, int?>> get delayMap;
@override
List<Group> get groups;
@override
int get checkIpNum;
@override
Brightness get brightness;
@override
int? get runTime;
@override
List<ExternalProvider> get providers;
@override
String? get localIp;
@override
FixedList<TrackerInfo> get requests;
@override
int get version;
@override
FixedList<Log> get logs;
@override
FixedList<Traffic> get traffics;
@override
Traffic get totalTraffic;
@override
bool get realTunEnable;
@override
bool get loading;
@override
SystemUiOverlayStyle get systemUiOverlayStyle;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$AppStateImplCopyWith<_$AppStateImpl> get copyWith =>
throw _privateConstructorUsedError;
@override@JsonKey() final int sortNum;
@override final Size viewSize;
@override@JsonKey() final double sideWidth;
final DelayMap _delayMap;
@override@JsonKey() DelayMap get delayMap {
if (_delayMap is EqualUnmodifiableMapView) return _delayMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_delayMap);
}
final List<Group> _groups;
@override@JsonKey() List<Group> get groups {
if (_groups is EqualUnmodifiableListView) return _groups;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_groups);
}
@override@JsonKey() final int checkIpNum;
@override final Brightness brightness;
@override final int? runTime;
final List<ExternalProvider> _providers;
@override@JsonKey() List<ExternalProvider> get providers {
if (_providers is EqualUnmodifiableListView) return _providers;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_providers);
}
@override final String? localIp;
@override final FixedList<TrackerInfo> requests;
@override final int version;
@override final FixedList<Log> logs;
@override final FixedList<Traffic> traffics;
@override final Traffic totalTraffic;
@override@JsonKey() final bool realTunEnable;
@override@JsonKey() final bool loading;
@override final SystemUiOverlayStyle systemUiOverlayStyle;
@override final ProfileOverrideModel? profileOverrideModel;
final Map<QueryTag, String> _queryMap;
@override@JsonKey() Map<QueryTag, String> get queryMap {
if (_queryMap is EqualUnmodifiableMapView) return _queryMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_queryMap);
}
@override@JsonKey() final CoreStatus coreStatus;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$AppStateCopyWith<_AppState> get copyWith => __$AppStateCopyWithImpl<_AppState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other._packages, _packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other._delayMap, _delayMap)&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other._providers, _providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other._queryMap, _queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
}
@override
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(_packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(_delayMap),const DeepCollectionEquality().hash(_groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(_providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(_queryMap),coreStatus]);
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
}
}
/// @nodoc
abstract mixin class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
factory _$AppStateCopyWith(_AppState value, $Res Function(_AppState) _then) = __$AppStateCopyWithImpl;
@override @useResult
$Res call({
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
});
@override $TrafficCopyWith<$Res> get totalTraffic;@override $ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel;
}
/// @nodoc
class __$AppStateCopyWithImpl<$Res>
implements _$AppStateCopyWith<$Res> {
__$AppStateCopyWithImpl(this._self, this._then);
final _AppState _self;
final $Res Function(_AppState) _then;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
return _then(_AppState(
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable
as PageLabel,packages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
as double,delayMap: null == delayMap ? _self._delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as DelayMap,groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable
as Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable
as int?,providers: null == providers ? _self._providers : providers // ignore: cast_nullable_to_non_nullable
as List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable
as String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable
as FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
as int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable
as FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable
as FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable
as bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
as SystemUiOverlayStyle,profileOverrideModel: freezed == profileOverrideModel ? _self.profileOverrideModel : profileOverrideModel // ignore: cast_nullable_to_non_nullable
as ProfileOverrideModel?,queryMap: null == queryMap ? _self._queryMap : queryMap // ignore: cast_nullable_to_non_nullable
as Map<QueryTag, String>,coreStatus: null == coreStatus ? _self.coreStatus : coreStatus // ignore: cast_nullable_to_non_nullable
as CoreStatus,
));
}
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TrafficCopyWith<$Res> get totalTraffic {
return $TrafficCopyWith<$Res>(_self.totalTraffic, (value) {
return _then(_self.copyWith(totalTraffic: value));
});
}/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel {
if (_self.profileOverrideModel == null) {
return null;
}
return $ProfileOverrideModelCopyWith<$Res>(_self.profileOverrideModel!, (value) {
return _then(_self.copyWith(profileOverrideModel: value));
});
}
}
// dart format on

File diff suppressed because it is too large Load Diff

View File

@@ -6,27 +6,27 @@ part of '../clash_config.dart';
// JsonSerializableGenerator
// **************************************************************************
_$ProxyGroupImpl _$$ProxyGroupImplFromJson(Map<String, dynamic> json) =>
_$ProxyGroupImpl(
name: json['name'] as String,
type: GroupType.parseProfileType(json['type'] as String),
proxies:
(json['proxies'] as List<dynamic>?)?.map((e) => e as String).toList(),
use: (json['use'] as List<dynamic>?)?.map((e) => e as String).toList(),
interval: (json['interval'] as num?)?.toInt(),
lazy: json['lazy'] as bool?,
url: json['url'] as String?,
timeout: (json['timeout'] as num?)?.toInt(),
maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(),
filter: json['filter'] as String?,
excludeFilter: json['expected-filter'] as String?,
excludeType: json['exclude-type'] as String?,
expectedStatus: json['expected-status'],
hidden: json['hidden'] as bool?,
icon: json['icon'] as String?,
);
_ProxyGroup _$ProxyGroupFromJson(Map<String, dynamic> json) => _ProxyGroup(
name: json['name'] as String,
type: GroupType.parseProfileType(json['type'] as String),
proxies: (json['proxies'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
use: (json['use'] as List<dynamic>?)?.map((e) => e as String).toList(),
interval: (json['interval'] as num?)?.toInt(),
lazy: json['lazy'] as bool?,
url: json['url'] as String?,
timeout: (json['timeout'] as num?)?.toInt(),
maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(),
filter: json['filter'] as String?,
excludeFilter: json['expected-filter'] as String?,
excludeType: json['exclude-type'] as String?,
expectedStatus: json['expected-status'],
hidden: json['hidden'] as bool?,
icon: json['icon'] as String?,
);
Map<String, dynamic> _$$ProxyGroupImplToJson(_$ProxyGroupImpl instance) =>
Map<String, dynamic> _$ProxyGroupToJson(_ProxyGroup instance) =>
<String, dynamic>{
'name': instance.name,
'type': _$GroupTypeEnumMap[instance.type]!,
@@ -53,106 +53,107 @@ const _$GroupTypeEnumMap = {
GroupType.Relay: 'Relay',
};
_$RuleProviderImpl _$$RuleProviderImplFromJson(Map<String, dynamic> json) =>
_$RuleProviderImpl(
name: json['name'] as String,
);
_RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) =>
_RuleProvider(name: json['name'] as String);
Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
<String, dynamic>{
'name': instance.name,
};
Map<String, dynamic> _$RuleProviderToJson(_RuleProvider instance) =>
<String, dynamic>{'name': instance.name};
_$SnifferImpl _$$SnifferImplFromJson(Map<String, dynamic> json) =>
_$SnifferImpl(
enable: json['enable'] as bool? ?? false,
overrideDest: json['override-destination'] as bool? ?? true,
sniffing: (json['sniffing'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
forceDomain: (json['force-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipSrcAddress: (json['skip-src-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDstAddress: (json['skip-dst-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDomain: (json['skip-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
port: (json['port-whitelist'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,
parsePureIp: json['parse-pure-ip'] as bool? ?? true,
sniff: (json['sniff'] as Map<String, dynamic>?)?.map(
(k, e) =>
MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),
) ??
const {},
);
_Sniffer _$SnifferFromJson(Map<String, dynamic> json) => _Sniffer(
enable: json['enable'] as bool? ?? false,
overrideDest: json['override-destination'] as bool? ?? true,
sniffing:
(json['sniffing'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [],
forceDomain:
(json['force-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipSrcAddress:
(json['skip-src-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDstAddress:
(json['skip-dst-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDomain:
(json['skip-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
port:
(json['port-whitelist'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,
parsePureIp: json['parse-pure-ip'] as bool? ?? true,
sniff:
(json['sniff'] as Map<String, dynamic>?)?.map(
(k, e) =>
MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),
) ??
const {},
);
Map<String, dynamic> _$$SnifferImplToJson(_$SnifferImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'override-destination': instance.overrideDest,
'sniffing': instance.sniffing,
'force-domain': instance.forceDomain,
'skip-src-address': instance.skipSrcAddress,
'skip-dst-address': instance.skipDstAddress,
'skip-domain': instance.skipDomain,
'port-whitelist': instance.port,
'force-dns-mapping': instance.forceDnsMapping,
'parse-pure-ip': instance.parsePureIp,
'sniff': instance.sniff,
};
Map<String, dynamic> _$SnifferToJson(_Sniffer instance) => <String, dynamic>{
'enable': instance.enable,
'override-destination': instance.overrideDest,
'sniffing': instance.sniffing,
'force-domain': instance.forceDomain,
'skip-src-address': instance.skipSrcAddress,
'skip-dst-address': instance.skipDstAddress,
'skip-domain': instance.skipDomain,
'port-whitelist': instance.port,
'force-dns-mapping': instance.forceDnsMapping,
'parse-pure-ip': instance.parsePureIp,
'sniff': instance.sniff,
};
_$SnifferConfigImpl _$$SnifferConfigImplFromJson(Map<String, dynamic> json) =>
_$SnifferConfigImpl(
_SnifferConfig _$SnifferConfigFromJson(Map<String, dynamic> json) =>
_SnifferConfig(
ports: json['ports'] == null
? const []
: _formJsonPorts(json['ports'] as List?),
overrideDest: json['override-destination'] as bool?,
);
Map<String, dynamic> _$$SnifferConfigImplToJson(_$SnifferConfigImpl instance) =>
Map<String, dynamic> _$SnifferConfigToJson(_SnifferConfig instance) =>
<String, dynamic>{
'ports': instance.ports,
'override-destination': instance.overrideDest,
};
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName,
autoRoute: json['auto-route'] as bool? ?? false,
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
TunStack.mixed,
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['any:53'],
routeAddress: (json['route-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
);
_Tun _$TunFromJson(Map<String, dynamic> json) => _Tun(
enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName,
autoRoute: json['auto-route'] as bool? ?? false,
stack:
$enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? TunStack.mixed,
dnsHijack:
(json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['any:53'],
routeAddress:
(json['route-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
);
Map<String, dynamic> _$$TunImplToJson(_$TunImpl instance) => <String, dynamic>{
'enable': instance.enable,
'device': instance.device,
'auto-route': instance.autoRoute,
'stack': _$TunStackEnumMap[instance.stack]!,
'dns-hijack': instance.dnsHijack,
'route-address': instance.routeAddress,
};
Map<String, dynamic> _$TunToJson(_Tun instance) => <String, dynamic>{
'enable': instance.enable,
'device': instance.device,
'auto-route': instance.autoRoute,
'stack': _$TunStackEnumMap[instance.stack]!,
'dns-hijack': instance.dnsHijack,
'route-address': instance.routeAddress,
};
const _$TunStackEnumMap = {
TunStack.gvisor: 'gvisor',
@@ -160,26 +161,23 @@ const _$TunStackEnumMap = {
TunStack.mixed: 'mixed',
};
_$FallbackFilterImpl _$$FallbackFilterImplFromJson(Map<String, dynamic> json) =>
_$FallbackFilterImpl(
geoip: json['geoip'] as bool? ?? true,
geoipCode: json['geoip-code'] as String? ?? 'CN',
geosite: (json['geosite'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['gfw'],
ipcidr: (json['ipcidr'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['240.0.0.0/4'],
domain: (json['domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['+.google.com', '+.facebook.com', '+.youtube.com'],
);
_FallbackFilter _$FallbackFilterFromJson(
Map<String, dynamic> json,
) => _FallbackFilter(
geoip: json['geoip'] as bool? ?? true,
geoipCode: json['geoip-code'] as String? ?? 'CN',
geosite:
(json['geosite'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const ['gfw'],
ipcidr:
(json['ipcidr'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const ['240.0.0.0/4'],
domain:
(json['domain'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const ['+.google.com', '+.facebook.com', '+.youtube.com'],
);
Map<String, dynamic> _$$FallbackFilterImplToJson(
_$FallbackFilterImpl instance) =>
Map<String, dynamic> _$FallbackFilterToJson(_FallbackFilter instance) =>
<String, dynamic>{
'geoip': instance.geoip,
'geoip-code': instance.geoipCode,
@@ -188,74 +186,75 @@ Map<String, dynamic> _$$FallbackFilterImplToJson(
'domain': instance.domain,
};
_$DnsImpl _$$DnsImplFromJson(Map<String, dynamic> json) => _$DnsImpl(
enable: json['enable'] as bool? ?? true,
listen: json['listen'] as String? ?? '0.0.0.0:1053',
preferH3: json['prefer-h3'] as bool? ?? false,
useHosts: json['use-hosts'] as bool? ?? true,
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
respectRules: json['respect-rules'] as bool? ?? false,
ipv6: json['ipv6'] as bool? ?? false,
defaultNameserver: (json['default-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['223.5.5.5'],
enhancedMode:
$enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??
DnsMode.fakeIp,
fakeIpRange: json['fake-ip-range'] as String? ?? '198.18.0.1/16',
fakeIpFilter: (json['fake-ip-filter'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['*.lan', 'localhost.ptlogin2.qq.com'],
nameserverPolicy:
(json['nameserver-policy'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {
'www.baidu.com': '114.114.114.114',
'+.internal.crop.com': '10.0.0.1',
'geosite:cn': 'https://doh.pub/dns-query'
},
nameserver: (json['nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [
'https://doh.pub/dns-query',
'https://dns.alidns.com/dns-query'
],
fallback: (json['fallback'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['tls://8.8.4.4', 'tls://1.1.1.1'],
proxyServerNameserver: (json['proxy-server-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['https://doh.pub/dns-query'],
fallbackFilter: json['fallback-filter'] == null
? const FallbackFilter()
: FallbackFilter.fromJson(
json['fallback-filter'] as Map<String, dynamic>),
);
_Dns _$DnsFromJson(Map<String, dynamic> json) => _Dns(
enable: json['enable'] as bool? ?? true,
listen: json['listen'] as String? ?? '0.0.0.0:1053',
preferH3: json['prefer-h3'] as bool? ?? false,
useHosts: json['use-hosts'] as bool? ?? true,
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
respectRules: json['respect-rules'] as bool? ?? false,
ipv6: json['ipv6'] as bool? ?? false,
defaultNameserver:
(json['default-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['223.5.5.5'],
enhancedMode:
$enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??
DnsMode.fakeIp,
fakeIpRange: json['fake-ip-range'] as String? ?? '198.18.0.1/16',
fakeIpFilter:
(json['fake-ip-filter'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['*.lan', 'localhost.ptlogin2.qq.com'],
nameserverPolicy:
(json['nameserver-policy'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {
'www.baidu.com': '114.114.114.114',
'+.internal.crop.com': '10.0.0.1',
'geosite:cn': 'https://doh.pub/dns-query',
},
nameserver:
(json['nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
fallback:
(json['fallback'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const ['tls://8.8.4.4', 'tls://1.1.1.1'],
proxyServerNameserver:
(json['proxy-server-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ['https://doh.pub/dns-query'],
fallbackFilter: json['fallback-filter'] == null
? const FallbackFilter()
: FallbackFilter.fromJson(
json['fallback-filter'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$$DnsImplToJson(_$DnsImpl instance) => <String, dynamic>{
'enable': instance.enable,
'listen': instance.listen,
'prefer-h3': instance.preferH3,
'use-hosts': instance.useHosts,
'use-system-hosts': instance.useSystemHosts,
'respect-rules': instance.respectRules,
'ipv6': instance.ipv6,
'default-nameserver': instance.defaultNameserver,
'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,
'fake-ip-range': instance.fakeIpRange,
'fake-ip-filter': instance.fakeIpFilter,
'nameserver-policy': instance.nameserverPolicy,
'nameserver': instance.nameserver,
'fallback': instance.fallback,
'proxy-server-nameserver': instance.proxyServerNameserver,
'fallback-filter': instance.fallbackFilter,
};
Map<String, dynamic> _$DnsToJson(_Dns instance) => <String, dynamic>{
'enable': instance.enable,
'listen': instance.listen,
'prefer-h3': instance.preferH3,
'use-hosts': instance.useHosts,
'use-system-hosts': instance.useSystemHosts,
'respect-rules': instance.respectRules,
'ipv6': instance.ipv6,
'default-nameserver': instance.defaultNameserver,
'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,
'fake-ip-range': instance.fakeIpRange,
'fake-ip-filter': instance.fakeIpFilter,
'nameserver-policy': instance.nameserverPolicy,
'nameserver': instance.nameserver,
'fallback': instance.fallback,
'proxy-server-nameserver': instance.proxyServerNameserver,
'fallback-filter': instance.fallbackFilter,
};
const _$DnsModeEnumMap = {
DnsMode.normal: 'normal',
@@ -264,51 +263,47 @@ const _$DnsModeEnumMap = {
DnsMode.hosts: 'hosts',
};
_$GeoXUrlImpl _$$GeoXUrlImplFromJson(Map<String, dynamic> json) =>
_$GeoXUrlImpl(
mmdb: json['mmdb'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
asn: json['asn'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
geoip: json['geoip'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
geosite: json['geosite'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
);
_GeoXUrl _$GeoXUrlFromJson(Map<String, dynamic> json) => _GeoXUrl(
mmdb:
json['mmdb'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
asn:
json['asn'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
geoip:
json['geoip'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
geosite:
json['geosite'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
);
Map<String, dynamic> _$$GeoXUrlImplToJson(_$GeoXUrlImpl instance) =>
<String, dynamic>{
'mmdb': instance.mmdb,
'asn': instance.asn,
'geoip': instance.geoip,
'geosite': instance.geosite,
};
Map<String, dynamic> _$GeoXUrlToJson(_GeoXUrl instance) => <String, dynamic>{
'mmdb': instance.mmdb,
'asn': instance.asn,
'geoip': instance.geoip,
'geosite': instance.geosite,
};
_$RuleImpl _$$RuleImplFromJson(Map<String, dynamic> json) => _$RuleImpl(
id: json['id'] as String,
value: json['value'] as String,
);
_Rule _$RuleFromJson(Map<String, dynamic> json) =>
_Rule(id: json['id'] as String, value: json['value'] as String);
Map<String, dynamic> _$$RuleImplToJson(_$RuleImpl instance) =>
<String, dynamic>{
'id': instance.id,
'value': instance.value,
};
Map<String, dynamic> _$RuleToJson(_Rule instance) => <String, dynamic>{
'id': instance.id,
'value': instance.value,
};
_$SubRuleImpl _$$SubRuleImplFromJson(Map<String, dynamic> json) =>
_$SubRuleImpl(
name: json['name'] as String,
);
_SubRule _$SubRuleFromJson(Map<String, dynamic> json) =>
_SubRule(name: json['name'] as String);
Map<String, dynamic> _$$SubRuleImplToJson(_$SubRuleImpl instance) =>
<String, dynamic>{
'name': instance.name,
};
Map<String, dynamic> _$SubRuleToJson(_SubRule instance) => <String, dynamic>{
'name': instance.name,
};
_$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
Map<String, dynamic> json) =>
_$ClashConfigSnippetImpl(
proxyGroups: (json['proxy-groups'] as List<dynamic>?)
_ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) =>
_ClashConfigSnippet(
proxyGroups:
(json['proxy-groups'] as List<dynamic>?)
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
@@ -321,8 +316,7 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
: _genSubRules(json['sub-rules'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
_$ClashConfigSnippetImpl instance) =>
Map<String, dynamic> _$ClashConfigSnippetToJson(_ClashConfigSnippet instance) =>
<String, dynamic>{
'proxy-groups': instance.proxyGroups,
'rules': instance.rule,
@@ -330,56 +324,65 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
'sub-rules': instance.subRules,
};
_$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>
_$ClashConfigImpl(
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
port: (json['port'] as num?)?.toInt() ?? 0,
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
allowLan: json['allow-lan'] as bool? ?? false,
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
LogLevel.error,
ipv6: json['ipv6'] as bool? ?? false,
findProcessMode: $enumDecodeNullable(
_$FindProcessModeEnumMap, json['find-process-mode'],
unknownValue: FindProcessMode.always) ??
FindProcessMode.off,
keepAliveInterval: (json['keep-alive-interval'] as num?)?.toInt() ??
defaultKeepAliveInterval,
unifiedDelay: json['unified-delay'] as bool? ?? true,
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
tun: json['tun'] == null
? defaultTun
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
dns: json['dns'] == null
? defaultDns
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
geoXUrl: json['geox-url'] == null
? defaultGeoXUrl
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
geodataLoader:
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
GeodataLoader.memconservative,
proxyGroups: (json['proxy-groups'] as List<dynamic>?)
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule:
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [],
globalUa: json['global-ua'] as String?,
externalController: $enumDecodeNullable(
_$ExternalControllerStatusEnumMap, json['external-controller']) ??
ExternalControllerStatus.close,
hosts: (json['hosts'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
);
_ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => _ClashConfig(
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
port: (json['port'] as num?)?.toInt() ?? 0,
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
allowLan: json['allow-lan'] as bool? ?? false,
logLevel:
$enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
LogLevel.error,
ipv6: json['ipv6'] as bool? ?? false,
findProcessMode:
$enumDecodeNullable(
_$FindProcessModeEnumMap,
json['find-process-mode'],
unknownValue: FindProcessMode.always,
) ??
FindProcessMode.always,
keepAliveInterval:
(json['keep-alive-interval'] as num?)?.toInt() ??
defaultKeepAliveInterval,
unifiedDelay: json['unified-delay'] as bool? ?? true,
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
tun: json['tun'] == null
? defaultTun
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
dns: json['dns'] == null
? defaultDns
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
geoXUrl: json['geox-url'] == null
? defaultGeoXUrl
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
geodataLoader:
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
GeodataLoader.memconservative,
proxyGroups:
(json['proxy-groups'] as List<dynamic>?)
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule:
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [],
globalUa: json['global-ua'] as String?,
externalController:
$enumDecodeNullable(
_$ExternalControllerStatusEnumMap,
json['external-controller'],
) ??
ExternalControllerStatus.close,
hosts:
(json['hosts'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
);
Map<String, dynamic> _$$ClashConfigImplToJson(_$ClashConfigImpl instance) =>
Map<String, dynamic> _$ClashConfigToJson(_ClashConfig instance) =>
<String, dynamic>{
'mixed-port': instance.mixedPort,
'socks-port': instance.socksPort,

File diff suppressed because it is too large Load Diff

View File

@@ -6,71 +6,69 @@ part of '../common.dart';
// JsonSerializableGenerator
// **************************************************************************
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
_$PackageImpl(
packageName: json['packageName'] as String,
label: json['label'] as String,
system: json['system'] as bool,
internet: json['internet'] as bool,
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
);
_Package _$PackageFromJson(Map<String, dynamic> json) => _Package(
packageName: json['packageName'] as String,
label: json['label'] as String,
system: json['system'] as bool,
internet: json['internet'] as bool,
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
);
Map<String, dynamic> _$$PackageImplToJson(_$PackageImpl instance) =>
<String, dynamic>{
'packageName': instance.packageName,
'label': instance.label,
'system': instance.system,
'internet': instance.internet,
'lastUpdateTime': instance.lastUpdateTime,
};
Map<String, dynamic> _$PackageToJson(_Package instance) => <String, dynamic>{
'packageName': instance.packageName,
'label': instance.label,
'system': instance.system,
'internet': instance.internet,
'lastUpdateTime': instance.lastUpdateTime,
};
_$MetadataImpl _$$MetadataImplFromJson(Map<String, dynamic> json) =>
_$MetadataImpl(
uid: (json['uid'] as num?)?.toInt() ?? 0,
network: json['network'] as String? ?? '',
sourceIP: json['sourceIP'] as String? ?? '',
sourcePort: json['sourcePort'] as String? ?? '',
destinationIP: json['destinationIP'] as String? ?? '',
destinationPort: json['destinationPort'] as String? ?? '',
host: json['host'] as String? ?? '',
dnsMode: $enumDecodeNullable(_$DnsModeEnumMap, json['dnsMode']),
process: json['process'] as String? ?? '',
processPath: json['processPath'] as String? ?? '',
remoteDestination: json['remoteDestination'] as String? ?? '',
sourceGeoIP: (json['sourceGeoIP'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
destinationGeoIP: (json['destinationGeoIP'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
destinationIPASN: json['destinationIPASN'] as String? ?? '',
sourceIPASN: json['sourceIPASN'] as String? ?? '',
specialRules: json['specialRules'] as String? ?? '',
specialProxy: json['specialProxy'] as String? ?? '',
);
_Metadata _$MetadataFromJson(Map<String, dynamic> json) => _Metadata(
uid: (json['uid'] as num?)?.toInt() ?? 0,
network: json['network'] as String? ?? '',
sourceIP: json['sourceIP'] as String? ?? '',
sourcePort: json['sourcePort'] as String? ?? '',
destinationIP: json['destinationIP'] as String? ?? '',
destinationPort: json['destinationPort'] as String? ?? '',
host: json['host'] as String? ?? '',
dnsMode: $enumDecodeNullable(_$DnsModeEnumMap, json['dnsMode']),
process: json['process'] as String? ?? '',
processPath: json['processPath'] as String? ?? '',
remoteDestination: json['remoteDestination'] as String? ?? '',
sourceGeoIP:
(json['sourceGeoIP'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
destinationGeoIP:
(json['destinationGeoIP'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
destinationIPASN: json['destinationIPASN'] as String? ?? '',
sourceIPASN: json['sourceIPASN'] as String? ?? '',
specialRules: json['specialRules'] as String? ?? '',
specialProxy: json['specialProxy'] as String? ?? '',
);
Map<String, dynamic> _$$MetadataImplToJson(_$MetadataImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'network': instance.network,
'sourceIP': instance.sourceIP,
'sourcePort': instance.sourcePort,
'destinationIP': instance.destinationIP,
'destinationPort': instance.destinationPort,
'host': instance.host,
'dnsMode': _$DnsModeEnumMap[instance.dnsMode],
'process': instance.process,
'processPath': instance.processPath,
'remoteDestination': instance.remoteDestination,
'sourceGeoIP': instance.sourceGeoIP,
'destinationGeoIP': instance.destinationGeoIP,
'destinationIPASN': instance.destinationIPASN,
'sourceIPASN': instance.sourceIPASN,
'specialRules': instance.specialRules,
'specialProxy': instance.specialProxy,
};
Map<String, dynamic> _$MetadataToJson(_Metadata instance) => <String, dynamic>{
'uid': instance.uid,
'network': instance.network,
'sourceIP': instance.sourceIP,
'sourcePort': instance.sourcePort,
'destinationIP': instance.destinationIP,
'destinationPort': instance.destinationPort,
'host': instance.host,
'dnsMode': _$DnsModeEnumMap[instance.dnsMode],
'process': instance.process,
'processPath': instance.processPath,
'remoteDestination': instance.remoteDestination,
'sourceGeoIP': instance.sourceGeoIP,
'destinationGeoIP': instance.destinationGeoIP,
'destinationIPASN': instance.destinationIPASN,
'sourceIPASN': instance.sourceIPASN,
'specialRules': instance.specialRules,
'specialProxy': instance.specialProxy,
};
const _$DnsModeEnumMap = {
DnsMode.normal: 'normal',
@@ -79,22 +77,20 @@ const _$DnsModeEnumMap = {
DnsMode.hosts: 'hosts',
};
_$TrackerInfoImpl _$$TrackerInfoImplFromJson(Map<String, dynamic> json) =>
_$TrackerInfoImpl(
id: json['id'] as String,
upload: (json['upload'] as num?)?.toInt() ?? 0,
download: (json['download'] as num?)?.toInt() ?? 0,
start: DateTime.parse(json['start'] as String),
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
chains:
(json['chains'] as List<dynamic>).map((e) => e as String).toList(),
rule: json['rule'] as String,
rulePayload: json['rulePayload'] as String,
downloadSpeed: (json['downloadSpeed'] as num?)?.toInt(),
uploadSpeed: (json['uploadSpeed'] as num?)?.toInt(),
);
_TrackerInfo _$TrackerInfoFromJson(Map<String, dynamic> json) => _TrackerInfo(
id: json['id'] as String,
upload: (json['upload'] as num?)?.toInt() ?? 0,
download: (json['download'] as num?)?.toInt() ?? 0,
start: DateTime.parse(json['start'] as String),
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
chains: (json['chains'] as List<dynamic>).map((e) => e as String).toList(),
rule: json['rule'] as String,
rulePayload: json['rulePayload'] as String,
downloadSpeed: (json['downloadSpeed'] as num?)?.toInt(),
uploadSpeed: (json['uploadSpeed'] as num?)?.toInt(),
);
Map<String, dynamic> _$$TrackerInfoImplToJson(_$TrackerInfoImpl instance) =>
Map<String, dynamic> _$TrackerInfoToJson(_TrackerInfo instance) =>
<String, dynamic>{
'id': instance.id,
'upload': instance.upload,
@@ -108,18 +104,18 @@ Map<String, dynamic> _$$TrackerInfoImplToJson(_$TrackerInfoImpl instance) =>
'uploadSpeed': instance.uploadSpeed,
};
_$LogImpl _$$LogImplFromJson(Map<String, dynamic> json) => _$LogImpl(
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??
LogLevel.info,
payload: json['Payload'] as String? ?? '',
dateTime: _logDateTime(json['dateTime']),
);
_Log _$LogFromJson(Map<String, dynamic> json) => _Log(
logLevel:
$enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ?? LogLevel.info,
payload: json['Payload'] as String? ?? '',
dateTime: _logDateTime(json['dateTime']),
);
Map<String, dynamic> _$$LogImplToJson(_$LogImpl instance) => <String, dynamic>{
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
'Payload': instance.payload,
'dateTime': instance.dateTime,
};
Map<String, dynamic> _$LogToJson(_Log instance) => <String, dynamic>{
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
'Payload': instance.payload,
'dateTime': instance.dateTime,
};
const _$LogLevelEnumMap = {
LogLevel.debug: 'debug',
@@ -129,68 +125,74 @@ const _$LogLevelEnumMap = {
LogLevel.silent: 'silent',
};
_$DAVImpl _$$DAVImplFromJson(Map<String, dynamic> json) => _$DAVImpl(
uri: json['uri'] as String,
user: json['user'] as String,
password: json['password'] as String,
fileName: json['fileName'] as String? ?? defaultDavFileName,
);
_DAV _$DAVFromJson(Map<String, dynamic> json) => _DAV(
uri: json['uri'] as String,
user: json['user'] as String,
password: json['password'] as String,
fileName: json['fileName'] as String? ?? defaultDavFileName,
);
Map<String, dynamic> _$$DAVImplToJson(_$DAVImpl instance) => <String, dynamic>{
'uri': instance.uri,
'user': instance.user,
'password': instance.password,
'fileName': instance.fileName,
};
Map<String, dynamic> _$DAVToJson(_DAV instance) => <String, dynamic>{
'uri': instance.uri,
'user': instance.user,
'password': instance.password,
'fileName': instance.fileName,
};
_$VersionInfoImpl _$$VersionInfoImplFromJson(Map<String, dynamic> json) =>
_$VersionInfoImpl(
clashName: json['clashName'] as String? ?? '',
version: json['version'] as String? ?? '',
);
_VersionInfo _$VersionInfoFromJson(Map<String, dynamic> json) => _VersionInfo(
clashName: json['clashName'] as String? ?? '',
version: json['version'] as String? ?? '',
);
Map<String, dynamic> _$$VersionInfoImplToJson(_$VersionInfoImpl instance) =>
Map<String, dynamic> _$VersionInfoToJson(_VersionInfo instance) =>
<String, dynamic>{
'clashName': instance.clashName,
'version': instance.version,
};
_$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl(
name: json['name'] as String,
type: json['type'] as String,
now: json['now'] as String?,
);
_Traffic _$TrafficFromJson(Map<String, dynamic> json) =>
_Traffic(up: json['up'] as num? ?? 0, down: json['down'] as num? ?? 0);
Map<String, dynamic> _$$ProxyImplToJson(_$ProxyImpl instance) =>
<String, dynamic>{
'name': instance.name,
'type': instance.type,
'now': instance.now,
};
Map<String, dynamic> _$TrafficToJson(_Traffic instance) => <String, dynamic>{
'up': instance.up,
'down': instance.down,
};
_$GroupImpl _$$GroupImplFromJson(Map<String, dynamic> json) => _$GroupImpl(
type: $enumDecode(_$GroupTypeEnumMap, json['type']),
all: (json['all'] as List<dynamic>?)
?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
now: json['now'] as String?,
hidden: json['hidden'] as bool?,
testUrl: json['testUrl'] as String?,
icon: json['icon'] as String? ?? '',
name: json['name'] as String,
);
_Proxy _$ProxyFromJson(Map<String, dynamic> json) => _Proxy(
name: json['name'] as String,
type: json['type'] as String,
now: json['now'] as String?,
);
Map<String, dynamic> _$$GroupImplToJson(_$GroupImpl instance) =>
<String, dynamic>{
'type': _$GroupTypeEnumMap[instance.type]!,
'all': instance.all,
'now': instance.now,
'hidden': instance.hidden,
'testUrl': instance.testUrl,
'icon': instance.icon,
'name': instance.name,
};
Map<String, dynamic> _$ProxyToJson(_Proxy instance) => <String, dynamic>{
'name': instance.name,
'type': instance.type,
'now': instance.now,
};
_Group _$GroupFromJson(Map<String, dynamic> json) => _Group(
type: $enumDecode(_$GroupTypeEnumMap, json['type']),
all:
(json['all'] as List<dynamic>?)
?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
now: json['now'] as String?,
hidden: json['hidden'] as bool?,
testUrl: json['testUrl'] as String?,
icon: json['icon'] as String? ?? '',
name: json['name'] as String,
);
Map<String, dynamic> _$GroupToJson(_Group instance) => <String, dynamic>{
'type': _$GroupTypeEnumMap[instance.type]!,
'all': instance.all,
'now': instance.now,
'hidden': instance.hidden,
'testUrl': instance.testUrl,
'icon': instance.icon,
'name': instance.name,
};
const _$GroupTypeEnumMap = {
GroupType.Selector: 'Selector',
@@ -200,22 +202,24 @@ const _$GroupTypeEnumMap = {
GroupType.Relay: 'Relay',
};
_$HotKeyActionImpl _$$HotKeyActionImplFromJson(Map<String, dynamic> json) =>
_$HotKeyActionImpl(
_HotKeyAction _$HotKeyActionFromJson(Map<String, dynamic> json) =>
_HotKeyAction(
action: $enumDecode(_$HotActionEnumMap, json['action']),
key: (json['key'] as num?)?.toInt(),
modifiers: (json['modifiers'] as List<dynamic>?)
modifiers:
(json['modifiers'] as List<dynamic>?)
?.map((e) => $enumDecode(_$KeyboardModifierEnumMap, e))
.toSet() ??
const {},
);
Map<String, dynamic> _$$HotKeyActionImplToJson(_$HotKeyActionImpl instance) =>
Map<String, dynamic> _$HotKeyActionToJson(_HotKeyAction instance) =>
<String, dynamic>{
'action': _$HotActionEnumMap[instance.action]!,
'key': instance.key,
'modifiers':
instance.modifiers.map((e) => _$KeyboardModifierEnumMap[e]!).toList(),
'modifiers': instance.modifiers
.map((e) => _$KeyboardModifierEnumMap[e]!)
.toList(),
};
const _$HotActionEnumMap = {
@@ -235,35 +239,30 @@ const _$KeyboardModifierEnumMap = {
KeyboardModifier.shift: 'shift',
};
_$TextPainterParamsImpl _$$TextPainterParamsImplFromJson(
Map<String, dynamic> json) =>
_$TextPainterParamsImpl(
text: json['text'] as String?,
fontSize: (json['fontSize'] as num?)?.toDouble(),
textScaleFactor: (json['textScaleFactor'] as num).toDouble(),
maxWidth: (json['maxWidth'] as num?)?.toDouble() ?? double.infinity,
maxLines: (json['maxLines'] as num?)?.toInt(),
_AndroidState _$AndroidStateFromJson(Map<String, dynamic> json) =>
_AndroidState(
currentProfileName: json['currentProfileName'] as String,
stopText: json['stopText'] as String,
onlyStatisticsProxy: json['onlyStatisticsProxy'] as bool,
crashlytics: json['crashlytics'] as bool,
);
Map<String, dynamic> _$$TextPainterParamsImplToJson(
_$TextPainterParamsImpl instance) =>
Map<String, dynamic> _$AndroidStateToJson(_AndroidState instance) =>
<String, dynamic>{
'text': instance.text,
'fontSize': instance.fontSize,
'textScaleFactor': instance.textScaleFactor,
'maxWidth': instance.maxWidth,
'maxLines': instance.maxLines,
'currentProfileName': instance.currentProfileName,
'stopText': instance.stopText,
'onlyStatisticsProxy': instance.onlyStatisticsProxy,
'crashlytics': instance.crashlytics,
};
_$ScriptImpl _$$ScriptImplFromJson(Map<String, dynamic> json) => _$ScriptImpl(
id: json['id'] as String,
label: json['label'] as String,
content: json['content'] as String,
);
_Script _$ScriptFromJson(Map<String, dynamic> json) => _Script(
id: json['id'] as String,
label: json['label'] as String,
content: json['content'] as String,
);
Map<String, dynamic> _$$ScriptImplToJson(_$ScriptImpl instance) =>
<String, dynamic>{
'id': instance.id,
'label': instance.label,
'content': instance.content,
};
Map<String, dynamic> _$ScriptToJson(_Script instance) => <String, dynamic>{
'id': instance.id,
'label': instance.label,
'content': instance.content,
};

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,8 @@ part of '../config.dart';
// JsonSerializableGenerator
// **************************************************************************
_$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
Map<String, dynamic> json) =>
_$AppSettingPropsImpl(
_AppSettingProps _$AppSettingPropsFromJson(Map<String, dynamic> json) =>
_AppSettingProps(
locale: json['locale'] as String?,
dashboardWidgets: json['dashboardWidgets'] == null
? defaultDashboardWidgets
@@ -24,16 +23,20 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
autoCheckUpdate: json['autoCheckUpdate'] as bool? ?? true,
showLabel: json['showLabel'] as bool? ?? false,
disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false,
crashlyticsTip: json['crashlyticsTip'] as bool? ?? false,
crashlytics: json['crashlytics'] as bool? ?? false,
minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,
hidden: json['hidden'] as bool? ?? false,
developerMode: json['developerMode'] as bool? ?? false,
recoveryStrategy: $enumDecodeNullable(
_$RecoveryStrategyEnumMap, json['recoveryStrategy']) ??
recoveryStrategy:
$enumDecodeNullable(
_$RecoveryStrategyEnumMap,
json['recoveryStrategy'],
) ??
RecoveryStrategy.compatible,
);
Map<String, dynamic> _$$AppSettingPropsImplToJson(
_$AppSettingPropsImpl instance) =>
Map<String, dynamic> _$AppSettingPropsToJson(_AppSettingProps instance) =>
<String, dynamic>{
'locale': instance.locale,
'dashboardWidgets': instance.dashboardWidgets
@@ -50,6 +53,8 @@ Map<String, dynamic> _$$AppSettingPropsImplToJson(
'autoCheckUpdate': instance.autoCheckUpdate,
'showLabel': instance.showLabel,
'disclaimerAccepted': instance.disclaimerAccepted,
'crashlyticsTip': instance.crashlyticsTip,
'crashlytics': instance.crashlytics,
'minimizeOnExit': instance.minimizeOnExit,
'hidden': instance.hidden,
'developerMode': instance.developerMode,
@@ -74,26 +79,30 @@ const _$DashboardWidgetEnumMap = {
DashboardWidget.memoryInfo: 'memoryInfo',
};
_$AccessControlImpl _$$AccessControlImplFromJson(Map<String, dynamic> json) =>
_$AccessControlImpl(
_AccessControl _$AccessControlFromJson(Map<String, dynamic> json) =>
_AccessControl(
enable: json['enable'] as bool? ?? false,
mode: $enumDecodeNullable(_$AccessControlModeEnumMap, json['mode']) ??
mode:
$enumDecodeNullable(_$AccessControlModeEnumMap, json['mode']) ??
AccessControlMode.rejectSelected,
acceptList: (json['acceptList'] as List<dynamic>?)
acceptList:
(json['acceptList'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
rejectList: (json['rejectList'] as List<dynamic>?)
rejectList:
(json['rejectList'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
sort: $enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
sort:
$enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
AccessSortType.none,
isFilterSystemApp: json['isFilterSystemApp'] as bool? ?? true,
isFilterNonInternetApp: json['isFilterNonInternetApp'] as bool? ?? true,
);
Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
Map<String, dynamic> _$AccessControlToJson(_AccessControl instance) =>
<String, dynamic>{
'enable': instance.enable,
'mode': _$AccessControlModeEnumMap[instance.mode]!,
@@ -115,15 +124,14 @@ const _$AccessSortTypeEnumMap = {
AccessSortType.time: 'time',
};
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
_$WindowPropsImpl(
width: (json['width'] as num?)?.toDouble() ?? 750,
height: (json['height'] as num?)?.toDouble() ?? 600,
top: (json['top'] as num?)?.toDouble(),
left: (json['left'] as num?)?.toDouble(),
);
_WindowProps _$WindowPropsFromJson(Map<String, dynamic> json) => _WindowProps(
width: (json['width'] as num?)?.toDouble() ?? 750,
height: (json['height'] as num?)?.toDouble() ?? 600,
top: (json['top'] as num?)?.toDouble(),
left: (json['left'] as num?)?.toDouble(),
);
Map<String, dynamic> _$$WindowPropsImplToJson(_$WindowPropsImpl instance) =>
Map<String, dynamic> _$WindowPropsToJson(_WindowProps instance) =>
<String, dynamic>{
'width': instance.width,
'height': instance.height,
@@ -131,40 +139,41 @@ Map<String, dynamic> _$$WindowPropsImplToJson(_$WindowPropsImpl instance) =>
'left': instance.left,
};
_$VpnPropsImpl _$$VpnPropsImplFromJson(Map<String, dynamic> json) =>
_$VpnPropsImpl(
enable: json['enable'] as bool? ?? true,
systemProxy: json['systemProxy'] as bool? ?? true,
ipv6: json['ipv6'] as bool? ?? false,
allowBypass: json['allowBypass'] as bool? ?? true,
accessControl: json['accessControl'] == null
? defaultAccessControl
: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>),
);
_VpnProps _$VpnPropsFromJson(Map<String, dynamic> json) => _VpnProps(
enable: json['enable'] as bool? ?? true,
systemProxy: json['systemProxy'] as bool? ?? true,
ipv6: json['ipv6'] as bool? ?? false,
allowBypass: json['allowBypass'] as bool? ?? true,
dnsHijacking: json['dnsHijacking'] as bool? ?? false,
accessControl: json['accessControl'] == null
? defaultAccessControl
: AccessControl.fromJson(json['accessControl'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$VpnPropsImplToJson(_$VpnPropsImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'systemProxy': instance.systemProxy,
'ipv6': instance.ipv6,
'allowBypass': instance.allowBypass,
'accessControl': instance.accessControl,
};
Map<String, dynamic> _$VpnPropsToJson(_VpnProps instance) => <String, dynamic>{
'enable': instance.enable,
'systemProxy': instance.systemProxy,
'ipv6': instance.ipv6,
'allowBypass': instance.allowBypass,
'dnsHijacking': instance.dnsHijacking,
'accessControl': instance.accessControl,
};
_$NetworkPropsImpl _$$NetworkPropsImplFromJson(Map<String, dynamic> json) =>
_$NetworkPropsImpl(
_NetworkProps _$NetworkPropsFromJson(Map<String, dynamic> json) =>
_NetworkProps(
systemProxy: json['systemProxy'] as bool? ?? true,
bypassDomain: (json['bypassDomain'] as List<dynamic>?)
bypassDomain:
(json['bypassDomain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
defaultBypassDomain,
routeMode: $enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
routeMode:
$enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
RouteMode.config,
autoSetSystemDns: json['autoSetSystemDns'] as bool? ?? true,
);
Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
Map<String, dynamic> _$NetworkPropsToJson(_NetworkProps instance) =>
<String, dynamic>{
'systemProxy': instance.systemProxy,
'bypassDomain': instance.bypassDomain,
@@ -177,27 +186,31 @@ const _$RouteModeEnumMap = {
RouteMode.config: 'config',
};
_$ProxiesStyleImpl _$$ProxiesStyleImplFromJson(Map<String, dynamic> json) =>
_$ProxiesStyleImpl(
type: $enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ??
_ProxiesStyle _$ProxiesStyleFromJson(Map<String, dynamic> json) =>
_ProxiesStyle(
type:
$enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ??
ProxiesType.tab,
sortType:
$enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['sortType']) ??
ProxiesSortType.none,
layout: $enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ??
ProxiesSortType.none,
layout:
$enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ??
ProxiesLayout.standard,
iconStyle:
$enumDecodeNullable(_$ProxiesIconStyleEnumMap, json['iconStyle']) ??
ProxiesIconStyle.standard,
cardType: $enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ??
ProxiesIconStyle.standard,
cardType:
$enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ??
ProxyCardType.expand,
iconMap: (json['iconMap'] as Map<String, dynamic>?)?.map(
iconMap:
(json['iconMap'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
);
Map<String, dynamic> _$$ProxiesStyleImplToJson(_$ProxiesStyleImpl instance) =>
Map<String, dynamic> _$ProxiesStyleToJson(_ProxiesStyle instance) =>
<String, dynamic>{
'type': _$ProxiesTypeEnumMap[instance.type]!,
'sortType': _$ProxiesSortTypeEnumMap[instance.sortType]!,
@@ -207,10 +220,7 @@ Map<String, dynamic> _$$ProxiesStyleImplToJson(_$ProxiesStyleImpl instance) =>
'iconMap': instance.iconMap,
};
const _$ProxiesTypeEnumMap = {
ProxiesType.tab: 'tab',
ProxiesType.list: 'list',
};
const _$ProxiesTypeEnumMap = {ProxiesType.tab: 'tab', ProxiesType.list: 'list'};
const _$ProxiesSortTypeEnumMap = {
ProxiesSortType.none: 'none',
@@ -236,37 +246,37 @@ const _$ProxyCardTypeEnumMap = {
ProxyCardType.min: 'min',
};
_$TextScaleImpl _$$TextScaleImplFromJson(Map<String, dynamic> json) =>
_$TextScaleImpl(
enable: json['enable'] as bool? ?? false,
scale: (json['scale'] as num?)?.toDouble() ?? 1.0,
);
_TextScale _$TextScaleFromJson(Map<String, dynamic> json) => _TextScale(
enable: json['enable'] as bool? ?? false,
scale: (json['scale'] as num?)?.toDouble() ?? 1.0,
);
Map<String, dynamic> _$$TextScaleImplToJson(_$TextScaleImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'scale': instance.scale,
};
Map<String, dynamic> _$TextScaleToJson(_TextScale instance) =>
<String, dynamic>{'enable': instance.enable, 'scale': instance.scale};
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
_$ThemePropsImpl(
primaryColor: (json['primaryColor'] as num?)?.toInt(),
primaryColors: (json['primaryColors'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
defaultPrimaryColors,
themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
ThemeMode.dark,
schemeVariant: $enumDecodeNullable(
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
DynamicSchemeVariant.content,
pureBlack: json['pureBlack'] as bool? ?? false,
textScale: json['textScale'] == null
? const TextScale()
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
);
_ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) => _ThemeProps(
primaryColor: (json['primaryColor'] as num?)?.toInt(),
primaryColors:
(json['primaryColors'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
defaultPrimaryColors,
themeMode:
$enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
ThemeMode.dark,
schemeVariant:
$enumDecodeNullable(
_$DynamicSchemeVariantEnumMap,
json['schemeVariant'],
) ??
DynamicSchemeVariant.content,
pureBlack: json['pureBlack'] as bool? ?? false,
textScale: json['textScale'] == null
? const TextScale()
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
Map<String, dynamic> _$ThemePropsToJson(_ThemeProps instance) =>
<String, dynamic>{
'primaryColor': instance.primaryColor,
'primaryColors': instance.primaryColors,
@@ -294,77 +304,77 @@ const _$DynamicSchemeVariantEnumMap = {
DynamicSchemeVariant.fruitSalad: 'fruitSalad',
};
_$ScriptPropsImpl _$$ScriptPropsImplFromJson(Map<String, dynamic> json) =>
_$ScriptPropsImpl(
currentId: json['currentId'] as String?,
scripts: (json['scripts'] as List<dynamic>?)
?.map((e) => Script.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
_ScriptProps _$ScriptPropsFromJson(Map<String, dynamic> json) => _ScriptProps(
currentId: json['currentId'] as String?,
scripts:
(json['scripts'] as List<dynamic>?)
?.map((e) => Script.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$ScriptPropsImplToJson(_$ScriptPropsImpl instance) =>
Map<String, dynamic> _$ScriptPropsToJson(_ScriptProps instance) =>
<String, dynamic>{
'currentId': instance.currentId,
'scripts': instance.scripts,
};
_$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
appSetting: json['appSetting'] == null
? defaultAppSettingProps
: AppSettingProps.safeFromJson(
json['appSetting'] as Map<String, Object?>?),
profiles: (json['profiles'] as List<dynamic>?)
?.map((e) => Profile.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
hotKeyActions: (json['hotKeyActions'] as List<dynamic>?)
?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
currentProfileId: json['currentProfileId'] as String?,
overrideDns: json['overrideDns'] as bool? ?? false,
dav: json['dav'] == null
? null
: DAV.fromJson(json['dav'] as Map<String, dynamic>),
networkProps: json['networkProps'] == null
? defaultNetworkProps
: NetworkProps.fromJson(
json['networkProps'] as Map<String, dynamic>?),
vpnProps: json['vpnProps'] == null
? defaultVpnProps
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
themeProps:
ThemeProps.safeFromJson(json['themeProps'] as Map<String, Object?>?),
proxiesStyle: json['proxiesStyle'] == null
? defaultProxiesStyle
: ProxiesStyle.fromJson(
json['proxiesStyle'] as Map<String, dynamic>?),
windowProps: json['windowProps'] == null
? defaultWindowProps
: WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),
patchClashConfig: json['patchClashConfig'] == null
? defaultClashConfig
: ClashConfig.fromJson(
json['patchClashConfig'] as Map<String, dynamic>),
scriptProps: json['scriptProps'] == null
? const ScriptProps()
: ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),
);
_Config _$ConfigFromJson(Map<String, dynamic> json) => _Config(
appSetting: json['appSetting'] == null
? defaultAppSettingProps
: AppSettingProps.safeFromJson(
json['appSetting'] as Map<String, Object?>?,
),
profiles:
(json['profiles'] as List<dynamic>?)
?.map((e) => Profile.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
hotKeyActions:
(json['hotKeyActions'] as List<dynamic>?)
?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
currentProfileId: json['currentProfileId'] as String?,
overrideDns: json['overrideDns'] as bool? ?? false,
dav: json['dav'] == null
? null
: DAV.fromJson(json['dav'] as Map<String, dynamic>),
networkProps: json['networkProps'] == null
? defaultNetworkProps
: NetworkProps.fromJson(json['networkProps'] as Map<String, dynamic>?),
vpnProps: json['vpnProps'] == null
? defaultVpnProps
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
themeProps: ThemeProps.safeFromJson(
json['themeProps'] as Map<String, Object?>?,
),
proxiesStyle: json['proxiesStyle'] == null
? defaultProxiesStyle
: ProxiesStyle.fromJson(json['proxiesStyle'] as Map<String, dynamic>?),
windowProps: json['windowProps'] == null
? defaultWindowProps
: WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),
patchClashConfig: json['patchClashConfig'] == null
? defaultClashConfig
: ClashConfig.fromJson(json['patchClashConfig'] as Map<String, dynamic>),
scriptProps: json['scriptProps'] == null
? const ScriptProps()
: ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ConfigImplToJson(_$ConfigImpl instance) =>
<String, dynamic>{
'appSetting': instance.appSetting,
'profiles': instance.profiles,
'hotKeyActions': instance.hotKeyActions,
'currentProfileId': instance.currentProfileId,
'overrideDns': instance.overrideDns,
'dav': instance.dav,
'networkProps': instance.networkProps,
'vpnProps': instance.vpnProps,
'themeProps': instance.themeProps,
'proxiesStyle': instance.proxiesStyle,
'windowProps': instance.windowProps,
'patchClashConfig': instance.patchClashConfig,
'scriptProps': instance.scriptProps,
};
Map<String, dynamic> _$ConfigToJson(_Config instance) => <String, dynamic>{
'appSetting': instance.appSetting,
'profiles': instance.profiles,
'hotKeyActions': instance.hotKeyActions,
'currentProfileId': instance.currentProfileId,
'overrideDns': instance.overrideDns,
'dav': instance.dav,
'networkProps': instance.networkProps,
'vpnProps': instance.vpnProps,
'themeProps': instance.themeProps,
'proxiesStyle': instance.proxiesStyle,
'windowProps': instance.windowProps,
'patchClashConfig': instance.patchClashConfig,
'scriptProps': instance.scriptProps,
};

File diff suppressed because it is too large Load Diff

View File

@@ -6,37 +6,38 @@ part of '../core.dart';
// JsonSerializableGenerator
// **************************************************************************
_$SetupParamsImpl _$$SetupParamsImplFromJson(Map<String, dynamic> json) =>
_$SetupParamsImpl(
config: json['config'] as Map<String, dynamic>,
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
testUrl: json['test-url'] as String,
);
_SetupParams _$SetupParamsFromJson(Map<String, dynamic> json) => _SetupParams(
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
testUrl: json['test-url'] as String,
);
Map<String, dynamic> _$$SetupParamsImplToJson(_$SetupParamsImpl instance) =>
Map<String, dynamic> _$SetupParamsToJson(_SetupParams instance) =>
<String, dynamic>{
'config': instance.config,
'selected-map': instance.selectedMap,
'test-url': instance.testUrl,
};
_$UpdateParamsImpl _$$UpdateParamsImplFromJson(Map<String, dynamic> json) =>
_$UpdateParamsImpl(
_UpdateParams _$UpdateParamsFromJson(Map<String, dynamic> json) =>
_UpdateParams(
tun: Tun.fromJson(json['tun'] as Map<String, dynamic>),
mixedPort: (json['mixed-port'] as num).toInt(),
allowLan: json['allow-lan'] as bool,
findProcessMode:
$enumDecode(_$FindProcessModeEnumMap, json['find-process-mode']),
findProcessMode: $enumDecode(
_$FindProcessModeEnumMap,
json['find-process-mode'],
),
mode: $enumDecode(_$ModeEnumMap, json['mode']),
logLevel: $enumDecode(_$LogLevelEnumMap, json['log-level']),
ipv6: json['ipv6'] as bool,
tcpConcurrent: json['tcp-concurrent'] as bool,
externalController: $enumDecode(
_$ExternalControllerStatusEnumMap, json['external-controller']),
_$ExternalControllerStatusEnumMap,
json['external-controller'],
),
unifiedDelay: json['unified-delay'] as bool,
);
Map<String, dynamic> _$$UpdateParamsImplToJson(_$UpdateParamsImpl instance) =>
Map<String, dynamic> _$UpdateParamsToJson(_UpdateParams instance) =>
<String, dynamic>{
'tun': instance.tun,
'mixed-port': instance.mixedPort,
@@ -75,129 +76,103 @@ const _$ExternalControllerStatusEnumMap = {
ExternalControllerStatus.open: '127.0.0.1:9090',
};
_$CoreStateImpl _$$CoreStateImplFromJson(Map<String, dynamic> json) =>
_$CoreStateImpl(
vpnProps: VpnProps.fromJson(json['vpn-props'] as Map<String, dynamic>?),
onlyStatisticsProxy: json['only-statistics-proxy'] as bool,
currentProfileName: json['current-profile-name'] as String,
bypassDomain: (json['bypass-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
);
_VpnOptions _$VpnOptionsFromJson(Map<String, dynamic> json) => _VpnOptions(
enable: json['enable'] as bool,
port: (json['port'] as num).toInt(),
ipv6: json['ipv6'] as bool,
dnsHijacking: json['dnsHijacking'] as bool,
accessControl: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>,
),
allowBypass: json['allowBypass'] as bool,
systemProxy: json['systemProxy'] as bool,
bypassDomain: (json['bypassDomain'] as List<dynamic>)
.map((e) => e as String)
.toList(),
stack: json['stack'] as String,
routeAddress:
(json['routeAddress'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
);
Map<String, dynamic> _$$CoreStateImplToJson(_$CoreStateImpl instance) =>
<String, dynamic>{
'vpn-props': instance.vpnProps,
'only-statistics-proxy': instance.onlyStatisticsProxy,
'current-profile-name': instance.currentProfileName,
'bypass-domain': instance.bypassDomain,
};
_$AndroidVpnOptionsImpl _$$AndroidVpnOptionsImplFromJson(
Map<String, dynamic> json) =>
_$AndroidVpnOptionsImpl(
enable: json['enable'] as bool,
port: (json['port'] as num).toInt(),
accessControl: json['accessControl'] == null
? null
: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>),
allowBypass: json['allowBypass'] as bool,
systemProxy: json['systemProxy'] as bool,
bypassDomain: (json['bypassDomain'] as List<dynamic>)
.map((e) => e as String)
.toList(),
ipv4Address: json['ipv4Address'] as String,
ipv6Address: json['ipv6Address'] as String,
routeAddress: (json['routeAddress'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
dnsServerAddress: json['dnsServerAddress'] as String,
);
Map<String, dynamic> _$$AndroidVpnOptionsImplToJson(
_$AndroidVpnOptionsImpl instance) =>
Map<String, dynamic> _$VpnOptionsToJson(_VpnOptions instance) =>
<String, dynamic>{
'enable': instance.enable,
'port': instance.port,
'ipv6': instance.ipv6,
'dnsHijacking': instance.dnsHijacking,
'accessControl': instance.accessControl,
'allowBypass': instance.allowBypass,
'systemProxy': instance.systemProxy,
'bypassDomain': instance.bypassDomain,
'ipv4Address': instance.ipv4Address,
'ipv6Address': instance.ipv6Address,
'stack': instance.stack,
'routeAddress': instance.routeAddress,
'dnsServerAddress': instance.dnsServerAddress,
};
_$InitParamsImpl _$$InitParamsImplFromJson(Map<String, dynamic> json) =>
_$InitParamsImpl(
homeDir: json['home-dir'] as String,
version: (json['version'] as num).toInt(),
);
_InitParams _$InitParamsFromJson(Map<String, dynamic> json) => _InitParams(
homeDir: json['home-dir'] as String,
version: (json['version'] as num).toInt(),
);
Map<String, dynamic> _$$InitParamsImplToJson(_$InitParamsImpl instance) =>
Map<String, dynamic> _$InitParamsToJson(_InitParams instance) =>
<String, dynamic>{
'home-dir': instance.homeDir,
'version': instance.version,
};
_$ChangeProxyParamsImpl _$$ChangeProxyParamsImplFromJson(
Map<String, dynamic> json) =>
_$ChangeProxyParamsImpl(
_ChangeProxyParams _$ChangeProxyParamsFromJson(Map<String, dynamic> json) =>
_ChangeProxyParams(
groupName: json['group-name'] as String,
proxyName: json['proxy-name'] as String,
);
Map<String, dynamic> _$$ChangeProxyParamsImplToJson(
_$ChangeProxyParamsImpl instance) =>
Map<String, dynamic> _$ChangeProxyParamsToJson(_ChangeProxyParams instance) =>
<String, dynamic>{
'group-name': instance.groupName,
'proxy-name': instance.proxyName,
};
_$UpdateGeoDataParamsImpl _$$UpdateGeoDataParamsImplFromJson(
Map<String, dynamic> json) =>
_$UpdateGeoDataParamsImpl(
_UpdateGeoDataParams _$UpdateGeoDataParamsFromJson(Map<String, dynamic> json) =>
_UpdateGeoDataParams(
geoType: json['geo-type'] as String,
geoName: json['geo-name'] as String,
);
Map<String, dynamic> _$$UpdateGeoDataParamsImplToJson(
_$UpdateGeoDataParamsImpl instance) =>
<String, dynamic>{
'geo-type': instance.geoType,
'geo-name': instance.geoName,
};
Map<String, dynamic> _$UpdateGeoDataParamsToJson(
_UpdateGeoDataParams instance,
) => <String, dynamic>{
'geo-type': instance.geoType,
'geo-name': instance.geoName,
};
_$AppMessageImpl _$$AppMessageImplFromJson(Map<String, dynamic> json) =>
_$AppMessageImpl(
type: $enumDecode(_$AppMessageTypeEnumMap, json['type']),
data: json['data'],
);
_CoreEvent _$CoreEventFromJson(Map<String, dynamic> json) => _CoreEvent(
type: $enumDecode(_$CoreEventTypeEnumMap, json['type']),
data: json['data'],
);
Map<String, dynamic> _$$AppMessageImplToJson(_$AppMessageImpl instance) =>
Map<String, dynamic> _$CoreEventToJson(_CoreEvent instance) =>
<String, dynamic>{
'type': _$AppMessageTypeEnumMap[instance.type]!,
'type': _$CoreEventTypeEnumMap[instance.type]!,
'data': instance.data,
};
const _$AppMessageTypeEnumMap = {
AppMessageType.log: 'log',
AppMessageType.delay: 'delay',
AppMessageType.request: 'request',
AppMessageType.loaded: 'loaded',
const _$CoreEventTypeEnumMap = {
CoreEventType.log: 'log',
CoreEventType.delay: 'delay',
CoreEventType.request: 'request',
CoreEventType.loaded: 'loaded',
CoreEventType.crash: 'crash',
};
_$InvokeMessageImpl _$$InvokeMessageImplFromJson(Map<String, dynamic> json) =>
_$InvokeMessageImpl(
_InvokeMessage _$InvokeMessageFromJson(Map<String, dynamic> json) =>
_InvokeMessage(
type: $enumDecode(_$InvokeMessageTypeEnumMap, json['type']),
data: json['data'],
);
Map<String, dynamic> _$$InvokeMessageImplToJson(_$InvokeMessageImpl instance) =>
Map<String, dynamic> _$InvokeMessageToJson(_InvokeMessage instance) =>
<String, dynamic>{
'type': _$InvokeMessageTypeEnumMap[instance.type]!,
'data': instance.data,
@@ -208,63 +183,59 @@ const _$InvokeMessageTypeEnumMap = {
InvokeMessageType.process: 'process',
};
_$DelayImpl _$$DelayImplFromJson(Map<String, dynamic> json) => _$DelayImpl(
name: json['name'] as String,
url: json['url'] as String,
value: (json['value'] as num?)?.toInt(),
);
_Delay _$DelayFromJson(Map<String, dynamic> json) => _Delay(
name: json['name'] as String,
url: json['url'] as String,
value: (json['value'] as num?)?.toInt(),
);
Map<String, dynamic> _$$DelayImplToJson(_$DelayImpl instance) =>
<String, dynamic>{
'name': instance.name,
'url': instance.url,
'value': instance.value,
};
Map<String, dynamic> _$DelayToJson(_Delay instance) => <String, dynamic>{
'name': instance.name,
'url': instance.url,
'value': instance.value,
};
_$NowImpl _$$NowImplFromJson(Map<String, dynamic> json) => _$NowImpl(
name: json['name'] as String,
value: json['value'] as String,
);
_Now _$NowFromJson(Map<String, dynamic> json) =>
_Now(name: json['name'] as String, value: json['value'] as String);
Map<String, dynamic> _$$NowImplToJson(_$NowImpl instance) => <String, dynamic>{
'name': instance.name,
'value': instance.value,
};
Map<String, dynamic> _$NowToJson(_Now instance) => <String, dynamic>{
'name': instance.name,
'value': instance.value,
};
_$ProviderSubscriptionInfoImpl _$$ProviderSubscriptionInfoImplFromJson(
Map<String, dynamic> json) =>
_$ProviderSubscriptionInfoImpl(
upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,
download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,
total: (json['TOTAL'] as num?)?.toInt() ?? 0,
expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,
);
_ProviderSubscriptionInfo _$ProviderSubscriptionInfoFromJson(
Map<String, dynamic> json,
) => _ProviderSubscriptionInfo(
upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,
download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,
total: (json['TOTAL'] as num?)?.toInt() ?? 0,
expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,
);
Map<String, dynamic> _$$ProviderSubscriptionInfoImplToJson(
_$ProviderSubscriptionInfoImpl instance) =>
<String, dynamic>{
'UPLOAD': instance.upload,
'DOWNLOAD': instance.download,
'TOTAL': instance.total,
'EXPIRE': instance.expire,
};
Map<String, dynamic> _$ProviderSubscriptionInfoToJson(
_ProviderSubscriptionInfo instance,
) => <String, dynamic>{
'UPLOAD': instance.upload,
'DOWNLOAD': instance.download,
'TOTAL': instance.total,
'EXPIRE': instance.expire,
};
_$ExternalProviderImpl _$$ExternalProviderImplFromJson(
Map<String, dynamic> json) =>
_$ExternalProviderImpl(
_ExternalProvider _$ExternalProviderFromJson(Map<String, dynamic> json) =>
_ExternalProvider(
name: json['name'] as String,
type: json['type'] as String,
path: json['path'] as String?,
count: (json['count'] as num).toInt(),
subscriptionInfo: subscriptionInfoFormCore(
json['subscription-info'] as Map<String, Object?>?),
json['subscription-info'] as Map<String, Object?>?,
),
isUpdating: json['isUpdating'] as bool? ?? false,
vehicleType: json['vehicle-type'] as String,
updateAt: DateTime.parse(json['update-at'] as String),
);
Map<String, dynamic> _$$ExternalProviderImplToJson(
_$ExternalProviderImpl instance) =>
Map<String, dynamic> _$ExternalProviderToJson(_ExternalProvider instance) =>
<String, dynamic>{
'name': instance.name,
'type': instance.type,
@@ -276,18 +247,17 @@ Map<String, dynamic> _$$ExternalProviderImplToJson(
'update-at': instance.updateAt.toIso8601String(),
};
_$ActionImpl _$$ActionImplFromJson(Map<String, dynamic> json) => _$ActionImpl(
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
data: json['data'],
id: json['id'] as String,
);
_Action _$ActionFromJson(Map<String, dynamic> json) => _Action(
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
data: json['data'],
id: json['id'] as String,
);
Map<String, dynamic> _$$ActionImplToJson(_$ActionImpl instance) =>
<String, dynamic>{
'method': _$ActionMethodEnumMap[instance.method]!,
'data': instance.data,
'id': instance.id,
};
Map<String, dynamic> _$ActionToJson(_Action instance) => <String, dynamic>{
'method': _$ActionMethodEnumMap[instance.method]!,
'data': instance.data,
'id': instance.id,
};
const _$ActionMethodEnumMap = {
ActionMethod.message: 'message',
@@ -321,6 +291,7 @@ const _$ActionMethodEnumMap = {
ActionMethod.getMemory: 'getMemory',
ActionMethod.crash: 'crash',
ActionMethod.setupConfig: 'setupConfig',
ActionMethod.deleteFile: 'deleteFile',
ActionMethod.setState: 'setState',
ActionMethod.startTun: 'startTun',
ActionMethod.stopTun: 'stopTun',
@@ -330,16 +301,17 @@ const _$ActionMethodEnumMap = {
ActionMethod.getCurrentProfileName: 'getCurrentProfileName',
};
_$ActionResultImpl _$$ActionResultImplFromJson(Map<String, dynamic> json) =>
_$ActionResultImpl(
_ActionResult _$ActionResultFromJson(Map<String, dynamic> json) =>
_ActionResult(
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
data: json['data'],
id: json['id'] as String?,
code: $enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??
code:
$enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??
ResultType.success,
);
Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
Map<String, dynamic> _$ActionResultToJson(_ActionResult instance) =>
<String, dynamic>{
'method': _$ActionMethodEnumMap[instance.method]!,
'data': instance.data,
@@ -347,7 +319,4 @@ Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
'code': _$ResultTypeEnumMap[instance.code]!,
};
const _$ResultTypeEnumMap = {
ResultType.success: 0,
ResultType.error: -1,
};
const _$ResultTypeEnumMap = {ResultType.success: 0, ResultType.error: -1};

File diff suppressed because it is too large Load Diff

View File

@@ -6,17 +6,15 @@ part of '../profile.dart';
// JsonSerializableGenerator
// **************************************************************************
_$SubscriptionInfoImpl _$$SubscriptionInfoImplFromJson(
Map<String, dynamic> json) =>
_$SubscriptionInfoImpl(
_SubscriptionInfo _$SubscriptionInfoFromJson(Map<String, dynamic> json) =>
_SubscriptionInfo(
upload: (json['upload'] as num?)?.toInt() ?? 0,
download: (json['download'] as num?)?.toInt() ?? 0,
total: (json['total'] as num?)?.toInt() ?? 0,
expire: (json['expire'] as num?)?.toInt() ?? 0,
);
Map<String, dynamic> _$$SubscriptionInfoImplToJson(
_$SubscriptionInfoImpl instance) =>
Map<String, dynamic> _$SubscriptionInfoToJson(_SubscriptionInfo instance) =>
<String, dynamic>{
'upload': instance.upload,
'download': instance.download,
@@ -24,79 +22,79 @@ Map<String, dynamic> _$$SubscriptionInfoImplToJson(
'expire': instance.expire,
};
_$ProfileImpl _$$ProfileImplFromJson(Map<String, dynamic> json) =>
_$ProfileImpl(
id: json['id'] as String,
label: json['label'] as String?,
currentGroupName: json['currentGroupName'] as String?,
url: json['url'] as String? ?? '',
lastUpdateDate: json['lastUpdateDate'] == null
? null
: DateTime.parse(json['lastUpdateDate'] as String),
autoUpdateDuration:
Duration(microseconds: (json['autoUpdateDuration'] as num).toInt()),
subscriptionInfo: json['subscriptionInfo'] == null
? null
: SubscriptionInfo.fromJson(
json['subscriptionInfo'] as Map<String, dynamic>),
autoUpdate: json['autoUpdate'] as bool? ?? true,
selectedMap: (json['selectedMap'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
unfoldSet: (json['unfoldSet'] as List<dynamic>?)
?.map((e) => e as String)
.toSet() ??
const {},
overrideData: json['overrideData'] == null
? const OverrideData()
: OverrideData.fromJson(json['overrideData'] as Map<String, dynamic>),
);
_Profile _$ProfileFromJson(Map<String, dynamic> json) => _Profile(
id: json['id'] as String,
label: json['label'] as String?,
currentGroupName: json['currentGroupName'] as String?,
url: json['url'] as String? ?? '',
lastUpdateDate: json['lastUpdateDate'] == null
? null
: DateTime.parse(json['lastUpdateDate'] as String),
autoUpdateDuration: Duration(
microseconds: (json['autoUpdateDuration'] as num).toInt(),
),
subscriptionInfo: json['subscriptionInfo'] == null
? null
: SubscriptionInfo.fromJson(
json['subscriptionInfo'] as Map<String, dynamic>,
),
autoUpdate: json['autoUpdate'] as bool? ?? true,
selectedMap:
(json['selectedMap'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
unfoldSet:
(json['unfoldSet'] as List<dynamic>?)?.map((e) => e as String).toSet() ??
const {},
overrideData: json['overrideData'] == null
? const OverrideData()
: OverrideData.fromJson(json['overrideData'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ProfileImplToJson(_$ProfileImpl instance) =>
<String, dynamic>{
'id': instance.id,
'label': instance.label,
'currentGroupName': instance.currentGroupName,
'url': instance.url,
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
'subscriptionInfo': instance.subscriptionInfo,
'autoUpdate': instance.autoUpdate,
'selectedMap': instance.selectedMap,
'unfoldSet': instance.unfoldSet.toList(),
'overrideData': instance.overrideData,
};
Map<String, dynamic> _$ProfileToJson(_Profile instance) => <String, dynamic>{
'id': instance.id,
'label': instance.label,
'currentGroupName': instance.currentGroupName,
'url': instance.url,
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
'subscriptionInfo': instance.subscriptionInfo,
'autoUpdate': instance.autoUpdate,
'selectedMap': instance.selectedMap,
'unfoldSet': instance.unfoldSet.toList(),
'overrideData': instance.overrideData,
};
_$OverrideDataImpl _$$OverrideDataImplFromJson(Map<String, dynamic> json) =>
_$OverrideDataImpl(
_OverrideData _$OverrideDataFromJson(Map<String, dynamic> json) =>
_OverrideData(
enable: json['enable'] as bool? ?? false,
rule: json['rule'] == null
? const OverrideRule()
: OverrideRule.fromJson(json['rule'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$OverrideDataImplToJson(_$OverrideDataImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'rule': instance.rule,
};
Map<String, dynamic> _$OverrideDataToJson(_OverrideData instance) =>
<String, dynamic>{'enable': instance.enable, 'rule': instance.rule};
_$OverrideRuleImpl _$$OverrideRuleImplFromJson(Map<String, dynamic> json) =>
_$OverrideRuleImpl(
type: $enumDecodeNullable(_$OverrideRuleTypeEnumMap, json['type']) ??
_OverrideRule _$OverrideRuleFromJson(Map<String, dynamic> json) =>
_OverrideRule(
type:
$enumDecodeNullable(_$OverrideRuleTypeEnumMap, json['type']) ??
OverrideRuleType.added,
overrideRules: (json['overrideRules'] as List<dynamic>?)
overrideRules:
(json['overrideRules'] as List<dynamic>?)
?.map((e) => Rule.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
addedRules: (json['addedRules'] as List<dynamic>?)
addedRules:
(json['addedRules'] as List<dynamic>?)
?.map((e) => Rule.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$OverrideRuleImplToJson(_$OverrideRuleImpl instance) =>
Map<String, dynamic> _$OverrideRuleToJson(_OverrideRule instance) =>
<String, dynamic>{
'type': _$OverrideRuleTypeEnumMap[instance.type]!,
'overrideRules': instance.overrideRules,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,9 @@
// ignore_for_file: invalid_annotation_target
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -16,7 +15,7 @@ part 'generated/profile.g.dart';
typedef SelectedMap = Map<String, String>;
@freezed
class SubscriptionInfo with _$SubscriptionInfo {
abstract class SubscriptionInfo with _$SubscriptionInfo {
const factory SubscriptionInfo({
@Default(0) int upload,
@Default(0) int download,
@@ -45,7 +44,7 @@ class SubscriptionInfo with _$SubscriptionInfo {
}
@freezed
class Profile with _$Profile {
abstract class Profile with _$Profile {
const factory Profile({
required String id,
String? label,
@@ -66,10 +65,7 @@ class Profile with _$Profile {
factory Profile.fromJson(Map<String, Object?> json) =>
_$ProfileFromJson(json);
factory Profile.normal({
String? label,
String url = '',
}) {
factory Profile.normal({String? label, String url = ''}) {
return Profile(
label: label,
url: url,
@@ -80,7 +76,7 @@ class Profile with _$Profile {
}
@freezed
class OverrideData with _$OverrideData {
abstract class OverrideData with _$OverrideData {
const factory OverrideData({
@Default(false) bool enable,
@Default(OverrideRule()) OverrideRule rule,
@@ -100,7 +96,7 @@ extension OverrideDataExt on OverrideData {
}
@freezed
class OverrideRule with _$OverrideRule {
abstract class OverrideRule with _$OverrideRule {
const factory OverrideRule({
@Default(OverrideRuleType.added) OverrideRuleType type,
@Default([]) List<Rule> overrideRules,
@@ -113,9 +109,9 @@ class OverrideRule with _$OverrideRule {
extension OverrideRuleExt on OverrideRule {
List<Rule> get rules => switch (type == OverrideRuleType.override) {
true => overrideRules,
false => addedRules,
};
true => overrideRules,
false => addedRules,
};
OverrideRule updateRules(List<Rule> Function(List<Rule> rules) builder) {
if (type == OverrideRuleType.added) {
@@ -178,7 +174,7 @@ extension ProfileExtension on Profile {
}
Future<Profile> saveFile(Uint8List bytes) async {
final message = await clashCore.validateConfig(utf8.decode(bytes));
final message = await coreController.validateConfig(utf8.decode(bytes));
if (message.isNotEmpty) {
throw message;
}
@@ -188,7 +184,7 @@ extension ProfileExtension on Profile {
}
Future<Profile> saveFileWithString(String value) async {
final message = await clashCore.validateConfig(value);
final message = await coreController.validateConfig(value);
if (message.isNotEmpty) {
throw message;
}

View File

@@ -1,5 +1,4 @@
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart';
@@ -8,34 +7,23 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/selector.freezed.dart';
@freezed
class VM2<A, B> with _$VM2<A, B> {
const factory VM2({
required A a,
required B b,
}) = _VM2;
abstract class VM2<A, B> with _$VM2<A, B> {
const factory VM2({required A a, required B b}) = _VM2;
}
@freezed
class VM3<A, B, C> with _$VM3<A, B, C> {
const factory VM3({
required A a,
required B b,
required C c,
}) = _VM3;
abstract class VM3<A, B, C> with _$VM3<A, B, C> {
const factory VM3({required A a, required B b, required C c}) = _VM3;
}
@freezed
class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
const factory VM4({
required A a,
required B b,
required C c,
required D d,
}) = _VM4;
abstract class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
const factory VM4({required A a, required B b, required C c, required D d}) =
_VM4;
}
@freezed
class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
abstract class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
const factory VM5({
required A a,
required B b,
@@ -46,7 +34,7 @@ class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
}
@freezed
class StartButtonSelectorState with _$StartButtonSelectorState {
abstract class StartButtonSelectorState with _$StartButtonSelectorState {
const factory StartButtonSelectorState({
required bool isInit,
required bool hasProfile,
@@ -54,7 +42,7 @@ class StartButtonSelectorState with _$StartButtonSelectorState {
}
@freezed
class ProfilesSelectorState with _$ProfilesSelectorState {
abstract class ProfilesSelectorState with _$ProfilesSelectorState {
const factory ProfilesSelectorState({
required List<Profile> profiles,
required String? currentProfileId,
@@ -63,7 +51,7 @@ class ProfilesSelectorState with _$ProfilesSelectorState {
}
@freezed
class NetworkDetectionState with _$NetworkDetectionState {
abstract class NetworkDetectionState with _$NetworkDetectionState {
const factory NetworkDetectionState({
required bool isLoading,
required IpInfo? ipInfo,
@@ -71,7 +59,7 @@ class NetworkDetectionState with _$NetworkDetectionState {
}
@freezed
class TrayState with _$TrayState {
abstract class TrayState with _$TrayState {
const factory TrayState({
required Mode mode,
required int port,
@@ -87,7 +75,7 @@ class TrayState with _$TrayState {
}
@freezed
class NavigationState with _$NavigationState {
abstract class NavigationState with _$NavigationState {
const factory NavigationState({
required PageLabel pageLabel,
required List<NavigationItem> navigationItems,
@@ -98,45 +86,38 @@ class NavigationState with _$NavigationState {
}
@freezed
class GroupsState with _$GroupsState {
const factory GroupsState({
required List<Group> value,
}) = _GroupsState;
abstract class GroupsState with _$GroupsState {
const factory GroupsState({required List<Group> value}) = _GroupsState;
}
@freezed
class NavigationItemsState with _$NavigationItemsState {
const factory NavigationItemsState({
required List<NavigationItem> value,
}) = _NavigationItemsState;
abstract class NavigationItemsState with _$NavigationItemsState {
const factory NavigationItemsState({required List<NavigationItem> value}) =
_NavigationItemsState;
}
@freezed
class ProxiesListState with _$ProxiesListState {
abstract class ProxiesListState with _$ProxiesListState {
const factory ProxiesListState({
required List<Group> groups,
required Set<String> currentUnfoldSet,
required ProxiesSortType proxiesSortType,
required ProxyCardType proxyCardType,
required num sortNum,
required int columns,
}) = _ProxiesListState;
}
@freezed
class ProxiesTabState with _$ProxiesTabState {
abstract class ProxiesTabState with _$ProxiesTabState {
const factory ProxiesTabState({
required List<Group> groups,
required String? currentGroupName,
required ProxiesSortType proxiesSortType,
required ProxyCardType proxyCardType,
required num sortNum,
required int columns,
}) = _ProxiesTabState;
}
@freezed
class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
abstract class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
const factory ProxyGroupSelectorState({
required String? testUrl,
required ProxiesSortType proxiesSortType,
@@ -149,14 +130,14 @@ class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
}
@freezed
class MoreToolsSelectorState with _$MoreToolsSelectorState {
abstract class MoreToolsSelectorState with _$MoreToolsSelectorState {
const factory MoreToolsSelectorState({
required List<NavigationItem> navigationItems,
}) = _MoreToolsSelectorState;
}
@freezed
class PackageListSelectorState with _$PackageListSelectorState {
abstract class PackageListSelectorState with _$PackageListSelectorState {
const factory PackageListSelectorState({
required List<Package> packages,
required AccessControl accessControl,
@@ -178,32 +159,26 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
List<Package> getSortList(List<String> selectedList) {
final sort = accessControl.sort;
return list.sorted(
(a, b) {
return switch (sort) {
AccessSortType.none => 0,
AccessSortType.name => utils.sortByChar(
utils.getPinyin(a.label),
utils.getPinyin(b.label),
),
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
};
},
).sorted(
(a, b) {
final isSelectA = selectedList.contains(a.packageName);
final isSelectB = selectedList.contains(b.packageName);
if (isSelectA && isSelectB) return 0;
if (isSelectA) return -1;
if (isSelectB) return 1;
return 0;
},
);
return list.sorted((a, b) {
final isSelectA = selectedList.contains(a.packageName);
final isSelectB = selectedList.contains(b.packageName);
if (isSelectA != isSelectB) {
return isSelectA ? -1 : 1;
}
return switch (sort) {
AccessSortType.none => 0,
AccessSortType.name => a.label.compareTo(b.label),
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
};
});
}
}
@freezed
class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
abstract class ProxiesListHeaderSelectorState
with _$ProxiesListHeaderSelectorState {
const factory ProxiesListHeaderSelectorState({
required double offset,
required int currentIndex,
@@ -211,7 +186,7 @@ class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
}
@freezed
class ProxiesActionsState with _$ProxiesActionsState {
abstract class ProxiesActionsState with _$ProxiesActionsState {
const factory ProxiesActionsState({
required PageLabel pageLabel,
required ProxiesType type,
@@ -220,7 +195,7 @@ class ProxiesActionsState with _$ProxiesActionsState {
}
@freezed
class ProxyState with _$ProxyState {
abstract class ProxyState with _$ProxyState {
const factory ProxyState({
required bool isStart,
required bool systemProxy,
@@ -230,7 +205,7 @@ class ProxyState with _$ProxyState {
}
@freezed
class ClashConfigState with _$ClashConfigState {
abstract class ClashConfigState with _$ClashConfigState {
const factory ClashConfigState({
required bool overrideDns,
required ClashConfig clashConfig,
@@ -240,23 +215,24 @@ class ClashConfigState with _$ClashConfigState {
}
@freezed
class DashboardState with _$DashboardState {
abstract class DashboardState with _$DashboardState {
const factory DashboardState({
required List<DashboardWidget> dashboardWidgets,
required double viewWidth,
required double contentWidth,
}) = _DashboardState;
}
@freezed
class ProxyCardState with _$ProxyCardState {
const factory ProxyCardState({
abstract class SelectedProxyState with _$SelectedProxyState {
const factory SelectedProxyState({
required String proxyName,
@Default(false) bool group,
String? testUrl,
}) = _ProxyCardState;
}) = _SelectedProxyState;
}
@freezed
class VpnState with _$VpnState {
abstract class VpnState with _$VpnState {
const factory VpnState({
required TunStack stack,
required VpnProps vpnProps,
@@ -264,10 +240,10 @@ class VpnState with _$VpnState {
}
@freezed
class ProfileOverrideStateModel with _$ProfileOverrideStateModel {
const factory ProfileOverrideStateModel({
ClashConfigSnippet? snippet,
required Set<String> selectedRules,
abstract class ProfileOverrideModel with _$ProfileOverrideModel {
const factory ProfileOverrideModel({
@Default(ClashConfigSnippet()) ClashConfigSnippet snippet,
@Default({}) Set<String> selectedRules,
OverrideData? overrideData,
}) = _ProfileOverrideStateModel;
}) = _ProfileOverrideModel;
}

View File

@@ -4,14 +4,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/widget.freezed.dart';
@freezed
class ActivateState with _$ActivateState {
abstract class ActivateState with _$ActivateState {
const factory ActivateState({
required bool active,
}) = _ActivateState;
}
@freezed
class CommonMessage with _$CommonMessage {
abstract class CommonMessage with _$CommonMessage {
const factory CommonMessage({
required String id,
required String text,
@@ -20,7 +20,7 @@ class CommonMessage with _$CommonMessage {
}
@freezed
class AppBarState with _$AppBarState {
abstract class AppBarState with _$AppBarState {
const factory AppBarState({
@Default([]) List<Widget> actions,
AppBarSearchState? searchState,
@@ -29,7 +29,7 @@ class AppBarState with _$AppBarState {
}
@freezed
class AppBarSearchState with _$AppBarSearchState {
abstract class AppBarSearchState with _$AppBarSearchState {
const factory AppBarSearchState({
required Function(String) onSearch,
@Default(null) String? query,
@@ -37,7 +37,7 @@ class AppBarSearchState with _$AppBarSearchState {
}
@freezed
class AppBarEditState with _$AppBarEditState {
abstract class AppBarEditState with _$AppBarEditState {
const factory AppBarEditState({
@Default(0) int editCount,
required Function() onExit,

View File

@@ -23,7 +23,11 @@ class EditorPage extends ConsumerStatefulWidget {
final bool titleEditable;
final Function(BuildContext context, String title, String content)? onSave;
final Future<bool> Function(
BuildContext context, String title, String content)? onPop;
BuildContext context,
String title,
String content,
)?
onPop;
const EditorPage({
super.key,
@@ -33,9 +37,7 @@ class EditorPage extends ConsumerStatefulWidget {
this.onSave,
this.onPop,
this.supportRemoteDownload = false,
this.languages = const [
Language.yaml,
],
this.languages = const [Language.yaml],
});
@override
@@ -92,7 +94,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
Widget _wrapController(EditingValueChangeBuilder builder) {
return ValueListenableBuilder(
valueListenable: _controller,
builder: (_, value, ___) {
builder: (_, value, _) {
return builder(value);
},
);
@@ -101,7 +103,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
Widget _wrapTitleController(TextEditingValueChangeBuilder builder) {
return ValueListenableBuilder(
valueListenable: _titleController,
builder: (_, value, ___) {
builder: (_, value, _) {
return builder(value);
},
);
@@ -154,7 +156,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
Widget build(BuildContext context) {
final isMobileView = ref.watch(isMobileViewProvider);
return CommonPopScope(
onPop: () async {
onPop: (context) async {
if (widget.onPop == null) {
return true;
}
@@ -185,7 +187,8 @@ class _EditorPageState extends ConsumerState<EditorPage> {
_wrapController(
(value) => _wrapTitleController(
(value) => IconButton(
onPressed: _controller.text != widget.content ||
onPressed:
_controller.text != widget.content ||
_titleController.text != widget.title
? () {
widget.onSave!(
@@ -202,18 +205,14 @@ class _EditorPageState extends ConsumerState<EditorPage> {
if (widget.supportRemoteDownload)
IconButton(
onPressed: _handleImport,
icon: Icon(
Icons.arrow_downward,
),
icon: Icon(Icons.arrow_downward),
),
_wrapController(
(value) => CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: () {
open(
offset: Offset(-20, 20),
);
open(offset: Offset(-20, 20));
},
icon: const Icon(Icons.more_vert),
);
@@ -248,9 +247,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
readOnly: readOnly,
isMobileView: isMobileView,
),
padding: EdgeInsets.only(
right: 16,
),
padding: EdgeInsets.only(right: 16),
autocompleteSymbols: true,
focusNode: _focusNode,
scrollbarBuilder: (context, child, details) {
@@ -260,26 +257,22 @@ class _EditorPageState extends ConsumerState<EditorPage> {
);
},
toolbarController: ContextMenuControllerImpl(),
indicatorBuilder: (
context,
editingController,
chunkController,
notifier,
) {
return Row(
children: [
DefaultCodeLineNumber(
controller: editingController,
notifier: notifier,
),
DefaultCodeChunkIndicator(
width: 20,
controller: chunkController,
notifier: notifier,
)
],
);
},
indicatorBuilder:
(context, editingController, chunkController, notifier) {
return Row(
children: [
DefaultCodeLineNumber(
controller: editingController,
notifier: notifier,
),
DefaultCodeChunkIndicator(
width: 20,
controller: chunkController,
notifier: notifier,
),
],
);
},
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
controller: _controller,
style: CodeEditorStyle(
@@ -288,13 +281,9 @@ class _EditorPageState extends ConsumerState<EditorPage> {
codeTheme: CodeHighlightTheme(
languages: {
if (widget.languages.contains(Language.yaml))
'yaml': CodeHighlightThemeMode(
mode: langYaml,
),
'yaml': CodeHighlightThemeMode(mode: langYaml),
if (widget.languages.contains(Language.javaScript))
'javascript': CodeHighlightThemeMode(
mode: langJavascript,
),
'javascript': CodeHighlightThemeMode(mode: langJavascript),
},
theme: atomOneLightTheme,
),
@@ -318,33 +307,24 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
required this.controller,
required this.readOnly,
required this.isMobileView,
}) : height = (isMobileView
? _kDefaultFindPanelHeight * 2
: _kDefaultFindPanelHeight) +
8;
}) : height =
(isMobileView
? _kDefaultFindPanelHeight * 2
: _kDefaultFindPanelHeight) +
8;
@override
Size get preferredSize => Size(
double.infinity,
controller.value == null ? 0 : height,
);
Size get preferredSize =>
Size(double.infinity, controller.value == null ? 0 : height);
@override
Widget build(BuildContext context) {
if (controller.value == null) {
return const SizedBox(
width: 0,
height: 0,
);
return const SizedBox(width: 0, height: 0);
}
return Container(
padding: EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
margin: EdgeInsets.only(
bottom: 8,
),
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
margin: EdgeInsets.only(bottom: 8),
color: context.colorScheme.surface,
alignment: Alignment.centerLeft,
height: height,
@@ -368,18 +348,13 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
constraints: BoxConstraints(maxWidth: 360),
child: _buildFindInput(context, value),
),
SizedBox(
width: 12,
),
SizedBox(width: 12),
],
Text(
result,
style: context.textTheme.bodyMedium,
),
Text(result, style: context.textTheme.bodyMedium),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
spacing: 8,
spacing: 6,
children: [
_buildIconButton(
onPressed: value.result == null
@@ -397,21 +372,12 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
},
icon: Icons.arrow_downward,
),
SizedBox(
width: 2,
),
SizedBox(width: 2),
IconButton.filledTonal(
visualDensity: VisualDensity.compact,
onPressed: controller.close,
style: ButtonStyle(
padding: WidgetStatePropertyAll(
EdgeInsets.all(0),
),
),
icon: Icon(
Icons.close,
size: 16,
),
style: IconButton.styleFrom(padding: EdgeInsets.zero),
icon: Icon(Icons.close, size: 16),
),
],
),
@@ -421,13 +387,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
if (isMobileView) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
bar,
SizedBox(
height: 4,
),
_buildFindInput(context, value),
],
children: [bar, SizedBox(height: 4), _buildFindInput(context, value)],
);
}
return bar;
@@ -469,11 +429,9 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
controller.toggleRegex();
},
),
SizedBox(
width: 4,
),
SizedBox(width: 4),
],
)
),
],
);
}
@@ -490,9 +448,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
style: context.textTheme.bodyMedium,
decoration: InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12,
),
contentPadding: EdgeInsets.symmetric(horizontal: 12),
),
onSubmitted: (_) {
onSubmitted();
@@ -521,27 +477,18 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
: IconButton(
onPressed: onPressed,
padding: EdgeInsets.all(2),
icon: Text(
text,
style: context.textTheme.bodySmall,
),
icon: Text(text, style: context.textTheme.bodySmall),
),
),
);
}
Widget _buildIconButton({
required IconData icon,
VoidCallback? onPressed,
}) {
Widget _buildIconButton({required IconData icon, VoidCallback? onPressed}) {
return IconButton(
visualDensity: VisualDensity.compact,
onPressed: onPressed,
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),
icon: Icon(
icon,
size: 16,
),
style: IconButton.styleFrom(padding: EdgeInsets.all(0)),
icon: Icon(icon, size: 16),
);
}
}
@@ -575,7 +522,7 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
builder: (context) => CodeEditorTapRegion(
child: ValueListenableBuilder(
valueListenable: controller,
builder: (_, __, child) {
builder: (_, _, child) {
final isNotEmpty = controller.selectedText.isNotEmpty;
final isAllSelected = controller.isAllSelected;
final hasSelected = controller.selectedText.isNotEmpty;
@@ -608,25 +555,25 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
return TextSelectionToolbar(
anchorAbove: anchors.primaryAnchor,
anchorBelow: anchors.secondaryAnchor ?? Offset.zero,
children: menus.asMap().entries.map(
(MapEntry<int, PopupMenuItemData> entry) {
return TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(
entry.key,
menus.length,
),
alignment: AlignmentDirectional.centerStart,
onPressed: () {
if (entry.value.onPressed == null) {
return;
}
entry.value.onPressed!();
_removeOverLayEntry();
},
child: Text(entry.value.label),
);
},
).toList(),
children: menus.asMap().entries.map((
MapEntry<int, PopupMenuItemData> entry,
) {
return TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(
entry.key,
menus.length,
),
alignment: AlignmentDirectional.centerStart,
onPressed: () {
if (entry.value.onPressed == null) {
return;
}
entry.value.onPressed!();
_removeOverLayEntry();
},
child: Text(entry.value.label),
);
}).toList(),
);
},
),
@@ -662,8 +609,12 @@ class _NoInputBorder extends InputBorder {
}
@override
void paintInterior(Canvas canvas, Rect rect, Paint paint,
{TextDirection? textDirection}) {
void paintInterior(
Canvas canvas,
Rect rect,
Paint paint, {
TextDirection? textDirection,
}) {
canvas.drawRect(rect, paint);
}
@@ -697,10 +648,7 @@ class _ImportOptionsDialogState extends State<_ImportOptionsDialog> {
Widget build(BuildContext context) {
return CommonDialog(
title: appLocalizations.import,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Wrap(
children: [
ListItem(
@@ -714,7 +662,7 @@ class _ImportOptionsDialogState extends State<_ImportOptionsDialog> {
_handleOnTab(ImportOption.file);
},
title: Text(appLocalizations.importFile),
)
),
],
),
);

View File

@@ -1,5 +1,6 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/manager/app_manager.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -15,88 +16,86 @@ class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return HomeBackScope(
child: Material(
color: context.colorScheme.surface,
child: Consumer(
builder: (context, ref, __) {
final state = ref.watch(navigationStateProvider);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final pageView = _HomePageView(pageBuilder: (_, index) {
final navigationItem = state.navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = isMobile
? KeepScope(
keep: navigationItem.keep,
child: navigationView,
)
: KeepScope(
keep: navigationItem.keep,
child: Navigator(
onGenerateRoute: (_) {
return CommonRoute(
builder: (_) => navigationView,
);
},
),
);
return view;
});
final currentIndex = state.currentIndex;
final bottomNavigationBar = NavigationBarTheme(
data: _NavigationBarDefaultsM3(context),
child: NavigationBar(
destinations: navigationItems
.map(
(e) => NavigationDestination(
icon: e.icon,
label: Intl.message(e.label.name),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
return HomeBackScopeContainer(
child: AppSidebarContainer(
child: Material(
color: context.colorScheme.surface,
child: Consumer(
builder: (context, ref, _) {
final state = ref.watch(navigationStateProvider);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final pageView = _HomePageView(
pageBuilder: (_, index) {
final navigationItem = state.navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = KeepScope(
keep: navigationItem.keep,
child: isMobile
? navigationView
: Navigator(
pages: [MaterialPage(child: navigationView)],
onDidRemovePage: (_) {},
),
);
return view;
},
selectedIndex: currentIndex,
),
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: globalState.appState.systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
);
final currentIndex = state.currentIndex;
final bottomNavigationBar = NavigationBarTheme(
data: _NavigationBarDefaultsM3(context),
child: NavigationBar(
destinations: navigationItems
.map(
(e) => NavigationDestination(
icon: e.icon,
label: Intl.message(e.label.name),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
selectedIndex: currentIndex,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: globalState.appState.systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: pageView,
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
removeLeft: true,
removeRight: true,
context: context,
child: pageView,
child: bottomNavigationBar,
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
),
],
),
);
} else {
return pageView;
}
},
],
),
);
} else {
return pageView;
}
},
),
),
),
);
@@ -106,9 +105,7 @@ class HomePage extends StatelessWidget {
class _HomePageView extends ConsumerStatefulWidget {
final IndexedWidgetBuilder pageBuilder;
const _HomePageView({
required this.pageBuilder,
});
const _HomePageView({required this.pageBuilder});
@override
ConsumerState createState() => _HomePageViewState();
@@ -120,9 +117,7 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
@override
initState() {
super.initState();
_pageController = PageController(
initialPage: _pageIndex,
);
_pageController = PageController(initialPage: _pageIndex);
ref.listenManual(currentPageLabelProvider, (prev, next) {
if (prev != next) {
_toPage(next);
@@ -142,8 +137,10 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
);
}
Future<void> _toPage(PageLabel pageLabel,
[bool ignoreAnimateTo = false]) async {
Future<void> _toPage(
PageLabel pageLabel, [
bool ignoreAnimateTo = false,
]) async {
if (!mounted) {
return;
}
@@ -178,8 +175,9 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
@override
Widget build(BuildContext context) {
final itemCount = ref.watch(currentNavigationItemsStateProvider
.select((state) => state.value.length));
final itemCount = ref.watch(
currentNavigationItemsStateProvider.select((state) => state.value.length),
);
return PageView.builder(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
@@ -193,11 +191,11 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
_NavigationBarDefaultsM3(this.context)
: super(
height: 80.0,
elevation: 3.0,
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
);
: super(
height: 80.0,
elevation: 3.0,
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@@ -220,8 +218,8 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
color: states.contains(WidgetState.disabled)
? _colors.onSurfaceVariant.opacity38
: states.contains(WidgetState.selected)
? _colors.onSecondaryContainer
: _colors.onSurfaceVariant,
? _colors.onSecondaryContainer
: _colors.onSurfaceVariant,
);
});
}
@@ -237,37 +235,38 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
final TextStyle style = _textTheme.labelMedium!;
return style.apply(
overflow: TextOverflow.ellipsis,
color: states.contains(WidgetState.disabled)
? _colors.onSurfaceVariant.opacity38
: states.contains(WidgetState.selected)
? _colors.onSurface
: _colors.onSurfaceVariant);
overflow: TextOverflow.ellipsis,
color: states.contains(WidgetState.disabled)
? _colors.onSurfaceVariant.opacity38
: states.contains(WidgetState.selected)
? _colors.onSurface
: _colors.onSurfaceVariant,
);
});
}
}
class HomeBackScope extends StatelessWidget {
class HomeBackScopeContainer extends ConsumerWidget {
final Widget child;
const HomeBackScope({super.key, required this.child});
const HomeBackScopeContainer({super.key, required this.child});
@override
Widget build(BuildContext context) {
if (system.isAndroid) {
return CommonPopScope(
onPop: () async {
final canPop = Navigator.canPop(context);
if (canPop) {
Navigator.pop(context);
} else {
await globalState.appController.handleBackOrExit();
}
return false;
},
child: child,
);
}
return child;
Widget build(BuildContext context, ref) {
return CommonPopScope(
onPop: (context) async {
final pageLabel = ref.read(currentPageLabelProvider);
final realContext =
GlobalObjectKey(pageLabel).currentContext ?? context;
final canPop = Navigator.canPop(realContext);
if (canPop) {
Navigator.of(realContext).pop();
} else {
await globalState.appController.handleBackOrExit();
}
return false;
},
child: child,
);
}
}

View File

@@ -33,10 +33,7 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
void _handleBarcode(BarcodeCapture barcodeCapture) {
final barcode = barcodeCapture.barcodes.first;
if (barcode.type == BarcodeType.url) {
Navigator.pop<String>(
context,
barcode.rawValue,
);
Navigator.pop<String>(context, barcode.rawValue);
} else {
Navigator.pop(context);
}
@@ -78,16 +75,14 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
scanWindow: scanWindow,
),
),
CustomPaint(
painter: ScannerOverlay(scanWindow: scanWindow),
),
CustomPaint(painter: ScannerOverlay(scanWindow: scanWindow)),
AppBar(
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
leading: IconButton(
style: const ButtonStyle(
iconSize: WidgetStatePropertyAll(32),
foregroundColor: WidgetStatePropertyAll(Colors.white),
style: IconButton.styleFrom(
iconSize: 32,
foregroundColor: Colors.white,
),
onPressed: () {
Navigator.of(context).pop();
@@ -121,18 +116,16 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
child: IconButton(
color: Colors.white,
icon: icon,
style: ButtonStyle(
foregroundColor:
const WidgetStatePropertyAll(Colors.white),
backgroundColor:
WidgetStatePropertyAll(backgroundColor),
style: IconButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: backgroundColor,
),
onPressed: () => controller.toggleTorch(),
),
),
);
},
)
),
],
),
Container(
@@ -140,9 +133,9 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
alignment: Alignment.bottomCenter,
child: IconButton(
color: Colors.white,
style: const ButtonStyle(
foregroundColor: WidgetStatePropertyAll(Colors.white),
backgroundColor: WidgetStatePropertyAll(Colors.grey),
style: IconButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.grey,
),
padding: const EdgeInsets.all(16),
iconSize: 32.0,
@@ -166,10 +159,7 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
}
class ScannerOverlay extends CustomPainter {
const ScannerOverlay({
required this.scanWindow,
this.borderRadius = 12.0,
});
const ScannerOverlay({required this.scanWindow, this.borderRadius = 12.0});
final Rect scanWindow;
final double borderRadius;
@@ -179,8 +169,8 @@ class ScannerOverlay extends CustomPainter {
final backgroundPath = Path()..addRect(Rect.largest);
final cutoutPath = Path()
..addRRect(
RRect.fromRectAndCorners(
..addRSuperellipse(
RSuperellipse.fromRectAndCorners(
scanWindow,
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),
@@ -205,7 +195,7 @@ class ScannerOverlay extends CustomPainter {
..style = PaintingStyle.stroke
..strokeWidth = 4.0;
final borderRect = RRect.fromRectAndCorners(
final border = RSuperellipse.fromRectAndCorners(
scanWindow,
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),
@@ -214,7 +204,7 @@ class ScannerOverlay extends CustomPainter {
);
canvas.drawPath(backgroundWithCutout, backgroundPaint);
canvas.drawRRect(borderRect, borderPaint);
canvas.drawRSuperellipse(border, borderPaint);
}
@override

View File

@@ -1,13 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/common/app_localizations.dart';
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/common/system.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
class App {
static App? _instance;
@@ -15,19 +16,13 @@ class App {
Function()? onExit;
App._internal() {
methodChannel = const MethodChannel('app');
methodChannel = const MethodChannel('$packageName/app');
methodChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'exit':
if (onExit != null) {
await onExit!();
}
case 'getText':
try {
return Intl.message(call.arguments as String);
} catch (_) {
return '';
}
default:
throw MissingPluginException();
}
@@ -44,40 +39,48 @@ class App {
}
Future<List<Package>> getPackages() async {
final packagesString =
await methodChannel.invokeMethod<String>('getPackages');
final packagesString = await methodChannel.invokeMethod<String>(
'getPackages',
);
return Isolate.run<List<Package>>(() {
final List<dynamic> packagesRaw =
packagesString != null ? json.decode(packagesString) : [];
final List<dynamic> packagesRaw = packagesString != null
? json.decode(packagesString)
: [];
return packagesRaw.map((e) => Package.fromJson(e)).toSet().toList();
});
}
Future<List<String>> getChinaPackageNames() async {
final packageNamesString =
await methodChannel.invokeMethod<String>('getChinaPackageNames');
final packageNamesString = await methodChannel.invokeMethod<String>(
'getChinaPackageNames',
);
return Isolate.run<List<String>>(() {
final List<dynamic> packageNamesRaw =
packageNamesString != null ? json.decode(packageNamesString) : [];
final List<dynamic> packageNamesRaw = packageNamesString != null
? json.decode(packageNamesString)
: [];
return packageNamesRaw.map((e) => e.toString()).toList();
});
}
Future<bool?> requestNotificationsPermission() async {
return await methodChannel.invokeMethod<bool>(
'requestNotificationsPermission',
);
}
Future<bool> openFile(String path) async {
return await methodChannel.invokeMethod<bool>('openFile', {
'path': path,
}) ??
return await methodChannel.invokeMethod<bool>('openFile', {'path': path}) ??
false;
}
Future<ImageProvider?> getPackageIcon(String packageName) async {
final base64 = await methodChannel.invokeMethod<String>('getPackageIcon', {
final path = await methodChannel.invokeMethod<String>('getPackageIcon', {
'packageName': packageName,
});
if (base64 == null) {
if (path == null) {
return null;
}
return MemoryImage(base64Decode(base64));
return FileImage(File(path));
}
Future<bool?> tip(String? message) async {

View File

@@ -2,45 +2,120 @@ import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/common/system.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../clash/lib.dart';
abstract mixin class ServiceListener {
void onServiceEvent(CoreEvent event) {}
void onServiceCrash(String message) {}
}
class Service {
static Service? _instance;
late MethodChannel methodChannel;
ReceivePort? receiver;
Service._internal() {
methodChannel = const MethodChannel('service');
}
final ObserverList<ServiceListener> _listeners =
ObserverList<ServiceListener>();
factory Service() {
_instance ??= Service._internal();
return _instance!;
}
Future<bool?> init() async {
return await methodChannel.invokeMethod<bool>('init');
}
Future<bool?> destroy() async {
return await methodChannel.invokeMethod<bool>('destroy');
}
Future<bool?> startVpn() async {
final options = await clashLib?.getAndroidVpnOptions();
return await methodChannel.invokeMethod<bool>('startVpn', {
'data': json.encode(options),
Service._internal() {
methodChannel = const MethodChannel('$packageName/service');
methodChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'getVpnOptions':
return handleGetVpnOptions();
case 'event':
final data = call.arguments as String? ?? '';
final result = ActionResult.fromJson(json.decode(data));
for (final listener in _listeners) {
listener.onServiceEvent(CoreEvent.fromJson(result.data));
}
break;
case 'crash':
final message = call.arguments as String? ?? '';
for (final listener in _listeners) {
listener.onServiceCrash(message);
}
break;
default:
throw MissingPluginException();
}
});
}
Future<bool?> stopVpn() async {
return await methodChannel.invokeMethod<bool>('stopVpn');
Future<ActionResult?> invokeAction(Action action) async {
final data = await methodChannel.invokeMethod<String>(
'invokeAction',
json.encode(action),
);
if (data == null) {
return null;
}
return ActionResult.fromJson(json.decode(data));
}
String handleGetVpnOptions() {
return json.encode(globalState.getVpnOptions());
}
Future<bool> start() async {
return await methodChannel.invokeMethod<bool>('start') ?? false;
}
Future<bool> stop() async {
return await methodChannel.invokeMethod<bool>('stop') ?? false;
}
Future<String> syncAndroidState(AndroidState state) async {
return await methodChannel.invokeMethod<String>(
'syncState',
json.encode(state),
) ??
'';
}
Future<String> init() async {
return await methodChannel.invokeMethod<String>(
'init',
!globalState.isService,
) ??
'';
}
Future<bool> shutdown() async {
return await methodChannel.invokeMethod<bool>('shutdown') ?? true;
}
Future<DateTime?> getRunTime() async {
final ms = await methodChannel.invokeMethod<int>('getRunTime') ?? 0;
if (ms == 0) {
return null;
}
return DateTime.fromMillisecondsSinceEpoch(ms);
}
bool get hasListeners {
return _listeners.isNotEmpty;
}
void addListener(ServiceListener listener) {
_listeners.add(listener);
}
void removeListener(ServiceListener listener) {
_listeners.remove(listener);
}
}
Service? get service =>
system.isAndroid && !globalState.isService ? Service() : null;
Service? get service => system.isAndroid ? Service() : null;

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/common/system.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -13,7 +14,7 @@ abstract mixin class TileListener {
}
class Tile {
final MethodChannel _channel = const MethodChannel('tile');
final MethodChannel _channel = const MethodChannel('$packageName/tile');
Tile._() {
_channel.setMethodCallHandler(_methodCallHandler);

View File

@@ -1,70 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
abstract mixin class VpnListener {
void onDnsChanged(String dns) {}
}
class Vpn {
static Vpn? _instance;
late MethodChannel methodChannel;
FutureOr<String> Function()? handleGetStartForegroundParams;
Vpn._internal() {
methodChannel = const MethodChannel('vpn');
methodChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'gc':
clashCore.requestGc();
case 'getStartForegroundParams':
if (handleGetStartForegroundParams != null) {
return await handleGetStartForegroundParams!();
}
return '';
case 'status':
return clashLibHandler?.getRunTime() != null;
default:
for (final VpnListener listener in _listeners) {
switch (call.method) {
case 'dnsChanged':
final dns = call.arguments as String;
listener.onDnsChanged(dns);
}
}
}
});
}
factory Vpn() {
_instance ??= Vpn._internal();
return _instance!;
}
final ObserverList<VpnListener> _listeners = ObserverList<VpnListener>();
Future<bool?> start(AndroidVpnOptions options) async {
return await methodChannel.invokeMethod<bool>('start', {
'data': json.encode(options),
});
}
Future<bool?> stop() async {
return await methodChannel.invokeMethod<bool>('stop');
}
void addListener(VpnListener listener) {
_listeners.add(listener);
}
void removeListener(VpnListener listener) {
_listeners.remove(listener);
}
}
Vpn? get vpn => globalState.isService ? Vpn() : null;

View File

@@ -3,7 +3,6 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'generated/app.g.dart';
@@ -17,9 +16,7 @@ class RealTunEnable extends _$RealTunEnable with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
realTunEnable: value,
);
globalState.appState = globalState.appState.copyWith(realTunEnable: value);
}
}
@@ -31,14 +28,12 @@ class Logs extends _$Logs with AutoDisposeNotifierMixin {
}
void addLog(Log value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
logs: value,
);
globalState.appState = globalState.appState.copyWith(logs: value);
}
}
@@ -51,18 +46,19 @@ class Requests extends _$Requests with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
requests: value,
);
globalState.appState = globalState.appState.copyWith(requests: value);
}
void addRequest(TrackerInfo value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
}
@riverpod
class Providers extends _$Providers with AutoDisposeNotifierMixin {
class Providers extends _$Providers with AnyNotifierMixin {
@override
List<ExternalProvider> get value => globalState.appState.providers;
@override
List<ExternalProvider> build() {
return globalState.appState.providers;
@@ -70,16 +66,18 @@ class Providers extends _$Providers with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
providers: value,
);
globalState.appState = globalState.appState.copyWith(providers: value);
}
void setProvider(ExternalProvider? provider) {
if (!ref.mounted) {
return;
}
if (provider == null) return;
final index = state.indexWhere((item) => item.name == provider.name);
final index = value.indexWhere((item) => item.name == provider.name);
if (index == -1) return;
state = List.from(state)..[index] = provider;
final newState = List<ExternalProvider>.from(value)..[index] = provider;
value = newState;
}
}
@@ -92,9 +90,7 @@ class Packages extends _$Packages with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
packages: value,
);
globalState.appState = globalState.appState.copyWith(packages: value);
}
}
@@ -108,13 +104,11 @@ class SystemBrightness extends _$SystemBrightness
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
brightness: value,
);
globalState.appState = globalState.appState.copyWith(brightness: value);
}
void setState(Brightness value) {
state = value;
this.value = value;
}
}
@@ -127,17 +121,15 @@ class Traffics extends _$Traffics with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
traffics: value,
);
globalState.appState = globalState.appState.copyWith(traffics: value);
}
void addTraffic(Traffic value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
void clear() {
state = state.copyWith()..clear();
value = state.copyWith()..clear();
}
}
@@ -150,9 +142,7 @@ class TotalTraffic extends _$TotalTraffic with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
totalTraffic: value,
);
globalState.appState = globalState.appState.copyWith(totalTraffic: value);
}
}
@@ -165,17 +155,7 @@ class LocalIp extends _$LocalIp with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
localIp: value,
);
}
@override
set state(String? value) {
super.state = value;
globalState.appState = globalState.appState.copyWith(
localIp: state,
);
globalState.appState = globalState.appState.copyWith(localIp: value);
}
}
@@ -188,13 +168,7 @@ class RunTime extends _$RunTime with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
runTime: value,
);
}
bool get isStart {
return state != null;
globalState.appState = globalState.appState.copyWith(runTime: value);
}
}
@@ -207,14 +181,21 @@ class ViewSize extends _$ViewSize with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
viewSize: value,
);
globalState.appState = globalState.appState.copyWith(viewSize: value);
}
}
@riverpod
class SideWidth extends _$SideWidth with AutoDisposeNotifierMixin {
@override
double build() {
return globalState.appState.sideWidth;
}
ViewMode get viewMode => utils.getViewMode(state.width);
bool get isMobileView => viewMode == ViewMode.mobile;
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(sideWidth: value);
}
}
@riverpod
@@ -246,9 +227,7 @@ class Init extends _$Init with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
isInit: value,
);
globalState.appState = globalState.appState.copyWith(isInit: value);
}
}
@@ -262,9 +241,7 @@ class CurrentPageLabel extends _$CurrentPageLabel
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
pageLabel: value,
);
globalState.appState = globalState.appState.copyWith(pageLabel: value);
}
}
@@ -277,9 +254,7 @@ class SortNum extends _$SortNum with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
sortNum: value,
);
globalState.appState = globalState.appState.copyWith(sortNum: value);
}
int add() => state++;
@@ -294,9 +269,7 @@ class CheckIpNum extends _$CheckIpNum with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
checkIpNum: value,
);
globalState.appState = globalState.appState.copyWith(checkIpNum: value);
}
int add() => state++;
@@ -311,9 +284,7 @@ class BackBlock extends _$BackBlock with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
backBlock: value,
);
globalState.appState = globalState.appState.copyWith(backBlock: value);
}
}
@@ -326,9 +297,7 @@ class Loading extends _$Loading with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
loading: value,
);
globalState.appState = globalState.appState.copyWith(loading: value);
}
}
@@ -341,9 +310,7 @@ class Version extends _$Version with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
version: value,
);
globalState.appState = globalState.appState.copyWith(version: value);
}
}
@@ -356,9 +323,7 @@ class Groups extends _$Groups with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
groups: value,
);
globalState.appState = globalState.appState.copyWith(groups: value);
}
}
@@ -371,9 +336,7 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
delayMap: value,
);
globalState.appState = globalState.appState.copyWith(delayMap: value);
}
void setDelay(Delay delay) {
@@ -383,7 +346,7 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
newDelayMap[delay.url] = {};
}
newDelayMap[delay.url]![delay.name] = delay.value;
state = newDelayMap;
value = newDelayMap;
}
}
}
@@ -403,3 +366,57 @@ class SystemUiOverlayStyleState extends _$SystemUiOverlayStyleState
);
}
}
@riverpod
class ProfileOverrideState extends _$ProfileOverrideState
with AutoDisposeNotifierMixin {
@override
ProfileOverrideModel? build() {
return globalState.appState.profileOverrideModel;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
profileOverrideModel: value,
);
}
void updateState(
ProfileOverrideModel? Function(ProfileOverrideModel? state) builder,
) {
final value = builder(state);
if (value == null) {
return;
}
this.value = value;
}
}
@Riverpod(name: 'coreStatusProvider')
class _CoreStatus extends _$CoreStatus with AutoDisposeNotifierMixin {
@override
CoreStatus build() {
return globalState.appState.coreStatus;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(coreStatus: value);
}
}
@riverpod
class QueryMap extends _$QueryMap with AutoDisposeNotifierMixin {
@override
Map<QueryTag, String> build() => globalState.appState.queryMap;
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(queryMap: value);
}
void updateQuery(QueryTag tag, String value) {
this.value = Map.from(globalState.appState.queryMap)..[tag] = value;
}
}

View File

@@ -14,13 +14,11 @@ class AppSetting extends _$AppSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
appSetting: value,
);
globalState.config = globalState.config.copyWith(appSetting: value);
}
void updateState(AppSettingProps Function(AppSettingProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -33,13 +31,11 @@ class WindowSetting extends _$WindowSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
windowProps: value,
);
globalState.config = globalState.config.copyWith(windowProps: value);
}
void updateState(WindowProps Function(WindowProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -52,13 +48,11 @@ class VpnSetting extends _$VpnSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
vpnProps: value,
);
globalState.config = globalState.config.copyWith(vpnProps: value);
}
void updateState(VpnProps Function(VpnProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -71,13 +65,11 @@ class NetworkSetting extends _$NetworkSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
networkProps: value,
);
globalState.config = globalState.config.copyWith(networkProps: value);
}
void updateState(NetworkProps Function(NetworkProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -90,13 +82,11 @@ class ThemeSetting extends _$ThemeSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
themeProps: value,
);
globalState.config = globalState.config.copyWith(themeProps: value);
}
void updateState(ThemeProps Function(ThemeProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -109,15 +99,15 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
profiles: value,
);
globalState.config = globalState.config.copyWith(profiles: value);
}
String? _getLabel(String? label, String id) {
final realLabel = label ?? id;
final hasDup = state.indexWhere(
(element) => element.label == realLabel && element.id != id) !=
final hasDup =
state.indexWhere(
(element) => element.label == realLabel && element.id != id,
) !=
-1;
if (hasDup) {
return _getLabel(utils.getOverwriteLabel(realLabel), id);
@@ -128,8 +118,9 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
void setProfile(Profile profile) {
final List<Profile> profilesTemp = List.from(state);
final index =
profilesTemp.indexWhere((element) => element.id == profile.id);
final index = profilesTemp.indexWhere(
(element) => element.id == profile.id,
);
final updateProfile = profile.copyWith(
label: _getLabel(profile.label, profile.id),
);
@@ -138,21 +129,23 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
} else {
profilesTemp[index] = updateProfile;
}
state = profilesTemp;
value = profilesTemp;
}
void updateProfile(
String profileId, Profile Function(Profile profile) builder) {
String profileId,
Profile Function(Profile profile) builder,
) {
final List<Profile> profilesTemp = List.from(state);
final index = profilesTemp.indexWhere((element) => element.id == profileId);
if (index != -1) {
profilesTemp[index] = builder(profilesTemp[index]);
}
state = profilesTemp;
value = profilesTemp;
}
void deleteProfileById(String id) {
state = state.where((element) => element.id != id).toList();
value = state.where((element) => element.id != id).toList();
}
}
@@ -166,9 +159,7 @@ class CurrentProfileId extends _$CurrentProfileId
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
currentProfileId: value,
);
globalState.config = globalState.config.copyWith(currentProfileId: value);
}
}
@@ -181,13 +172,11 @@ class AppDAVSetting extends _$AppDAVSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
dav: value,
);
globalState.config = globalState.config.copyWith(dav: value);
}
void updateState(DAV? Function(DAV? state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -200,9 +189,7 @@ class OverrideDns extends _$OverrideDns with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
overrideDns: value,
);
globalState.config = globalState.config.copyWith(overrideDns: value);
}
}
@@ -215,9 +202,7 @@ class HotKeyActions extends _$HotKeyActions with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
hotKeyActions: value,
);
globalState.config = globalState.config.copyWith(hotKeyActions: value);
}
}
@@ -231,13 +216,11 @@ class ProxiesStyleSetting extends _$ProxiesStyleSetting
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
proxiesStyle: value,
);
globalState.config = globalState.config.copyWith(proxiesStyle: value);
}
void updateState(ProxiesStyle Function(ProxiesStyle state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -250,9 +233,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
scriptProps: value,
);
globalState.config = globalState.config.copyWith(scriptProps: value);
}
void setScript(Script script) {
@@ -263,15 +244,11 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
} else {
list.add(script);
}
state = state.copyWith(
scripts: list,
);
value = state.copyWith(scripts: list);
}
void setId(String id) {
state = state.copyWith(
currentId: state.currentId != id ? id : null,
);
value = state.copyWith(currentId: state.currentId != id ? id : null);
}
void del(String id) {
@@ -281,10 +258,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
list.removeAt(index);
}
final nextId = id == state.currentId ? null : state.currentId;
state = state.copyWith(
scripts: list,
currentId: nextId,
);
state = state.copyWith(scripts: list, currentId: nextId);
}
bool isExits(String label) {
@@ -305,13 +279,11 @@ class PatchClashConfig extends _$PatchClashConfig
if (newState == null) {
return;
}
state = newState;
value = newState;
}
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
patchClashConfig: value,
);
globalState.config = globalState.config.copyWith(patchClashConfig: value);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,208 +6,693 @@ part of '../config.dart';
// RiverpodGenerator
// **************************************************************************
String _$appSettingHash() => r'13a93334e18b97f5d52eb3e05bbc7b0b8a5c453e';
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// See also [AppSetting].
@ProviderFor(AppSetting)
final appSettingProvider =
AutoDisposeNotifierProvider<AppSetting, AppSettingProps>.internal(
AppSetting.new,
name: r'appSettingProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$appSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const appSettingProvider = AppSettingProvider._();
typedef _$AppSetting = AutoDisposeNotifier<AppSettingProps>;
String _$windowSettingHash() => r'9bf31c7e08fab84213f31e249270f9d730bdf711';
final class AppSettingProvider
extends $NotifierProvider<AppSetting, AppSettingProps> {
const AppSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'appSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$appSettingHash();
@$internal
@override
AppSetting create() => AppSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(AppSettingProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<AppSettingProps>(value),
);
}
}
String _$appSettingHash() => r'7ec7fbf146e690dea42cf854fa4452b2652d8a46';
abstract class _$AppSetting extends $Notifier<AppSettingProps> {
AppSettingProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AppSettingProps, AppSettingProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AppSettingProps, AppSettingProps>,
AppSettingProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [WindowSetting].
@ProviderFor(WindowSetting)
final windowSettingProvider =
AutoDisposeNotifierProvider<WindowSetting, WindowProps>.internal(
WindowSetting.new,
name: r'windowSettingProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$windowSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const windowSettingProvider = WindowSettingProvider._();
typedef _$WindowSetting = AutoDisposeNotifier<WindowProps>;
String _$vpnSettingHash() => r'3dae8b56504bfb906aca546c5a5389d79d259a5e';
final class WindowSettingProvider
extends $NotifierProvider<WindowSetting, WindowProps> {
const WindowSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'windowSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$windowSettingHash();
@$internal
@override
WindowSetting create() => WindowSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(WindowProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<WindowProps>(value),
);
}
}
String _$windowSettingHash() => r'fc0e5c4ec95a57a24e0e656fc2fab6f31add31e7';
abstract class _$WindowSetting extends $Notifier<WindowProps> {
WindowProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<WindowProps, WindowProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<WindowProps, WindowProps>,
WindowProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [VpnSetting].
@ProviderFor(VpnSetting)
final vpnSettingProvider =
AutoDisposeNotifierProvider<VpnSetting, VpnProps>.internal(
VpnSetting.new,
name: r'vpnSettingProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$vpnSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const vpnSettingProvider = VpnSettingProvider._();
typedef _$VpnSetting = AutoDisposeNotifier<VpnProps>;
String _$networkSettingHash() => r'5a30d4cbfaba94cc29ad08dc1771ebb368b4ba14';
final class VpnSettingProvider extends $NotifierProvider<VpnSetting, VpnProps> {
const VpnSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'vpnSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$vpnSettingHash();
@$internal
@override
VpnSetting create() => VpnSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(VpnProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<VpnProps>(value),
);
}
}
String _$vpnSettingHash() => r'1dad4881ae7bcec76678585ac7b84f820b2ca92b';
abstract class _$VpnSetting extends $Notifier<VpnProps> {
VpnProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<VpnProps, VpnProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<VpnProps, VpnProps>,
VpnProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [NetworkSetting].
@ProviderFor(NetworkSetting)
final networkSettingProvider =
AutoDisposeNotifierProvider<NetworkSetting, NetworkProps>.internal(
NetworkSetting.new,
name: r'networkSettingProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$networkSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const networkSettingProvider = NetworkSettingProvider._();
typedef _$NetworkSetting = AutoDisposeNotifier<NetworkProps>;
String _$themeSettingHash() => r'0b5620b696d73260d94f63cbfb65857acd2000f0';
final class NetworkSettingProvider
extends $NotifierProvider<NetworkSetting, NetworkProps> {
const NetworkSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'networkSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$networkSettingHash();
@$internal
@override
NetworkSetting create() => NetworkSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(NetworkProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<NetworkProps>(value),
);
}
}
String _$networkSettingHash() => r'6ac5959ad478247fd60329221743cccc7a7d010b';
abstract class _$NetworkSetting extends $Notifier<NetworkProps> {
NetworkProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<NetworkProps, NetworkProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<NetworkProps, NetworkProps>,
NetworkProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [ThemeSetting].
@ProviderFor(ThemeSetting)
final themeSettingProvider =
AutoDisposeNotifierProvider<ThemeSetting, ThemeProps>.internal(
ThemeSetting.new,
name: r'themeSettingProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$themeSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const themeSettingProvider = ThemeSettingProvider._();
typedef _$ThemeSetting = AutoDisposeNotifier<ThemeProps>;
String _$profilesHash() => r'3203cc7de88b91fff86b79c75c2cacd8116fffb7';
final class ThemeSettingProvider
extends $NotifierProvider<ThemeSetting, ThemeProps> {
const ThemeSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'themeSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$themeSettingHash();
@$internal
@override
ThemeSetting create() => ThemeSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ThemeProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ThemeProps>(value),
);
}
}
String _$themeSettingHash() => r'0ddad89cb63fc2b2094dd82262c76d972c2def5c';
abstract class _$ThemeSetting extends $Notifier<ThemeProps> {
ThemeProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ThemeProps, ThemeProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ThemeProps, ThemeProps>,
ThemeProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [Profiles].
@ProviderFor(Profiles)
final profilesProvider =
AutoDisposeNotifierProvider<Profiles, List<Profile>>.internal(
Profiles.new,
name: r'profilesProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$profilesHash,
dependencies: null,
allTransitiveDependencies: null,
);
const profilesProvider = ProfilesProvider._();
final class ProfilesProvider
extends $NotifierProvider<Profiles, List<Profile>> {
const ProfilesProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'profilesProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$profilesHash();
@$internal
@override
Profiles create() => Profiles();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(List<Profile> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<List<Profile>>(value),
);
}
}
String _$profilesHash() => r'aad57222a4a0bd16f2c70f9eb8ba0053d1a26d0f';
abstract class _$Profiles extends $Notifier<List<Profile>> {
List<Profile> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<List<Profile>, List<Profile>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<List<Profile>, List<Profile>>,
List<Profile>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@ProviderFor(CurrentProfileId)
const currentProfileIdProvider = CurrentProfileIdProvider._();
final class CurrentProfileIdProvider
extends $NotifierProvider<CurrentProfileId, String?> {
const CurrentProfileIdProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'currentProfileIdProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$currentProfileIdHash();
@$internal
@override
CurrentProfileId create() => CurrentProfileId();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(String? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<String?>(value),
);
}
}
typedef _$Profiles = AutoDisposeNotifier<List<Profile>>;
String _$currentProfileIdHash() => r'0c3e324e751aac1164da479e1796e826615bdcbe';
/// See also [CurrentProfileId].
@ProviderFor(CurrentProfileId)
final currentProfileIdProvider =
AutoDisposeNotifierProvider<CurrentProfileId, String?>.internal(
CurrentProfileId.new,
name: r'currentProfileIdProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$currentProfileIdHash,
dependencies: null,
allTransitiveDependencies: null,
);
abstract class _$CurrentProfileId extends $Notifier<String?> {
String? build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<String?, String?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<String?, String?>,
String?,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
typedef _$CurrentProfileId = AutoDisposeNotifier<String?>;
String _$appDAVSettingHash() => r'4bf293ac0d1fba157f60df920b7ffd5afefaab26';
/// See also [AppDAVSetting].
@ProviderFor(AppDAVSetting)
final appDAVSettingProvider =
AutoDisposeNotifierProvider<AppDAVSetting, DAV?>.internal(
AppDAVSetting.new,
name: r'appDAVSettingProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$appDAVSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const appDAVSettingProvider = AppDAVSettingProvider._();
final class AppDAVSettingProvider
extends $NotifierProvider<AppDAVSetting, DAV?> {
const AppDAVSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'appDAVSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$appDAVSettingHash();
@$internal
@override
AppDAVSetting create() => AppDAVSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(DAV? value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<DAV?>(value),
);
}
}
String _$appDAVSettingHash() => r'fa8de5d89d7a11f34f3f8e20b71cf164e5e11888';
abstract class _$AppDAVSetting extends $Notifier<DAV?> {
DAV? build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<DAV?, DAV?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<DAV?, DAV?>,
DAV?,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@ProviderFor(OverrideDns)
const overrideDnsProvider = OverrideDnsProvider._();
final class OverrideDnsProvider extends $NotifierProvider<OverrideDns, bool> {
const OverrideDnsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'overrideDnsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$overrideDnsHash();
@$internal
@override
OverrideDns create() => OverrideDns();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(bool value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<bool>(value),
);
}
}
typedef _$AppDAVSetting = AutoDisposeNotifier<DAV?>;
String _$overrideDnsHash() => r'1fc914de471319bf1e003edf9627b8c646b641bf';
/// See also [OverrideDns].
@ProviderFor(OverrideDns)
final overrideDnsProvider =
AutoDisposeNotifierProvider<OverrideDns, bool>.internal(
OverrideDns.new,
name: r'overrideDnsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$overrideDnsHash,
dependencies: null,
allTransitiveDependencies: null,
);
abstract class _$OverrideDns extends $Notifier<bool> {
bool build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<bool, bool>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<bool, bool>,
bool,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@ProviderFor(HotKeyActions)
const hotKeyActionsProvider = HotKeyActionsProvider._();
final class HotKeyActionsProvider
extends $NotifierProvider<HotKeyActions, List<HotKeyAction>> {
const HotKeyActionsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'hotKeyActionsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$hotKeyActionsHash();
@$internal
@override
HotKeyActions create() => HotKeyActions();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(List<HotKeyAction> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<List<HotKeyAction>>(value),
);
}
}
typedef _$OverrideDns = AutoDisposeNotifier<bool>;
String _$hotKeyActionsHash() => r'1d308d61b74accebbb11b1771a55975760503691';
/// See also [HotKeyActions].
@ProviderFor(HotKeyActions)
final hotKeyActionsProvider =
AutoDisposeNotifierProvider<HotKeyActions, List<HotKeyAction>>.internal(
HotKeyActions.new,
name: r'hotKeyActionsProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hotKeyActionsHash,
dependencies: null,
allTransitiveDependencies: null,
);
abstract class _$HotKeyActions extends $Notifier<List<HotKeyAction>> {
List<HotKeyAction> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<List<HotKeyAction>, List<HotKeyAction>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<List<HotKeyAction>, List<HotKeyAction>>,
List<HotKeyAction>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
typedef _$HotKeyActions = AutoDisposeNotifier<List<HotKeyAction>>;
String _$proxiesStyleSettingHash() =>
r'54ebf20a8d4455b2d7a65824f375c4c02a5fba28';
/// See also [ProxiesStyleSetting].
@ProviderFor(ProxiesStyleSetting)
final proxiesStyleSettingProvider =
AutoDisposeNotifierProvider<ProxiesStyleSetting, ProxiesStyle>.internal(
ProxiesStyleSetting.new,
name: r'proxiesStyleSettingProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$proxiesStyleSettingHash,
dependencies: null,
allTransitiveDependencies: null,
);
const proxiesStyleSettingProvider = ProxiesStyleSettingProvider._();
typedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;
String _$scriptStateHash() => r'afbb70d1dd7e577b2377ecd8ab35d0905c1d0e87';
final class ProxiesStyleSettingProvider
extends $NotifierProvider<ProxiesStyleSetting, ProxiesStyle> {
const ProxiesStyleSettingProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'proxiesStyleSettingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$proxiesStyleSettingHash();
@$internal
@override
ProxiesStyleSetting create() => ProxiesStyleSetting();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ProxiesStyle value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ProxiesStyle>(value),
);
}
}
String _$proxiesStyleSettingHash() =>
r'4ff62951ddc8289220191850516b6751ee69d642';
abstract class _$ProxiesStyleSetting extends $Notifier<ProxiesStyle> {
ProxiesStyle build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ProxiesStyle, ProxiesStyle>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ProxiesStyle, ProxiesStyle>,
ProxiesStyle,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [ScriptState].
@ProviderFor(ScriptState)
final scriptStateProvider =
AutoDisposeNotifierProvider<ScriptState, ScriptProps>.internal(
ScriptState.new,
name: r'scriptStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$scriptStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
const scriptStateProvider = ScriptStateProvider._();
typedef _$ScriptState = AutoDisposeNotifier<ScriptProps>;
String _$patchClashConfigHash() => r'd9acdd0ace673fc1c1460b63d7a27c5787713c14';
final class ScriptStateProvider
extends $NotifierProvider<ScriptState, ScriptProps> {
const ScriptStateProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'scriptStateProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$scriptStateHash();
@$internal
@override
ScriptState create() => ScriptState();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ScriptProps value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ScriptProps>(value),
);
}
}
String _$scriptStateHash() => r'4770c34c3d24451fef95e372450e4a333b419977';
abstract class _$ScriptState extends $Notifier<ScriptProps> {
ScriptProps build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ScriptProps, ScriptProps>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ScriptProps, ScriptProps>,
ScriptProps,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// See also [PatchClashConfig].
@ProviderFor(PatchClashConfig)
final patchClashConfigProvider =
AutoDisposeNotifierProvider<PatchClashConfig, ClashConfig>.internal(
PatchClashConfig.new,
name: r'patchClashConfigProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$patchClashConfigHash,
dependencies: null,
allTransitiveDependencies: null,
);
const patchClashConfigProvider = PatchClashConfigProvider._();
typedef _$PatchClashConfig = AutoDisposeNotifier<ClashConfig>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
final class PatchClashConfigProvider
extends $NotifierProvider<PatchClashConfig, ClashConfig> {
const PatchClashConfigProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'patchClashConfigProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$patchClashConfigHash();
@$internal
@override
PatchClashConfig create() => PatchClashConfig();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ClashConfig value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ClashConfig>(value),
);
}
}
String _$patchClashConfigHash() => r'b355bd89969d4d119631fdf117df230a71493fa8';
abstract class _$PatchClashConfig extends $Notifier<ClashConfig> {
ClashConfig build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ClashConfig, ClashConfig>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ClashConfig, ClashConfig>,
ClashConfig,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -46,17 +46,19 @@ Config configState(Ref ref) {
@riverpod
GroupsState currentGroupsState(Ref ref) {
final mode =
ref.watch(patchClashConfigProvider.select((state) => state.mode));
final mode = ref.watch(
patchClashConfigProvider.select((state) => state.mode),
);
final groups = ref.watch(groupsProvider);
return GroupsState(
value: switch (mode) {
Mode.direct => [],
Mode.global => groups.toList(),
Mode.rule => groups
.where((item) => item.hidden == false)
.where((element) => element.name != GroupName.GLOBAL.name)
.toList(),
Mode.rule =>
groups
.where((item) => item.hidden == false)
.where((element) => element.name != GroupName.GLOBAL.name)
.toList(),
},
);
}
@@ -64,11 +66,12 @@ GroupsState currentGroupsState(Ref ref) {
@riverpod
NavigationItemsState navigationItemsState(Ref ref) {
final openLogs = ref.watch(appSettingProvider).openLogs;
final hasProfiles =
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
final hasProxies = ref.watch(currentGroupsStateProvider.select(
(state) => state.value.isNotEmpty,
));
final hasProfiles = ref.watch(
profilesProvider.select((state) => state.isNotEmpty),
);
final hasProxies = ref.watch(
currentGroupsStateProvider.select((state) => state.value.isNotEmpty),
);
final isInit = ref.watch(initProvider);
return NavigationItemsState(
value: navigation.getItems(
@@ -88,31 +91,15 @@ NavigationItemsState currentNavigationItemsState(Ref ref) {
};
return NavigationItemsState(
value: navigationItemsState.value
.where(
(element) => element.modes.contains(navigationItemMode),
)
.where((element) => element.modes.contains(navigationItemMode))
.toList(),
);
}
@riverpod
CoreState coreState(Ref ref) {
final vpnProps = ref.watch(vpnSettingProvider);
final currentProfile = ref.watch(currentProfileProvider);
final onlyStatisticsProxy = ref.watch(appSettingProvider).onlyStatisticsProxy;
return CoreState(
vpnProps: vpnProps,
onlyStatisticsProxy: onlyStatisticsProxy,
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',
);
}
@riverpod
UpdateParams updateParams(Ref ref) {
final routeMode = ref.watch(
networkSettingProvider.select(
(state) => state.routeMode,
),
networkSettingProvider.select((state) => state.routeMode),
);
return ref.watch(
patchClashConfigProvider.select(
@@ -135,12 +122,11 @@ UpdateParams updateParams(Ref ref) {
@riverpod
ProxyState proxyState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final vm2 = ref.watch(networkSettingProvider.select(
(state) => VM2(
a: state.systemProxy,
b: state.bypassDomain,
final vm2 = ref.watch(
networkSettingProvider.select(
(state) => VM2(a: state.systemProxy, b: state.bypassDomain),
),
));
);
final mixedPort = ref.watch(
patchClashConfigProvider.select((state) => state.mixedPort),
);
@@ -156,20 +142,10 @@ ProxyState proxyState(Ref ref) {
TrayState trayState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final networkProps = ref.watch(networkSettingProvider);
final clashConfig = ref.watch(
patchClashConfigProvider,
);
final appSetting = ref.watch(
appSettingProvider,
);
final groups = ref
.watch(
currentGroupsStateProvider,
)
.value;
final brightness = ref.watch(
systemBrightnessProvider,
);
final clashConfig = ref.watch(patchClashConfigProvider);
final appSetting = ref.watch(appSettingProvider);
final groups = ref.watch(currentGroupsStateProvider).value;
final brightness = ref.watch(systemBrightnessProvider);
final selectedMap = ref.watch(selectedMapProvider);
@@ -194,10 +170,7 @@ VpnState vpnState(Ref ref) {
patchClashConfigProvider.select((state) => state.tun.stack),
);
return VpnState(
stack: stack,
vpnProps: vpnProps,
);
return VpnState(stack: stack, vpnProps: vpnProps);
}
@riverpod
@@ -220,25 +193,33 @@ NavigationState navigationState(Ref ref) {
}
@riverpod
DashboardState dashboardState(Ref ref) {
final dashboardWidgets =
ref.watch(appSettingProvider.select((state) => state.dashboardWidgets));
double contentWidth(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final sideWidth = ref.watch(sideWidthProvider);
return viewWidth - sideWidth;
}
@riverpod
DashboardState dashboardState(Ref ref) {
final dashboardWidgets = ref.watch(
appSettingProvider.select((state) => state.dashboardWidgets),
);
final contentWidth = ref.watch(contentWidthProvider);
return DashboardState(
dashboardWidgets: dashboardWidgets,
viewWidth: viewWidth,
contentWidth: contentWidth,
);
}
@riverpod
ProxiesActionsState proxiesActionsState(Ref ref) {
final pageLabel = ref.watch(currentPageLabelProvider);
final hasProviders = ref.watch(providersProvider.select(
(state) => state.isNotEmpty,
));
final type = ref.watch(proxiesStyleSettingProvider.select(
(state) => state.type,
));
final hasProviders = ref.watch(
providersProvider.select((state) => state.isNotEmpty),
);
final type = ref.watch(
proxiesStyleSettingProvider.select((state) => state.type),
);
return ProxiesActionsState(
pageLabel: pageLabel,
hasProviders: hasProviders,
@@ -249,12 +230,10 @@ ProxiesActionsState proxiesActionsState(Ref ref) {
@riverpod
StartButtonSelectorState startButtonSelectorState(Ref ref) {
final isInit = ref.watch(initProvider);
final hasProfile =
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
return StartButtonSelectorState(
isInit: isInit,
hasProfile: hasProfile,
final hasProfile = ref.watch(
profilesProvider.select((state) => state.isNotEmpty),
);
return StartButtonSelectorState(isInit: isInit, hasProfile: hasProfile);
}
@riverpod
@@ -262,9 +241,7 @@ ProfilesSelectorState profilesSelectorState(Ref ref) {
final currentProfileId = ref.watch(currentProfileIdProvider);
final profiles = ref.watch(profilesProvider);
final columns = ref.watch(
viewWidthProvider.select(
(state) => utils.getProfilesColumns(state),
),
contentWidthProvider.select((state) => utils.getProfilesColumns(state)),
);
return ProfilesSelectorState(
profiles: profiles,
@@ -281,75 +258,61 @@ GroupsState filterGroupsState(Ref ref, String query) {
}
final lowQuery = query.toLowerCase();
final groups = currentGroups.value
.map(
(group) {
return group.copyWith(
all: group.all
.where((proxy) => proxy.name.toLowerCase().contains(lowQuery))
.toList());
},
)
.where(
(group) => group.all.isNotEmpty,
)
.map((group) {
return group.copyWith(
all: group.all
.where((proxy) => proxy.name.toLowerCase().contains(lowQuery))
.toList(),
);
})
.where((group) => group.all.isNotEmpty)
.toList();
return GroupsState(value: groups);
}
@Riverpod(dependencies: [Query])
@riverpod
ProxiesListState proxiesListState(Ref ref) {
final query = ref.watch(queryProvider);
final query = ref.watch(queryProvider(QueryTag.proxies));
final currentGroups = ref.watch(filterGroupsStateProvider(query));
final currentUnfoldSet = ref.watch(unfoldSetProvider);
final vm2 = ref.watch(
proxiesStyleSettingProvider.select(
(state) => VM2(
a: state.sortType,
b: state.cardType,
),
),
final cardType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.cardType),
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxiesListState(
groups: currentGroups.value,
currentUnfoldSet: currentUnfoldSet,
proxiesSortType: vm2.a,
proxyCardType: vm2.b,
sortNum: sortNum,
proxyCardType: cardType,
columns: columns,
);
}
@Riverpod(dependencies: [Query])
@riverpod
ProxiesTabState proxiesTabState(Ref ref) {
final query = ref.watch(queryProvider);
final query = ref.watch(queryProvider(QueryTag.proxies));
final currentGroups = ref.watch(filterGroupsStateProvider(query));
final currentGroupName = ref.watch(currentProfileProvider.select(
(state) => state?.currentGroupName,
));
final vm2 = ref.watch(
proxiesStyleSettingProvider.select(
(state) => VM2(
a: state.sortType,
b: state.cardType,
),
),
final currentGroupName = ref.watch(
currentProfileProvider.select((state) => state?.currentGroupName),
);
final cardType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.cardType),
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxiesTabState(
groups: currentGroups.value,
currentGroupName: currentGroupName,
proxiesSortType: vm2.a,
proxyCardType: vm2.b,
sortNum: sortNum,
proxyCardType: cardType,
columns: columns,
);
}
@Riverpod(dependencies: [proxiesTabState])
@riverpod
bool isStart(Ref ref) {
return ref.watch(runTimeProvider.select((state) => state != null));
}
@riverpod
VM2<List<String>, String?> proxiesTabControllerState(Ref ref) {
return ref.watch(
proxiesTabStateProvider.select(
@@ -367,9 +330,7 @@ ProxyGroupSelectorState proxyGroupSelectorState(
String groupName,
String query,
) {
final proxiesStyle = ref.watch(
proxiesStyleSettingProvider,
);
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
final group = ref.watch(
currentGroupsStateProvider.select(
(state) => state.value.getGroup(groupName),
@@ -378,7 +339,8 @@ ProxyGroupSelectorState proxyGroupSelectorState(
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
final lowQuery = query.toLowerCase();
final proxies = group?.all.where((item) {
final proxies =
group?.all.where((item) {
return item.name.toLowerCase().contains(lowQuery);
}).toList() ??
[];
@@ -396,8 +358,9 @@ ProxyGroupSelectorState proxyGroupSelectorState(
@riverpod
PackageListSelectorState packageListSelectorState(Ref ref) {
final packages = ref.watch(packagesProvider);
final accessControl =
ref.watch(vpnSettingProvider.select((state) => state.accessControl));
final accessControl = ref.watch(
vpnSettingProvider.select((state) => state.accessControl),
);
return PackageListSelectorState(
packages: packages,
accessControl: accessControl,
@@ -407,18 +370,19 @@ PackageListSelectorState packageListSelectorState(Ref ref) {
@riverpod
MoreToolsSelectorState moreToolsSelectorState(Ref ref) {
final viewMode = ref.watch(viewModeProvider);
final navigationItems =
ref.watch(navigationItemsStateProvider.select((state) {
return state.value.where((element) {
final isMore = element.modes.contains(NavigationItemMode.more);
final isDesktop = element.modes.contains(NavigationItemMode.desktop);
if (isMore && !isDesktop) return true;
if (viewMode != ViewMode.mobile || !isMore) {
return false;
}
return true;
}).toList();
}));
final navigationItems = ref.watch(
navigationItemsStateProvider.select((state) {
return state.value.where((element) {
final isMore = element.modes.contains(NavigationItemMode.more);
final isDesktop = element.modes.contains(NavigationItemMode.desktop);
if (isMore && !isDesktop) return true;
if (viewMode != ViewMode.mobile || !isMore) {
return false;
}
return true;
}).toList();
}),
);
return MoreToolsSelectorState(navigationItems: navigationItems);
}
@@ -447,26 +411,16 @@ String getRealTestUrl(Ref ref, [String? testUrl]) {
}
@riverpod
int? getDelay(
Ref ref, {
required String proxyName,
String? testUrl,
}) {
int? getDelay(Ref ref, {required String proxyName, String? testUrl}) {
final currentTestUrl = ref.watch(getRealTestUrlProvider(testUrl));
final proxyCardState = ref.watch(
getProxyCardStateProvider(
proxyName,
),
);
final proxyState = ref.watch(realSelectedProxyStateProvider(proxyName));
final delay = ref.watch(
delayDataSourceProvider.select(
(state) {
final delayMap =
state[proxyCardState.testUrl.getSafeValue(currentTestUrl)];
return delayMap?[proxyCardState.proxyName];
},
),
delayDataSourceProvider.select((state) {
final delayMap = state[proxyState.testUrl.getSafeValue(currentTestUrl)];
return delayMap?[proxyState.proxyName];
}),
);
return delay;
}
@@ -489,71 +443,46 @@ Set<String> unfoldSet(Ref ref) {
@riverpod
HotKeyAction getHotKeyAction(Ref ref, HotAction hotAction) {
return ref.watch(
hotKeyActionsProvider.select(
(state) {
final index = state.indexWhere((item) => item.action == hotAction);
return index != -1
? state[index]
: HotKeyAction(
action: hotAction,
);
},
),
hotKeyActionsProvider.select((state) {
final index = state.indexWhere((item) => item.action == hotAction);
return index != -1 ? state[index] : HotKeyAction(action: hotAction);
}),
);
}
@riverpod
Profile? currentProfile(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
return ref
.watch(profilesProvider.select((state) => state.getProfile(profileId)));
}
@riverpod
int getProxiesColumns(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final proxiesLayout =
ref.watch(proxiesStyleSettingProvider.select((state) => state.layout));
return utils.getProxiesColumns(viewWidth, proxiesLayout);
}
ProxyCardState _getProxyCardState(
List<Group> groups,
SelectedMap selectedMap,
ProxyCardState proxyDelayState,
) {
if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;
final index =
groups.indexWhere((element) => element.name == proxyDelayState.proxyName);
if (index == -1) return proxyDelayState;
final group = groups[index];
final currentSelectedName = group
.getCurrentSelectedName(selectedMap[proxyDelayState.proxyName] ?? '');
if (currentSelectedName.isEmpty) {
return proxyDelayState;
}
return _getProxyCardState(
groups,
selectedMap,
proxyDelayState.copyWith(
proxyName: currentSelectedName,
testUrl: group.testUrl,
),
return ref.watch(
profilesProvider.select((state) => state.getProfile(profileId)),
);
}
@riverpod
ProxyCardState getProxyCardState(Ref ref, String proxyName) {
int getProxiesColumns(Ref ref) {
final contentWidth = ref.watch(contentWidthProvider);
final proxiesLayout = ref.watch(
proxiesStyleSettingProvider.select((state) => state.layout),
);
return utils.getProxiesColumns(contentWidth, proxiesLayout);
}
@riverpod
SelectedProxyState realSelectedProxyState(Ref ref, String proxyName) {
final groups = ref.watch(groupsProvider);
final selectedMap = ref.watch(selectedMapProvider);
return _getProxyCardState(
groups, selectedMap, ProxyCardState(proxyName: proxyName));
return computeRealSelectedProxyState(
proxyName,
groups: groups,
selectedMap: selectedMap,
);
}
@riverpod
String? getProxyName(Ref ref, String groupName) {
final proxyName =
ref.watch(selectedMapProvider.select((state) => state[groupName]));
final proxyName = ref.watch(
selectedMapProvider.select((state) => state[groupName]),
);
return proxyName;
}
@@ -561,9 +490,7 @@ String? getProxyName(Ref ref, String groupName) {
String? getSelectedProxyName(Ref ref, String groupName) {
final proxyName = ref.watch(getProxyNameProvider(groupName));
final group = ref.watch(
groupsProvider.select(
(state) => state.getGroup(groupName),
),
groupsProvider.select((state) => state.getGroup(groupName)),
);
return group?.getCurrentSelectedName(proxyName ?? '');
}
@@ -577,32 +504,11 @@ String getProxyDesc(Ref ref, Proxy proxy) {
final groups = ref.watch(groupsProvider);
final index = groups.indexWhere((element) => element.name == proxy.name);
if (index == -1) return proxy.type;
final state = ref.watch(getProxyCardStateProvider(proxy.name));
final state = ref.watch(realSelectedProxyStateProvider(proxy.name));
return "${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})";
}
}
@riverpod
class ProfileOverrideState extends _$ProfileOverrideState {
@override
ProfileOverrideStateModel build() {
return ProfileOverrideStateModel(
selectedRules: {},
);
}
void updateState(
ProfileOverrideStateModel? Function(ProfileOverrideStateModel state)
builder,
) {
final value = builder(state);
if (value == null) {
return;
}
state = value;
}
}
@riverpod
OverrideData? getProfileOverrideData(Ref ref, String profileId) {
return ref.watch(
@@ -615,12 +521,10 @@ OverrideData? getProfileOverrideData(Ref ref, String profileId) {
@riverpod
VM2? layoutChange(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final textScale =
ref.watch(themeSettingProvider.select((state) => state.textScale));
return VM2(
a: viewWidth,
b: textScale,
final textScale = ref.watch(
themeSettingProvider.select((state) => state.textScale),
);
return VM2(a: viewWidth, b: textScale);
}
@riverpod
@@ -632,10 +536,7 @@ VM2<int, bool> checkIp(Ref ref) {
state.dashboardWidgets.contains(DashboardWidget.networkDetection),
),
);
return VM2(
a: checkIpNum,
b: containsDetection,
);
return VM2(a: checkIpNum, b: containsDetection);
}
@riverpod
@@ -647,10 +548,7 @@ ColorScheme genColorScheme(
}) {
final vm2 = ref.watch(
themeSettingProvider.select(
(state) => VM2(
a: state.primaryColor,
b: state.schemeVariant,
),
(state) => VM2(a: state.primaryColor, b: state.schemeVariant),
),
);
if (color == null && (ignoreConfig == true || vm2.a == null)) {
@@ -658,7 +556,8 @@ ColorScheme genColorScheme(
// return globalState.corePalette!.toColorScheme(brightness: brightness);
// }
return ColorScheme.fromSeed(
seedColor: globalState.corePalette
seedColor:
globalState.corePalette
?.toColorScheme(brightness: brightness)
.primary ??
globalState.accentColor,
@@ -677,24 +576,20 @@ ColorScheme genColorScheme(
VM3<String?, String?, Dns?> needSetup(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
final content = ref.watch(
scriptStateProvider.select((state) => state.currentScript?.content));
scriptStateProvider.select((state) => state.currentScript?.content),
);
final overrideDns = ref.watch(overrideDnsProvider);
final dns = overrideDns == true
? ref.watch(patchClashConfigProvider.select(
(state) => state.dns,
))
? ref.watch(patchClashConfigProvider.select((state) => state.dns))
: null;
return VM3(
a: profileId,
b: content,
c: dns,
);
return VM3(a: profileId, b: content, c: dns);
}
@riverpod
Brightness currentBrightness(Ref ref) {
final themeMode =
ref.watch(themeSettingProvider.select((state) => state.themeMode));
final themeMode = ref.watch(
themeSettingProvider.select((state) => state.themeMode),
);
final systemBrightness = ref.watch(systemBrightnessProvider);
return switch (themeMode) {
ThemeMode.system => systemBrightness,
@@ -708,18 +603,46 @@ VM2<bool, bool> autoSetSystemDnsState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final realTunEnable = ref.watch(realTunEnableProvider);
final autoSetSystemDns = ref.watch(
networkSettingProvider.select(
(state) => state.autoSetSystemDns,
),
networkSettingProvider.select((state) => state.autoSetSystemDns),
);
return VM2(
a: isStart ? realTunEnable : false,
b: autoSetSystemDns,
return VM2(a: isStart ? realTunEnable : false, b: autoSetSystemDns);
}
@riverpod
VM3<bool, int, ProxiesSortType> needUpdateGroups(Ref ref) {
final isProxies = ref.watch(
currentPageLabelProvider.select((state) => state == PageLabel.proxies),
);
final sortNum = ref.watch(sortNumProvider);
final sortType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.sortType),
);
return VM3(a: isProxies, b: sortNum, c: sortType);
}
@riverpod
AndroidState androidState(Ref ref) {
final currentProfileName = ref.watch(
currentProfileProvider.select((state) => state?.label ?? ''),
);
final onlyStatisticsProxy = ref.watch(
appSettingProvider.select((state) => state.onlyStatisticsProxy),
);
ref.watch((appSettingProvider).select((state) => state.locale));
final crashlytics = ref.watch(
(appSettingProvider).select((state) => state.crashlytics),
);
return AndroidState(
currentProfileName: currentProfileName,
onlyStatisticsProxy: onlyStatisticsProxy,
stopText: appLocalizations.stop,
crashlytics: crashlytics,
);
}
@riverpod
class Query extends _$Query with AutoDisposeNotifierMixin {
class Query extends _$Query {
@override
String build() => '';
String build(QueryTag id) =>
ref.watch(queryMapProvider.select((state) => state[id] ?? ''));
}

View File

@@ -1,12 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' show Pointer;
import 'dart:io';
import 'dart:isolate';
import 'package:animations/animations.dart';
import 'package:dio/dio.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/theme.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/l10n/l10n.dart';
import 'package:fl_clash/plugins/service.dart';
@@ -16,6 +18,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_js/flutter_js.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart';
import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
@@ -27,10 +30,6 @@ typedef UpdateTasks = List<FutureOr Function()>;
class GlobalState {
static GlobalState? _instance;
Map<CacheTag, FixedMap<String, double>> computeHeightMapCache = {};
// Map<CacheTag, double> computeScrollPositionCache = {};
// final Map<String, double> scrollPositionCache = {};
bool isService = false;
Timer? timer;
Timer? groupsUpdateTimer;
late Config config;
@@ -47,9 +46,9 @@ class GlobalState {
UpdateTasks tasks = [];
final navigatorKey = GlobalKey<NavigatorState>();
AppController? _appController;
// GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
bool isInit = false;
bool isUserDisconnected = false;
bool isService = false;
bool get isStart => startTime != null && startTime!.isBeforeNow;
@@ -82,22 +81,54 @@ class GlobalState {
);
await _initDynamicColor();
await init();
await window?.init(version);
_shakingStore();
}
Future<void> _shakingStore() async {
final profileIds = config.profiles.map((item) => item.id);
final providersRootPath = await appPath.getProvidersRootPath();
final profilesRootPath = await appPath.profilesPath;
Isolate.run(() async {
final profilesDir = Directory(profilesRootPath);
final providersDir = Directory(providersRootPath);
final List<FileSystemEntity> entities = [];
if (await profilesDir.exists()) {
entities.addAll(
profilesDir.listSync().where(
(item) => !item.path.contains('providers'),
),
);
}
if (await providersDir.exists()) {
entities.addAll(providersDir.listSync());
}
final deleteFutures = entities.map((entity) async {
if (!profileIds.contains(basenameWithoutExtension(entity.path))) {
final res = await coreController.deleteFile(entity.path);
if (res.isNotEmpty) {
throw res;
}
}
return true;
});
await Future.wait(deleteFutures);
});
}
Future<void> _initDynamicColor() async {
try {
corePalette = await DynamicColorPlugin.getCorePalette();
accentColor = await DynamicColorPlugin.getAccentColor() ??
accentColor =
await DynamicColorPlugin.getAccentColor() ??
Color(defaultPrimaryColor);
} catch (_) {}
}
Future<void> init() async {
packageInfo = await PackageInfo.fromPlatform();
config = await preferences.getConfig() ??
Config(
themeProps: defaultThemeProps,
);
config =
await preferences.getConfig() ?? Config(themeProps: defaultThemeProps);
await globalState.migrateOldData(config);
await AppLocalizations.load(
utils.getLocaleForString(config.appSetting.locale) ??
@@ -112,6 +143,9 @@ class GlobalState {
if (tasks != null) {
this.tasks = tasks;
}
if (this.tasks.isEmpty) {
return;
}
await executorUpdateTask();
timer = Timer(const Duration(seconds: 1), () async {
startUpdateTasks();
@@ -133,29 +167,33 @@ class GlobalState {
Future<void> handleStart([UpdateTasks? tasks]) async {
startTime ??= DateTime.now();
await clashCore.startListener();
await service?.startVpn();
await coreController.startListener();
await service?.start();
startUpdateTasks(tasks);
}
Future updateStartTime() async {
startTime = await clashLib?.getRunTime();
startTime = await service?.getRunTime();
}
Future handleStop() async {
startTime = null;
await clashCore.stopListener();
await service?.stopVpn();
await coreController.stopListener();
await service?.stop();
stopUpdateTasks();
}
Future<bool?> showMessage({
String? title,
required InlineSpan message,
BuildContext? context,
String? title,
String? confirmText,
bool cancelable = true,
bool? dismissible,
}) async {
return await showCommonDialog<bool>(
context: context,
dismissible: dismissible,
child: Builder(
builder: (context) {
return CommonDialog(
@@ -173,7 +211,7 @@ class GlobalState {
Navigator.of(context).pop(true);
},
child: Text(confirmText ?? appLocalizations.confirm),
)
),
],
child: Container(
width: 300,
@@ -184,9 +222,7 @@ class GlobalState {
style: Theme.of(context).textTheme.labelLarge,
children: [message],
),
style: const TextStyle(
overflow: TextOverflow.visible,
),
style: const TextStyle(overflow: TextOverflow.visible),
),
),
),
@@ -196,36 +232,34 @@ class GlobalState {
);
}
// Future<Map<String, dynamic>> getProfileMap(String id) async {
// final profilePath = await appPath.getProfilePath(id);
// final res = await Isolate.run<Result<dynamic>>(() async {
// try {
// final file = File(profilePath);
// if (!await file.exists()) {
// return Result.error("");
// }
// final value = await file.readAsString();
// return Result.success(utils.convertYamlNode(loadYaml(value)));
// } catch (e) {
// return Result.error(e.toString());
// }
// });
// if (res.isSuccess) {
// return res.data as Map<String, dynamic>;
// } else {
// throw res.message;
// }
// }
VpnOptions getVpnOptions() {
final vpnProps = config.vpnProps;
final networkProps = config.networkProps;
final port = config.patchClashConfig.mixedPort;
return VpnOptions(
stack: config.patchClashConfig.tun.stack.name,
enable: vpnProps.enable,
systemProxy: networkProps.systemProxy,
port: port,
ipv6: vpnProps.ipv6,
dnsHijacking: vpnProps.dnsHijacking,
accessControl: vpnProps.accessControl,
allowBypass: vpnProps.allowBypass,
bypassDomain: networkProps.bypassDomain,
);
}
Future<T?> showCommonDialog<T>({
required Widget child,
bool dismissible = true,
BuildContext? context,
bool? dismissible,
}) async {
return await showModal<T>(
context: navigatorKey.currentState!.context,
useRootNavigator: false,
context: context ?? globalState.navigatorKey.currentContext!,
configuration: FadeScaleTransitionConfiguration(
barrierColor: Colors.black38,
barrierDismissible: dismissible,
barrierDismissible: dismissible ?? true,
),
builder: (_) => child,
filter: commonFilter,
@@ -254,38 +288,50 @@ class GlobalState {
Future<void> migrateOldData(Config config) async {
final clashConfig = await preferences.getClashConfig();
if (clashConfig != null) {
config = config.copyWith(
patchClashConfig: clashConfig,
);
config = config.copyWith(patchClashConfig: clashConfig);
preferences.clearClashConfig();
preferences.saveConfig(config);
}
}
CoreState getCoreState() {
final currentProfile = config.currentProfile;
return CoreState(
vpnProps: config.vpnProps,
onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',
bypassDomain: config.networkProps.bypassDomain,
);
}
Future<SetupParams> getSetupParams({
required ClashConfig pathConfig,
}) async {
final clashConfig = await patchRawConfig(
patchConfig: pathConfig,
);
Future<SetupParams> getSetupParams() async {
final params = SetupParams(
config: clashConfig,
selectedMap: config.currentProfile?.selectedMap ?? {},
testUrl: config.appSetting.testUrl,
);
return params;
}
Future<void> genConfigFile(ClashConfig pathConfig) async {
final configFilePath = await appPath.configFilePath;
final config = await patchRawConfig(patchConfig: pathConfig);
final res = await Isolate.run<String>(() async {
try {
final res = json.encode(config);
final file = File(configFilePath);
if (!await file.exists()) {
await file.create(recursive: true);
}
await file.writeAsString(res);
return '';
} catch (e) {
return e.toString();
}
});
if (res.isNotEmpty) {
throw res;
}
}
AndroidState getAndroidState() {
return AndroidState(
currentProfileName: config.currentProfile?.label ?? '',
onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
stopText: appLocalizations.stop,
crashlytics: config.appSetting.crashlytics,
);
}
Future<Map<String, dynamic>> patchRawConfig({
required ClashConfig patchConfig,
}) async {
@@ -390,7 +436,8 @@ class GlobalState {
if (overrideDns || !isEnableDns) {
final dns = switch (!isEnableDns) {
true => realPatchConfig.dns.copyWith(
nameserver: [...realPatchConfig.dns.nameserver, 'system://']),
nameserver: [...realPatchConfig.dns.nameserver, 'system://'],
),
false => realPatchConfig.dns,
};
rawConfig['dns'] = dns.toJson();
@@ -400,7 +447,7 @@ class GlobalState {
entry.value.splitByMultipleSeparators;
}
}
var rules = [];
List rules = [];
if (rawConfig['rules'] != null) {
rules = rawConfig['rules'];
}
@@ -419,10 +466,7 @@ class GlobalState {
}
Future<Map<String, dynamic>> getProfileConfig(String profileId) async {
final configMap = await switch (clashLibHandler != null) {
true => clashLibHandler!.getConfig(profileId),
false => clashCore.getConfig(profileId),
};
final configMap = await coreController.getConfig(profileId);
configMap['rules'] = configMap['rule'];
configMap.remove('rule');
return configMap;
@@ -464,10 +508,7 @@ class DetectionState {
CancelToken? cancelToken;
final state = ValueNotifier<NetworkDetectionState>(
const NetworkDetectionState(
isLoading: true,
ipInfo: null,
),
const NetworkDetectionState(isLoading: true, ipInfo: null),
);
DetectionState._internal();
@@ -481,9 +522,7 @@ class DetectionState {
debouncer.call(
FunctionTag.checkIp,
_checkIp,
duration: Duration(
milliseconds: 1200,
),
duration: Duration(milliseconds: 1200),
);
}
@@ -504,10 +543,7 @@ class DetectionState {
return;
}
_clearSetTimeoutTimer();
state.value = state.value.copyWith(
isLoading: true,
ipInfo: null,
);
state.value = state.value.copyWith(isLoading: true, ipInfo: null);
_preIsStart = isStart;
if (cancelToken != null) {
cancelToken!.cancel();
@@ -516,26 +552,17 @@ class DetectionState {
cancelToken = CancelToken();
final res = await request.checkIp(cancelToken: cancelToken);
if (res.isError) {
state.value = state.value.copyWith(
isLoading: true,
ipInfo: null,
);
state.value = state.value.copyWith(isLoading: true, ipInfo: null);
return;
}
final ipInfo = res.data;
if (ipInfo != null) {
state.value = state.value.copyWith(
isLoading: false,
ipInfo: ipInfo,
);
state.value = state.value.copyWith(isLoading: false, ipInfo: ipInfo);
return;
}
_clearSetTimeoutTimer();
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
state.value = state.value.copyWith(
isLoading: false,
ipInfo: null,
);
state.value = state.value.copyWith(isLoading: false, ipInfo: null);
});
}

View File

@@ -51,18 +51,14 @@ class AboutView extends StatelessWidget {
ListItem(
title: const Text('Telegram'),
onTap: () {
globalState.openUrl(
'https://t.me/FlClash',
);
globalState.openUrl('https://t.me/FlClash');
},
trailing: const Icon(Icons.launch),
),
ListItem(
title: Text(appLocalizations.project),
onTap: () {
globalState.openUrl(
'https://github.com/$repository',
);
globalState.openUrl('https://github.com/$repository');
},
trailing: const Icon(Icons.launch),
),
@@ -103,13 +99,11 @@ class AboutView extends StatelessWidget {
spacing: 24,
children: [
for (final contributor in contributors)
Avatar(
contributor: contributor,
),
Avatar(contributor: contributor),
],
),
),
)
),
],
);
}
@@ -121,46 +115,50 @@ class AboutView extends StatelessWidget {
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer(builder: (_, ref, ___) {
return _DeveloperModeDetector(
child: Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Image.asset(
'assets/images/icon.png',
width: 64,
height: 64,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
Consumer(
builder: (_, ref, _) {
return _DeveloperModeDetector(
child: Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Image.asset(
'assets/images/icon.png',
width: 64,
height: 64,
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
)
],
)
],
),
onEnterDeveloperMode: () {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(developerMode: true),
);
context.showNotifier(appLocalizations.developerModeEnableTip);
},
);
}),
const SizedBox(
height: 24,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
),
],
),
],
),
onEnterDeveloperMode: () {
ref
.read(appSettingProvider.notifier)
.updateState(
(state) => state.copyWith(developerMode: true),
);
context.showNotifier(
appLocalizations.developerModeEnableTip,
);
},
);
},
),
const SizedBox(height: 24),
Text(
appLocalizations.desc,
style: Theme.of(context).textTheme.bodySmall,
@@ -168,17 +166,12 @@ class AboutView extends StatelessWidget {
],
),
),
const SizedBox(
height: 12,
),
const SizedBox(height: 12),
..._buildContributorsSection(),
..._buildMoreSection(context),
];
return Padding(
padding: kMaterialListPadding.copyWith(
top: 16,
bottom: 16,
),
padding: kMaterialListPadding.copyWith(top: 16, bottom: 16),
child: generateListView(items),
);
}
@@ -187,10 +180,7 @@ class AboutView extends StatelessWidget {
class Avatar extends StatelessWidget {
final Contributor contributor;
const Avatar({
super.key,
required this.contributor,
});
const Avatar({super.key, required this.contributor});
@override
Widget build(BuildContext context) {
@@ -201,18 +191,11 @@ class Avatar extends StatelessWidget {
width: 36,
height: 36,
child: CircleAvatar(
foregroundImage: AssetImage(
contributor.avatar,
),
foregroundImage: AssetImage(contributor.avatar),
),
),
const SizedBox(
height: 4,
),
Text(
contributor.name,
style: context.textTheme.bodySmall,
)
const SizedBox(height: 4),
Text(contributor.name, style: context.textTheme.bodySmall),
],
),
onTap: () {
@@ -264,9 +247,6 @@ class _DeveloperModeDetectorState extends State<_DeveloperModeDetector> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: widget.child,
);
return GestureDetector(onTap: _handleTap, child: widget.child);
}
}

View File

@@ -55,11 +55,9 @@ class _AccessViewState extends ConsumerState<AccessView> {
rejectList: rejectList,
),
).then(
(_) => setState(
() {
_updateInitList();
},
),
(_) => setState(() {
_updateInitList();
}),
);
},
icon: const Icon(Icons.search),
@@ -70,39 +68,38 @@ class _AccessViewState extends ConsumerState<AccessView> {
required bool isSelectedAll,
required List<String> allValueList,
}) {
final tooltip = isSelectedAll
? appLocalizations.cancelSelectAll
: appLocalizations.selectAll;
return IconButton(
tooltip: tooltip,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState((state) {
final isAccept =
state.accessControl.mode == AccessControlMode.acceptSelected;
if (isSelectedAll) {
return switch (isAccept) {
true => state.copyWith.accessControl(
acceptList: [],
),
false => state.copyWith.accessControl(
rejectList: [],
),
};
} else {
return switch (isAccept) {
true => state.copyWith.accessControl(
acceptList: allValueList,
),
false => state.copyWith.accessControl(
rejectList: allValueList,
),
};
}
});
},
icon: isSelectedAll
? const Icon(Icons.deselect)
: const Icon(Icons.select_all),
onPressed() {
ref.read(vpnSettingProvider.notifier).updateState((state) {
final isAccept =
state.accessControl.mode == AccessControlMode.acceptSelected;
if (isSelectedAll) {
return switch (isAccept) {
true => state.copyWith.accessControl(acceptList: []),
false => state.copyWith.accessControl(rejectList: []),
};
} else {
return switch (isAccept) {
true => state.copyWith.accessControl(acceptList: allValueList),
false => state.copyWith.accessControl(rejectList: allValueList),
};
}
});
}
return FadeRotationScaleBox(
child: isSelectedAll
? IconButton(
key: ValueKey(true),
tooltip: appLocalizations.cancelSelectAll,
onPressed: onPressed,
icon: const Icon(Icons.deselect),
)
: IconButton(
key: ValueKey(false),
tooltip: appLocalizations.selectAll,
onPressed: onPressed,
icon: const Icon(Icons.select_all),
),
);
}
@@ -116,20 +113,21 @@ class _AccessViewState extends ConsumerState<AccessView> {
if (commonScaffoldState?.mounted != true) return;
final selectedPackageNames =
(await globalState.appController.safeRun<List<String>>(
needLoading: true,
() async {
return await app?.getChinaPackageNames() ?? [];
},
))
?.toSet() ??
{};
needLoading: true,
() async {
return await app?.getChinaPackageNames() ?? [];
},
))?.toSet() ??
{};
final acceptList = packageNames
.where((item) => !selectedPackageNames.contains(item))
.toList();
final rejectList = packageNames
.where((item) => selectedPackageNames.contains(item))
.toList();
ref.read(vpnSettingProvider.notifier).updateState(
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => state.copyWith.accessControl(
acceptList: acceptList,
rejectList: rejectList,
@@ -142,9 +140,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
onPressed: () async {
final res = await showSheet<int>(
context: context,
props: SheetProps(
isScrollControlled: true,
),
props: SheetProps(isScrollControlled: true),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
@@ -168,14 +164,10 @@ class _AccessViewState extends ConsumerState<AccessView> {
valueList.remove(package.packageName);
}
ref.read(vpnSettingProvider.notifier).updateState((state) {
return switch (
state.accessControl.mode == AccessControlMode.acceptSelected) {
true => state.copyWith.accessControl(
acceptList: valueList,
),
false => state.copyWith.accessControl(
rejectList: valueList,
),
return switch (state.accessControl.mode ==
AccessControlMode.acceptSelected) {
true => state.copyWith.accessControl(acceptList: valueList),
false => state.copyWith.accessControl(rejectList: valueList),
};
});
}
@@ -206,10 +198,10 @@ class _AccessViewState extends ConsumerState<AccessView> {
delegate: SwitchDelegate(
value: accessControl.enable,
onChanged: (enable) {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
enable: enable,
),
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => state.copyWith.accessControl(enable: enable),
);
},
),
@@ -217,9 +209,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Divider(
height: 12,
),
child: Divider(height: 12),
),
Flexible(
child: DisabledMask(
@@ -255,17 +245,13 @@ class _AccessViewState extends ConsumerState<AccessView> {
.textTheme
.labelLarge
?.copyWith(
color: Theme.of(context)
.colorScheme
.primary,
color: Theme.of(
context,
).colorScheme.primary,
),
),
),
const Flexible(
child: SizedBox(
width: 8,
),
),
const Flexible(child: SizedBox(width: 8)),
Flexible(
child: Text(
'${valueList.length}',
@@ -273,18 +259,16 @@ class _AccessViewState extends ConsumerState<AccessView> {
.textTheme
.labelLarge
?.copyWith(
color: Theme.of(context)
.colorScheme
.primary,
color: Theme.of(
context,
).colorScheme.primary,
),
),
),
],
),
),
Flexible(
child: Text(describe),
)
Flexible(child: Text(describe)),
],
),
),
@@ -293,9 +277,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: _buildSearchButton(),
),
Flexible(child: _buildSearchButton()),
Flexible(
child: _buildSelectedAllButton(
isSelectedAll:
@@ -303,9 +285,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
allValueList: packageNameList,
),
),
Flexible(
child: _buildSettingButton(),
),
Flexible(child: _buildSettingButton()),
],
),
],
@@ -315,41 +295,42 @@ class _AccessViewState extends ConsumerState<AccessView> {
Expanded(
flex: 1,
child: FutureBuilder(
future: _completer.future,
builder: (_, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
return packages.isEmpty
? NullStatus(
label: appLocalizations.noData,
)
: CommonScrollBar(
future: _completer.future,
builder: (_, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(child: CircularProgressIndicator());
}
return packages.isEmpty
? NullStatus(label: appLocalizations.noData)
: CommonScrollBar(
controller: _controller,
child: ListView.builder(
controller: _controller,
child: ListView.builder(
controller: _controller,
itemCount: packages.length,
itemExtent: 72,
itemBuilder: (_, index) {
final package = packages[index];
return PackageListItem(
key: Key(package.packageName),
package: package,
value: valueList
.contains(package.packageName),
isActive: accessControl.enable,
onChanged: (value) {
_handleSelected(
valueList, package, value);
},
);
},
),
);
}),
)
itemCount: packages.length,
itemExtent: 72,
itemBuilder: (_, index) {
final package = packages[index];
return PackageListItem(
key: Key(package.packageName),
package: package,
value: valueList.contains(
package.packageName,
),
isActive: accessControl.enable,
onChanged: (value) {
_handleSelected(
valueList,
package,
value,
);
},
);
},
),
);
},
),
),
],
),
),
@@ -400,22 +381,15 @@ class PackageListItem extends StatelessWidget {
),
title: Text(
package.label,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
style: const TextStyle(overflow: TextOverflow.ellipsis),
maxLines: 1,
),
subtitle: Text(
package.packageName,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
style: const TextStyle(overflow: TextOverflow.ellipsis),
maxLines: 1,
),
delegate: CheckboxDelegate(
value: value,
onChanged: onChanged,
),
delegate: CheckboxDelegate(value: value, onChanged: onChanged),
),
),
);
@@ -444,9 +418,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
},
icon: const Icon(Icons.clear),
),
const SizedBox(
width: 8,
)
const SizedBox(width: 8),
];
}
@@ -461,21 +433,21 @@ class AccessControlSearchDelegate extends SearchDelegate {
}
void _handleSelected(
WidgetRef ref, List<String> valueList, Package package, bool? value) {
WidgetRef ref,
List<String> valueList,
Package package,
bool? value,
) {
if (value == true) {
valueList.add(package.packageName);
} else {
valueList.remove(package.packageName);
}
ref.read(vpnSettingProvider.notifier).updateState((state) {
return switch (
state.accessControl.mode == AccessControlMode.acceptSelected) {
true => state.copyWith.accessControl(
acceptList: valueList,
),
false => state.copyWith.accessControl(
rejectList: valueList,
),
return switch (state.accessControl.mode ==
AccessControlMode.acceptSelected) {
true => state.copyWith.accessControl(acceptList: valueList),
false => state.copyWith.accessControl(rejectList: valueList),
};
});
}
@@ -483,7 +455,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
Widget _packageList() {
final lowQuery = query.toLowerCase();
return Consumer(
builder: (context, ref, __) {
builder: (context, ref, _) {
final vm3 = ref.watch(
packageListSelectorStateProvider.select(
(state) => VM3(
@@ -521,12 +493,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
value: valueList.contains(package.packageName),
isActive: isAccessControl,
onChanged: (value) {
_handleSelected(
ref,
valueList,
package,
value,
);
_handleSelected(ref, valueList, package, value);
},
);
},
@@ -548,9 +515,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
}
class AccessControlPanel extends ConsumerStatefulWidget {
const AccessControlPanel({
super.key,
});
const AccessControlPanel({super.key});
@override
ConsumerState createState() => _AccessControlPanelState();
@@ -595,7 +560,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final accessControlMode = ref.watch(
vpnSettingProvider.select((state) => state.accessControl.mode),
);
@@ -610,18 +575,19 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
),
isSelected: accessControlMode == item,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
mode: item,
),
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith.accessControl(mode: item),
);
},
)
),
],
);
},
),
)
),
],
);
}
@@ -634,7 +600,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final accessSortType = ref.watch(
vpnSettingProvider.select((state) => state.accessControl.sort),
);
@@ -649,10 +615,11 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
),
isSelected: accessSortType == item,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
sort: item,
),
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith.accessControl(sort: item),
);
},
),
@@ -673,7 +640,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final vm2 = ref.watch(
vpnSettingProvider.select(
(state) => VM2(
@@ -689,7 +656,9 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
appLocalizations.systemApp,
isSelected: vm2.a == false,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => state.copyWith.accessControl(
isFilterSystemApp: !vm2.a,
),
@@ -700,18 +669,20 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
appLocalizations.noNetworkApp,
isSelected: vm2.b == false,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => state.copyWith.accessControl(
isFilterNonInternetApp: !vm2.b,
),
);
},
)
),
],
);
},
),
)
),
],
);
}
@@ -719,31 +690,25 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
Future<void> _copyToClipboard() async {
await globalState.appController.safeRun(() {
final data = globalState.config.vpnProps.accessControl.toJson();
Clipboard.setData(
ClipboardData(
text: json.encode(data),
),
);
Clipboard.setData(ClipboardData(text: json.encode(data)));
});
if (!mounted) return;
Navigator.of(context).pop();
}
Future<void> _pasteToClipboard() async {
await globalState.appController.safeRun(
() async {
final data = await Clipboard.getData('text/plain');
final text = data?.text;
if (text == null) return;
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
accessControl: AccessControl.fromJson(
json.decode(text),
),
),
);
},
);
await globalState.appController.safeRun(() async {
final data = await Clipboard.getData('text/plain');
final text = data?.text;
if (text == null) return;
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => state.copyWith(
accessControl: AccessControl.fromJson(json.decode(text)),
),
);
});
if (!mounted) return;
Navigator.of(context).pop();
}
@@ -753,9 +718,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
title: appLocalizations.action,
items: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Wrap(
runSpacing: 16,
spacing: 16,
@@ -776,10 +739,10 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
avatar: const Icon(Icons.content_copy),
label: appLocalizations.clipboardExport,
onPressed: _copyToClipboard,
)
),
],
),
)
),
],
);
}

View File

@@ -19,11 +19,9 @@ class CloseConnectionsItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: closeConnections,
onChanged: (value) async {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
closeConnections: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(closeConnections: value));
},
),
);
@@ -44,10 +42,10 @@ class UsageItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: onlyStatisticsProxy,
onChanged: (bool value) async {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
onlyStatisticsProxy: value,
),
ref
.read(appSettingProvider.notifier)
.updateState(
(state) => state.copyWith(onlyStatisticsProxy: value),
);
},
),
@@ -69,11 +67,9 @@ class MinimizeItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: minimizeOnExit,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
minimizeOnExit: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(minimizeOnExit: value));
},
),
);
@@ -94,11 +90,9 @@ class AutoLaunchItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: autoLaunch,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoLaunch: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(autoLaunch: value));
},
),
);
@@ -119,11 +113,9 @@ class SilentLaunchItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: silentLaunch,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
silentLaunch: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(silentLaunch: value));
},
),
);
@@ -144,11 +136,9 @@ class AutoRunItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: autoRun,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoRun: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(autoRun: value));
},
),
);
@@ -169,11 +159,9 @@ class HiddenItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: hidden,
onChanged: (value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
hidden: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(hidden: value));
},
),
);
@@ -194,11 +182,9 @@ class AnimateTabItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: isAnimateToPage,
onChanged: (value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
isAnimateToPage: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(isAnimateToPage: value));
},
),
);
@@ -219,11 +205,32 @@ class OpenLogsItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: openLogs,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
openLogs: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(openLogs: value));
},
),
);
}
}
class CrashlyticsItem extends ConsumerWidget {
const CrashlyticsItem({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final crashlytics = ref.watch(
appSettingProvider.select((state) => state.crashlytics),
);
return ListItem.switchItem(
title: Text(appLocalizations.crashlytics),
subtitle: Text(appLocalizations.crashlyticsTip),
delegate: SwitchDelegate(
value: crashlytics,
onChanged: (bool value) {
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(crashlytics: value));
},
),
);
@@ -244,11 +251,9 @@ class AutoCheckUpdateItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: autoCheckUpdate,
onChanged: (bool value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoCheckUpdate: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(autoCheckUpdate: value));
},
),
);
@@ -267,18 +272,14 @@ class ApplicationSettingView extends StatelessWidget {
Widget build(BuildContext context) {
List<Widget> items = [
MinimizeItem(),
if (system.isDesktop) ...[
AutoLaunchItem(),
SilentLaunchItem(),
],
if (system.isDesktop) ...[AutoLaunchItem(), SilentLaunchItem()],
AutoRunItem(),
if (system.isAndroid) ...[
HiddenItem(),
],
if (system.isAndroid) ...[HiddenItem()],
AnimateTabItem(),
OpenLogsItem(),
CloseConnectionsItem(),
UsageItem(),
if (system.isAndroid) CrashlyticsItem(),
AutoCheckUpdateItem(),
];
return ListView.separated(
@@ -286,10 +287,8 @@ class ApplicationSettingView extends StatelessWidget {
final item = items[index];
return item;
},
separatorBuilder: (_, __) {
return const Divider(
height: 0,
);
separatorBuilder: (_, _) {
return const Divider(height: 0);
},
itemCount: items.length,
);

View File

@@ -20,9 +20,7 @@ class BackupAndRecovery extends ConsumerWidget {
Future<void> _showAddWebDAV(DAV? dav) async {
await globalState.showCommonDialog<String>(
child: WebDAVFormDialog(
dav: dav?.copyWith(),
),
child: WebDAVFormDialog(dav: dav?.copyWith()),
);
}
@@ -64,7 +62,9 @@ class BackupAndRecovery extends ConsumerWidget {
}
Future<void> _handleRecoveryOnWebDAV(
BuildContext context, DAVClient client) async {
BuildContext context,
DAVClient client,
) async {
final recoveryOption = await globalState.showCommonDialog<RecoveryOption>(
child: const RecoveryOptionsDialog(),
);
@@ -84,6 +84,7 @@ class BackupAndRecovery extends ConsumerWidget {
return true;
},
title: appLocalizations.backup,
needLoading: true,
);
if (res != true) return;
globalState.showMessage(
@@ -92,9 +93,7 @@ class BackupAndRecovery extends ConsumerWidget {
);
}
Future<void> _recoveryOnLocal(
RecoveryOption recoveryOption,
) async {
Future<void> _recoveryOnLocal(RecoveryOption recoveryOption) async {
final file = await picker.pickerFile();
final data = file?.bytes;
if (data == null) return;
@@ -128,35 +127,29 @@ class BackupAndRecovery extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(appDAVSettingProvider.notifier).updateState(
(state) => state?.copyWith(
fileName: value,
),
);
ref
.read(appDAVSettingProvider.notifier)
.updateState((state) => state?.copyWith(fileName: value));
}
Future<void> _handleUpdateRecoveryStrategy(WidgetRef ref) async {
final recoveryStrategy = ref.read(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
final recoveryStrategy = ref.read(
appSettingProvider.select((state) => state.recoveryStrategy),
);
final res = await globalState.showCommonDialog(
child: OptionsDialog<RecoveryStrategy>(
title: appLocalizations.recoveryStrategy,
options: RecoveryStrategy.values,
textBuilder: (mode) => Intl.message(
'recoveryStrategy_${mode.name}',
),
textBuilder: (mode) => Intl.message('recoveryStrategy_${mode.name}'),
value: recoveryStrategy,
),
);
if (res == null) {
return;
}
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
recoveryStrategy: res,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(recoveryStrategy: res));
}
@override
@@ -175,9 +168,7 @@ class BackupAndRecovery extends ConsumerWidget {
onPressed: () {
_showAddWebDAV(dav);
},
child: Text(
appLocalizations.bind,
),
child: Text(appLocalizations.bind),
),
)
else ...[
@@ -203,23 +194,23 @@ class BackupAndRecovery extends ConsumerWidget {
child: FadeThroughBox(
child:
snapshot.connectionState != ConnectionState.done
? const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
strokeWidth: 1,
),
)
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: snapshot.data == true
? Colors.green
: Colors.red,
),
width: 12,
height: 12,
),
? const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
strokeWidth: 1,
),
)
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: snapshot.data == true
? Colors.green
: Colors.red,
),
width: 12,
height: 12,
),
),
);
},
@@ -231,14 +222,10 @@ class BackupAndRecovery extends ConsumerWidget {
onPressed: () {
_showAddWebDAV(dav);
},
child: Text(
appLocalizations.edit,
),
child: Text(appLocalizations.edit),
),
),
const SizedBox(
height: 4,
),
const SizedBox(height: 4),
ListItem.input(
title: Text(appLocalizations.file),
subtitle: Text(dav.fileName),
@@ -282,25 +269,27 @@ class BackupAndRecovery extends ConsumerWidget {
subtitle: Text(appLocalizations.localRecoveryDesc),
),
ListHeader(title: appLocalizations.options),
Consumer(builder: (_, ref, __) {
final recoveryStrategy = ref.watch(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
return ListItem(
onTap: () {
_handleUpdateRecoveryStrategy(ref);
},
title: Text(appLocalizations.recoveryStrategy),
trailing: FilledButton(
onPressed: () {
Consumer(
builder: (_, ref, _) {
final recoveryStrategy = ref.watch(
appSettingProvider.select((state) => state.recoveryStrategy),
);
return ListItem(
onTap: () {
_handleUpdateRecoveryStrategy(ref);
},
child: Text(
Intl.message('recoveryStrategy_${recoveryStrategy.name}'),
title: Text(appLocalizations.recoveryStrategy),
trailing: FilledButton(
onPressed: () {
_handleUpdateRecoveryStrategy(ref);
},
child: Text(
Intl.message('recoveryStrategy_${recoveryStrategy.name}'),
),
),
),
);
}),
);
},
),
],
);
}
@@ -323,10 +312,7 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
Widget build(BuildContext context) {
return CommonDialog(
title: appLocalizations.recovery,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Wrap(
children: [
ListItem(
@@ -340,7 +326,7 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
_handleOnTab(RecoveryOption.all);
},
title: Text(appLocalizations.recoveryAll),
)
),
],
),
);
@@ -398,14 +384,8 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
title: appLocalizations.webDAVConfiguration,
actions: [
if (widget.dav != null)
TextButton(
onPressed: _delete,
child: Text(appLocalizations.delete),
),
TextButton(
onPressed: _submit,
child: Text(appLocalizations.save),
)
TextButton(onPressed: _delete, child: Text(appLocalizations.delete)),
TextButton(onPressed: _submit, child: Text(appLocalizations.save)),
],
child: Form(
key: _formKey,
@@ -445,7 +425,7 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
),
ValueListenableBuilder(
valueListenable: _obscureController,
builder: (_, obscure, __) {
builder: (_, obscure, _) {
return TextFormField(
controller: passwordController,
obscureText: obscure,
@@ -464,8 +444,9 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations
.emptyTip(appLocalizations.password);
return appLocalizations.emptyTip(
appLocalizations.password,
);
}
return null;
},

View File

@@ -27,9 +27,7 @@ class _ConfigViewState extends State<ConfigView> {
leading: const Icon(Icons.build),
delegate: OpenDelegate(
title: appLocalizations.general,
widget: generateListView(
generalItems,
),
widget: generateListView(generalItems),
blur: false,
),
),
@@ -41,35 +39,35 @@ class _ConfigViewState extends State<ConfigView> {
title: appLocalizations.network,
blur: false,
actions: [
Consumer(builder: (_, ref, __) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(vpnSettingProvider.notifier).updateState(
(state) => defaultVpnProps.copyWith(
accessControl: state.accessControl,
),
);
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
tun: defaultTun,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
);
})
Consumer(
builder: (_, ref, _) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(text: appLocalizations.resetTip),
);
if (res != true) {
return;
}
ref
.read(vpnSettingProvider.notifier)
.updateState(
(state) => defaultVpnProps.copyWith(
accessControl: state.accessControl,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(tun: defaultTun),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(Icons.replay),
);
},
),
],
widget: const NetworkListView(),
),
@@ -81,44 +79,34 @@ class _ConfigViewState extends State<ConfigView> {
delegate: OpenDelegate(
title: 'DNS',
actions: [
Consumer(builder: (_, ref, __) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
dns: defaultDns,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
);
})
Consumer(
builder: (_, ref, _) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(text: appLocalizations.resetTip),
);
if (res != true) {
return;
}
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(dns: defaultDns),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(Icons.replay),
);
},
),
],
widget: const DnsListView(),
blur: false,
),
)
),
];
return generateListView(
items
.separated(
const Divider(
height: 0,
),
)
.toList(),
);
return generateListView(items.separated(const Divider(height: 0)).toList());
}
}

View File

@@ -29,8 +29,9 @@ class StatusItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final enable =
ref.watch(patchClashConfigProvider.select((state) => state.dns.enable));
final enable = ref.watch(
patchClashConfigProvider.select((state) => state.dns.enable),
);
return ListItem.switchItem(
title: Text(appLocalizations.status),
subtitle: Text(appLocalizations.statusDesc),
@@ -51,8 +52,9 @@ class ListenItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final listen =
ref.watch(patchClashConfigProvider.select((state) => state.dns.listen));
final listen = ref.watch(
patchClashConfigProvider.select((state) => state.dns.listen),
);
return ListItem.input(
title: Text(appLocalizations.listen),
subtitle: Text(listen),
@@ -83,8 +85,9 @@ class PreferH3Item extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferH3 = ref
.watch(patchClashConfigProvider.select((state) => state.dns.preferH3));
final preferH3 = ref.watch(
patchClashConfigProvider.select((state) => state.dns.preferH3),
);
return ListItem.switchItem(
title: const Text('PreferH3'),
subtitle: Text(appLocalizations.preferH3Desc),
@@ -218,10 +221,11 @@ class FakeIpFilterItem extends StatelessWidget {
blur: false,
title: appLocalizations.fakeipFilter,
widget: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final fakeIpFilter = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fakeIpFilter),
patchClashConfigProvider.select(
(state) => state.dns.fakeIpFilter,
),
);
return ListInputPage(
title: appLocalizations.fakeipFilter,
@@ -230,9 +234,10 @@ class FakeIpFilterItem extends StatelessWidget {
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(
fakeIpFilter: List.from(items),
));
.updateState(
(state) =>
state.copyWith.dns(fakeIpFilter: List.from(items)),
);
},
);
},
@@ -253,24 +258,29 @@ class DefaultNameserverItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: appLocalizations.defaultNameserver,
widget: Consumer(builder: (_, ref, __) {
final defaultNameserver = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.defaultNameserver),
);
return ListInputPage(
title: appLocalizations.defaultNameserver,
items: defaultNameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
defaultNameserver: List.from(items),
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final defaultNameserver = ref.watch(
patchClashConfigProvider.select(
(state) => state.dns.defaultNameserver,
),
);
return ListInputPage(
title: appLocalizations.defaultNameserver,
items: defaultNameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns(
defaultNameserver: List.from(items),
),
);
},
);
},
),
),
);
}
@@ -287,23 +297,26 @@ class NameserverItem extends StatelessWidget {
delegate: OpenDelegate(
title: appLocalizations.nameserver,
blur: false,
widget: Consumer(builder: (_, ref, __) {
final nameserver = ref.watch(
patchClashConfigProvider.select((state) => state.dns.nameserver),
);
return ListInputPage(
title: appLocalizations.nameserver,
items: nameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
nameserver: List.from(items),
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final nameserver = ref.watch(
patchClashConfigProvider.select((state) => state.dns.nameserver),
);
return ListInputPage(
title: appLocalizations.nameserver,
items: nameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) =>
state.copyWith.dns(nameserver: List.from(items)),
);
},
);
},
),
),
);
}
@@ -346,9 +359,9 @@ class UseSystemHostsItem extends ConsumerWidget {
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(
useSystemHosts: value,
));
.updateState(
(state) => state.copyWith.dns(useSystemHosts: value),
);
},
),
);
@@ -366,25 +379,28 @@ class NameserverPolicyItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: appLocalizations.nameserverPolicy,
widget: Consumer(builder: (_, ref, __) {
final nameserverPolicy = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.nameserverPolicy),
);
return MapInputPage(
title: appLocalizations.nameserverPolicy,
map: nameserverPolicy,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (value) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
nameserverPolicy: value,
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final nameserverPolicy = ref.watch(
patchClashConfigProvider.select(
(state) => state.dns.nameserverPolicy,
),
);
return MapInputPage(
title: appLocalizations.nameserverPolicy,
map: nameserverPolicy,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (value) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns(nameserverPolicy: value),
);
},
);
},
),
),
);
}
@@ -402,17 +418,20 @@ class ProxyServerNameserverItem extends StatelessWidget {
blur: false,
title: appLocalizations.proxyNameserver,
widget: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final proxyServerNameserver = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.proxyServerNameserver),
patchClashConfigProvider.select(
(state) => state.dns.proxyServerNameserver,
),
);
return ListInputPage(
title: appLocalizations.proxyNameserver,
items: proxyServerNameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns(
proxyServerNameserver: List.from(items),
),
@@ -437,23 +456,25 @@ class FallbackItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: appLocalizations.fallback,
widget: Consumer(builder: (_, ref, __) {
final fallback = ref.watch(
patchClashConfigProvider.select((state) => state.dns.fallback),
);
return ListInputPage(
title: appLocalizations.fallback,
items: fallback,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
fallback: List.from(items),
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final fallback = ref.watch(
patchClashConfigProvider.select((state) => state.dns.fallback),
);
return ListInputPage(
title: appLocalizations.fallback,
items: fallback,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns(fallback: List.from(items)),
);
},
);
},
),
),
);
}
@@ -465,8 +486,9 @@ class GeoipItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final geoip = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geoip),
patchClashConfigProvider.select(
(state) => state.dns.fallbackFilter.geoip,
),
);
return ListItem.switchItem(
title: const Text('Geoip'),
@@ -475,9 +497,9 @@ class GeoipItem extends ConsumerWidget {
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns.fallbackFilter(
geoip: value,
));
.updateState(
(state) => state.copyWith.dns.fallbackFilter(geoip: value),
);
},
),
);
@@ -490,8 +512,9 @@ class GeoipCodeItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final geoipCode = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geoipCode),
patchClashConfigProvider.select(
(state) => state.dns.fallbackFilter.geoipCode,
),
);
return ListItem.input(
title: Text(appLocalizations.geoipCode),
@@ -509,10 +532,10 @@ class GeoipCodeItem extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
geoipCode: value,
),
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns.fallbackFilter(geoipCode: value),
);
},
),
@@ -530,24 +553,29 @@ class GeositeItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: 'Geosite',
widget: Consumer(builder: (_, ref, __) {
final geosite = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geosite),
);
return ListInputPage(
title: 'Geosite',
items: geosite,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
geosite: List.from(items),
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final geosite = ref.watch(
patchClashConfigProvider.select(
(state) => state.dns.fallbackFilter.geosite,
),
);
return ListInputPage(
title: 'Geosite',
items: geosite,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns.fallbackFilter(
geosite: List.from(items),
),
);
},
);
},
),
),
);
}
@@ -563,24 +591,29 @@ class IpcidrItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: appLocalizations.ipcidr,
widget: Consumer(builder: (_, ref, ___) {
final ipcidr = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.ipcidr),
);
return ListInputPage(
title: appLocalizations.ipcidr,
items: ipcidr,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns.fallbackFilter(
widget: Consumer(
builder: (_, ref, _) {
final ipcidr = ref.watch(
patchClashConfigProvider.select(
(state) => state.dns.fallbackFilter.ipcidr,
),
);
return ListInputPage(
title: appLocalizations.ipcidr,
items: ipcidr,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns.fallbackFilter(
ipcidr: List.from(items),
));
},
);
}),
),
);
},
);
},
),
),
);
}
@@ -596,24 +629,29 @@ class DomainItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
title: appLocalizations.domain,
widget: Consumer(builder: (_, ref, __) {
final domain = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.domain),
);
return ListInputPage(
title: appLocalizations.domain,
items: domain,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
domain: List.from(items),
),
);
},
);
}),
widget: Consumer(
builder: (_, ref, _) {
final domain = ref.watch(
patchClashConfigProvider.select(
(state) => state.dns.fallbackFilter.domain,
),
);
return ListInputPage(
title: appLocalizations.domain,
items: domain,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith.dns.fallbackFilter(
domain: List.from(items),
),
);
},
);
},
),
),
);
}
@@ -680,8 +718,6 @@ class DnsListView extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
return generateListView(
dnsItems,
);
return generateListView(dnsItems);
}
}

View File

@@ -12,8 +12,9 @@ class LogLevelItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final logLevel =
ref.watch(patchClashConfigProvider.select((state) => state.logLevel));
final logLevel = ref.watch(
patchClashConfigProvider.select((state) => state.logLevel),
);
return ListItem<LogLevel>.options(
leading: const Icon(Icons.info_outline),
title: Text(appLocalizations.logLevel),
@@ -25,11 +26,9 @@ class LogLevelItem extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
logLevel: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(logLevel: value));
},
textBuilder: (logLevel) => logLevel.name,
value: logLevel,
@@ -43,26 +42,21 @@ class UaItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final globalUa =
ref.watch(patchClashConfigProvider.select((state) => state.globalUa));
final globalUa = ref.watch(
patchClashConfigProvider.select((state) => state.globalUa),
);
return ListItem<String?>.options(
leading: const Icon(Icons.computer_outlined),
title: const Text('UA'),
subtitle: Text(globalUa ?? appLocalizations.defaultText),
delegate: OptionsDelegate<String?>(
title: 'UA',
options: [
null,
'clash-verge/v1.6.6',
'ClashforWindows/0.19.23',
],
options: [null, 'clash-verge/v2.4.2', 'ClashforWindows/0.19.23'],
value: globalUa,
onChanged: (value) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
globalUa: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(globalUa: value));
},
textBuilder: (ua) => ua ?? appLocalizations.defaultText,
),
@@ -76,7 +70,8 @@ class KeepAliveIntervalItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final keepAliveInterval = ref.watch(
patchClashConfigProvider.select((state) => state.keepAliveInterval));
patchClashConfigProvider.select((state) => state.keepAliveInterval),
);
return ListItem.input(
leading: const Icon(Icons.timer_outlined),
title: Text(appLocalizations.keepAliveIntervalDesc),
@@ -101,10 +96,10 @@ class KeepAliveIntervalItem extends ConsumerWidget {
return;
}
final intValue = int.parse(value);
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
keepAliveInterval: intValue,
),
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(keepAliveInterval: intValue),
);
},
),
@@ -117,8 +112,9 @@ class TestUrlItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final testUrl =
ref.watch(appSettingProvider.select((state) => state.testUrl));
final testUrl = ref.watch(
appSettingProvider.select((state) => state.testUrl),
);
return ListItem.input(
leading: const Icon(Icons.timeline),
title: Text(appLocalizations.testUrl),
@@ -140,11 +136,9 @@ class TestUrlItem extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
testUrl: value,
),
);
ref
.read(appSettingProvider.notifier)
.updateState((state) => state.copyWith(testUrl: value));
},
),
);
@@ -155,16 +149,15 @@ class PortItem extends ConsumerWidget {
const PortItem({super.key});
Future<void> handleShowPortDialog() async {
await globalState.showCommonDialog(
child: _PortDialog(),
);
await globalState.showCommonDialog(child: _PortDialog());
// inputDelegate.onChanged(value);
}
@override
Widget build(BuildContext context, ref) {
final mixedPort =
ref.watch(patchClashConfigProvider.select((state) => state.mixedPort));
final mixedPort = ref.watch(
patchClashConfigProvider.select((state) => state.mixedPort),
);
return ListItem(
leading: const Icon(Icons.adjust_outlined),
title: Text(appLocalizations.port),
@@ -218,20 +211,19 @@ class HostsItem extends StatelessWidget {
blur: false,
title: 'Hosts',
widget: Consumer(
builder: (_, ref, __) {
final hosts = ref
.watch(patchClashConfigProvider.select((state) => state.hosts));
builder: (_, ref, _) {
final hosts = ref.watch(
patchClashConfigProvider.select((state) => state.hosts),
);
return MapInputPage(
title: 'Hosts',
map: hosts,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (value) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
hosts: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(hosts: value));
},
);
},
@@ -246,8 +238,9 @@ class Ipv6Item extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final ipv6 =
ref.watch(patchClashConfigProvider.select((state) => state.ipv6));
final ipv6 = ref.watch(
patchClashConfigProvider.select((state) => state.ipv6),
);
return ListItem.switchItem(
leading: const Icon(Icons.water_outlined),
title: const Text('IPv6'),
@@ -255,11 +248,9 @@ class Ipv6Item extends ConsumerWidget {
delegate: SwitchDelegate(
value: ipv6,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
ipv6: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(ipv6: value));
},
),
);
@@ -271,8 +262,9 @@ class AllowLanItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final allowLan =
ref.watch(patchClashConfigProvider.select((state) => state.allowLan));
final allowLan = ref.watch(
patchClashConfigProvider.select((state) => state.allowLan),
);
return ListItem.switchItem(
leading: const Icon(Icons.device_hub),
title: Text(appLocalizations.allowLan),
@@ -280,11 +272,9 @@ class AllowLanItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: allowLan,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
allowLan: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(allowLan: value));
},
),
);
@@ -296,8 +286,9 @@ class UnifiedDelayItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final unifiedDelay = ref
.watch(patchClashConfigProvider.select((state) => state.unifiedDelay));
final unifiedDelay = ref.watch(
patchClashConfigProvider.select((state) => state.unifiedDelay),
);
return ListItem.switchItem(
leading: const Icon(Icons.compress_outlined),
@@ -306,11 +297,9 @@ class UnifiedDelayItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: unifiedDelay,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
unifiedDelay: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(unifiedDelay: value));
},
),
);
@@ -322,8 +311,11 @@ class FindProcessItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final findProcess = ref.watch(patchClashConfigProvider
.select((state) => state.findProcessMode == FindProcessMode.always));
final findProcess = ref.watch(
patchClashConfigProvider.select(
(state) => state.findProcessMode == FindProcessMode.always,
),
);
return ListItem.switchItem(
leading: const Icon(Icons.polymer_outlined),
@@ -332,10 +324,13 @@ class FindProcessItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: findProcess,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(
findProcessMode:
value ? FindProcessMode.always : FindProcessMode.off,
findProcessMode: value
? FindProcessMode.always
: FindProcessMode.off,
),
);
},
@@ -349,8 +344,9 @@ class TcpConcurrentItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final tcpConcurrent = ref
.watch(patchClashConfigProvider.select((state) => state.tcpConcurrent));
final tcpConcurrent = ref.watch(
patchClashConfigProvider.select((state) => state.tcpConcurrent),
);
return ListItem.switchItem(
leading: const Icon(Icons.double_arrow_outlined),
title: Text(appLocalizations.tcpConcurrent),
@@ -358,11 +354,9 @@ class TcpConcurrentItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: tcpConcurrent,
onChanged: (value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
tcpConcurrent: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith(tcpConcurrent: value));
},
),
);
@@ -374,8 +368,11 @@ class GeodataLoaderItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final isMemconservative = ref.watch(patchClashConfigProvider.select(
(state) => state.geodataLoader == GeodataLoader.memconservative));
final isMemconservative = ref.watch(
patchClashConfigProvider.select(
(state) => state.geodataLoader == GeodataLoader.memconservative,
),
);
return ListItem.switchItem(
leading: const Icon(Icons.memory),
title: Text(appLocalizations.geodataLoader),
@@ -383,7 +380,9 @@ class GeodataLoaderItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: isMemconservative,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(
geodataLoader: value
? GeodataLoader.memconservative
@@ -401,8 +400,11 @@ class ExternalControllerItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final hasExternalController = ref.watch(patchClashConfigProvider.select(
(state) => state.externalController == ExternalControllerStatus.open));
final hasExternalController = ref.watch(
patchClashConfigProvider.select(
(state) => state.externalController == ExternalControllerStatus.open,
),
);
return ListItem.switchItem(
leading: const Icon(Icons.api_outlined),
title: Text(appLocalizations.externalController),
@@ -410,7 +412,9 @@ class ExternalControllerItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: hasExternalController,
onChanged: (bool value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(
externalController: value
? ExternalControllerStatus.open
@@ -437,13 +441,7 @@ final generalItems = <Widget>[
TcpConcurrentItem(),
GeodataLoaderItem(),
ExternalControllerItem(),
]
.separated(
const Divider(
height: 0,
),
)
.toList();
].separated(const Divider(height: 0)).toList();
class _PortDialog extends ConsumerStatefulWidget {
const _PortDialog();
@@ -465,42 +463,34 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
@override
void initState() {
super.initState();
final vm5 = ref.read(patchClashConfigProvider.select((state) {
return VM5(
a: state.mixedPort,
b: state.port,
c: state.socksPort,
d: state.redirPort,
e: state.tproxyPort,
);
}));
_mixedPortController = TextEditingController(
text: vm5.a.toString(),
);
_portController = TextEditingController(
text: vm5.b.toString(),
);
_socksPortController = TextEditingController(
text: vm5.c.toString(),
);
_redirPortController = TextEditingController(
text: vm5.d.toString(),
);
_tProxyPortController = TextEditingController(
text: vm5.e.toString(),
final vm5 = ref.read(
patchClashConfigProvider.select((state) {
return VM5(
a: state.mixedPort,
b: state.port,
c: state.socksPort,
d: state.redirPort,
e: state.tproxyPort,
);
}),
);
_mixedPortController = TextEditingController(text: vm5.a.toString());
_portController = TextEditingController(text: vm5.b.toString());
_socksPortController = TextEditingController(text: vm5.c.toString());
_redirPortController = TextEditingController(text: vm5.d.toString());
_tProxyPortController = TextEditingController(text: vm5.e.toString());
}
Future<void> _handleReset() async {
final res = await globalState.showMessage(
message: TextSpan(
text: appLocalizations.resetTip,
),
message: TextSpan(text: appLocalizations.resetTip),
);
if (res != true) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(
mixedPort: 7890,
port: 0,
@@ -516,7 +506,9 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
void _handleUpdate() {
if (_formKey.currentState?.validate() == false) return;
ref.read(patchClashConfigProvider.notifier).updateState(
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) => state.copyWith(
mixedPort: int.parse(_mixedPortController.text),
port: int.parse(_portController.text),
@@ -544,9 +536,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
children: [
IconButton.filledTonal(
onPressed: _handleMore,
icon: CommonExpandIcon(
expand: _isMore,
),
icon: CommonExpandIcon(expand: _isMore),
),
Row(
children: [
@@ -554,17 +544,15 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
onPressed: _handleReset,
child: Text(appLocalizations.reset),
),
const SizedBox(
width: 4,
),
const SizedBox(width: 4),
TextButton(
onPressed: _handleUpdate,
child: Text(appLocalizations.submit),
)
),
],
)
),
],
)
),
],
child: Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
@@ -592,23 +580,26 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
),
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations
.emptyTip(appLocalizations.mixedPort);
return appLocalizations.emptyTip(
appLocalizations.mixedPort,
);
}
final port = int.tryParse(value);
if (port == null) {
return appLocalizations
.numberTip(appLocalizations.mixedPort);
return appLocalizations.numberTip(
appLocalizations.mixedPort,
);
}
if (port < 1024 || port > 49151) {
return appLocalizations
.portTip(appLocalizations.mixedPort);
return appLocalizations.portTip(
appLocalizations.mixedPort,
);
}
final ports = [
_portController.text,
_socksPortController.text,
_tProxyPortController.text,
_redirPortController.text
_redirPortController.text,
].map((item) => item.trim());
if (ports.contains(value.trim())) {
return appLocalizations.portConflictTip;
@@ -649,7 +640,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
_mixedPortController.text,
_socksPortController.text,
_tProxyPortController.text,
_redirPortController.text
_redirPortController.text,
].map((item) => item.trim());
if (ports.contains(value.trim())) {
return appLocalizations.portConflictTip;
@@ -671,26 +662,29 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
),
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations
.emptyTip(appLocalizations.socksPort);
return appLocalizations.emptyTip(
appLocalizations.socksPort,
);
}
final port = int.tryParse(value);
if (port == null) {
return appLocalizations
.numberTip(appLocalizations.socksPort);
return appLocalizations.numberTip(
appLocalizations.socksPort,
);
}
if (port == 0) {
return null;
}
if (port < 1024 || port > 49151) {
return appLocalizations
.portTip(appLocalizations.socksPort);
return appLocalizations.portTip(
appLocalizations.socksPort,
);
}
final ports = [
_portController.text,
_mixedPortController.text,
_tProxyPortController.text,
_redirPortController.text
_redirPortController.text,
].map((item) => item.trim());
if (ports.contains(value.trim())) {
return appLocalizations.portConflictTip;
@@ -712,26 +706,29 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
),
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations
.emptyTip(appLocalizations.redirPort);
return appLocalizations.emptyTip(
appLocalizations.redirPort,
);
}
final port = int.tryParse(value);
if (port == null) {
return appLocalizations
.numberTip(appLocalizations.redirPort);
return appLocalizations.numberTip(
appLocalizations.redirPort,
);
}
if (port == 0) {
return null;
}
if (port < 1024 || port > 49151) {
return appLocalizations
.portTip(appLocalizations.redirPort);
return appLocalizations.portTip(
appLocalizations.redirPort,
);
}
final ports = [
_portController.text,
_socksPortController.text,
_tProxyPortController.text,
_mixedPortController.text
_mixedPortController.text,
].map((item) => item.trim());
if (ports.contains(value.trim())) {
return appLocalizations.portConflictTip;
@@ -753,13 +750,15 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
),
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations
.emptyTip(appLocalizations.tproxyPort);
return appLocalizations.emptyTip(
appLocalizations.tproxyPort,
);
}
final port = int.tryParse(value);
if (port == null) {
return appLocalizations
.numberTip(appLocalizations.tproxyPort);
return appLocalizations.numberTip(
appLocalizations.tproxyPort,
);
}
if (port == 0) {
return null;
@@ -773,7 +772,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
_portController.text,
_socksPortController.text,
_mixedPortController.text,
_redirPortController.text
_redirPortController.text,
].map((item) => item.trim());
if (ports.contains(value.trim())) {
return appLocalizations.portConflictTip;
@@ -782,7 +781,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
return null;
},
),
]
],
],
),
),

View File

@@ -13,19 +13,18 @@ class VPNItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final enable =
ref.watch(vpnSettingProvider.select((state) => state.enable));
final enable = ref.watch(
vpnSettingProvider.select((state) => state.enable),
);
return ListItem.switchItem(
title: const Text('VPN'),
subtitle: Text(appLocalizations.vpnEnableDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (value) async {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
enable: value,
),
);
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(enable: value));
},
),
);
@@ -37,8 +36,9 @@ class TUNItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final enable =
ref.watch(patchClashConfigProvider.select((state) => state.tun.enable));
final enable = ref.watch(
patchClashConfigProvider.select((state) => state.tun.enable),
);
return ListItem.switchItem(
title: Text(appLocalizations.tun),
@@ -46,11 +46,9 @@ class TUNItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: enable,
onChanged: (value) async {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.tun(
enable: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.tun(enable: value));
},
),
);
@@ -62,19 +60,18 @@ class AllowBypassItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final allowBypass =
ref.watch(vpnSettingProvider.select((state) => state.allowBypass));
final allowBypass = ref.watch(
vpnSettingProvider.select((state) => state.allowBypass),
);
return ListItem.switchItem(
title: Text(appLocalizations.allowBypass),
subtitle: Text(appLocalizations.allowBypassDesc),
delegate: SwitchDelegate(
value: allowBypass,
onChanged: (bool value) async {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
allowBypass: value,
),
);
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(allowBypass: value));
},
),
);
@@ -86,19 +83,18 @@ class VpnSystemProxyItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final systemProxy =
ref.watch(vpnSettingProvider.select((state) => state.systemProxy));
final systemProxy = ref.watch(
vpnSettingProvider.select((state) => state.systemProxy),
);
return ListItem.switchItem(
title: Text(appLocalizations.systemProxy),
subtitle: Text(appLocalizations.systemProxyDesc),
delegate: SwitchDelegate(
value: systemProxy,
onChanged: (bool value) async {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
systemProxy: value,
),
);
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(systemProxy: value));
},
),
);
@@ -110,8 +106,9 @@ class SystemProxyItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final systemProxy =
ref.watch(networkSettingProvider.select((state) => state.systemProxy));
final systemProxy = ref.watch(
networkSettingProvider.select((state) => state.systemProxy),
);
return ListItem.switchItem(
title: Text(appLocalizations.systemProxy),
@@ -119,11 +116,9 @@ class SystemProxyItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: systemProxy,
onChanged: (bool value) async {
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
systemProxy: value,
),
);
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(systemProxy: value));
},
),
);
@@ -142,11 +137,9 @@ class Ipv6Item extends ConsumerWidget {
delegate: SwitchDelegate(
value: ipv6,
onChanged: (bool value) async {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
ipv6: value,
),
);
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(ipv6: value));
},
),
);
@@ -159,17 +152,16 @@ class AutoSetSystemDnsItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final autoSetSystemDns = ref.watch(
networkSettingProvider.select((state) => state.autoSetSystemDns));
networkSettingProvider.select((state) => state.autoSetSystemDns),
);
return ListItem.switchItem(
title: Text(appLocalizations.autoSetSystemDns),
delegate: SwitchDelegate(
value: autoSetSystemDns,
onChanged: (bool value) async {
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoSetSystemDns: value,
),
);
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(autoSetSystemDns: value));
},
),
);
@@ -181,8 +173,9 @@ class TunStackItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final stack =
ref.watch(patchClashConfigProvider.select((state) => state.tun.stack));
final stack = ref.watch(
patchClashConfigProvider.select((state) => state.tun.stack),
);
return ListItem.options(
title: Text(appLocalizations.stackMode),
@@ -195,11 +188,9 @@ class TunStackItem extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.tun(
stack: value,
),
);
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.tun(stack: value));
},
title: appLocalizations.stackMode,
),
@@ -218,45 +209,45 @@ class BypassDomainItem extends StatelessWidget {
delegate: OpenDelegate(
blur: false,
actions: [
Consumer(builder: (_, ref, __) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
bypassDomain: defaultBypassDomain,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
);
})
Consumer(
builder: (_, ref, _) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(text: appLocalizations.resetTip),
);
if (res != true) {
return;
}
ref
.read(networkSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith(bypassDomain: defaultBypassDomain),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(Icons.replay),
);
},
),
],
title: appLocalizations.bypassDomain,
widget: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final bypassDomain = ref.watch(
networkSettingProvider.select((state) => state.bypassDomain));
networkSettingProvider.select((state) => state.bypassDomain),
);
return ListInputPage(
title: appLocalizations.bypassDomain,
items: bypassDomain,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
bypassDomain: List.from(items),
),
ref
.read(networkSettingProvider.notifier)
.updateState(
(state) => state.copyWith(bypassDomain: List.from(items)),
);
},
);
@@ -267,13 +258,36 @@ class BypassDomainItem extends StatelessWidget {
}
}
class DNSHijackingItem extends ConsumerWidget {
const DNSHijackingItem({super.key});
@override
Widget build(BuildContext context, ref) {
final dnsHijacking = ref.watch(
vpnSettingProvider.select((state) => state.dnsHijacking),
);
return ListItem<RouteMode>.switchItem(
title: Text(appLocalizations.dnsHijacking),
delegate: SwitchDelegate(
value: dnsHijacking,
onChanged: (value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(dnsHijacking: value));
},
),
);
}
}
class RouteModeItem extends ConsumerWidget {
const RouteModeItem({super.key});
@override
Widget build(BuildContext context, ref) {
final routeMode =
ref.watch(networkSettingProvider.select((state) => state.routeMode));
final routeMode = ref.watch(
networkSettingProvider.select((state) => state.routeMode),
);
return ListItem<RouteMode>.options(
title: Text(appLocalizations.routeMode),
subtitle: Text(Intl.message('routeMode_${routeMode.name}')),
@@ -284,15 +298,11 @@ class RouteModeItem extends ConsumerWidget {
if (value == null) {
return;
}
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
routeMode: value,
),
);
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(routeMode: value));
},
textBuilder: (routeMode) => Intl.message(
'routeMode_${routeMode.name}',
),
textBuilder: (routeMode) => Intl.message('routeMode_${routeMode.name}'),
value: routeMode,
),
);
@@ -304,8 +314,11 @@ class RouteAddressItem extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final bypassPrivate = ref.watch(networkSettingProvider
.select((state) => state.routeMode == RouteMode.bypassPrivate));
final bypassPrivate = ref.watch(
networkSettingProvider.select(
(state) => state.routeMode == RouteMode.bypassPrivate,
),
);
if (bypassPrivate) {
return Container();
}
@@ -317,7 +330,7 @@ class RouteAddressItem extends ConsumerWidget {
maxWidth: 360,
title: appLocalizations.routeAddress,
widget: Consumer(
builder: (_, ref, __) {
builder: (_, ref, _) {
final routeAddress = ref.watch(
patchClashConfigProvider.select(
(state) => state.tun.routeAddress,
@@ -328,10 +341,11 @@ class RouteAddressItem extends ConsumerWidget {
items: routeAddress,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.tun(
routeAddress: List.from(items),
),
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) =>
state.copyWith.tun(routeAddress: List.from(items)),
);
},
);
@@ -352,15 +366,13 @@ final networkItems = [
const BypassDomainItem(),
const AllowBypassItem(),
const Ipv6Item(),
const DNSHijackingItem(),
],
),
if (system.isDesktop)
...generateSection(
title: appLocalizations.system,
items: [
SystemProxyItem(),
BypassDomainItem(),
],
items: [SystemProxyItem(), BypassDomainItem()],
),
...generateSection(
title: appLocalizations.options,
@@ -371,7 +383,7 @@ final networkItems = [
if (!system.isDesktop) ...[
const RouteModeItem(),
const RouteAddressItem(),
]
],
],
),
];
@@ -381,8 +393,6 @@ class NetworkListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return generateListView(
networkItems,
);
return generateListView(networkItems);
}
}

View File

@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/controller.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
@@ -28,7 +28,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
return [
IconButton(
onPressed: () async {
clashCore.closeConnections();
coreController.closeConnections();
await _updateConnections();
},
icon: const Icon(Icons.delete_sweep_outlined),
@@ -43,8 +43,9 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
}
void _onKeywordsUpdate(List<String> keywords) {
_connectionsStateNotifier.value =
_connectionsStateNotifier.value.copyWith(keywords: keywords);
_connectionsStateNotifier.value = _connectionsStateNotifier.value.copyWith(
keywords: keywords,
);
}
Future<void> _updateConnectionsTask() async {
@@ -66,12 +67,12 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
Future<void> _updateConnections() async {
_connectionsStateNotifier.value = _connectionsStateNotifier.value.copyWith(
trackerInfos: await clashCore.getConnections(),
trackerInfos: await coreController.getConnections(),
);
}
Future<void> _handleBlockConnection(String id) async {
clashCore.closeConnection(id);
coreController.closeConnection(id);
await _updateConnections();
}
@@ -93,7 +94,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
actions: _buildActions(),
body: ValueListenableBuilder<TrackerInfosState>(
valueListenable: _connectionsStateNotifier,
builder: (context, state, __) {
builder: (context, state, _) {
final connections = state.list;
if (connections.isEmpty) {
return NullStatus(
@@ -111,9 +112,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
trailing: IconButton(
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
style: ButtonStyle(
minimumSize: WidgetStatePropertyAll(Size.zero),
),
style: IconButton.styleFrom(minimumSize: Size.zero),
icon: const Icon(Icons.block),
onPressed: () {
_handleBlockConnection(trackerInfo.id);
@@ -124,11 +123,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
),
),
)
.separated(
const Divider(
height: 0,
),
)
.separated(const Divider(height: 0))
.toList();
return ListView.builder(
controller: _scrollController,

Some files were not shown because too many files have changed in this diff Show More