Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
b987c2376a Add android separates the core process
Support core status check and force restart

Optimize proxies page and access page

Update flutter and pub dependencies
2025-09-03 17:05:46 +08:00
89 changed files with 36626 additions and 46499 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- platform: android
os: ubuntu-latest
- platform: windows
os: Windows-2022
os: windows-latest
arch: amd64
- platform: linux
os: ubuntu-22.04
@@ -52,7 +52,6 @@ jobs:
if: startsWith(matrix.platform,'android')
run: |
echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/keystore.jks
echo "${{ secrets.SERVICE_JSON }}" | base64 --decode > android/app/google-services.json
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/local.properties
echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/local.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/local.properties

View File

@@ -5,8 +5,6 @@ plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
val localPropertiesFile = rootProject.file("local.properties")
@@ -20,9 +18,10 @@ val mStoreFile: File = file("keystore.jks")
val mStorePassword: String? = localProperties.getProperty("storePassword")
val mKeyAlias: String? = localProperties.getProperty("keyAlias")
val mKeyPassword: String? = localProperties.getProperty("keyPassword")
val isRelease =
mStoreFile.exists() && mStorePassword != null && mKeyAlias != null && mKeyPassword != null
val isRelease = mStoreFile.exists()
&& mStorePassword != null
&& mKeyAlias != null
&& mKeyPassword != null
android {
namespace = "com.follow.clash"
@@ -77,7 +76,8 @@ android {
}
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
@@ -93,7 +93,6 @@ flutter {
source = "../.."
}
dependencies {
implementation(project(":service"))
implementation(project(":common"))
@@ -102,7 +101,4 @@ dependencies {
implementation(libs.smali.dexlib2) {
exclude(group = "com.google.guava", module = "guava")
}
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)
}

View File

@@ -1,46 +0,0 @@
{
"project_info": {
"project_number": "000000000000",
"project_id": "dev"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000",
"android_client_info": {
"package_name": "com.follow.clash"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000",
"android_client_info": {
"package_name": "com.follow.clash.debug"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
]
}

View File

@@ -3,9 +3,6 @@ package com.follow.clash
import android.app.Application
import android.content.Context
import com.follow.clash.common.GlobalState
import com.follow.clash.common.processName
import com.google.firebase.FirebaseApp
import com.google.firebase.crashlytics.FirebaseCrashlytics
class Application : Application() {
@@ -13,21 +10,4 @@ class Application : Application() {
super.attachBaseContext(base)
GlobalState.init(this)
}
override fun onCreate() {
super.onCreate()
initRemoteCrashlytics(this)
}
private fun initRemoteCrashlytics(context: Context) {
try {
if (processName?.endsWith(":remote") == true) {
FirebaseApp.initializeApp(context)
FirebaseCrashlytics.getInstance().isCrashlyticsCollectionEnabled = true
GlobalState.log("init remote crashlytics")
}
} catch (e: Exception) {
GlobalState.log("initRemoteCrashlytics error: $e")
}
}
}

View File

@@ -97,6 +97,7 @@ inline fun <reified T : FlutterPlugin> FlutterEngine.plugin(): T? {
return plugins.get(T::class.java) as T?
}
fun <T> MethodChannel.invokeMethodOnMainThread(
method: String, arguments: Any? = null, callback: ((Result<T>) -> Unit)? = null
) {

View File

@@ -1,10 +1,9 @@
package com.follow.clash
import com.follow.clash.common.ServiceDelegate
import com.follow.clash.common.formatString
import com.follow.clash.common.intent
import com.follow.clash.service.ICallbackInterface
import com.follow.clash.service.IEventInterface
import com.follow.clash.service.IMessageInterface
import com.follow.clash.service.IRemoteInterface
import com.follow.clash.service.RemoteService
import com.follow.clash.service.models.NotificationParams
@@ -31,43 +30,13 @@ object Service {
delegate.bind()
}
fun unbind() {
delegate.unbind()
}
suspend fun invokeAction(data: String, cb: (result: String) -> Unit): Result<Unit> {
val res = mutableListOf<ByteArray>()
return delegate.useService {
it.invokeAction(
data,
object : ICallbackInterface.Stub() {
override fun onResult(result: ByteArray?, isSuccess: Boolean) {
res.add(result ?: byteArrayOf())
if (isSuccess) {
cb(res.formatString())
}
}
})
}
}
suspend fun setEventListener(
cb: (result: String?) -> Unit
suspend fun invokeAction(
data: String, cb: (result: ByteArray?, isSuccess: Boolean) -> Unit
): Result<Unit> {
val results = HashMap<String, MutableList<ByteArray>>()
return delegate.useService {
it.setEventListener(object : IEventInterface.Stub() {
override fun onEvent(
id: String, data: ByteArray?, isSuccess: Boolean
) {
if (results[id] == null) {
results[id] = mutableListOf()
}
results[id]?.add(data ?: byteArrayOf())
if (isSuccess) {
cb(results[id]?.formatString())
results.remove(id)
}
it.invokeAction(data, object : ICallbackInterface.Stub() {
override fun onResult(result: ByteArray?, isSuccess: Boolean) {
cb(result, isSuccess)
}
})
}
@@ -81,6 +50,18 @@ object Service {
}
}
suspend fun setMessageCallback(
cb: (result: String?) -> Unit
): Result<Unit> {
return delegate.useService {
it.setMessageCallback(object : IMessageInterface.Stub() {
override fun onResult(result: String?) {
cb(result)
}
})
}
}
suspend fun startService(options: VpnOptions, inApp: Boolean) {
delegate.useService { it.startService(options, inApp) }
}

View File

@@ -57,7 +57,9 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
private var vpnPrepareCallback: (suspend () -> Unit)? = null
private var requestNotificationCallback: (() -> Unit)? = null
private val iconMap = mutableMapOf<String, String?>()
private val packages = mutableListOf<Package>()
private val skipPrefixList = listOf(
@@ -170,7 +172,8 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
result.success("")
return@launch
}
val path = GlobalState.application.packageManager.getPackageIconPath(packageName)
val path =
GlobalState.application.packageManager.getPackageIconPath(packageName)
result.success(path)
}
}
@@ -220,12 +223,13 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
if (packages.isNotEmpty()) return packages
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS)
?.filter {
it.packageName != GlobalState.application.packageName && it.packageName != "android"
it.packageName != GlobalState.application.packageName || it.packageName == "android"
}?.map {
Package(
packageName = it.packageName,
label = it.applicationInfo?.loadLabel(packageManager).toString(),
system = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) != 0,
system = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) == 1,
lastUpdateTime = it.lastUpdateTime,
internet = it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
)

View File

@@ -5,6 +5,7 @@ import com.follow.clash.Service
import com.follow.clash.State
import com.follow.clash.awaitResult
import com.follow.clash.common.Components
import com.follow.clash.common.formatString
import com.follow.clash.invokeMethodOnMainThread
import com.follow.clash.models.AppState
import com.follow.clash.service.models.NotificationParams
@@ -40,10 +41,6 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
handleInit(result)
}
"shutdown" -> {
handleShutdown(result)
}
"invokeAction" -> {
handleInvokeAction(call, result)
}
@@ -72,17 +69,16 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
private fun handleInvokeAction(call: MethodCall, result: MethodChannel.Result) {
launch {
val data = call.arguments<String>()!!
Service.invokeAction(data) {
result.success(it)
val res = mutableListOf<ByteArray>()
Service.invokeAction(data) { byteArray, isSuccess ->
res.add(byteArray ?: byteArrayOf())
if (isSuccess) {
result.success(res.formatString())
}
}
}
}
private fun handleShutdown(result: MethodChannel.Result) {
Service.unbind()
result.success(true)
}
private fun handleStart(result: MethodChannel.Result) {
State.handleStartService()
result.success(true)
@@ -134,7 +130,7 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
fun handleInit(result: MethodChannel.Result) {
Service.bind()
launch {
Service.setEventListener {
Service.setMessageCallback {
handleSendEvent(it)
}.onSuccess {
result.success("")

View File

@@ -1,7 +1,6 @@
package com.follow.clash.common
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
@@ -21,7 +20,6 @@ import android.os.IBinder
import android.os.Looper
import android.os.RemoteException
import android.util.Log
import androidx.core.content.getSystemService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -69,16 +67,6 @@ val QuickAction.quickIntent: Intent
val BroadcastAction.action: String
get() = "${GlobalState.application.packageName}.intent.action.${this.name}"
val Context.processName: String?
get() {
val pid = android.os.Process.myPid()
val activityManager = getSystemService<ActivityManager>()
activityManager?.runningAppProcesses?.find { it.pid == pid }?.let {
return it.processName
}
return null
}
val BroadcastAction.quickIntent: Intent
get() = Components.BROADCAST_RECEIVER.intent.apply {
action = this@quickIntent.action

View File

@@ -1,6 +1,5 @@
package com.follow.clash.common
import android.app.Application
import android.util.Log
import kotlinx.coroutines.CoroutineScope

View File

@@ -50,7 +50,7 @@ class ServiceDelegate<T>(
}
suspend inline fun <R> useService(
timeoutMillis: Long = 5000, crossinline block: suspend (T) -> R
timeoutMillis: Long = 5000, crossinline block: (T) -> R
): Result<R> {
return runCatching {
withTimeout(timeoutMillis) {

View File

@@ -1,6 +1,5 @@
[versions]
#agp = "8.10.1"
firebaseBom = "34.2.0"
minSdk = "23"
targetSdk = "36"
compileSdk = "36"
@@ -11,18 +10,11 @@ coreSplashscreen = "1.0.1"
gson = "2.13.1"
kotlin = "2.2.10"
smaliDexlib2 = "3.0.9"
firebaseCrashlyticsKtx = "20.0.1"
firebaseCommonKtx = "22.0.0"
[libraries]
build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
annotation-jvm = { module = "androidx.annotation:annotation-jvm", version.ref = "annotationJvm" }
core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
firebase-crashlytics-ndk = { module = "com.google.firebase:firebase-crashlytics-ndk" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
smali-dexlib2 = { module = "com.android.tools.smali:smali-dexlib2", version.ref = "smaliDexlib2" }
firebase-crashlytics-ktx = { group = "com.google.firebase", name = "firebase-crashlytics-ktx", version.ref = "firebaseCrashlyticsKtx" }
firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
smali-dexlib2 = { module = "com.android.tools.smali:smali-dexlib2", version.ref = "smaliDexlib2" }

View File

@@ -2,5 +2,5 @@
package com.follow.clash.service;
interface ICallbackInterface {
void onResult(in byte[] data,in boolean isSuccess);
void onResult(in byte[] result, boolean isSuccess);
}

View File

@@ -1,7 +0,0 @@
// IEventInterface.aidl
package com.follow.clash.service;
interface IEventInterface {
void onEvent(in String id, in byte[] data,in boolean isSuccess);
}

View File

@@ -0,0 +1,6 @@
// IMessageInterface.aidl
package com.follow.clash.service;
interface IMessageInterface {
void onResult(String result);
}

View File

@@ -2,7 +2,7 @@
package com.follow.clash.service;
import com.follow.clash.service.ICallbackInterface;
import com.follow.clash.service.IEventInterface;
import com.follow.clash.service.IMessageInterface;
import com.follow.clash.service.models.VpnOptions;
import com.follow.clash.service.models.NotificationParams;
@@ -11,5 +11,5 @@ interface IRemoteInterface {
void updateNotificationParams(in NotificationParams params);
void startService(in VpnOptions options,in boolean inApp);
void stopService();
void setEventListener(in IEventInterface event);
void setMessageCallback(in IMessageInterface messageCallback);
}

View File

@@ -13,7 +13,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.util.UUID
class RemoteService : Service(),
CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
@@ -61,12 +60,10 @@ class RemoteService : Service(),
private val binder = object : IRemoteInterface.Stub() {
override fun invokeAction(data: String, callback: ICallbackInterface) {
Core.invokeAction(data) {
runCatching {
val chunks = it?.chunkedForAidl() ?: listOf()
val totalSize = chunks.size
chunks.forEachIndexed { index, chunk ->
callback.onResult(chunk, totalSize - 1 == index)
}
val chunks = it?.chunkedForAidl() ?: listOf()
val totalSize = chunks.size
chunks.forEachIndexed { index, chunk ->
callback.onResult(chunk, totalSize - 1 == index)
}
}
}
@@ -87,20 +84,15 @@ class RemoteService : Service(),
handleStopService()
}
override fun setEventListener(event: IEventInterface) {
Core.setMessageCallback {
runCatching {
val id = UUID.randomUUID().toString()
val chunks = it?.chunkedForAidl() ?: listOf()
val totalSize = chunks.size
chunks.forEachIndexed { index, chunk ->
event.onEvent(id, chunk, totalSize - 1 == index)
}
}
}
override fun setMessageCallback(messageCallback: IMessageInterface) {
setMessageCallback(messageCallback::onResult)
}
}
private fun setMessageCallback(cb: (result: String?) -> Unit) {
Core.setMessageCallback(cb)
}
override fun onBind(intent: Intent?): IBinder {
return binder
}

View File

@@ -13,11 +13,7 @@ val Traffic.speedText: String
get() = "${up.formatBytes}/s↑ ${down.formatBytes}/s↓"
fun Core.getSpeedTrafficText(onlyStatisticsProxy: Boolean): String {
try {
val res = getTraffic(onlyStatisticsProxy)
val traffic = Gson().fromJson(res, Traffic::class.java)
return traffic.speedText
} catch (_: Exception) {
return ""
}
val res = getTraffic(onlyStatisticsProxy)
val traffic = Gson().fromJson(res, Traffic::class.java)
return traffic.speedText
}

View File

@@ -102,7 +102,7 @@ class NetworkObserveModule(private val service: Service) : Module() {
return
}
preDnsList = dnsList
Core.updateDNS(dnsList.toSet().joinToString(","))
Core.updateDNS(dnsList.joinToString { "," })
}
fun setUnderlyingNetworks(network: Network) {

View File

@@ -18,10 +18,8 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.12.2" apply false
id("com.android.application") version "8.12.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.10" apply false
id("com.google.gms.google-services") version ("4.3.15") apply false
id("com.google.firebase.crashlytics") version ("2.8.1") apply false
}

View File

@@ -409,7 +409,7 @@
"autoSetSystemDns": "Auto set system DNS",
"details": "{label} details",
"creationTime": "Creation time",
"process": "Process",
"progress": "Progress",
"host": "Host",
"destination": "Destination",
"destinationGeoIP": "Destination GeoIP",

View File

@@ -410,7 +410,7 @@
"autoSetSystemDns": "オートセットシステムDNS",
"details": "{label}詳細",
"creationTime": "作成時間",
"process": "プロセス",
"progress": "進捗",
"host": "ホスト",
"destination": "宛先",
"destinationGeoIP": "宛先地理情報",

View File

@@ -410,7 +410,7 @@
"autoSetSystemDns": "Автоматическая настройка системного DNS",
"details": "Детали {}",
"creationTime": "Время создания",
"process": "процесс",
"progress": "Прогресс",
"host": "Хост",
"destination": "Назначение",
"destinationGeoIP": "Геолокация назначения",

View File

@@ -410,7 +410,7 @@
"autoSetSystemDns": "自动设置系统DNS",
"details": "{label}详情",
"creationTime": "创建时间",
"process": "进",
"progress": "进",
"host": "主机",
"destination": "目标地址",
"destinationGeoIP": "目标地理定位",

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

35960
assets/data/GeoSite.dat Normal file

File diff suppressed because one or more lines are too long

BIN
assets/data/geoip.metadb Normal file

Binary file not shown.

View File

@@ -115,15 +115,11 @@ func updateListeners() {
listeners := currentConfig.Listeners
general := currentConfig.General
listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
allowLan := general.AllowLan
listener.SetAllowLan(allowLan)
listener.SetAllowLan(general.AllowLan)
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
inbound.SetAllowedIPs(general.LanAllowedIPs)
inbound.SetDisAllowedIPs(general.LanDisAllowedIPs)
bindAddress := general.BindAddress
listener.SetBindAddress(bindAddress)
listener.SetBindAddress(general.BindAddress)
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)

View File

@@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"fmt"
"github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/outboundgroup"
"github.com/metacubex/mihomo/common/observable"
@@ -60,7 +61,6 @@ func handleStopListener() bool {
defer runLock.Unlock()
isRunning = false
listener.StopListener()
resolver.ResetConnection()
return true
}
@@ -144,7 +144,7 @@ func handleGetTraffic(onlyStatisticsProxy bool) string {
}
data, err := json.Marshal(traffic)
if err != nil {
log.Errorln("Error: %s", err)
fmt.Println("Error:", err)
return ""
}
return string(data)
@@ -158,7 +158,7 @@ func handleGetTotalTraffic(onlyStatisticsProxy bool) string {
}
data, err := json.Marshal(traffic)
if err != nil {
log.Errorln("Error: %s", err)
fmt.Println("Error:", err)
return ""
}
return string(data)
@@ -228,7 +228,7 @@ func handleGetConnections() string {
snapshot := statistic.DefaultManager.Snapshot()
data, err := json.Marshal(snapshot)
if err != nil {
log.Errorln("Error: %s", err)
fmt.Println("Error:", err)
return ""
}
return string(data)
@@ -323,13 +323,13 @@ func handleUpdateGeoData(geoType string, geoName string, fn func(value string))
fn(err.Error())
return
}
case "GEOIP":
case "GeoIp":
err := updater.UpdateGeoIpWithPath(path)
if err != nil {
fn(err.Error())
return
}
case "GEOSITE":
case "GeoSite":
err := updater.UpdateGeoSiteWithPath(path)
if err != nil {
fn(err.Error())

View File

@@ -164,7 +164,7 @@ func (result ActionResult) send() {
}
invokeResult(result.callback, string(data))
if result.Method != messageMethod {
defer releaseObject(result.callback)
releaseObject(result.callback)
}
}

View File

@@ -66,7 +66,7 @@ class ApplicationState extends ConsumerState<Application> {
});
}
Widget _buildPlatformState({required Widget child}) {
Widget _buildPlatformState(Widget child) {
if (system.isDesktop) {
return WindowManager(
child: TrayManager(
@@ -77,7 +77,7 @@ class ApplicationState extends ConsumerState<Application> {
return AndroidManager(child: TileManager(child: child));
}
Widget _buildState({required Widget child}) {
Widget _buildState(Widget child) {
return AppStateManager(
child: CoreManager(
child: ConnectivityManager(
@@ -94,14 +94,14 @@ class ApplicationState extends ConsumerState<Application> {
);
}
Widget _buildPlatformApp({required Widget child}) {
Widget _buildPlatformApp(Widget child) {
if (system.isDesktop) {
return WindowHeaderContainer(child: child);
}
return VpnManager(child: child);
}
Widget _buildApp({required Widget child}) {
Widget _buildApp(Widget child) {
return MessageManager(child: ThemeManager(child: child));
}
@@ -125,8 +125,10 @@ class ApplicationState extends ConsumerState<Application> {
builder: (_, child) {
return AppEnvManager(
child: _buildApp(
child: _buildPlatformState(
child: _buildState(child: _buildPlatformApp(child: child!)),
_buildPlatformState(
_buildState(
AppSidebarContainer(child: _buildPlatformApp(child!)),
),
),
),
);

View File

@@ -2,6 +2,7 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'string.dart';
import 'utils.dart';
List<Group> computeSort({
required List<Group> groups,
@@ -106,5 +107,8 @@ List<Proxy> _sortOfDelay({
}
List<Proxy> _sortOfName(List<Proxy> proxies) {
return List.of(proxies)..sort((a, b) => a.name.compareTo(b.name));
return List.of(proxies)..sort(
(a, b) =>
utils.sortByChar(utils.getPinyin(a.name), utils.getPinyin(b.name)),
);
}

View File

@@ -30,10 +30,10 @@ const animateDuration = Duration(milliseconds: 100);
const midDuration = Duration(milliseconds: 200);
const commonDuration = Duration(milliseconds: 300);
const defaultUpdateDuration = Duration(days: 1);
const MMDB = 'GEOIP.metadb';
const ASN = 'ASN.mmdb';
const GEOIP = 'GEOIP.dat';
const GEOSITE = 'GEOSITE.dat';
const mmdbFileName = 'geoip.metadb';
const asnFileName = 'ASN.mmdb';
const geoIpFileName = 'GeoIP.dat';
const geoSiteFileName = 'GeoSite.dat';
final double kHeaderHeight = system.isDesktop
? !system.isMacOS
? 40

View File

@@ -7,17 +7,28 @@ extension BuildContextExtension on BuildContext {
return findAncestorStateOfType<CommonScaffoldState>();
}
void showNotifier(String text) {
Future<void>? showNotifier(String text) {
return findAncestorStateOfType<MessageManagerState>()?.message(text);
}
void showSnackBar(String message, {SnackBarAction? action}) {
void showSnackBar(
String message, {
SnackBarAction? action,
}) {
final width = viewWidth;
EdgeInsets margin;
if (width < 600) {
margin = const EdgeInsets.only(bottom: 16, right: 16, left: 16);
margin = const EdgeInsets.only(
bottom: 16,
right: 16,
left: 16,
);
} else {
margin = EdgeInsets.only(bottom: 16, left: 16, right: width - 316);
margin = EdgeInsets.only(
bottom: 16,
left: 16,
right: width - 316,
);
}
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(
@@ -65,11 +76,8 @@ extension BuildContextExtension on BuildContext {
class BackHandleInherited extends InheritedWidget {
final Function handleBack;
const BackHandleInherited({
super.key,
required this.handleBack,
required super.child,
});
const BackHandleInherited(
{super.key, required this.handleBack, required super.child});
static BackHandleInherited? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<BackHandleInherited>();

View File

@@ -15,14 +15,12 @@ class Navigation {
keep: false,
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
builder: (_) =>
const DashboardView(key: GlobalObjectKey(PageLabel.dashboard)),
builder: (_) => const DashboardView(),
),
NavigationItem(
icon: const Icon(Icons.article),
label: PageLabel.proxies,
builder: (_) =>
const ProxiesView(key: GlobalObjectKey(PageLabel.proxies)),
builder: (_) => const ProxiesView(),
modes: hasProxies
? [NavigationItemMode.mobile, NavigationItemMode.desktop]
: [],
@@ -30,22 +28,19 @@ class Navigation {
NavigationItem(
icon: Icon(Icons.folder),
label: PageLabel.profiles,
builder: (_) =>
const ProfilesView(key: GlobalObjectKey(PageLabel.profiles)),
builder: (_) => const ProfilesView(),
),
NavigationItem(
icon: Icon(Icons.view_timeline),
label: PageLabel.requests,
builder: (_) =>
const RequestsView(key: GlobalObjectKey(PageLabel.requests)),
builder: (_) => const RequestsView(),
description: 'requestsDesc',
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
NavigationItem(
icon: Icon(Icons.ballot),
label: PageLabel.connections,
builder: (_) =>
const ConnectionsView(key: GlobalObjectKey(PageLabel.connections)),
builder: (_) => const ConnectionsView(),
description: 'connectionsDesc',
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
@@ -53,14 +48,13 @@ class Navigation {
icon: Icon(Icons.storage),
label: PageLabel.resources,
description: 'resourcesDesc',
builder: (_) =>
const ResourcesView(key: GlobalObjectKey(PageLabel.resources)),
builder: (_) => const ResourcesView(),
modes: [NavigationItemMode.more],
),
NavigationItem(
icon: const Icon(Icons.adb),
label: PageLabel.logs,
builder: (_) => const LogsView(key: GlobalObjectKey(PageLabel.logs)),
builder: (_) => const LogsView(),
description: 'logsDesc',
modes: openLogs
? [NavigationItemMode.desktop, NavigationItemMode.more]
@@ -69,7 +63,7 @@ class Navigation {
NavigationItem(
icon: Icon(Icons.construction),
label: PageLabel.tools,
builder: (_) => const ToolsView(key: GlobalObjectKey(PageLabel.tools)),
builder: (_) => const ToolsView(),
modes: [NavigationItemMode.desktop, NavigationItemMode.mobile],
),
];

View File

@@ -8,40 +8,13 @@ import 'package:flutter/material.dart';
class BaseScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
PointerDeviceKind.trackpad,
if (system.isDesktop) PointerDeviceKind.mouse,
PointerDeviceKind.unknown,
};
@override
Widget buildScrollbar(
BuildContext context,
Widget child,
ScrollableDetails details,
) {
switch (axisDirectionToAxis(details.direction)) {
case Axis.horizontal:
return child;
case Axis.vertical:
switch (getPlatform(context)) {
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
assert(details.controller != null);
return CommonScrollBar(
controller: details.controller,
child: child,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
return child;
}
}
}
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
PointerDeviceKind.trackpad,
if (system.isDesktop) PointerDeviceKind.mouse,
PointerDeviceKind.unknown,
};
}
class HiddenBarScrollBehavior extends BaseScrollBehavior {
@@ -62,7 +35,10 @@ class ShowBarScrollBehavior extends BaseScrollBehavior {
Widget child,
ScrollableDetails details,
) {
return CommonScrollBar(controller: details.controller, child: child);
return CommonScrollBar(
controller: details.controller,
child: child,
);
}
}
@@ -76,9 +52,7 @@ class NextClampingScrollPhysics extends ClampingScrollPhysics {
@override
Simulation? createBallisticSimulation(
ScrollMetrics position,
double velocity,
) {
ScrollMetrics position, double velocity) {
final Tolerance tolerance = toleranceFor(position);
if (position.outOfRange) {
double? end;

View File

@@ -8,6 +8,7 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lpinyin/lpinyin.dart';
class Utils {
Color? getDelayColor(int? delay) {
@@ -177,11 +178,11 @@ class Utils {
return build1.compareTo(build2);
}
// String getPinyin(String value) {
// return value.isNotEmpty
// ? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
// : '';
// }
String getPinyin(String value) {
return value.isNotEmpty
? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
: '';
}
String? getFileNameForDisposition(String? disposition) {
if (disposition == null) return null;
@@ -227,7 +228,7 @@ class Utils {
}
int getProxiesColumns(double viewWidth, ProxiesLayout proxiesLayout) {
final columns = max((viewWidth / 250).ceil(), 2);
final columns = max((viewWidth / 300).ceil(), 2);
return switch (proxiesLayout) {
ProxiesLayout.tight => columns + 1,
ProxiesLayout.standard => columns,
@@ -236,7 +237,7 @@ class Utils {
}
int getProfilesColumns(double viewWidth) {
return max((viewWidth / 280).floor(), 1);
return max((viewWidth / 320).floor(), 1);
}
final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900];

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart' as acrylic;
import 'package:screen_retriever/screen_retriever.dart';
import 'package:window_manager/window_manager.dart';
@@ -18,6 +19,9 @@ class Window {
protocol.register('clashmeta');
protocol.register('flclash');
}
if ((version > 10 && system.isMacOS)) {
await acrylic.Window.initialize();
}
await windowManager.ensureInitialized();
WindowOptions windowOptions = WindowOptions(
size: Size(props.width, props.height),
@@ -35,18 +39,25 @@ class Window {
await windowManager.setAlignment(Alignment.center);
} else {
final displays = await screenRetriever.getAllDisplays();
final isPositionValid = displays.any((display) {
final displayBounds = Rect.fromLTWH(
display.visiblePosition!.dx,
display.visiblePosition!.dy,
display.size.width,
display.size.height,
);
return displayBounds.contains(Offset(left, top)) ||
displayBounds.contains(Offset(right, bottom));
});
final isPositionValid = displays.any(
(display) {
final displayBounds = Rect.fromLTWH(
display.visiblePosition!.dx,
display.visiblePosition!.dy,
display.size.width,
display.size.height,
);
return displayBounds.contains(Offset(left, top)) ||
displayBounds.contains(Offset(right, bottom));
},
);
if (isPositionValid) {
await windowManager.setPosition(Offset(left, top));
await windowManager.setPosition(
Offset(
left,
top,
),
);
}
}
}
@@ -55,6 +66,13 @@ class Window {
});
}
void updateMacOSBrightness(Brightness brightness) {
if (!system.isMacOS) {
return;
}
acrylic.Window.overrideMacOSBrightness(dark: brightness == Brightness.dark);
}
Future<void> show() async {
render?.resume();
await windowManager.show();

View File

@@ -73,7 +73,6 @@ class AppController {
}
Future<void> restartCore() async {
globalState.isUserDisconnected = true;
await coreController.shutdown();
await _connectCore();
await _initCore();
@@ -528,9 +527,11 @@ class AppController {
Future<void> init() async {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
);
if (kDebugMode) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
);
}
};
updateTray(true);
autoUpdateProfiles();
@@ -560,7 +561,9 @@ class AppController {
}
return;
}
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connected;
Future.delayed(const Duration(milliseconds: 600), () {
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connected;
});
}
Future<void> _initStatus() async {
@@ -620,7 +623,6 @@ class AppController {
Future<bool> showDisclaimer() async {
return await globalState.showCommonDialog<bool>(
context: context,
dismissible: false,
child: CommonDialog(
title: appLocalizations.disclaimer,

View File

@@ -40,7 +40,12 @@ class CoreController {
if (!isExists) {
await homeDir.create(recursive: true);
}
const geoFileNameList = [MMDB, GEOIP, GEOSITE, ASN];
const geoFileNameList = [
mmdbFileName,
geoIpFileName,
geoSiteFileName,
asnFileName,
];
try {
for (final geoFileName in geoFileNameList) {
final geoFile = File(join(homePath, geoFileName));

View File

@@ -35,7 +35,6 @@ class CoreLib extends CoreHandlerInterface {
@override
Future<bool> shutdown() async {
await service?.shutdown();
_connectedCompleter = Completer();
return true;
}

View File

@@ -100,7 +100,6 @@ class CoreService extends CoreHandlerInterface {
commonPrint.log(error);
}
});
await _socketCompleter.future;
}
@override
@@ -127,9 +126,9 @@ class CoreService extends CoreHandlerInterface {
Future<void> _destroySocket() async {
if (_socketCompleter.isCompleted) {
final socket = await _socketCompleter.future;
final lastSocket = await _socketCompleter.future;
_socketCompleter = Completer();
socket.close();
lastSocket.close();
}
}
@@ -180,9 +179,7 @@ class CoreService extends CoreHandlerInterface {
}
@override
Future get connected {
return _socketCompleter.future;
}
Future get connected => _socketCompleter.future;
}
final coreService = system.isDesktop ? CoreService() : null;

View File

@@ -521,7 +521,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Please press the keyboard.",
),
"preview": MessageLookupByLibrary.simpleMessage("Preview"),
"process": MessageLookupByLibrary.simpleMessage("Process"),
"profile": MessageLookupByLibrary.simpleMessage("Profile"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage(
@@ -548,6 +547,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
"profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"),
"progress": MessageLookupByLibrary.simpleMessage("Progress"),
"project": MessageLookupByLibrary.simpleMessage("Project"),
"providers": MessageLookupByLibrary.simpleMessage("Providers"),
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),

View File

@@ -395,7 +395,6 @@ class MessageLookup extends MessageLookupByLibrary {
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
"preview": MessageLookupByLibrary.simpleMessage("プレビュー"),
"process": MessageLookupByLibrary.simpleMessage("プロセス"),
"profile": MessageLookupByLibrary.simpleMessage("プロファイル"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage("有効な間隔形式を入力してください"),
@@ -418,6 +417,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("プロファイル一覧"),
"profilesSort": MessageLookupByLibrary.simpleMessage("プロファイルの並び替え"),
"progress": MessageLookupByLibrary.simpleMessage("進捗"),
"project": MessageLookupByLibrary.simpleMessage("プロジェクト"),
"providers": MessageLookupByLibrary.simpleMessage("プロバイダー"),
"proxies": MessageLookupByLibrary.simpleMessage("プロキシ"),

View File

@@ -548,7 +548,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Пожалуйста, нажмите клавишу.",
),
"preview": MessageLookupByLibrary.simpleMessage("Предпросмотр"),
"process": MessageLookupByLibrary.simpleMessage("процесс"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage(
@@ -575,6 +574,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("Профили"),
"profilesSort": MessageLookupByLibrary.simpleMessage("Сортировка профилей"),
"progress": MessageLookupByLibrary.simpleMessage("Прогресс"),
"project": MessageLookupByLibrary.simpleMessage("Проект"),
"providers": MessageLookupByLibrary.simpleMessage("Провайдеры"),
"proxies": MessageLookupByLibrary.simpleMessage("Прокси"),

View File

@@ -347,7 +347,6 @@ class MessageLookup extends MessageLookupByLibrary {
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
"preview": MessageLookupByLibrary.simpleMessage("预览"),
"process": MessageLookupByLibrary.simpleMessage("进程"),
"profile": MessageLookupByLibrary.simpleMessage("配置"),
"profileAutoUpdateIntervalInvalidValidationDesc":
MessageLookupByLibrary.simpleMessage("请输入有效间隔时间格式"),
@@ -368,6 +367,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"profiles": MessageLookupByLibrary.simpleMessage("配置"),
"profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"),
"progress": MessageLookupByLibrary.simpleMessage("进度"),
"project": MessageLookupByLibrary.simpleMessage("项目"),
"providers": MessageLookupByLibrary.simpleMessage("提供者"),
"proxies": MessageLookupByLibrary.simpleMessage("代理"),

View File

@@ -3159,9 +3159,9 @@ class AppLocalizations {
);
}
/// `Process`
String get process {
return Intl.message('Process', name: 'process', desc: '', args: []);
/// `Progress`
String get progress {
return Intl.message('Progress', name: 'progress', desc: '', args: []);
}
/// `Host`

View File

@@ -7,6 +7,7 @@ import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_acrylic/widgets/transparent_macos_sidebar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
@@ -60,6 +61,12 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
macOS?.updateDns(true);
}
});
ref.listenManual(currentBrightnessProvider, (prev, next) {
if (prev == next) {
return;
}
window?.updateMacOSBrightness(next);
}, fireImmediately: true);
}
@override
@@ -151,25 +158,15 @@ class AppSidebarContainer extends ConsumerWidget {
required BuildContext context,
required Widget child,
}) {
return Material(color: context.colorScheme.surfaceContainer, child: child);
// if (!system.isMacOS) {
// return Material(
// color: context.colorScheme.surfaceContainer,
// child: child,
// );
// }
// return child;
// return TransparentMacOSSidebar(
// child: Material(color: Colors.transparent, child: child),
// );
}
void _updateSideBarWidth(WidgetRef ref, double contentWidth) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(sideWidthProvider.notifier).value =
ref.read(viewSizeProvider.select((state) => state.width)) -
contentWidth;
});
if (!system.isMacOS) {
return Material(
color: context.colorScheme.surfaceContainer,
child: child,
);
}
return TransparentMacOSSidebar(
child: Material(color: Colors.transparent, child: child),
);
}
@override
@@ -184,110 +181,76 @@ class AppSidebarContainer extends ConsumerWidget {
final showLabel = ref.watch(appSettingProvider).showLabel;
return Row(
children: [
_buildBackground(
context: context,
child: SafeArea(
child: Stack(
alignment: Alignment.topRight,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (system.isMacOS) SizedBox(height: 22),
SizedBox(height: 10),
if (!system.isMacOS) ...[
ClipRect(
child: Padding(
padding: EdgeInsets.only(left: 20),
child: AppIcon(),
),
),
SizedBox(height: 12),
],
Expanded(
child: ScrollConfiguration(
behavior: HiddenBarScrollBehavior(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: NavigationRail(
scrollable: true,
minExtendedWidth: 200,
backgroundColor: Colors.transparent,
selectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(
color: context.colorScheme.onSurface,
),
unselectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(
color: context.colorScheme.onSurface,
),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(Intl.message(e.label.name)),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
),
),
],
),
),
),
const SizedBox(height: 16),
Padding(
padding: EdgeInsets.only(left: 20),
child: IconButton(
onPressed: () {
ref
.read(appSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith(showLabel: !state.showLabel),
Stack(
alignment: Alignment.topRight,
children: [
_buildBackground(
context: context,
child: Column(
children: [
SizedBox(height: 32),
if (!system.isMacOS) ...[AppIcon(), SizedBox(height: 12)],
Expanded(
child: ScrollConfiguration(
behavior: HiddenBarScrollBehavior(),
child: SingleChildScrollView(
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: Colors.transparent,
selectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(color: context.colorScheme.onSurface),
unselectedLabelTextStyle: context
.textTheme
.labelLarge!
.copyWith(color: context.colorScheme.onSurface),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(Intl.message(e.label.name)),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
icon: Icon(
Icons.menu,
color: context.colorScheme.onSurfaceVariant,
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
),
),
),
),
const SizedBox(height: 16),
],
),
_buildLoading(),
],
),
const SizedBox(height: 16),
IconButton(
onPressed: () {
ref
.read(appSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith(showLabel: !state.showLabel),
);
},
icon: Icon(
Icons.menu,
color: context.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 16),
],
),
),
),
),
Expanded(
flex: 1,
child: ClipRect(
child: LayoutBuilder(
builder: (_, constraints) {
_updateSideBarWidth(ref, constraints.maxWidth);
return child;
},
),
),
_buildLoading(),
],
),
Expanded(flex: 1, child: ClipRect(child: child)),
],
);
}

View File

@@ -93,10 +93,7 @@ class _CoreContainerState extends ConsumerState<CoreManager>
@override
Future<void> onCrash(String message) async {
if (!globalState.isUserDisconnected) {
context.showNotifier(message);
globalState.isUserDisconnected = false;
}
context.showNotifier(message);
if (ref.read(coreStatusProvider) != CoreStatus.connected) {
return;
}

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:collection';
import 'dart:math';
import 'package:fl_clash/common/common.dart';
@@ -18,9 +17,8 @@ class MessageManager extends StatefulWidget {
class MessageManagerState extends State<MessageManager> {
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
final _bufferMessages = Queue<CommonMessage>();
final _activeTimers = <String, Timer>{};
bool _isDisplayingMessage = false;
final List<CommonMessage> _bufferMessages = [];
bool _pushing = false;
@override
void initState() {
@@ -30,48 +28,38 @@ class MessageManagerState extends State<MessageManager> {
@override
void dispose() {
_messagesNotifier.dispose();
for (final timer in _activeTimers.values) {
timer.cancel();
}
_activeTimers.clear();
_bufferMessages.clear();
super.dispose();
}
void message(String text) {
Future<void> message(String text) async {
final commonMessage = CommonMessage(id: utils.uuidV4, text: text);
commonPrint.log(text);
_bufferMessages.add(commonMessage);
commonPrint.log('message: $text');
_processQueue();
await _showMessage();
}
void _cancelMessage(String id) {
_bufferMessages.removeWhere((msg) => msg.id == id);
if (_activeTimers.containsKey(id)) {
_removeMessage(id);
}
}
void _processQueue() {
if (_isDisplayingMessage || _bufferMessages.isEmpty) {
Future<void> _showMessage() async {
if (_pushing == true) {
return;
}
_isDisplayingMessage = true;
final message = _bufferMessages.removeFirst();
_messagesNotifier.value = List.from(_messagesNotifier.value)..add(message);
final timer = Timer(message.duration, () {
_removeMessage(message.id);
});
_activeTimers[message.id] = timer;
_pushing = true;
while (_bufferMessages.isNotEmpty) {
final commonMessage = _bufferMessages.removeAt(0);
_messagesNotifier.value = List.from(_messagesNotifier.value)
..add(commonMessage);
await Future.delayed(Duration(seconds: 1));
Future.delayed(commonMessage.duration, () {
_handleRemove(commonMessage);
});
}
}
void _removeMessage(String id) {
_activeTimers.remove(id)?.cancel();
final currentMessages = List<CommonMessage>.from(_messagesNotifier.value);
currentMessages.removeWhere((msg) => msg.id == id);
_messagesNotifier.value = currentMessages;
_isDisplayingMessage = false;
_processQueue();
Future<void> _handleRemove(CommonMessage commonMessage) async {
_messagesNotifier.value = List<CommonMessage>.from(_messagesNotifier.value)
..remove(commonMessage);
if (_bufferMessages.isEmpty) {
_pushing = false;
}
}
@override
@@ -95,45 +83,35 @@ class MessageManagerState extends State<MessageManager> {
: LayoutBuilder(
key: Key(messages.last.id),
builder: (_, constraints) {
return Dismissible(
key: ValueKey(messages.last.id),
onDismissed: (_) {
_cancelMessage(messages.last.id);
},
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(14),
),
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12.0),
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(constraints.maxWidth, 500),
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
messages.last.text,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 16),
TextButton(
onPressed: () {
_cancelMessage(messages.last.id);
},
child: Text(appLocalizations.cancel),
),
],
),
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(constraints.maxWidth, 500),
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 10,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(messages.last.text)),
IconButton(
visualDensity: VisualDensity.compact,
iconSize: 18,
padding: EdgeInsets.zero,
onPressed: () {
_handleRemove(messages.last);
},
icon: Icon(Icons.close),
),
],
),
),
);

View File

@@ -13,7 +13,10 @@ import '../providers/state.dart';
class ThemeManager extends ConsumerWidget {
final Widget child;
const ThemeManager({super.key, required this.child});
const ThemeManager({
super.key,
required this.child,
});
Widget _buildSystemUi(Widget child) {
if (!system.isAndroid) {
@@ -82,7 +85,9 @@ class ThemeManager extends ConsumerWidget {
final height = MediaQuery.of(context).size.height;
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(textScaleFactor),
textScaler: TextScaler.linear(
textScaleFactor,
),
padding: padding.copyWith(
top: padding.top > height * 0.3 ? 20.0 : padding.top,
),
@@ -90,7 +95,10 @@ class ThemeManager extends ConsumerWidget {
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewSize(
Size(container.maxWidth, container.maxHeight),
Size(
container.maxWidth,
container.maxHeight,
),
);
return _buildSystemUi(child);
},

View File

@@ -20,7 +20,6 @@ abstract class AppState with _$AppState {
@Default([]) List<Package> packages,
@Default(0) int sortNum,
required Size viewSize,
@Default(0) double sideWidth,
@Default({}) DelayMap delayMap,
@Default([]) List<Group> groups,
@Default(0) int checkIpNum,

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AppState {
bool get isInit; bool get backBlock; PageLabel get pageLabel; List<Package> get packages; int get sortNum; Size get viewSize; double get sideWidth; DelayMap get delayMap; List<Group> get groups; int get checkIpNum; Brightness get brightness; int? get runTime; List<ExternalProvider> get providers; String? get localIp; FixedList<TrackerInfo> get requests; int get version; FixedList<Log> get logs; FixedList<Traffic> get traffics; Traffic get totalTraffic; bool get realTunEnable; bool get loading; SystemUiOverlayStyle get systemUiOverlayStyle; ProfileOverrideModel? get profileOverrideModel; Map<QueryTag, String> get queryMap; CoreStatus get coreStatus;
bool get isInit; bool get backBlock; PageLabel get pageLabel; List<Package> get packages; int get sortNum; Size get viewSize; DelayMap get delayMap; List<Group> get groups; int get checkIpNum; Brightness get brightness; int? get runTime; List<ExternalProvider> get providers; String? get localIp; FixedList<TrackerInfo> get requests; int get version; FixedList<Log> get logs; FixedList<Traffic> get traffics; Traffic get totalTraffic; bool get realTunEnable; bool get loading; SystemUiOverlayStyle get systemUiOverlayStyle; ProfileOverrideModel? get profileOverrideModel; Map<QueryTag, String> get queryMap; CoreStatus get coreStatus;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $AppStateCopyWith<AppState> get copyWith => _$AppStateCopyWithImpl<AppState>(thi
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppState&&(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)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other.delayMap, delayMap)&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other.providers, providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other.queryMap, queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppState&&(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)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&const DeepCollectionEquality().equals(other.delayMap, delayMap)&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other.providers, providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other.queryMap, queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
}
@override
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(delayMap),const DeepCollectionEquality().hash(groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(queryMap),coreStatus]);
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,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(queryMap),coreStatus]);
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
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, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
}
@@ -45,7 +45,7 @@ abstract mixin class $AppStateCopyWith<$Res> {
factory $AppStateCopyWith(AppState value, $Res Function(AppState) _then) = _$AppStateCopyWithImpl;
@useResult
$Res call({
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
});
@@ -62,7 +62,7 @@ class _$AppStateCopyWithImpl<$Res>
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
return _then(_self.copyWith(
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
@@ -70,8 +70,7 @@ as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: ca
as PageLabel,packages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
as double,delayMap: null == delayMap ? _self.delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as Size,delayMap: null == delayMap ? _self.delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as DelayMap,groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable
@@ -195,10 +194,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return orElse();
}
@@ -216,10 +215,10 @@ return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_tha
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus) $default,) {final _that = this;
switch (_that) {
case _AppState():
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
throw StateError('Unexpected subclass');
}
@@ -236,10 +235,10 @@ return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_tha
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,) {final _that = this;
switch (_that) {
case _AppState() when $default != null:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
return null;
}
@@ -251,7 +250,7 @@ return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_tha
class _AppState implements AppState {
const _AppState({this.isInit = false, this.backBlock = false, this.pageLabel = PageLabel.dashboard, final List<Package> packages = const [], this.sortNum = 0, required this.viewSize, this.sideWidth = 0, final DelayMap delayMap = const {}, final List<Group> groups = const [], this.checkIpNum = 0, required this.brightness, this.runTime, final List<ExternalProvider> providers = const [], this.localIp, required this.requests, required this.version, required this.logs, required this.traffics, required this.totalTraffic, this.realTunEnable = false, this.loading = false, required this.systemUiOverlayStyle, this.profileOverrideModel, final Map<QueryTag, String> queryMap = const {}, this.coreStatus = CoreStatus.connecting}): _packages = packages,_delayMap = delayMap,_groups = groups,_providers = providers,_queryMap = queryMap;
const _AppState({this.isInit = false, this.backBlock = false, this.pageLabel = PageLabel.dashboard, final List<Package> packages = const [], this.sortNum = 0, required this.viewSize, final DelayMap delayMap = const {}, final List<Group> groups = const [], this.checkIpNum = 0, required this.brightness, this.runTime, final List<ExternalProvider> providers = const [], this.localIp, required this.requests, required this.version, required this.logs, required this.traffics, required this.totalTraffic, this.realTunEnable = false, this.loading = false, required this.systemUiOverlayStyle, this.profileOverrideModel, final Map<QueryTag, String> queryMap = const {}, this.coreStatus = CoreStatus.connecting}): _packages = packages,_delayMap = delayMap,_groups = groups,_providers = providers,_queryMap = queryMap;
@override@JsonKey() final bool isInit;
@@ -266,7 +265,6 @@ class _AppState implements AppState {
@override@JsonKey() final int sortNum;
@override final Size viewSize;
@override@JsonKey() final double sideWidth;
final DelayMap _delayMap;
@override@JsonKey() DelayMap get delayMap {
if (_delayMap is EqualUnmodifiableMapView) return _delayMap;
@@ -320,16 +318,16 @@ _$AppStateCopyWith<_AppState> get copyWith => __$AppStateCopyWithImpl<_AppState>
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppState&&(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)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other._delayMap, _delayMap)&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other._providers, _providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other._queryMap, _queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppState&&(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)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&const DeepCollectionEquality().equals(other._delayMap, _delayMap)&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other._providers, _providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other._queryMap, _queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
}
@override
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(_packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(_delayMap),const DeepCollectionEquality().hash(_groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(_providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(_queryMap),coreStatus]);
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,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(_queryMap),coreStatus]);
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
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, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
}
@@ -340,7 +338,7 @@ abstract mixin class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res>
factory _$AppStateCopyWith(_AppState value, $Res Function(_AppState) _then) = __$AppStateCopyWithImpl;
@override @useResult
$Res call({
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
});
@@ -357,7 +355,7 @@ class __$AppStateCopyWithImpl<$Res>
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
return _then(_AppState(
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
@@ -365,8 +363,7 @@ as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: ca
as PageLabel,packages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
as double,delayMap: null == delayMap ? _self._delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as Size,delayMap: null == delayMap ? _self._delayMap : delayMap // ignore: cast_nullable_to_non_nullable
as DelayMap,groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable

View File

@@ -5457,7 +5457,7 @@ $OverrideDataCopyWith<$Res> get overrideData {
/// @nodoc
mixin _$DashboardState {
List<DashboardWidget> get dashboardWidgets; double get contentWidth;
List<DashboardWidget> get dashboardWidgets; double get viewWidth;
/// Create a copy of DashboardState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -5468,16 +5468,16 @@ $DashboardStateCopyWith<DashboardState> get copyWith => _$DashboardStateCopyWith
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardState&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.contentWidth, contentWidth) || other.contentWidth == contentWidth));
return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardState&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.viewWidth, viewWidth) || other.viewWidth == viewWidth));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(dashboardWidgets),contentWidth);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(dashboardWidgets),viewWidth);
@override
String toString() {
return 'DashboardState(dashboardWidgets: $dashboardWidgets, contentWidth: $contentWidth)';
return 'DashboardState(dashboardWidgets: $dashboardWidgets, viewWidth: $viewWidth)';
}
@@ -5488,7 +5488,7 @@ abstract mixin class $DashboardStateCopyWith<$Res> {
factory $DashboardStateCopyWith(DashboardState value, $Res Function(DashboardState) _then) = _$DashboardStateCopyWithImpl;
@useResult
$Res call({
List<DashboardWidget> dashboardWidgets, double contentWidth
List<DashboardWidget> dashboardWidgets, double viewWidth
});
@@ -5505,10 +5505,10 @@ class _$DashboardStateCopyWithImpl<$Res>
/// Create a copy of DashboardState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? dashboardWidgets = null,Object? contentWidth = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? dashboardWidgets = null,Object? viewWidth = null,}) {
return _then(_self.copyWith(
dashboardWidgets: null == dashboardWidgets ? _self.dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
as List<DashboardWidget>,contentWidth: null == contentWidth ? _self.contentWidth : contentWidth // ignore: cast_nullable_to_non_nullable
as List<DashboardWidget>,viewWidth: null == viewWidth ? _self.viewWidth : viewWidth // ignore: cast_nullable_to_non_nullable
as double,
));
}
@@ -5594,10 +5594,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double contentWidth)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double viewWidth)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DashboardState() when $default != null:
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
return $default(_that.dashboardWidgets,_that.viewWidth);case _:
return orElse();
}
@@ -5615,10 +5615,10 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double contentWidth) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets, double viewWidth) $default,) {final _that = this;
switch (_that) {
case _DashboardState():
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
return $default(_that.dashboardWidgets,_that.viewWidth);case _:
throw StateError('Unexpected subclass');
}
@@ -5635,10 +5635,10 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DashboardWidget> dashboardWidgets, double contentWidth)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DashboardWidget> dashboardWidgets, double viewWidth)? $default,) {final _that = this;
switch (_that) {
case _DashboardState() when $default != null:
return $default(_that.dashboardWidgets,_that.contentWidth);case _:
return $default(_that.dashboardWidgets,_that.viewWidth);case _:
return null;
}
@@ -5650,7 +5650,7 @@ return $default(_that.dashboardWidgets,_that.contentWidth);case _:
class _DashboardState implements DashboardState {
const _DashboardState({required final List<DashboardWidget> dashboardWidgets, required this.contentWidth}): _dashboardWidgets = dashboardWidgets;
const _DashboardState({required final List<DashboardWidget> dashboardWidgets, required this.viewWidth}): _dashboardWidgets = dashboardWidgets;
final List<DashboardWidget> _dashboardWidgets;
@@ -5660,7 +5660,7 @@ class _DashboardState implements DashboardState {
return EqualUnmodifiableListView(_dashboardWidgets);
}
@override final double contentWidth;
@override final double viewWidth;
/// Create a copy of DashboardState
/// with the given fields replaced by the non-null parameter values.
@@ -5672,16 +5672,16 @@ _$DashboardStateCopyWith<_DashboardState> get copyWith => __$DashboardStateCopyW
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardState&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.contentWidth, contentWidth) || other.contentWidth == contentWidth));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardState&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.viewWidth, viewWidth) || other.viewWidth == viewWidth));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_dashboardWidgets),contentWidth);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_dashboardWidgets),viewWidth);
@override
String toString() {
return 'DashboardState(dashboardWidgets: $dashboardWidgets, contentWidth: $contentWidth)';
return 'DashboardState(dashboardWidgets: $dashboardWidgets, viewWidth: $viewWidth)';
}
@@ -5692,7 +5692,7 @@ abstract mixin class _$DashboardStateCopyWith<$Res> implements $DashboardStateCo
factory _$DashboardStateCopyWith(_DashboardState value, $Res Function(_DashboardState) _then) = __$DashboardStateCopyWithImpl;
@override @useResult
$Res call({
List<DashboardWidget> dashboardWidgets, double contentWidth
List<DashboardWidget> dashboardWidgets, double viewWidth
});
@@ -5709,10 +5709,10 @@ class __$DashboardStateCopyWithImpl<$Res>
/// Create a copy of DashboardState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? dashboardWidgets = null,Object? contentWidth = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? dashboardWidgets = null,Object? viewWidth = null,}) {
return _then(_DashboardState(
dashboardWidgets: null == dashboardWidgets ? _self._dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
as List<DashboardWidget>,contentWidth: null == contentWidth ? _self.contentWidth : contentWidth // ignore: cast_nullable_to_non_nullable
as List<DashboardWidget>,viewWidth: null == viewWidth ? _self.viewWidth : viewWidth // ignore: cast_nullable_to_non_nullable
as double,
));
}

View File

@@ -1,4 +1,5 @@
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart';
@@ -159,20 +160,25 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
List<Package> getSortList(List<String> selectedList) {
final sort = accessControl.sort;
return list.sorted((a, b) {
final isSelectA = selectedList.contains(a.packageName);
final isSelectB = selectedList.contains(b.packageName);
if (isSelectA != isSelectB) {
return isSelectA ? -1 : 1;
}
return switch (sort) {
AccessSortType.none => 0,
AccessSortType.name => a.label.compareTo(b.label),
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
};
});
return list
.sorted((a, b) {
return switch (sort) {
AccessSortType.none => 0,
AccessSortType.name => utils.sortByChar(
utils.getPinyin(a.label),
utils.getPinyin(b.label),
),
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
};
})
.sorted((a, b) {
final isSelectA = selectedList.contains(a.packageName);
final isSelectB = selectedList.contains(b.packageName);
if (isSelectA && isSelectB) return 0;
if (isSelectA) return -1;
if (isSelectB) return 1;
return 0;
});
}
}
@@ -218,7 +224,7 @@ abstract class ClashConfigState with _$ClashConfigState {
abstract class DashboardState with _$DashboardState {
const factory DashboardState({
required List<DashboardWidget> dashboardWidgets,
required double contentWidth,
required double viewWidth,
}) = _DashboardState;
}

View File

@@ -156,7 +156,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
Widget build(BuildContext context) {
final isMobileView = ref.watch(isMobileViewProvider);
return CommonPopScope(
onPop: (context) async {
onPop: () async {
if (widget.onPop == null) {
return true;
}

View File

@@ -1,6 +1,5 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/manager/app_manager.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -16,86 +15,84 @@ class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return HomeBackScopeContainer(
child: AppSidebarContainer(
child: Material(
color: context.colorScheme.surface,
child: Consumer(
builder: (context, ref, _) {
final state = ref.watch(navigationStateProvider);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final pageView = _HomePageView(
pageBuilder: (_, index) {
final navigationItem = state.navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = KeepScope(
keep: navigationItem.keep,
child: isMobile
? navigationView
: Navigator(
pages: [MaterialPage(child: navigationView)],
onDidRemovePage: (_) {},
),
);
return view;
},
);
final currentIndex = state.currentIndex;
final bottomNavigationBar = NavigationBarTheme(
data: _NavigationBarDefaultsM3(context),
child: NavigationBar(
destinations: navigationItems
.map(
(e) => NavigationDestination(
icon: e.icon,
label: Intl.message(e.label.name),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
selectedIndex: currentIndex,
),
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: globalState.appState.systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: pageView,
return HomeBackScope(
child: Material(
color: context.colorScheme.surface,
child: Consumer(
builder: (context, ref, _) {
final state = ref.watch(navigationStateProvider);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final pageView = _HomePageView(
pageBuilder: (_, index) {
final navigationItem = state.navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = KeepScope(
keep: navigationItem.keep,
child: isMobile
? navigationView
: Navigator(
pages: [MaterialPage(child: navigationView)],
onDidRemovePage: (_) {},
),
);
return view;
},
);
final currentIndex = state.currentIndex;
final bottomNavigationBar = NavigationBarTheme(
data: _NavigationBarDefaultsM3(context),
child: NavigationBar(
destinations: navigationItems
.map(
(e) => NavigationDestination(
icon: e.icon,
label: Intl.message(e.label.name),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
)
.toList(),
onDestinationSelected: (index) {
globalState.appController.toPage(
navigationItems[index].label,
);
},
selectedIndex: currentIndex,
),
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: globalState.appState.systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
child: pageView,
),
],
),
);
} else {
return pageView;
}
},
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
),
],
),
);
} else {
return pageView;
}
},
),
),
);
@@ -246,27 +243,27 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
}
}
class HomeBackScopeContainer extends ConsumerWidget {
class HomeBackScope extends StatelessWidget {
final Widget child;
const HomeBackScopeContainer({super.key, required this.child});
const HomeBackScope({super.key, required this.child});
@override
Widget build(BuildContext context, ref) {
return CommonPopScope(
onPop: (context) async {
final pageLabel = ref.read(currentPageLabelProvider);
final realContext =
GlobalObjectKey(pageLabel).currentContext ?? context;
final canPop = Navigator.canPop(realContext);
if (canPop) {
Navigator.of(realContext).pop();
} else {
await globalState.appController.handleBackOrExit();
}
return false;
},
child: child,
);
Widget build(BuildContext context) {
if (system.isAndroid) {
return CommonPopScope(
onPop: () async {
final canPop = Navigator.canPop(context);
if (canPop) {
Navigator.pop(context);
} else {
await globalState.appController.handleBackOrExit();
}
return false;
},
child: child,
);
}
return child;
}
}

View File

@@ -89,10 +89,6 @@ class Service {
return await methodChannel.invokeMethod<String>('init') ?? '';
}
Future<bool> shutdown() async {
return await methodChannel.invokeMethod<bool>('shutdown') ?? true;
}
Future<DateTime?> getRunTime() async {
final ms = await methodChannel.invokeMethod<int>('getRunTime') ?? 0;
if (ms == 0) {

View File

@@ -185,19 +185,6 @@ class ViewSize extends _$ViewSize with AutoDisposeNotifierMixin {
}
}
@riverpod
class SideWidth extends _$SideWidth with AutoDisposeNotifierMixin {
@override
double build() {
return globalState.appState.sideWidth;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(sideWidth: value);
}
}
@riverpod
double viewWidth(Ref ref) {
return ref.watch(viewSizeProvider).width;

View File

@@ -587,58 +587,6 @@ abstract class _$ViewSize extends $Notifier<Size> {
}
}
@ProviderFor(SideWidth)
const sideWidthProvider = SideWidthProvider._();
final class SideWidthProvider extends $NotifierProvider<SideWidth, double> {
const SideWidthProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'sideWidthProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$sideWidthHash();
@$internal
@override
SideWidth create() => SideWidth();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(double value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<double>(value),
);
}
}
String _$sideWidthHash() => r'380c2ae2136dc75346259d3c1d0dd3325e41fe49';
abstract class _$SideWidth extends $Notifier<double> {
double build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<double, double>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<double, double>,
double,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@ProviderFor(viewWidth)
const viewWidthProvider = ViewWidthProvider._();

View File

@@ -392,47 +392,6 @@ final class NavigationStateProvider
String _$navigationStateHash() => r'657dc47ecc35ba0807b58cb37e7f1baa14f6c2f9';
@ProviderFor(contentWidth)
const contentWidthProvider = ContentWidthProvider._();
final class ContentWidthProvider
extends $FunctionalProvider<double, double, double>
with $Provider<double> {
const ContentWidthProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'contentWidthProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$contentWidthHash();
@$internal
@override
$ProviderElement<double> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
double create(Ref ref) {
return contentWidth(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(double value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<double>(value),
);
}
}
String _$contentWidthHash() => r'9ac0dc2bcdc957160f59d6065cd7f846f41a95b1';
@ProviderFor(dashboardState)
const dashboardStateProvider = DashboardStateProvider._();
@@ -472,7 +431,7 @@ final class DashboardStateProvider
}
}
String _$dashboardStateHash() => r'e8616e327c1b96658d917e4e4950f70ecb099d85';
String _$dashboardStateHash() => r'4434206df2753d7df9eb5223c07ddead4ed170fa';
@ProviderFor(proxiesActionsState)
const proxiesActionsStateProvider = ProxiesActionsStateProvider._();
@@ -616,7 +575,7 @@ final class ProfilesSelectorStateProvider
}
String _$profilesSelectorStateHash() =>
r'da4a4382d7054dfe4010e44e55368d31ec805536';
r'aac2deee6e747eceaf62cb5f279ec99ce9227a5a';
@ProviderFor(filterGroupsState)
const filterGroupsStateProvider = FilterGroupsStateFamily._();
@@ -1551,7 +1510,7 @@ final class GetProxiesColumnsProvider extends $FunctionalProvider<int, int, int>
}
}
String _$getProxiesColumnsHash() => r'd5340932d4812727caa670065bce30450f1c4da5';
String _$getProxiesColumnsHash() => r'725066b5fc21f590a4c2656a1fd5e14ab7079079';
@ProviderFor(realSelectedProxyState)
const realSelectedProxyStateProvider = RealSelectedProxyStateFamily._();

View File

@@ -192,22 +192,15 @@ NavigationState navigationState(Ref ref) {
);
}
@riverpod
double contentWidth(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final sideWidth = ref.watch(sideWidthProvider);
return viewWidth - sideWidth;
}
@riverpod
DashboardState dashboardState(Ref ref) {
final dashboardWidgets = ref.watch(
appSettingProvider.select((state) => state.dashboardWidgets),
);
final contentWidth = ref.watch(contentWidthProvider);
final viewWidth = ref.watch(viewWidthProvider);
return DashboardState(
dashboardWidgets: dashboardWidgets,
contentWidth: contentWidth,
viewWidth: viewWidth,
);
}
@@ -241,7 +234,7 @@ ProfilesSelectorState profilesSelectorState(Ref ref) {
final currentProfileId = ref.watch(currentProfileIdProvider);
final profiles = ref.watch(profilesProvider);
final columns = ref.watch(
contentWidthProvider.select((state) => utils.getProfilesColumns(state)),
viewWidthProvider.select((state) => utils.getProfilesColumns(state)),
);
return ProfilesSelectorState(
profiles: profiles,
@@ -460,11 +453,11 @@ Profile? currentProfile(Ref ref) {
@riverpod
int getProxiesColumns(Ref ref) {
final contentWidth = ref.watch(contentWidthProvider);
final viewWidth = ref.watch(viewWidthProvider);
final proxiesLayout = ref.watch(
proxiesStyleSettingProvider.select((state) => state.layout),
);
return utils.getProxiesColumns(contentWidth, proxiesLayout);
return utils.getProxiesColumns(viewWidth, proxiesLayout);
}
@riverpod

View File

@@ -47,7 +47,6 @@ class GlobalState {
final navigatorKey = GlobalKey<NavigatorState>();
AppController? _appController;
bool isInit = false;
bool isUserDisconnected = false;
bool get isStart => startTime != null && startTime!.isBeforeNow;
@@ -183,14 +182,12 @@ class GlobalState {
}
Future<bool?> showMessage({
required InlineSpan message,
BuildContext? context,
String? title,
required InlineSpan message,
String? confirmText,
bool cancelable = true,
}) async {
return await showCommonDialog<bool>(
context: context,
child: Builder(
builder: (context) {
return CommonDialog(
@@ -248,12 +245,10 @@ class GlobalState {
Future<T?> showCommonDialog<T>({
required Widget child,
BuildContext? context,
bool dismissible = true,
}) async {
return await showModal<T>(
useRootNavigator: false,
context: context ?? globalState.navigatorKey.currentContext!,
context: navigatorKey.currentState!.context,
configuration: FadeScaleTransitionConfiguration(
barrierColor: Colors.black38,
barrierDismissible: dismissible,

View File

@@ -202,7 +202,7 @@ class TrackerInfoDetailView extends StatelessWidget {
return rule;
}
String _getProcessText() {
String _getProgressText() {
final process = trackerInfo.metadata.process;
final uid = trackerInfo.metadata.uid;
if (uid != 0) {
@@ -297,8 +297,8 @@ class TrackerInfoDetailView extends StatelessWidget {
title: appLocalizations.creationTime,
desc: trackerInfo.start.showFull,
),
if (_getProcessText().isNotEmpty)
_buildItem(title: appLocalizations.process, desc: _getProcessText()),
if (_getProgressText().isNotEmpty)
_buildItem(title: appLocalizations.progress, desc: _getProgressText()),
_buildItem(
title: appLocalizations.networkType,
desc: trackerInfo.metadata.network,

View File

@@ -63,84 +63,64 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
Consumer(
builder: (_, ref, _) {
final coreStatus = ref.watch(coreStatusProvider);
return Tooltip(
message: appLocalizations.coreStatus,
child: FadeScaleBox(
alignment: Alignment.centerRight,
child: coreStatus == CoreStatus.connected
? IconButton.filled(
return FadeScaleBox(
child: coreStatus == CoreStatus.connected
? SizedBox()
: FilledButton.icon(
key: ValueKey(coreStatus),
onPressed: coreStatus == CoreStatus.connecting
? () {}
: _handleConnection,
style: FilledButton.styleFrom(
visualDensity: VisualDensity.compact,
iconSize: 20,
padding: EdgeInsets.zero,
style: IconButton.styleFrom(
backgroundColor: Colors.greenAccent,
foregroundColor: switch (Theme.brightnessOf(
padding: EdgeInsets.symmetric(horizontal: 12),
backgroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => Colors.greenAccent,
CoreStatus.disconnected => context.colorScheme.error,
},
foregroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => switch (Theme.brightnessOf(
context,
)) {
Brightness.light =>
context.colorScheme.onSurfaceVariant,
Brightness.dark =>
context.colorScheme.onPrimaryFixedVariant,
Brightness.dark => null,
},
),
onPressed: _handleConnection,
icon: Icon(Icons.check, fontWeight: FontWeight.w900),
)
: FilledButton.icon(
key: ValueKey(coreStatus),
onPressed: _handleConnection,
style: FilledButton.styleFrom(
visualDensity: VisualDensity.compact,
padding: EdgeInsets.symmetric(horizontal: 12),
backgroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => Colors.greenAccent,
CoreStatus.disconnected =>
context.colorScheme.error,
},
foregroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => switch (Theme.brightnessOf(
context,
)) {
Brightness.light =>
context.colorScheme.onSurfaceVariant,
Brightness.dark => null,
},
CoreStatus.disconnected =>
context.colorScheme.onError,
},
),
icon: SizedBox(
height: globalState.measure.bodyMediumHeight,
width: globalState.measure.bodyMediumHeight,
child: switch (coreStatus) {
CoreStatus.connecting => Padding(
padding: EdgeInsets.all(2),
child: CircularProgressIndicator(
strokeWidth: 3,
color: context.colorScheme.onPrimary,
backgroundColor: Colors.transparent,
),
),
CoreStatus.connected => Icon(
Icons.check_sharp,
fontWeight: FontWeight.w900,
),
CoreStatus.disconnected => Icon(
Icons.restart_alt_sharp,
fontWeight: FontWeight.w900,
),
},
),
label: Text(switch (coreStatus) {
CoreStatus.connecting => appLocalizations.connecting,
CoreStatus.connected => appLocalizations.connected,
CoreStatus.disconnected =>
appLocalizations.disconnected,
}),
context.colorScheme.onError,
},
),
),
icon: SizedBox(
height: globalState.measure.bodyMediumHeight,
width: globalState.measure.bodyMediumHeight,
child: switch (coreStatus) {
CoreStatus.connecting => Padding(
padding: EdgeInsets.all(2),
child: CircularProgressIndicator(
strokeWidth: 3,
color: context.colorScheme.onPrimary,
backgroundColor: Colors.transparent,
),
),
CoreStatus.connected => Icon(
Icons.check_sharp,
fontWeight: FontWeight.w900,
),
CoreStatus.disconnected => Icon(
Icons.restart_alt_sharp,
fontWeight: FontWeight.w900,
),
},
),
label: Text(switch (coreStatus) {
CoreStatus.connecting => appLocalizations.connecting,
CoreStatus.connected => appLocalizations.connected,
CoreStatus.disconnected =>
appLocalizations.disconnected,
}),
),
);
},
),
@@ -227,7 +207,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
@override
Widget build(BuildContext context) {
final dashboardState = ref.watch(dashboardStateProvider);
final columns = max(4 * ((dashboardState.contentWidth / 300).ceil()), 8);
final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);
final spacing = 16.ap;
final children = [
...dashboardState.dashboardWidgets
@@ -276,7 +256,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
_handleSave();
},
),
onPop: (context) {
onPop: () {
_handleUpdateIsEdit();
return false;
},

View File

@@ -108,75 +108,56 @@ class OutboundModeV2 extends StatelessWidget {
Mode.global => globalState.theme.darken3PrimaryContainer,
Mode.direct => context.colorScheme.tertiaryContainer,
};
return LayoutBuilder(
builder: (_, constraints) {
return Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(12),
constraints: BoxConstraints.expand(),
child: CommonTabBar<Mode>(
children: Map.fromEntries(
Mode.values.map(
(item) => MapEntry(
item,
Container(
clipBehavior: Clip.antiAlias,
alignment: Alignment.center,
decoration: BoxDecoration(),
height: height - 8.ap - 24,
padding: EdgeInsets.all(4),
child: Text(
Intl.message(item.name),
style: Theme.of(context)
.textTheme
.titleSmall
?.adjustSize(1)
.copyWith(
color: item == mode
? _getTextColor(context, item)
: null,
),
),
),
return Container(
constraints: BoxConstraints.expand(),
padding: EdgeInsets.all(4.ap),
color: thumbColor.opacity38,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: context.colorScheme.surfaceContainer,
),
padding: EdgeInsets.all(12.ap),
child: LayoutBuilder(
builder: (_, constraints) {
return CommonTabBar<Mode>(
children: Map.fromEntries(
Mode.values.map(
(item) => MapEntry(
item,
Container(
clipBehavior: Clip.antiAlias,
alignment: Alignment.center,
decoration: BoxDecoration(),
height: constraints.maxHeight,
padding: EdgeInsets.all(4),
child: Text(
Intl.message(item.name),
style: Theme.of(context).textTheme.titleSmall
?.adjustSize(1)
.copyWith(
color: item == mode
? _getTextColor(context, item)
: null,
),
),
),
),
padding: EdgeInsets.symmetric(horizontal: 0),
groupValue: mode,
onValueChanged: (value) {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
thumbColor: thumbColor,
),
),
),
Container(
color: thumbColor.opacity50,
height: 8.ap,
width: constraints.maxWidth,
padding: EdgeInsets.symmetric(horizontal: 16),
// child: Row(
// children: [
// Container(
// width: (constraints.maxWidth - 32) / 3,
// height: 3,
// decoration: BoxDecoration(
// color: _getTextColor(context, mode),
// borderRadius: BorderRadius.circular(2),
// ),
// ),
// ],
// ),
),
],
);
},
padding: EdgeInsets.symmetric(horizontal: 0),
groupValue: mode,
onValueChanged: (value) {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
thumbColor: thumbColor,
);
},
),
),
);
},
),

View File

@@ -306,7 +306,7 @@ class _EditProfileViewState extends State<EditProfileView> {
),
];
return CommonPopScope(
onPop: (context) {
onPop: () {
if (fileData == null) {
return true;
}

View File

@@ -214,7 +214,7 @@ class _OverrideProfileViewState extends ConsumerState<OverrideProfileView> {
return SizedBox();
}
return CommonPopScope(
onPop: (context) async {
onPop: () async {
if (equals) {
return true;
}

View File

@@ -237,7 +237,7 @@ class ProfileItem extends StatelessWidget {
SubscriptionInfoView(subscriptionInfo: subscriptionInfo),
Text(
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '',
style: context.textTheme.labelMedium?.toLighter,
style: context.textTheme.labelMedium?.toLight,
),
];
}

View File

@@ -575,7 +575,7 @@ class _ListHeaderState extends State<ListHeader> {
iconSize: 19,
icon: const Icon(Icons.adjust),
),
const SizedBox(width: 2),
const SizedBox(width: 4),
IconButton(
iconSize: 20,
visualDensity: VisualDensity.compact,
@@ -583,13 +583,13 @@ class _ListHeaderState extends State<ListHeader> {
onPressed: _delayTest,
icon: const Icon(Icons.network_ping),
),
const SizedBox(width: 6),
const SizedBox(width: 8),
] else
SizedBox(width: 6),
IconButton.filledTonal(
visualDensity: VisualDensity.compact,
padding: EdgeInsets.all(2),
iconSize: 24,
padding: EdgeInsets.all(4),
iconSize: 22,
onPressed: () {
_handleChange(groupName);
},

View File

@@ -29,10 +29,10 @@ class ResourcesView extends StatelessWidget {
@override
Widget build(BuildContext context) {
const geoItems = <GeoItem>[
GeoItem(label: 'GEOIP', fileName: GEOIP, key: 'geoip'),
GeoItem(label: 'GEOSITE', fileName: GEOSITE, key: 'geosite'),
GeoItem(label: 'MMDB', fileName: MMDB, key: 'mmdb'),
GeoItem(label: 'ASN', fileName: ASN, key: 'asn'),
GeoItem(label: 'GeoIp', fileName: geoIpFileName, key: 'geoip'),
GeoItem(label: 'GeoSite', fileName: geoSiteFileName, key: 'geosite'),
GeoItem(label: 'MMDB', fileName: mmdbFileName, key: 'mmdb'),
GeoItem(label: 'ASN', fileName: asnFileName, key: 'asn'),
];
return CommonScaffold(

View File

@@ -277,7 +277,7 @@ class _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {
final isEquals = vm4.d;
return CommonPopScope(
onPop: (context) {
onPop: () {
if (_removablePrimaryColor != null) {
setState(() {
_removablePrimaryColor = null;

View File

@@ -125,7 +125,7 @@ class CommonCard extends StatelessWidget {
if (isSelected) {
return colorScheme.secondaryContainer.opacity80;
}
return colorScheme.surfaceContainerHigh;
return colorScheme.surfaceContainer;
}
if (isSelected) {
return colorScheme.secondaryContainer;

View File

@@ -94,13 +94,11 @@ class FadeRotationScaleBox extends StatelessWidget {
class FadeScaleBox extends StatelessWidget {
final Widget child;
final AlignmentGeometry? alignment;
const FadeScaleBox({super.key, required this.child, this.alignment});
const FadeScaleBox({super.key, required this.child});
@override
Widget build(BuildContext context) {
final realAlignment = alignment ?? Alignment.center;
return AnimatedSwitcher(
duration: commonDuration,
switchOutCurve: Curves.easeOutBack,
@@ -114,16 +112,6 @@ class FadeScaleBox extends StatelessWidget {
),
);
},
layoutBuilder: (currentChild, previousChildren) => Align(
alignment: realAlignment,
child: Stack(
alignment: realAlignment,
children: <Widget>[
...previousChildren,
if (currentChild != null) currentChild,
],
),
),
child: child,
);
}

View File

@@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart';
class CommonPopScope extends StatelessWidget {
final Widget child;
final FutureOr<bool> Function(BuildContext context)? onPop;
final FutureOr<bool> Function()? onPop;
final FutureOr<void> Function()? onPopSuccess;
const CommonPopScope({
@@ -25,7 +25,7 @@ class CommonPopScope extends StatelessWidget {
if (didPop) {
return;
}
final res = await onPop!(context);
final res = await onPop!();
if (!context.mounted) {
return;
}

View File

@@ -224,7 +224,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
if (_isEdit || _isSearch) {
return SystemBackBlock(
child: CommonPopScope(
onPop: (context) {
onPop: () {
if (_isEdit || _isSearch) {
_handleExitSearching();
_appBarState.value.editState?.onExit();

View File

@@ -26,8 +26,8 @@ class CommonScrollBar extends StatelessWidget {
controller: controller,
thumbVisibility: thumbVisibility,
trackVisibility: trackVisibility,
thickness: 6,
radius: const Radius.circular(6),
thickness: 8,
radius: const Radius.circular(8),
interactive: true,
child: child,
);

View File

@@ -8,6 +8,7 @@
#include <dynamic_color/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <flutter_js/flutter_js_plugin.h>
#include <gtk/gtk_plugin.h>
#include <hotkey_manager_linux/hotkey_manager_linux_plugin.h>
@@ -23,6 +24,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin");
flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar);
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
flutter_js_plugin_register_with_registrar(flutter_js_registrar);

View File

@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
file_selector_linux
flutter_acrylic
flutter_js
gtk
hotkey_manager_linux

View File

@@ -13,6 +13,7 @@ import file_picker
import file_selector_macos
import flutter_js
import hotkey_manager_macos
import macos_window_utils
import mobile_scanner
import package_info_plus
import path_provider_foundation
@@ -33,6 +34,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View File

@@ -18,6 +18,8 @@ PODS:
- hotkey_manager_macos (0.0.1):
- FlutterMacOS
- HotKey
- macos_window_utils (1.0.0):
- FlutterMacOS
- mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS
@@ -53,6 +55,7 @@ DEPENDENCIES:
- flutter_js (from `Flutter/ephemeral/.symlinks/plugins/flutter_js/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/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`)
@@ -87,6 +90,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
hotkey_manager_macos:
:path: Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos
macos_window_utils:
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
mobile_scanner:
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin
package_info_plus:
@@ -119,6 +124,7 @@ SPEC CHECKSUMS:
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6
hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe
macos_window_utils: 23f54331a0fd51eea9e0ed347253bf48fd379d1d
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564

View File

@@ -462,6 +462,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_acrylic:
dependency: "direct main"
description:
name: flutter_acrylic
sha256: b3996dbde5abf5823cc9ead4cf2e5267c3181f15585fe47ce4dc4472e7ec827a
url: "https://pub.dev"
source: hosted
version: "1.1.4"
flutter_cache_manager:
dependency: "direct main"
description:
@@ -814,6 +822,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
lpinyin:
dependency: "direct main"
description:
name: lpinyin
sha256: "0bb843363f1f65170efd09fbdfc760c7ec34fc6354f9fcb2f89e74866a0d814a"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
macos_window_utils:
dependency: transitive
description:
name: macos_window_utils
sha256: d4df3501fd32ac0d2d7590cb6a8e4758337d061c8fa0db816fdd636be63a8438
url: "https://pub.dev"
source: hosted
version: "1.9.0"
matcher:
dependency: transitive
description:

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.88+2025090702
version: 0.8.88+2025083102
environment:
sdk: '>=3.8.0 <4.0.0'
@@ -43,6 +43,7 @@ dependencies:
ref: main
re_highlight: ^0.0.3
archive: ^4.0.7
lpinyin: ^2.0.3
emoji_regex: ^0.0.5
hotkey_manager: ^0.2.3
uni_platform: ^0.1.3
@@ -61,6 +62,7 @@ dependencies:
flutter_svg: ^2.1.0
flutter_cache_manager: ^3.4.1
crypto: ^3.0.3
flutter_acrylic: ^1.1.4
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -10,6 +10,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <flutter_js/flutter_js_plugin.h>
#include <hotkey_manager_windows/hotkey_manager_windows_plugin_c_api.h>
#include <proxy/proxy_plugin_c_api.h>
@@ -28,6 +29,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterAcrylicPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterAcrylicPlugin"));
FlutterJsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
HotkeyManagerWindowsPluginCApiRegisterWithRegistrar(

View File

@@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
dynamic_color
file_selector_windows
flutter_acrylic
flutter_js
hotkey_manager_windows
proxy