Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068fe14d89 | ||
|
|
43c397007c | ||
|
|
5e801d5f5e |
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
core/hub.go
23
core/hub.go
@@ -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)
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
@@ -156,5 +156,8 @@
|
|||||||
"goDownload": "前往下载",
|
"goDownload": "前往下载",
|
||||||
"unknown": "未知",
|
"unknown": "未知",
|
||||||
"geoData": "地理数据",
|
"geoData": "地理数据",
|
||||||
"externalResources": "外部资源"
|
"externalResources": "外部资源",
|
||||||
|
"checking": "检测中...",
|
||||||
|
"country": "区域",
|
||||||
|
"ipCheckError": "Ip检测超时"
|
||||||
}
|
}
|
||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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("浅色"),
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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() ??
|
||||||
|
|||||||
@@ -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>{
|
||||||
|
|||||||
@@ -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
70
lib/models/ip.dart
Normal 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}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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';
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
pubspec.lock
32
pubspec.lock
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user