Compare commits
1 Commits
v0.8.92-pr
...
v0.8.92-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
755c66e5b0 |
@@ -1,7 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||
<option name="additionalArgs" value="--dart-define-from-file env.json" />
|
||||
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
10
Makefile
Normal file
10
Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
android_arm64:
|
||||
dart ./setup.dart android --arch arm64
|
||||
macos_arm64:
|
||||
dart ./setup.dart macos --arch arm64
|
||||
android_app:
|
||||
dart ./setup.dart android
|
||||
android_arm64_core:
|
||||
dart ./setup.dart android --arch arm64 --out core
|
||||
macos_arm64_core:
|
||||
dart ./setup.dart macos --arch arm64 --out core
|
||||
@@ -475,6 +475,5 @@
|
||||
"restoreFromFileDesc": "Restore data via file",
|
||||
"restoreOnlyConfig": "Restore configuration files only",
|
||||
"restoreAllData": "Restore all data",
|
||||
"addProfile": "Add Profile",
|
||||
"delayTest": "Delay Test"
|
||||
"addProfile": "Add Profile"
|
||||
}
|
||||
@@ -476,6 +476,5 @@
|
||||
"restoreFromFileDesc": "ファイルを介してデータを復元する",
|
||||
"restoreOnlyConfig": "設定ファイルのみを復元する",
|
||||
"restoreAllData": "すべてのデータを復元する",
|
||||
"addProfile": "プロファイルを追加",
|
||||
"delayTest": "遅延テスト"
|
||||
"addProfile": "プロファイルを追加"
|
||||
}
|
||||
@@ -484,6 +484,5 @@
|
||||
"restoreFromFileDesc": "Восстановить данные из файла",
|
||||
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
|
||||
"restoreAllData": "Восстановить все данные",
|
||||
"addProfile": "Добавить профиль",
|
||||
"delayTest": "Тест задержки"
|
||||
"addProfile": "Добавить профиль"
|
||||
}
|
||||
@@ -476,6 +476,5 @@
|
||||
"restoreFromFileDesc": "通过文件恢复数据",
|
||||
"restoreOnlyConfig": "仅恢复配置文件",
|
||||
"restoreAllData": "恢复所有数据",
|
||||
"addProfile": "添加配置",
|
||||
"delayTest": "延迟测试"
|
||||
"addProfile": "添加配置"
|
||||
}
|
||||
|
||||
Submodule core/Clash.Meta updated: c27f82fbdf...4d4492c38c
40
core/hub.go
40
core/hub.go
@@ -99,47 +99,31 @@ func handleValidateConfig(path string) string {
|
||||
func handleGetProxies() ProxiesData {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
|
||||
nameList := config.GetProxyNameList()
|
||||
|
||||
var all []string
|
||||
all = append(all, config.ProxyList...)
|
||||
proxies := make(map[string]constant.Proxy)
|
||||
|
||||
for name, proxy := range tunnel.Proxies() {
|
||||
proxies[name] = proxy
|
||||
}
|
||||
for _, p := range tunnel.Providers() {
|
||||
for _, proxy := range p.Proxies() {
|
||||
proxies[proxy.Name()] = proxy
|
||||
name := proxy.Name()
|
||||
proxies[name] = proxy
|
||||
}
|
||||
}
|
||||
|
||||
hasGlobal := false
|
||||
allNames := make([]string, 0, len(nameList)+1)
|
||||
|
||||
for _, name := range nameList {
|
||||
if name == "GLOBAL" {
|
||||
hasGlobal = true
|
||||
}
|
||||
|
||||
p, ok := proxies[name]
|
||||
if !ok || p == nil {
|
||||
continue
|
||||
}
|
||||
switch p.Type() {
|
||||
case constant.Selector, constant.URLTest, constant.Fallback, constant.Relay, constant.LoadBalance:
|
||||
allNames = append(allNames, name)
|
||||
default:
|
||||
}
|
||||
if !slices.Contains(all, "GLOBAL") {
|
||||
all = append([]string{"GLOBAL"}, all...)
|
||||
}
|
||||
|
||||
if !hasGlobal {
|
||||
if p, ok := proxies["GLOBAL"]; ok && p != nil {
|
||||
allNames = append([]string{"GLOBAL"}, allNames...)
|
||||
}
|
||||
types := []constant.AdapterType{
|
||||
constant.Selector, constant.URLTest, constant.Fallback, constant.Relay, constant.LoadBalance,
|
||||
}
|
||||
|
||||
nextAll := slices.DeleteFunc(all, func(name string) bool {
|
||||
return !slices.Contains(types, proxies[name].Type())
|
||||
})
|
||||
return ProxiesData{
|
||||
All: allNames,
|
||||
All: nextAll,
|
||||
Proxies: proxies,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ const helperPort = 47890;
|
||||
const maxTextScale = 1.4;
|
||||
const minTextScale = 0.8;
|
||||
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16.mAp,
|
||||
horizontal: 16.mAp,
|
||||
vertical: 16.ap,
|
||||
horizontal: 16.ap,
|
||||
);
|
||||
final listHeaderPadding = EdgeInsets.only(
|
||||
left: 16.mAp,
|
||||
right: 8.mAp,
|
||||
top: 24.mAp,
|
||||
bottom: 8.mAp,
|
||||
left: 16.ap,
|
||||
right: 8.ap,
|
||||
top: 24.ap,
|
||||
bottom: 8.ap,
|
||||
);
|
||||
|
||||
const watchExecution = true;
|
||||
@@ -102,8 +102,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
|
||||
const defaultPrimaryColor = 0XFFD8C0C3;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
final space = 14.mAp;
|
||||
return max(lines * (80.ap + space) - space, 0);
|
||||
return max(lines * 80 + (lines - 1) * 16, 0).ap;
|
||||
}
|
||||
|
||||
const maxLength = 1000;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -22,10 +20,6 @@ extension NumExt on num {
|
||||
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
||||
}
|
||||
|
||||
double get mAp {
|
||||
return this * min((1 + (globalState.theme.textScaleFactor - 1) * 0.5), 1);
|
||||
}
|
||||
|
||||
TrafficShow get traffic {
|
||||
final units = TrafficUnit.values;
|
||||
var size = toDouble();
|
||||
@@ -57,7 +51,7 @@ extension NumExt on num {
|
||||
|
||||
extension DoubleExt on double {
|
||||
bool moreOrEqual(double value) {
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 1;
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ class Tray {
|
||||
return system.isWindows ? 'ico' : 'png';
|
||||
}
|
||||
|
||||
Future<void> destroy() async {
|
||||
await trayManager.destroy();
|
||||
}
|
||||
|
||||
String getTryIcon({required bool isStart, required bool tunEnable}) {
|
||||
if (system.isMacOS || !isStart) {
|
||||
return 'assets/images/icon/status_1.$trayIconSuffix';
|
||||
|
||||
@@ -83,7 +83,6 @@ class Window {
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
await windowManager.close();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
@@ -138,11 +139,6 @@ extension InitControllerExt on AppController {
|
||||
}
|
||||
|
||||
Future<void> _initStatus() async {
|
||||
if (!globalState.needInitStatus) {
|
||||
commonPrint.log('init status cancel');
|
||||
return;
|
||||
}
|
||||
commonPrint.log('init status');
|
||||
if (system.isAndroid) {
|
||||
await globalState.updateStartTime();
|
||||
}
|
||||
@@ -555,18 +551,18 @@ extension SetupControllerExt on AppController {
|
||||
|
||||
Future<void> updateStatus(bool isStart, {bool isInit = false}) async {
|
||||
if (isStart) {
|
||||
final res = await tryStartCore(true);
|
||||
if (res) {
|
||||
return;
|
||||
}
|
||||
if (!isInit) {
|
||||
final res = await tryStartCore(true);
|
||||
if (res) {
|
||||
return;
|
||||
}
|
||||
if (!_ref.read(initProvider)) {
|
||||
return;
|
||||
}
|
||||
await globalState.handleStart([updateRunTime, updateTraffic]);
|
||||
applyProfileDebounce(force: true, silence: true);
|
||||
} else {
|
||||
globalState.needInitStatus = false;
|
||||
_ref.read(runTimeProvider.notifier).value = 0;
|
||||
await applyProfile(
|
||||
force: true,
|
||||
preloadInvoke: () async {
|
||||
@@ -614,18 +610,6 @@ extension SetupControllerExt on AppController {
|
||||
_ref.read(checkIpNumProvider.notifier).add();
|
||||
}
|
||||
|
||||
void tryCheckIp() {
|
||||
final isTimeout = _ref.read(
|
||||
networkDetectionProvider.select(
|
||||
(state) => state.ipInfo == null && state.isLoading == false,
|
||||
),
|
||||
);
|
||||
if (!isTimeout) {
|
||||
return;
|
||||
}
|
||||
_ref.read(checkIpNumProvider.notifier).add();
|
||||
}
|
||||
|
||||
void applyProfileDebounce({bool silence = false, bool force = false}) {
|
||||
debouncer.call(FunctionTag.applyProfile, (silence, force) {
|
||||
applyProfile(silence: silence, force: force);
|
||||
@@ -653,14 +637,9 @@ extension SetupControllerExt on AppController {
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
}) async {
|
||||
if (!force && !await needSetup()) {
|
||||
return;
|
||||
}
|
||||
await loadingRun(
|
||||
() async {
|
||||
await _setupConfig(preloadInvoke);
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
await _applyProfile(force, preloadInvoke);
|
||||
},
|
||||
silence: true,
|
||||
tag: !silence ? LoadingTag.proxies : null,
|
||||
@@ -730,8 +709,13 @@ extension SetupControllerExt on AppController {
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> _setupConfig([VoidCallback? preloadInvoke]) async {
|
||||
commonPrint.log('setup ===>');
|
||||
Future<void> _setupConfig([
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
]) async {
|
||||
if (!force && !await needSetup()) {
|
||||
return;
|
||||
}
|
||||
var profile = _ref.read(currentProfileProvider);
|
||||
final nextProfile = await profile?.checkAndUpdateAndCopy();
|
||||
if (nextProfile != null) {
|
||||
@@ -749,6 +733,9 @@ extension SetupControllerExt on AppController {
|
||||
globalState.lastSetupState = setupState;
|
||||
if (system.isAndroid) {
|
||||
globalState.lastVpnState = _ref.read(vpnStateProvider);
|
||||
}
|
||||
|
||||
if (system.isAndroid) {
|
||||
preferences.saveShareState(this.sharedState);
|
||||
}
|
||||
final config = await getProfile(
|
||||
@@ -768,6 +755,15 @@ extension SetupControllerExt on AppController {
|
||||
}
|
||||
addCheckIp();
|
||||
}
|
||||
|
||||
Future _applyProfile([
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
]) async {
|
||||
await _setupConfig(force, preloadInvoke);
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
}
|
||||
}
|
||||
|
||||
extension CoreControllerExt on AppController {
|
||||
@@ -799,6 +795,9 @@ extension CoreControllerExt on AppController {
|
||||
}
|
||||
|
||||
Future<Result<bool>> _requestAdmin(bool enableTun) async {
|
||||
if (system.isWindows && kDebugMode) {
|
||||
return Result.success(false);
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
if (enableTun != realTunEnable && realTunEnable == false) {
|
||||
final code = await system.authorizeCore();
|
||||
@@ -818,8 +817,8 @@ extension CoreControllerExt on AppController {
|
||||
}
|
||||
|
||||
Future<void> restartCore([bool start = false]) async {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
await coreController.shutdown(true);
|
||||
globalState.isUserDisconnected = true;
|
||||
await coreController.shutdown();
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
if (start || _ref.read(isStartProvider)) {
|
||||
@@ -833,7 +832,7 @@ extension CoreControllerExt on AppController {
|
||||
if (coreController.isCompleted) {
|
||||
return false;
|
||||
}
|
||||
await restartCore(start);
|
||||
await restartCore();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -859,12 +858,11 @@ extension SystemControllerExt on AppController {
|
||||
system.exit();
|
||||
});
|
||||
try {
|
||||
await Future.wait([
|
||||
if (needSave) preferences.saveConfig(config),
|
||||
if (macOS != null) macOS!.updateDns(true),
|
||||
if (proxy != null) proxy!.stopProxy(),
|
||||
if (tray != null) tray!.destroy(),
|
||||
]);
|
||||
if (needSave) {
|
||||
await preferences.saveConfig(config);
|
||||
}
|
||||
await proxy?.stopProxy();
|
||||
await macOS?.updateDns(true);
|
||||
await coreController.destroy();
|
||||
commonPrint.log('exit');
|
||||
} finally {
|
||||
@@ -1180,8 +1178,8 @@ extension CommonControllerExt on AppController {
|
||||
}
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e, s) {
|
||||
commonPrint.log('$title ===> $e, $s', logLevel: LogLevel.warning);
|
||||
} catch (e) {
|
||||
commonPrint.log('$title===> $e', logLevel: LogLevel.warning);
|
||||
if (silence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
|
||||
@@ -65,8 +65,8 @@ class CoreController {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> shutdown(bool isUser) async {
|
||||
await _interface.shutdown(isUser);
|
||||
Future<void> shutdown() async {
|
||||
await _interface.shutdown();
|
||||
}
|
||||
|
||||
FutureOr<bool> get isInit => _interface.isInit;
|
||||
@@ -201,10 +201,9 @@ class CoreController {
|
||||
final profilePath = await appPath.getProfilePath(id.toString());
|
||||
final res = await _interface.getConfig(profilePath);
|
||||
if (res.isSuccess) {
|
||||
final data = Map<String, dynamic>.from(res.data);
|
||||
data['rules'] = data['rule'];
|
||||
data.remove('rule');
|
||||
return data;
|
||||
res.data['rules'] = res.data['rule'];
|
||||
res.data.remove('rule');
|
||||
return res.data;
|
||||
} else {
|
||||
throw res.message;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ mixin CoreInterface {
|
||||
|
||||
Future<String> preload();
|
||||
|
||||
Future<bool> shutdown(bool isUser);
|
||||
Future<bool> shutdown();
|
||||
|
||||
Future<bool> get isInit;
|
||||
|
||||
@@ -86,9 +86,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
try {
|
||||
if (!completer.isCompleted) {
|
||||
return null;
|
||||
}
|
||||
await completer.future.timeout(const Duration(seconds: 10));
|
||||
} catch (e) {
|
||||
commonPrint.log(
|
||||
'Invoke pre ${method.name} timeout $e',
|
||||
@@ -100,9 +98,9 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
|
||||
}
|
||||
|
||||
return await utils.handleWatch(
|
||||
return utils.handleWatch(
|
||||
function: () async {
|
||||
return await invoke<T>(method: method, data: data, timeout: timeout);
|
||||
return await invoke(method: method, data: data, timeout: timeout);
|
||||
},
|
||||
onWatch: (data, elapsedMilliseconds) {
|
||||
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
|
||||
@@ -133,7 +131,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> shutdown(bool isUser);
|
||||
Future<bool> shutdown();
|
||||
|
||||
@override
|
||||
Future<bool> get isInit async {
|
||||
@@ -165,8 +163,8 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
final res = await _invoke(method: ActionMethod.getConfig, data: path);
|
||||
return res ?? Result.success({});
|
||||
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
|
||||
Result.success({});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -37,12 +37,10 @@ class CoreLib extends CoreHandlerInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> shutdown(_) async {
|
||||
if (!_connectedCompleter.isCompleted) {
|
||||
return false;
|
||||
}
|
||||
Future<bool> shutdown() async {
|
||||
await service?.shutdown();
|
||||
_connectedCompleter = Completer();
|
||||
return service?.shutdown() ?? true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -16,8 +16,6 @@ class CoreService extends CoreHandlerInterface {
|
||||
|
||||
Completer<Socket> _socketCompleter = Completer();
|
||||
|
||||
Completer<bool> _shutdownCompleter = Completer();
|
||||
|
||||
final Map<String, Completer> _callbackCompleterMap = {};
|
||||
|
||||
Process? _process;
|
||||
@@ -37,9 +35,6 @@ class CoreService extends CoreHandlerInterface {
|
||||
if (result.id?.isEmpty == true) {
|
||||
coreEventManager.sendEvent(CoreEvent.fromJson(result.data));
|
||||
}
|
||||
if (completer?.isCompleted == true) {
|
||||
return;
|
||||
}
|
||||
completer?.complete(data);
|
||||
}
|
||||
|
||||
@@ -81,9 +76,6 @@ class CoreService extends CoreHandlerInterface {
|
||||
})
|
||||
.onDone(() {
|
||||
_handleInvokeCrashEvent();
|
||||
if (!_shutdownCompleter.isCompleted) {
|
||||
_shutdownCompleter.complete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,7 +87,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
|
||||
Future<void> start() async {
|
||||
if (_process != null) {
|
||||
await shutdown(false);
|
||||
await shutdown();
|
||||
}
|
||||
final serverSocket = await _serverCompleter.future;
|
||||
final arg = system.isWindows
|
||||
@@ -121,7 +113,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
@override
|
||||
destroy() async {
|
||||
final server = await _serverCompleter.future;
|
||||
await shutdown(false);
|
||||
await shutdown();
|
||||
await server.close();
|
||||
await _deleteSocketFile();
|
||||
return true;
|
||||
@@ -143,16 +135,12 @@ class CoreService extends CoreHandlerInterface {
|
||||
if (_socketCompleter.isCompleted) {
|
||||
final socket = await _socketCompleter.future;
|
||||
_socketCompleter = Completer();
|
||||
await socket.close();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
shutdown(bool isUser) async {
|
||||
if (!_socketCompleter.isCompleted && _process == null) {
|
||||
return false;
|
||||
}
|
||||
_shutdownCompleter = Completer();
|
||||
shutdown() async {
|
||||
await _destroySocket();
|
||||
_clearCompleter();
|
||||
if (system.isWindows) {
|
||||
@@ -160,11 +148,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
}
|
||||
_process?.kill();
|
||||
_process = null;
|
||||
if (isUser) {
|
||||
return _shutdownCompleter.future;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void _clearCompleter() {
|
||||
|
||||
@@ -270,7 +270,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("Default"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("Delay Test"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -205,7 +205,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("デフォルト"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("遅延テスト"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("削除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -277,7 +277,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("По умолчанию"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("Тест задержки"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -185,7 +185,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("默认"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("延迟测试"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -3743,11 +3743,6 @@ class AppLocalizations {
|
||||
String get addProfile {
|
||||
return Intl.message('Add Profile', name: 'addProfile', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Delay Test`
|
||||
String get delayTest {
|
||||
return Intl.message('Delay Test', name: 'delayTest', desc: '', args: []);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -68,8 +68,8 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
render?.resume();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
appController.tryCheckIp();
|
||||
if (system.isAndroid) {
|
||||
appController.addCheckIp();
|
||||
appController.tryStartCore();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,14 +98,16 @@ class _CoreContainerState extends ConsumerState<CoreManager>
|
||||
|
||||
@override
|
||||
Future<void> onCrash(String message) async {
|
||||
if (!globalState.isUserDisconnected &&
|
||||
WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
globalState.isUserDisconnected = false;
|
||||
if (ref.read(coreStatusProvider) != CoreStatus.connected) {
|
||||
return;
|
||||
}
|
||||
ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
await coreController.shutdown(false);
|
||||
await coreController.shutdown();
|
||||
super.onCrash(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
@@ -127,7 +125,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final res = utf8.decode(file.bytes?.toList() ?? []);
|
||||
final res = String.fromCharCodes(file.bytes?.toList() ?? []);
|
||||
_controller.text = res;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/common/color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@@ -50,9 +49,9 @@ class InitErrorScreen extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.errorContainer.opacity50,
|
||||
color: colorScheme.errorContainer.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: colorScheme.error.opacity50),
|
||||
border: Border.all(color: colorScheme.error.withOpacity(0.5)),
|
||||
),
|
||||
child: SelectableText(
|
||||
error.toString(),
|
||||
@@ -72,7 +71,7 @@ class InitErrorScreen extends StatelessWidget {
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey.opacity50),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.5)),
|
||||
),
|
||||
child: SelectableText(
|
||||
stack.toString(),
|
||||
|
||||
@@ -1822,7 +1822,7 @@ final class NetworkDetectionProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$networkDetectionHash() => r'501babec2bbf2a38e4fef96cf20c76e9352bc5ee';
|
||||
String _$networkDetectionHash() => r'29770de6a7ea2ac04b6584145d5200a7d43e45b0';
|
||||
|
||||
abstract class _$NetworkDetection extends $Notifier<NetworkDetectionState> {
|
||||
NetworkDetectionState build();
|
||||
|
||||
@@ -38,10 +38,10 @@ class GlobalState {
|
||||
late Measure measure;
|
||||
late CommonTheme theme;
|
||||
late Color accentColor;
|
||||
bool needInitStatus = true;
|
||||
CorePalette? corePalette;
|
||||
DateTime? startTime;
|
||||
UpdateTasks tasks = [];
|
||||
bool isUserDisconnected = false;
|
||||
SetupState? lastSetupState;
|
||||
VpnState? lastVpnState;
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
Widget build(BuildContext context) {
|
||||
final dashboardState = ref.watch(dashboardStateProvider);
|
||||
final columns = max(4 * ((dashboardState.contentWidth / 280).ceil()), 8);
|
||||
final spacing = 14.mAp;
|
||||
final spacing = 14.ap;
|
||||
final children = [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
|
||||
@@ -41,54 +41,45 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
final color = context.colorScheme.onSurfaceVariant.opacity80;
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
child: RepaintBoundary(
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final traffics = ref.watch(trafficsProvider).list;
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(bottom: 0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: InfoHeader(
|
||||
padding: EdgeInsets.zero,
|
||||
info: Info(
|
||||
label: appLocalizations.networkSpeed,
|
||||
iconData: Icons.speed_sharp,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
_getLastTraffic(traffics).speedText,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.networkSpeed,
|
||||
iconData: Icons.speed_sharp,
|
||||
),
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final traffics = ref.watch(trafficsProvider).list;
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
).copyWith(bottom: 0, left: 0, right: 0),
|
||||
child: LineChart(
|
||||
gradient: true,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
points: _getPoints(traffics),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
).copyWith(bottom: 0, left: 0, right: 0),
|
||||
child: LineChart(
|
||||
gradient: true,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
points: _getPoints(traffics),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Transform.translate(
|
||||
offset: Offset(-16, -20),
|
||||
child: Text(
|
||||
_getLastTraffic(traffics).speedText,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -33,7 +33,10 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
parent: _controller!,
|
||||
curve: Curves.easeOutBack,
|
||||
);
|
||||
ref.listenManual(isStartProvider, (prev, next) {
|
||||
ref.listenManual(runTimeProvider.select((state) => state != null), (
|
||||
prev,
|
||||
next,
|
||||
) {
|
||||
if (next != isStart) {
|
||||
isStart = next;
|
||||
updateController();
|
||||
@@ -52,7 +55,7 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
debouncer.call(FunctionTag.updateStatus, () {
|
||||
appController.updateStatus(isStart, isInit: !ref.read(initProvider));
|
||||
appController.updateStatus(isStart);
|
||||
}, duration: commonDuration);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/features/overwrite/rule.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/pages/editor.dart';
|
||||
import 'package:fl_clash/providers/database.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -29,33 +28,10 @@ class _OverwriteViewState extends ConsumerState<OverwriteView> {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> _handlePreview() async {
|
||||
final profile = ref.read(profileProvider(widget.profileId));
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
final configMap = await appController.getProfileWithId(profile.id);
|
||||
final content = await encodeYamlTask(configMap);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final previewPage = EditorPage(title: profile.realLabel, content: content);
|
||||
BaseNavigator.push<String>(context, previewPage);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonScaffold(
|
||||
title: appLocalizations.override,
|
||||
actions: [
|
||||
CommonMinFilledButtonTheme(
|
||||
child: FilledButton(
|
||||
onPressed: _handlePreview,
|
||||
child: Text(appLocalizations.preview),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
body: CustomScrollView(
|
||||
slivers: [_Title(widget.profileId), _Content(widget.profileId)],
|
||||
),
|
||||
@@ -365,9 +341,7 @@ class _ScriptContent extends ConsumerWidget {
|
||||
|
||||
void _handleChange(WidgetRef ref, int scriptId) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||
return state.copyWith(
|
||||
scriptId: state.scriptId == scriptId ? null : scriptId,
|
||||
);
|
||||
return state.copyWith(scriptId: scriptId);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ class ProfilesView extends StatefulWidget {
|
||||
|
||||
class _ProfilesViewState extends State<ProfilesView> {
|
||||
Function? applyConfigDebounce;
|
||||
bool _isUpdating = false;
|
||||
|
||||
void _handleShowAddExtendPage() {
|
||||
showExtend(
|
||||
@@ -41,10 +40,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
}
|
||||
|
||||
Future<void> _updateProfiles(List<Profile> profiles) async {
|
||||
if (_isUpdating == true) {
|
||||
return;
|
||||
}
|
||||
_isUpdating = true;
|
||||
final List<UpdatingMessage> messages = [];
|
||||
final updateProfiles = profiles.map<Future>((profile) async {
|
||||
if (profile.type == ProfileType.file) return;
|
||||
@@ -60,7 +55,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
if (messages.isNotEmpty) {
|
||||
globalState.showAllUpdatingMessagesDialog(messages);
|
||||
}
|
||||
_isUpdating = false;
|
||||
}
|
||||
|
||||
List<Widget> _buildActions(List<Profile> profiles) {
|
||||
@@ -92,10 +86,10 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
}
|
||||
|
||||
Widget _buildFAB() {
|
||||
return CommonFloatingActionButton(
|
||||
return FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
icon: const Icon(Icons.add),
|
||||
label: context.appLocalizations.addProfile,
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,7 +99,7 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
builder: (_, ref, _) {
|
||||
final isLoading = ref.watch(loadingProvider(LoadingTag.profiles));
|
||||
final state = ref.watch(profilesStateProvider);
|
||||
final spacing = 14.mAp;
|
||||
final spacing = 14.ap;
|
||||
return CommonScaffold(
|
||||
isLoading: isLoading,
|
||||
title: appLocalizations.profiles,
|
||||
|
||||
@@ -410,15 +410,19 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
return AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: CommonFloatingActionButton(
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _healthcheck,
|
||||
label: appLocalizations.delayTest,
|
||||
icon: const Icon(Icons.network_ping),
|
||||
child: const Icon(Icons.network_ping),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScrollOverBuilder extends StatefulWidget {
|
||||
@@ -36,18 +35,58 @@ class _ScrollOverBuilderState extends State<ScrollOverBuilder> {
|
||||
}
|
||||
}
|
||||
|
||||
class FloatingActionButtonExtendedBuilder extends StatelessWidget {
|
||||
final Widget Function(bool isExtend) builder;
|
||||
// class ProxiesActionsBuilder extends StatelessWidget {
|
||||
// final Widget? child;
|
||||
// final Widget Function(
|
||||
// ProxiesActionsState state,
|
||||
// Widget? child,
|
||||
// ) builder;
|
||||
//
|
||||
// const ProxiesActionsBuilder({
|
||||
// super.key,
|
||||
// required this.child,
|
||||
// required this.builder,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Selector<AppState, ProxiesActionsState>(
|
||||
// selector: (_, appState) => ProxiesActionsState(
|
||||
// isCurrent: appState.currentLabel == "proxies",
|
||||
// hasProvider: appState.providers.isNotEmpty,
|
||||
// ),
|
||||
// builder: (_, state, child) => builder(state, child),
|
||||
// child: child,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
const FloatingActionButtonExtendedBuilder({super.key, required this.builder});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isExtended =
|
||||
CommonScaffoldFabExtendedProvider.of(context)?.isExtended ?? false;
|
||||
return builder(isExtended);
|
||||
}
|
||||
}
|
||||
// class ActiveBuilder extends StatelessWidget {
|
||||
// final String label;
|
||||
// final StateAndChildWidgetBuilder<bool> builder;
|
||||
// final Widget? child;
|
||||
//
|
||||
// const ActiveBuilder({
|
||||
// super.key,
|
||||
// required this.label,
|
||||
// required this.builder,
|
||||
// required this.child,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Selector<AppState, bool>(
|
||||
// selector: (_, appState) => appState.currentLabel == label,
|
||||
// builder: (_, state, child) {
|
||||
// return builder(
|
||||
// state,
|
||||
// child,
|
||||
// );
|
||||
// },
|
||||
// child: child,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
typedef StateWidgetBuilder<T> = Widget Function(T state);
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'builder.dart';
|
||||
|
||||
class CommonFloatingActionButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final Icon icon;
|
||||
final String label;
|
||||
|
||||
const CommonFloatingActionButton({
|
||||
super.key,
|
||||
this.onPressed,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
extendedIconLabelSpacing: 0,
|
||||
extendedPadding: EdgeInsets.all(16),
|
||||
),
|
||||
),
|
||||
child: FloatingActionButtonExtendedBuilder(
|
||||
builder: (isExtended) {
|
||||
return FloatingActionButton.extended(
|
||||
heroTag: null,
|
||||
icon: icon,
|
||||
onPressed: onPressed,
|
||||
isExtended: true,
|
||||
label: AnimatedSize(
|
||||
alignment: Alignment.centerLeft,
|
||||
duration: midDuration,
|
||||
curve: Curves.easeOutBack,
|
||||
child: AnimatedOpacity(
|
||||
duration: midDuration,
|
||||
opacity: isExtended ? 1.0 : 0.0,
|
||||
curve: Curves.linear,
|
||||
child: isExtended
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(label, softWrap: false),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class InfoHeader extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
EdgeInsetsGeometry nextPadding = (padding ?? baseInfoEdgeInsets);
|
||||
if (actions.isNotEmpty) {
|
||||
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.mAp));
|
||||
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.ap));
|
||||
}
|
||||
return Padding(
|
||||
padding: nextPadding,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
typedef WrapBuilder = Widget Function(Widget child);
|
||||
|
||||
@@ -28,10 +27,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
TextDirection? textDirection,
|
||||
this.mainAxisExtent,
|
||||
List<Widget>? children,
|
||||
}) : crossAxisCount = crossAxisCount ?? 1,
|
||||
axisDirection = axisDirection ?? AxisDirection.down,
|
||||
textDirection = textDirection ?? TextDirection.ltr,
|
||||
super(children: children ?? const []);
|
||||
}) : crossAxisCount = crossAxisCount ?? 1,
|
||||
axisDirection = axisDirection ?? AxisDirection.down,
|
||||
textDirection = textDirection ?? TextDirection.ltr,
|
||||
super(children: children ?? const []);
|
||||
|
||||
const Grid.baseGap({
|
||||
Key? key,
|
||||
@@ -43,15 +42,15 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
double? mainAxisExtent,
|
||||
List<Widget>? children,
|
||||
}) : this(
|
||||
key: key,
|
||||
mainAxisSpacing: mainAxisSpacing,
|
||||
crossAxisSpacing: crossAxisSpacing,
|
||||
crossAxisCount: crossAxisCount,
|
||||
axisDirection: axisDirection,
|
||||
textDirection: textDirection,
|
||||
mainAxisExtent: mainAxisExtent,
|
||||
children: children,
|
||||
);
|
||||
key: key,
|
||||
mainAxisSpacing: mainAxisSpacing,
|
||||
crossAxisSpacing: crossAxisSpacing,
|
||||
crossAxisCount: crossAxisCount,
|
||||
axisDirection: axisDirection,
|
||||
textDirection: textDirection,
|
||||
mainAxisExtent: mainAxisExtent,
|
||||
children: children,
|
||||
);
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
@@ -66,7 +65,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderGrid renderObject) {
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
RenderGrid renderObject,
|
||||
) {
|
||||
renderObject
|
||||
..mainAxisSpacing = mainAxisSpacing
|
||||
..mainAxisExtent = mainAxisExtent
|
||||
@@ -88,12 +90,12 @@ class RenderGrid extends RenderBox
|
||||
required AxisDirection axisDirection,
|
||||
required TextDirection textDirection,
|
||||
double? mainAxisExtent,
|
||||
}) : _crossAxisCount = crossAxisCount,
|
||||
_crossAxisSpacing = crossAxisSpacing,
|
||||
_mainAxisSpacing = mainAxisSpacing,
|
||||
_axisDirection = axisDirection,
|
||||
_textDirection = textDirection,
|
||||
_mainAxisExtent = mainAxisExtent;
|
||||
}) : _crossAxisCount = crossAxisCount,
|
||||
_crossAxisSpacing = crossAxisSpacing,
|
||||
_mainAxisSpacing = mainAxisSpacing,
|
||||
_axisDirection = axisDirection,
|
||||
_textDirection = textDirection,
|
||||
_mainAxisExtent = mainAxisExtent;
|
||||
|
||||
int _crossAxisCount;
|
||||
|
||||
@@ -212,10 +214,15 @@ class RenderGrid extends RenderBox
|
||||
GridParentData childParentData,
|
||||
int crossAxisCount,
|
||||
) {
|
||||
return math.min(childParentData.crossAxisCellCount ?? 1, crossAxisCount);
|
||||
return math.min(
|
||||
childParentData.crossAxisCellCount ?? 1,
|
||||
crossAxisCount,
|
||||
);
|
||||
}
|
||||
|
||||
Size _computeSize({required BoxConstraints constraints}) {
|
||||
Size _computeSize({
|
||||
required BoxConstraints constraints,
|
||||
}) {
|
||||
final crossAxisExtent = mainAxis == Axis.vertical
|
||||
? constraints.maxWidth
|
||||
: constraints.maxHeight;
|
||||
@@ -238,13 +245,11 @@ class RenderGrid extends RenderBox
|
||||
? BoxConstraints.tightFor(width: crossAxisExtent)
|
||||
: BoxConstraints.tightFor(height: crossAxisExtent);
|
||||
_layoutChild(child, childConstraints, parentUsesSize: true);
|
||||
mainAxisExtent = mainAxis == Axis.vertical
|
||||
? child.size.height
|
||||
: child.size.width;
|
||||
mainAxisExtent =
|
||||
mainAxis == Axis.vertical ? child.size.height : child.size.width;
|
||||
} else {
|
||||
final mainAxisCellCount = childParentData.mainAxisCellCount ?? 1;
|
||||
mainAxisExtent =
|
||||
(this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
||||
mainAxisExtent = (this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
||||
mainAxisSpacing;
|
||||
childParentData.realMainAxisExtent = mainAxisExtent;
|
||||
final childSize = mainAxis == Axis.vertical
|
||||
@@ -260,7 +265,9 @@ class RenderGrid extends RenderBox
|
||||
? Offset(crossAxisOffset, mainAxisOffset)
|
||||
: Offset(mainAxisOffset, crossAxisOffset);
|
||||
childParentData.offset = offset;
|
||||
|
||||
final nextOffset = mainAxisOffset + mainAxisExtent + mainAxisSpacing;
|
||||
|
||||
for (int i = 0; i < crossAxisCellCount; i++) {
|
||||
offsets[origin.crossAxisIndex + i] = nextOffset;
|
||||
}
|
||||
@@ -274,8 +281,7 @@ class RenderGrid extends RenderBox
|
||||
final childParentData = _getParentData(child);
|
||||
final offset = childParentData.offset;
|
||||
final crossAxisOffset = offset.getCrossAxisOffset(mainAxis);
|
||||
final mainAxisOffset =
|
||||
mainAxisExtent -
|
||||
final mainAxisOffset = mainAxisExtent -
|
||||
offset.getMainAxisOffset(mainAxis) -
|
||||
childParentData.realMainAxisExtent!;
|
||||
final newOffset = mainAxis == Axis.vertical
|
||||
@@ -359,11 +365,15 @@ class GridItem extends ParentDataWidget<GridParentData> {
|
||||
@override
|
||||
Type get debugTypicalAncestorWidgetClass => GridItem;
|
||||
|
||||
GridItem wrap({required WrapBuilder builder}) {
|
||||
GridItem wrap({
|
||||
required WrapBuilder builder,
|
||||
}) {
|
||||
return GridItem(
|
||||
mainAxisCellCount: mainAxisCellCount,
|
||||
crossAxisCellCount: crossAxisCellCount,
|
||||
child: builder(child),
|
||||
child: builder(
|
||||
child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -385,13 +395,11 @@ _Origin _getOrigin(List<double> offsets, int crossAxisCount) {
|
||||
}
|
||||
int start = 0;
|
||||
int span = 0;
|
||||
for (
|
||||
int j = 0;
|
||||
span < crossAxisCount &&
|
||||
j < length &&
|
||||
length - j >= crossAxisCount - span;
|
||||
j++
|
||||
) {
|
||||
for (int j = 0;
|
||||
span < crossAxisCount &&
|
||||
j < length &&
|
||||
length - j >= crossAxisCount - span;
|
||||
j++) {
|
||||
if (offset.moreOrEqual(offsets[j])) {
|
||||
span++;
|
||||
if (span == crossAxisCount) {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||
final VoidCallback? backAction;
|
||||
|
||||
const CommonScaffoldBackActionProvider({
|
||||
super.key,
|
||||
required this.backAction,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static CommonScaffoldBackActionProvider? of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<CommonScaffoldBackActionProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CommonScaffoldBackActionProvider oldWidget) =>
|
||||
backAction != oldWidget.backAction;
|
||||
}
|
||||
|
||||
class CommonScaffoldFabExtendedProvider extends InheritedWidget {
|
||||
final bool isExtended;
|
||||
|
||||
const CommonScaffoldFabExtendedProvider({
|
||||
super.key,
|
||||
required this.isExtended,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static CommonScaffoldFabExtendedProvider? of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<
|
||||
CommonScaffoldFabExtendedProvider
|
||||
>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CommonScaffoldFabExtendedProvider oldWidget) =>
|
||||
isExtended != oldWidget.isExtended;
|
||||
}
|
||||
@@ -3,10 +3,8 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/widgets/pop_scope.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
import 'chip.dart';
|
||||
import 'inherited.dart';
|
||||
|
||||
typedef OnKeywordsUpdateCallback = void Function(List<String> keywords);
|
||||
|
||||
@@ -49,8 +47,8 @@ class CommonScaffold extends StatefulWidget {
|
||||
|
||||
class CommonScaffoldState extends State<CommonScaffold> {
|
||||
late final ValueNotifier<AppBarState> _appBarState;
|
||||
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
|
||||
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _isFabExtendedNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);
|
||||
final _textController = TextEditingController();
|
||||
|
||||
@@ -85,6 +83,12 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
_updateSearchState((state) => state?.copyWith(query: ''));
|
||||
}
|
||||
|
||||
set floatingActionButton(Widget? floatingActionButton) {
|
||||
if (_floatingActionButton.value != floatingActionButton) {
|
||||
_floatingActionButton.value = floatingActionButton;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSearchingAppBarTheme(Widget child) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
@@ -152,7 +156,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
void dispose() {
|
||||
_appBarState.dispose();
|
||||
_textController.dispose();
|
||||
_isFabExtendedNotifier.dispose();
|
||||
_floatingActionButton.dispose();
|
||||
_loadingNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -346,31 +350,17 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(backActionProvider?.backAction),
|
||||
body: NotificationListener<UserScrollNotification>(
|
||||
child: body,
|
||||
onNotification: (notification) {
|
||||
if (notification.direction == ScrollDirection.reverse) {
|
||||
_isFabExtendedNotifier.value = false;
|
||||
} else if (notification.direction == ScrollDirection.forward) {
|
||||
_isFabExtendedNotifier.value = true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
),
|
||||
body: body,
|
||||
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
floatingActionButton: widget.floatingActionButton != null
|
||||
? ValueListenableBuilder<bool>(
|
||||
valueListenable: _isFabExtendedNotifier,
|
||||
builder: (_, isExtended, child) {
|
||||
return CommonScaffoldFabExtendedProvider(
|
||||
isExtended: isExtended,
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
child: widget.floatingActionButton,
|
||||
)
|
||||
: null,
|
||||
floatingActionButton:
|
||||
widget.floatingActionButton ??
|
||||
ValueListenableBuilder<Widget?>(
|
||||
valueListenable: _floatingActionButton,
|
||||
builder: (_, value, _) {
|
||||
return value ?? SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -382,6 +372,25 @@ List<Widget> genActions(List<Widget> actions, {double? space}) {
|
||||
];
|
||||
}
|
||||
|
||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||
final VoidCallback? backAction;
|
||||
|
||||
const CommonScaffoldBackActionProvider({
|
||||
super.key,
|
||||
required this.backAction,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static CommonScaffoldBackActionProvider? of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<CommonScaffoldBackActionProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CommonScaffoldBackActionProvider oldWidget) =>
|
||||
backAction != oldWidget.backAction;
|
||||
}
|
||||
|
||||
class BaseScaffold extends StatelessWidget {
|
||||
final String title;
|
||||
final List<Widget> actions;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export 'activate_box.dart';
|
||||
export 'animate_grid.dart';
|
||||
export 'builder.dart';
|
||||
export 'button.dart';
|
||||
export 'card.dart';
|
||||
export 'chip.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
@@ -14,7 +13,6 @@ export 'fade_box.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'grid.dart';
|
||||
export 'icon.dart';
|
||||
export 'inherited.dart';
|
||||
export 'input.dart';
|
||||
export 'keep_scope.dart';
|
||||
export 'line_chart.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.92+2026012602
|
||||
version: 0.8.92+2026012404
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
|
||||
25
setup.dart
25
setup.dart
@@ -369,15 +369,6 @@ class BuildCommand extends Command {
|
||||
.map((e) => e.arch!)
|
||||
.toList();
|
||||
|
||||
Future<void> _buildEnvFile(String env, {String? coreSha256}) async {
|
||||
final data = {
|
||||
'APP_ENV': env,
|
||||
if (coreSha256 != null) 'CORE_SHA256': coreSha256,
|
||||
};
|
||||
final envFile = File(join(current, 'env.json'))..create();
|
||||
await envFile.writeAsString(json.encode(data));
|
||||
}
|
||||
|
||||
Future<void> _getLinuxDependencies(Arch arch) async {
|
||||
await Build.exec(Build.getExecutable('sudo apt update -y'));
|
||||
await Build.exec(
|
||||
@@ -421,7 +412,7 @@ class BuildCommand extends Command {
|
||||
await Build.exec(
|
||||
name: name,
|
||||
Build.getExecutable(
|
||||
'flutter_distributor package --skip-clean --platform ${target.name} --targets $targets --flutter-build-args=verbose,dart-define-from-file=env.json$args',
|
||||
'flutter_distributor package --skip-clean --platform ${target.name} --targets $targets --flutter-build-args=verbose$args --build-dart-define=APP_ENV=$env',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -457,23 +448,21 @@ class BuildCommand extends Command {
|
||||
mode: mode,
|
||||
);
|
||||
|
||||
String? coreSha256;
|
||||
|
||||
if (Platform.isWindows) {
|
||||
coreSha256 = await Build.calcSha256(corePaths.first);
|
||||
await Build.buildHelper(target, coreSha256);
|
||||
}
|
||||
await _buildEnvFile(env, coreSha256: coreSha256);
|
||||
if (out != 'app') {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
case Target.windows:
|
||||
final token = target != Target.android
|
||||
? await Build.calcSha256(corePaths.first)
|
||||
: null;
|
||||
Build.buildHelper(target, token!);
|
||||
_buildDistributor(
|
||||
target: target,
|
||||
targets: 'exe,zip',
|
||||
args: ' --description $archName',
|
||||
args:
|
||||
' --description $archName --build-dart-define=CORE_SHA256=$token',
|
||||
env: env,
|
||||
);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user