Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
5756fba7e8 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-08-31 16:05:25 +08:00
51 changed files with 1226 additions and 766 deletions

View File

@@ -54,7 +54,7 @@ Support the following actions
com.follow.clash.action.STOP
com.follow.clash.action.CHANGE
com.follow.clash.action.TOGGLE
```
## Download

View File

@@ -54,7 +54,7 @@ on Mobile:
com.follow.clash.action.STOP
com.follow.clash.action.CHANGE
com.follow.clash.action.TOGGLE
```
## Download

View File

@@ -1,9 +1,9 @@
include: package:flutter_lints/flutter.yaml
analyzer:
plugins:
- custom_lint
exclude:
- lib/l10n/intl/**
errors:
invalid_annotation_target: ignore
linter:
rules:

View File

@@ -1,28 +1,76 @@
package com.follow.clash
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Base64
import androidx.core.graphics.drawable.toBitmap
import com.follow.clash.common.GlobalState
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
suspend fun Drawable.getBase64(): String {
val drawable = this
return withContext(Dispatchers.IO) {
val bitmap = drawable.toBitmap()
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP)
private const val ICON_TTL_DAYS = 1L
suspend fun PackageManager.getPackageIconPath(packageName: String): String =
withContext(Dispatchers.IO) {
val cacheDir = GlobalState.application.cacheDir
val iconDir = File(cacheDir, "icons").apply { mkdirs() }
val pkgInfo = getPackageInfo(packageName, 0)
val lastUpdateTime = pkgInfo.lastUpdateTime
val iconFile = File(iconDir, "${packageName}_${lastUpdateTime}.webp")
if (iconFile.exists() && !isExpired(iconFile)) {
return@withContext iconFile.absolutePath
}
iconDir.listFiles()?.forEach { file ->
if (file.name.startsWith(packageName + "_")) file.delete()
}
return@withContext try {
val icon = getApplicationIcon(packageName)
saveDrawableToFile(icon, iconFile)
iconFile.absolutePath
} catch (_: Exception) {
val defaultIconFile = File(iconDir, "default_icon.webp")
if (!defaultIconFile.exists()) {
saveDrawableToFile(defaultActivityIcon, defaultIconFile)
}
defaultIconFile.absolutePath
}
}
private fun saveDrawableToFile(drawable: Drawable, file: File) {
val bitmap = drawable.toBitmap()
try {
val format = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
Bitmap.CompressFormat.WEBP_LOSSY
}
else -> {
Bitmap.CompressFormat.WEBP
}
}
FileOutputStream(file).use { fos ->
bitmap.compress(format, 90, fos)
}
} finally {
if (!bitmap.isRecycled) bitmap.recycle()
}
}
private fun isExpired(file: File): Boolean {
val now = System.currentTimeMillis()
val age = now - file.lastModified()
return age > TimeUnit.DAYS.toMillis(ICON_TTL_DAYS)
}
suspend fun <T> MethodChannel.awaitResult(

View File

@@ -22,7 +22,7 @@ import com.follow.clash.common.Components
import com.follow.clash.common.GlobalState
import com.follow.clash.common.QuickAction
import com.follow.clash.common.quickIntent
import com.follow.clash.getBase64
import com.follow.clash.getPackageIconPath
import com.follow.clash.models.Package
import com.google.gson.Gson
import io.flutter.embedding.android.FlutterActivity
@@ -150,26 +150,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
"getPackageIcon" -> {
scope.launch {
val packageName = call.argument<String>("packageName")
if (packageName == null) {
result.success(null)
return@launch
}
val packageIcon = getPackageIcon(packageName)
packageIcon.let {
if (it != null) {
result.success(it)
return@launch
}
if (iconMap["default"] == null) {
iconMap["default"] =
GlobalState.application.packageManager?.defaultActivityIcon?.getBase64()
}
result.success(iconMap["default"])
return@launch
}
}
handleGetPackageIcon(call, result)
}
"tip" -> {
@@ -184,6 +165,19 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
}
private fun handleGetPackageIcon(call: MethodCall, result: Result) {
scope.launch {
val packageName = call.argument<String>("packageName")
if (packageName == null) {
result.success("")
return@launch
}
val path =
GlobalState.application.packageManager.getPackageIconPath(packageName)
result.success(path)
}
}
private fun initShortcuts(label: String) {
val shortcut = with(ShortcutInfoCompat.Builder(GlobalState.application, "toggle")) {
setShortLabel(label)
@@ -223,18 +217,6 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
}
private suspend fun getPackageIcon(packageName: String): String? {
val packageManager = GlobalState.application.packageManager
if (iconMap[packageName] == null) {
iconMap[packageName] = try {
packageManager?.getApplicationIcon(packageName)?.getBase64()
} catch (_: Exception) {
null
}
}
return iconMap[packageName]
}
private fun getPackages(): List<Package> {
val packageManager = GlobalState.application.packageManager

View File

@@ -28,10 +28,6 @@ class RemoteService : Service(),
}
}
fun onServiceDisconnected() {
handleStopService()
}
private fun handleStartService() {
launch {
val nextIntent = when (State.options?.enable == true) {
@@ -40,7 +36,7 @@ class RemoteService : Service(),
}
if (intent != nextIntent) {
delegate?.unbind()
delegate = ServiceDelegate(nextIntent, ::onServiceDisconnected) { binder ->
delegate = ServiceDelegate(nextIntent) { binder ->
when (binder) {
is VpnService.LocalBinder -> binder.getService()
is CommonService.LocalBinder -> binder.getService()

View File

@@ -181,6 +181,10 @@ func handleAction(action *Action, result ActionResult) {
case crashMethod:
result.success(true)
handleCrash()
case deleteFile:
path := action.Data.(string)
handleDelFile(path, result)
return
default:
nextHandle(action, result)
}

View File

@@ -99,6 +99,7 @@ const (
crashMethod Method = "crash"
setupConfigMethod Method = "setupConfig"
getConfigMethod Method = "getConfig"
deleteFile Method = "deleteFile"
)
type Method string

View File

@@ -10,7 +10,6 @@ require (
)
require (
github.com/3andne/restls-client-go v0.1.6 // indirect
github.com/RyuaNerin/go-krypto v1.3.0 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/ajg/form v1.5.1 // indirect
@@ -19,15 +18,15 @@ require (
github.com/buger/jsonparser v1.1.1 // indirect
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/ebitengine/purego v0.8.3 // indirect
github.com/enfein/mieru/v3 v3.16.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/enfein/mieru/v3 v3.19.1 // indirect
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.2.2 // indirect
github.com/go-chi/chi/v5 v5.2.3 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
@@ -42,35 +41,36 @@ require (
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a // indirect
github.com/metacubex/ascon v0.1.0 // indirect
github.com/metacubex/bart v0.20.5 // indirect
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b // indirect
github.com/metacubex/blake3 v0.1.0 // indirect
github.com/metacubex/chacha v0.1.5 // indirect
github.com/metacubex/fswatch v0.1.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/metacubex/quic-go v0.53.1-0.20250628094454-fda5262d1d9c // indirect
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing v0.5.4 // indirect
github.com/metacubex/sing-mux v0.3.2 // indirect
github.com/metacubex/restls-client-go v0.1.7 // indirect
github.com/metacubex/sing v0.5.5 // indirect
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac // indirect
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb // indirect
github.com/metacubex/sing-shadowsocks v0.2.11 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.5 // indirect
github.com/metacubex/sing-shadowsocks v0.2.12 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.6 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97 // indirect
github.com/metacubex/sing-vmess v0.2.3 // indirect
github.com/metacubex/sing-tun v0.4.7 // indirect
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 // indirect
github.com/metacubex/utls v1.8.0 // indirect
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 // indirect
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 // indirect
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 // indirect
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
@@ -110,5 +110,4 @@ require (
golang.org/x/tools v0.24.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
)

View File

@@ -1,5 +1,3 @@
github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08=
github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY=
github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=
github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=
@@ -24,10 +22,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.16.1 h1:CfIt1pQCCQbohkw+HBD2o8V9tnhZvB5yuXGGQIXTLOs=
github.com/enfein/mieru/v3 v3.16.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I=
github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
@@ -41,8 +39,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
@@ -80,24 +78,24 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a h1:c1QSGpacSeQdBdWcEKZKGuWLcqIG2wxHEygAcXuDwS4=
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=
github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=
github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=
github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk=
github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
@@ -108,37 +106,39 @@ github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
github.com/metacubex/quic-go v0.53.1-0.20250628094454-fda5262d1d9c h1:ABQzmOaZddM3q0OYeoZEc0XF+KW+dUdPNvY/c5rsunI=
github.com/metacubex/quic-go v0.53.1-0.20250628094454-fda5262d1d9c/go.mod h1:eWlAK3zsKI0P8UhYpXlIsl3mtW4D6MpMNuYLIu8CKWI=
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uSJAvmyCd4TjvDxs57xjb0WxEoaWafK5+qs=
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.4 h1:a4kAOZmF+OXosbzPEcrSc5QD35/ex+MNuZsrcuWskHk=
github.com/metacubex/sing v0.5.4/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA=
github.com/metacubex/sing-shadowsocks v0.2.11 h1:p2NGNOdF95e6XvdDKipLj1FRRqR8dnbfC/7pw2CCTlw=
github.com/metacubex/sing-shadowsocks v0.2.11/go.mod h1:bT1PCTV316zFnlToRMk5zt9HmIQYRBveiT71mplYPfc=
github.com/metacubex/sing-shadowsocks2 v0.2.5 h1:MnPn0hbcDkSJt6TlpI15XImHKK6IqaOwBUGPKyMnJnE=
github.com/metacubex/sing-shadowsocks2 v0.2.5/go.mod h1:Zyh+rAQRyevYfG/COCvDs1c/YMhGqCuknn7QrGmoQIw=
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97 h1:YYpc60UZE2G0pUeHbRw9erDrUDZrPQy8QzWFqA3kHsk=
github.com/metacubex/sing-tun v0.4.7-0.20250721020617-8e7c37ed3d97/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
github.com/metacubex/sing-vmess v0.2.3 h1:QKLdIk5A2FcR3Y7m2/JO1XhfzgDA8tF4W9/ffsH9opo=
github.com/metacubex/sing-vmess v0.2.3/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 h1:WZepq4TOZa6WewB8tGAZrrL+bL2R2ivoBzuEgAeolWc=
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac=
github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ=
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 h1:yN3mQ4cT9sPUciw/rO0Isc/8QlO86DB6g9SEMRgQ8Cw=
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 h1:csEbKOzRAxJXffOeZnnS3/kA/F55JiTbKv5jcYqCXms=
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
@@ -191,7 +191,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -271,5 +271,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -20,6 +20,7 @@ import (
"github.com/metacubex/mihomo/tunnel"
"github.com/metacubex/mihomo/tunnel/statistic"
"net"
"os"
"runtime"
"sort"
"strconv"
@@ -454,6 +455,33 @@ func handleUpdateConfig(bytes []byte) string {
return ""
}
func handleDelFile(path string, result ActionResult) {
go func() {
fileInfo, err := os.Stat(path)
if err != nil {
if !os.IsNotExist(err) {
result.success(err.Error())
}
result.success("")
return
}
if fileInfo.IsDir() {
err = os.RemoveAll(path)
if err != nil {
result.success(err.Error())
return
}
} else {
err = os.Remove(path)
if err != nil {
result.success(err.Error())
return
}
}
result.success("")
}()
}
func handleSetupConfig(bytes []byte) string {
var params = defaultSetupParams()
err := UnmarshalJson(bytes, params)

View File

@@ -1,6 +1,7 @@
export 'android.dart';
export 'app_localizations.dart';
export 'color.dart';
export 'compute.dart';
export 'constant.dart';
export 'context.dart';
export 'converter.dart';

114
lib/common/compute.dart Normal file
View File

@@ -0,0 +1,114 @@
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,
required ProxiesSortType sortType,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String defaultTestUrl,
}) {
return groups.map((group) {
final proxies = group.all;
final newProxies = switch (sortType) {
ProxiesSortType.none => proxies,
ProxiesSortType.delay => _sortOfDelay(
groups: groups,
proxies: proxies,
delayMap: delayMap,
selectedMap: selectedMap,
testUrl: group.testUrl.getSafeValue(defaultTestUrl),
),
ProxiesSortType.name => _sortOfName(proxies),
};
return group.copyWith(all: newProxies);
}).toList();
}
DelayState computeProxyDelayState({
required String proxyName,
required String testUrl,
required List<Group> groups,
required SelectedMap selectedMap,
required DelayMap delayMap,
}) {
final state = computeRealSelectedProxyState(
proxyName,
groups: groups,
selectedMap: selectedMap,
);
final currentDelayMap = delayMap[state.testUrl.getSafeValue(testUrl)] ?? {};
final delay = currentDelayMap[state.proxyName];
return DelayState(delay: delay ?? 0, group: state.group);
}
SelectedProxyState computeRealSelectedProxyState(
String proxyName, {
required List<Group> groups,
required SelectedMap selectedMap,
}) {
return _getRealSelectedProxyState(
SelectedProxyState(proxyName: proxyName),
groups: groups,
selectedMap: selectedMap,
);
}
SelectedProxyState _getRealSelectedProxyState(
SelectedProxyState state, {
required List<Group> groups,
required SelectedMap selectedMap,
}) {
if (state.proxyName.isEmpty) return state;
final index = groups.indexWhere((element) => element.name == state.proxyName);
final newState = state.copyWith(group: true);
if (index == -1) return newState;
final group = groups[index];
final currentSelectedName = group.getCurrentSelectedName(
selectedMap[newState.proxyName] ?? '',
);
if (currentSelectedName.isEmpty) {
return newState;
}
return _getRealSelectedProxyState(
newState.copyWith(proxyName: currentSelectedName, testUrl: group.testUrl),
groups: groups,
selectedMap: selectedMap,
);
}
List<Proxy> _sortOfDelay({
required List<Group> groups,
required List<Proxy> proxies,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String testUrl,
}) {
return List.from(proxies)..sort((a, b) {
final aDelayState = computeProxyDelayState(
proxyName: a.name,
testUrl: testUrl,
groups: groups,
selectedMap: selectedMap,
delayMap: delayMap,
);
final bDelayState = computeProxyDelayState(
proxyName: b.name,
testUrl: testUrl,
groups: groups,
selectedMap: selectedMap,
delayMap: delayMap,
);
return aDelayState.compareTo(bDelayState);
});
}
List<Proxy> _sortOfName(List<Proxy> proxies) {
return List.of(proxies)..sort(
(a, b) =>
utils.sortByChar(utils.getPinyin(a.name), utils.getPinyin(b.name)),
);
}

View File

@@ -36,8 +36,8 @@ const geoIpFileName = 'GeoIP.dat';
const geoSiteFileName = 'GeoSite.dat';
final double kHeaderHeight = system.isDesktop
? !system.isMacOS
? 40
: 28
? 40
: 28
: 0;
const profilesDirectoryName = 'profiles';
const localhost = '127.0.0.1';
@@ -84,7 +84,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
const defaultPrimaryColor = 0XFFD8C0C3;
double getWidgetHeight(num lines) {
return max(lines * 84 + (lines - 1) * 16, 0).ap;
return max(lines * 80 + (lines - 1) * 16, 0).ap;
}
const maxLength = 1000;

View File

@@ -9,23 +9,17 @@ class Debouncer {
FunctionTag tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
Duration? duration,
}) {
final timer = _operations[tag];
if (timer != null) {
timer.cancel();
}
_operations[tag] = Timer(
duration,
() {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(
func,
args,
);
},
);
_operations[tag] = Timer(duration ?? const Duration(milliseconds: 600), () {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(func, args);
});
}
void cancel(dynamic tag) {
@@ -47,17 +41,11 @@ class Throttler {
if (timer != null) {
return true;
}
_operations[tag] = Timer(
duration,
() {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(
func,
args,
);
},
);
_operations[tag] = Timer(duration, () {
_operations[tag]?.cancel();
_operations.remove(tag);
Function.apply(func, args);
});
return false;
}

View File

@@ -1,4 +1,5 @@
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
set value(T value) {
@@ -20,3 +21,26 @@ mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
void onUpdate(T value) {}
}
mixin AnyNotifierMixin<T> on AnyNotifier<T, T> {
T get value;
set value(T value) {
if (ref.mounted) {
state = value;
} else {
onUpdate(value);
}
}
@override
bool updateShouldNotify(previous, next) {
final res = super.updateShouldNotify(previous, next);
if (res) {
onUpdate(next);
}
return res;
}
void onUpdate(T value) {}
}

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

@@ -10,6 +10,7 @@ class AppPath {
Completer<Directory> dataDir = Completer();
Completer<Directory> downloadDir = Completer();
Completer<Directory> tempDir = Completer();
Completer<Directory> cacheDir = Completer();
late String appDirPath;
AppPath._internal() {
@@ -23,6 +24,9 @@ class AppPath {
getDownloadsDirectory().then((value) {
downloadDir.complete(value);
});
getApplicationCacheDirectory().then((value) {
cacheDir.complete(value);
});
}
factory AppPath() {
@@ -82,6 +86,11 @@ class AppPath {
return join(directory, '$id.yaml');
}
Future<String> getIconsCacheDir() async {
final directory = await cacheDir.future;
return join(directory.path, 'icons');
}
Future<String> getProvidersRootPath() async {
final directory = await profilesPath;
return join(directory, 'providers');

View File

@@ -16,30 +16,24 @@ class Request {
String? userAgent;
Request() {
_dio = Dio(
BaseOptions(
headers: {
'User-Agent': browserUa,
},
),
);
_dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));
_clashDio = Dio();
_clashDio.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
final client = HttpClient();
client.findProxy = (Uri uri) {
client.userAgent = globalState.ua;
return FlClashHttpOverrides.handleFindProxy(uri);
};
return client;
});
_clashDio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.findProxy = (Uri uri) {
client.userAgent = globalState.ua;
return FlClashHttpOverrides.handleFindProxy(uri);
};
return client;
},
);
}
Future<Response> getFileResponseForUrl(String url) async {
final response = await _clashDio.get(
url,
options: Options(
responseType: ResponseType.bytes,
),
options: Options(responseType: ResponseType.bytes),
);
return response;
}
@@ -47,9 +41,7 @@ class Request {
Future<Response> getTextResponseForUrl(String url) async {
final response = await _clashDio.get(
url,
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
);
return response;
}
@@ -58,9 +50,7 @@ class Request {
if (url.isEmpty) return null;
final response = await _dio.get<Uint8List>(
url,
options: Options(
responseType: ResponseType.bytes,
),
options: Options(responseType: ResponseType.bytes),
);
final data = response.data;
if (data == null) return null;
@@ -70,9 +60,7 @@ class Request {
Future<Map<String, dynamic>?> checkForUpdate() async {
final response = await _dio.get(
'https://api.github.com/repos/$repository/releases/latest',
options: Options(
responseType: ResponseType.json,
),
options: Options(responseType: ResponseType.json),
);
if (response.statusCode != 200) return null;
final data = response.data as Map<String, dynamic>;
@@ -85,9 +73,12 @@ class Request {
}
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
'https://ipwho.is/': IpInfo.fromIpwhoIsJson,
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
'https://ipwho.is/': IpInfo.fromIpWhoIsJson,
'https://api.myip.com/': IpInfo.fromMyIpJson,
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
'https://ident.me/json/': IpInfo.fromIdentMeJson,
'http://ip-api.com/json/': IpInfo.fromIpAPIJson,
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
};
@@ -101,27 +92,28 @@ class Request {
}
}
final future = Dio().get<Map<String, dynamic>>(
final future = _dio.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(
responseType: ResponseType.json,
),
options: Options(responseType: ResponseType.json),
);
future.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
} else {
failureCount++;
handleFailRes();
}
}).catchError((e) {
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
}
handleFailRes();
});
future
.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
failureCount++;
handleFailRes();
})
.catchError((e) {
print(e);
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
}
handleFailRes();
});
return completer.future;
});
final res = await Future.any(futures);
@@ -134,15 +126,9 @@ class Request {
final response = await _dio
.get(
'http://$localhost:$helperPort/ping',
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}
@@ -157,19 +143,10 @@ class Request {
final response = await _dio
.post(
'http://$localhost:$helperPort/start',
data: json.encode({
'path': appPath.corePath,
'arg': arg,
}),
options: Options(
responseType: ResponseType.plain,
),
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}
@@ -185,15 +162,9 @@ class Request {
final response = await _dio
.post(
'http://$localhost:$helperPort/stop',
options: Options(
responseType: ResponseType.plain,
),
options: Options(responseType: ResponseType.plain),
)
.timeout(
const Duration(
milliseconds: 2000,
),
);
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
}

View File

@@ -21,16 +21,15 @@ class Utils {
String get id {
final timestamp = DateTime.now().microsecondsSinceEpoch;
final random = Random();
final randomStr =
String.fromCharCodes(List.generate(8, (_) => random.nextInt(26) + 97));
final randomStr = String.fromCharCodes(
List.generate(8, (_) => random.nextInt(26) + 97),
);
return '$timestamp$randomStr';
}
String getDateStringLast2(int value) {
var valueRaw = '0$value';
return valueRaw.substring(
valueRaw.length - 2,
);
return valueRaw.substring(valueRaw.length - 2);
}
String generateRandomString({int minLength = 10, int maxLength = 100}) {
@@ -43,8 +42,9 @@ class Utils {
String result = '';
for (int i = 0; i < length; i++) {
if (random.nextBool()) {
result +=
String.fromCharCode(0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1));
result += String.fromCharCode(
0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1),
);
} else {
result += latinChars[random.nextInt(latinChars.length)];
}
@@ -60,8 +60,9 @@ class Utils {
bytes[6] = (bytes[6] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
final hex =
bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
final hex = bytes
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join();
return '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20, 32)}';
}
@@ -102,9 +103,10 @@ class Utils {
}
if (localSplit.length == 3) {
return Locale.fromSubtags(
languageCode: localSplit[0],
scriptCode: localSplit[1],
countryCode: localSplit[2]);
languageCode: localSplit[0],
scriptCode: localSplit[1],
countryCode: localSplit[2],
);
}
return null;
}
@@ -141,9 +143,7 @@ class Utils {
}
}
String getTrayIconPath({
required Brightness brightness,
}) {
String getTrayIconPath({required Brightness brightness}) {
if (system.isMacOS) {
return 'assets/images/icon_white.png';
}
@@ -188,16 +188,20 @@ class Utils {
if (disposition == null) return null;
final parseValue = HeaderValue.parse(disposition);
final parameters = parseValue.parameters;
final fileNamePointKey = parameters.keys
.firstWhere((key) => key == 'filename*', orElse: () => '');
final fileNamePointKey = parameters.keys.firstWhere(
(key) => key == 'filename*',
orElse: () => '',
);
if (fileNamePointKey.isNotEmpty) {
final res = parameters[fileNamePointKey]?.split("''") ?? [];
if (res.length >= 2) {
return Uri.decodeComponent(res[1]);
}
}
final fileNameKey = parameters.keys
.firstWhere((key) => key == 'filename', orElse: () => '');
final fileNameKey = parameters.keys.firstWhere(
(key) => key == 'filename',
orElse: () => '',
);
if (fileNameKey.isEmpty) return null;
return parameters[fileNameKey];
}
@@ -236,19 +240,7 @@ class Utils {
return max((viewWidth / 320).floor(), 1);
}
final _indexPrimary = [
50,
100,
200,
300,
400,
500,
600,
700,
800,
850,
900,
];
final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900];
MaterialColor _createPrimarySwatch(Color color) {
final Map<int, Color> swatch = <int, Color>{};
@@ -302,16 +294,15 @@ class Utils {
}
Future<String?> getLocalIpAddress() async {
List<NetworkInterface> interfaces = await NetworkInterface.list(
includeLoopback: false,
)
..sort((a, b) {
if (a.isWifi && !b.isWifi) return -1;
if (!a.isWifi && b.isWifi) return 1;
if (a.includesIPv4 && !b.includesIPv4) return -1;
if (!a.includesIPv4 && b.includesIPv4) return 1;
return 0;
});
List<NetworkInterface> interfaces =
await NetworkInterface.list(includeLoopback: false)
..sort((a, b) {
if (a.isWifi && !b.isWifi) return -1;
if (!a.isWifi && b.isWifi) return 1;
if (a.includesIPv4 && !b.includesIPv4) return -1;
if (!a.includesIPv4 && b.includesIPv4) return 1;
return 0;
});
for (final interface in interfaces) {
final addresses = interface.addresses;
if (addresses.isEmpty) {
@@ -329,59 +320,9 @@ class Utils {
SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) {
final control = system.isMacOS ? false : true;
return SingleActivator(
trigger,
control: control,
meta: !control,
);
return SingleActivator(trigger, control: control, meta: !control);
}
// dynamic convertYamlNode(dynamic node) {
// if (node is YamlMap) {
// final map = <String, dynamic>{};
// YamlNode? mergeKeyNode;
// for (final entry in node.nodes.entries) {
// if (entry.key is YamlScalar &&
// (entry.key as YamlScalar).value == '<<') {
// mergeKeyNode = entry.value;
// break;
// }
// }
// if (mergeKeyNode != null) {
// final mergeValue = mergeKeyNode.value;
// if (mergeValue is YamlMap) {
// map.addAll(convertYamlNode(mergeValue) as Map<String, dynamic>);
// } else if (mergeValue is YamlList) {
// for (final node in mergeValue.nodes) {
// if (node.value is YamlMap) {
// map.addAll(convertYamlNode(node.value) as Map<String, dynamic>);
// }
// }
// }
// }
//
// node.nodes.forEach((key, value) {
// String stringKey;
// if (key is YamlScalar) {
// stringKey = key.value.toString();
// } else {
// stringKey = key.toString();
// }
// map[stringKey] = convertYamlNode(value.value);
// });
// return map;
// } else if (node is YamlList) {
// final list = <dynamic>[];
// for (final item in node.nodes) {
// list.add(convertYamlNode(item.value));
// }
// return list;
// } else if (node is YamlScalar) {
// return node.value;
// }
// return node;
// }
FutureOr<T> handleWatch<T>(Function function) async {
if (kDebugMode) {
final stopwatch = Stopwatch()..start();

View File

@@ -41,8 +41,8 @@ class AppController {
});
}
void updateGroupsDebounce() {
debouncer.call(FunctionTag.updateGroups, updateGroups);
void updateGroupsDebounce([Duration? duration]) {
debouncer.call(FunctionTag.updateGroups, updateGroups, duration: duration);
}
void addCheckIpNumDebounce() {
@@ -231,10 +231,6 @@ class AppController {
return currentGroupName;
}
ProxyCardState getProxyCardState(String proxyName) {
return _ref.read(getProxyCardStateProvider(proxyName));
}
String? getSelectedProxyName(String groupName) {
return _ref.read(getSelectedProxyNameProvider(groupName));
}
@@ -366,7 +362,22 @@ class AppController {
try {
_ref.read(groupsProvider.notifier).value = await retry(
task: () async {
return await coreController.getProxiesGroups();
final sortType = _ref.read(
proxiesStyleSettingProvider.select((state) => state.sortType),
);
final delayMap = _ref.read(delayDataSourceProvider);
final testUrl = _ref.read(
appSettingProvider.select((state) => state.testUrl),
);
final selectedMap = _ref.read(
currentProfileProvider.select((state) => state?.selectedMap ?? {}),
);
return await coreController.getProxiesGroups(
selectedMap: selectedMap,
sortType: sortType,
delayMap: delayMap,
defaultTestUrl: testUrl,
);
},
retryIf: (res) => res.isEmpty,
);
@@ -691,47 +702,7 @@ class AppController {
_ref.read(providersProvider.notifier).setProvider(provider);
}
List<Proxy> _sortOfName(List<Proxy> proxies) {
return List.of(proxies)..sort(
(a, b) =>
utils.sortByChar(utils.getPinyin(a.name), utils.getPinyin(b.name)),
);
}
List<Proxy> _sortOfDelay({required List<Proxy> proxies, String? testUrl}) {
return List.of(proxies)..sort((a, b) {
final aDelay = _ref.read(
getDelayProvider(proxyName: a.name, testUrl: testUrl),
);
final bDelay = _ref.read(
getDelayProvider(proxyName: b.name, testUrl: testUrl),
);
if (aDelay == null && bDelay == null) {
return 0;
}
if (aDelay == null || aDelay == -1) {
return 1;
}
if (bDelay == null || bDelay == -1) {
return -1;
}
return aDelay.compareTo(bDelay);
});
}
List<Proxy> getSortProxies({
required List<Proxy> proxies,
required ProxiesSortType sortType,
String? testUrl,
}) {
return switch (sortType) {
ProxiesSortType.none => proxies,
ProxiesSortType.delay => _sortOfDelay(proxies: proxies, testUrl: testUrl),
ProxiesSortType.name => _sortOfName(proxies),
};
}
Future<Null> clearEffect(String profileId) async {
Future<void> clearEffect(String profileId) async {
final profilePath = await appPath.getProfilePath(profileId);
final providersDirPath = await appPath.getProvidersDirPath(profileId);
return await Isolate.run(() async {
@@ -740,11 +711,7 @@ class AppController {
if (isExists) {
profileFile.delete(recursive: true);
}
final providersFileDir = File(providersDirPath);
final providersFileIsExists = await providersFileDir.exists();
if (providersFileIsExists) {
providersFileDir.delete(recursive: true);
}
await coreController.deleteFile(providersDirPath);
});
}

View File

@@ -90,25 +90,39 @@ class CoreController {
return await _interface.setupConfig(params);
}
Future<List<Group>> getProxiesGroups() async {
Future<List<Group>> getProxiesGroups({
required ProxiesSortType sortType,
required DelayMap delayMap,
required SelectedMap selectedMap,
required String defaultTestUrl,
}) async {
final proxies = await _interface.getProxies();
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
}),
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group['all'] = ((group['all'] ?? []) as List)
.map((name) => proxies[name])
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
return groupsRaw.map((e) => Group.fromJson(e)).toList();
return Isolate.run<List<Group>>(() {
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
}),
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group['all'] = ((group['all'] ?? []) as List)
.map((name) => proxies[name])
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
final groups = groupsRaw.map((e) => Group.fromJson(e)).toList();
return computeSort(
groups: groups,
sortType: sortType,
delayMap: delayMap,
selectedMap: selectedMap,
defaultTestUrl: defaultTestUrl,
);
});
}
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
@@ -258,6 +272,10 @@ class CoreController {
Future<void> crash() async {
await _interface.crash();
}
Future<String> deleteFile(String path) async {
return await _interface.deleteFile(path);
}
}
final coreController = CoreController();

View File

@@ -68,6 +68,8 @@ mixin CoreInterface {
FutureOr<bool> closeConnection(String id);
FutureOr<String> deleteFile(String path);
FutureOr<bool> closeConnections();
FutureOr<bool> resetConnections();
@@ -263,6 +265,12 @@ abstract class CoreHandlerInterface with CoreInterface {
'';
}
@override
Future<String> deleteFile(String path) async {
return await _invoke<String>(method: ActionMethod.deleteFile, data: path) ??
'';
}
@override
resetTraffic() {
_invoke(method: ActionMethod.resetTraffic);

View File

@@ -243,6 +243,7 @@ enum ActionMethod {
getMemory,
crash,
setupConfig,
deleteFile,
///Android,
setState,

View File

@@ -28,10 +28,9 @@ class AppLocalizations {
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future<AppLocalizations> load(Locale locale) {
final name =
(locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final name = (locale.countryCode?.isEmpty ?? false)
? locale.languageCode
: locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;

View File

@@ -43,6 +43,11 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
globalState.appController.savePreferencesDebounce();
}
});
ref.listenManual(needUpdateGroupsProvider, (prev, next) {
if (prev != next) {
globalState.appController.updateGroupsDebounce(commonDuration);
}
});
if (window == null) {
return;
}

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
@@ -365,13 +363,41 @@ abstract class IpInfo with _$IpInfo {
};
}
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
static IpInfo fromIpWhoIsJson(Map<String, dynamic> json) {
return switch (json) {
{'ip': final String ip, 'country_code': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromMyIpJson(Map<String, dynamic> json) {
return switch (json) {
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIpAPIJson(Map<String, dynamic> json) {
return switch (json) {
{'query': final String ip, 'countryCode': final String countryCode} =>
IpInfo(ip: ip, countryCode: countryCode),
_ => throw const FormatException('invalid json'),
};
}
static IpInfo fromIdentMeJson(Map<String, dynamic> json) {
return switch (json) {
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException('invalid json'),
};
}
}
@freezed
@@ -464,3 +490,29 @@ abstract class Script with _$Script {
factory Script.fromJson(Map<String, Object?> json) => _$ScriptFromJson(json);
}
@freezed
abstract class DelayState with _$DelayState {
const factory DelayState({required int delay, required bool group}) =
_DelayState;
}
extension DelayStateExt on DelayState {
int get priority {
if (delay > 0) return 0;
if (delay == 0) return 1;
return 2;
}
int compareTo(DelayState other) {
if (priority != other.priority) {
return priority.compareTo(other.priority);
}
if (delay != other.delay) {
return delay.compareTo(other.delay);
}
if (group && !group) return -1;
if (!group && group) return 1;
return 0;
}
}

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
@@ -27,7 +25,7 @@ const defaultBypassDomain = [
'172.2*',
'172.30.*',
'172.31.*',
'192.168.*'
'192.168.*',
];
const defaultAppSettingProps = AppSettingProps();
@@ -36,9 +34,7 @@ const defaultNetworkProps = NetworkProps();
const defaultProxiesStyle = ProxiesStyle();
const defaultWindowProps = WindowProps();
const defaultAccessControl = AccessControl();
final defaultThemeProps = ThemeProps(
primaryColor: defaultPrimaryColor,
);
final defaultThemeProps = ThemeProps(primaryColor: defaultPrimaryColor);
const List<DashboardWidget> defaultDashboardWidgets = [
DashboardWidget.networkSpeed,
@@ -115,9 +111,9 @@ abstract class AccessControl with _$AccessControl {
extension AccessControlExt on AccessControl {
List<String> get currentList => switch (mode) {
AccessControlMode.acceptSelected => acceptList,
AccessControlMode.rejectSelected => rejectList,
};
AccessControlMode.acceptSelected => acceptList,
AccessControlMode.rejectSelected => rejectList,
};
}
@freezed

View File

@@ -1,5 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

View File

@@ -5768,6 +5768,266 @@ as String,
}
}
/// @nodoc
mixin _$DelayState {
int get delay; bool get group;
/// Create a copy of DelayState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$DelayStateCopyWith<DelayState> get copyWith => _$DelayStateCopyWithImpl<DelayState>(this as DelayState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DelayState&&(identical(other.delay, delay) || other.delay == delay)&&(identical(other.group, group) || other.group == group));
}
@override
int get hashCode => Object.hash(runtimeType,delay,group);
@override
String toString() {
return 'DelayState(delay: $delay, group: $group)';
}
}
/// @nodoc
abstract mixin class $DelayStateCopyWith<$Res> {
factory $DelayStateCopyWith(DelayState value, $Res Function(DelayState) _then) = _$DelayStateCopyWithImpl;
@useResult
$Res call({
int delay, bool group
});
}
/// @nodoc
class _$DelayStateCopyWithImpl<$Res>
implements $DelayStateCopyWith<$Res> {
_$DelayStateCopyWithImpl(this._self, this._then);
final DelayState _self;
final $Res Function(DelayState) _then;
/// Create a copy of DelayState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? delay = null,Object? group = null,}) {
return _then(_self.copyWith(
delay: null == delay ? _self.delay : delay // ignore: cast_nullable_to_non_nullable
as int,group: null == group ? _self.group : group // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [DelayState].
extension DelayStatePatterns on DelayState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DelayState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _DelayState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DelayState value) $default,){
final _that = this;
switch (_that) {
case _DelayState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DelayState value)? $default,){
final _that = this;
switch (_that) {
case _DelayState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int delay, bool group)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DelayState() when $default != null:
return $default(_that.delay,_that.group);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int delay, bool group) $default,) {final _that = this;
switch (_that) {
case _DelayState():
return $default(_that.delay,_that.group);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int delay, bool group)? $default,) {final _that = this;
switch (_that) {
case _DelayState() when $default != null:
return $default(_that.delay,_that.group);case _:
return null;
}
}
}
/// @nodoc
class _DelayState implements DelayState {
const _DelayState({required this.delay, required this.group});
@override final int delay;
@override final bool group;
/// Create a copy of DelayState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$DelayStateCopyWith<_DelayState> get copyWith => __$DelayStateCopyWithImpl<_DelayState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DelayState&&(identical(other.delay, delay) || other.delay == delay)&&(identical(other.group, group) || other.group == group));
}
@override
int get hashCode => Object.hash(runtimeType,delay,group);
@override
String toString() {
return 'DelayState(delay: $delay, group: $group)';
}
}
/// @nodoc
abstract mixin class _$DelayStateCopyWith<$Res> implements $DelayStateCopyWith<$Res> {
factory _$DelayStateCopyWith(_DelayState value, $Res Function(_DelayState) _then) = __$DelayStateCopyWithImpl;
@override @useResult
$Res call({
int delay, bool group
});
}
/// @nodoc
class __$DelayStateCopyWithImpl<$Res>
implements _$DelayStateCopyWith<$Res> {
__$DelayStateCopyWithImpl(this._self, this._then);
final _DelayState _self;
final $Res Function(_DelayState) _then;
/// Create a copy of DelayState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? delay = null,Object? group = null,}) {
return _then(_DelayState(
delay: null == delay ? _self.delay : delay // ignore: cast_nullable_to_non_nullable
as int,group: null == group ? _self.group : group // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View File

@@ -291,6 +291,7 @@ const _$ActionMethodEnumMap = {
ActionMethod.getMemory: 'getMemory',
ActionMethod.crash: 'crash',
ActionMethod.setupConfig: 'setupConfig',
ActionMethod.deleteFile: 'deleteFile',
ActionMethod.setState: 'setState',
ActionMethod.startTun: 'startTun',
ActionMethod.stopTun: 'stopTun',

View File

@@ -2982,7 +2982,7 @@ as List<NavigationItem>,
/// @nodoc
mixin _$ProxiesListState {
List<Group> get groups; Set<String> get currentUnfoldSet; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; int get columns;
List<Group> get groups; Set<String> get currentUnfoldSet; ProxyCardType get proxyCardType; int get columns;
/// Create a copy of ProxiesListState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -2993,16 +2993,16 @@ $ProxiesListStateCopyWith<ProxiesListState> get copyWith => _$ProxiesListStateCo
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListState&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.currentUnfoldSet, currentUnfoldSet)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListState&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.currentUnfoldSet, currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(currentUnfoldSet),proxiesSortType,proxyCardType,sortNum,columns);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(currentUnfoldSet),proxyCardType,columns);
@override
String toString() {
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType, columns: $columns)';
}
@@ -3013,7 +3013,7 @@ abstract mixin class $ProxiesListStateCopyWith<$Res> {
factory $ProxiesListStateCopyWith(ProxiesListState value, $Res Function(ProxiesListState) _then) = _$ProxiesListStateCopyWithImpl;
@useResult
$Res call({
List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns
});
@@ -3030,14 +3030,12 @@ class _$ProxiesListStateCopyWithImpl<$Res>
/// Create a copy of ProxiesListState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,Object? columns = null,}) {
return _then(_self.copyWith(
groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self.currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable
as Set<String>,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as Set<String>,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as int,
));
}
@@ -3123,10 +3121,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ProxiesListState() when $default != null:
return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
return orElse();
}
@@ -3144,10 +3142,10 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns) $default,) {final _that = this;
switch (_that) {
case _ProxiesListState():
return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
throw StateError('Unexpected subclass');
}
@@ -3164,10 +3162,10 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns)? $default,) {final _that = this;
switch (_that) {
case _ProxiesListState() when $default != null:
return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentUnfoldSet,_that.proxyCardType,_that.columns);case _:
return null;
}
@@ -3179,7 +3177,7 @@ return $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.
class _ProxiesListState implements ProxiesListState {
const _ProxiesListState({required final List<Group> groups, required final Set<String> currentUnfoldSet, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.columns}): _groups = groups,_currentUnfoldSet = currentUnfoldSet;
const _ProxiesListState({required final List<Group> groups, required final Set<String> currentUnfoldSet, required this.proxyCardType, required this.columns}): _groups = groups,_currentUnfoldSet = currentUnfoldSet;
final List<Group> _groups;
@@ -3196,9 +3194,7 @@ class _ProxiesListState implements ProxiesListState {
return EqualUnmodifiableSetView(_currentUnfoldSet);
}
@override final ProxiesSortType proxiesSortType;
@override final ProxyCardType proxyCardType;
@override final num sortNum;
@override final int columns;
/// Create a copy of ProxiesListState
@@ -3211,16 +3207,16 @@ _$ProxiesListStateCopyWith<_ProxiesListState> get copyWith => __$ProxiesListStat
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListState&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._currentUnfoldSet, _currentUnfoldSet)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListState&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._currentUnfoldSet, _currentUnfoldSet)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_currentUnfoldSet),proxiesSortType,proxyCardType,sortNum,columns);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_currentUnfoldSet),proxyCardType,columns);
@override
String toString() {
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';
return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxyCardType: $proxyCardType, columns: $columns)';
}
@@ -3231,7 +3227,7 @@ abstract mixin class _$ProxiesListStateCopyWith<$Res> implements $ProxiesListSta
factory _$ProxiesListStateCopyWith(_ProxiesListState value, $Res Function(_ProxiesListState) _then) = __$ProxiesListStateCopyWithImpl;
@override @useResult
$Res call({
List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns
List<Group> groups, Set<String> currentUnfoldSet, ProxyCardType proxyCardType, int columns
});
@@ -3248,14 +3244,12 @@ class __$ProxiesListStateCopyWithImpl<$Res>
/// Create a copy of ProxiesListState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxyCardType = null,Object? columns = null,}) {
return _then(_ProxiesListState(
groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self._currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable
as Set<String>,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as Set<String>,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as int,
));
}
@@ -3266,7 +3260,7 @@ as int,
/// @nodoc
mixin _$ProxiesTabState {
List<Group> get groups; String? get currentGroupName; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; int get columns;
List<Group> get groups; String? get currentGroupName; ProxyCardType get proxyCardType; int get columns;
/// Create a copy of ProxiesTabState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -3277,16 +3271,16 @@ $ProxiesTabStateCopyWith<ProxiesTabState> get copyWith => _$ProxiesTabStateCopyW
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesTabState&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesTabState&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),currentGroupName,proxiesSortType,proxyCardType,sortNum,columns);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),currentGroupName,proxyCardType,columns);
@override
String toString() {
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType, columns: $columns)';
}
@@ -3297,7 +3291,7 @@ abstract mixin class $ProxiesTabStateCopyWith<$Res> {
factory $ProxiesTabStateCopyWith(ProxiesTabState value, $Res Function(ProxiesTabState) _then) = _$ProxiesTabStateCopyWithImpl;
@useResult
$Res call({
List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns
});
@@ -3314,14 +3308,12 @@ class _$ProxiesTabStateCopyWithImpl<$Res>
/// Create a copy of ProxiesTabState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,Object? columns = null,}) {
return _then(_self.copyWith(
groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable
as String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as String?,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as int,
));
}
@@ -3407,10 +3399,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ProxiesTabState() when $default != null:
return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
return orElse();
}
@@ -3428,10 +3420,10 @@ return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns) $default,) {final _that = this;
switch (_that) {
case _ProxiesTabState():
return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
throw StateError('Unexpected subclass');
}
@@ -3448,10 +3440,10 @@ return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns)? $default,) {final _that = this;
switch (_that) {
case _ProxiesTabState() when $default != null:
return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:
return $default(_that.groups,_that.currentGroupName,_that.proxyCardType,_that.columns);case _:
return null;
}
@@ -3463,7 +3455,7 @@ return $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.
class _ProxiesTabState implements ProxiesTabState {
const _ProxiesTabState({required final List<Group> groups, required this.currentGroupName, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.columns}): _groups = groups;
const _ProxiesTabState({required final List<Group> groups, required this.currentGroupName, required this.proxyCardType, required this.columns}): _groups = groups;
final List<Group> _groups;
@@ -3474,9 +3466,7 @@ class _ProxiesTabState implements ProxiesTabState {
}
@override final String? currentGroupName;
@override final ProxiesSortType proxiesSortType;
@override final ProxyCardType proxyCardType;
@override final num sortNum;
@override final int columns;
/// Create a copy of ProxiesTabState
@@ -3489,16 +3479,16 @@ _$ProxiesTabStateCopyWith<_ProxiesTabState> get copyWith => __$ProxiesTabStateCo
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesTabState&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesTabState&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.columns, columns) || other.columns == columns));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),currentGroupName,proxiesSortType,proxyCardType,sortNum,columns);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),currentGroupName,proxyCardType,columns);
@override
String toString() {
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';
return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxyCardType: $proxyCardType, columns: $columns)';
}
@@ -3509,7 +3499,7 @@ abstract mixin class _$ProxiesTabStateCopyWith<$Res> implements $ProxiesTabState
factory _$ProxiesTabStateCopyWith(_ProxiesTabState value, $Res Function(_ProxiesTabState) _then) = __$ProxiesTabStateCopyWithImpl;
@override @useResult
$Res call({
List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns
List<Group> groups, String? currentGroupName, ProxyCardType proxyCardType, int columns
});
@@ -3526,14 +3516,12 @@ class __$ProxiesTabStateCopyWithImpl<$Res>
/// Create a copy of ProxiesTabState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxyCardType = null,Object? columns = null,}) {
return _then(_ProxiesTabState(
groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
as List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable
as String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable
as ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
as num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as String?,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable
as ProxyCardType,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable
as int,
));
}
@@ -5733,40 +5721,40 @@ as double,
}
/// @nodoc
mixin _$ProxyCardState {
mixin _$SelectedProxyState {
String get proxyName; String? get testUrl;
/// Create a copy of ProxyCardState
String get proxyName; bool get group; String? get testUrl;
/// Create a copy of SelectedProxyState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ProxyCardStateCopyWith<ProxyCardState> get copyWith => _$ProxyCardStateCopyWithImpl<ProxyCardState>(this as ProxyCardState, _$identity);
$SelectedProxyStateCopyWith<SelectedProxyState> get copyWith => _$SelectedProxyStateCopyWithImpl<SelectedProxyState>(this as SelectedProxyState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyCardState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
return identical(this, other) || (other.runtimeType == runtimeType&&other is SelectedProxyState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.group, group) || other.group == group)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
}
@override
int get hashCode => Object.hash(runtimeType,proxyName,testUrl);
int get hashCode => Object.hash(runtimeType,proxyName,group,testUrl);
@override
String toString() {
return 'ProxyCardState(proxyName: $proxyName, testUrl: $testUrl)';
return 'SelectedProxyState(proxyName: $proxyName, group: $group, testUrl: $testUrl)';
}
}
/// @nodoc
abstract mixin class $ProxyCardStateCopyWith<$Res> {
factory $ProxyCardStateCopyWith(ProxyCardState value, $Res Function(ProxyCardState) _then) = _$ProxyCardStateCopyWithImpl;
abstract mixin class $SelectedProxyStateCopyWith<$Res> {
factory $SelectedProxyStateCopyWith(SelectedProxyState value, $Res Function(SelectedProxyState) _then) = _$SelectedProxyStateCopyWithImpl;
@useResult
$Res call({
String proxyName, String? testUrl
String proxyName, bool group, String? testUrl
});
@@ -5774,19 +5762,20 @@ $Res call({
}
/// @nodoc
class _$ProxyCardStateCopyWithImpl<$Res>
implements $ProxyCardStateCopyWith<$Res> {
_$ProxyCardStateCopyWithImpl(this._self, this._then);
class _$SelectedProxyStateCopyWithImpl<$Res>
implements $SelectedProxyStateCopyWith<$Res> {
_$SelectedProxyStateCopyWithImpl(this._self, this._then);
final ProxyCardState _self;
final $Res Function(ProxyCardState) _then;
final SelectedProxyState _self;
final $Res Function(SelectedProxyState) _then;
/// Create a copy of ProxyCardState
/// Create a copy of SelectedProxyState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? proxyName = null,Object? testUrl = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? proxyName = null,Object? group = null,Object? testUrl = freezed,}) {
return _then(_self.copyWith(
proxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable
as String,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String,group: null == group ? _self.group : group // ignore: cast_nullable_to_non_nullable
as bool,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String?,
));
}
@@ -5794,8 +5783,8 @@ as String?,
}
/// Adds pattern-matching-related methods to [ProxyCardState].
extension ProxyCardStatePatterns on ProxyCardState {
/// Adds pattern-matching-related methods to [SelectedProxyState].
extension SelectedProxyStatePatterns on SelectedProxyState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
@@ -5808,10 +5797,10 @@ extension ProxyCardStatePatterns on ProxyCardState {
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxyCardState value)? $default,{required TResult orElse(),}){
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SelectedProxyState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ProxyCardState() when $default != null:
case _SelectedProxyState() when $default != null:
return $default(_that);case _:
return orElse();
@@ -5830,10 +5819,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxyCardState value) $default,){
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SelectedProxyState value) $default,){
final _that = this;
switch (_that) {
case _ProxyCardState():
case _SelectedProxyState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
@@ -5851,10 +5840,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxyCardState value)? $default,){
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SelectedProxyState value)? $default,){
final _that = this;
switch (_that) {
case _ProxyCardState() when $default != null:
case _SelectedProxyState() when $default != null:
return $default(_that);case _:
return null;
@@ -5872,10 +5861,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String proxyName, String? testUrl)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String proxyName, bool group, String? testUrl)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ProxyCardState() when $default != null:
return $default(_that.proxyName,_that.testUrl);case _:
case _SelectedProxyState() when $default != null:
return $default(_that.proxyName,_that.group,_that.testUrl);case _:
return orElse();
}
@@ -5893,10 +5882,10 @@ return $default(_that.proxyName,_that.testUrl);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String proxyName, String? testUrl) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String proxyName, bool group, String? testUrl) $default,) {final _that = this;
switch (_that) {
case _ProxyCardState():
return $default(_that.proxyName,_that.testUrl);case _:
case _SelectedProxyState():
return $default(_that.proxyName,_that.group,_that.testUrl);case _:
throw StateError('Unexpected subclass');
}
@@ -5913,10 +5902,10 @@ return $default(_that.proxyName,_that.testUrl);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String proxyName, String? testUrl)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String proxyName, bool group, String? testUrl)? $default,) {final _that = this;
switch (_that) {
case _ProxyCardState() when $default != null:
return $default(_that.proxyName,_that.testUrl);case _:
case _SelectedProxyState() when $default != null:
return $default(_that.proxyName,_that.group,_that.testUrl);case _:
return null;
}
@@ -5927,44 +5916,45 @@ return $default(_that.proxyName,_that.testUrl);case _:
/// @nodoc
class _ProxyCardState implements ProxyCardState {
const _ProxyCardState({required this.proxyName, this.testUrl});
class _SelectedProxyState implements SelectedProxyState {
const _SelectedProxyState({required this.proxyName, this.group = false, this.testUrl});
@override final String proxyName;
@override@JsonKey() final bool group;
@override final String? testUrl;
/// Create a copy of ProxyCardState
/// Create a copy of SelectedProxyState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ProxyCardStateCopyWith<_ProxyCardState> get copyWith => __$ProxyCardStateCopyWithImpl<_ProxyCardState>(this, _$identity);
_$SelectedProxyStateCopyWith<_SelectedProxyState> get copyWith => __$SelectedProxyStateCopyWithImpl<_SelectedProxyState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyCardState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SelectedProxyState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.group, group) || other.group == group)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
}
@override
int get hashCode => Object.hash(runtimeType,proxyName,testUrl);
int get hashCode => Object.hash(runtimeType,proxyName,group,testUrl);
@override
String toString() {
return 'ProxyCardState(proxyName: $proxyName, testUrl: $testUrl)';
return 'SelectedProxyState(proxyName: $proxyName, group: $group, testUrl: $testUrl)';
}
}
/// @nodoc
abstract mixin class _$ProxyCardStateCopyWith<$Res> implements $ProxyCardStateCopyWith<$Res> {
factory _$ProxyCardStateCopyWith(_ProxyCardState value, $Res Function(_ProxyCardState) _then) = __$ProxyCardStateCopyWithImpl;
abstract mixin class _$SelectedProxyStateCopyWith<$Res> implements $SelectedProxyStateCopyWith<$Res> {
factory _$SelectedProxyStateCopyWith(_SelectedProxyState value, $Res Function(_SelectedProxyState) _then) = __$SelectedProxyStateCopyWithImpl;
@override @useResult
$Res call({
String proxyName, String? testUrl
String proxyName, bool group, String? testUrl
});
@@ -5972,19 +5962,20 @@ $Res call({
}
/// @nodoc
class __$ProxyCardStateCopyWithImpl<$Res>
implements _$ProxyCardStateCopyWith<$Res> {
__$ProxyCardStateCopyWithImpl(this._self, this._then);
class __$SelectedProxyStateCopyWithImpl<$Res>
implements _$SelectedProxyStateCopyWith<$Res> {
__$SelectedProxyStateCopyWithImpl(this._self, this._then);
final _ProxyCardState _self;
final $Res Function(_ProxyCardState) _then;
final _SelectedProxyState _self;
final $Res Function(_SelectedProxyState) _then;
/// Create a copy of ProxyCardState
/// Create a copy of SelectedProxyState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? proxyName = null,Object? testUrl = freezed,}) {
return _then(_ProxyCardState(
@override @pragma('vm:prefer-inline') $Res call({Object? proxyName = null,Object? group = null,Object? testUrl = freezed,}) {
return _then(_SelectedProxyState(
proxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable
as String,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String,group: null == group ? _self.group : group // ignore: cast_nullable_to_non_nullable
as bool,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@@ -1,4 +1,3 @@
// ignore_for_file: invalid_annotation_target
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

View File

@@ -102,9 +102,7 @@ abstract class ProxiesListState with _$ProxiesListState {
const factory ProxiesListState({
required List<Group> groups,
required Set<String> currentUnfoldSet,
required ProxiesSortType proxiesSortType,
required ProxyCardType proxyCardType,
required num sortNum,
required int columns,
}) = _ProxiesListState;
}
@@ -114,9 +112,7 @@ abstract class ProxiesTabState with _$ProxiesTabState {
const factory ProxiesTabState({
required List<Group> groups,
required String? currentGroupName,
required ProxiesSortType proxiesSortType,
required ProxyCardType proxyCardType,
required num sortNum,
required int columns,
}) = _ProxiesTabState;
}
@@ -233,9 +229,12 @@ abstract class DashboardState with _$DashboardState {
}
@freezed
abstract class ProxyCardState with _$ProxyCardState {
const factory ProxyCardState({required String proxyName, String? testUrl}) =
_ProxyCardState;
abstract class SelectedProxyState with _$SelectedProxyState {
const factory SelectedProxyState({
required String proxyName,
@Default(false) bool group,
String? testUrl,
}) = _SelectedProxyState;
}
@freezed

View File

@@ -27,19 +27,15 @@ class HomePage extends StatelessWidget {
pageBuilder: (_, index) {
final navigationItem = state.navigationItems[index];
final navigationView = navigationItem.builder(context);
final view = isMobile
? KeepScope(
keep: navigationItem.keep,
child: navigationView,
)
: KeepScope(
keep: navigationItem.keep,
child: Navigator(
onGenerateRoute: (_) {
return CommonRoute(builder: (_) => navigationView);
},
final view = KeepScope(
keep: navigationItem.keep,
child: isMobile
? navigationView
: Navigator(
pages: [MaterialPage(child: navigationView)],
onDidRemovePage: (_) {},
),
);
);
return view;
},
);

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/common/app_localizations.dart';
@@ -38,45 +39,48 @@ class App {
}
Future<List<Package>> getPackages() async {
final packagesString =
await methodChannel.invokeMethod<String>('getPackages');
final packagesString = await methodChannel.invokeMethod<String>(
'getPackages',
);
return Isolate.run<List<Package>>(() {
final List<dynamic> packagesRaw =
packagesString != null ? json.decode(packagesString) : [];
final List<dynamic> packagesRaw = packagesString != null
? json.decode(packagesString)
: [];
return packagesRaw.map((e) => Package.fromJson(e)).toSet().toList();
});
}
Future<List<String>> getChinaPackageNames() async {
final packageNamesString =
await methodChannel.invokeMethod<String>('getChinaPackageNames');
final packageNamesString = await methodChannel.invokeMethod<String>(
'getChinaPackageNames',
);
return Isolate.run<List<String>>(() {
final List<dynamic> packageNamesRaw =
packageNamesString != null ? json.decode(packageNamesString) : [];
final List<dynamic> packageNamesRaw = packageNamesString != null
? json.decode(packageNamesString)
: [];
return packageNamesRaw.map((e) => e.toString()).toList();
});
}
Future<bool?> requestNotificationsPermission() async {
return await methodChannel
.invokeMethod<bool>('requestNotificationsPermission');
return await methodChannel.invokeMethod<bool>(
'requestNotificationsPermission',
);
}
Future<bool> openFile(String path) async {
return await methodChannel.invokeMethod<bool>('openFile', {
'path': path,
}) ??
return await methodChannel.invokeMethod<bool>('openFile', {'path': path}) ??
false;
}
Future<ImageProvider?> getPackageIcon(String packageName) async {
final base64 = await methodChannel.invokeMethod<String>('getPackageIcon', {
final path = await methodChannel.invokeMethod<String>('getPackageIcon', {
'packageName': packageName,
});
if (base64 == null) {
if (path == null) {
return null;
}
return MemoryImage(base64Decode(base64));
return FileImage(File(path));
}
Future<bool?> tip(String? message) async {

View File

@@ -54,8 +54,11 @@ class Requests extends _$Requests with AutoDisposeNotifierMixin {
}
}
@Riverpod(keepAlive: true)
class Providers extends _$Providers with AutoDisposeNotifierMixin {
@riverpod
class Providers extends _$Providers with AnyNotifierMixin {
@override
List<ExternalProvider> get value => globalState.appState.providers;
@override
List<ExternalProvider> build() {
return globalState.appState.providers;
@@ -71,9 +74,9 @@ class Providers extends _$Providers with AutoDisposeNotifierMixin {
return;
}
if (provider == null) return;
final index = state.indexWhere((item) => item.name == provider.name);
final index = value.indexWhere((item) => item.name == provider.name);
if (index == -1) return;
final newState = List<ExternalProvider>.from(state)..[index] = provider;
final newState = List<ExternalProvider>.from(value)..[index] = provider;
value = newState;
}
}

View File

@@ -176,7 +176,7 @@ final class ProvidersProvider
argument: null,
retry: null,
name: r'providersProvider',
isAutoDispose: false,
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@@ -197,7 +197,7 @@ final class ProvidersProvider
}
}
String _$providersHash() => r'b1175e1e2b22e34f8d414386fe672c4933fc47a3';
String _$providersHash() => r'9cb491314be6dca0d9ff2d09aa276d19a92895af';
abstract class _$Providers extends $Notifier<List<ExternalProvider>> {
List<ExternalProvider> build();

View File

@@ -698,7 +698,7 @@ final class ProxiesListStateProvider
}
}
String _$proxiesListStateHash() => r'1438dbcaf0d45fbd2cb7fdde81cf1dc06bbd992f';
String _$proxiesListStateHash() => r'b16ad96516ece78f6cb22f558a0535000b784317';
@ProviderFor(proxiesTabState)
const proxiesTabStateProvider = ProxiesTabStateProvider._();
@@ -740,7 +740,7 @@ final class ProxiesTabStateProvider
}
}
String _$proxiesTabStateHash() => r'591fd8c2fa0df4c6a4832ffa04eb8d62453ba225';
String _$proxiesTabStateHash() => r'143b106d74da618327cbac48af15078efd8cabee';
@ProviderFor(isStart)
const isStartProvider = IsStartProvider._();
@@ -1248,7 +1248,7 @@ final class GetDelayProvider extends $FunctionalProvider<int?, int?, int?>
}
}
String _$getDelayHash() => r'b5920ac7de0aaadb8ff63fac993bd90ff87cd25a';
String _$getDelayHash() => r'15df90fb31665501b21ea671a72e35beaf32141b';
final class GetDelayFamily extends $Family
with
@@ -1512,55 +1512,62 @@ final class GetProxiesColumnsProvider extends $FunctionalProvider<int, int, int>
String _$getProxiesColumnsHash() => r'725066b5fc21f590a4c2656a1fd5e14ab7079079';
@ProviderFor(getProxyCardState)
const getProxyCardStateProvider = GetProxyCardStateFamily._();
@ProviderFor(realSelectedProxyState)
const realSelectedProxyStateProvider = RealSelectedProxyStateFamily._();
final class GetProxyCardStateProvider
extends $FunctionalProvider<ProxyCardState, ProxyCardState, ProxyCardState>
with $Provider<ProxyCardState> {
const GetProxyCardStateProvider._({
required GetProxyCardStateFamily super.from,
final class RealSelectedProxyStateProvider
extends
$FunctionalProvider<
SelectedProxyState,
SelectedProxyState,
SelectedProxyState
>
with $Provider<SelectedProxyState> {
const RealSelectedProxyStateProvider._({
required RealSelectedProxyStateFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'getProxyCardStateProvider',
name: r'realSelectedProxyStateProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$getProxyCardStateHash();
String debugGetCreateSourceHash() => _$realSelectedProxyStateHash();
@override
String toString() {
return r'getProxyCardStateProvider'
return r'realSelectedProxyStateProvider'
''
'($argument)';
}
@$internal
@override
$ProviderElement<ProxyCardState> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
$ProviderElement<SelectedProxyState> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
ProxyCardState create(Ref ref) {
SelectedProxyState create(Ref ref) {
final argument = this.argument as String;
return getProxyCardState(ref, argument);
return realSelectedProxyState(ref, argument);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ProxyCardState value) {
Override overrideWithValue(SelectedProxyState value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ProxyCardState>(value),
providerOverride: $SyncValueProvider<SelectedProxyState>(value),
);
}
@override
bool operator ==(Object other) {
return other is GetProxyCardStateProvider && other.argument == argument;
return other is RealSelectedProxyStateProvider &&
other.argument == argument;
}
@override
@@ -1569,24 +1576,25 @@ final class GetProxyCardStateProvider
}
}
String _$getProxyCardStateHash() => r'0f131148cb5ed60c9c4c4f31fbe32f114ac346bb';
String _$realSelectedProxyStateHash() =>
r'42fa131419f0a26e30c4f5269bf020893b7f828c';
final class GetProxyCardStateFamily extends $Family
with $FunctionalFamilyOverride<ProxyCardState, String> {
const GetProxyCardStateFamily._()
final class RealSelectedProxyStateFamily extends $Family
with $FunctionalFamilyOverride<SelectedProxyState, String> {
const RealSelectedProxyStateFamily._()
: super(
retry: null,
name: r'getProxyCardStateProvider',
name: r'realSelectedProxyStateProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
GetProxyCardStateProvider call(String proxyName) =>
GetProxyCardStateProvider._(argument: proxyName, from: this);
RealSelectedProxyStateProvider call(String proxyName) =>
RealSelectedProxyStateProvider._(argument: proxyName, from: this);
@override
String toString() => r'getProxyCardStateProvider';
String toString() => r'realSelectedProxyStateProvider';
}
@ProviderFor(getProxyName)
@@ -1801,7 +1809,7 @@ final class GetProxyDescProvider
}
}
String _$getProxyDescHash() => r'c173fe2393d9c4f5d5d17480e69f9126bb76a17d';
String _$getProxyDescHash() => r'4579b55bf7e9fbcfdf91b91619bd0320c585f23d';
final class GetProxyDescFamily extends $Family
with $FunctionalFamilyOverride<String, Proxy> {
@@ -2205,6 +2213,55 @@ final class AutoSetSystemDnsStateProvider
String _$autoSetSystemDnsStateHash() =>
r'2e0976e079100325b1ca797285df48a94c2c066c';
@ProviderFor(needUpdateGroups)
const needUpdateGroupsProvider = NeedUpdateGroupsProvider._();
final class NeedUpdateGroupsProvider
extends
$FunctionalProvider<
VM3<bool, int, ProxiesSortType>,
VM3<bool, int, ProxiesSortType>,
VM3<bool, int, ProxiesSortType>
>
with $Provider<VM3<bool, int, ProxiesSortType>> {
const NeedUpdateGroupsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'needUpdateGroupsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$needUpdateGroupsHash();
@$internal
@override
$ProviderElement<VM3<bool, int, ProxiesSortType>> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
VM3<bool, int, ProxiesSortType> create(Ref ref) {
return needUpdateGroups(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(VM3<bool, int, ProxiesSortType> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<VM3<bool, int, ProxiesSortType>>(
value,
),
);
}
}
String _$needUpdateGroupsHash() => r'1d1fbf135b4b5d2a2ee984e421ccffe7c4bb0a47';
@ProviderFor(androidState)
const androidStateProvider = AndroidStateProvider._();

View File

@@ -268,20 +268,15 @@ ProxiesListState proxiesListState(Ref ref) {
final query = ref.watch(queryProvider(QueryTag.proxies));
final currentGroups = ref.watch(filterGroupsStateProvider(query));
final currentUnfoldSet = ref.watch(unfoldSetProvider);
final vm2 = ref.watch(
proxiesStyleSettingProvider.select(
(state) => VM2(a: state.sortType, b: state.cardType),
),
final cardType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.cardType),
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxiesListState(
groups: currentGroups.value,
currentUnfoldSet: currentUnfoldSet,
proxiesSortType: vm2.a,
proxyCardType: vm2.b,
sortNum: sortNum,
proxyCardType: cardType,
columns: columns,
);
}
@@ -293,19 +288,14 @@ ProxiesTabState proxiesTabState(Ref ref) {
final currentGroupName = ref.watch(
currentProfileProvider.select((state) => state?.currentGroupName),
);
final vm2 = ref.watch(
proxiesStyleSettingProvider.select(
(state) => VM2(a: state.sortType, b: state.cardType),
),
final cardType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.cardType),
);
final sortNum = ref.watch(sortNumProvider);
final columns = ref.watch(getProxiesColumnsProvider);
return ProxiesTabState(
groups: currentGroups.value,
currentGroupName: currentGroupName,
proxiesSortType: vm2.a,
proxyCardType: vm2.b,
sortNum: sortNum,
proxyCardType: cardType,
columns: columns,
);
}
@@ -416,14 +406,14 @@ String getRealTestUrl(Ref ref, [String? testUrl]) {
@riverpod
int? getDelay(Ref ref, {required String proxyName, String? testUrl}) {
final currentTestUrl = ref.watch(getRealTestUrlProvider(testUrl));
final proxyCardState = ref.watch(getProxyCardStateProvider(proxyName));
final proxyState = ref.watch(realSelectedProxyStateProvider(proxyName));
final delay = ref.watch(
delayDataSourceProvider.select((state) {
final delayMap =
state[proxyCardState.testUrl.getSafeValue(currentTestUrl)];
return delayMap?[proxyCardState.proxyName];
final delayMap = state[proxyState.testUrl.getSafeValue(currentTestUrl)];
return delayMap?[proxyState.proxyName];
}),
);
return delay;
}
@@ -470,41 +460,14 @@ int getProxiesColumns(Ref ref) {
return utils.getProxiesColumns(viewWidth, proxiesLayout);
}
ProxyCardState _getProxyCardState(
List<Group> groups,
SelectedMap selectedMap,
ProxyCardState proxyDelayState,
) {
if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;
final index = groups.indexWhere(
(element) => element.name == proxyDelayState.proxyName,
);
if (index == -1) return proxyDelayState;
final group = groups[index];
final currentSelectedName = group.getCurrentSelectedName(
selectedMap[proxyDelayState.proxyName] ?? '',
);
if (currentSelectedName.isEmpty) {
return proxyDelayState;
}
return _getProxyCardState(
groups,
selectedMap,
proxyDelayState.copyWith(
proxyName: currentSelectedName,
testUrl: group.testUrl,
),
);
}
@riverpod
ProxyCardState getProxyCardState(Ref ref, String proxyName) {
SelectedProxyState realSelectedProxyState(Ref ref, String proxyName) {
final groups = ref.watch(groupsProvider);
final selectedMap = ref.watch(selectedMapProvider);
return _getProxyCardState(
groups,
selectedMap,
ProxyCardState(proxyName: proxyName),
return computeRealSelectedProxyState(
proxyName,
groups: groups,
selectedMap: selectedMap,
);
}
@@ -534,7 +497,7 @@ String getProxyDesc(Ref ref, Proxy proxy) {
final groups = ref.watch(groupsProvider);
final index = groups.indexWhere((element) => element.name == proxy.name);
if (index == -1) return proxy.type;
final state = ref.watch(getProxyCardStateProvider(proxy.name));
final state = ref.watch(realSelectedProxyStateProvider(proxy.name));
return "${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})";
}
}
@@ -638,6 +601,18 @@ VM2<bool, bool> autoSetSystemDnsState(Ref ref) {
return VM2(a: isStart ? realTunEnable : false, b: autoSetSystemDns);
}
@riverpod
VM3<bool, int, ProxiesSortType> needUpdateGroups(Ref ref) {
final isProxies = ref.watch(
currentPageLabelProvider.select((state) => state == PageLabel.proxies),
);
final sortNum = ref.watch(sortNumProvider);
final sortType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.sortType),
);
return VM3(a: isProxies, b: sortNum, c: sortType);
}
@riverpod
AndroidState androidState(Ref ref) {
final currentProfileName = ref.watch(

View File

@@ -18,6 +18,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_js/flutter_js.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:yaml_writer/yaml_writer.dart';
@@ -81,6 +82,38 @@ class GlobalState {
await init();
appState = appState.copyWith(coreStatus: CoreStatus.connected);
await window?.init(version);
_shakingStore();
}
Future<void> _shakingStore() async {
final profileIds = config.profiles.map((item) => item.id);
final providersRootPath = await appPath.getProvidersRootPath();
final profilesRootPath = await appPath.profilesPath;
Isolate.run(() async {
final profilesDir = Directory(profilesRootPath);
final providersDir = Directory(providersRootPath);
final List<FileSystemEntity> entities = [];
if (await profilesDir.exists()) {
entities.addAll(
profilesDir.listSync().where(
(item) => !item.path.contains('providers'),
),
);
}
if (await providersDir.exists()) {
entities.addAll(providersDir.listSync());
}
final deleteFutures = entities.map((entity) async {
if (!profileIds.contains(basenameWithoutExtension(entity.path))) {
final res = await coreController.deleteFile(entity.path);
if (res.isNotEmpty) {
throw res;
}
}
return true;
});
await Future.wait(deleteFutures);
});
}
Future<void> _initDynamicColor() async {
@@ -275,7 +308,7 @@ class GlobalState {
if (!await file.exists()) {
await file.create(recursive: true);
}
await file.writeAsString(data, flush: true);
await file.writeAsString(data);
return '';
} catch (e) {
return e.toString();

View File

@@ -93,7 +93,7 @@ class OutboundModeV2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
final height = getWidgetHeight(0.72);
final height = getWidgetHeight(1);
return SizedBox(
height: height,
child: CommonCard(
@@ -108,42 +108,56 @@ class OutboundModeV2 extends StatelessWidget {
Mode.global => globalState.theme.darken3PrimaryContainer,
Mode.direct => context.colorScheme.tertiaryContainer,
};
return Container(
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 - 16,
child: Text(
Intl.message(item.name),
style: Theme.of(context).textTheme.titleSmall
?.adjustSize(1)
.copyWith(
color: item == mode
? _getTextColor(context, item)
: null,
return Column(
children: [
Expanded(
child: Container(
constraints: BoxConstraints.expand(),
padding: EdgeInsets.all(12),
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,
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,
);
},
),
),
),
padding: EdgeInsets.symmetric(horizontal: 8),
groupValue: mode,
onValueChanged: (value) {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
thumbColor: thumbColor,
),
Container(color: thumbColor, height: 10.ap),
],
);
},
),

View File

@@ -58,7 +58,7 @@ class _StartButtonState extends ConsumerState<StartButton>
void updateController() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (isStart) {
if (isStart && mounted) {
_controller.forward();
} else {
_controller.reverse();

View File

@@ -22,13 +22,25 @@ double getItemHeight(ProxyCardType proxyCardType) {
Future<void> proxyDelayTest(Proxy proxy, [String? testUrl]) async {
final appController = globalState.appController;
final state = appController.getProxyCardState(proxy.name);
final url = state.testUrl.getSafeValue(appController.getRealTestUrl(testUrl));
final groups = globalState.appState.groups;
final selectedMap = globalState.config.currentProfile?.selectedMap ?? {};
final state = computeRealSelectedProxyState(
proxy.name,
groups: groups,
selectedMap: selectedMap,
);
final currentTestUrl = state.testUrl.getSafeValue(
appController.getRealTestUrl(testUrl),
);
if (state.proxyName.isEmpty) {
return;
}
appController.setDelay(Delay(url: url, name: state.proxyName, value: 0));
appController.setDelay(await coreController.getDelay(url, state.proxyName));
appController.setDelay(
Delay(url: currentTestUrl, name: state.proxyName, value: 0),
);
appController.setDelay(
await coreController.getDelay(currentTestUrl, state.proxyName),
);
}
Future<void> delayTest(List<Proxy> proxies, [String? testUrl]) async {
@@ -36,7 +48,13 @@ Future<void> delayTest(List<Proxy> proxies, [String? testUrl]) async {
final proxyNames = proxies.map((proxy) => proxy.name).toSet().toList();
final delayProxies = proxyNames.map<Future>((proxyName) async {
final state = appController.getProxyCardState(proxyName);
final groups = globalState.appState.groups;
final selectedMap = globalState.config.currentProfile?.selectedMap ?? {};
final state = computeRealSelectedProxyState(
proxyName,
groups: groups,
selectedMap: selectedMap,
);
final url = state.testUrl.getSafeValue(
appController.getRealTestUrl(testUrl),
);

View File

@@ -28,7 +28,6 @@ class _ProxiesListViewState extends State<ProxiesListView> {
null,
);
List<double> _headerOffset = [];
GroupNameProxiesMap _lastGroupNameProxiesMap = {};
@override
void initState() {
@@ -118,10 +117,8 @@ class _ProxiesListViewState extends State<ProxiesListView> {
required int columns,
required Set<String> currentUnfoldSet,
required ProxyCardType cardType,
required ProxiesSortType sortType,
}) {
final items = <Widget>[];
final GroupNameProxiesMap groupNameProxiesMap = {};
for (final group in groups) {
final groupName = group.name;
final isExpand = currentUnfoldSet.contains(groupName);
@@ -137,25 +134,23 @@ class _ProxiesListViewState extends State<ProxiesListView> {
const SizedBox(height: 8),
]);
if (isExpand) {
final sortedProxies = globalState.appController.getSortProxies(
proxies: group.all,
sortType: sortType,
testUrl: group.testUrl,
);
groupNameProxiesMap[groupName] = sortedProxies;
final chunks = sortedProxies.chunks(columns);
final proxies = group.all;
final chunks = proxies.chunks(columns);
final rows = chunks
.map<Widget>((proxies) {
final children = proxies
.map<Widget>(
(proxy) => Flexible(
child: ProxyCard(
testUrl: group.testUrl,
type: cardType,
groupType: group.type,
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
child: SizedBox(
height: getItemHeight(cardType),
child: ProxyCard(
testUrl: group.testUrl,
type: cardType,
groupType: group.type,
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
),
),
),
)
@@ -171,7 +166,6 @@ class _ProxiesListViewState extends State<ProxiesListView> {
items.addAll([...rows, const SizedBox(height: 8)]);
}
}
_lastGroupNameProxiesMap = groupNameProxiesMap;
return items;
}
@@ -203,11 +197,12 @@ class _ProxiesListViewState extends State<ProxiesListView> {
}
final appController = globalState.appController;
final currentGroups = appController.getCurrentGroups();
final groupNames = currentGroups.map((e) => e.name).toList();
final findIndex = groupNames.indexWhere((item) => item == groupName);
final findIndex = currentGroups.indexWhere(
(item) => item.name == groupName,
);
final index = findIndex != -1 ? findIndex : 0;
final currentInitOffset = _headerOffset[index];
final proxies = _lastGroupNameProxiesMap[groupName];
final proxies = currentGroups.getGroup(groupName)?.all;
_controller.animateTo(
min(
currentInitOffset +
@@ -240,7 +235,6 @@ class _ProxiesListViewState extends State<ProxiesListView> {
currentUnfoldSet: state.currentUnfoldSet,
columns: state.columns,
cardType: state.proxyCardType,
sortType: state.proxiesSortType,
);
final itemsOffset = _getItemHeightList(items, state.proxyCardType);
return CommonScrollBar(

View File

@@ -3,7 +3,6 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/models/widget.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/views/proxies/list.dart';
import 'package:fl_clash/views/proxies/providers.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -138,11 +137,6 @@ class _ProxiesViewState extends ConsumerState<ProxiesView> {
},
fireImmediately: true,
);
ref.listenManual(currentPageLabelProvider, (prev, next) {
if (prev != next) {
globalState.appController.updateGroupsDebounce();
}
});
}
@override

View File

@@ -2,15 +2,14 @@ import 'dart:math';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/models/config.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/experimental/scope.dart';
import '../../models/common.dart';
import 'card.dart';
import 'common.dart';
@@ -61,7 +60,7 @@ class ProxiesTabViewState extends ConsumerState<ProxiesTabView>
Future<void> delayTestCurrentGroup() async {
final currentGroupName = globalState.appController.getCurrentGroupName();
final currentState = _keyMap[currentGroupName]?.currentState;
await delayTest(currentState?.proxies ?? [], currentState?.testUrl);
await delayTest(currentState?.currentProxies ?? [], currentState?.testUrl);
}
Widget _buildMoreButton() {
@@ -182,15 +181,11 @@ class ProxiesTabViewState extends ConsumerState<ProxiesTabView>
final children = groups.map((group) {
final key = GlobalObjectKey<_ProxyGroupViewState>(group.name);
keyMap[group.name] = key;
return KeepScope(
keep: true,
child: ProxyGroupView(
key: key,
group: group,
columns: state.columns,
cardType: state.proxyCardType,
sortType: state.proxiesSortType,
),
return ProxyGroupView(
key: key,
group: group,
columns: state.columns,
cardType: state.proxyCardType,
);
}).toList();
_keyMap = keyMap;
@@ -252,19 +247,16 @@ class ProxiesTabViewState extends ConsumerState<ProxiesTabView>
}
}
@Dependencies([proxiesTabState, proxiesTabControllerState])
class ProxyGroupView extends ConsumerStatefulWidget {
final Group group;
final int columns;
final ProxyCardType cardType;
final ProxiesSortType sortType;
const ProxyGroupView({
super.key,
required this.group,
required this.columns,
required this.cardType,
required this.sortType,
});
@override
@@ -274,7 +266,7 @@ class ProxyGroupView extends ConsumerStatefulWidget {
class _ProxyGroupViewState extends ConsumerState<ProxyGroupView> {
late final ScrollController _controller;
List<Proxy> proxies = [];
List<Proxy> currentProxies = [];
String? testUrl;
@override
@@ -308,7 +300,7 @@ class _ProxyGroupViewState extends ConsumerState<ProxyGroupView> {
16 +
getScrollToSelectedOffset(
groupName: widget.group.name,
proxies: proxies,
proxies: currentProxies,
),
_controller.position.maxScrollExtent,
),
@@ -321,45 +313,36 @@ class _ProxyGroupViewState extends ConsumerState<ProxyGroupView> {
Widget build(BuildContext context) {
final group = widget.group;
final proxies = group.all;
final sortedProxies = globalState.appController.getSortProxies(
proxies: proxies,
sortType: widget.sortType,
testUrl: group.testUrl,
);
this.proxies = sortedProxies;
testUrl = group.testUrl;
return Align(
alignment: Alignment.topCenter,
child: CommonScrollBar(
currentProxies = proxies;
return CommonScrollBar(
controller: _controller,
child: GridView.builder(
key: _getPageStorageKey(),
controller: _controller,
child: GridView.builder(
key: _getPageStorageKey(),
controller: _controller,
padding: const EdgeInsets.only(
top: 16,
left: 16,
right: 16,
bottom: 96,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.columns,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: getItemHeight(widget.cardType),
),
itemCount: sortedProxies.length,
itemBuilder: (_, index) {
final proxy = sortedProxies[index];
return ProxyCard(
testUrl: group.testUrl,
groupType: group.type,
type: widget.cardType,
proxy: proxy,
groupName: group.name,
);
},
padding: const EdgeInsets.only(
top: 16,
left: 16,
right: 16,
bottom: 96,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.columns,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: getItemHeight(widget.cardType),
),
itemCount: currentProxies.length,
itemBuilder: (_, index) {
final proxy = currentProxies[index];
return ProxyCard(
testUrl: group.testUrl,
groupType: group.type,
type: widget.cardType,
proxy: proxy,
groupName: group.name,
);
},
),
);
}

View File

@@ -274,7 +274,7 @@ packages:
source: hosted
version: "3.0.6"
custom_lint:
dependency: "direct dev"
dependency: transitive
description:
name: custom_lint
sha256: "78085fbe842de7c5bef92de811ca81536968dbcbbcdac5c316711add2d15e796"
@@ -770,10 +770,10 @@ packages:
dependency: "direct dev"
description:
name: json_serializable
sha256: ce2cf974ccdee13be2a510832d7fba0b94b364e0b0395dee42abaa51b855be27
sha256: "3f2913b7c2430afe8ac5afe6fb15c1de4a60af4f630625e6e238f80ba4b80cbd"
url: "https://pub.dev"
source: hosted
version: "6.10.0"
version: "6.11.0"
launch_at_startup:
dependency: "direct main"
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+2025082901
version: 0.8.88+2025083101
environment:
sdk: '>=3.8.0 <4.0.0'
@@ -74,7 +74,6 @@ dev_dependencies:
freezed: ^3.2.0
riverpod_generator: ^3.0.0-dev.17
riverpod_lint: ^3.0.0-dev.17
custom_lint: ^0.8.0
flutter:
uses-material-design: true