Compare commits

...

3 Commits

Author SHA1 Message Date
chen08209
068fe14d89 Optimize delay test2 2024-06-06 17:13:32 +08:00
chen08209
43c397007c Optimize delay test
Add check ip
2024-06-06 16:35:09 +08:00
chen08209
5e801d5f5e add check ip request 2024-06-06 16:34:31 +08:00
27 changed files with 567 additions and 423 deletions

View File

@@ -412,6 +412,6 @@ func applyConfig(isPatch bool) {
patchConfig(cfg.General) patchConfig(cfg.General)
} else { } else {
executor.ApplyConfig(cfg, true) executor.ApplyConfig(cfg, true)
healthcheck() hcCompatibleProvider(tunnel.Providers())
} }
} }

View File

@@ -181,7 +181,8 @@ func getTraffic() *C.char {
} }
//export asyncTestDelay //export asyncTestDelay
func asyncTestDelay(s *C.char) { func asyncTestDelay(s *C.char, port C.longlong) {
i := int64(port)
go func() { go func() {
paramsString := C.GoString(s) paramsString := C.GoString(s)
var params = &TestDelayParams{} var params = &TestDelayParams{}
@@ -205,26 +206,25 @@ func asyncTestDelay(s *C.char) {
Name: params.ProxyName, Name: params.ProxyName,
} }
message := bridge.Message{
Type: bridge.Delay,
Data: delayData,
}
if proxy == nil { if proxy == nil {
delayData.Value = -1 delayData.Value = -1
bridge.SendMessage(message) data, _ := json.Marshal(delayData)
bridge.SendToPort(i, string(data))
return return
} }
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus) delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
if err != nil || delay == 0 { if err != nil || delay == 0 {
delayData.Value = -1 delayData.Value = -1
bridge.SendMessage(message) data, _ := json.Marshal(delayData)
bridge.SendToPort(i, string(data))
return return
} }
delayData.Value = int32(delay) delayData.Value = int32(delay)
bridge.SendMessage(message) data, _ := json.Marshal(delayData)
bridge.SendToPort(i, string(data))
return
}() }()
} }
@@ -374,11 +374,6 @@ func updateExternalProvider(providerName *C.char, providerType *C.char, port C.l
}() }()
} }
//export healthcheck
func healthcheck() {
hcCompatibleProvider(tunnel.Providers())
}
//export initNativeApiBridge //export initNativeApiBridge
func initNativeApiBridge(api unsafe.Pointer, port C.longlong) { func initNativeApiBridge(api unsafe.Pointer, port C.longlong) {
bridge.InitDartApi(api) bridge.InitDartApi(api)

View File

@@ -156,23 +156,36 @@ class ClashCore {
return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1; return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1;
} }
bool delay(String proxyName) { Future<Delay> getDelay(String proxyName) {
final delayParams = { final delayParams = {
"proxy-name": proxyName, "proxy-name": proxyName,
"timeout": httpTimeoutDuration.inMilliseconds, "timeout": httpTimeoutDuration.inMilliseconds,
}; };
clashFFI.asyncTestDelay(json.encode(delayParams).toNativeUtf8().cast()); final completer = Completer<Delay>();
return true; final receiver = ReceivePort();
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(Delay.fromJson(json.decode(message)));
receiver.close();
}
});
clashFFI.asyncTestDelay(
json.encode(delayParams).toNativeUtf8().cast(),
receiver.sendPort.nativePort,
);
Future.delayed(httpTimeoutDuration + moreDuration, () {
receiver.close();
completer.complete(
Delay(name: proxyName, value: -1),
);
});
return completer.future;
} }
clearEffect(String path) { clearEffect(String path) {
clashFFI.clearEffect(path.toNativeUtf8().cast()); clashFFI.clearEffect(path.toNativeUtf8().cast());
} }
healthcheck() {
clashFFI.healthcheck();
}
VersionInfo getVersionInfo() { VersionInfo getVersionInfo() {
final versionInfoRaw = clashFFI.getVersionInfo(); final versionInfoRaw = clashFFI.getVersionInfo();
final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString()); final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString());

View File

@@ -977,17 +977,20 @@ class ClashFFI {
void asyncTestDelay( void asyncTestDelay(
ffi.Pointer<ffi.Char> s, ffi.Pointer<ffi.Char> s,
int port,
) { ) {
return _asyncTestDelay( return _asyncTestDelay(
s, s,
port,
); );
} }
late final _asyncTestDelayPtr = late final _asyncTestDelayPtr = _lookup<
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>( ffi.NativeFunction<
'asyncTestDelay'); ffi.Void Function(
late final _asyncTestDelay = ffi.Pointer<ffi.Char>, ffi.LongLong)>>('asyncTestDelay');
_asyncTestDelayPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>(); late final _asyncTestDelay = _asyncTestDelayPtr
.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
ffi.Pointer<ffi.Char> getVersionInfo() { ffi.Pointer<ffi.Char> getVersionInfo() {
return _getVersionInfo(); return _getVersionInfo();
@@ -1086,14 +1089,6 @@ class ClashFFI {
late final _updateExternalProvider = _updateExternalProviderPtr.asFunction< late final _updateExternalProvider = _updateExternalProviderPtr.asFunction<
void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)>(); void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)>();
void healthcheck() {
return _healthcheck();
}
late final _healthcheckPtr =
_lookup<ffi.NativeFunction<ffi.Void Function()>>('healthcheck');
late final _healthcheck = _healthcheckPtr.asFunction<void Function()>();
void initNativeApiBridge( void initNativeApiBridge(
ffi.Pointer<ffi.Void> api, ffi.Pointer<ffi.Void> api,
int port, int port,

View File

@@ -24,9 +24,9 @@ class Measure {
double? _bodySmallHeight; double? _bodySmallHeight;
double? _labelSmallHeight; double? _labelSmallHeight;
double? _titleLargeHeight; double? _titleLargeHeight;
double? _titleMediumHeight;
double get bodyMediumHeight {
double get bodyMediumHeight{
_bodyMediumHeight ??= computeTextSize( _bodyMediumHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -36,7 +36,7 @@ class Measure {
return _bodyMediumHeight!; return _bodyMediumHeight!;
} }
double get bodySmallHeight{ double get bodySmallHeight {
_bodySmallHeight ??= computeTextSize( _bodySmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -46,7 +46,7 @@ class Measure {
return _bodySmallHeight!; return _bodySmallHeight!;
} }
double get labelSmallHeight{ double get labelSmallHeight {
_labelSmallHeight ??= computeTextSize( _labelSmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -56,7 +56,7 @@ class Measure {
return _labelSmallHeight!; return _labelSmallHeight!;
} }
double get titleLargeHeight{ double get titleLargeHeight {
_titleLargeHeight ??= computeTextSize( _titleLargeHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -65,4 +65,14 @@ class Measure {
).height; ).height;
return _titleLargeHeight!; return _titleLargeHeight!;
} }
double get titleMediumHeight {
_titleMediumHeight ??= computeTextSize(
Text(
"",
style: context.textTheme.titleMedium,
),
).height;
return _titleMediumHeight!;
}
} }

View File

@@ -3,11 +3,13 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart'; import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ip.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
class Request { class Request {
late final Dio _dio; late final Dio _dio;
int? _port; int? _port;
bool _isStart = false;
Request() { Request() {
_dio = Dio( _dio = Dio(
@@ -26,13 +28,16 @@ class Request {
)); ));
} }
_syncProxy(){ _syncProxy() {
final port = globalState.appController.clashConfig.mixedPort; final port = globalState.appController.clashConfig.mixedPort;
if (_port != port) { final isStart = globalState.appController.appState.isStart;
if (_port != port || isStart != _isStart) {
_port = port; _port = port;
_isStart = isStart;
_dio.httpClientAdapter = IOHttpClientAdapter( _dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () { createHttpClient: () {
final client = HttpClient(); final client = HttpClient();
if(!_isStart) return client;
client.findProxy = (url) { client.findProxy = (url) {
return "PROXY localhost:$_port;DIRECT"; return "PROXY localhost:$_port;DIRECT";
}; };
@@ -45,14 +50,14 @@ class Request {
Future<Response> getFileResponseForUrl(String url) async { Future<Response> getFileResponseForUrl(String url) async {
final response = await _dio final response = await _dio
.get( .get(
url, url,
options: Options( options: Options(
responseType: ResponseType.bytes, responseType: ResponseType.bytes,
), ),
) )
.timeout( .timeout(
httpTimeoutDuration, httpTimeoutDuration,
); );
return response; return response;
} }
@@ -73,6 +78,29 @@ class Request {
if (!hasUpdate) return null; if (!hasUpdate) return null;
return data; return data;
} }
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
"https://ipwho.is/": IpInfo.fromIpwhoIsJson,
"https://api.ip.sb/geoip/": IpInfo.fromIpSbJson,
"https://ipapi.co/json/": IpInfo.fromIpApiCoJson,
"https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson,
};
Future<IpInfo?> checkIp() async {
for (final source in _ipInfoSources.entries) {
try {
final response = await _dio.get<Map<String, dynamic>>(
source.key,
);
if (response.statusCode == 200 && response.data != null) {
return source.value(response.data!);
}
} catch (e) {
continue;
}
}
return null;
}
} }
final request = Request(); final request = Request();

View File

@@ -39,8 +39,6 @@ class AppController {
updateRunTime, updateRunTime,
updateTraffic, updateTraffic,
]; ];
clearShowProxyDelay();
testShowProxyDelay();
} else { } else {
await globalState.stopSystemProxy(); await globalState.stopSystemProxy();
appState.traffics = []; appState.traffics = [];
@@ -263,19 +261,6 @@ class AppController {
autoCheckUpdate(); autoCheckUpdate();
} }
healthcheck() {
if (globalState.healthcheckLock) return;
for (final delay in appState.delayMap.entries) {
setDelay(
Delay(
name: delay.key,
value: 0,
),
);
}
clashCore.healthcheck();
}
setDelay(Delay delay) { setDelay(Delay delay) {
appState.setDelay(delay); appState.setDelay(delay);
} }
@@ -385,22 +370,6 @@ class AppController {
addProfileFormURL(url); addProfileFormURL(url);
} }
clearShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
appState.setDelay(
Delay(name: showProxyDelay, value: null),
);
}
}
testShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
globalState.updateCurrentDelay(showProxyDelay);
}
}
updateViewWidth(double width) { updateViewWidth(double width) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
appState.viewWidth = width; appState.viewWidth = width;

View File

@@ -1,3 +1,4 @@
import 'package:country_flags/country_flags.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
@@ -13,118 +14,134 @@ class NetworkDetection extends StatefulWidget {
} }
class _NetworkDetectionState extends State<NetworkDetection> { class _NetworkDetectionState extends State<NetworkDetection> {
Widget _buildDescription(String? currentProxyName, int? delay) { final ipInfoNotifier = ValueNotifier<IpInfo?>(null);
if (currentProxyName == null) { final timeoutNotifier = ValueNotifier<bool>(false);
return TooltipText(
text: Text( _checkIp(
appLocalizations.noProxyDesc, bool isInit,
style: Theme.of(context).textTheme.titleMedium?.copyWith( ) async {
color: Theme.of(context).colorScheme.secondary, if (!isInit) return;
), ipInfoNotifier.value = null;
overflow: TextOverflow.ellipsis, final ipInfo = await request.checkIp();
), if (ipInfo == null) {
); timeoutNotifier.value = true;
return;
} else {
timeoutNotifier.value = false;
} }
if (delay == 0 || delay == null) { ipInfoNotifier.value = await request.checkIp();
return const AspectRatio( }
aspectRatio: 1,
child: CircularProgressIndicator( _checkIpContainer(Widget child) {
strokeCap: StrokeCap.round, return Selector2<AppState, Config, CheckIpSelectorState>(
), selector: (_, appState, config) {
); return CheckIpSelectorState(
} isInit: appState.isInit,
if (delay > 0) { selectedMap: appState.selectedMap,
return Row( isStart: appState.isStart,
mainAxisAlignment: MainAxisAlignment.start, );
crossAxisAlignment: CrossAxisAlignment.center, },
children: [ builder: (_, state, __) {
TooltipText( _checkIp(
text: Text( state.isInit,
"$delay", );
overflow: TextOverflow.ellipsis, return child;
maxLines: 1, },
style: context.textTheme.titleLarge child: child,
?.copyWith(
color: context.colorScheme.primary,
)
.toSoftBold(),
),
),
const Flexible(
child: SizedBox(
width: 4,
),
),
Flexible(
flex: 0,
child: Text(
'ms',
style: Theme.of(context).textTheme.bodyMedium?.toLight(),
),
),
],
);
}
return Text(
"Timeout",
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.red,
),
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonCard( return _checkIpContainer(
info: Info( ValueListenableBuilder<IpInfo?>(
iconData: Icons.network_check, valueListenable: ipInfoNotifier,
label: appLocalizations.networkDetection, builder: (_, ipInfo, __) {
), return CommonCard(
child: Selector<AppState, NetworkDetectionSelectorState>(
selector: (_, appState) {
return NetworkDetectionSelectorState(
currentProxyName: appState.showProxyName,
delay: appState.getDelay(
appState.showProxyName,
),
);
},
builder: (_, state, __) {
return Container(
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Flexible( Flexible(
flex: 0, flex: 0,
child: TooltipText(
text: Text(
state.currentProxyName ?? appLocalizations.noProxy,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: Theme.of(context)
.textTheme
.titleMedium
?.toSoftBold(),
),
),
),
const SizedBox(
height: 8,
),
Flexible(
child: Container( child: Container(
height: globalState.appController.measure.titleLargeHeight, padding: const EdgeInsets.all(16),
alignment: Alignment.centerLeft, child: Row(
child: FadeBox( children: [
child: _buildDescription( Icon(
state.currentProxyName, Icons.network_check,
state.delay, color: Theme.of(context).colorScheme.primary,
), ),
const SizedBox(
width: 8,
),
Flexible(
flex: 1,
child: FadeBox(
child: ipInfo != null
? CountryFlag.fromCountryCode(
ipInfo.countryCode,
width: 24,
height: 24,
)
: TooltipText(
text: Text(
appLocalizations.checking,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleMedium,
),
),
),
),
],
), ),
), ),
), ),
Container(
height:
globalState.appController.measure.titleLargeHeight + 24,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: FadeBox(
child: ipInfo != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
flex: 1,
child: TooltipText(
text: Text(
ipInfo.ip,
style: context.textTheme.titleLarge
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
)
: ValueListenableBuilder(
valueListenable: timeoutNotifier,
builder: (_, timeout, __) {
if(timeout){
return Text(
appLocalizations.ipCheckError,
style: context.textTheme.bodyMedium
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
return const SizedBox(
child: CircularProgressIndicator(),
);
},
),
),
)
], ],
), ),
); );

View File

@@ -19,7 +19,7 @@ class _StartButtonState extends State<StartButton>
@override @override
void initState() { void initState() {
isStart = context.read<AppState>().runTime != null; isStart = globalState.appController.appState.isStart;
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
value: isStart ? 1 : 0, value: isStart ? 1 : 0,
@@ -48,9 +48,11 @@ class _StartButtonState extends State<StartButton>
} }
} }
updateSystemProxy() async { updateSystemProxy() {
final appController = globalState.appController; WidgetsBinding.instance.addPostFrameCallback((_) async {
await appController.updateSystemProxy(isStart); final appController = globalState.appController;
await appController.updateSystemProxy(isStart);
});
} }
@override @override

View File

@@ -1,3 +1,4 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -57,72 +58,69 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DelayTestButtonContainer( return Selector<AppState, bool>(
child: Selector<AppState, bool>( selector: (_, appState) => appState.currentLabel == 'proxies',
selector: (_, appState) => appState.currentLabel == 'proxies', builder: (_, isCurrent, child) {
builder: (_, isCurrent, child) { if (isCurrent) {
if (isCurrent) { _initActions();
_initActions(); }
} return child!;
return child!; },
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
selector: (_, appState, config, clashConfig) {
final currentGroups = appState.currentGroups;
final groupNames = currentGroups.map((e) => e.name).toList();
return ProxiesSelectorState(
groupNames: groupNames,
);
}, },
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>( shouldRebuild: (prev, next) {
selector: (_, appState, config, clashConfig) { if (prev.groupNames.length != next.groupNames.length) {
final currentGroups = appState.currentGroups; _tabController?.dispose();
final groupNames = currentGroups.map((e) => e.name).toList(); _tabController = null;
return ProxiesSelectorState( }
groupNames: groupNames, return prev != next;
); },
}, builder: (_, state, __) {
shouldRebuild: (prev, next) { _tabController ??= TabController(
if (prev.groupNames.length != next.groupNames.length) { length: state.groupNames.length,
_tabController?.dispose(); vsync: this,
_tabController = null; );
} return Column(
return prev != next; mainAxisAlignment: MainAxisAlignment.start,
}, crossAxisAlignment: CrossAxisAlignment.start,
builder: (_, state, __) { children: [
_tabController ??= TabController( TabBar(
length: state.groupNames.length, controller: _tabController,
vsync: this, padding: const EdgeInsets.symmetric(horizontal: 16),
); dividerColor: Colors.transparent,
return Column( isScrollable: true,
mainAxisAlignment: MainAxisAlignment.start, tabAlignment: TabAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, overlayColor: const WidgetStatePropertyAll(Colors.transparent),
children: [ tabs: [
TabBar( for (final groupName in state.groupNames)
Tab(
text: groupName,
),
],
),
Expanded(
child: TabBarView(
controller: _tabController, controller: _tabController,
padding: const EdgeInsets.symmetric(horizontal: 16), children: [
dividerColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
overlayColor:
const WidgetStatePropertyAll(Colors.transparent),
tabs: [
for (final groupName in state.groupNames) for (final groupName in state.groupNames)
Tab( KeepContainer(
text: groupName, key: ObjectKey(groupName),
child: ProxiesTabView(
groupName: groupName,
),
), ),
], ],
), ),
Expanded( )
child: TabBarView( ],
controller: _tabController, );
children: [ },
for (final groupName in state.groupNames)
KeepContainer(
key: ObjectKey(groupName),
child: ProxiesTabView(
groupName: groupName,
),
),
],
),
)
],
);
},
),
), ),
); );
} }
@@ -196,6 +194,18 @@ class ProxiesTabView extends StatelessWidget {
} }
} }
_delayTest(List<Proxy> proxies) async {
for (final proxy in proxies) {
globalState.appController.setDelay(
Delay(name: proxy.name, value: 0),
);
clashCore.getDelay(proxy.name).then((delay) {
globalState.appController.setDelay(delay);
});
}
await Future.delayed(httpTimeoutDuration + moreDuration);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector2<AppState, Config, ProxiesTabViewSelectorState>( return Selector2<AppState, Config, ProxiesTabViewSelectorState>(
@@ -213,25 +223,32 @@ class ProxiesTabView extends StatelessWidget {
state.group.all, state.group.all,
state.proxiesSortType, state.proxiesSortType,
); );
return Align( return DelayTestButtonContainer(
alignment: Alignment.topCenter, onClick: () async {
child: GridView.builder( await _delayTest(
padding: const EdgeInsets.all(16), state.group.all,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( );
crossAxisCount: _getColumns(state.viewMode), },
mainAxisSpacing: 8, child: Align(
crossAxisSpacing: 8, alignment: Alignment.topCenter,
mainAxisExtent: _getItemHeight(context), child: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _getColumns(state.viewMode),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: _getItemHeight(context),
),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
); );
}, },
@@ -384,10 +401,12 @@ class ProxyCard extends StatelessWidget {
class DelayTestButtonContainer extends StatefulWidget { class DelayTestButtonContainer extends StatefulWidget {
final Widget child; final Widget child;
final Future Function() onClick;
const DelayTestButtonContainer({ const DelayTestButtonContainer({
super.key, super.key,
required this.child, required this.child,
required this.onClick,
}); });
@override @override
@@ -401,12 +420,9 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
late Animation<double> _scale; late Animation<double> _scale;
_healthcheck() async { _healthcheck() async {
if (globalState.healthcheckLock) return;
_controller.forward(); _controller.forward();
globalState.appController.healthcheck(); await widget.onClick();
Future.delayed(httpTimeoutDuration + moreDuration, () { _controller.reverse();
_controller.reverse();
});
} }
@override @override
@@ -415,7 +431,7 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration( duration: const Duration(
milliseconds: 600, milliseconds: 1200,
), ),
); );
_scale = Tween<double>( _scale = Tween<double>(
@@ -441,6 +457,7 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_controller.reverse();
return FloatLayout( return FloatLayout(
floatingWidget: FloatWrapper( floatingWidget: FloatWrapper(
child: AnimatedBuilder( child: AnimatedBuilder(

View File

@@ -156,5 +156,8 @@
"goDownload": "Go to download", "goDownload": "Go to download",
"unknown": "Unknown", "unknown": "Unknown",
"geoData": "GeoData", "geoData": "GeoData",
"externalResources": "External resources" "externalResources": "External resources",
"checking": "Checking...",
"country": "Country",
"ipCheckError": "Ip check timeout"
} }

View File

@@ -156,5 +156,8 @@
"goDownload": "前往下载", "goDownload": "前往下载",
"unknown": "未知", "unknown": "未知",
"geoData": "地理数据", "geoData": "地理数据",
"externalResources": "外部资源" "externalResources": "外部资源",
"checking": "检测中...",
"country": "区域",
"ipCheckError": "Ip检测超时"
} }

View File

@@ -80,6 +80,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Check for updates"), MessageLookupByLibrary.simpleMessage("Check for updates"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage( "checkUpdateError": MessageLookupByLibrary.simpleMessage(
"The current application is already the latest version"), "The current application is already the latest version"),
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
"compatible": "compatible":
MessageLookupByLibrary.simpleMessage("Compatibility mode"), MessageLookupByLibrary.simpleMessage("Compatibility mode"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage( "compatibleDesc": MessageLookupByLibrary.simpleMessage(
@@ -88,6 +89,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"), "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"core": MessageLookupByLibrary.simpleMessage("Core"), "core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"create": MessageLookupByLibrary.simpleMessage("Create"), "create": MessageLookupByLibrary.simpleMessage("Create"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"), "dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
@@ -122,6 +124,8 @@ class MessageLookup extends MessageLookupByLibrary {
"hours": MessageLookupByLibrary.simpleMessage("Hours"), "hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL": "importFromURL":
MessageLookupByLibrary.simpleMessage("Import from URL"), MessageLookupByLibrary.simpleMessage("Import from URL"),
"ipCheckError":
MessageLookupByLibrary.simpleMessage("Ip check timeout"),
"just": MessageLookupByLibrary.simpleMessage("Just"), "just": MessageLookupByLibrary.simpleMessage("Just"),
"language": MessageLookupByLibrary.simpleMessage("Language"), "language": MessageLookupByLibrary.simpleMessage("Language"),
"light": MessageLookupByLibrary.simpleMessage("Light"), "light": MessageLookupByLibrary.simpleMessage("Light"),

View File

@@ -65,6 +65,7 @@ class MessageLookup extends MessageLookupByLibrary {
"cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
"compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"),
"compatibleDesc": "compatibleDesc":
MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"), MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"),
@@ -72,6 +73,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"core": MessageLookupByLibrary.simpleMessage("内核"), "core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"create": MessageLookupByLibrary.simpleMessage("创建"), "create": MessageLookupByLibrary.simpleMessage("创建"),
"dark": MessageLookupByLibrary.simpleMessage("深色"), "dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
@@ -99,6 +101,7 @@ class MessageLookup extends MessageLookupByLibrary {
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"), "hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"ipCheckError": MessageLookupByLibrary.simpleMessage("Ip检测超时"),
"just": MessageLookupByLibrary.simpleMessage("刚刚"), "just": MessageLookupByLibrary.simpleMessage("刚刚"),
"language": MessageLookupByLibrary.simpleMessage("语言"), "language": MessageLookupByLibrary.simpleMessage("语言"),
"light": MessageLookupByLibrary.simpleMessage("浅色"), "light": MessageLookupByLibrary.simpleMessage("浅色"),

View File

@@ -1629,6 +1629,36 @@ class AppLocalizations {
args: [], args: [],
); );
} }
/// `Checking...`
String get checking {
return Intl.message(
'Checking...',
name: 'checking',
desc: '',
args: [],
);
}
/// `Country`
String get country {
return Intl.message(
'Country',
name: 'country',
desc: '',
args: [],
);
}
/// `Ip check timeout`
String get ipCheckError {
return Intl.message(
'Ip check timeout',
name: 'ipCheckError',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> { class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -89,6 +89,8 @@ class AppState with ChangeNotifier {
} }
} }
bool get isStart => _runTime != null;
int? get runTime => _runTime; int? get runTime => _runTime;
set runTime(int? value) { set runTime(int? value) {
@@ -166,7 +168,7 @@ class AppState with ChangeNotifier {
addLog(Log log) { addLog(Log log) {
_logs.add(log); _logs.add(log);
if(_logs.length > 60){ if (_logs.length > 60) {
_logs = _logs.sublist(_logs.length - 60); _logs = _logs.sublist(_logs.length - 60);
} }
notifyListeners(); notifyListeners();

View File

@@ -135,7 +135,7 @@ class _$TunImpl implements _Tun {
const _$TunImpl( const _$TunImpl(
{this.enable = false, {this.enable = false,
this.device = appName, this.device = appName,
this.stack = TunStack.gvisor, this.stack = TunStack.mixed,
@JsonKey(name: "dns-hijack") @JsonKey(name: "dns-hijack")
final List<String> dnsHijack = const ["any:53"]}) final List<String> dnsHijack = const ["any:53"]})
: _dnsHijack = dnsHijack; : _dnsHijack = dnsHijack;

View File

@@ -79,7 +79,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false, enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName, device: json['device'] as String? ?? appName,
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
TunStack.gvisor, TunStack.mixed,
dnsHijack: (json['dns-hijack'] as List<dynamic>?) dnsHijack: (json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toList() ?? .toList() ??

View File

@@ -31,7 +31,7 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
? null ? null
: DAV.fromJson(json['dav'] as Map<String, dynamic>) : DAV.fromJson(json['dav'] as Map<String, dynamic>)
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
..isCompatible = json['isCompatible'] as bool? ?? false ..isCompatible = json['isCompatible'] as bool? ?? true
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true; ..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{

View File

@@ -157,34 +157,30 @@ abstract class _StartButtonSelectorState implements StartButtonSelectorState {
} }
/// @nodoc /// @nodoc
mixin _$UpdateCurrentDelaySelectorState { mixin _$CheckIpSelectorState {
String? get currentProxyName => throw _privateConstructorUsedError;
bool get isCurrent => throw _privateConstructorUsedError;
int? get delay => throw _privateConstructorUsedError;
bool get isInit => throw _privateConstructorUsedError; bool get isInit => throw _privateConstructorUsedError;
bool get isStart => throw _privateConstructorUsedError;
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$UpdateCurrentDelaySelectorStateCopyWith<UpdateCurrentDelaySelectorState> $CheckIpSelectorStateCopyWith<CheckIpSelectorState> get copyWith =>
get copyWith => throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc /// @nodoc
abstract class $UpdateCurrentDelaySelectorStateCopyWith<$Res> { abstract class $CheckIpSelectorStateCopyWith<$Res> {
factory $UpdateCurrentDelaySelectorStateCopyWith( factory $CheckIpSelectorStateCopyWith(CheckIpSelectorState value,
UpdateCurrentDelaySelectorState value, $Res Function(CheckIpSelectorState) then) =
$Res Function(UpdateCurrentDelaySelectorState) then) = _$CheckIpSelectorStateCopyWithImpl<$Res, CheckIpSelectorState>;
_$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
UpdateCurrentDelaySelectorState>;
@useResult @useResult
$Res call( $Res call({bool isInit, bool isStart, Map<String, String> selectedMap});
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, class _$CheckIpSelectorStateCopyWithImpl<$Res,
$Val extends UpdateCurrentDelaySelectorState> $Val extends CheckIpSelectorState>
implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> { implements $CheckIpSelectorStateCopyWith<$Res> {
_$UpdateCurrentDelaySelectorStateCopyWithImpl(this._value, this._then); _$CheckIpSelectorStateCopyWithImpl(this._value, this._then);
// ignore: unused_field // ignore: unused_field
final $Val _value; final $Val _value;
@@ -194,154 +190,136 @@ class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value.selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
) as $Val); ) as $Val);
} }
} }
/// @nodoc /// @nodoc
abstract class _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> abstract class _$$CheckIpSelectorStateImplCopyWith<$Res>
implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> { implements $CheckIpSelectorStateCopyWith<$Res> {
factory _$$UpdateCurrentDelaySelectorStateImplCopyWith( factory _$$CheckIpSelectorStateImplCopyWith(_$CheckIpSelectorStateImpl value,
_$UpdateCurrentDelaySelectorStateImpl value, $Res Function(_$CheckIpSelectorStateImpl) then) =
$Res Function(_$UpdateCurrentDelaySelectorStateImpl) then) = __$$CheckIpSelectorStateImplCopyWithImpl<$Res>;
__$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call( $Res call({bool isInit, bool isStart, Map<String, String> selectedMap});
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res> class __$$CheckIpSelectorStateImplCopyWithImpl<$Res>
extends _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, extends _$CheckIpSelectorStateCopyWithImpl<$Res, _$CheckIpSelectorStateImpl>
_$UpdateCurrentDelaySelectorStateImpl> implements _$$CheckIpSelectorStateImplCopyWith<$Res> {
implements _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> { __$$CheckIpSelectorStateImplCopyWithImpl(_$CheckIpSelectorStateImpl _value,
__$$UpdateCurrentDelaySelectorStateImplCopyWithImpl( $Res Function(_$CheckIpSelectorStateImpl) _then)
_$UpdateCurrentDelaySelectorStateImpl _value,
$Res Function(_$UpdateCurrentDelaySelectorStateImpl) _then)
: super(_value, _then); : super(_value, _then);
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_$UpdateCurrentDelaySelectorStateImpl( return _then(_$CheckIpSelectorStateImpl(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value._selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
)); ));
} }
} }
/// @nodoc /// @nodoc
class _$UpdateCurrentDelaySelectorStateImpl class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState {
implements _UpdateCurrentDelaySelectorState { const _$CheckIpSelectorStateImpl(
const _$UpdateCurrentDelaySelectorStateImpl( {required this.isInit,
{required this.currentProxyName, required this.isStart,
required this.isCurrent, required final Map<String, String> selectedMap})
required this.delay, : _selectedMap = selectedMap;
required this.isInit});
@override
final String? currentProxyName;
@override
final bool isCurrent;
@override
final int? delay;
@override @override
final bool isInit; final bool isInit;
@override
final bool isStart;
final Map<String, String> _selectedMap;
@override
Map<String, String> get selectedMap {
if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_selectedMap);
}
@override @override
String toString() { String toString() {
return 'UpdateCurrentDelaySelectorState(currentProxyName: $currentProxyName, isCurrent: $isCurrent, delay: $delay, isInit: $isInit)'; return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap)';
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$UpdateCurrentDelaySelectorStateImpl && other is _$CheckIpSelectorStateImpl &&
(identical(other.currentProxyName, currentProxyName) || (identical(other.isInit, isInit) || other.isInit == isInit) &&
other.currentProxyName == currentProxyName) && (identical(other.isStart, isStart) || other.isStart == isStart) &&
(identical(other.isCurrent, isCurrent) || const DeepCollectionEquality()
other.isCurrent == isCurrent) && .equals(other._selectedMap, _selectedMap));
(identical(other.delay, delay) || other.delay == delay) &&
(identical(other.isInit, isInit) || other.isInit == isInit));
} }
@override @override
int get hashCode => int get hashCode => Object.hash(runtimeType, isInit, isStart,
Object.hash(runtimeType, currentProxyName, isCurrent, delay, isInit); const DeepCollectionEquality().hash(_selectedMap));
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$UpdateCurrentDelaySelectorStateImplCopyWith< _$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl>
_$UpdateCurrentDelaySelectorStateImpl> get copyWith =>
get copyWith => __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl< __$$CheckIpSelectorStateImplCopyWithImpl<_$CheckIpSelectorStateImpl>(
_$UpdateCurrentDelaySelectorStateImpl>(this, _$identity); this, _$identity);
} }
abstract class _UpdateCurrentDelaySelectorState abstract class _CheckIpSelectorState implements CheckIpSelectorState {
implements UpdateCurrentDelaySelectorState { const factory _CheckIpSelectorState(
const factory _UpdateCurrentDelaySelectorState( {required final bool isInit,
{required final String? currentProxyName, required final bool isStart,
required final bool isCurrent, required final Map<String, String> selectedMap}) =
required final int? delay, _$CheckIpSelectorStateImpl;
required final bool isInit}) = _$UpdateCurrentDelaySelectorStateImpl;
@override
String? get currentProxyName;
@override
bool get isCurrent;
@override
int? get delay;
@override @override
bool get isInit; bool get isInit;
@override @override
bool get isStart;
@override
Map<String, String> get selectedMap;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$UpdateCurrentDelaySelectorStateImplCopyWith< _$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl>
_$UpdateCurrentDelaySelectorStateImpl>
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }

70
lib/models/ip.dart Normal file
View File

@@ -0,0 +1,70 @@
class IpInfo {
final String ip;
final String countryCode;
const IpInfo({
required this.ip,
required this.countryCode,
});
static IpInfo fromIpInfoIoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country": final String country,
} =>
IpInfo(
ip: ip,
countryCode: country,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpApiCoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpSbJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
@override
String toString() {
return 'IpInfo{ip: $ip, countryCode: $countryCode}';
}
}

View File

@@ -12,4 +12,5 @@ export 'package.dart';
export 'ffi.dart'; export 'ffi.dart';
export 'selector.dart'; export 'selector.dart';
export 'navigation.dart'; export 'navigation.dart';
export 'dav.dart'; export 'dav.dart';
export 'ip.dart';

View File

@@ -1,10 +1,7 @@
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'config.dart';
import 'navigation.dart';
import 'profile.dart';
import 'proxy.dart';
part 'generated/selector.freezed.dart'; part 'generated/selector.freezed.dart';
@@ -17,13 +14,12 @@ class StartButtonSelectorState with _$StartButtonSelectorState {
} }
@freezed @freezed
class UpdateCurrentDelaySelectorState with _$UpdateCurrentDelaySelectorState { class CheckIpSelectorState with _$CheckIpSelectorState {
const factory UpdateCurrentDelaySelectorState({ const factory CheckIpSelectorState({
required String? currentProxyName,
required bool isCurrent,
required int? delay,
required bool isInit, required bool isInit,
}) = _UpdateCurrentDelaySelectorState; required bool isStart,
required SelectedMap selectedMap,
}) = _CheckIpSelectorState;
} }
@freezed @freezed
@@ -53,24 +49,23 @@ class ApplicationSelectorState with _$ApplicationSelectorState {
} }
@freezed @freezed
class TrayContainerSelectorState with _$TrayContainerSelectorState{ class TrayContainerSelectorState with _$TrayContainerSelectorState {
const factory TrayContainerSelectorState({ const factory TrayContainerSelectorState({
required Mode mode, required Mode mode,
required bool autoLaunch, required bool autoLaunch,
required bool isRun, required bool isRun,
required String? locale, required String? locale,
})=_TrayContainerSelectorState; }) = _TrayContainerSelectorState;
} }
@freezed @freezed
class UpdateNavigationsSelector with _$UpdateNavigationsSelector{ class UpdateNavigationsSelector with _$UpdateNavigationsSelector {
const factory UpdateNavigationsSelector({ const factory UpdateNavigationsSelector({
required bool openLogs, required bool openLogs,
required bool hasProxies, required bool hasProxies,
}) = _UpdateNavigationsSelector; }) = _UpdateNavigationsSelector;
} }
@freezed @freezed
class HomeSelectorState with _$HomeSelectorState { class HomeSelectorState with _$HomeSelectorState {
const factory HomeSelectorState({ const factory HomeSelectorState({
@@ -89,21 +84,21 @@ class HomeBodySelectorState with _$HomeBodySelectorState {
} }
@freezed @freezed
class ProxiesCardSelectorState with _$ProxiesCardSelectorState{ class ProxiesCardSelectorState with _$ProxiesCardSelectorState {
const factory ProxiesCardSelectorState({ const factory ProxiesCardSelectorState({
required bool isSelected, required bool isSelected,
}) = _ProxiesCardSelectorState; }) = _ProxiesCardSelectorState;
} }
@freezed @freezed
class ProxiesSelectorState with _$ProxiesSelectorState{ class ProxiesSelectorState with _$ProxiesSelectorState {
const factory ProxiesSelectorState({ const factory ProxiesSelectorState({
required List<String> groupNames, required List<String> groupNames,
}) = _ProxiesSelectorState; }) = _ProxiesSelectorState;
} }
@freezed @freezed
class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState{ class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState {
const factory ProxiesTabViewSelectorState({ const factory ProxiesTabViewSelectorState({
required ProxiesSortType proxiesSortType, required ProxiesSortType proxiesSortType,
required num sortNum, required num sortNum,
@@ -125,4 +120,4 @@ class PackageListSelectorState with _$PackageListSelectorState {
required AccessControl accessControl, required AccessControl accessControl,
required bool isAccessControl, required bool isAccessControl,
}) = _PackageListSelectorState; }) = _PackageListSelectorState;
} }

View File

@@ -13,7 +13,6 @@ import 'common/common.dart';
class GlobalState { class GlobalState {
Timer? timer; Timer? timer;
Function? healthcheckLockDebounce;
Timer? groupsUpdateTimer; Timer? groupsUpdateTimer;
Function? updateCurrentDelayDebounce; Function? updateCurrentDelayDebounce;
PageController? pageController; PageController? pageController;
@@ -22,7 +21,6 @@ class GlobalState {
late AppController appController; late AppController appController;
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey(); GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
List<Function> updateFunctionLists = []; List<Function> updateFunctionLists = [];
bool healthcheckLock = false;
startListenUpdate() { startListenUpdate() {
if (timer != null && timer!.isActive == true) return; if (timer != null && timer!.isActive == true) return;
@@ -253,20 +251,6 @@ class GlobalState {
); );
} }
void updateCurrentDelay(
String? proxyName,
) {
updateCurrentDelayDebounce ??= debounce<Function(String?)>((proxyName) {
if (proxyName != null) {
debugPrint("[delay]=====> $proxyName");
clashCore.delay(
proxyName,
);
}
});
updateCurrentDelayDebounce!([proxyName]);
}
Future<T?> safeRun<T>( Future<T?> safeRun<T>(
FutureOr<T> Function() futureFunction, { FutureOr<T> Function() futureFunction, {
String? title, String? title,

View File

@@ -38,16 +38,8 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
@override @override
void onDelay(Delay delay) { void onDelay(Delay delay) {
globalState.healthcheckLock = true;
final appController = globalState.appController; final appController = globalState.appController;
appController.setDelay(delay); appController.setDelay(delay);
globalState.healthcheckLockDebounce ??= debounce<Function()>(
() async {
globalState.healthcheckLock = false;
},
milliseconds: 5000,
);
globalState.healthcheckLockDebounce!();
super.onDelay(delay); super.onDelay(delay);
} }

View File

@@ -193,6 +193,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
country_flags:
dependency: "direct main"
description:
name: country_flags
sha256: dbc4f76e7c801619b2d841023e0327191ba00663f1f1b4770394e9bc6632c444
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@@ -509,6 +517,22 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
jovial_misc:
dependency: transitive
description:
name: jovial_misc
sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.5"
jovial_svg:
dependency: transitive
description:
name: jovial_svg
sha256: "000cafe183bdeba28f76d65bf93fc9b636d6f7eaa7e2a33e80c4345a28866ea8"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.21"
js: js:
dependency: transitive dependency: transitive
description: description:
@@ -741,6 +765,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.9.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:

View File

@@ -1,7 +1,7 @@
name: fl_clash name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none' publish_to: 'none'
version: 0.8.12 version: 0.8.13
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'
@@ -38,6 +38,7 @@ dependencies:
image: ^4.1.7 image: ^4.1.7
webdav_client: ^1.2.2 webdav_client: ^1.2.2
dio: ^5.4.3+1 dio: ^5.4.3+1
country_flags: ^2.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter