Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
2f9dc02436 Support override script
Support proxies search

Support svg display

Add some scenes auto close connections

Update core

Optimize more details
2025-06-02 15:37:08 +08:00
29 changed files with 359 additions and 201 deletions

View File

@@ -402,5 +402,8 @@
"redirPort": "Redir Port",
"tproxyPort": "Tproxy Port",
"portTip": "{label} must be between 1024 and 49151",
"portConflictTip": "Please enter a different port"
"portConflictTip": "Please enter a different port",
"import": "Import",
"importFile": "Import from file",
"importUrl": "Import from URL"
}

View File

@@ -403,5 +403,8 @@
"redirPort": "Redirポート",
"tproxyPort": "Tproxyポート",
"portTip": "{label} は 1024 から 49151 の間でなければなりません",
"portConflictTip": "別のポートを入力してください"
"portConflictTip": "別のポートを入力してください",
"import": "インポート",
"importFile": "ファイルからインポート",
"importUrl": "URLからインポート"
}

View File

@@ -403,5 +403,8 @@
"redirPort": "Redir-порт",
"tproxyPort": "Tproxy-порт",
"portTip": "{label} должен быть числом от 1024 до 49151",
"portConflictTip": "Введите другой порт"
"portConflictTip": "Введите другой порт",
"import": "Импорт",
"importFile": "Импорт из файла",
"importUrl": "Импорт по URL"
}

View File

@@ -403,5 +403,8 @@
"redirPort": "Redir端口",
"tproxyPort": "Tproxy端口",
"portTip": "{label} 必须在 1024 到 49151 之间",
"portConflictTip": "请输入不同的端口"
"portConflictTip": "请输入不同的端口",
"import": "导入",
"importFile": "通过文件导入",
"importUrl": "通过URL导入"
}

View File

@@ -214,7 +214,10 @@ class ClashCore {
final profilePath = await appPath.getProfilePath(id);
final res = await clashInterface.getConfig(profilePath);
if (res.isSuccess) {
return res.data as Map<String, dynamic>;
final data = (res.data ?? {}) as Map<String, dynamic>;
data["rules"] = data["rule"];
data.remove("rule");
return data;
} else {
throw res.message;
}

View File

@@ -6,7 +6,7 @@ import 'dart:isolate';
import 'dart:ui';
import 'package:ffi/ffi.dart';
import 'package:fl_clash/common/constant.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';
@@ -240,18 +240,21 @@ class ClashLibHandler {
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
}
// Map<String, dynamic> getConfig(String path) {
// 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<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);
config["rules"] = config["rule"];
config.remove("rule");
malloc.free(pathChar);
clashFFI.freeCString(configRaw);
return config;
}
Future<String> quickStart(
InitParams initParams,
@@ -289,4 +292,4 @@ ClashLib? get clashLib =>
Platform.isAndroid && !globalState.isService ? ClashLib() : null;
ClashLibHandler? get clashLibHandler =>
Platform.isAndroid ? ClashLibHandler() : null;
Platform.isAndroid && globalState.isService ? ClashLibHandler() : null;

View File

@@ -11,7 +11,6 @@ export 'future.dart';
export 'http.dart';
export 'icons.dart';
export 'iterable.dart';
export 'javascript.dart';
export 'keyboard.dart';
export 'launch.dart';
export 'link.dart';

View File

@@ -1,18 +0,0 @@
import 'package:flutter_js/flutter_js.dart';
class Javascript {
static Javascript? _instance;
Javascript._internal();
JavascriptRuntime get runTime {
return getJavascriptRuntime();
}
factory Javascript() {
_instance ??= Javascript._internal();
return _instance!;
}
}
final js = Javascript();

View File

@@ -1,11 +1,10 @@
import 'dart:async';
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'constant.dart';
class AppPath {
static AppPath? _instance;
Completer<Directory> dataDir = Completer();
@@ -78,17 +77,28 @@ class AppPath {
return join(directory, "$id.yaml");
}
Future<String> getProvidersPath(String id, {String filePath = ""}) async {
Future<String> getProvidersDirPath(String id) async {
final directory = await profilesPath;
final path = join(
return join(
directory,
"providers",
id,
);
if (filePath.isNotEmpty) {
return join(path, filePath);
}
return path;
}
Future<String> getProvidersFilePath(
String id,
String type,
String url,
) async {
final directory = await profilesPath;
return join(
directory,
"providers",
id,
type,
url.toMd5(),
);
}
Future<String> get tempPath async {

View File

@@ -1,6 +1,8 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'print.dart';
extension StringExtension on String {
@@ -45,6 +47,10 @@ extension StringExtension on String {
}
}
bool get isSvg {
return endsWith(".svg");
}
bool get isRegex {
try {
RegExp(this);
@@ -55,6 +61,11 @@ extension StringExtension on String {
}
}
String toMd5() {
final bytes = utf8.encode(this);
return md5.convert(bytes).toString();
}
// bool containsToLower(String target) {
// return toLowerCase().contains(target);
// }

View File

@@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:yaml/yaml.dart';
class Utils {
Color? getDelayColor(int? delay) {
@@ -337,51 +336,51 @@ class Utils {
);
}
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;
}
// 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) {

View File

@@ -774,14 +774,14 @@ class AppController {
clearEffect(String profileId) async {
final profilePath = await appPath.getProfilePath(profileId);
final providersPath = await appPath.getProvidersPath(profileId);
final providersDirPath = await appPath.getProvidersDirPath(profileId);
return await Isolate.run(() async {
final profileFile = File(profilePath);
final isExists = await profileFile.exists();
if (isExists) {
profileFile.delete(recursive: true);
}
final providersFileDir = File(providersPath);
final providersFileDir = File(providersDirPath);
final providersFileIsExists = await providersFileDir.exists();
if (providersFileIsExists) {
providersFileDir.delete(recursive: true);

View File

@@ -499,3 +499,8 @@ enum Language {
yaml,
javaScript,
}
enum ImportOption {
file,
url,
}

View File

@@ -323,7 +323,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Icon configuration",
),
"iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"),
"import": MessageLookupByLibrary.simpleMessage("Import"),
"importFile": MessageLookupByLibrary.simpleMessage("Import from file"),
"importFromURL": MessageLookupByLibrary.simpleMessage("Import from URL"),
"importUrl": MessageLookupByLibrary.simpleMessage("Import from URL"),
"infiniteTime": MessageLookupByLibrary.simpleMessage("Long term effective"),
"init": MessageLookupByLibrary.simpleMessage("Init"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage(

View File

@@ -245,7 +245,10 @@ class MessageLookup extends MessageLookupByLibrary {
"icon": MessageLookupByLibrary.simpleMessage("アイコン"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage("アイコン設定"),
"iconStyle": MessageLookupByLibrary.simpleMessage("アイコンスタイル"),
"import": MessageLookupByLibrary.simpleMessage("インポート"),
"importFile": MessageLookupByLibrary.simpleMessage("ファイルからインポート"),
"importFromURL": MessageLookupByLibrary.simpleMessage("URLからインポート"),
"importUrl": MessageLookupByLibrary.simpleMessage("URLからインポート"),
"infiniteTime": MessageLookupByLibrary.simpleMessage("長期有効"),
"init": MessageLookupByLibrary.simpleMessage("初期化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("正しいホットキーを入力"),

View File

@@ -342,7 +342,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Конфигурация иконки",
),
"iconStyle": MessageLookupByLibrary.simpleMessage("Стиль иконки"),
"import": MessageLookupByLibrary.simpleMessage("Импорт"),
"importFile": MessageLookupByLibrary.simpleMessage("Импорт из файла"),
"importFromURL": MessageLookupByLibrary.simpleMessage("Импорт из URL"),
"importUrl": MessageLookupByLibrary.simpleMessage("Импорт по URL"),
"infiniteTime": MessageLookupByLibrary.simpleMessage(
"Долгосрочное действие",
),

View File

@@ -221,7 +221,10 @@ class MessageLookup extends MessageLookupByLibrary {
"icon": MessageLookupByLibrary.simpleMessage("图片"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"),
"iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"),
"import": MessageLookupByLibrary.simpleMessage("导入"),
"importFile": MessageLookupByLibrary.simpleMessage("通过文件导入"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"importUrl": MessageLookupByLibrary.simpleMessage("通过URL导入"),
"infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"),
"init": MessageLookupByLibrary.simpleMessage("初始化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"),

View File

@@ -3104,6 +3104,31 @@ class AppLocalizations {
args: [],
);
}
/// `Import`
String get import {
return Intl.message('Import', name: 'import', desc: '', args: []);
}
/// `Import from file`
String get importFile {
return Intl.message(
'Import from file',
name: 'importFile',
desc: '',
args: [],
);
}
/// `Import from URL`
String get importUrl {
return Intl.message(
'Import from URL',
name: 'importUrl',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -441,7 +441,7 @@ List<SubRule> _genSubRules(Map<String, dynamic> json) {
class ClashConfigSnippet with _$ClashConfigSnippet {
const factory ClashConfigSnippet({
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule, name: "rule") @Default([]) List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") @Default([]) List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
@Default([])
List<RuleProvider> ruleProvider,

View File

@@ -3216,7 +3216,7 @@ ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
mixin _$ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
@JsonKey(fromJson: _genRule, name: "rule")
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule => throw _privateConstructorUsedError;
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> get ruleProvider => throw _privateConstructorUsedError;
@@ -3241,7 +3241,7 @@ abstract class $ClashConfigSnippetCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule, name: "rule") List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -3299,7 +3299,7 @@ abstract class _$$ClashConfigSnippetImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule, name: "rule") List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -3351,7 +3351,7 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
const _$ClashConfigSnippetImpl(
{@JsonKey(name: "proxy-groups")
final List<ProxyGroup> proxyGroups = const [],
@JsonKey(fromJson: _genRule, name: "rule")
@JsonKey(fromJson: _genRule, name: "rules")
final List<Rule> rule = const [],
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider = const [],
@@ -3376,7 +3376,7 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
final List<Rule> _rule;
@override
@JsonKey(fromJson: _genRule, name: "rule")
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule {
if (_rule is EqualUnmodifiableListView) return _rule;
// ignore: implicit_dynamic_type
@@ -3448,7 +3448,7 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
abstract class _ClashConfigSnippet implements ClashConfigSnippet {
const factory _ClashConfigSnippet(
{@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule, name: "rule") final List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") final List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -3461,7 +3461,7 @@ abstract class _ClashConfigSnippet implements ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups;
@override
@JsonKey(fromJson: _genRule, name: "rule")
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule;
@override
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)

View File

@@ -312,7 +312,7 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule: json['rule'] == null ? const [] : _genRule(json['rule'] as List?),
rule: json['rules'] == null ? const [] : _genRule(json['rules'] as List?),
ruleProvider: json['rule-providers'] == null
? const []
: _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),
@@ -325,7 +325,7 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
_$ClashConfigSnippetImpl instance) =>
<String, dynamic>{
'proxy-groups': instance.proxyGroups,
'rule': instance.rule,
'rules': instance.rule,
'rule-providers': instance.ruleProvider,
'sub-rules': instance.subRules,
};

View File

@@ -111,10 +111,25 @@ class _EditorPageState extends ConsumerState<EditorPage> {
_findController.findMode();
}
_handleRemoteDownload() async {
_handleImport() async {
final option = await globalState.showCommonDialog<ImportOption>(
child: _ImportOptionsDialog(),
);
if (option == null) {
return;
}
if (option == ImportOption.file) {
final file = await picker.pickerFile();
if (file == null) {
return;
}
final res = String.fromCharCodes(file.bytes?.toList() ?? []);
_controller.text = res;
return;
}
final url = await globalState.showCommonDialog(
child: InputDialog(
title: appLocalizations.download,
title: "导入",
value: "",
labelText: appLocalizations.url,
validator: (value) {
@@ -186,7 +201,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
),
if (widget.supportRemoteDownload)
IconButton(
onPressed: _handleRemoteDownload,
onPressed: _handleImport,
icon: Icon(
Icons.arrow_downward,
),
@@ -236,6 +251,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
padding: EdgeInsets.only(
right: 16,
),
autocompleteSymbols: true,
focusNode: _focusNode,
scrollbarBuilder: (context, child, details) {
return CommonScrollBar(
@@ -662,7 +678,45 @@ class _NoInputBorder extends InputBorder {
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection? textDirection,
}) {
// Do not paint.
}) {}
}
class _ImportOptionsDialog extends StatefulWidget {
const _ImportOptionsDialog();
@override
State<_ImportOptionsDialog> createState() => _ImportOptionsDialogState();
}
class _ImportOptionsDialogState extends State<_ImportOptionsDialog> {
_handleOnTab(ImportOption value) {
Navigator.of(context).pop(value);
}
@override
Widget build(BuildContext context) {
return CommonDialog(
title: appLocalizations.import,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
child: Wrap(
children: [
ListItem(
onTap: () {
_handleOnTab(ImportOption.url);
},
title: Text(appLocalizations.importUrl),
),
ListItem(
onTap: () {
_handleOnTab(ImportOption.file);
},
title: Text(appLocalizations.importFile),
)
],
),
);
}
}

View File

@@ -1959,11 +1959,11 @@ class _GenColorSchemeProviderElement
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
}
String _$needSetupHash() => r'1116c73bb2964321de63bdca631a983d31e30d69';
String _$needSetupHash() => r'db01ec73ea3232c99d1c5445c80e31b98785f416';
/// See also [needSetup].
@ProviderFor(needSetup)
final needSetupProvider = AutoDisposeProvider<VM2>.internal(
final needSetupProvider = AutoDisposeProvider<VM3>.internal(
needSetup,
name: r'needSetupProvider',
debugGetCreateSourceHash:
@@ -1974,7 +1974,7 @@ final needSetupProvider = AutoDisposeProvider<VM2>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef NeedSetupRef = AutoDisposeProviderRef<VM2>;
typedef NeedSetupRef = AutoDisposeProviderRef<VM3>;
String _$profileOverrideStateHash() =>
r'fa26570a355ab39e27b1f93d1d2f358717065592';

View File

@@ -590,9 +590,19 @@ ColorScheme genColorScheme(
}
@riverpod
VM2 needSetup(Ref ref) {
VM3 needSetup(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
final content = ref.watch(
scriptStateProvider.select((state) => state.currentScript?.content));
return VM2(a: profileId, b: content);
final overrideDns = ref.watch(overrideDnsProvider);
final dns = overrideDns == true
? ref.watch(patchClashConfigProvider.select(
(state) => state.dns,
))
: null;
return VM3(
a: profileId,
b: content,
c: dns,
);
}

View File

@@ -1,8 +1,6 @@
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';
@@ -15,10 +13,10 @@ import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.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:url_launcher/url_launcher.dart';
import 'package:yaml/yaml.dart';
import 'common/common.dart';
import 'controller.dart';
@@ -193,26 +191,26 @@ 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;
}
}
// 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;
// }
// }
Future<T?> showCommonDialog<T>({
required Widget child,
@@ -315,10 +313,11 @@ class GlobalState {
return {};
}
final profileId = profile.id;
final rawConfig =
await handleEvaluate(await globalState.getProfileMap(profileId));
final configMap = await switch (clashLibHandler != null) {
true => clashLibHandler!.getConfig(profileId),
false => clashCore.getConfig(profileId),
};
final rawConfig = await handleEvaluate(configMap);
final routeAddress =
config.networkProps.routeMode == RouteMode.bypassPrivate
? defaultBypassPrivateRouteAddress
@@ -371,9 +370,15 @@ class GlobalState {
final proxyProviders = rawConfig["proxy-providers"] as Map;
for (final key in proxyProviders.keys) {
final proxyProvider = proxyProviders[key];
if (proxyProvider["path"] != null) {
proxyProvider["path"] = await appPath.getProvidersPath(profile.id,
filePath: proxyProvider["path"]);
if (proxyProvider["type"] != "http") {
continue;
}
if (proxyProvider["url"] != null) {
proxyProvider["path"] = await appPath.getProvidersFilePath(
profile.id,
"proxies",
proxyProvider["url"],
);
}
}
}
@@ -382,9 +387,15 @@ class GlobalState {
final ruleProviders = rawConfig["rule-providers"] as Map;
for (final key in ruleProviders.keys) {
final ruleProvider = ruleProviders[key];
if (ruleProvider["path"] != null) {
ruleProvider["path"] = await appPath.getProvidersPath(profile.id,
filePath: ruleProvider["path"]);
if (ruleProvider["type"] != "http") {
continue;
}
if (ruleProvider["url"] != null) {
ruleProvider["path"] = await appPath.getProvidersFilePath(
profile.id,
"rules",
ruleProvider["url"],
);
}
}
}
@@ -415,11 +426,8 @@ class GlobalState {
}
}
var rules = [];
// if (rawConfig["rule"] != null) {
// rules.addAll(rawConfig["rule"]);
// }
if (rawConfig["rules"] != null) {
rules.addAll(rawConfig["rules"]);
rules = rawConfig["rules"];
}
rawConfig.remove("rules");
@@ -442,9 +450,12 @@ class GlobalState {
if (currentScript == null) {
return config;
}
if (config["proxy-providers"] == null) {
config["proxy-providers"] = {};
}
final configJs = json.encode(config);
final runtime = js.runTime;
final res = await js.runTime.evaluateAsync("""
final runtime = getJavascriptRuntime();
final res = await runtime.evaluateAsync("""
${currentScript.content}
main($configJs)
""");

View File

@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:fl_clash/common/common.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_svg/svg.dart';
class CommonTargetIcon extends StatelessWidget {
final String src;
@@ -33,11 +34,23 @@ class CommonTargetIcon extends StatelessWidget {
},
);
}
return CachedNetworkImage(
imageUrl: src,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
errorWidget: (_, __, ___) => _defaultIcon(),
return FutureBuilder(
future: DefaultCacheManager().getSingleFile(src),
builder: (_, snapshot) {
final data = snapshot.data;
if (data == null) {
return SizedBox();
}
return src.isSvg
? SvgPicture.file(
data,
errorBuilder: (_, __, ___) => _defaultIcon(),
)
: Image.file(
data,
errorBuilder: (_, __, ___) => _defaultIcon(),
);
},
);
}

View File

@@ -161,30 +161,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.9.5"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
characters:
dependency: transitive
description:
@@ -274,7 +250,7 @@ packages:
source: hosted
version: "0.3.4+2"
crypto:
dependency: "direct dev"
dependency: "direct main"
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
@@ -479,7 +455,7 @@ packages:
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
dependency: "direct main"
description:
name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
@@ -524,6 +500,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.6.1"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1
url: "https://pub.dev"
source: hosted
version: "2.1.0"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -886,14 +870,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.0"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_config:
dependency: transitive
description:
@@ -926,6 +902,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
path_provider:
dependency: "direct main"
description:
@@ -1086,7 +1070,7 @@ packages:
source: hosted
version: "2.6.4"
riverpod_lint:
dependency: "direct dev"
dependency: "direct main"
description:
name: riverpod_lint
sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693
@@ -1474,6 +1458,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de"
url: "https://pub.dev"
source: hosted
version: "1.1.18"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
url: "https://pub.dev"
source: hosted
version: "1.1.13"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331"
url: "https://pub.dev"
source: hosted
version: "1.1.17"
vector_math:
dependency: transitive
description:
@@ -1578,7 +1586,7 @@ packages:
source: hosted
version: "6.5.0"
yaml:
dependency: "direct main"
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.85+202506011
version: 0.8.85+202506021
environment:
sdk: '>=3.1.0 <4.0.0'
@@ -42,7 +42,6 @@ dependencies:
archive: ^3.6.1
lpinyin: ^2.0.3
emoji_regex: ^0.0.5
cached_network_image: ^3.4.0
hotkey_manager: ^0.2.3
uni_platform: ^0.1.3
device_info_plus: ^11.3.3
@@ -57,7 +56,11 @@ dependencies:
git:
url: https://github.com/chen08209/flutter_js
ref: master
yaml: ^3.1.3
# yaml: ^3.1.3
flutter_svg: ^2.1.0
flutter_cache_manager: ^3.4.1
riverpod_lint: ^2.6.3
crypto: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
@@ -69,8 +72,6 @@ dev_dependencies:
freezed: ^2.5.1
riverpod_generator: ^2.6.3
custom_lint: ^0.7.0
riverpod_lint: ^2.6.3
crypto: ^3.0.3
flutter:
uses-material-design: true