Compare commits

...

1 Commits

Author SHA1 Message Date
chen08209
2fbc4170b9 Add some scenes auto close connections
Support proxies query

Optimize more details
2025-05-09 16:11:20 +08:00
63 changed files with 1097 additions and 690 deletions

View File

@@ -73,6 +73,7 @@ android {
applicationIdSuffix '.debug'
}
release {
debuggable false
if (isRelease) {
signingConfig signingConfigs.release
} else {

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.argumentsWithVarargAsSingleArray
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
@@ -15,6 +17,7 @@ android {
buildTypes {
release {
isJniDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"

View File

@@ -4,6 +4,8 @@ project("core")
message("CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}")
message("CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}")
if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_compile_options(-O3)

View File

@@ -1,21 +1,18 @@
#include <jni.h>
#ifdef LIBCLASH
#include <jni.h>
#include <string>
#include "jni_helper.h"
#include "libclash.h"
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject thiz, jint fd, jobject cb) {
auto interface = new_global(cb);
Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject, const jint fd, jobject cb) {
const auto interface = new_global(cb);
startTUN(fd, interface);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_stopTun(JNIEnv *env, jobject thiz) {
Java_com_follow_clash_core_Core_stopTun(JNIEnv *) {
stopTun();
}
@@ -26,50 +23,50 @@ static jmethodID m_tun_interface_resolve_process;
static void release_jni_object_impl(void *obj) {
ATTACH_JNI();
del_global((jobject) obj);
del_global(static_cast<jobject>(obj));
}
static void call_tun_interface_protect_impl(void *tun_interface, int fd) {
static void call_tun_interface_protect_impl(void *tun_interface, const int fd) {
ATTACH_JNI();
env->CallVoidMethod((jobject) tun_interface,
(jmethodID) m_tun_interface_protect,
(jint) fd);
env->CallVoidMethod(static_cast<jobject>(tun_interface),
m_tun_interface_protect,
fd);
}
static const char*
static const char *
call_tun_interface_resolve_process_impl(void *tun_interface, int protocol,
const char *source,
const char *target,
int uid) {
const char *source,
const char *target,
const int uid) {
ATTACH_JNI();
jstring packageName = (jstring)env->CallObjectMethod((jobject) tun_interface,
(jmethodID) m_tun_interface_resolve_process,
(jint) protocol,
(jstring) new_string(source),
(jstring) new_string(target),
(jint) uid);
const auto packageName = reinterpret_cast<jstring>(env->CallObjectMethod(static_cast<jobject>(tun_interface),
m_tun_interface_resolve_process,
protocol,
new_string(source),
new_string(target),
uid));
return get_string(packageName);
}
extern "C"
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNI_OnLoad(JavaVM *vm, void *) {
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
initialize_jni(vm, env);
jclass c_tun_interface = find_class("com/follow/clash/core/TunInterface");
const auto c_tun_interface = find_class("com/follow/clash/core/TunInterface");
m_tun_interface_protect = find_method(c_tun_interface, "protect", "(I)V");
m_tun_interface_resolve_process = find_method(c_tun_interface, "resolverProcess",
"(ILjava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
"(ILjava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
registerCallbacks(&call_tun_interface_protect_impl,
&call_tun_interface_resolve_process_impl,
&release_jni_object_impl);
return JNI_VERSION_1_6;
}
#endif
#endif

View File

@@ -1,5 +1,6 @@
#include "jni_helper.h"
#include <cstdlib>
#include <malloc.h>
#include <cstring>
@@ -12,7 +13,7 @@ static jmethodID m_get_bytes;
void initialize_jni(JavaVM *vm, JNIEnv *env) {
global_vm = vm;
c_string = (jclass) new_global(find_class("java/lang/String"));
c_string = reinterpret_cast<jclass>(new_global(find_class("java/lang/String")));
m_new_string = find_method(c_string, "<init>", "([B)V");
m_get_bytes = find_method(c_string, "getBytes", "()[B");
}
@@ -22,23 +23,23 @@ JavaVM *global_java_vm() {
}
char *jni_get_string(JNIEnv *env, jstring str) {
auto array = (jbyteArray) env->CallObjectMethod(str, m_get_bytes);
int length = env->GetArrayLength(array);
char *content = (char *) malloc(length + 1);
env->GetByteArrayRegion(array, 0, length, (jbyte *) content);
const auto array = reinterpret_cast<jbyteArray>(env->CallObjectMethod(str, m_get_bytes));
const int length = env->GetArrayLength(array);
const auto content = static_cast<char *>(malloc(length + 1));
env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte *>(content));
content[length] = 0;
return content;
}
jstring jni_new_string(JNIEnv *env, const char *str) {
auto length = (int) strlen(str);
jbyteArray array = env->NewByteArray(length);
env->SetByteArrayRegion(array, 0, length, (const jbyte *) str);
return (jstring) env->NewObject(c_string, m_new_string, array);
const auto length = static_cast<int>(strlen(str));
const auto array = env->NewByteArray(length);
env->SetByteArrayRegion(array, 0, length, reinterpret_cast<const jbyte *>(str));
return reinterpret_cast<jstring>(env->NewObject(c_string, m_new_string, array));
}
int jni_catch_exception(JNIEnv *env) {
int result = env->ExceptionCheck();
const int result = env->ExceptionCheck();
if (result) {
env->ExceptionDescribe();
env->ExceptionClear();
@@ -46,9 +47,9 @@ int jni_catch_exception(JNIEnv *env) {
return result;
}
void jni_attach_thread(struct scoped_jni *jni) {
void jni_attach_thread(scoped_jni *jni) {
JavaVM *vm = global_java_vm();
if (vm->GetEnv((void **) &jni->env, JNI_VERSION_1_6) == JNI_OK) {
if (vm->GetEnv(reinterpret_cast<void **>(&jni->env), JNI_VERSION_1_6) == JNI_OK) {
jni->require_release = 0;
return;
}
@@ -58,9 +59,9 @@ void jni_attach_thread(struct scoped_jni *jni) {
jni->require_release = 1;
}
void jni_detach_thread(struct scoped_jni *jni) {
void jni_detach_thread(const scoped_jni *env) {
JavaVM *vm = global_java_vm();
if (jni->require_release) {
if (env->require_release) {
vm->DetachCurrentThread();
}
}

View File

@@ -1,9 +1,6 @@
#pragma once
#include <jni.h>
#include <cstdint>
#include <cstdlib>
#include <malloc.h>
struct scoped_jni {
JNIEnv *env;
@@ -18,14 +15,14 @@ extern char *jni_get_string(JNIEnv *env, jstring str);
extern int jni_catch_exception(JNIEnv *env);
extern void jni_attach_thread(struct scoped_jni *jni);
extern void jni_attach_thread(scoped_jni *jni);
extern void jni_detach_thread(struct scoped_jni *env);
extern void jni_detach_thread(const scoped_jni *env);
extern void release_string(char **str);
#define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) \
struct scoped_jni _jni; \
scoped_jni _jni{}; \
jni_attach_thread(&_jni); \
JNIEnv *env = _jni.env
@@ -36,4 +33,4 @@ extern void release_string(char **str);
#define new_global(obj) env->NewGlobalRef(obj)
#define del_global(obj) env->DeleteGlobalRef(obj)
#define get_string(jstr) jni_get_string(env, jstr)
#define new_string(cstr) jni_new_string(env, cstr)
#define new_string(cstr) jni_new_string(env, cstr)

View File

@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx4G
android.useAndroidX=true
android.enableJetifier=true
kotlin_version=1.9.22
agp_version=8.9.1
agp_version=8.9.2

View File

@@ -53,6 +53,7 @@ func handleStartListener() bool {
defer runLock.Unlock()
isRunning = true
updateListeners(true)
closeConnections()
return true
}
@@ -276,6 +277,16 @@ func handleCloseConnections() bool {
return true
}
func closeConnections() {
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
err := c.Close()
if err != nil {
return false
}
return true
})
}
func handleCloseConnection(connectionId string) bool {
runLock.Lock()
defer runLock.Unlock()

View File

@@ -100,7 +100,8 @@ class ApplicationState extends ConsumerState<Application> {
return AppStateManager(
child: ClashManager(
child: ConnectivityManager(
onConnectivityChanged: () {
onConnectivityChanged: () async {
await clashCore.closeConnections();
globalState.appController.updateLocalIp();
globalState.appController.addCheckIpNumDebounce();
},

View File

@@ -36,4 +36,4 @@ export 'window.dart';
export 'windows.dart';
export 'render.dart';
export 'mixin.dart';
export 'print.dart';
export 'print.dart';

View File

@@ -23,7 +23,8 @@ final baseInfoEdgeInsets = EdgeInsets.symmetric(
horizontal: 16.ap,
);
final defaultTextScaleFactor = WidgetsBinding.instance.platformDispatcher.textScaleFactor;
final defaultTextScaleFactor =
WidgetsBinding.instance.platformDispatcher.textScaleFactor;
const httpTimeoutDuration = Duration(milliseconds: 5000);
const moreDuration = Duration(milliseconds: 100);
const animateDuration = Duration(milliseconds: 100);
@@ -76,6 +77,10 @@ const viewModeColumnsMap = {
ViewMode.desktop: [4, 3],
};
// const proxiesStoreKey = PageStorageKey<String>('proxies');
// const toolsStoreKey = PageStorageKey<String>('tools');
// const profilesStoreKey = PageStorageKey<String>('profiles');
const defaultPrimaryColor = 0XFFD8C0C3;
double getWidgetHeight(num lines) {

View File

@@ -1,10 +1,12 @@
import 'dart:async';
import 'package:fl_clash/enum/enum.dart';
class Debouncer {
final Map<dynamic, Timer?> _operations = {};
final Map<FunctionTag, Timer?> _operations = {};
call(
dynamic tag,
FunctionTag tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
@@ -33,10 +35,10 @@ class Debouncer {
}
class Throttler {
final Map<dynamic, Timer?> _operations = {};
final Map<FunctionTag, Timer?> _operations = {};
call(
dynamic tag,
FunctionTag tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),

View File

@@ -12,6 +12,7 @@ class Navigation {
}) {
return [
const NavigationItem(
keep: false,
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
fragment: DashboardFragment(
@@ -65,7 +66,6 @@ class Navigation {
icon: Icon(Icons.storage),
label: PageLabel.resources,
description: "resourcesDesc",
keep: false,
fragment: Resources(
key: GlobalObjectKey(
PageLabel.resources,

View File

@@ -23,14 +23,14 @@ class Render {
pause() {
throttler.call(
DebounceTag.renderPause,
FunctionTag.renderPause,
_pause,
duration: Duration(seconds: 5),
);
}
resume() {
throttler.cancel(DebounceTag.renderPause);
throttler.cancel(FunctionTag.renderPause);
_resume();
}

View File

@@ -47,6 +47,10 @@ extension StringExtension on String {
return false;
}
}
// bool containsToLower(String target) {
// return toLowerCase().contains(target);
// }
}
extension StringExtensionSafe on String? {

View File

@@ -5,6 +5,7 @@ import 'dart:ui';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lpinyin/lpinyin.dart';
class Utils {
@@ -230,7 +231,7 @@ class Utils {
}
int getProfilesColumns(double viewWidth) {
return max((viewWidth / 350).floor(), 1);
return max((viewWidth / 320).floor(), 1);
}
final _indexPrimary = [
@@ -323,6 +324,15 @@ class Utils {
}
return "";
}
SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) {
final control = Platform.isMacOS ? false : true;
return SingleActivator(
trigger,
control: control,
meta: !control,
);
}
}
final utils = Utils();

View File

@@ -8,6 +8,7 @@ import 'package:archive/archive.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/archive.dart';
import 'package:fl_clash/enum/enum.dart';
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';
@@ -30,18 +31,18 @@ class AppController {
AppController(this.context, WidgetRef ref) : _ref = ref;
updateClashConfigDebounce() {
debouncer.call(DebounceTag.updateClashConfig, () async {
debouncer.call(FunctionTag.updateClashConfig, () async {
final isPatch = globalState.appState.needApply ? false : true;
await updateClashConfig(isPatch);
});
}
updateGroupsDebounce() {
debouncer.call(DebounceTag.updateGroups, updateGroups);
debouncer.call(FunctionTag.updateGroups, updateGroups);
}
addCheckIpNumDebounce() {
debouncer.call(DebounceTag.addCheckIpNum, () {
debouncer.call(FunctionTag.addCheckIpNum, () {
_ref.read(checkIpNumProvider.notifier).add();
});
}
@@ -49,17 +50,17 @@ class AppController {
applyProfileDebounce({
bool silence = false,
}) {
debouncer.call(DebounceTag.applyProfile, (silence) {
debouncer.call(FunctionTag.applyProfile, (silence) {
applyProfile(silence: silence);
}, args: [silence]);
}
savePreferencesDebounce() {
debouncer.call(DebounceTag.savePreferences, savePreferences);
debouncer.call(FunctionTag.savePreferences, savePreferences);
}
changeProxyDebounce(String groupName, String proxyName) {
debouncer.call(DebounceTag.changeProxy,
debouncer.call(FunctionTag.changeProxy,
(String groupName, String proxyName) async {
await changeProxy(
groupName: groupName,
@@ -385,6 +386,9 @@ class AppController {
}
handleBackOrExit() async {
if (_ref.read(backBlockProvider)) {
return;
}
if (_ref.read(appSettingProvider).minimizeOnExit) {
if (system.isDesktop) {
await savePreferencesDebounce();
@@ -395,6 +399,14 @@ class AppController {
}
}
backBlock() {
_ref.read(backBlockProvider.notifier).value = true;
}
unBackBlock() {
_ref.read(backBlockProvider.notifier).value = false;
}
handleExit() async {
try {
await updateStatus(false);
@@ -498,8 +510,9 @@ class AppController {
}
init() async {
await _handlePreference();
await _handlerDisclaimer();
FlutterError.onError = (details) {
commonPrint.log(details.stack.toString());
};
await _initCore();
await _initStatus();
updateTray(true);
@@ -513,6 +526,8 @@ class AppController {
} else {
window?.hide();
}
await _handlePreference();
await _handlerDisclaimer();
_ref.read(initProvider.notifier).value = true;
}
@@ -690,10 +705,16 @@ class AppController {
return List.of(proxies)
..sort(
(a, b) {
final aDelay =
_ref.read(getDelayProvider(proxyName: a.name, testUrl: testUrl));
final bDelay =
_ref.read(getDelayProvider(proxyName: b.name, testUrl: testUrl));
final aDelay = _ref.read(getDelayProvider(
proxyName: a.name,
testUrl: testUrl,
));
final bDelay = _ref.read(
getDelayProvider(
proxyName: b.name,
testUrl: testUrl,
),
);
if (aDelay == null && bDelay == null) {
return 0;
}
@@ -754,6 +775,17 @@ class AppController {
);
}
Future<List<Package>> getPackages() async {
if (_ref.read(isMobileViewProvider)) {
await Future.delayed(commonDuration);
}
if (_ref.read(packagesProvider).isEmpty) {
_ref.read(packagesProvider.notifier).value =
await app?.getPackages() ?? [];
}
return _ref.read(packagesProvider);
}
updateStart() {
updateStatus(!_ref.read(runTimeProvider.notifier).isStart);
}

View File

@@ -291,7 +291,7 @@ enum WindowsHelperServiceStatus {
running,
}
enum DebounceTag {
enum FunctionTag {
updateClashConfig,
updateStatus,
updateGroups,
@@ -308,6 +308,9 @@ enum DebounceTag {
updatePageIndex,
pageChange,
proxiesTabChange,
logs,
requests,
}
enum DashboardWidget {

View File

@@ -202,10 +202,7 @@ class Avatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InkWell(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
hoverColor: Colors.transparent,
return GestureDetector(
child: Column(
children: [
SizedBox(

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:fl_clash/enum/enum.dart';
@@ -22,21 +23,14 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
List<String> acceptList = [];
List<String> rejectList = [];
late ScrollController _controller;
final _completer = Completer();
@override
void initState() {
super.initState();
_updateInitList();
_controller = ScrollController();
WidgetsBinding.instance.addPostFrameCallback((_) {
final appState = globalState.appState;
if (appState.packages.isEmpty) {
Future.delayed(const Duration(milliseconds: 300), () async {
ref.read(packagesProvider.notifier).value =
await app?.getPackages() ?? [];
});
}
});
_completer.complete(globalState.appController.getPackages());
}
@override
@@ -319,31 +313,42 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
),
Expanded(
flex: 1,
child: packages.isEmpty
? const Center(
child: CircularProgressIndicator(),
)
: CommonScrollBar(
controller: _controller,
child: ListView.builder(
controller: _controller,
itemCount: packages.length,
itemExtent: 72,
itemBuilder: (_, index) {
final package = packages[index];
return PackageListItem(
key: Key(package.packageName),
package: package,
value: valueList.contains(package.packageName),
isActive: accessControl.enable,
onChanged: (value) {
_handleSelected(valueList, package, value);
},
child: FutureBuilder(
future: _completer.future,
builder: (_, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(),
);
}
return packages.isEmpty
? NullStatus(
label: appLocalizations.noData,
)
: CommonScrollBar(
controller: _controller,
child: ListView.builder(
controller: _controller,
itemCount: packages.length,
itemExtent: 72,
itemBuilder: (_, index) {
final package = packages[index];
return PackageListItem(
key: Key(package.packageName),
package: package,
value: valueList
.contains(package.packageName),
isActive: accessControl.enable,
onChanged: (value) {
_handleSelected(
valueList, package, value);
},
);
},
),
);
},
),
),
),
}),
)
],
),
),

View File

@@ -202,25 +202,25 @@ class BackupAndRecovery extends ConsumerWidget {
builder: (_, snapshot) {
return Center(
child: FadeThroughBox(
child: snapshot.connectionState ==
ConnectionState.waiting
? const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
strokeWidth: 1,
),
)
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: snapshot.data == true
? Colors.green
: Colors.red,
),
width: 12,
height: 12,
),
child:
snapshot.connectionState != ConnectionState.done
? const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
strokeWidth: 1,
),
)
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: snapshot.data == true
? Colors.green
: Colors.red,
),
width: 12,
height: 12,
),
),
);
},

View File

@@ -111,7 +111,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
}
updateRequestsThrottler() {
throttler.call("request", () {
throttler.call(FunctionTag.requests, () {
final isEquality = connectionListEquality.equals(
_requests,
_requestsStateNotifier.value.connections,
@@ -120,9 +120,11 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
connections: _requests,
);
if(mounted){
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
connections: _requests,
);
}
});
}, duration: commonDuration);
}

View File

@@ -6,6 +6,7 @@ import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'widgets/start_button.dart';
class DashboardFragment extends ConsumerStatefulWidget {
@@ -66,7 +67,16 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
valueListenable: key.currentState!.isEditNotifier,
builder: (_, isEdit, ___) {
return isEdit
? Icon(Icons.save)
? SystemBackBlock(
child: CommonPopScope(
child: Icon(Icons.save),
onPop: () {
key.currentState!.isEditNotifier.value =
!key.currentState!.isEditNotifier.value;
return false;
},
),
)
: Icon(
Icons.edit,
);

View File

@@ -1,23 +1,11 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final _networkDetectionState = ValueNotifier<NetworkDetectionState>(
const NetworkDetectionState(
isTesting: false,
isLoading: true,
ipInfo: null,
),
);
class NetworkDetection extends ConsumerStatefulWidget {
const NetworkDetection({super.key});
@@ -26,101 +14,6 @@ class NetworkDetection extends ConsumerStatefulWidget {
}
class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
bool? _preIsStart;
Timer? _setTimeoutTimer;
CancelToken? cancelToken;
@override
void initState() {
ref.listenManual(checkIpNumProvider, (prev, next) {
if (prev != next) {
_startCheck();
}
});
if (!_networkDetectionState.value.isTesting &&
_networkDetectionState.value.isLoading) {
_startCheck();
}
super.initState();
}
_startCheck() async {
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
}
debouncer.call(
DebounceTag.checkIp,
_checkIp,
);
}
_checkIp() async {
final appState = globalState.appState;
final isInit = appState.isInit;
if (!isInit) return;
final isStart = appState.runTime != null;
if (_preIsStart == false &&
_preIsStart == isStart &&
_networkDetectionState.value.ipInfo != null) {
return;
}
_clearSetTimeoutTimer();
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isLoading: true,
ipInfo: null,
);
_preIsStart = isStart;
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
}
cancelToken = CancelToken();
try {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: true,
);
final ipInfo = await request.checkIp(cancelToken: cancelToken);
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: false,
);
if (ipInfo != null) {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isLoading: false,
ipInfo: ipInfo,
);
return;
}
_clearSetTimeoutTimer();
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isLoading: false,
ipInfo: null,
);
});
} catch (e) {
if (e.toString() == "cancelled") {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isLoading: true,
ipInfo: null,
);
}
}
}
@override
void dispose() {
_clearSetTimeoutTimer();
super.dispose();
}
_clearSetTimeoutTimer() {
if (_setTimeoutTimer != null) {
_setTimeoutTimer?.cancel();
_setTimeoutTimer = null;
}
}
_countryCodeToEmoji(String countryCode) {
final String code = countryCode.toUpperCase();
if (code.length != 2) {
@@ -136,7 +29,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
return SizedBox(
height: getWidgetHeight(1),
child: ValueListenableBuilder<NetworkDetectionState>(
valueListenable: _networkDetectionState,
valueListenable: detectionState.state,
builder: (_, state, __) {
final ipInfo = state.ipInfo;
final isLoading = state.isLoading;

View File

@@ -38,7 +38,7 @@ class _StartButtonState extends State<StartButton>
isStart = !isStart;
updateController();
debouncer.call(
DebounceTag.updateStatus,
FunctionTag.updateStatus,
() {
globalState.appController.updateStatus(isStart);
},

View File

@@ -93,8 +93,6 @@ class DeveloperView extends ConsumerWidget {
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 4,
bottom: 4,
),
title: Text(appLocalizations.developerMode),
delegate: SwitchDelegate(

View File

@@ -103,7 +103,7 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
modifiers: modifiers,
key: key.usbHidUsage,
);
return true;
return false;
}
@override
@@ -157,59 +157,65 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
@override
Widget build(BuildContext context) {
return CommonDialog(
title: IntlExt.actionMessage(widget.hotKeyAction.action.name),
actions: [
TextButton(
onPressed: () {
_handleRemove();
},
child: Text(appLocalizations.remove),
),
const SizedBox(
width: 8,
),
TextButton(
onPressed: () {
_handleConfirm();
},
child: Text(
appLocalizations.confirm,
return Focus(
onKeyEvent: (_, __) {
return KeyEventResult.handled;
},
autofocus: true,
child: CommonDialog(
title: IntlExt.actionMessage(widget.hotKeyAction.action.name),
actions: [
TextButton(
onPressed: () {
_handleRemove();
},
child: Text(appLocalizations.remove),
),
),
],
child: ValueListenableBuilder(
valueListenable: hotKeyActionNotifier,
builder: (_, hotKeyAction, ___) {
final key = hotKeyAction.key;
final modifiers = hotKeyAction.modifiers;
return SizedBox(
width: dialogCommonWidth,
child: key != null
? Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
for (final modifier in modifiers)
const SizedBox(
width: 8,
),
TextButton(
onPressed: () {
_handleConfirm();
},
child: Text(
appLocalizations.confirm,
),
),
],
child: ValueListenableBuilder(
valueListenable: hotKeyActionNotifier,
builder: (_, hotKeyAction, ___) {
final key = hotKeyAction.key;
final modifiers = hotKeyAction.modifiers;
return SizedBox(
width: dialogCommonWidth,
child: key != null
? Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
for (final modifier in modifiers)
KeyboardKeyBox(
keyboardKey: modifier.physicalKeys.first,
),
if (modifiers.isNotEmpty)
Text(
"+",
style: context.textTheme.titleMedium,
),
KeyboardKeyBox(
keyboardKey: modifier.physicalKeys.first,
keyboardKey: PhysicalKeyboardKey(key),
),
if (modifiers.isNotEmpty)
Text(
"+",
style: context.textTheme.titleMedium,
),
KeyboardKeyBox(
keyboardKey: PhysicalKeyboardKey(key),
),
],
)
: Text(
appLocalizations.pressKeyboard,
style: context.textTheme.titleMedium,
),
);
},
],
)
: Text(
appLocalizations.pressKeyboard,
style: context.textTheme.titleMedium,
),
);
},
),
),
);
}

View File

@@ -131,7 +131,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
}
updateLogsThrottler() {
throttler.call("logs", () {
throttler.call(FunctionTag.logs, () {
final isEquality = logListEquality.equals(
_logs,
_logsStateNotifier.value.logs,

View File

@@ -305,8 +305,6 @@ class OverrideSwitch extends ConsumerWidget {
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 4,
bottom: 4,
),
title: Text(appLocalizations.enableOverride),
delegate: SwitchDelegate(

View File

@@ -349,12 +349,10 @@ class ProfileItem extends StatelessWidget {
),
PopupMenuItemData(
icon: Icons.delete_outlined,
iconSize: 20,
label: appLocalizations.delete,
onPressed: () {
_handleDeleteProfile(context);
},
type: PopupMenuItemType.danger,
),
],
),

View File

@@ -6,7 +6,7 @@ import 'package:fl_clash/state.dart';
double get listHeaderHeight {
final measure = globalState.measure;
return 28 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
return 20 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
}
double getItemHeight(ProxyCardType proxyCardType) {

View File

@@ -114,12 +114,16 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
required int columns,
required Set<String> currentUnfoldSet,
required ProxyCardType type,
required String query,
}) {
final items = <Widget>[];
final GroupNameProxiesMap groupNameProxiesMap = {};
for (final groupName in groupNames) {
final group =
ref.read(groupsProvider.select((state) => state.getGroup(groupName)));
final group = ref.read(
groupsProvider.select(
(state) => state.getGroup(groupName),
),
);
if (group == null) {
continue;
}
@@ -140,7 +144,9 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
]);
if (isExpand) {
final sortedProxies = globalState.appController.getSortProxies(
group.all,
group.all
.where((item) => item.name.toLowerCase().contains(query))
.toList(),
group.testUrl,
);
groupNameProxiesMap[groupName] = sortedProxies;
@@ -250,6 +256,7 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
return Consumer(
builder: (_, ref, __) {
final state = ref.watch(proxiesListSelectorStateProvider);
ref.watch(themeSettingProvider.select((state) => state.textScale));
if (state.groupNames.isEmpty) {
return NullStatus(
label: appLocalizations.nullProxies,
@@ -261,6 +268,7 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
currentUnfoldSet: state.currentUnfoldSet,
columns: state.columns,
type: state.proxyCardType,
query: state.query,
);
final itemsOffset = _getItemHeightList(items, state.proxyCardType);
return CommonScrollBar(
@@ -484,7 +492,7 @@ class _ListHeaderState extends State<ListHeader>
return CommonCard(
enterAnimated: widget.enterAnimated,
key: widget.key,
radius: 14,
radius: 16.ap,
type: CommonCardType.filled,
child: Padding(
padding: const EdgeInsets.symmetric(
@@ -587,7 +595,10 @@ class _ListHeaderState extends State<ListHeader>
const SizedBox(
width: 6,
),
],
] else
SizedBox(
width: 4,
),
AnimatedBuilder(
animation: _animationController.view,
builder: (_, __) {

View File

@@ -2,10 +2,12 @@ import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/fragments/proxies/list.dart';
import 'package:fl_clash/fragments/proxies/providers.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'common.dart';
import 'setting.dart';
import 'tab.dart';
@@ -25,70 +27,98 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
@override
get actions => [
if (_hasProviders)
IconButton(
onPressed: () {
showExtend(
context,
builder: (_, type) {
return ProvidersView(
type: type,
);
},
);
},
icon: const Icon(
Icons.poll_outlined,
),
),
_isTab
? IconButton(
CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: () {
open(
offset: Offset(0, 20),
);
},
icon: Icon(
Icons.more_vert,
),
);
},
popup: CommonPopupMenu(
minWidth: 180,
items: [
PopupMenuItemData(
icon: Icons.tune,
label: appLocalizations.settings,
onPressed: () {
_proxiesTabKey.currentState?.scrollToGroupSelected();
},
icon: const Icon(
Icons.adjust_outlined,
),
)
: IconButton(
onPressed: () {
showExtend(
context,
showSheet(
context: context,
props: SheetProps(
isScrollControlled: true,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const _IconConfigView(),
title: appLocalizations.iconConfiguration,
body: const ProxiesSetting(),
title: appLocalizations.settings,
);
},
);
},
icon: const Icon(
Icons.style_outlined,
),
if (_hasProviders)
PopupMenuItemData(
icon: Icons.poll_outlined,
label: appLocalizations.providers,
onPressed: () {
showExtend(
context,
builder: (_, type) {
return ProvidersView(
type: type,
);
},
);
},
),
),
IconButton(
onPressed: () {
showSheet(
context: context,
props: SheetProps(
isScrollControlled: true,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const ProxiesSetting(),
title: appLocalizations.settings,
);
},
);
},
icon: const Icon(
Icons.tune,
_isTab
? PopupMenuItemData(
icon: Icons.adjust_outlined,
label: "聚焦",
onPressed: () {
_proxiesTabKey.currentState?.scrollToGroupSelected();
},
)
: PopupMenuItemData(
icon: Icons.style_outlined,
label: appLocalizations.iconConfiguration,
onPressed: () {
showExtend(
context,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const _IconConfigView(),
title: appLocalizations.iconConfiguration,
);
},
);
},
),
],
),
)
];
@override
get onSearch => (value) {
ref.read(proxiesQueryProvider.notifier).value = value;
};
@override
void dispose() {
super.dispose();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(proxiesQueryProvider.notifier).value = "";
});
}
@override
get floatingActionButton => _isTab
? DelayTestButton(
@@ -103,6 +133,70 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
@override
void initState() {
[
if (_hasProviders)
IconButton(
onPressed: () {
showExtend(
context,
builder: (_, type) {
return ProvidersView(
type: type,
);
},
);
},
icon: const Icon(
Icons.poll_outlined,
),
),
_isTab
? IconButton(
onPressed: () {
_proxiesTabKey.currentState?.scrollToGroupSelected();
},
icon: const Icon(
Icons.adjust_outlined,
),
)
: IconButton(
onPressed: () {
showExtend(
context,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const _IconConfigView(),
title: appLocalizations.iconConfiguration,
);
},
);
},
icon: const Icon(
Icons.style_outlined,
),
),
IconButton(
onPressed: () {
showSheet(
context: context,
props: SheetProps(
isScrollControlled: true,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const ProxiesSetting(),
title: appLocalizations.settings,
);
},
);
},
icon: const Icon(
Icons.tune,
),
)
];
ref.listenManual(
proxiesActionsStateProvider,
fireImmediately: true,
@@ -128,8 +222,6 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
(state) => state.type,
),
);
ref.watch(themeSettingProvider.select((state) => state.textScale));
return switch (proxiesType) {
ProxiesType.tab => ProxiesTabFragment(
key: _proxiesTabKey,
@@ -144,8 +236,9 @@ class _IconConfigView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final iconMap =
ref.watch(proxiesStyleSettingProvider.select((state) => state.iconMap));
final iconMap = ref.watch(proxiesStyleSettingProvider.select(
(state) => state.iconMap,
));
return MapInputPage(
title: appLocalizations.iconConfiguration,
map: iconMap,

View File

@@ -164,7 +164,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
if (prev == next) {
return;
}
if (prev?.groupNames.length != next.groupNames.length) {
if (!stringListEquality.equals(prev?.groupNames, next.groupNames)) {
_destroyTabController();
final index = next.groupNames.indexWhere(
(item) => item == next.currentGroupName,
@@ -178,6 +178,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
@override
Widget build(BuildContext context) {
ref.watch(themeSettingProvider.select((state) => state.textScale));
final state = ref.watch(groupNamesStateProvider);
final groupNames = state.groupNames;
if (groupNames.isEmpty) {

View File

@@ -21,9 +21,6 @@ import 'common/common.dart';
Future<void> main() async {
globalState.isService = false;
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
commonPrint.log(details.stack.toString());
};
final version = await system.version;
await clashCore.preload();
await globalState.initApp(version);

View File

@@ -32,6 +32,15 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
}
});
});
ref.listenManual(
checkIpProvider,
(prev, next) {
if (prev != next && next.b) {
detectionState.startCheck();
}
},
fireImmediately: true,
);
}
@override

View File

@@ -61,7 +61,7 @@ class _ClashContainerState extends ConsumerState<ClashManager>
final appController = globalState.appController;
appController.setDelay(delay);
debouncer.call(
DebounceTag.updateDelay,
FunctionTag.updateDelay,
() async {
await appController.updateGroupsDebounce();
},

View File

@@ -8,7 +8,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
class HotKeyManager extends StatelessWidget {
class HotKeyManager extends ConsumerStatefulWidget {
final Widget child;
const HotKeyManager({
@@ -16,6 +16,25 @@ class HotKeyManager extends StatelessWidget {
required this.child,
});
@override
ConsumerState<HotKeyManager> createState() => _HotKeyManagerState();
}
class _HotKeyManagerState extends ConsumerState<HotKeyManager> {
@override
void initState() {
super.initState();
ref.listenManual(
hotKeyActionsProvider,
(prev, next) {
if (!hotKeyActionListEquality.equals(prev, next)) {
_updateHotKeys(hotKeyActions: next);
}
},
fireImmediately: true,
);
}
_handleHotKeyAction(HotAction action) async {
switch (action) {
case HotAction.mode:
@@ -59,22 +78,30 @@ class HotKeyManager extends StatelessWidget {
await Future.wait(hotkeyActionHandles);
}
_buildShortcuts(Widget child) {
return Shortcuts(
shortcuts: {
utils.controlSingleActivator(LogicalKeyboardKey.keyW):
CloseWindowIntent(),
},
child: Actions(
actions: {
CloseWindowIntent: CallbackAction<CloseWindowIntent>(
onInvoke: (_) => globalState.appController.handleBackOrExit(),
),
DoNothingIntent: CallbackAction<DoNothingIntent>(
onInvoke: (_) => null,
),
},
child: child,
),
);
}
@override
Widget build(BuildContext context) {
return Consumer(
builder: (_, ref, child) {
ref.listenManual(
hotKeyActionsProvider,
(prev, next) {
if (!hotKeyActionListEquality.equals(prev, next)) {
_updateHotKeys(hotKeyActions: next);
}
},
fireImmediately: true,
);
return child!;
},
child: child,
return _buildShortcuts(
widget.child,
);
}
}

View File

@@ -38,7 +38,7 @@ class ThemeManager extends ConsumerWidget {
textScaleFactor,
),
padding: padding.copyWith(
top: padding.top > height * 0.3 ? 0.0 : padding.top,
top: padding.top > height * 0.3 ? 20.0 : padding.top,
),
),
child: LayoutBuilder(

View File

@@ -29,7 +29,7 @@ class _VpnContainerState extends ConsumerState<VpnManager> {
showTip() {
debouncer.call(
DebounceTag.vpnTip,
FunctionTag.vpnTip,
() {
if (ref.read(runTimeProvider.notifier).isStart) {
globalState.showNotifier(

View File

@@ -38,7 +38,7 @@ class _WindowContainerState extends ConsumerState<WindowManager>
(prev, next) {
if (prev != next) {
debouncer.call(
DebounceTag.autoLaunch,
FunctionTag.autoLaunch,
() {
autoLaunch?.updateStatus(next);
},
@@ -103,7 +103,7 @@ class _WindowContainerState extends ConsumerState<WindowManager>
@override
Future<void> onTaskbarCreated() async {
globalState.appController.updateTray(true);
// globalState.appController.updateTray(true);
super.onTaskbarCreated();
}

View File

@@ -14,6 +14,7 @@ typedef DelayMap = Map<String, Map<String, int?>>;
class AppState with _$AppState {
const factory AppState({
@Default(false) bool isInit,
@Default(false) bool backBlock,
@Default(PageLabel.dashboard) PageLabel pageLabel,
@Default([]) List<Package> packages,
@Default(0) int sortNum,
@@ -30,6 +31,7 @@ class AppState with _$AppState {
required FixedList<Log> logs,
required FixedList<Traffic> traffics,
required Traffic totalTraffic,
@Default("") String proxiesQuery,
@Default(false) bool needApply,
}) = _AppState;
}

View File

@@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/common.freezed.dart';
part 'generated/common.g.dart';
@freezed
@@ -129,10 +128,10 @@ extension LogsStateExt on LogsState {
final lowQuery = query.toLowerCase();
return logs.where(
(log) {
final payload = log.payload.toLowerCase();
final logLevelName = log.logLevel.name;
return {logLevelName}.containsAll(keywords) &&
((payload.contains(lowQuery)) || logLevelName.contains(lowQuery));
((log.payload.toLowerCase().contains(lowQuery)) ||
logLevelName.contains(lowQuery));
},
).toList();
}
@@ -504,15 +503,11 @@ class PopupMenuItemData {
this.icon,
required this.label,
required this.onPressed,
this.type,
this.iconSize,
});
final double? iconSize;
final String label;
final VoidCallback? onPressed;
final IconData? icon;
final PopupMenuItemType? type;
}
@freezed
@@ -528,3 +523,7 @@ class TextPainterParams with _$TextPainterParams {
factory TextPainterParams.fromJson(Map<String, Object?> json) =>
_$TextPainterParamsFromJson(json);
}
class CloseWindowIntent extends Intent {
const CloseWindowIntent();
}

View File

@@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$AppState {
bool get isInit => throw _privateConstructorUsedError;
bool get backBlock => throw _privateConstructorUsedError;
PageLabel get pageLabel => throw _privateConstructorUsedError;
List<Package> get packages => throw _privateConstructorUsedError;
int get sortNum => throw _privateConstructorUsedError;
@@ -34,6 +35,7 @@ mixin _$AppState {
FixedList<Log> get logs => throw _privateConstructorUsedError;
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
Traffic get totalTraffic => throw _privateConstructorUsedError;
String get proxiesQuery => throw _privateConstructorUsedError;
bool get needApply => throw _privateConstructorUsedError;
/// Create a copy of AppState
@@ -50,6 +52,7 @@ abstract class $AppStateCopyWith<$Res> {
@useResult
$Res call(
{bool isInit,
bool backBlock,
PageLabel pageLabel,
List<Package> packages,
int sortNum,
@@ -66,6 +69,7 @@ abstract class $AppStateCopyWith<$Res> {
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery,
bool needApply});
}
@@ -85,6 +89,7 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
@override
$Res call({
Object? isInit = null,
Object? backBlock = null,
Object? pageLabel = null,
Object? packages = null,
Object? sortNum = null,
@@ -101,6 +106,7 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
Object? logs = null,
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? needApply = null,
}) {
return _then(_value.copyWith(
@@ -108,6 +114,10 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
backBlock: null == backBlock
? _value.backBlock
: backBlock // ignore: cast_nullable_to_non_nullable
as bool,
pageLabel: null == pageLabel
? _value.pageLabel
: pageLabel // ignore: cast_nullable_to_non_nullable
@@ -172,6 +182,10 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.totalTraffic
: totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,
proxiesQuery: null == proxiesQuery
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
needApply: null == needApply
? _value.needApply
: needApply // ignore: cast_nullable_to_non_nullable
@@ -190,6 +204,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
@useResult
$Res call(
{bool isInit,
bool backBlock,
PageLabel pageLabel,
List<Package> packages,
int sortNum,
@@ -206,6 +221,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery,
bool needApply});
}
@@ -223,6 +239,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
@override
$Res call({
Object? isInit = null,
Object? backBlock = null,
Object? pageLabel = null,
Object? packages = null,
Object? sortNum = null,
@@ -239,6 +256,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
Object? logs = null,
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? needApply = null,
}) {
return _then(_$AppStateImpl(
@@ -246,6 +264,10 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
backBlock: null == backBlock
? _value.backBlock
: backBlock // ignore: cast_nullable_to_non_nullable
as bool,
pageLabel: null == pageLabel
? _value.pageLabel
: pageLabel // ignore: cast_nullable_to_non_nullable
@@ -310,6 +332,10 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value.totalTraffic
: totalTraffic // ignore: cast_nullable_to_non_nullable
as Traffic,
proxiesQuery: null == proxiesQuery
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
needApply: null == needApply
? _value.needApply
: needApply // ignore: cast_nullable_to_non_nullable
@@ -323,6 +349,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
class _$AppStateImpl implements _AppState {
const _$AppStateImpl(
{this.isInit = false,
this.backBlock = false,
this.pageLabel = PageLabel.dashboard,
final List<Package> packages = const [],
this.sortNum = 0,
@@ -339,6 +366,7 @@ class _$AppStateImpl implements _AppState {
required this.logs,
required this.traffics,
required this.totalTraffic,
this.proxiesQuery = "",
this.needApply = false})
: _packages = packages,
_delayMap = delayMap,
@@ -350,6 +378,9 @@ class _$AppStateImpl implements _AppState {
final bool isInit;
@override
@JsonKey()
final bool backBlock;
@override
@JsonKey()
final PageLabel pageLabel;
final List<Package> _packages;
@override
@@ -413,11 +444,14 @@ class _$AppStateImpl implements _AppState {
final Traffic totalTraffic;
@override
@JsonKey()
final String proxiesQuery;
@override
@JsonKey()
final bool needApply;
@override
String toString() {
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, needApply: $needApply)';
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, proxiesQuery: $proxiesQuery, needApply: $needApply)';
}
@override
@@ -426,6 +460,8 @@ class _$AppStateImpl implements _AppState {
(other.runtimeType == runtimeType &&
other is _$AppStateImpl &&
(identical(other.isInit, isInit) || other.isInit == isInit) &&
(identical(other.backBlock, backBlock) ||
other.backBlock == backBlock) &&
(identical(other.pageLabel, pageLabel) ||
other.pageLabel == pageLabel) &&
const DeepCollectionEquality().equals(other._packages, _packages) &&
@@ -450,31 +486,36 @@ class _$AppStateImpl implements _AppState {
other.traffics == traffics) &&
(identical(other.totalTraffic, totalTraffic) ||
other.totalTraffic == totalTraffic) &&
(identical(other.proxiesQuery, proxiesQuery) ||
other.proxiesQuery == proxiesQuery) &&
(identical(other.needApply, needApply) ||
other.needApply == needApply));
}
@override
int get hashCode => Object.hash(
runtimeType,
isInit,
pageLabel,
const DeepCollectionEquality().hash(_packages),
sortNum,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
brightness,
runTime,
const DeepCollectionEquality().hash(_providers),
localIp,
requests,
version,
logs,
traffics,
totalTraffic,
needApply);
int get hashCode => Object.hashAll([
runtimeType,
isInit,
backBlock,
pageLabel,
const DeepCollectionEquality().hash(_packages),
sortNum,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
brightness,
runTime,
const DeepCollectionEquality().hash(_providers),
localIp,
requests,
version,
logs,
traffics,
totalTraffic,
proxiesQuery,
needApply
]);
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@@ -488,6 +529,7 @@ class _$AppStateImpl implements _AppState {
abstract class _AppState implements AppState {
const factory _AppState(
{final bool isInit,
final bool backBlock,
final PageLabel pageLabel,
final List<Package> packages,
final int sortNum,
@@ -504,11 +546,14 @@ abstract class _AppState implements AppState {
required final FixedList<Log> logs,
required final FixedList<Traffic> traffics,
required final Traffic totalTraffic,
final String proxiesQuery,
final bool needApply}) = _$AppStateImpl;
@override
bool get isInit;
@override
bool get backBlock;
@override
PageLabel get pageLabel;
@override
List<Package> get packages;
@@ -541,6 +586,8 @@ abstract class _AppState implements AppState {
@override
Traffic get totalTraffic;
@override
String get proxiesQuery;
@override
bool get needApply;
/// Create a copy of AppState

View File

@@ -2142,6 +2142,7 @@ mixin _$ProxiesListSelectorState {
ProxyCardType get proxyCardType => throw _privateConstructorUsedError;
num get sortNum => throw _privateConstructorUsedError;
int get columns => throw _privateConstructorUsedError;
String get query => throw _privateConstructorUsedError;
/// Create a copy of ProxiesListSelectorState
/// with the given fields replaced by the non-null parameter values.
@@ -2162,7 +2163,8 @@ abstract class $ProxiesListSelectorStateCopyWith<$Res> {
ProxiesSortType proxiesSortType,
ProxyCardType proxyCardType,
num sortNum,
int columns});
int columns,
String query});
}
/// @nodoc
@@ -2187,6 +2189,7 @@ class _$ProxiesListSelectorStateCopyWithImpl<$Res,
Object? proxyCardType = null,
Object? sortNum = null,
Object? columns = null,
Object? query = null,
}) {
return _then(_value.copyWith(
groupNames: null == groupNames
@@ -2213,6 +2216,10 @@ class _$ProxiesListSelectorStateCopyWithImpl<$Res,
? _value.columns
: columns // ignore: cast_nullable_to_non_nullable
as int,
query: null == query
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
@@ -2232,7 +2239,8 @@ abstract class _$$ProxiesListSelectorStateImplCopyWith<$Res>
ProxiesSortType proxiesSortType,
ProxyCardType proxyCardType,
num sortNum,
int columns});
int columns,
String query});
}
/// @nodoc
@@ -2256,6 +2264,7 @@ class __$$ProxiesListSelectorStateImplCopyWithImpl<$Res>
Object? proxyCardType = null,
Object? sortNum = null,
Object? columns = null,
Object? query = null,
}) {
return _then(_$ProxiesListSelectorStateImpl(
groupNames: null == groupNames
@@ -2282,6 +2291,10 @@ class __$$ProxiesListSelectorStateImplCopyWithImpl<$Res>
? _value.columns
: columns // ignore: cast_nullable_to_non_nullable
as int,
query: null == query
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
@@ -2295,7 +2308,8 @@ class _$ProxiesListSelectorStateImpl implements _ProxiesListSelectorState {
required this.proxiesSortType,
required this.proxyCardType,
required this.sortNum,
required this.columns})
required this.columns,
required this.query})
: _groupNames = groupNames,
_currentUnfoldSet = currentUnfoldSet;
@@ -2323,10 +2337,12 @@ class _$ProxiesListSelectorStateImpl implements _ProxiesListSelectorState {
final num sortNum;
@override
final int columns;
@override
final String query;
@override
String toString() {
return 'ProxiesListSelectorState(groupNames: $groupNames, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';
return 'ProxiesListSelectorState(groupNames: $groupNames, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns, query: $query)';
}
@override
@@ -2343,7 +2359,8 @@ class _$ProxiesListSelectorStateImpl implements _ProxiesListSelectorState {
(identical(other.proxyCardType, proxyCardType) ||
other.proxyCardType == proxyCardType) &&
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
(identical(other.columns, columns) || other.columns == columns));
(identical(other.columns, columns) || other.columns == columns) &&
(identical(other.query, query) || other.query == query));
}
@override
@@ -2354,7 +2371,8 @@ class _$ProxiesListSelectorStateImpl implements _ProxiesListSelectorState {
proxiesSortType,
proxyCardType,
sortNum,
columns);
columns,
query);
/// Create a copy of ProxiesListSelectorState
/// with the given fields replaced by the non-null parameter values.
@@ -2373,7 +2391,8 @@ abstract class _ProxiesListSelectorState implements ProxiesListSelectorState {
required final ProxiesSortType proxiesSortType,
required final ProxyCardType proxyCardType,
required final num sortNum,
required final int columns}) = _$ProxiesListSelectorStateImpl;
required final int columns,
required final String query}) = _$ProxiesListSelectorStateImpl;
@override
List<String> get groupNames;
@@ -2387,6 +2406,8 @@ abstract class _ProxiesListSelectorState implements ProxiesListSelectorState {
num get sortNum;
@override
int get columns;
@override
String get query;
/// Create a copy of ProxiesListSelectorState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -25,7 +25,7 @@ class VM3<A, B, C> with _$VM3<A, B, C> {
}
@freezed
class VM4<A, B, C,D> with _$VM4<A, B, C,D> {
class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
const factory VM4({
required A a,
required B b,
@@ -34,7 +34,6 @@ class VM4<A, B, C,D> with _$VM4<A, B, C,D> {
}) = _VM4;
}
@freezed
class StartButtonSelectorState with _$StartButtonSelectorState {
const factory StartButtonSelectorState({
@@ -125,6 +124,7 @@ class ProxiesListSelectorState with _$ProxiesListSelectorState {
required ProxyCardType proxyCardType,
required num sortNum,
required int columns,
required String query,
}) = _ProxiesListSelectorState;
}

View File

@@ -122,12 +122,15 @@ class _EditorPageState extends ConsumerState<EditorPage> {
(value) => CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: open,
onPressed: () {
open(
offset: Offset(0, 20),
);
},
icon: const Icon(Icons.more_vert),
);
},
popup: CommonPopupMenu(
minWidth: 180,
items: [
PopupMenuItemData(
icon: Icons.search,
@@ -151,6 +154,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
],
body: CodeEditor(
findController: _findController,
maxLengthSingleLineRendering: 200,
findBuilder: (context, controller, readOnly) => FindPanel(
controller: controller,
readOnly: readOnly,
@@ -190,7 +194,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
controller: _controller,
style: CodeEditorStyle(
fontSize: 14.ap,
fontSize: context.textTheme.bodyLarge?.fontSize?.ap,
fontFamily: FontFamily.jetBrainsMono.value,
codeTheme: CodeHighlightTheme(
languages: {

View File

@@ -286,6 +286,21 @@ class CheckIpNum extends _$CheckIpNum with AutoDisposeNotifierMixin {
add() => state++;
}
@riverpod
class BackBlock extends _$BackBlock with AutoDisposeNotifierMixin {
@override
bool build() {
return globalState.appState.backBlock;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
backBlock: value,
);
}
}
@riverpod
class Version extends _$Version with AutoDisposeNotifierMixin {
@override
@@ -356,3 +371,18 @@ class NeedApply extends _$NeedApply with AutoDisposeNotifierMixin {
);
}
}
@riverpod
class ProxiesQuery extends _$ProxiesQuery with AutoDisposeNotifierMixin {
@override
String build() {
return globalState.appState.proxiesQuery;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
proxiesQuery: value,
);
}
}

View File

@@ -276,6 +276,20 @@ final checkIpNumProvider =
);
typedef _$CheckIpNum = AutoDisposeNotifier<int>;
String _$backBlockHash() => r'c0223e0776b72d3a8c8842fc32fdb5287353999f';
/// See also [BackBlock].
@ProviderFor(BackBlock)
final backBlockProvider = AutoDisposeNotifierProvider<BackBlock, bool>.internal(
BackBlock.new,
name: r'backBlockProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$backBlockHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$BackBlock = AutoDisposeNotifier<bool>;
String _$versionHash() => r'8c0ee019d20df3f112c38ae4dc4abd61148d3809';
/// See also [Version].
@@ -335,5 +349,20 @@ final needApplyProvider = AutoDisposeNotifierProvider<NeedApply, bool>.internal(
);
typedef _$NeedApply = AutoDisposeNotifier<bool>;
String _$proxiesQueryHash() => r'9f3907e06534b6882684bec47ca3ba2988297e19';
/// See also [ProxiesQuery].
@ProviderFor(ProxiesQuery)
final proxiesQueryProvider =
AutoDisposeNotifierProvider<ProxiesQuery, String>.internal(
ProxiesQuery.new,
name: r'proxiesQueryProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$proxiesQueryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ProxiesQuery = AutoDisposeNotifier<String>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -236,7 +236,7 @@ final profilesSelectorStateProvider =
typedef ProfilesSelectorStateRef
= AutoDisposeProviderRef<ProfilesSelectorState>;
String _$proxiesListSelectorStateHash() =>
r'0e63ea2fb141e086156a2ed8452584e2375c5aa5';
r'5e6bbe1a0cecbdea6c9c62e6ccf314968deac264';
/// See also [proxiesListSelectorState].
@ProviderFor(proxiesListSelectorState)
@@ -292,7 +292,7 @@ final groupNamesStateProvider = AutoDisposeProvider<GroupNamesState>.internal(
// ignore: unused_element
typedef GroupNamesStateRef = AutoDisposeProviderRef<GroupNamesState>;
String _$proxyGroupSelectorStateHash() =>
r'5bc86d13286c6c859f0b874235a281122cc612ba';
r'50940ff452859b02af0095cb7c4bcda813847645';
/// Copied from Dart SDK
class _SystemHash {
@@ -1781,6 +1781,22 @@ final layoutChangeProvider = AutoDisposeProvider<VM2?>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef LayoutChangeRef = AutoDisposeProviderRef<VM2?>;
String _$checkIpHash() => r'07ebf8d032349e2b3adda483e68b1936ffbed68d';
/// See also [checkIp].
@ProviderFor(checkIp)
final checkIpProvider = AutoDisposeProvider<VM2<int, bool>>.internal(
checkIp,
name: r'checkIpProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$checkIpHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef CheckIpRef = AutoDisposeProviderRef<VM2<int, bool>>;
String _$genColorSchemeHash() => r'b18f15c938a8132ee4ed02cdfc02f3b9f01724e2';
/// See also [genColorScheme].

View File

@@ -210,7 +210,10 @@ ProfilesSelectorState profilesSelectorState(Ref ref) {
final currentProfileId = ref.watch(currentProfileIdProvider);
final profiles = ref.watch(profilesProvider);
final columns = ref.watch(
viewWidthProvider.select((state) => utils.getProfilesColumns(state)));
viewWidthProvider.select(
(state) => utils.getProfilesColumns(state),
),
);
return ProfilesSelectorState(
profiles: profiles,
currentProfileId: currentProfileId,
@@ -227,6 +230,8 @@ ProxiesListSelectorState proxiesListSelectorState(Ref ref) {
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
final query =
ref.watch(proxiesQueryProvider.select((state) => state.toLowerCase()));
return ProxiesListSelectorState(
groupNames: groupNames,
currentUnfoldSet: currentUnfoldSet,
@@ -234,6 +239,7 @@ ProxiesListSelectorState proxiesListSelectorState(Ref ref) {
proxyCardType: proxiesStyle.cardType,
sortNum: sortNum,
columns: columns,
query: query,
);
}
@@ -280,13 +286,19 @@ ProxyGroupSelectorState proxyGroupSelectorState(Ref ref, String groupName) {
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
final query =
ref.watch(proxiesQueryProvider.select((state) => state.toLowerCase()));
final proxies = group?.all.where((item) {
return item.name.contains(query);
}).toList() ??
[];
return ProxyGroupSelectorState(
testUrl: group?.testUrl,
proxiesSortType: proxiesStyle.sortType,
proxyCardType: proxiesStyle.cardType,
sortNum: sortNum,
groupType: group?.type ?? GroupType.Selector,
proxies: group?.all ?? [],
proxies: proxies,
columns: columns,
);
}
@@ -521,6 +533,21 @@ VM2? layoutChange(Ref ref) {
);
}
@riverpod
VM2<int, bool> checkIp(Ref ref) {
final checkIpNum = ref.watch(checkIpNumProvider);
final containsDetection = ref.watch(
dashboardStateProvider.select(
(state) =>
state.dashboardWidgets.contains(DashboardWidget.networkDetection),
),
);
return VM2(
a: checkIpNum,
b: containsDetection,
);
}
@riverpod
ColorScheme genColorScheme(
Ref ref,

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:animations/animations.dart';
import 'package:dio/dio.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/theme.dart';
@@ -289,3 +290,94 @@ class GlobalState {
}
final globalState = GlobalState();
class DetectionState {
static DetectionState? _instance;
bool? _preIsStart;
Timer? _setTimeoutTimer;
CancelToken? cancelToken;
final state = ValueNotifier<NetworkDetectionState>(
const NetworkDetectionState(
isTesting: false,
isLoading: true,
ipInfo: null,
),
);
DetectionState._internal();
factory DetectionState() {
_instance ??= DetectionState._internal();
return _instance!;
}
startCheck() {
debouncer.call(
FunctionTag.checkIp,
_checkIp,
);
}
_checkIp() async {
final appState = globalState.appState;
final isInit = appState.isInit;
if (!isInit) return;
final isStart = appState.runTime != null;
if (_preIsStart == false &&
_preIsStart == isStart &&
state.value.ipInfo != null) {
return;
}
_clearSetTimeoutTimer();
state.value = state.value.copyWith(
isLoading: true,
ipInfo: null,
);
_preIsStart = isStart;
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
}
cancelToken = CancelToken();
try {
state.value = state.value.copyWith(
isTesting: true,
);
final ipInfo = await request.checkIp(cancelToken: cancelToken);
state.value = state.value.copyWith(
isTesting: false,
);
if (ipInfo != null) {
state.value = state.value.copyWith(
isLoading: false,
ipInfo: ipInfo,
);
return;
}
_clearSetTimeoutTimer();
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
state.value = state.value.copyWith(
isLoading: false,
ipInfo: null,
);
});
} catch (e) {
if (e.toString() == "cancelled") {
state.value = state.value.copyWith(
isLoading: true,
ipInfo: null,
);
}
}
}
_clearSetTimeoutTimer() {
if (_setTimeoutTimer != null) {
_setTimeoutTimer?.cancel();
_setTimeoutTimer = null;
}
}
}
final detectionState = DetectionState();

View File

@@ -10,7 +10,7 @@ import 'card.dart';
import 'float_layout.dart';
import 'list.dart';
class OptionsDialog<T> extends StatefulWidget {
class OptionsDialog<T> extends StatelessWidget {
final String title;
final List<T> options;
final T value;
@@ -24,48 +24,35 @@ class OptionsDialog<T> extends StatefulWidget {
required this.value,
});
@override
State<OptionsDialog<T>> createState() => _OptionsDialogState();
}
class _OptionsDialogState<T> extends State<OptionsDialog<T>> {
final _defaultValue = "";
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final context =
GlobalObjectKey(widget.value ?? _defaultValue).currentContext;
if (context != null) {
Scrollable.ensureVisible(context);
}
});
}
@override
Widget build(BuildContext context) {
return CommonDialog(
title: widget.title,
title: title,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
child: Wrap(
children: [
for (final option in widget.options)
ListItem.radio(
key: GlobalObjectKey(option ?? _defaultValue),
delegate: RadioDelegate(
value: option,
groupValue: widget.value,
onChanged: (T? value) {
Navigator.of(context).pop(value);
},
),
title: Text(
widget.textBuilder(option),
),
for (final option in options)
Builder(
builder: (context) {
if (value == option) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(context);
});
}
return ListItem.radio(
delegate: RadioDelegate(
value: option,
groupValue: value,
onChanged: (T? value) {
Navigator.of(context).pop(value);
},
),
title: Text(textBuilder(option)),
);
},
),
],
),

View File

@@ -1,4 +1,6 @@
import 'dart:async';
import 'package:fl_clash/state.dart';
import 'package:flutter/widgets.dart';
class CommonPopScope extends StatelessWidget {
@@ -34,3 +36,38 @@ class CommonPopScope extends StatelessWidget {
);
}
}
class SystemBackBlock extends StatefulWidget {
final Widget child;
const SystemBackBlock({
super.key,
required this.child,
});
@override
State<SystemBackBlock> createState() => _SystemBackBlockState();
}
class _SystemBackBlockState extends State<SystemBackBlock> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
globalState.appController.backBlock();
});
}
@override
void dispose() {
super.dispose();
WidgetsBinding.instance.addPostFrameCallback((_) {
globalState.appController.unBackBlock();
});
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}

View File

@@ -201,12 +201,16 @@ class OverflowAwareLayoutDelegate extends SingleChildLayoutDelegate {
class CommonPopupMenu extends StatelessWidget {
final List<PopupMenuItemData> items;
final double? minWidth;
final double minWidth;
final double minItemVerticalPadding;
final double fontSize;
const CommonPopupMenu({
super.key,
required this.items,
this.minWidth,
this.minWidth = 160,
this.minItemVerticalPadding = 16,
this.fontSize = 15,
});
Widget _popupMenuItem(
@@ -214,16 +218,11 @@ class CommonPopupMenu extends StatelessWidget {
required PopupMenuItemData item,
required int index,
}) {
final isDanger = item.type == PopupMenuItemType.danger;
final onPressed = item.onPressed;
final disabled = onPressed == null;
final color = isDanger
? disabled
? context.colorScheme.error.opacity30
: context.colorScheme.error
: disabled
? context.colorScheme.onSurface.opacity30
: context.colorScheme.onSurface;
final color = disabled
? context.colorScheme.onSurface.opacity30
: context.colorScheme.onSurface;
return InkWell(
onTap: onPressed != null
? () {
@@ -233,13 +232,13 @@ class CommonPopupMenu extends StatelessWidget {
: null,
child: Container(
constraints: BoxConstraints(
minWidth: minWidth ?? 120,
minWidth: minWidth,
),
padding: EdgeInsets.only(
left: 16,
right: 64,
top: 14,
bottom: 14,
top: minItemVerticalPadding,
bottom: minItemVerticalPadding,
),
child: Row(
mainAxisSize: MainAxisSize.max,
@@ -247,7 +246,7 @@ class CommonPopupMenu extends StatelessWidget {
if (item.icon != null) ...[
Icon(
item.icon,
size: item.iconSize ?? 18,
size: fontSize + 4,
color: color,
),
SizedBox(
@@ -259,6 +258,7 @@ class CommonPopupMenu extends StatelessWidget {
item.label,
style: context.textTheme.bodyMedium?.copyWith(
color: color,
fontSize: fontSize,
),
),
),

View File

@@ -305,20 +305,24 @@ class CommonScaffoldState extends State<CommonScaffold> {
);
}
Widget _buildAppBarWrap(Widget appBar) {
if (_isEdit) {
return CommonPopScope(
onPop: () {
if (_isEdit) {
_appBarState.value.editState?.onExit();
return false;
}
return true;
},
child: appBar,
Widget _buildAppBarWrap(Widget child) {
final appBar = _isSearch ? _buildSearchingAppBarTheme(child) : child;
if (_isEdit || _isSearch) {
return SystemBackBlock(
child: CommonPopScope(
onPop: () {
if (_isEdit || _isSearch) {
_handleExitSearching();
_appBarState.value.editState?.onExit();
return false;
}
return true;
},
child: appBar,
),
);
}
return _isSearch ? _buildSearchingAppBarTheme(appBar) : appBar;
return appBar;
}
PreferredSizeWidget _buildAppBar() {
@@ -431,6 +435,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
final scaffold = Scaffold(
appBar: _buildAppBar(),
body: body,
resizeToAvoidBottomInset: true,
backgroundColor: widget.backgroundColor,
floatingActionButton: ValueListenableBuilder<Widget?>(
valueListenable: _floatingActionButton,

View File

@@ -56,6 +56,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
final ValueNotifier<bool> _animating = ValueNotifier(false);
final _dragWidgetSizeNotifier = ValueNotifier(Size.zero);
final _dragIndexNotifier = ValueNotifier(-1);
late AnimationController _transformController;
@@ -304,7 +305,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
}
_handleDragEnd(DraggableDetails details) async {
debouncer.cancel(DebounceTag.handleWill);
debouncer.cancel(FunctionTag.handleWill);
if (_targetIndex == -1) {
return;
}
@@ -313,7 +314,12 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
stiffness: 100,
damping: 10,
);
final simulation = SpringSimulation(spring, 0, 1, 0);
final simulation = SpringSimulation(
spring,
0,
1,
0,
);
_fakeDragWidgetAnimation = Tween(
begin: details.offset - _parentOffset,
end: _targetOffset,
@@ -369,7 +375,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
}
_handleDelete(int index) async {
await _transformCompleter?.future;
_preTransformState();
final indexWhere = _tempIndexList.indexWhere((i) => i == index);
_tempIndexList.removeAt(indexWhere);
@@ -496,7 +501,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
},
onWillAcceptWithDetails: (_) {
debouncer.call(
DebounceTag.handleWill,
FunctionTag.handleWill,
_handleWill,
args: [index],
);
@@ -504,21 +509,31 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
},
);
final shakeTarget = ValueListenableBuilder(
valueListenable: _dragIndexNotifier,
builder: (_, dragIndex, child) {
if (dragIndex == index) {
valueListenable: _animating,
builder: (_, animating, child) {
if (animating) {
return target;
} else {
return child!;
}
return _shakeWrap(
_DeletableContainer(
onDelete: () {
_handleDelete(index);
},
child: child!,
),
);
},
child: target,
child: ValueListenableBuilder(
valueListenable: _dragIndexNotifier,
builder: (_, dragIndex, child) {
if (dragIndex == index) {
return child!;
}
return _shakeWrap(
_DeletableContainer(
onDelete: () {
_handleDelete(index);
},
child: child!,
),
);
},
child: target,
),
);
final draggableChild = system.isDesktop
? Draggable(

View File

@@ -9,6 +9,7 @@ import app_links
import connectivity_plus
import device_info_plus
import dynamic_color
import file_picker
import file_selector_macos
import hotkey_manager_macos
import mobile_scanner
@@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))

View File

@@ -2,12 +2,13 @@ PODS:
- app_links (1.0.0):
- FlutterMacOS
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- device_info_plus (0.0.1):
- FlutterMacOS
- dynamic_color (0.0.2):
- FlutterMacOS
- file_picker (0.0.1):
- FlutterMacOS
- file_selector_macos (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
@@ -15,7 +16,8 @@ PODS:
- hotkey_manager_macos (0.0.1):
- FlutterMacOS
- HotKey
- mobile_scanner (6.0.2):
- mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
@@ -41,13 +43,14 @@ PODS:
DEPENDENCIES:
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
@@ -66,11 +69,13 @@ EXTERNAL SOURCES:
app_links:
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
device_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
dynamic_color:
:path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos
file_picker:
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
file_selector_macos:
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
FlutterMacOS:
@@ -78,7 +83,7 @@ EXTERNAL SOURCES:
hotkey_manager_macos:
:path: Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos
mobile_scanner:
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
@@ -100,14 +105,15 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
app_links: afe860c55c7ef176cea7fb630a2b7d7736de591d
connectivity_plus: 2256d3e20624a7749ed21653aafe291a46446fee
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
dynamic_color: b820c000cc68df65e7ba7ff177cb98404ce56651
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6
hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe
mobile_scanner: 0e365ed56cad24f28c0fd858ca04edefb40dfac3
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f

View File

@@ -5,31 +5,26 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "80.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "7.3.0"
analyzer_plugin:
dependency: transitive
description:
name: analyzer_plugin
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
sha256: "1d460d14e3c2ae36dc2b32cef847c4479198cf87704f63c3c3c8150ee50c3916"
url: "https://pub.dev"
source: hosted
version: "0.11.3"
version: "0.12.0"
animations:
dependency: "direct main"
description:
@@ -82,10 +77,10 @@ packages:
dependency: "direct dev"
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.6.0"
version: "2.7.0"
async:
dependency: transitive
description:
@@ -106,50 +101,50 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.4"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "7.3.2"
version: "8.0.0"
built_collection:
dependency: transitive
description:
@@ -162,10 +157,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4
url: "https://pub.dev"
source: hosted
version: "8.9.2"
version: "8.9.5"
cached_network_image:
dependency: "direct main"
description:
@@ -250,10 +245,10 @@ packages:
dependency: "direct main"
description:
name: connectivity_plus
sha256: e0817759ec6d2d8e57eb234e6e57d2173931367a865850c7acea40d4b4f9c27d
sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
version: "6.1.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
@@ -290,50 +285,50 @@ packages:
dependency: "direct dev"
description:
name: custom_lint
sha256: "3486c470bb93313a9417f926c7dd694a2e349220992d7b9d14534dc49c15bba9"
sha256: "021897cce2b6c783b2521543e362e7fe1a2eaab17bf80514d8de37f99942ed9e"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.3"
custom_lint_builder:
dependency: transitive
description:
name: custom_lint_builder
sha256: "42cdc41994eeeddab0d7a722c7093ec52bd0761921eeb2cbdbf33d192a234759"
sha256: e4235b9d8cef59afe621eba086d245205c8a0a6c70cd470be7cb17494d6df32d
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.3"
custom_lint_core:
dependency: transitive
description:
name: custom_lint_core
sha256: "02450c3e45e2a6e8b26c4d16687596ab3c4644dd5792e3313aa9ceba5a49b7f5"
sha256: "6dcee8a017181941c51a110da7e267c1d104dc74bec8862eeb8c85b5c8759a9e"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.1"
custom_lint_visitor:
dependency: transitive
description:
name: custom_lint_visitor
sha256: bfe9b7a09c4775a587b58d10ebb871d4fe618237639b1e84d5ec62d7dfef25f9
sha256: "36282d85714af494ee2d7da8c8913630aa6694da99f104fb2ed4afcf8fc857d8"
url: "https://pub.dev"
source: hosted
version: "1.0.0+6.11.0"
version: "1.0.0+7.3.0"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
version: "3.0.1"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.10"
version: "0.7.11"
defer_pointer:
dependency: "direct main"
description:
@@ -346,10 +341,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513"
sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53"
url: "https://pub.dev"
source: hosted
version: "11.3.3"
version: "11.4.0"
device_info_plus_platform_interface:
dependency: transitive
description:
@@ -370,10 +365,10 @@ packages:
dependency: transitive
description:
name: dio_web_adapter
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.1.1"
dynamic_color:
dependency: "direct main"
description:
@@ -418,10 +413,10 @@ packages:
dependency: "direct dev"
description:
name: ffigen
sha256: a0ca4853028c6a9e4d9a0a40bb744fceb898c89d75931d08e87b3987d0087060
sha256: "72d732c33557fc0ca9b46379d3deff2dadbdc539696dc0b270189e2989be20ef"
url: "https://pub.dev"
source: hosted
version: "15.0.0"
version: "18.1.0"
file:
dependency: transitive
description:
@@ -434,10 +429,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: c2376a6aae82358a9f9ccdd7d1f4006d08faa39a2767cce01031d9f593a8bd3b
sha256: "8986dec4581b4bcd4b6df5d75a2ea0bede3db802f500635d05fa8be298f9467f"
url: "https://pub.dev"
source: hosted
version: "8.1.6"
version: "10.1.2"
file_selector_linux:
dependency: transitive
description:
@@ -466,10 +461,10 @@ packages:
dependency: transitive
description:
name: file_selector_windows
sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4"
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
url: "https://pub.dev"
source: hosted
version: "0.9.3+3"
version: "0.9.3+4"
fixnum:
dependency: transitive
description:
@@ -508,10 +503,10 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
url: "https://pub.dev"
source: hosted
version: "2.0.23"
version: "2.0.28"
flutter_riverpod:
dependency: "direct main"
description:
@@ -534,10 +529,10 @@ packages:
dependency: "direct dev"
description:
name: freezed
sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e"
sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c"
url: "https://pub.dev"
source: hosted
version: "2.5.7"
version: "2.5.8"
freezed_annotation:
dependency: "direct main"
description:
@@ -558,10 +553,10 @@ packages:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
graphs:
dependency: transitive
description:
@@ -630,26 +625,26 @@ packages:
dependency: transitive
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.2.2"
version: "1.3.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.2"
image_picker:
dependency: "direct main"
description:
@@ -662,10 +657,10 @@ packages:
dependency: transitive
description:
name: image_picker_android
sha256: fa8141602fde3f7e2f81dbf043613eb44dfa325fa0bcf93c0f142c9f7a2c193e
sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb"
url: "https://pub.dev"
source: hosted
version: "0.8.12+18"
version: "0.8.12+23"
image_picker_for_web:
dependency: transitive
description:
@@ -678,34 +673,34 @@ packages:
dependency: transitive
description:
name: image_picker_ios
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
url: "https://pub.dev"
source: hosted
version: "0.8.12+1"
version: "0.8.12+2"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
version: "0.2.1+2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
version: "0.2.1+2"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
version: "2.10.1"
image_picker_windows:
dependency: transitive
description:
@@ -750,10 +745,10 @@ packages:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.1"
version: "0.7.2"
json_annotation:
dependency: "direct main"
description:
@@ -766,10 +761,10 @@ packages:
dependency: "direct dev"
description:
name: json_serializable
sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c
sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
url: "https://pub.dev"
source: hosted
version: "6.9.0"
version: "6.9.5"
launch_at_startup:
dependency: "direct main"
description:
@@ -806,10 +801,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "5.1.1"
logging:
dependency: transitive
description:
@@ -826,14 +821,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
@@ -878,10 +865,10 @@ packages:
dependency: "direct main"
description:
name: mobile_scanner
sha256: "728828a798d1a2ee506beb652ca23d974c542c96ed03dcbd5eaf97bef96cdaad"
sha256: "72f06a071aa8b14acea3ab43ea7949eefe4a2469731ae210e006ba330a033a8c"
url: "https://pub.dev"
source: hosted
version: "6.0.2"
version: "7.0.0"
nm:
dependency: transitive
description:
@@ -902,26 +889,26 @@ packages:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: "70c421fe9d9cc1a9a7f3b05ae56befd469fe4f8daa3b484823141a55442d858d"
sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191"
url: "https://pub.dev"
source: hosted
version: "8.1.2"
version: "8.3.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b
sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.2.0"
path:
dependency: "direct main"
description:
@@ -942,10 +929,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.15"
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
@@ -982,10 +969,10 @@ packages:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
url: "https://pub.dev"
source: hosted
version: "6.0.2"
version: "6.1.0"
platform:
dependency: transitive
description:
@@ -1021,18 +1008,18 @@ packages:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.5.0"
quiver:
dependency: transitive
description:
@@ -1069,10 +1056,10 @@ packages:
dependency: transitive
description:
name: riverpod_analyzer_utils
sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8
sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3"
url: "https://pub.dev"
source: hosted
version: "0.5.8"
version: "0.5.9"
riverpod_annotation:
dependency: "direct main"
description:
@@ -1085,18 +1072,18 @@ packages:
dependency: "direct dev"
description:
name: riverpod_generator
sha256: "63546d70952015f0981361636bf8f356d9cfd9d7f6f0815e3c07789a41233188"
sha256: "120d3310f687f43e7011bb213b90a436f1bbc300f0e4b251a72c39bccb017a4f"
url: "https://pub.dev"
source: hosted
version: "2.6.3"
version: "2.6.4"
riverpod_lint:
dependency: "direct dev"
description:
name: riverpod_lint
sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99"
sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693
url: "https://pub.dev"
source: hosted
version: "2.6.3"
version: "2.6.4"
rxdart:
dependency: transitive
description:
@@ -1149,18 +1136,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "688ee90fbfb6989c980254a56cb26ebe9bb30a3a2dff439a78894211f73de67a"
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.5.1"
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d"
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.10"
shared_preferences_foundation:
dependency: transitive
description:
@@ -1189,10 +1176,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_web
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
@@ -1205,18 +1192,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
shortid:
dependency: transitive
description:
@@ -1234,18 +1221,18 @@ packages:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "2.0.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.4"
version: "1.3.5"
source_span:
dependency: transitive
description:
@@ -1266,34 +1253,34 @@ packages:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
version: "2.5.5"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474"
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
sqflite_platform_interface:
dependency: transitive
description:
@@ -1330,10 +1317,10 @@ packages:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -1346,10 +1333,10 @@ packages:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
version: "3.3.1"
term_glyph:
dependency: transitive
description:
@@ -1378,10 +1365,10 @@ packages:
dependency: "direct main"
description:
name: tray_manager
sha256: c2da0f0f1ddb455e721cf68d05d1281fec75cf5df0a1d3cb67b6ca0bdfd5709d
sha256: ad18c4cd73003097d182884bacb0578ad2865f3ab842a0ad00f6d043ed49eaf0
url: "https://pub.dev"
source: hosted
version: "0.4.0"
version: "0.5.0"
typed_data:
dependency: transitive
description:
@@ -1410,18 +1397,18 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193"
sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79"
url: "https://pub.dev"
source: hosted
version: "6.3.14"
version: "6.3.16"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
url: "https://pub.dev"
source: hosted
version: "6.3.2"
version: "6.3.3"
url_launcher_linux:
dependency: transitive
description:
@@ -1450,18 +1437,18 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "3.1.4"
uuid:
dependency: transitive
description:
@@ -1490,34 +1477,34 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
sha256: bfe6f435f6ec49cb6c01da1e275ae4228719e59a6b067048c51e72d9d63bcc4b
url: "https://pub.dev"
source: hosted
version: "0.1.6"
version: "1.0.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.3"
webdav_client:
dependency: "direct main"
description:
@@ -1557,14 +1544,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.4.3"
windows_single_instance:
dependency: "direct main"
description:
name: windows_single_instance
sha256: "50d5dcd6bec90b4a5ed588b1822b1aad21b39fc96da843e61c734b3caccfd2fc"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
xdg_directories:
dependency: transitive
description:
@@ -1585,18 +1564,18 @@ packages:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "3.1.3"
yaml_edit:
dependency: transitive
description:
name: yaml_edit
sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f
sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
sdks:
dart: ">=3.7.0 <4.0.0"
flutter: ">=3.24.0"
flutter: ">=3.29.0"

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.84+202505013
version: 0.8.85+202505091
environment:
sdk: '>=3.1.0 <4.0.0'
@@ -13,7 +13,7 @@ dependencies:
intl: any
path_provider: ^2.1.0
path: ^1.9.0
shared_preferences: ^2.5.1
shared_preferences: ^2.5.3
window_manager: ^0.4.3
dynamic_color: ^1.7.0
proxy:
@@ -21,13 +21,12 @@ dependencies:
window_ext:
path: plugins/window_ext
launch_at_startup: ^0.5.1
windows_single_instance: ^1.0.1
json_annotation: ^4.9.0
file_picker: ^8.0.3
mobile_scanner: ^6.0.2
file_picker: ^10.1.2
mobile_scanner: ^7.0.0
app_links: ^6.4.0
win32_registry: ^2.0.0
tray_manager: ^0.4.0
tray_manager: ^0.5.0
collection: ^1.18.0
animations: ^2.0.11
package_info_plus: ^8.0.0
@@ -58,7 +57,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
ffigen: ^15.0.0
ffigen: ^18.1.0
json_serializable: ^6.7.1
build_runner: ^2.4.13
args: ^2.4.2

View File

@@ -17,7 +17,6 @@
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_ext/window_ext_plugin_c_api.h>
#include <window_manager/window_manager_plugin.h>
#include <windows_single_instance/windows_single_instance_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
@@ -42,6 +41,4 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("WindowExtPluginCApi"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
WindowsSingleInstancePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowsSingleInstancePlugin"));
}

View File

@@ -14,7 +14,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_windows
window_ext
window_manager
windows_single_instance
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST