Compare commits
1 Commits
v0.8.86-pr
...
v0.8.85-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f9dc02436 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,19 +1,3 @@
|
||||
## v0.8.85
|
||||
|
||||
- Support override script
|
||||
|
||||
- Support proxies search
|
||||
|
||||
- Support svg display
|
||||
|
||||
- Optimize config persistence
|
||||
|
||||
- Add some scenes auto close connections
|
||||
|
||||
- Update core
|
||||
|
||||
- Optimize more details
|
||||
|
||||
## v0.8.84
|
||||
|
||||
- Fix windows service verify issues
|
||||
|
||||
@@ -104,7 +104,7 @@ fun ConnectivityManager.resolveDns(network: Network?): List<String> {
|
||||
fun InetAddress.asSocketAddressText(port: Int): String {
|
||||
return when (this) {
|
||||
is Inet6Address ->
|
||||
"[${numericToTextFormat(this)}]:$port"
|
||||
"[${numericToTextFormat(this.address)}]:$port"
|
||||
|
||||
is Inet4Address ->
|
||||
"${this.hostAddress}:$port"
|
||||
@@ -141,8 +141,7 @@ fun Context.getActionPendingIntent(action: String): PendingIntent {
|
||||
}
|
||||
}
|
||||
|
||||
private fun numericToTextFormat(address: Inet6Address): String {
|
||||
val src = address.address
|
||||
private fun numericToTextFormat(src: ByteArray): String {
|
||||
val sb = StringBuilder(39)
|
||||
for (i in 0 until 8) {
|
||||
sb.append(
|
||||
@@ -155,10 +154,6 @@ private fun numericToTextFormat(address: Inet6Address): String {
|
||||
sb.append(":")
|
||||
}
|
||||
}
|
||||
if (address.scopeId > 0) {
|
||||
sb.append("%")
|
||||
sb.append(address.scopeId)
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ data object VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
|
||||
}
|
||||
|
||||
fun handleStart(options: VpnOptions): Boolean {
|
||||
onUpdateNetwork();
|
||||
if (options.enable != this.options?.enable) {
|
||||
this.flClashService = null
|
||||
}
|
||||
|
||||
@@ -405,6 +405,5 @@
|
||||
"portConflictTip": "Please enter a different port",
|
||||
"import": "Import",
|
||||
"importFile": "Import from file",
|
||||
"importUrl": "Import from URL",
|
||||
"autoSetSystemDns": "Auto set system DNS"
|
||||
"importUrl": "Import from URL"
|
||||
}
|
||||
@@ -406,6 +406,5 @@
|
||||
"portConflictTip": "別のポートを入力してください",
|
||||
"import": "インポート",
|
||||
"importFile": "ファイルからインポート",
|
||||
"importUrl": "URLからインポート",
|
||||
"autoSetSystemDns": "オートセットシステムDNS"
|
||||
"importUrl": "URLからインポート"
|
||||
}
|
||||
@@ -406,6 +406,5 @@
|
||||
"portConflictTip": "Введите другой порт",
|
||||
"import": "Импорт",
|
||||
"importFile": "Импорт из файла",
|
||||
"importUrl": "Импорт по URL",
|
||||
"autoSetSystemDns": "Автоматическая настройка системного DNS"
|
||||
"importUrl": "Импорт по URL"
|
||||
}
|
||||
@@ -406,6 +406,5 @@
|
||||
"portConflictTip": "请输入不同的端口",
|
||||
"import": "导入",
|
||||
"importFile": "通过文件导入",
|
||||
"importUrl": "通过URL导入",
|
||||
"autoSetSystemDns": "自动设置系统DNS"
|
||||
"importUrl": "通过URL导入"
|
||||
}
|
||||
|
||||
@@ -66,11 +66,6 @@ class ClashCore {
|
||||
|
||||
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(
|
||||
@@ -219,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;
|
||||
}
|
||||
|
||||
@@ -249,6 +249,8 @@ class ClashLibHandler {
|
||||
return {};
|
||||
}
|
||||
final config = json.decode(configString);
|
||||
config["rules"] = config["rule"];
|
||||
config.remove("rule");
|
||||
malloc.free(pathChar);
|
||||
clashFFI.freeCString(configRaw);
|
||||
return config;
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
|
||||
|
||||
class System {
|
||||
static System? _instance;
|
||||
List<String>? originDns;
|
||||
|
||||
System._internal();
|
||||
|
||||
@@ -105,100 +104,6 @@ class System {
|
||||
return AuthorizeCode.error;
|
||||
}
|
||||
|
||||
Future<String?> getMacOSDefaultServiceName() async {
|
||||
if (!Platform.isMacOS) {
|
||||
return null;
|
||||
}
|
||||
final result = await Process.run('route', ['-n', 'get', 'default']);
|
||||
final output = result.stdout.toString();
|
||||
final deviceLine = output
|
||||
.split('\n')
|
||||
.firstWhere((s) => s.contains('interface:'), orElse: () => "");
|
||||
final lineSplits = deviceLine.trim().split(' ');
|
||||
if (lineSplits.length != 2) {
|
||||
return null;
|
||||
}
|
||||
final device = lineSplits[1];
|
||||
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: () => "",
|
||||
);
|
||||
if (currentService.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final currentServiceNameLine = currentService.split("\n").firstWhere(
|
||||
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
|
||||
orElse: () => "");
|
||||
final currentServiceNameLineSplits =
|
||||
currentServiceNameLine.trim().split(' ');
|
||||
if (currentServiceNameLineSplits.length < 2) {
|
||||
return null;
|
||||
}
|
||||
return currentServiceNameLineSplits[1];
|
||||
}
|
||||
|
||||
Future<List<String>?> getMacOSOriginDns() async {
|
||||
if (!Platform.isMacOS) {
|
||||
return null;
|
||||
}
|
||||
final deviceServiceName = await getMacOSDefaultServiceName();
|
||||
if (deviceServiceName == null) {
|
||||
return null;
|
||||
}
|
||||
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 = [];
|
||||
} else {
|
||||
originDns = output.split("\n");
|
||||
}
|
||||
return originDns;
|
||||
}
|
||||
|
||||
setMacOSDns(bool restore) async {
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
final serviceName = await getMacOSDefaultServiceName();
|
||||
if (serviceName == null) {
|
||||
return;
|
||||
}
|
||||
List<String>? nextDns;
|
||||
if (restore) {
|
||||
nextDns = originDns;
|
||||
} else {
|
||||
final originDns = await system.getMacOSOriginDns();
|
||||
if (originDns == null) {
|
||||
return;
|
||||
}
|
||||
final needAddDns = "223.5.5.5";
|
||||
if (originDns.contains(needAddDns)) {
|
||||
return;
|
||||
}
|
||||
nextDns = List.from(originDns)..add(needAddDns);
|
||||
}
|
||||
if (nextDns == null) {
|
||||
return;
|
||||
}
|
||||
await Process.run(
|
||||
'networksetup',
|
||||
[
|
||||
'-setdnsservers',
|
||||
serviceName,
|
||||
if (nextDns.isNotEmpty) ...nextDns,
|
||||
if (nextDns.isEmpty) "Empty",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
back() async {
|
||||
await app?.moveTaskToBack();
|
||||
await window?.hide();
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'models/models.dart';
|
||||
import 'views/profiles/override_profile.dart';
|
||||
|
||||
class AppController {
|
||||
bool lastTunEnable = false;
|
||||
int? lastProfileModified;
|
||||
|
||||
final BuildContext context;
|
||||
@@ -262,31 +263,29 @@ class AppController {
|
||||
if (res.isError) {
|
||||
return;
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
lastTunEnable = res.data == true;
|
||||
final message = await clashCore.updateConfig(
|
||||
updateParams.copyWith.tun(
|
||||
enable: realTunEnable,
|
||||
enable: lastTunEnable,
|
||||
),
|
||||
);
|
||||
if (message.isNotEmpty) throw message;
|
||||
}
|
||||
|
||||
Future<Result<bool>> _requestAdmin(bool enableTun) async {
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
if (enableTun != realTunEnable && realTunEnable == false) {
|
||||
if (enableTun != lastTunEnable && lastTunEnable == false) {
|
||||
final code = await system.authorizeCore();
|
||||
switch (code) {
|
||||
case AuthorizeCode.none:
|
||||
return Result.success(enableTun);
|
||||
case AuthorizeCode.success:
|
||||
await restartCore();
|
||||
return Result.error("");
|
||||
case AuthorizeCode.none:
|
||||
break;
|
||||
case AuthorizeCode.error:
|
||||
enableTun = false;
|
||||
break;
|
||||
return Result.success(false);
|
||||
}
|
||||
}
|
||||
_ref.read(realTunEnableProvider.notifier).value = enableTun;
|
||||
return Result.success(enableTun);
|
||||
}
|
||||
|
||||
@@ -305,8 +304,8 @@ class AppController {
|
||||
if (res.isError) {
|
||||
return;
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
|
||||
lastTunEnable = res.data == true;
|
||||
final realPatchConfig = patchConfig.copyWith.tun(enable: lastTunEnable);
|
||||
final params = await globalState.getSetupParams(
|
||||
pathConfig: realPatchConfig,
|
||||
);
|
||||
@@ -444,7 +443,6 @@ class AppController {
|
||||
});
|
||||
try {
|
||||
await savePreferences();
|
||||
await system.setMacOSDns(true);
|
||||
await proxy?.stopProxy();
|
||||
await clashCore.shutdown();
|
||||
await clashService?.destroy();
|
||||
|
||||
@@ -123,9 +123,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"autoRunDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Auto run when the application is opened",
|
||||
),
|
||||
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage(
|
||||
"Auto set system DNS",
|
||||
),
|
||||
"autoUpdate": MessageLookupByLibrary.simpleMessage("Auto update"),
|
||||
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage(
|
||||
"Auto update interval (minutes)",
|
||||
|
||||
@@ -93,7 +93,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"autoLaunchDesc": MessageLookupByLibrary.simpleMessage("システムの自動起動に従う"),
|
||||
"autoRun": MessageLookupByLibrary.simpleMessage("自動実行"),
|
||||
"autoRunDesc": MessageLookupByLibrary.simpleMessage("アプリ起動時に自動実行"),
|
||||
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage("オートセットシステムDNS"),
|
||||
"autoUpdate": MessageLookupByLibrary.simpleMessage("自動更新"),
|
||||
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage("自動更新間隔(分)"),
|
||||
"backup": MessageLookupByLibrary.simpleMessage("バックアップ"),
|
||||
|
||||
@@ -120,9 +120,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"autoRunDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Автоматический запуск при открытии приложения",
|
||||
),
|
||||
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage(
|
||||
"Автоматическая настройка системного DNS",
|
||||
),
|
||||
"autoUpdate": MessageLookupByLibrary.simpleMessage("Автообновление"),
|
||||
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage(
|
||||
"Интервал автообновления (минуты)",
|
||||
|
||||
@@ -87,7 +87,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"autoLaunchDesc": MessageLookupByLibrary.simpleMessage("跟随系统自启动"),
|
||||
"autoRun": MessageLookupByLibrary.simpleMessage("自动运行"),
|
||||
"autoRunDesc": MessageLookupByLibrary.simpleMessage("应用打开时自动运行"),
|
||||
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage("自动设置系统DNS"),
|
||||
"autoUpdate": MessageLookupByLibrary.simpleMessage("自动更新"),
|
||||
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage("自动更新间隔(分钟)"),
|
||||
"backup": MessageLookupByLibrary.simpleMessage("备份"),
|
||||
|
||||
@@ -3129,16 +3129,6 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Auto set system DNS`
|
||||
String get autoSetSystemDns {
|
||||
return Intl.message(
|
||||
'Auto set system DNS',
|
||||
name: 'autoSetSystemDns',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -62,7 +62,6 @@ Future<void> _service(List<String> flags) async {
|
||||
vpn?.addListener(
|
||||
_VpnListenerWithService(
|
||||
onDnsChanged: (String dns) {
|
||||
print("handle dns $dns");
|
||||
clashLibHandler.updateDns(dns);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -41,34 +41,10 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(configStateProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.savePreferencesDebounce();
|
||||
}
|
||||
});
|
||||
ref.listenManual(
|
||||
autoSetSystemDnsStateProvider,
|
||||
(prev, next) async {
|
||||
if (prev == next) {
|
||||
return;
|
||||
}
|
||||
if (next.a == true && next.b == true) {
|
||||
system.setMacOSDns(false);
|
||||
} else {
|
||||
system.setMacOSDns(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
reassemble() {
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() async {
|
||||
await system.setMacOSDns(true);
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
clashCore.stopLog();
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,10 +102,9 @@ class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowRestore() {
|
||||
commonPrint.log("restore");
|
||||
render?.resume();
|
||||
super.onWindowRestore();
|
||||
Future<void> onTaskbarCreated() async {
|
||||
// globalState.appController.updateTray(true);
|
||||
super.onTaskbarCreated();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -32,7 +32,6 @@ class AppState with _$AppState {
|
||||
required FixedList<Traffic> traffics,
|
||||
required Traffic totalTraffic,
|
||||
@Default("") String proxiesQuery,
|
||||
@Default(false) bool realTunEnable,
|
||||
}) = _AppState;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,24 +200,6 @@ class Tun with _$Tun {
|
||||
}
|
||||
}
|
||||
|
||||
extension TunExt on Tun {
|
||||
Tun getRealTun(RouteMode routeMode) {
|
||||
final mRouteAddress = routeMode == RouteMode.bypassPrivate
|
||||
? defaultBypassPrivateRouteAddress
|
||||
: routeAddress;
|
||||
return switch (system.isDesktop) {
|
||||
true => copyWith(
|
||||
autoRoute: true,
|
||||
routeAddress: [],
|
||||
),
|
||||
false => copyWith(
|
||||
autoRoute: mRouteAddress.isEmpty ? true : false,
|
||||
routeAddress: mRouteAddress,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FallbackFilter with _$FallbackFilter {
|
||||
const factory FallbackFilter({
|
||||
|
||||
@@ -152,8 +152,7 @@ class NetworkProps with _$NetworkProps {
|
||||
const factory NetworkProps({
|
||||
@Default(true) bool systemProxy,
|
||||
@Default(defaultBypassDomain) List<String> bypassDomain,
|
||||
@Default(RouteMode.config) RouteMode routeMode,
|
||||
@Default(true) bool autoSetSystemDns,
|
||||
@Default(RouteMode.bypassPrivate) RouteMode routeMode,
|
||||
}) = _NetworkProps;
|
||||
|
||||
factory NetworkProps.fromJson(Map<String, Object?>? json) =>
|
||||
|
||||
@@ -36,7 +36,6 @@ mixin _$AppState {
|
||||
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
|
||||
Traffic get totalTraffic => throw _privateConstructorUsedError;
|
||||
String get proxiesQuery => throw _privateConstructorUsedError;
|
||||
bool get realTunEnable => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -69,8 +68,7 @@ abstract class $AppStateCopyWith<$Res> {
|
||||
FixedList<Log> logs,
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
String proxiesQuery,
|
||||
bool realTunEnable});
|
||||
String proxiesQuery});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -107,7 +105,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
Object? traffics = null,
|
||||
Object? totalTraffic = null,
|
||||
Object? proxiesQuery = null,
|
||||
Object? realTunEnable = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isInit: null == isInit
|
||||
@@ -186,10 +183,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
? _value.proxiesQuery
|
||||
: proxiesQuery // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
realTunEnable: null == realTunEnable
|
||||
? _value.realTunEnable
|
||||
: realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -221,8 +214,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
|
||||
FixedList<Log> logs,
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
String proxiesQuery,
|
||||
bool realTunEnable});
|
||||
String proxiesQuery});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -257,7 +249,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
Object? traffics = null,
|
||||
Object? totalTraffic = null,
|
||||
Object? proxiesQuery = null,
|
||||
Object? realTunEnable = null,
|
||||
}) {
|
||||
return _then(_$AppStateImpl(
|
||||
isInit: null == isInit
|
||||
@@ -336,10 +327,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
? _value.proxiesQuery
|
||||
: proxiesQuery // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
realTunEnable: null == realTunEnable
|
||||
? _value.realTunEnable
|
||||
: realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -366,8 +353,7 @@ class _$AppStateImpl implements _AppState {
|
||||
required this.logs,
|
||||
required this.traffics,
|
||||
required this.totalTraffic,
|
||||
this.proxiesQuery = "",
|
||||
this.realTunEnable = false})
|
||||
this.proxiesQuery = ""})
|
||||
: _packages = packages,
|
||||
_delayMap = delayMap,
|
||||
_groups = groups,
|
||||
@@ -445,13 +431,10 @@ class _$AppStateImpl implements _AppState {
|
||||
@override
|
||||
@JsonKey()
|
||||
final String proxiesQuery;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool realTunEnable;
|
||||
|
||||
@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, proxiesQuery: $proxiesQuery, realTunEnable: $realTunEnable)';
|
||||
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, proxiesQuery: $proxiesQuery)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -487,9 +470,7 @@ class _$AppStateImpl implements _AppState {
|
||||
(identical(other.totalTraffic, totalTraffic) ||
|
||||
other.totalTraffic == totalTraffic) &&
|
||||
(identical(other.proxiesQuery, proxiesQuery) ||
|
||||
other.proxiesQuery == proxiesQuery) &&
|
||||
(identical(other.realTunEnable, realTunEnable) ||
|
||||
other.realTunEnable == realTunEnable));
|
||||
other.proxiesQuery == proxiesQuery));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -513,8 +494,7 @@ class _$AppStateImpl implements _AppState {
|
||||
logs,
|
||||
traffics,
|
||||
totalTraffic,
|
||||
proxiesQuery,
|
||||
realTunEnable
|
||||
proxiesQuery
|
||||
]);
|
||||
|
||||
/// Create a copy of AppState
|
||||
@@ -546,8 +526,7 @@ abstract class _AppState implements AppState {
|
||||
required final FixedList<Log> logs,
|
||||
required final FixedList<Traffic> traffics,
|
||||
required final Traffic totalTraffic,
|
||||
final String proxiesQuery,
|
||||
final bool realTunEnable}) = _$AppStateImpl;
|
||||
final String proxiesQuery}) = _$AppStateImpl;
|
||||
|
||||
@override
|
||||
bool get isInit;
|
||||
@@ -587,8 +566,6 @@ abstract class _AppState implements AppState {
|
||||
Traffic get totalTraffic;
|
||||
@override
|
||||
String get proxiesQuery;
|
||||
@override
|
||||
bool get realTunEnable;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -1325,7 +1325,6 @@ mixin _$NetworkProps {
|
||||
bool get systemProxy => throw _privateConstructorUsedError;
|
||||
List<String> get bypassDomain => throw _privateConstructorUsedError;
|
||||
RouteMode get routeMode => throw _privateConstructorUsedError;
|
||||
bool get autoSetSystemDns => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this NetworkProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -1343,11 +1342,7 @@ abstract class $NetworkPropsCopyWith<$Res> {
|
||||
NetworkProps value, $Res Function(NetworkProps) then) =
|
||||
_$NetworkPropsCopyWithImpl<$Res, NetworkProps>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool systemProxy,
|
||||
List<String> bypassDomain,
|
||||
RouteMode routeMode,
|
||||
bool autoSetSystemDns});
|
||||
$Res call({bool systemProxy, List<String> bypassDomain, RouteMode routeMode});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1368,7 +1363,6 @@ class _$NetworkPropsCopyWithImpl<$Res, $Val extends NetworkProps>
|
||||
Object? systemProxy = null,
|
||||
Object? bypassDomain = null,
|
||||
Object? routeMode = null,
|
||||
Object? autoSetSystemDns = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
systemProxy: null == systemProxy
|
||||
@@ -1383,10 +1377,6 @@ class _$NetworkPropsCopyWithImpl<$Res, $Val extends NetworkProps>
|
||||
? _value.routeMode
|
||||
: routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
autoSetSystemDns: null == autoSetSystemDns
|
||||
? _value.autoSetSystemDns
|
||||
: autoSetSystemDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1399,11 +1389,7 @@ abstract class _$$NetworkPropsImplCopyWith<$Res>
|
||||
__$$NetworkPropsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool systemProxy,
|
||||
List<String> bypassDomain,
|
||||
RouteMode routeMode,
|
||||
bool autoSetSystemDns});
|
||||
$Res call({bool systemProxy, List<String> bypassDomain, RouteMode routeMode});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1422,7 +1408,6 @@ class __$$NetworkPropsImplCopyWithImpl<$Res>
|
||||
Object? systemProxy = null,
|
||||
Object? bypassDomain = null,
|
||||
Object? routeMode = null,
|
||||
Object? autoSetSystemDns = null,
|
||||
}) {
|
||||
return _then(_$NetworkPropsImpl(
|
||||
systemProxy: null == systemProxy
|
||||
@@ -1437,10 +1422,6 @@ class __$$NetworkPropsImplCopyWithImpl<$Res>
|
||||
? _value.routeMode
|
||||
: routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
autoSetSystemDns: null == autoSetSystemDns
|
||||
? _value.autoSetSystemDns
|
||||
: autoSetSystemDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1451,8 +1432,7 @@ class _$NetworkPropsImpl implements _NetworkProps {
|
||||
const _$NetworkPropsImpl(
|
||||
{this.systemProxy = true,
|
||||
final List<String> bypassDomain = defaultBypassDomain,
|
||||
this.routeMode = RouteMode.config,
|
||||
this.autoSetSystemDns = true})
|
||||
this.routeMode = RouteMode.bypassPrivate})
|
||||
: _bypassDomain = bypassDomain;
|
||||
|
||||
factory _$NetworkPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -1473,13 +1453,10 @@ class _$NetworkPropsImpl implements _NetworkProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final RouteMode routeMode;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool autoSetSystemDns;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeMode: $routeMode, autoSetSystemDns: $autoSetSystemDns)';
|
||||
return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeMode: $routeMode)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1492,19 +1469,13 @@ class _$NetworkPropsImpl implements _NetworkProps {
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._bypassDomain, _bypassDomain) &&
|
||||
(identical(other.routeMode, routeMode) ||
|
||||
other.routeMode == routeMode) &&
|
||||
(identical(other.autoSetSystemDns, autoSetSystemDns) ||
|
||||
other.autoSetSystemDns == autoSetSystemDns));
|
||||
other.routeMode == routeMode));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
systemProxy,
|
||||
const DeepCollectionEquality().hash(_bypassDomain),
|
||||
routeMode,
|
||||
autoSetSystemDns);
|
||||
int get hashCode => Object.hash(runtimeType, systemProxy,
|
||||
const DeepCollectionEquality().hash(_bypassDomain), routeMode);
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1526,8 +1497,7 @@ abstract class _NetworkProps implements NetworkProps {
|
||||
const factory _NetworkProps(
|
||||
{final bool systemProxy,
|
||||
final List<String> bypassDomain,
|
||||
final RouteMode routeMode,
|
||||
final bool autoSetSystemDns}) = _$NetworkPropsImpl;
|
||||
final RouteMode routeMode}) = _$NetworkPropsImpl;
|
||||
|
||||
factory _NetworkProps.fromJson(Map<String, dynamic> json) =
|
||||
_$NetworkPropsImpl.fromJson;
|
||||
@@ -1538,8 +1508,6 @@ abstract class _NetworkProps implements NetworkProps {
|
||||
List<String> get bypassDomain;
|
||||
@override
|
||||
RouteMode get routeMode;
|
||||
@override
|
||||
bool get autoSetSystemDns;
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -160,8 +160,7 @@ _$NetworkPropsImpl _$$NetworkPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
.toList() ??
|
||||
defaultBypassDomain,
|
||||
routeMode: $enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
|
||||
RouteMode.config,
|
||||
autoSetSystemDns: json['autoSetSystemDns'] as bool? ?? true,
|
||||
RouteMode.bypassPrivate,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
|
||||
@@ -169,7 +168,6 @@ Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
|
||||
'systemProxy': instance.systemProxy,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
'routeMode': _$RouteModeEnumMap[instance.routeMode]!,
|
||||
'autoSetSystemDns': instance.autoSetSystemDns,
|
||||
};
|
||||
|
||||
const _$RouteModeEnumMap = {
|
||||
|
||||
@@ -8,21 +8,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'generated/app.g.dart';
|
||||
|
||||
@riverpod
|
||||
class RealTunEnable extends _$RealTunEnable with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
bool build() {
|
||||
return globalState.appState.realTunEnable;
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
realTunEnable: value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Logs extends _$Logs with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
|
||||
@@ -285,10 +285,6 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
|
||||
currentId: nextId,
|
||||
);
|
||||
}
|
||||
|
||||
isExits(String label) {
|
||||
return state.scripts.indexWhere((item) => item.label == label) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
|
||||
@@ -70,22 +70,6 @@ final viewHeightProvider = AutoDisposeProvider<double>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ViewHeightRef = AutoDisposeProviderRef<double>;
|
||||
String _$realTunEnableHash() => r'a4e995c86deca4c8307966470e69d93d64a40df6';
|
||||
|
||||
/// See also [RealTunEnable].
|
||||
@ProviderFor(RealTunEnable)
|
||||
final realTunEnableProvider =
|
||||
AutoDisposeNotifierProvider<RealTunEnable, bool>.internal(
|
||||
RealTunEnable.new,
|
||||
name: r'realTunEnableProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$realTunEnableHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$RealTunEnable = AutoDisposeNotifier<bool>;
|
||||
String _$logsHash() => r'56fb8aa9d62a97b026b749d204576a7384084737';
|
||||
|
||||
/// See also [Logs].
|
||||
|
||||
@@ -178,7 +178,7 @@ final proxiesStyleSettingProvider =
|
||||
);
|
||||
|
||||
typedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;
|
||||
String _$scriptStateHash() => r'884581c71fd5afa3c9d34f31625d967cf561cdbe';
|
||||
String _$scriptStateHash() => r'16d669009ffb233d95b2cb206cf771342ebc1cc1';
|
||||
|
||||
/// See also [ScriptState].
|
||||
@ProviderFor(ScriptState)
|
||||
|
||||
@@ -6,22 +6,6 @@ part of '../state.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$configStateHash() => r'1f4ea3cc8f6461ba734e7e0c5d7295bfa4fd5afb';
|
||||
|
||||
/// See also [configState].
|
||||
@ProviderFor(configState)
|
||||
final configStateProvider = AutoDisposeProvider<Config>.internal(
|
||||
configState,
|
||||
name: r'configStateProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$configStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ConfigStateRef = AutoDisposeProviderRef<Config>;
|
||||
String _$currentGroupsStateHash() =>
|
||||
r'6222c006e1970e7435268d32903b9019cf1a4351';
|
||||
|
||||
@@ -94,7 +78,7 @@ final coreStateProvider = AutoDisposeProvider<CoreState>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef CoreStateRef = AutoDisposeProviderRef<CoreState>;
|
||||
String _$updateParamsHash() => r'012df72ab0e769a51c573f4692031506d7b1f1b4';
|
||||
String _$updateParamsHash() => r'79fd7a5a8650fabac3a2ca7ce903c1d9eb363ed2';
|
||||
|
||||
/// See also [updateParams].
|
||||
@ProviderFor(updateParams)
|
||||
@@ -126,7 +110,7 @@ final proxyStateProvider = AutoDisposeProvider<ProxyState>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ProxyStateRef = AutoDisposeProviderRef<ProxyState>;
|
||||
String _$trayStateHash() => r'61c99bbae2cb7ed69dc9ee0f2149510eb6a87df4';
|
||||
String _$trayStateHash() => r'39ff84c50ad9c9cc666fa2538fe13ec0d7236b2e';
|
||||
|
||||
/// See also [trayState].
|
||||
@ProviderFor(trayState)
|
||||
@@ -1975,12 +1959,11 @@ class _GenColorSchemeProviderElement
|
||||
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
|
||||
}
|
||||
|
||||
String _$needSetupHash() => r'3668e8dc9f40a9bea45c94321804eb3afa0e7c51';
|
||||
String _$needSetupHash() => r'db01ec73ea3232c99d1c5445c80e31b98785f416';
|
||||
|
||||
/// See also [needSetup].
|
||||
@ProviderFor(needSetup)
|
||||
final needSetupProvider =
|
||||
AutoDisposeProvider<VM3<String?, String?, Dns?>>.internal(
|
||||
final needSetupProvider = AutoDisposeProvider<VM3>.internal(
|
||||
needSetup,
|
||||
name: r'needSetupProvider',
|
||||
debugGetCreateSourceHash:
|
||||
@@ -1991,26 +1974,7 @@ final needSetupProvider =
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef NeedSetupRef = AutoDisposeProviderRef<VM3<String?, String?, Dns?>>;
|
||||
String _$autoSetSystemDnsStateHash() =>
|
||||
r'2e0976e079100325b1ca797285df48a94c2c066c';
|
||||
|
||||
/// See also [autoSetSystemDnsState].
|
||||
@ProviderFor(autoSetSystemDnsState)
|
||||
final autoSetSystemDnsStateProvider =
|
||||
AutoDisposeProvider<VM2<bool, bool>>.internal(
|
||||
autoSetSystemDnsState,
|
||||
name: r'autoSetSystemDnsStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$autoSetSystemDnsStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef AutoSetSystemDnsStateRef = AutoDisposeProviderRef<VM2<bool, bool>>;
|
||||
typedef NeedSetupRef = AutoDisposeProviderRef<VM3>;
|
||||
String _$profileOverrideStateHash() =>
|
||||
r'fa26570a355ab39e27b1f93d1d2f358717065592';
|
||||
|
||||
|
||||
@@ -12,38 +12,6 @@ import 'config.dart';
|
||||
|
||||
part 'generated/state.g.dart';
|
||||
|
||||
@riverpod
|
||||
Config configState(Ref ref) {
|
||||
final themeProps = ref.watch(themeSettingProvider);
|
||||
final patchClashConfig = ref.watch(patchClashConfigProvider);
|
||||
final appSetting = ref.watch(appSettingProvider);
|
||||
final profiles = ref.watch(profilesProvider);
|
||||
final currentProfileId = ref.watch(currentProfileIdProvider);
|
||||
final overrideDns = ref.watch(overrideDnsProvider);
|
||||
final networkProps = ref.watch(networkSettingProvider);
|
||||
final vpnProps = ref.watch(vpnSettingProvider);
|
||||
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
|
||||
final scriptProps = ref.watch(scriptStateProvider);
|
||||
final hotKeyActions = ref.watch(hotKeyActionsProvider);
|
||||
final dav = ref.watch(appDAVSettingProvider);
|
||||
final windowProps = ref.watch(windowSettingProvider);
|
||||
return Config(
|
||||
dav: dav,
|
||||
windowProps: windowProps,
|
||||
hotKeyActions: hotKeyActions,
|
||||
scriptProps: scriptProps,
|
||||
proxiesStyle: proxiesStyle,
|
||||
vpnProps: vpnProps,
|
||||
networkProps: networkProps,
|
||||
overrideDns: overrideDns,
|
||||
currentProfileId: currentProfileId,
|
||||
profiles: profiles,
|
||||
appSetting: appSetting,
|
||||
themeProps: themeProps,
|
||||
patchClashConfig: patchClashConfig,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
GroupsState currentGroupsState(Ref ref) {
|
||||
final mode =
|
||||
@@ -105,15 +73,10 @@ CoreState coreState(Ref ref) {
|
||||
|
||||
@riverpod
|
||||
UpdateParams updateParams(Ref ref) {
|
||||
final routeMode = ref.watch(
|
||||
networkSettingProvider.select(
|
||||
(state) => state.routeMode,
|
||||
),
|
||||
);
|
||||
return ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => UpdateParams(
|
||||
tun: state.tun.getRealTun(routeMode),
|
||||
tun: state.tun,
|
||||
allowLan: state.allowLan,
|
||||
findProcessMode: state.findProcessMode,
|
||||
mode: state.mode,
|
||||
@@ -158,11 +121,9 @@ TrayState trayState(Ref ref) {
|
||||
final appSetting = ref.watch(
|
||||
appSettingProvider,
|
||||
);
|
||||
final groups = ref
|
||||
.watch(
|
||||
currentGroupsStateProvider,
|
||||
)
|
||||
.value;
|
||||
final groups = ref.watch(
|
||||
groupsProvider,
|
||||
);
|
||||
final brightness = ref.watch(
|
||||
appBrightnessProvider,
|
||||
);
|
||||
@@ -629,7 +590,7 @@ ColorScheme genColorScheme(
|
||||
}
|
||||
|
||||
@riverpod
|
||||
VM3<String?, String?, Dns?> needSetup(Ref ref) {
|
||||
VM3 needSetup(Ref ref) {
|
||||
final profileId = ref.watch(currentProfileIdProvider);
|
||||
final content = ref.watch(
|
||||
scriptStateProvider.select((state) => state.currentScript?.content));
|
||||
@@ -645,18 +606,3 @@ VM3<String?, String?, Dns?> needSetup(Ref ref) {
|
||||
c: dns,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
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,
|
||||
),
|
||||
);
|
||||
return VM2(
|
||||
a: isStart ? realTunEnable : false,
|
||||
b: autoSetSystemDns,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -313,10 +313,18 @@ class GlobalState {
|
||||
return {};
|
||||
}
|
||||
final profileId = profile.id;
|
||||
final configMap = await getProfileConfig(profileId);
|
||||
final configMap = await switch (clashLibHandler != null) {
|
||||
true => clashLibHandler!.getConfig(profileId),
|
||||
false => clashCore.getConfig(profileId),
|
||||
};
|
||||
final rawConfig = await handleEvaluate(configMap);
|
||||
final realPatchConfig = patchConfig.copyWith(
|
||||
tun: patchConfig.tun.getRealTun(config.networkProps.routeMode),
|
||||
final routeAddress =
|
||||
config.networkProps.routeMode == RouteMode.bypassPrivate
|
||||
? defaultBypassPrivateRouteAddress
|
||||
: patchConfig.tun.routeAddress;
|
||||
final realPatchConfig = patchConfig.copyWith.tun(
|
||||
autoRoute: routeAddress.isEmpty ? true : false,
|
||||
routeAddress: routeAddress,
|
||||
);
|
||||
rawConfig["external-controller"] = realPatchConfig.externalController.value;
|
||||
rawConfig["external-ui"] = "";
|
||||
@@ -401,23 +409,21 @@ class GlobalState {
|
||||
for (final host in realPatchConfig.hosts.entries) {
|
||||
rawConfig["hosts"][host.key] = host.value.splitByMultipleSeparators;
|
||||
}
|
||||
if (rawConfig["dns"] == null) {
|
||||
rawConfig["dns"] = {};
|
||||
}
|
||||
final isEnableDns = rawConfig["dns"]["enable"] == true;
|
||||
final overrideDns = globalState.config.overrideDns;
|
||||
if (overrideDns || !isEnableDns) {
|
||||
final dns = switch (!isEnableDns) {
|
||||
true => realPatchConfig.dns.copyWith(
|
||||
nameserver: [...realPatchConfig.dns.nameserver, "system://"]),
|
||||
false => realPatchConfig.dns,
|
||||
};
|
||||
rawConfig["dns"] = dns.toJson();
|
||||
if (overrideDns) {
|
||||
rawConfig["dns"] = realPatchConfig.dns.toJson();
|
||||
rawConfig["dns"]["nameserver-policy"] = {};
|
||||
for (final entry in dns.nameserverPolicy.entries) {
|
||||
for (final entry in realPatchConfig.dns.nameserverPolicy.entries) {
|
||||
rawConfig["dns"]["nameserver-policy"][entry.key] =
|
||||
entry.value.splitByMultipleSeparators;
|
||||
}
|
||||
} else {
|
||||
if (rawConfig["dns"] == null) {
|
||||
rawConfig["dns"] = {};
|
||||
}
|
||||
if (rawConfig["dns"]["enable"] != false) {
|
||||
rawConfig["dns"]["enable"] = true;
|
||||
}
|
||||
}
|
||||
var rules = [];
|
||||
if (rawConfig["rules"] != null) {
|
||||
@@ -437,16 +443,6 @@ class GlobalState {
|
||||
return rawConfig;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getProfileConfig(String profileId) async {
|
||||
final configMap = await switch (clashLibHandler != null) {
|
||||
true => clashLibHandler!.getConfig(profileId),
|
||||
false => clashCore.getConfig(profileId),
|
||||
};
|
||||
configMap["rules"] = configMap["rule"];
|
||||
configMap.remove("rule");
|
||||
return configMap;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> handleEvaluate(
|
||||
Map<String, dynamic> config,
|
||||
) async {
|
||||
|
||||
@@ -47,6 +47,15 @@ class AboutView extends StatelessWidget {
|
||||
_checkUpdate(context);
|
||||
},
|
||||
),
|
||||
// ListItem(
|
||||
// title: Text(appLocalizations.contactMe),
|
||||
// onTap: () {
|
||||
// globalState.showMessage(
|
||||
// title: appLocalizations.contactMe,
|
||||
// message: TextSpan(text: "chen08209@gmail.com"),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
ListItem(
|
||||
title: const Text("Telegram"),
|
||||
onTap: () {
|
||||
|
||||
@@ -155,29 +155,6 @@ class Ipv6Item extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AutoSetSystemDnsItem extends ConsumerWidget {
|
||||
const AutoSetSystemDnsItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final autoSetSystemDns = ref.watch(
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TunStackItem extends ConsumerWidget {
|
||||
const TunStackItem({super.key});
|
||||
|
||||
@@ -372,12 +349,9 @@ final networkItems = [
|
||||
title: appLocalizations.options,
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
if (Platform.isMacOS) const AutoSetSystemDnsItem(),
|
||||
const TunStackItem(),
|
||||
if (!system.isDesktop) ...[
|
||||
const RouteModeItem(),
|
||||
const RouteAddressItem(),
|
||||
]
|
||||
const RouteModeItem(),
|
||||
const RouteAddressItem(),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:defer_pointer/defer_pointer.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
@@ -10,8 +9,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'widgets/start_button.dart';
|
||||
|
||||
typedef _IsEditWidgetBuilder = Widget Function(bool isEdit);
|
||||
|
||||
class DashboardView extends ConsumerStatefulWidget {
|
||||
const DashboardView({super.key});
|
||||
|
||||
@@ -21,8 +18,6 @@ class DashboardView extends ConsumerStatefulWidget {
|
||||
|
||||
class _DashboardViewState extends ConsumerState<DashboardView> with PageMixin {
|
||||
final key = GlobalKey<SuperGridState>();
|
||||
final _isEditNotifier = ValueNotifier<bool>(false);
|
||||
final _addedWidgetsNotifier = ValueNotifier<List<GridItem>>([]);
|
||||
|
||||
@override
|
||||
initState() {
|
||||
@@ -38,282 +33,115 @@ class _DashboardViewState extends ConsumerState<DashboardView> with PageMixin {
|
||||
return super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
_isEditNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget? get floatingActionButton => const StartButton();
|
||||
|
||||
Widget _buildIsEdit(_IsEditWidgetBuilder builder) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _isEditNotifier,
|
||||
builder: (_, isEdit, ___) {
|
||||
return builder(isEdit);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> get actions => [
|
||||
_buildIsEdit((isEdit) {
|
||||
return isEdit
|
||||
? ValueListenableBuilder(
|
||||
valueListenable: _addedWidgetsNotifier,
|
||||
builder: (_, addedChildren, child) {
|
||||
if (addedChildren.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
_showAddWidgetsModal();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add_circle,
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox();
|
||||
}),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: key.currentState!.addedChildrenNotifier,
|
||||
builder: (_, addedChildren, child) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: key.currentState!.isEditNotifier,
|
||||
builder: (_, isEdit, child) {
|
||||
if (!isEdit || addedChildren.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
key.currentState!.showAddModal();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add_circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: _buildIsEdit((isEdit) {
|
||||
return isEdit
|
||||
? Icon(Icons.save)
|
||||
: Icon(
|
||||
Icons.edit,
|
||||
);
|
||||
}),
|
||||
onPressed: _handleUpdateIsEdit,
|
||||
icon: ValueListenableBuilder(
|
||||
valueListenable: key.currentState!.isEditNotifier,
|
||||
builder: (_, isEdit, ___) {
|
||||
return isEdit
|
||||
? SystemBackBlock(
|
||||
child: CommonPopScope(
|
||||
child: Icon(Icons.save),
|
||||
onPop: () {
|
||||
key.currentState!.isEditNotifier.value =
|
||||
!key.currentState!.isEditNotifier.value;
|
||||
return false;
|
||||
},
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
Icons.edit,
|
||||
);
|
||||
},
|
||||
),
|
||||
onPressed: () {
|
||||
key.currentState!.isEditNotifier.value =
|
||||
!key.currentState!.isEditNotifier.value;
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
_showAddWidgetsModal() {
|
||||
showSheet(
|
||||
builder: (_, type) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _addedWidgetsNotifier,
|
||||
builder: (_, value, __) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: _AddDashboardWidgetModal(
|
||||
items: value,
|
||||
onAdd: (gridItem) {
|
||||
key.currentState?.handleAdd(gridItem);
|
||||
},
|
||||
),
|
||||
title: appLocalizations.add,
|
||||
);
|
||||
},
|
||||
_handleSave(List<GridItem> girdItems, WidgetRef ref) {
|
||||
final dashboardWidgets = girdItems
|
||||
.map(
|
||||
(item) => DashboardWidget.getDashboardWidget(item),
|
||||
)
|
||||
.toList();
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(dashboardWidgets: dashboardWidgets),
|
||||
);
|
||||
},
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
_handleUpdateIsEdit() {
|
||||
if (_isEditNotifier.value == true) {
|
||||
_handleSave();
|
||||
}
|
||||
_isEditNotifier.value = !_isEditNotifier.value;
|
||||
}
|
||||
|
||||
_handleSave() {
|
||||
final children = key.currentState?.children;
|
||||
if (children == null) {
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final dashboardWidgets = children
|
||||
.map(
|
||||
(item) => DashboardWidget.getDashboardWidget(item),
|
||||
)
|
||||
.toList();
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(dashboardWidgets: dashboardWidgets),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dashboardState = ref.watch(dashboardStateProvider);
|
||||
final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);
|
||||
final spacing = 16.ap;
|
||||
final children = [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
(item) => item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(item) => item.widget,
|
||||
),
|
||||
];
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_addedWidgetsNotifier.value = DashboardWidget.values
|
||||
.where(
|
||||
(item) =>
|
||||
!children.contains(item.widget) &&
|
||||
item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map((item) => item.widget)
|
||||
.toList();
|
||||
});
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16).copyWith(
|
||||
bottom: 88,
|
||||
),
|
||||
child: _buildIsEdit((isEdit) {
|
||||
return isEdit
|
||||
? SystemBackBlock(
|
||||
child: CommonPopScope(
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
(item) => item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(item) => item.widget,
|
||||
),
|
||||
],
|
||||
onUpdate: () {
|
||||
_handleSave();
|
||||
},
|
||||
),
|
||||
onPop: () {
|
||||
_handleUpdateIsEdit();
|
||||
return false;
|
||||
},
|
||||
),
|
||||
)
|
||||
: Grid(
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
);
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddDashboardWidgetModal extends StatelessWidget {
|
||||
final List<GridItem> items;
|
||||
final Function(GridItem item) onAdd;
|
||||
|
||||
const _AddDashboardWidgetModal({
|
||||
required this.items,
|
||||
required this.onAdd,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DeferredPointerHandler(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
padding: const EdgeInsets.all(16).copyWith(
|
||||
bottom: 88,
|
||||
),
|
||||
child: Grid(
|
||||
crossAxisCount: 8,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
children: items
|
||||
.map(
|
||||
(item) => item.wrap(
|
||||
builder: (child) {
|
||||
return _AddedContainer(
|
||||
onAdd: () {
|
||||
onAdd(item);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: 16.ap,
|
||||
mainAxisSpacing: 16.ap,
|
||||
children: [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
(item) => item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(item) => item.widget,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
],
|
||||
onSave: (girdItems) {
|
||||
_handleSave(girdItems, ref);
|
||||
},
|
||||
addedItemsBuilder: (girdItems) {
|
||||
return DashboardWidget.values
|
||||
.where(
|
||||
(item) =>
|
||||
!girdItems.contains(item.widget) &&
|
||||
item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map((item) => item.widget)
|
||||
.toList();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddedContainer extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onAdd;
|
||||
|
||||
const _AddedContainer({
|
||||
required this.child,
|
||||
required this.onAdd,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_AddedContainer> createState() => _AddedContainerState();
|
||||
}
|
||||
|
||||
class _AddedContainerState extends State<_AddedContainer> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(_AddedContainer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.child != widget.child) {}
|
||||
}
|
||||
|
||||
_handleAdd() async {
|
||||
widget.onAdd();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
ActivateBox(
|
||||
child: widget.child,
|
||||
),
|
||||
Positioned(
|
||||
top: -8,
|
||||
right: -8,
|
||||
child: DeferPointer(
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: IconButton.filled(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(2),
|
||||
onPressed: _handleAdd,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/views/config/network.dart';
|
||||
@@ -25,7 +23,6 @@ class TUNButton extends StatelessWidget {
|
||||
generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
if (Platform.isMacOS) const AutoSetSystemDnsItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -26,7 +27,7 @@ class _OverrideProfileViewState extends State<OverrideProfileView> {
|
||||
_initState(WidgetRef ref) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Future.delayed(Duration(milliseconds: 300), () async {
|
||||
final rawConfig = await globalState.getProfileConfig(widget.profileId);
|
||||
final rawConfig = await clashCore.getConfig(widget.profileId);
|
||||
final snippet = ClashConfigSnippet.fromJson(rawConfig);
|
||||
final overrideData = ref.read(
|
||||
getProfileOverrideDataProvider(widget.profileId),
|
||||
@@ -597,7 +598,7 @@ class RuleContent extends ConsumerWidget {
|
||||
tag: CacheTag.rules,
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
return ReorderableDelayedDragStartListener(
|
||||
return ReorderableDragStartListener(
|
||||
key: ObjectKey(rule),
|
||||
index: index,
|
||||
child: _buildItem(
|
||||
|
||||
@@ -136,9 +136,12 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
|
||||
return appLocalizations.emptyTip(appLocalizations.name);
|
||||
}
|
||||
if (value != script?.label) {
|
||||
final isExits =
|
||||
ref.read(scriptStateProvider.notifier).isExits(value);
|
||||
if (isExits) {
|
||||
final index = ref
|
||||
.read(scriptStateProvider.select((state) => state.scripts))
|
||||
.indexWhere(
|
||||
(item) => item.label == value,
|
||||
);
|
||||
if (index != -1) {
|
||||
return appLocalizations.existsTip(
|
||||
appLocalizations.name,
|
||||
);
|
||||
@@ -153,18 +156,6 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
|
||||
}
|
||||
newScript = newScript.copyWith(label: res);
|
||||
}
|
||||
if (newScript.label != script?.label) {
|
||||
final isExits =
|
||||
ref.read(scriptStateProvider.notifier).isExits(newScript.label);
|
||||
if (isExits) {
|
||||
globalState.showMessage(
|
||||
message: TextSpan(
|
||||
text: appLocalizations.existsTip(appLocalizations.name),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ref.read(scriptStateProvider.notifier).setScript(newScript);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -288,7 +288,7 @@ class ListInputPage extends StatelessWidget {
|
||||
final e = items[index];
|
||||
return _InputItem(
|
||||
key: ValueKey(e),
|
||||
ReorderableDelayedDragStartListener(
|
||||
ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: CommonCard(
|
||||
child: ListItem(
|
||||
@@ -440,7 +440,7 @@ class MapInputPage extends StatelessWidget {
|
||||
final e = items[index];
|
||||
return _InputItem(
|
||||
key: ValueKey(e.key),
|
||||
ReorderableDelayedDragStartListener(
|
||||
ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: CommonCard(
|
||||
child: ListItem(
|
||||
@@ -613,7 +613,7 @@ class _InputItem extends StatelessWidget {
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
margin: EdgeInsets.symmetric(vertical: 8),
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/widgets/activate_box.dart';
|
||||
import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:fl_clash/widgets/grid.dart';
|
||||
import 'package:fl_clash/widgets/sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
|
||||
@@ -17,7 +18,8 @@ class SuperGrid extends StatefulWidget {
|
||||
final double mainAxisSpacing;
|
||||
final double crossAxisSpacing;
|
||||
final int crossAxisCount;
|
||||
final VoidCallback? onUpdate;
|
||||
final void Function(List<GridItem> newChildren)? onSave;
|
||||
final List<GridItem> Function(List<GridItem> newChildren)? addedItemsBuilder;
|
||||
|
||||
const SuperGrid({
|
||||
super.key,
|
||||
@@ -25,7 +27,8 @@ class SuperGrid extends StatefulWidget {
|
||||
this.crossAxisCount = 1,
|
||||
this.mainAxisSpacing = 0,
|
||||
this.crossAxisSpacing = 0,
|
||||
this.onUpdate,
|
||||
this.onSave,
|
||||
this.addedItemsBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -34,7 +37,7 @@ class SuperGrid extends StatefulWidget {
|
||||
|
||||
class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
final ValueNotifier<List<GridItem>> _childrenNotifier = ValueNotifier([]);
|
||||
List<GridItem> children = [];
|
||||
final ValueNotifier<List<GridItem>> addedChildrenNotifier = ValueNotifier([]);
|
||||
|
||||
int get length => _childrenNotifier.value.length;
|
||||
List<int> _tempIndexList = [];
|
||||
@@ -46,6 +49,8 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
List<Offset> _offsets = [];
|
||||
Offset _parentOffset = Offset.zero;
|
||||
EdgeDraggingAutoScroller? _edgeDraggingAutoScroller;
|
||||
final ValueNotifier<bool> isEditNotifier = ValueNotifier(false);
|
||||
|
||||
Map<int, Tween<Offset>> _transformTweenMap = {};
|
||||
|
||||
final ValueNotifier<bool> _animating = ValueNotifier(false);
|
||||
@@ -90,6 +95,35 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_containerSize = context.size!;
|
||||
}
|
||||
|
||||
showAddModal() {
|
||||
if (!isEditNotifier.value) {
|
||||
return;
|
||||
}
|
||||
showSheet(
|
||||
builder: (_, type) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: addedChildrenNotifier,
|
||||
builder: (_, value, __) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: _AddedWidgetsModal(
|
||||
items: value,
|
||||
onAdd: (gridItem) {
|
||||
_childrenNotifier.value = List.from(_childrenNotifier.value)
|
||||
..add(
|
||||
gridItem,
|
||||
);
|
||||
},
|
||||
),
|
||||
title: appLocalizations.add,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
_initState() {
|
||||
_transformController.value = 0;
|
||||
_sizes = List.generate(length, (index) => Size.zero);
|
||||
@@ -105,18 +139,22 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_targetIndex = -1;
|
||||
}
|
||||
|
||||
_handleChildrenNotifierChange() {
|
||||
addedChildrenNotifier.value = widget.addedItemsBuilder != null
|
||||
? widget.addedItemsBuilder!(_childrenNotifier.value)
|
||||
: [];
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_childrenNotifier.addListener(() {
|
||||
children = _childrenNotifier.value;
|
||||
if (widget.onUpdate != null) {
|
||||
widget.onUpdate!();
|
||||
}
|
||||
});
|
||||
|
||||
_childrenNotifier.value = widget.children;
|
||||
|
||||
_childrenNotifier.addListener(_handleChildrenNotifierChange);
|
||||
|
||||
isEditNotifier.addListener(_handleIsEditChange);
|
||||
|
||||
_fakeDragWidgetController = AnimationController.unbounded(
|
||||
vsync: this,
|
||||
duration: commonDuration,
|
||||
@@ -126,7 +164,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 120),
|
||||
);
|
||||
|
||||
_shakeAnimation = Tween<double>(
|
||||
begin: -0.012,
|
||||
end: 0.012,
|
||||
@@ -145,11 +182,15 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_initState();
|
||||
}
|
||||
|
||||
handleAdd(GridItem gridItem) {
|
||||
_childrenNotifier.value = List.from(_childrenNotifier.value)
|
||||
..add(
|
||||
gridItem,
|
||||
);
|
||||
_handleIsEditChange() async {
|
||||
_handleChildrenNotifierChange();
|
||||
if (isEditNotifier.value == false) {
|
||||
if (widget.onSave != null) {
|
||||
await _transformCompleter?.future;
|
||||
await Future.delayed(commonDuration);
|
||||
widget.onSave!(_childrenNotifier.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -264,9 +305,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
_handleDragEnd(DraggableDetails details) async {
|
||||
final children = List<GridItem>.from(_childrenNotifier.value);
|
||||
children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));
|
||||
this.children = children;
|
||||
debouncer.cancel(FunctionTag.handleWill);
|
||||
if (_targetIndex == -1) {
|
||||
return;
|
||||
@@ -296,6 +334,8 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_fakeDragWidgetAnimation = null;
|
||||
_transformTweenMap.clear();
|
||||
_transformAnimationMap.clear();
|
||||
final children = List<GridItem>.from(_childrenNotifier.value);
|
||||
children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));
|
||||
_childrenNotifier.value = children;
|
||||
_initState();
|
||||
}
|
||||
@@ -345,15 +385,17 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_initState();
|
||||
}
|
||||
|
||||
Widget _buildTransform(Widget rawChild, int index) {
|
||||
Widget _wrapTransform(Widget rawChild, int index) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _animating,
|
||||
builder: (_, animating, child) {
|
||||
if (animating && _dragIndexNotifier.value == index) {
|
||||
return _buildSizeBox(
|
||||
Container(),
|
||||
index,
|
||||
);
|
||||
if (animating) {
|
||||
if (_dragIndexNotifier.value == index) {
|
||||
return _sizeBoxWrap(
|
||||
Container(),
|
||||
index,
|
||||
);
|
||||
}
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
@@ -400,7 +442,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
return nextOffset;
|
||||
}
|
||||
|
||||
Widget _buildSizeBox(Widget child, int index) {
|
||||
Widget _sizeBoxWrap(Widget child, int index) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _dragWidgetSizeNotifier,
|
||||
builder: (_, size, child) {
|
||||
@@ -413,7 +455,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInactivate(Widget child) {
|
||||
Widget _ignoreWrap(Widget child) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _animating,
|
||||
builder: (_, animating, child) {
|
||||
@@ -429,7 +471,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShake(Widget child) {
|
||||
Widget _shakeWrap(Widget child) {
|
||||
final random = 0.7 + Random().nextDouble() * 0.3;
|
||||
_shakeController.stop();
|
||||
_shakeController.repeat(reverse: true);
|
||||
@@ -445,7 +487,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDraggable({
|
||||
Widget _draggableWrap({
|
||||
required Widget childWhenDragging,
|
||||
required Widget feedback,
|
||||
required Widget item,
|
||||
@@ -481,7 +523,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
if (dragIndex == index) {
|
||||
return child!;
|
||||
}
|
||||
return _buildShake(
|
||||
return _shakeWrap(
|
||||
_DeletableContainer(
|
||||
onDelete: () {
|
||||
_handleDelete(index);
|
||||
@@ -524,7 +566,16 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
},
|
||||
child: shakeTarget,
|
||||
);
|
||||
return draggableChild;
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: isEditNotifier,
|
||||
builder: (_, isEdit, child) {
|
||||
if (!isEdit) {
|
||||
return item;
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: draggableChild,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _builderItem(int index) {
|
||||
@@ -539,7 +590,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
final childWhenDragging = ActivateBox(
|
||||
child: Opacity(
|
||||
opacity: 0.6,
|
||||
child: _buildSizeBox(
|
||||
child: _sizeBoxWrap(
|
||||
CommonCard(
|
||||
child: child,
|
||||
),
|
||||
@@ -548,7 +599,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
),
|
||||
);
|
||||
final feedback = ActivateBox(
|
||||
child: _buildSizeBox(
|
||||
child: _sizeBoxWrap(
|
||||
CommonCard(
|
||||
child: Material(
|
||||
elevation: 6,
|
||||
@@ -558,8 +609,8 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
index,
|
||||
),
|
||||
);
|
||||
return _buildTransform(
|
||||
_buildDraggable(
|
||||
return _wrapTransform(
|
||||
_draggableWrap(
|
||||
childWhenDragging: childWhenDragging,
|
||||
feedback: feedback,
|
||||
item: child,
|
||||
@@ -580,7 +631,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
if (!animating || _fakeDragWidgetAnimation == null || index == -1) {
|
||||
return Container();
|
||||
}
|
||||
return _buildSizeBox(
|
||||
return _sizeBoxWrap(
|
||||
AnimatedBuilder(
|
||||
animation: _fakeDragWidgetAnimation!,
|
||||
builder: (_, child) {
|
||||
@@ -607,7 +658,10 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_transformController.dispose();
|
||||
_dragIndexNotifier.dispose();
|
||||
_animating.dispose();
|
||||
_childrenNotifier.removeListener(_handleChildrenNotifierChange);
|
||||
_childrenNotifier.dispose();
|
||||
isEditNotifier.removeListener(_handleIsEditChange);
|
||||
isEditNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -616,7 +670,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
return DeferredPointerHandler(
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildInactivate(
|
||||
_ignoreWrap(
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _childrenNotifier,
|
||||
builder: (_, children, __) {
|
||||
@@ -640,6 +694,46 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
}
|
||||
}
|
||||
|
||||
class _AddedWidgetsModal extends StatelessWidget {
|
||||
final List<GridItem> items;
|
||||
final Function(GridItem item) onAdd;
|
||||
|
||||
const _AddedWidgetsModal({
|
||||
required this.items,
|
||||
required this.onAdd,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DeferredPointerHandler(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
child: Grid(
|
||||
crossAxisCount: 8,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
children: items
|
||||
.map(
|
||||
(item) => item.wrap(
|
||||
builder: (child) {
|
||||
return _AddedContainer(
|
||||
onAdd: () {
|
||||
onAdd(item);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeletableContainer extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onDelete;
|
||||
@@ -747,3 +841,68 @@ class _DeletableContainerState extends State<_DeletableContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddedContainer extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onAdd;
|
||||
|
||||
const _AddedContainer({
|
||||
required this.child,
|
||||
required this.onAdd,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_AddedContainer> createState() => _AddedContainerState();
|
||||
}
|
||||
|
||||
class _AddedContainerState extends State<_AddedContainer> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(_AddedContainer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.child != widget.child) {}
|
||||
}
|
||||
|
||||
_handleAdd() async {
|
||||
widget.onAdd();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
ActivateBox(
|
||||
child: widget.child,
|
||||
),
|
||||
Positioned(
|
||||
top: -8,
|
||||
right: -8,
|
||||
child: DeferPointer(
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: IconButton.filled(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(2),
|
||||
onPressed: _handleAdd,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,24 +81,6 @@ static gboolean my_application_local_command_line(GApplication* application, gch
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements GApplication::startup.
|
||||
static void my_application_startup(GApplication* application) {
|
||||
//MyApplication* self = MY_APPLICATION(object);
|
||||
|
||||
// Perform any actions required at application startup.
|
||||
|
||||
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
|
||||
}
|
||||
|
||||
// Implements GApplication::shutdown.
|
||||
static void my_application_shutdown(GApplication* application) {
|
||||
//MyApplication* self = MY_APPLICATION(object);
|
||||
|
||||
// Perform any actions required at application shutdown.
|
||||
|
||||
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
|
||||
}
|
||||
|
||||
// Implements GObject::dispose.
|
||||
static void my_application_dispose(GObject* object) {
|
||||
MyApplication* self = MY_APPLICATION(object);
|
||||
@@ -109,21 +91,12 @@ static void my_application_dispose(GObject* object) {
|
||||
static void my_application_class_init(MyApplicationClass* klass) {
|
||||
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
|
||||
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
|
||||
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
|
||||
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
|
||||
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
|
||||
}
|
||||
|
||||
static void my_application_init(MyApplication* self) {}
|
||||
|
||||
MyApplication* my_application_new() {
|
||||
// Set the program name to the application ID, which helps various systems
|
||||
// like GTK and desktop environments map this running application to its
|
||||
// corresponding .desktop file. This ensures better integration by allowing
|
||||
// the application to be recognized beyond its binary name.
|
||||
g_set_prgname(APPLICATION_ID);
|
||||
|
||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||
"application-id", APPLICATION_ID,
|
||||
nullptr));
|
||||
}
|
||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||
"application-id", APPLICATION_ID,
|
||||
nullptr)); }
|
||||
|
||||
@@ -40,7 +40,7 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- window_ext (0.0.1):
|
||||
- FlutterMacOS
|
||||
- window_manager (0.5.0):
|
||||
- window_manager (0.2.0):
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -128,7 +128,7 @@ SPEC CHECKSUMS:
|
||||
tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166
|
||||
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
|
||||
window_ext: 4afef727fe428b30c68ce800ba92e890fd329f63
|
||||
window_manager: b729e31d38fb04905235df9ea896128991cad99e
|
||||
window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c
|
||||
|
||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||
|
||||
|
||||
@@ -1070,7 +1070,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.6.4"
|
||||
riverpod_lint:
|
||||
dependency: "direct dev"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: riverpod_lint
|
||||
sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693
|
||||
@@ -1565,10 +1565,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: window_manager
|
||||
sha256: "51d50168ab267d344b975b15390426b1243600d436770d3f13de67e55b05ec16"
|
||||
sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.4.3"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -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.86+202506141
|
||||
version: 0.8.85+202506021
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -14,7 +14,7 @@ dependencies:
|
||||
path_provider: ^2.1.0
|
||||
path: ^1.9.0
|
||||
shared_preferences: ^2.5.3
|
||||
window_manager: ^0.5.0
|
||||
window_manager: ^0.4.3
|
||||
dynamic_color: ^1.7.0
|
||||
proxy:
|
||||
path: plugins/proxy
|
||||
@@ -59,6 +59,7 @@ dependencies:
|
||||
# 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:
|
||||
@@ -70,7 +71,6 @@ dev_dependencies:
|
||||
args: ^2.4.2
|
||||
freezed: ^2.5.1
|
||||
riverpod_generator: ^2.6.3
|
||||
riverpod_lint: ^2.6.3
|
||||
custom_lint: ^0.7.0
|
||||
|
||||
flutter:
|
||||
|
||||
Reference in New Issue
Block a user