Add rule override

Optimize more details
This commit is contained in:
chen08209
2025-03-12 17:15:31 +08:00
parent de9c5ba9cc
commit a8c0519ffe
144 changed files with 7637 additions and 2203 deletions

View File

@@ -4,6 +4,8 @@ on:
push:
tags:
- 'v*'
env:
IS_STABLE: ${{ !contains(github.ref, '-') }}
jobs:
build:
@@ -74,7 +76,7 @@ jobs:
run: flutter pub get
- name: Setup
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }}
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }}
- name: Upload
uses: actions/upload-artifact@v4
@@ -88,14 +90,13 @@ jobs:
needs: [ build ]
steps:
- name: Checkout
if: ${{ !contains(github.ref, '+') }}
uses: actions/checkout@v4
if: ${{ env.IS_STABLE == 'true' }}
with:
fetch-depth: 0
ref: refs/heads/main
- name: Generate
if: ${{ !contains(github.ref, '+') }}
if: ${{ env.IS_STABLE == 'true' }}
run: |
tags=($(git tag --merged $(git rev-parse HEAD) --sort=-creatordate))
preTag=$(grep -oP '^## \K.*' CHANGELOG.md | head -n 1)
@@ -127,7 +128,7 @@ jobs:
cat NEW_CHANGELOG.md > CHANGELOG.md
- name: Commit
if: ${{ !contains(github.ref, '+') }}
if: ${{ env.IS_STABLE == 'true' }}
run: |
git add CHANGELOG.md
if ! git diff --cached --quiet; then
@@ -206,7 +207,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install requests
python release.py
python release_telegram.py
- name: Patch release.md
run: |
@@ -214,21 +215,21 @@ jobs:
sed "s|VERSION|$version|g" ./.github/release_template.md >> release.md
- name: Release
if: ${{ !contains(github.ref, '+') }}
if: ${{ env.IS_STABLE == 'true' }}
uses: softprops/action-gh-release@v2
with:
files: ./dist/*
body_path: './release.md'
- name: Create Fdroid Source Dir
if: ${{ !contains(github.ref, '+') }}
if: ${{ env.IS_STABLE == 'true' }}
run: |
mkdir -p ./tmp
cp ./dist/*android-arm64-v8a* ./tmp/ || true
echo "Files copied successfully"
- name: Push to fdroid repo
if: ${{ !contains(github.ref, '+') }}
if: ${{ env.IS_STABLE == 'true' }}
uses: cpina/github-action-push-to-another-repository@v1.7.2
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
@@ -238,7 +239,7 @@ jobs:
destination-repository-name: FlClash-fdroid-repo
user-name: 'github-actions[bot]'
user-email: 'github-actions[bot]@users.noreply.github.com'
target-branch: action-pr
target-branch: main
commit-message: Update from ${{ github.ref_name }}
target-directory: /tmp/

2
.gitmodules vendored
View File

@@ -1,7 +1,7 @@
[submodule "core/Clash.Meta"]
path = core/Clash.Meta
url = git@github.com:chen08209/Clash.Meta.git
branch = FlClash-Alpha
branch = FlClash
[submodule "plugins/flutter_distributor"]
path = plugins/flutter_distributor
url = git@github.com:chen08209/flutter_distributor.git

View File

@@ -1,8 +1 @@
include: package:flutter_lints/flutter.yaml
linter:
rules:
analyzer:
plugins:
- custom_lint

View File

@@ -33,7 +33,7 @@ def isRelease = defStoreFile.exists() && defStorePassword != null && defKeyAlias
android {
namespace "com.follow.clash"
compileSdkVersion 34
compileSdkVersion 35
ndkVersion "27.1.12297006"
compileOptions {
@@ -63,7 +63,7 @@ android {
defaultConfig {
applicationId "com.follow.clash"
minSdkVersion 21
targetSdkVersion 34
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

View File

@@ -10,14 +10,12 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
tools:ignore="SystemPermissionTypo" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
@@ -64,7 +62,9 @@
</intent-filter>
</activity>
<meta-data android:name="io.flutter.embedding.android.EnableImpeller" android:value="false" />
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" />
<activity
android:name=".TempActivity"
@@ -87,7 +87,6 @@
<service
android:name=".services.FlClashTileService"
android:exported="true"
android:foregroundServiceType="specialUse"
android:icon="@drawable/ic_stat_name"
android:label="FlClash"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
@@ -125,7 +124,7 @@
<service
android:name=".services.FlClashVpnService"
android:exported="false"
android:foregroundServiceType="specialUse"
android:foregroundServiceType="dataSync"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
@@ -138,7 +137,7 @@
<service
android:name=".services.FlClashService"
android:exported="false"
android:foregroundServiceType="specialUse">
android:foregroundServiceType="dataSync">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="service" />

View File

@@ -1,7 +1,7 @@
package com.follow.clash;
import android.app.Application
import android.content.Context;
import android.content.Context
class FlClashApplication : Application() {
companion object {

View File

@@ -1,9 +1,7 @@
package com.follow.clash
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.follow.clash.plugins.AppPlugin
import com.follow.clash.plugins.ServicePlugin
import com.follow.clash.plugins.TilePlugin
import com.follow.clash.plugins.VpnPlugin
import io.flutter.FlutterInjector

View File

@@ -291,16 +291,18 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
private fun getPackages(): List<Package> {
val packageManager = FlClashApplication.getAppContext().packageManager
if (packages.isNotEmpty()) return packages
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA)?.filter {
it.packageName != FlClashApplication.getAppContext().packageName
|| it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|| it.packageName == "android"
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS)
?.filter {
it.packageName != FlClashApplication.getAppContext().packageName && (
it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|| it.packageName == "android"
)
}?.map {
}?.map {
Package(
packageName = it.packageName,
label = it.applicationInfo.loadLabel(packageManager).toString(),
isSystem = (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 1,
label = it.applicationInfo?.loadLabel(packageManager).toString(),
isSystem = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) == 1,
lastUpdateTime = it.lastUpdateTime
)
}?.let { packages.addAll(it) }
@@ -353,7 +355,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
suspend fun getText(text: String): String? {
return withContext(Dispatchers.Default){
return withContext(Dispatchers.Default) {
channel.awaitResult<String>("getText", text)
}
}
@@ -391,31 +393,33 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}.forEach {
if (it.name.matches(chinaAppRegex)) return true
}
ZipFile(File(packageInfo.applicationInfo.publicSourceDir)).use {
for (packageEntry in it.entries()) {
if (packageEntry.name.startsWith("firebase-")) return false
}
for (packageEntry in it.entries()) {
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
".dex"
))
) {
continue
packageInfo.applicationInfo?.publicSourceDir?.let {
ZipFile(File(it)).use {
for (packageEntry in it.entries()) {
if (packageEntry.name.startsWith("firebase-")) return false
}
if (packageEntry.size > 15000000) {
return true
}
val input = it.getInputStream(packageEntry).buffered()
val dexFile = try {
DexBackedDexFile.fromInputStream(null, input)
} catch (e: Exception) {
return false
}
for (clazz in dexFile.classes) {
val clazzName =
clazz.type.substring(1, clazz.type.length - 1).replace("/", ".")
.replace("$", ".")
if (clazzName.matches(chinaAppRegex)) return true
for (packageEntry in it.entries()) {
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
".dex"
))
) {
continue
}
if (packageEntry.size > 15000000) {
return true
}
val input = it.getInputStream(packageEntry).buffered()
val dexFile = try {
DexBackedDexFile.fromInputStream(null, input)
} catch (e: Exception) {
return false
}
for (clazz in dexFile.classes) {
val clazzName =
clazz.type.substring(1, clazz.type.length - 1).replace("/", ".")
.replace("$", ".")
if (clazzName.matches(chinaAppRegex)) return true
}
}
}
}

View File

@@ -1,6 +1,5 @@
package com.follow.clash.plugins
import com.follow.clash.FlClashApplication
import com.follow.clash.GlobalState
import com.follow.clash.models.VpnOptions
import com.google.gson.Gson

View File

@@ -10,7 +10,6 @@ import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.content.getSystemService
import com.follow.clash.FlClashApplication
import com.follow.clash.GlobalState

View File

@@ -7,7 +7,7 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.os.Binder
import android.os.Build
import android.os.IBinder
@@ -87,6 +87,7 @@ class FlClashService : Service(), BaseServiceInterface {
}
}
}
private suspend fun getNotificationBuilder(): NotificationCompat.Builder {
return notificationBuilderDeferred.await()
}
@@ -100,7 +101,8 @@ class FlClashService : Service(), BaseServiceInterface {
}
}
@SuppressLint("ForegroundServiceType", "WrongConstant")
@SuppressLint("ForegroundServiceType")
override suspend fun startForeground(title: String, content: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = getSystemService(NotificationManager::class.java)
@@ -116,7 +118,11 @@ class FlClashService : Service(), BaseServiceInterface {
.setContentTitle(title)
.setContentText(content).build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
try {
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
} catch (_: Exception) {
startForeground(notificationId, notification)
}
} else {
startForeground(notificationId, notification)
}

View File

@@ -6,7 +6,7 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.net.ProxyInfo
import android.net.VpnService
import android.os.Binder
@@ -45,9 +45,16 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
addAddress(cidr.address, cidr.prefixLength)
val routeAddress = options.getIpv4RouteAddress()
if (routeAddress.isNotEmpty()) {
routeAddress.forEach { i ->
Log.d("addRoute4", "address: ${i.address} prefixLength:${i.prefixLength}")
addRoute(i.address, i.prefixLength)
try {
routeAddress.forEach { i ->
Log.d(
"addRoute4",
"address: ${i.address} prefixLength:${i.prefixLength}"
)
addRoute(i.address, i.prefixLength)
}
} catch (_: Exception) {
addRoute("0.0.0.0", 0)
}
} else {
addRoute("0.0.0.0", 0)
@@ -58,9 +65,16 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
addAddress(cidr.address, cidr.prefixLength)
val routeAddress = options.getIpv6RouteAddress()
if (routeAddress.isNotEmpty()) {
routeAddress.forEach { i ->
Log.d("addRoute6", "address: ${i.address} prefixLength:${i.prefixLength}")
addRoute(i.address, i.prefixLength)
try {
routeAddress.forEach { i ->
Log.d(
"addRoute6",
"address: ${i.address} prefixLength:${i.prefixLength}"
)
addRoute(i.address, i.prefixLength)
}
} catch (_: Exception) {
addRoute("::", 0)
}
} else {
addRoute("::", 0)
@@ -165,7 +179,7 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
return notificationBuilderDeferred.await()
}
@SuppressLint("ForegroundServiceType", "WrongConstant")
@SuppressLint("ForegroundServiceType")
override suspend fun startForeground(title: String, content: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = getSystemService(NotificationManager::class.java)
@@ -182,7 +196,11 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
.setContentText(content)
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
try {
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
} catch (_: Exception) {
startForeground(notificationId, notification)
}
} else {
startForeground(notificationId, notification)
}

View File

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

View File

@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip

Binary file not shown.

View File

@@ -28,8 +28,12 @@ import (
"sync"
)
func splitByComma(s string) interface{} {
parts := strings.Split(s, ",")
func splitByMultipleSeparators(s string) interface{} {
isSeparator := func(r rune) bool {
return r == ',' || r == ' ' || r == ';'
}
parts := strings.FieldsFunc(s, isSeparator)
if len(parts) > 1 {
return parts
}
@@ -168,18 +172,18 @@ func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig
return prof
}
func genHosts(hosts, patchHosts map[string]any) {
func attachHosts(hosts, patchHosts map[string]any) {
for k, v := range patchHosts {
if str, ok := v.(string); ok {
hosts[k] = splitByComma(str)
hosts[k] = splitByMultipleSeparators(str)
}
}
}
func modPatchDns(dns *config.RawDNS) {
func updatePatchDns(dns config.RawDNS) {
for pair := dns.NameServerPolicy.Oldest(); pair != nil; pair = pair.Next() {
if str, ok := pair.Value.(string); ok {
dns.NameServerPolicy.Set(pair.Key, splitByComma(str))
dns.NameServerPolicy.Set(pair.Key, splitByMultipleSeparators(str))
}
}
}
@@ -191,26 +195,25 @@ func trimArr(arr []string) (r []string) {
return
}
func overrideRules(rules *[]string) {
var target = ""
for _, line := range *rules {
func overrideRules(rules, patchRules []string) []string {
target := ""
for _, line := range rules {
rule := trimArr(strings.Split(line, ","))
l := len(rule)
if l != 2 {
return
if len(rule) != 2 {
continue
}
if strings.ToUpper(rule[0]) == "MATCH" {
if strings.EqualFold(rule[0], "MATCH") {
target = rule[1]
break
}
}
if target == "" {
return
return rules
}
var rulesExt = lo.Map(ips, func(ip string, index int) string {
return fmt.Sprintf("DOMAIN %s %s", ip, target)
rulesExt := lo.Map(ips, func(ip string, _ int) string {
return fmt.Sprintf("DOMAIN,%s,%s", ip, target)
})
*rules = append(rulesExt, *rules...)
return append(append(rulesExt, patchRules...), rules...)
}
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
@@ -244,16 +247,20 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
for idx := range targetConfig.ProxyGroup {
targetConfig.ProxyGroup[idx]["url"] = ""
}
genHosts(targetConfig.Hosts, patchConfig.Hosts)
attachHosts(targetConfig.Hosts, patchConfig.Hosts)
if configParams.OverrideDns {
modPatchDns(&patchConfig.DNS)
updatePatchDns(patchConfig.DNS)
targetConfig.DNS = patchConfig.DNS
} else {
if targetConfig.DNS.Enable == false {
targetConfig.DNS.Enable = true
}
}
overrideRules(&targetConfig.Rule)
if configParams.OverrideRule {
targetConfig.Rule = overrideRules(patchConfig.Rule, []string{})
} else {
targetConfig.Rule = overrideRules(targetConfig.Rule, patchConfig.Rule)
}
}
func patchConfig() {
@@ -267,6 +274,7 @@ func patchConfig() {
dialer.DefaultInterface.Store(general.Interface)
adapter.UnifiedDelay.Store(general.UnifiedDelay)
tunnel.SetMode(general.Mode)
tunnel.UpdateRules(currentConfig.Rules, currentConfig.SubRules, currentConfig.RuleProviders)
log.SetLevel(general.LogLevel)
resolver.DisableIPv6 = !general.IPv6

View File

@@ -13,6 +13,7 @@ type ConfigExtendedParams struct {
SelectedMap map[string]string `json:"selected-map"`
TestURL *string `json:"test-url"`
OverrideDns bool `json:"override-dns"`
OverrideRule bool `json:"override-rule"`
}
type GenerateConfigParams struct {

View File

@@ -21,7 +21,7 @@ require (
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/enfein/mieru/v3 v3.11.2 // indirect
github.com/enfein/mieru/v3 v3.13.0 // indirect
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
@@ -50,21 +50,22 @@ require (
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/bart v0.19.0 // indirect
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect
github.com/metacubex/chacha v0.1.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
github.com/metacubex/sing-tun v0.4.5 // indirect
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 // indirect
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 // indirect
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
github.com/metacubex/utls v1.6.6 // indirect
github.com/metacubex/utls v1.6.8-alpha.4 // indirect
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
@@ -74,7 +75,7 @@ require (
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.0 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/sagernet/cors v1.2.1 // indirect

View File

@@ -28,8 +28,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.11.2 h1:06KyGbXiiGz2nSHLJDOOkztAVY3cRr3wBMOpYxPotTo=
github.com/enfein/mieru/v3 v3.11.2/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
github.com/enfein/mieru/v3 v3.13.0 h1:eGyxLGkb+lut9ebmx+BGwLJ5UMbEc/wGIYO0AXEKy98=
github.com/enfein/mieru/v3 v3.13.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
@@ -97,14 +97,16 @@ 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/bart v0.19.0 h1:XQ9AJeI+WO+phRPkUOoflAFwlqDJnm5BPQpixciJQBY=
github.com/metacubex/bart v0.19.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic=
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM9MQ2ySuqu2aeBqcA8rtfWUYLZ8RtI=
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/jBDS/kCYjz/x+0BCOKfd2VODYevyeIt+Ds=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
@@ -117,16 +119,16 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ
github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0=
github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo=
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0=
github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 h1:B211C+i/I8CWf4I/BaAV0mmkEHrDBJ0XR9EWxjPbFEg=
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8=
github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo=
github.com/metacubex/utls v1.6.8-alpha.4 h1:5EvsCHxDNneaOtAyc8CztoNSpmonLvkvuGs01lIeeEI=
github.com/metacubex/utls v1.6.8-alpha.4/go.mod h1:MEZ5WO/VLKYs/s/dOzEK/mlXOQxc04ESeLzRgjmLYtk=
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/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
@@ -153,8 +155,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/puzpuzpuz/xsync/v3 v3.5.0 h1:i+cMcpEDY1BkNm7lPDkCtE4oElsYLn+EKF8kAu2vXT4=
github.com/puzpuzpuz/xsync/v3 v3.5.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=

View File

@@ -61,16 +61,6 @@ class ApplicationState extends ConsumerState<Application> {
_autoUpdateGroupTask();
_autoUpdateProfilesTask();
globalState.appController = AppController(context, ref);
globalState.measure = Measure.of(context);
// ref.listenManual(themeSettingProvider.select((state) => state.fontFamily),
// (prev, next) {
// if (prev != next) {
// globalState.measure = Measure.of(
// context,
// fontFamily: next.value,
// );
// }
// }, fireImmediately: true);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final currentContext = globalState.navigatorKey.currentContext;
if (currentContext != null) {
@@ -98,7 +88,7 @@ class ApplicationState extends ConsumerState<Application> {
});
}
_buildPlatformWrap(Widget child) {
_buildPlatformState(Widget child) {
if (system.isDesktop) {
return WindowManager(
child: TrayManager(
@@ -117,18 +107,7 @@ class ApplicationState extends ConsumerState<Application> {
);
}
_buildPage(Widget page) {
if (system.isDesktop) {
return WindowHeaderContainer(
child: page,
);
}
return VpnManager(
child: page,
);
}
_buildWrap(Widget child) {
_buildState(Widget child) {
return AppStateManager(
child: ClashManager(
child: ConnectivityManager(
@@ -142,6 +121,25 @@ class ApplicationState extends ConsumerState<Application> {
);
}
_buildPlatformApp(Widget child) {
if (system.isDesktop) {
return WindowHeaderContainer(
child: child,
);
}
return VpnManager(
child: child,
);
}
_buildApp(Widget child) {
return MessageManager(
child: ThemeManager(
child: child,
),
);
}
_updateSystemColorSchemes(
ColorScheme? lightDynamic,
ColorScheme? darkDynamic,
@@ -157,8 +155,8 @@ class ApplicationState extends ConsumerState<Application> {
@override
Widget build(context) {
return _buildPlatformWrap(
_buildWrap(
return _buildPlatformState(
_buildState(
Consumer(
builder: (_, ref, child) {
final locale =
@@ -168,6 +166,7 @@ class ApplicationState extends ConsumerState<Application> {
builder: (lightDynamic, darkDynamic) {
_updateSystemColorSchemes(lightDynamic, darkDynamic);
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: globalState.navigatorKey,
localizationsDelegates: const [
AppLocalizations.delegate,
@@ -176,14 +175,9 @@ class ApplicationState extends ConsumerState<Application> {
GlobalWidgetsLocalizations.delegate
],
builder: (_, child) {
return MessageManager(
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewWidth(
container.maxWidth,
);
return _buildPage(child!);
},
return AppEnvManager(
child: _buildPlatformApp(
_buildApp(child!),
),
);
},

View File

@@ -240,7 +240,7 @@ class ClashCore {
if (res.isEmpty) {
return null;
}
return ClashConfigSnippet.fromJson(json.decode(res));
return Isolate.run(() => ClashConfigSnippet.fromJson(json.decode(res)));
}
resetTraffic() {

View File

@@ -1,20 +1,40 @@
import 'package:flutter/material.dart';
extension ColorExtension on Color {
Color get toLight {
return withOpacity(0.8);
Color get opacity80 {
return withAlpha(204);
}
Color get toLighter {
return withOpacity(0.6);
Color get opacity60 {
return withAlpha(153);
}
Color get toSoft {
return withOpacity(0.15);
Color get opacity50 {
return withAlpha(128);
}
Color get toLittle {
return withOpacity(0.03);
Color get opacity38 {
return withAlpha(97);
}
Color get opacity30 {
return withAlpha(77);
}
Color get opacity15 {
return withAlpha(38);
}
Color get opacity10 {
return withAlpha(15);
}
Color get opacity3 {
return withAlpha(76);
}
Color get opacity0 {
return withAlpha(0);
}
Color darken([double amount = .1]) {

View File

@@ -21,6 +21,11 @@ const baseInfoEdgeInsets = EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
);
double textScaleFactor = min(
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
1.2,
);
const httpTimeoutDuration = Duration(milliseconds: 5000);
const moreDuration = Duration(milliseconds: 100);
const animateDuration = Duration(milliseconds: 100);
@@ -46,7 +51,7 @@ const defaultExternalController = "127.0.0.1:9090";
const maxMobileWidth = 600;
const maxLaptopWidth = 840;
const defaultTestUrl = "https://www.gstatic.com/generate_204";
final filter = ImageFilter.blur(
final commonFilter = ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
tileMode: TileMode.mirror,
@@ -76,7 +81,7 @@ const viewModeColumnsMap = {
const defaultPrimaryColor = Colors.brown;
double getWidgetHeight(num lines) {
return max(lines * 84 + (lines - 1) * 16, 0);
return max(lines * 84 * textScaleFactor + (lines - 1) * 16, 0);
}
final mainIsolate = "FlClashMainIsolate";

View File

@@ -1,7 +1,7 @@
import 'dart:async';
class Debouncer {
final Map<dynamic, Timer> _operations = {};
final Map<dynamic, Timer?> _operations = {};
call(
dynamic tag,
@@ -28,14 +28,15 @@ class Debouncer {
cancel(dynamic tag) {
_operations[tag]?.cancel();
_operations[tag] = null;
}
}
class Throttler {
final Map<dynamic, Timer> _operations = {};
final Map<dynamic, Timer?> _operations = {};
call(
String tag,
dynamic tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
@@ -60,6 +61,7 @@ class Throttler {
cancel(dynamic tag) {
_operations[tag]?.cancel();
_operations[tag] = null;
}
}

View File

@@ -65,3 +65,12 @@ extension DoubleListExt on List<double> {
return -1;
}
}
extension MapExt<K, V> on Map<K, V> {
getCacheValue(K key, V defaultValue) {
if (this[key] == null) {
this[key] = defaultValue;
}
return this[key];
}
}

View File

@@ -32,7 +32,7 @@ class FixedList<T> {
}
class FixedMap<K, V> {
final int maxSize;
int maxSize;
final Map<K, V> _map = {};
final Queue<K> _queue = Queue<K>();
@@ -45,6 +45,7 @@ class FixedMap<K, V> {
}
_map[key] = value;
_queue.add(key);
return value;
}
clear() {
@@ -52,8 +53,13 @@ class FixedMap<K, V> {
_queue.clear();
}
updateMaxSize(int size){
maxSize = size;
}
V? get(K key) => _map[key];
bool containsKey(K key) => _map.containsKey(key);
int get length => _map.length;

View File

@@ -5,13 +5,11 @@ import 'package:flutter/material.dart';
class Measure {
final TextScaler _textScale;
final BuildContext context;
final String? _fontFamily;
Measure.of(this.context, {String? fontFamily})
Measure.of(this.context)
: _textScale = TextScaler.linear(
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
),
_fontFamily = fontFamily ?? "";
textScaleFactor,
);
Size computeTextSize(
Text text, {
@@ -20,9 +18,7 @@ class Measure {
final textPainter = TextPainter(
text: TextSpan(
text: text.data,
style: text.style?.copyWith(
fontFamily: _fontFamily,
),
style: text.style,
),
maxLines: text.maxLines,
textScaler: _textScale,

View File

@@ -1,3 +1,4 @@
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart';
import 'package:riverpod/riverpod.dart';
import 'context.dart';
@@ -29,8 +30,14 @@ mixin PageMixin<T extends StatefulWidget> on State<T> {
final commonScaffoldState = context.commonScaffoldState;
commonScaffoldState?.actions = actions;
commonScaffoldState?.floatingActionButton = floatingActionButton;
commonScaffoldState?.onSearch = onSearch;
commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate;
commonScaffoldState?.updateSearchState(
(_) => onSearch != null
? AppBarSearchState(
onSearch: onSearch!,
)
: null,
);
});
}

View File

@@ -70,7 +70,7 @@ class CommonRoute<T> extends MaterialPageRoute<T> {
Duration get transitionDuration => const Duration(milliseconds: 500);
@override
Duration get reverseTransitionDuration => const Duration(milliseconds: 250);
Duration get reverseTransitionDuration => const Duration(milliseconds: 500);
}
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
@@ -194,7 +194,7 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
_primaryPositionCurve = CurvedAnimation(
parent: widget.primaryRouteAnimation,
curve: Curves.fastEaseInToSlowEaseOut,
reverseCurve: Curves.easeInOut,
reverseCurve: Curves.fastEaseInToSlowEaseOut.flipped,
);
_secondaryPositionCurve = CurvedAnimation(
parent: widget.secondaryRouteAnimation,
@@ -218,9 +218,8 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(
<Color>[
widget.context.colorScheme.inverseSurface.withOpacity(
0.06,
),
widget.context.colorScheme.inverseSurface
.withValues(alpha: 0.02),
Colors.transparent,
],
),
@@ -274,7 +273,7 @@ class _CommonEdgeShadowPainter extends BoxPainter {
return;
}
final double shadowWidth = 0.03 * configuration.size!.width;
final double shadowWidth = 1 * configuration.size!.width;
final double shadowHeight = configuration.size!.height;
final double bandWidth = shadowWidth / (colors.length - 1);

View File

@@ -241,11 +241,6 @@ class Other {
return "${appName}_${DateTime.now().show}.log";
}
Size getScreenSize() {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
return view.physicalSize / view.devicePixelRatio;
}
Future<String?> getLocalIpAddress() async {
List<NetworkInterface> interfaces = await NetworkInterface.list(
includeLoopback: false,

View File

@@ -14,15 +14,13 @@ class Protocol {
void register(String scheme) {
String protocolRegKey = 'Software\\Classes\\$scheme';
RegistryValue protocolRegValue = const RegistryValue(
RegistryValue protocolRegValue = RegistryValue.string(
'URL Protocol',
RegistryValueType.string,
'',
);
String protocolCmdRegKey = 'shell\\open\\command';
RegistryValue protocolCmdRegValue = RegistryValue(
RegistryValue protocolCmdRegValue = RegistryValue.string(
'',
RegistryValueType.string,
'"${Platform.resolvedExecutable}" "%1"',
);
final regKey = Registry.currentUser.createKey(protocolRegKey);
@@ -31,4 +29,4 @@ class Protocol {
}
}
final protocol = Protocol();
final protocol = Protocol();

View File

@@ -22,7 +22,7 @@ class Render {
}
pause() {
debouncer.call(
throttler.call(
DebounceTag.renderPause,
_pause,
duration: Duration(seconds: 5),
@@ -30,11 +30,11 @@ class Render {
}
resume() {
debouncer.cancel(DebounceTag.renderPause);
throttler.cancel(DebounceTag.renderPause);
_resume();
}
void _pause() {
void _pause() async {
if (_isPaused) return;
_isPaused = true;
_beginFrame = _dispatcher.onBeginFrame;

View File

@@ -83,13 +83,19 @@ class Request {
Future<IpInfo?> checkIp({CancelToken? cancelToken}) async {
for (final source in _ipInfoSources.entries) {
try {
final response = await _dio.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(
responseType: ResponseType.json,
),
);
final response = await Dio()
.get<Map<String, dynamic>>(
source.key,
cancelToken: cancelToken,
options: Options(
responseType: ResponseType.json,
),
)
.timeout(
Duration(
seconds: 30,
),
);
if (response.statusCode != 200 || response.data == null) {
continue;
}

View File

@@ -2,6 +2,7 @@ import 'dart:math';
import 'dart:ui';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/widgets/scroll.dart';
import 'package:flutter/material.dart';
class BaseScrollBehavior extends MaterialScrollBehavior {
@@ -16,8 +17,6 @@ class BaseScrollBehavior extends MaterialScrollBehavior {
};
}
class BaseScrollBehavior2 extends ScrollBehavior {}
class HiddenBarScrollBehavior extends BaseScrollBehavior {
@override
Widget buildScrollbar(
@@ -36,8 +35,7 @@ class ShowBarScrollBehavior extends BaseScrollBehavior {
Widget child,
ScrollableDetails details,
) {
return Scrollbar(
interactive: true,
return CommonAutoHiddenScrollBar(
controller: details.controller,
child: child,
);

View File

@@ -1,15 +1,20 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
import 'color.dart';
extension TextStyleExtension on TextStyle {
TextStyle get toLight => copyWith(color: color?.toLight);
TextStyle get toLight => copyWith(color: color?.opacity80);
TextStyle get toLighter => copyWith(color: color?.toLighter);
TextStyle get toLighter => copyWith(color: color?.opacity60);
TextStyle get toSoftBold => copyWith(fontWeight: FontWeight.w500);
TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);
TextStyle get toJetBrainsMono => copyWith(
fontFamily: FontFamily.jetBrainsMono.value,
);
TextStyle adjustSize(int size) => copyWith(
fontSize: fontSize! + size,
);

39
lib/common/theme.dart Normal file
View File

@@ -0,0 +1,39 @@
import 'package:fl_clash/common/common.dart';
import 'package:flutter/material.dart';
class CommonTheme {
final BuildContext context;
final Map<String, Color> _colorMap;
CommonTheme.of(this.context) : _colorMap = {};
Color get darkenSecondaryContainer {
return _colorMap.getCacheValue(
"darkenSecondaryContainer",
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.1),
);
}
Color get darkenSecondaryContainerLighter {
return _colorMap.getCacheValue(
"darkenSecondaryContainerLighter",
context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1)
.opacity60,
);
}
Color get darken2SecondaryContainer {
return _colorMap.getCacheValue(
"darken2SecondaryContainer",
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.2),
);
}
Color get darken3PrimaryContainer {
return _colorMap.getCacheValue(
"darken3PrimaryContainer",
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3),
);
}
}

View File

@@ -21,12 +21,12 @@ class Window {
await windowManager.ensureInitialized();
WindowOptions windowOptions = WindowOptions(
size: Size(props.width, props.height),
minimumSize: const Size(380, 500),
minimumSize: const Size(380, 400),
);
if (!Platform.isMacOS || version > 10) {
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
}
if(!Platform.isMacOS){
if (!Platform.isMacOS) {
final left = props.left ?? 0;
final top = props.top ?? 0;
final right = left + props.width;
@@ -36,7 +36,7 @@ class Window {
} else {
final displays = await screenRetriever.getAllDisplays();
final isPositionValid = displays.any(
(display) {
(display) {
final displayBounds = Rect.fromLTWH(
display.visiblePosition!.dx,
display.visiblePosition!.dy,
@@ -69,8 +69,10 @@ class Window {
await windowManager.setSkipTaskbar(false);
}
Future<bool> isVisible() async {
return await windowManager.isVisible();
Future<bool> get isVisible async {
final value = await windowManager.isVisible();
commonPrint.log("window visible check: $value");
return value;
}
close() async {

View File

@@ -10,12 +10,14 @@ import 'package:fl_clash/common/archive.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path/path.dart';
import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
import 'fragments/profiles/override_profile.dart';
import 'models/models.dart';
class AppController {
@@ -242,6 +244,7 @@ class AppController {
}
Future<void> updateClashConfig([bool? isPatch]) async {
commonPrint.log("update clash patch: ${isPatch ?? false}");
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return;
await commonScaffoldState?.loadingRun(() async {
@@ -414,6 +417,9 @@ class AppController {
Map<String, dynamic>? data,
bool handleError = false,
}) async {
if(globalState.isPre){
return;
}
if (data != null) {
final tagName = data['tag_name'];
final body = data['body'];
@@ -520,37 +526,12 @@ class AppController {
_ref.read(delayDataSourceProvider.notifier).setDelay(delay);
}
toPage(
int index, {
bool hasAnimate = false,
}) {
final navigations = _ref.read(currentNavigationsStateProvider).value;
if (index > navigations.length - 1) {
return;
}
_ref.read(currentPageLabelProvider.notifier).value =
navigations[index].label;
final isAnimateToPage = _ref.read(appSettingProvider).isAnimateToPage;
final isMobile =
_ref.read(viewWidthProvider.notifier).viewMode == ViewMode.mobile;
if (isAnimateToPage && isMobile || hasAnimate) {
globalState.pageController?.animateToPage(
index,
duration: kTabScrollDuration,
curve: Curves.easeOut,
);
} else {
globalState.pageController?.jumpToPage(index);
}
toPage(PageLabel pageLabel) {
_ref.read(currentPageLabelProvider.notifier).value = pageLabel;
}
toProfiles() {
final index = _ref.read(currentNavigationsStateProvider).value.indexWhere(
(element) => element.label == PageLabel.profiles,
);
if (index != -1) {
toPage(index);
}
toPage(PageLabel.profiles);
}
initLink() {
@@ -587,17 +568,8 @@ class AppController {
Future<bool> showDisclaimer() async {
return await globalState.showCommonDialog<bool>(
dismissible: false,
child: AlertDialog(
title: Text(appLocalizations.disclaimer),
content: Container(
width: dialogCommonWidth,
constraints: const BoxConstraints(maxHeight: 200),
child: SingleChildScrollView(
child: SelectableText(
appLocalizations.disclaimerDesc,
),
),
),
child: CommonDialog(
title: appLocalizations.disclaimer,
actions: [
TextButton(
onPressed: () {
@@ -615,6 +587,9 @@ class AppController {
child: Text(appLocalizations.agree),
)
],
child: SelectableText(
appLocalizations.disclaimerDesc,
),
),
) ??
false;
@@ -680,9 +655,9 @@ class AppController {
addProfileFormURL(url);
}
updateViewWidth(double width) {
updateViewSize(Size size) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_ref.read(viewWidthProvider.notifier).value = width;
_ref.read(viewSizeProvider.notifier).value = size;
});
}
@@ -741,10 +716,18 @@ class AppController {
final providersPath = await appPath.getProvidersPath(profileId);
return await Isolate.run(() async {
if (profilePath != null) {
await File(profilePath).delete(recursive: true);
final profileFile = File(profilePath);
final isExists = await profileFile.exists();
if (isExists) {
profileFile.delete(recursive: true);
}
}
if (providersPath != null) {
await File(providersPath).delete(recursive: true);
final providersFileDir = File(providersPath);
final isExists = await providersFileDir.exists();
if (isExists) {
providersFileDir.delete(recursive: true);
}
}
});
}
@@ -798,10 +781,10 @@ class AppController {
_ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(mode: mode),
);
// if (mode == Mode.global) {
// updateCurrentGroupName(GroupName.GLOBAL.name);
// }
// addCheckIpNumDebounce();
if (mode == Mode.global) {
updateCurrentGroupName(GroupName.GLOBAL.name);
}
addCheckIpNumDebounce();
}
updateAutoLaunch() {
@@ -813,7 +796,7 @@ class AppController {
}
updateVisible() async {
final visible = await window?.isVisible();
final visible = await window?.isVisible;
if (visible != null && !visible) {
window?.show();
} else {
@@ -836,6 +819,38 @@ class AppController {
);
}
handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {
final res = await globalState.showCommonDialog<Rule>(
child: AddRuleDialog(
rule: rule,
snippet: ref.read(
profileOverrideStateProvider.select(
(state) => state.snippet!,
),
),
),
);
if (res == null) {
return;
}
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) {
final model = state.copyWith.overrideData!(
rule: state.overrideData!.rule.updateRules(
(rules) {
final index = rules.indexWhere((item) => item.id == res.id);
if (index == -1) {
return List.from([res, ...rules]);
}
return List.from(rules)..[index] = res;
},
),
);
return model;
},
);
}
Future<bool> exportLogs() async {
final logsRaw = _ref.read(logsProvider).list.map(
(item) => item.toString(),

View File

@@ -220,11 +220,12 @@ enum ProxiesIconStyle {
enum FontFamily {
twEmoji("Twemoji"),
jetBrainsMono("JetBrainsMono"),
icon("Icons");
final String? value;
final String value;
const FontFamily([this.value]);
const FontFamily(this.value);
}
enum RouteMode {
@@ -384,3 +385,65 @@ enum PageLabel {
resources,
connections,
}
enum RuleAction {
DOMAIN("DOMAIN"),
DOMAIN_SUFFIX("DOMAIN-SUFFIX"),
DOMAIN_KEYWORD("DOMAIN-KEYWORD"),
DOMAIN_REGEX("DOMAIN-REGEX"),
GEOSITE("GEOSITE"),
IP_CIDR("IP-CIDR"),
IP_CIDR6("IP-CIDR6"),
IP_SUFFIX("IP-SUFFIX"),
IP_ASN("IP-ASN"),
GEOIP("GEOIP"),
SRC_GEOIP("SRC-GEOIP"),
SRC_IP_ASN("SRC-IP-ASN"),
SRC_IP_CIDR("SRC-IP-CIDR"),
SRC_IP_SUFFIX("SRC-IP-SUFFIX"),
DST_PORT("DST-PORT"),
SRC_PORT("SRC-PORT"),
IN_PORT("IN-PORT"),
IN_TYPE("IN-TYPE"),
IN_USER("IN-USER"),
IN_NAME("IN-NAME"),
PROCESS_PATH("PROCESS-PATH"),
PROCESS_PATH_REGEX("PROCESS-PATH-REGEX"),
PROCESS_NAME("PROCESS-NAME"),
PROCESS_NAME_REGEX("PROCESS-NAME-REGEX"),
UID("UID"),
NETWORK("NETWORK"),
DSCP("DSCP"),
RULE_SET("RULE-SET"),
AND("AND"),
OR("OR"),
NOT("NOT"),
SUB_RULE("SUB-RULE"),
MATCH("MATCH");
final String value;
const RuleAction(this.value);
}
extension RuleActionExt on RuleAction {
bool get hasParams => [
RuleAction.GEOIP,
RuleAction.IP_ASN,
RuleAction.SRC_IP_ASN,
RuleAction.IP_CIDR,
RuleAction.IP_CIDR6,
RuleAction.IP_SUFFIX,
RuleAction.RULE_SET,
].contains(this);
}
enum OverrideRuleType {
override,
added,
}
enum RuleTarget {
DIRECT,
REJECT,
}

View File

@@ -150,9 +150,17 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
return IconButton(
onPressed: () async {
final res = await showSheet<int>(
title: appLocalizations.proxiesSetting,
context: context,
body: AccessControlPanel(),
props: SheetProps(
isScrollControlled: true,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: AccessControlPanel(),
title: appLocalizations.proxiesSetting,
);
},
);
if (res == 1) {
_intelligentSelected();
@@ -763,17 +771,19 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
..._buildModeSetting(),
..._buildSortSetting(),
..._buildSourceSetting(),
..._buildActionSetting(),
],
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(bottom: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
..._buildModeSetting(),
..._buildSortSetting(),
..._buildSourceSetting(),
..._buildActionSetting(),
],
),
),
);
}

View File

@@ -276,8 +276,8 @@ class ApplicationSettingFragment extends StatelessWidget {
AutoRunItem(),
if (Platform.isAndroid) ...[
HiddenItem(),
AnimateTabItem(),
],
AnimateTabItem(),
OpenLogsItem(),
CloseConnectionsItem(),
UsageItem(),

View File

@@ -6,6 +6,7 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/fade_box.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:fl_clash/widgets/text.dart';
@@ -174,7 +175,7 @@ class BackupAndRecovery extends ConsumerWidget {
future: client!.pingCompleter.future,
builder: (_, snapshot) {
return Center(
child: FadeBox(
child: FadeThroughBox(
child: snapshot.connectionState ==
ConnectionState.waiting
? const SizedBox(
@@ -275,30 +276,27 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.recovery),
contentPadding: const EdgeInsets.symmetric(
return CommonDialog(
title: appLocalizations.recovery,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
content: SizedBox(
width: 250,
child: Wrap(
children: [
ListItem(
onTap: () {
_handleOnTab(RecoveryOption.onlyProfiles);
},
title: Text(appLocalizations.recoveryProfiles),
),
ListItem(
onTap: () {
_handleOnTab(RecoveryOption.all);
},
title: Text(appLocalizations.recoveryAll),
)
],
),
child: Wrap(
children: [
ListItem(
onTap: () {
_handleOnTab(RecoveryOption.onlyProfiles);
},
title: Text(appLocalizations.recoveryProfiles),
),
ListItem(
onTap: () {
_handleOnTab(RecoveryOption.all);
},
title: Text(appLocalizations.recoveryAll),
)
],
),
);
}
@@ -351,78 +349,8 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.webDAVConfiguration),
content: Form(
key: _formKey,
child: SizedBox(
width: dialogCommonWidth,
child: Wrap(
runSpacing: 16,
children: [
TextFormField(
controller: uriController,
maxLines: 5,
minLines: 1,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.link),
border: const OutlineInputBorder(),
labelText: appLocalizations.address,
helperText: appLocalizations.addressHelp,
),
validator: (String? value) {
if (value == null || value.isEmpty || !value.isUrl) {
return appLocalizations.addressTip;
}
return null;
},
),
TextFormField(
controller: userController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.account_circle),
border: const OutlineInputBorder(),
labelText: appLocalizations.account,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.accountTip;
}
return null;
},
),
ValueListenableBuilder(
valueListenable: _obscureController,
builder: (_, obscure, __) {
return TextFormField(
controller: passwordController,
obscureText: obscure,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.password),
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
obscure ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
_obscureController.value = !obscure;
},
),
labelText: appLocalizations.password,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.passwordTip;
}
return null;
},
);
},
),
],
),
),
),
return CommonDialog(
title: appLocalizations.webDAVConfiguration,
actions: [
if (widget.dav != null)
TextButton(
@@ -434,6 +362,73 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
child: Text(appLocalizations.save),
)
],
child: Form(
key: _formKey,
child: Wrap(
runSpacing: 16,
children: [
TextFormField(
controller: uriController,
maxLines: 5,
minLines: 1,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.link),
border: const OutlineInputBorder(),
labelText: appLocalizations.address,
helperText: appLocalizations.addressHelp,
),
validator: (String? value) {
if (value == null || value.isEmpty || !value.isUrl) {
return appLocalizations.addressTip;
}
return null;
},
),
TextFormField(
controller: userController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.account_circle),
border: const OutlineInputBorder(),
labelText: appLocalizations.account,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.accountTip;
}
return null;
},
),
ValueListenableBuilder(
valueListenable: _obscureController,
builder: (_, obscure, __) {
return TextFormField(
controller: passwordController,
obscureText: obscure,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.password),
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
obscure ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
_obscureController.value = !obscure;
},
),
labelText: appLocalizations.password,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.passwordTip;
}
return null;
},
);
},
),
],
),
),
);
}
}

View File

@@ -2,8 +2,13 @@ import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/fragments/config/dns.dart';
import 'package:fl_clash/fragments/config/general.dart';
import 'package:fl_clash/fragments/config/network.dart';
import 'package:fl_clash/models/clash_config.dart';
import 'package:fl_clash/providers/config.dart' show patchClashConfigProvider;
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../state.dart';
class ConfigFragment extends StatefulWidget {
const ConfigFragment({super.key});
@@ -16,18 +21,6 @@ class _ConfigFragmentState extends State<ConfigFragment> {
@override
Widget build(BuildContext context) {
List<Widget> items = [
ListItem.open(
title: Text(appLocalizations.network),
subtitle: Text(appLocalizations.networkDesc),
leading: const Icon(Icons.vpn_key),
delegate: OpenDelegate(
title: appLocalizations.network,
isScaffold: true,
isBlur: false,
extendPageWidth: 360,
widget: const NetworkListView(),
),
),
ListItem.open(
title: Text(appLocalizations.general),
subtitle: Text(appLocalizations.generalDesc),
@@ -37,20 +30,52 @@ class _ConfigFragmentState extends State<ConfigFragment> {
widget: generateListView(
generalItems,
),
isBlur: false,
extendPageWidth: 360,
blur: false,
),
),
ListItem.open(
title: Text(appLocalizations.network),
subtitle: Text(appLocalizations.networkDesc),
leading: const Icon(Icons.vpn_key),
delegate: OpenDelegate(
title: appLocalizations.network,
blur: false,
widget: const NetworkListView(),
),
),
ListItem.open(
title: const Text("DNS"),
subtitle: Text(appLocalizations.dnsDesc),
leading: const Icon(Icons.dns),
delegate: const OpenDelegate(
delegate: OpenDelegate(
title: "DNS",
widget: DnsListView(),
isScaffold: true,
isBlur: false,
extendPageWidth: 360,
action: Consumer(builder: (_, ref, __) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
dns: defaultDns,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
);
}),
widget: const DnsListView(),
blur: false,
),
)
];

View File

@@ -1,8 +1,6 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/config.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';
@@ -205,7 +203,7 @@ class FakeIpFilterItem extends StatelessWidget {
return ListItem.open(
title: Text(appLocalizations.fakeipFilter),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.fakeipFilter,
widget: Consumer(
builder: (_, ref, __) {
@@ -213,7 +211,7 @@ class FakeIpFilterItem extends StatelessWidget {
patchClashConfigProvider
.select((state) => state.dns.fakeIpFilter),
);
return ListPage(
return ListInputPage(
title: appLocalizations.fakeipFilter,
items: fakeIpFilter,
titleBuilder: (item) => Text(item),
@@ -227,7 +225,6 @@ class FakeIpFilterItem extends StatelessWidget {
);
},
),
extendPageWidth: 360,
),
);
}
@@ -242,14 +239,14 @@ class DefaultNameserverItem extends StatelessWidget {
title: Text(appLocalizations.defaultNameserver),
subtitle: Text(appLocalizations.defaultNameserverDesc),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.defaultNameserver,
widget: Consumer(builder: (_, ref, __) {
final defaultNameserver = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.defaultNameserver),
);
return ListPage(
return ListInputPage(
title: appLocalizations.defaultNameserver,
items: defaultNameserver,
titleBuilder: (item) => Text(item),
@@ -262,7 +259,6 @@ class DefaultNameserverItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -278,13 +274,13 @@ class NameserverItem extends StatelessWidget {
subtitle: Text(appLocalizations.nameserverDesc),
delegate: OpenDelegate(
title: appLocalizations.nameserver,
isBlur: false,
blur: false,
widget: Consumer(builder: (_, ref, __) {
final nameserver = ref.watch(
patchClashConfigProvider.select((state) => state.dns.nameserver),
);
return ListPage(
title: "域名服务器",
return ListInputPage(
title: appLocalizations.nameserver,
items: nameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
@@ -296,7 +292,6 @@ class NameserverItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -357,28 +352,27 @@ class NameserverPolicyItem extends StatelessWidget {
title: Text(appLocalizations.nameserverPolicy),
subtitle: Text(appLocalizations.nameserverPolicyDesc),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.nameserverPolicy,
widget: Consumer(builder: (_, ref, __) {
final nameserverPolicy = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.nameserverPolicy),
);
return ListPage(
return MapInputPage(
title: appLocalizations.nameserverPolicy,
items: nameserverPolicy.entries,
map: nameserverPolicy,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (items) {
onChange: (value) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
nameserverPolicy: Map.fromEntries(items),
nameserverPolicy: value,
),
);
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -393,7 +387,7 @@ class ProxyServerNameserverItem extends StatelessWidget {
title: Text(appLocalizations.proxyNameserver),
subtitle: Text(appLocalizations.proxyNameserverDesc),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.proxyNameserver,
widget: Consumer(
builder: (_, ref, __) {
@@ -401,7 +395,7 @@ class ProxyServerNameserverItem extends StatelessWidget {
patchClashConfigProvider
.select((state) => state.dns.proxyServerNameserver),
);
return ListPage(
return ListInputPage(
title: appLocalizations.proxyNameserver,
items: proxyServerNameserver,
titleBuilder: (item) => Text(item),
@@ -415,7 +409,6 @@ class ProxyServerNameserverItem extends StatelessWidget {
);
},
),
extendPageWidth: 360,
),
);
}
@@ -430,13 +423,13 @@ class FallbackItem extends StatelessWidget {
title: Text(appLocalizations.fallback),
subtitle: Text(appLocalizations.fallbackDesc),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.fallback,
widget: Consumer(builder: (_, ref, __) {
final fallback = ref.watch(
patchClashConfigProvider.select((state) => state.dns.fallback),
);
return ListPage(
return ListInputPage(
title: appLocalizations.fallback,
items: fallback,
titleBuilder: (item) => Text(item),
@@ -449,7 +442,6 @@ class FallbackItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -518,14 +510,14 @@ class GeositeItem extends StatelessWidget {
return ListItem.open(
title: const Text("Geosite"),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: "Geosite",
widget: Consumer(builder: (_, ref, __) {
final geosite = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geosite),
);
return ListPage(
return ListInputPage(
title: "Geosite",
items: geosite,
titleBuilder: (item) => Text(item),
@@ -538,7 +530,6 @@ class GeositeItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -552,14 +543,14 @@ class IpcidrItem extends StatelessWidget {
return ListItem.open(
title: Text(appLocalizations.ipcidr),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.ipcidr,
widget: Consumer(builder: (_, ref, ___) {
final ipcidr = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.ipcidr),
);
return ListPage(
return ListInputPage(
title: appLocalizations.ipcidr,
items: ipcidr,
titleBuilder: (item) => Text(item),
@@ -572,7 +563,6 @@ class IpcidrItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -586,14 +576,14 @@ class DomainItem extends StatelessWidget {
return ListItem.open(
title: Text(appLocalizations.domain),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: appLocalizations.domain,
widget: Consumer(builder: (_, ref, __) {
final domain = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.domain),
);
return ListPage(
return ListInputPage(
title: appLocalizations.domain,
items: domain,
titleBuilder: (item) => Text(item),
@@ -606,7 +596,6 @@ class DomainItem extends StatelessWidget {
},
);
}),
extendPageWidth: 360,
),
);
}
@@ -671,39 +660,8 @@ const dnsItems = <Widget>[
class DnsListView extends ConsumerWidget {
const DnsListView({super.key});
_initActions(BuildContext context, WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
context.commonScaffoldState?.actions = [
IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
dns: defaultDns,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
)
];
});
}
@override
Widget build(BuildContext context, ref) {
_initActions(context, ref);
return generateListView(
dnsItems,
);

View File

@@ -199,28 +199,27 @@ class HostsItem extends StatelessWidget {
title: const Text("Hosts"),
subtitle: Text(appLocalizations.hostsDesc),
delegate: OpenDelegate(
isBlur: false,
blur: false,
title: "Hosts",
widget: Consumer(
builder: (_, ref, __) {
final hosts = ref
.watch(patchClashConfigProvider.select((state) => state.hosts));
return ListPage(
return MapInputPage(
title: "Hosts",
items: hosts.entries,
map: hosts,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (items) {
onChange: (value) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
hosts: Map.fromEntries(items),
hosts: value,
),
);
},
);
},
),
extendPageWidth: 360,
),
);
}

View File

@@ -224,15 +224,14 @@ class BypassDomainItem extends StatelessWidget {
title: Text(appLocalizations.bypassDomain),
subtitle: Text(appLocalizations.bypassDomainDesc),
delegate: OpenDelegate(
isBlur: false,
isScaffold: true,
blur: false,
title: appLocalizations.bypassDomain,
widget: Consumer(
builder: (_, ref, __) {
_initActions(context, ref);
final bypassDomain = ref.watch(
networkSettingProvider.select((state) => state.bypassDomain));
return ListPage(
return ListInputPage(
title: appLocalizations.bypassDomain,
items: bypassDomain,
titleBuilder: (item) => Text(item),
@@ -246,7 +245,6 @@ class BypassDomainItem extends StatelessWidget {
);
},
),
extendPageWidth: 360,
),
);
}
@@ -298,14 +296,14 @@ class RouteAddressItem extends ConsumerWidget {
title: Text(appLocalizations.routeAddress),
subtitle: Text(appLocalizations.routeAddressDesc),
delegate: OpenDelegate(
isBlur: false,
isScaffold: true,
blur: false,
maxWidth: 360,
title: appLocalizations.routeAddress,
widget: Consumer(
builder: (_, ref, __) {
final routeAddress = ref.watch(patchClashConfigProvider
.select((state) => state.tun.routeAddress));
return ListPage(
return ListInputPage(
title: appLocalizations.routeAddress,
items: routeAddress,
titleBuilder: (item) => Text(item),
@@ -319,7 +317,6 @@ class RouteAddressItem extends ConsumerWidget {
);
},
),
extendPageWidth: 360,
),
);
}

View File

@@ -125,7 +125,7 @@ class _ConnectionsFragmentState extends ConsumerState<ConnectionsFragment>
return ConnectionItem(
key: Key(connection.id),
connection: connection,
onClick: (value) {
onClickKeyword: (value) {
context.commonScaffoldState?.addKeyword(value);
},
trailing: IconButton(

View File

@@ -9,40 +9,15 @@ import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FindProcessBuilder extends StatelessWidget {
final Widget Function(bool value) builder;
const FindProcessBuilder({
super.key,
required this.builder,
});
@override
Widget build(BuildContext context) {
return Consumer(
builder: (_, ref, __) {
final value = ref.watch(
patchClashConfigProvider.select(
(state) =>
state.findProcessMode == FindProcessMode.always &&
Platform.isAndroid,
),
);
return builder(value);
},
);
}
}
class ConnectionItem extends StatelessWidget {
class ConnectionItem extends ConsumerWidget {
final Connection connection;
final Function(String)? onClick;
final Function(String)? onClickKeyword;
final Widget? trailing;
const ConnectionItem({
super.key,
required this.connection,
this.onClick,
this.onClickKeyword,
this.trailing,
});
@@ -59,7 +34,14 @@ class ConnectionItem extends StatelessWidget {
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, ref) {
final value = ref.watch(
patchClashConfigProvider.select(
(state) =>
state.findProcessMode == FindProcessMode.always &&
Platform.isAndroid,
),
);
final title = Text(
connection.desc,
style: context.textTheme.bodyLarge,
@@ -86,70 +68,143 @@ class ConnectionItem extends StatelessWidget {
CommonChip(
label: chain,
onPressed: () {
if (onClick == null) return;
onClick!(chain);
if (onClickKeyword == null) return;
onClickKeyword!(chain);
},
),
],
),
],
);
if (!Platform.isAndroid) {
return ListItem(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
title: title,
subtitle: subTitle,
trailing: trailing,
);
}
return FindProcessBuilder(
builder: (bool value) {
final leading = value
? GestureDetector(
onTap: () {
if (onClick == null) return;
final process = connection.metadata.process;
if (process.isEmpty) return;
onClick!(process);
},
child: Container(
margin: const EdgeInsets.only(top: 4),
width: 48,
height: 48,
child: FutureBuilder<ImageProvider?>(
future: _getPackageIcon(connection),
builder: (_, snapshot) {
if (!snapshot.hasData && snapshot.data == null) {
return Container();
} else {
return Image(
image: snapshot.data!,
gaplessPlayback: true,
width: 48,
height: 48,
);
}
},
),
),
)
: null;
return CommonPopupBox(
targetBuilder: (open) {
return ListItem(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
leading: leading,
leading: value
? GestureDetector(
onTap: () {
if (onClickKeyword == null) return;
final process = connection.metadata.process;
if (process.isEmpty) return;
onClickKeyword!(process);
},
child: Container(
margin: const EdgeInsets.only(top: 4),
width: 48,
height: 48,
child: FutureBuilder<ImageProvider?>(
future: _getPackageIcon(connection),
builder: (_, snapshot) {
if (!snapshot.hasData && snapshot.data == null) {
return Container();
} else {
return Image(
image: snapshot.data!,
gaplessPlayback: true,
width: 48,
height: 48,
);
}
},
),
),
)
: null,
title: title,
subtitle: subTitle,
trailing: trailing,
);
// return InkWell(
// child: GestureDetector(
// onLongPressStart: (details) {
// if (!system.isDesktop) {
// return;
// }
// open(
// offset: details.localPosition.translate(
// 0,
// -12,
// ),
// );
// },
// onSecondaryTapDown: (details) {
// if (!system.isDesktop) {
// return;
// }
// open(
// offset: details.localPosition.translate(
// 0,
// -12,
// ),
// );
// },
// child: ListItem(
// padding: const EdgeInsets.symmetric(
// horizontal: 16,
// vertical: 4,
// ),
// tileTitleAlignment: ListTileTitleAlignment.titleHeight,
// leading: value
// ? GestureDetector(
// onTap: () {
// if (onClickKeyword == null) return;
// final process = connection.metadata.process;
// if (process.isEmpty) return;
// onClickKeyword!(process);
// },
// child: Container(
// margin: const EdgeInsets.only(top: 4),
// width: 48,
// height: 48,
// child: FutureBuilder<ImageProvider?>(
// future: _getPackageIcon(connection),
// builder: (_, snapshot) {
// if (!snapshot.hasData && snapshot.data == null) {
// return Container();
// } else {
// return Image(
// image: snapshot.data!,
// gaplessPlayback: true,
// width: 48,
// height: 48,
// );
// }
// },
// ),
// ),
// )
// : null,
// title: title,
// subtitle: subTitle,
// trailing: trailing,
// ),
// ),
// onTap: () {},
// );
},
popup: CommonPopupMenu(
minWidth: 160,
items: [
PopupMenuItemData(
label: "编辑规则",
onPressed: () {
// _handleShowEditExtendPage(context);
},
),
PopupMenuItemData(
label: "设置直连",
onPressed: () {},
),
PopupMenuItemData(
label: "一键屏蔽",
onPressed: () {},
),
],
),
);
}
}

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
@@ -20,6 +22,7 @@ class RequestsFragment extends ConsumerStatefulWidget {
class _RequestsFragmentState extends ConsumerState<RequestsFragment>
with PageMixin {
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
final _requestsStateNotifier =
ValueNotifier<ConnectionsState>(const ConnectionsState());
List<Connection> _requests = [];
@@ -28,8 +31,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite,
);
final FixedMap<String, double?> _cacheDynamicHeightMap = FixedMap(1000);
double _currentMaxWidth = 0;
@override
@@ -78,10 +79,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
}
double _calcCacheHeight(Connection item) {
final cacheHeight = _cacheDynamicHeightMap.get(item.id);
if (cacheHeight != null) {
return cacheHeight;
}
final size = globalState.measure.computeTextSize(
Text(
item.desc,
@@ -102,14 +99,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
final lines = (chainSize.height / baseHeight).round();
final computerHeight =
size.height + chainSize.height + 24 + 24 * (lines - 1);
_cacheDynamicHeightMap.put(item.id, computerHeight);
return computerHeight;
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_cacheDynamicHeightMap.clear();
_key.currentState?.clearCache();
}
}
@@ -118,7 +114,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
_requestsStateNotifier.dispose();
_scrollController.dispose();
_currentMaxWidth = 0;
_cacheDynamicHeightMap.clear();
super.dispose();
}
@@ -143,9 +138,19 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (_, constraints) {
return FindProcessBuilder(builder: (value) {
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
return ValueListenableBuilder<ConnectionsState>(
return Consumer(
builder: (_, ref, child) {
final value = ref.watch(
patchClashConfigProvider.select(
(state) =>
state.findProcessMode == FindProcessMode.always &&
Platform.isAndroid,
),
);
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
return child!;
},
child: ValueListenableBuilder<ConnectionsState>(
valueListenable: _requestsStateNotifier,
builder: (_, state, __) {
final connections = state.list;
@@ -159,7 +164,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
(connection) => ConnectionItem(
key: Key(connection.id),
connection: connection,
onClick: (value) {
onClickKeyword: (value) {
context.commonScaffoldState?.addKeyword(value);
},
),
@@ -179,12 +184,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
},
child: CommonScrollBar(
controller: _scrollController,
child: ListView.builder(
child: CacheItemExtentListView(
key: _key,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
controller: _scrollController,
itemExtentBuilder: (index, __) {
itemExtentBuilder: (index) {
final widget = items[index];
if (widget.runtimeType == Divider) {
return 0;
@@ -199,13 +205,21 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
return items[index];
},
itemCount: items.length,
keyBuilder: (int index) {
final widget = items[index];
if (widget.runtimeType == Divider) {
return "divider";
}
final connection = connections[(index / 2).floor()];
return connection.id;
},
),
),
),
);
},
);
});
),
);
},
);
}

View File

@@ -31,7 +31,7 @@ class IntranetIP extends StatelessWidget {
child: Consumer(
builder: (_, ref, __) {
final localIp = ref.watch(localIpProvider);
return FadeBox(
return FadeThroughBox(
child: localIp != null
? TooltipText(
text: Text(

View File

@@ -39,7 +39,7 @@ class _MemoryInfoState extends State<MemoryInfo> {
_memoryInfoStateNotifier.value = TrafficValue(
value: clashLib != null ? rss : await clashCore.getMemory() + rss,
);
timer = Timer(Duration(seconds: 5), () async {
timer = Timer(Duration(seconds: 2), () async {
_updateMemory();
});
});
@@ -47,13 +47,8 @@ class _MemoryInfoState extends State<MemoryInfo> {
@override
Widget build(BuildContext context) {
final darkenLighter = context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1)
.toLighter;
final darken = context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1);
return SizedBox(
height: getWidgetHeight(2),
height: getWidgetHeight(1),
child: CommonCard(
info: Info(
iconData: Icons.memory,
@@ -76,43 +71,94 @@ class _MemoryInfoState extends State<MemoryInfo> {
children: [
Text(
trafficValue.showValue,
style: context.textTheme.titleLarge?.toLight,
style:
context.textTheme.bodyMedium?.toLight.adjustSize(1),
),
SizedBox(
width: 8,
),
Text(
trafficValue.showUnit,
style: context.textTheme.titleLarge?.toLight,
style:
context.textTheme.bodyMedium?.toLight.adjustSize(1),
)
],
),
);
},
),
Flexible(
child: Stack(
children: [
Positioned.fill(
child: WaveView(
waveAmplitude: 12.0,
waveFrequency: 0.35,
waveColor: darkenLighter,
),
),
Positioned.fill(
child: WaveView(
waveAmplitude: 12.0,
waveFrequency: 0.9,
waveColor: darken,
),
),
],
),
),
],
),
),
);
}
}
// class AnimatedCounter extends StatefulWidget {
// final double value;
// final TextStyle? style;
//
// const AnimatedCounter({
// super.key,
// required this.value,
// this.style,
// });
//
// @override
// State<AnimatedCounter> createState() => _AnimatedCounterState();
// }
//
// class _AnimatedCounterState extends State<AnimatedCounter> {
// late double _previousValue;
// late double _currentValue;
//
// @override
// void initState() {
// super.initState();
// _previousValue = widget.value;
// _currentValue = widget.value;
// }
//
// @override
// void didUpdateWidget(AnimatedCounter oldWidget) {
// super.didUpdateWidget(oldWidget);
// if (oldWidget.value != widget.value) {
// // if (_previousValue == _currentValue) {
// // _previousValue = widget.value;
// // _currentValue = widget.value;
// // return;
// // }
// _currentValue = widget.value;
// }
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// @override
// Widget build(BuildContext context) {
// return Text(
// _currentValue.fixed(decimals: 1),
// style: widget.style,
// );
// return TweenAnimationBuilder(
// tween: Tween(
// begin: _previousValue,
// end: _currentValue,
// ),
// onEnd: () {
// _previousValue = _currentValue;
// },
// duration: Duration(seconds: 6),
// curve: Curves.easeOut,
// builder: (_, value, ___) {
// return Text(
// value.fixed(decimals: 1),
// style: widget.style,
// );
// },
// );
// }
// }

View File

@@ -12,7 +12,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
final _networkDetectionState = ValueNotifier<NetworkDetectionState>(
const NetworkDetectionState(
isTesting: true,
isTesting: false,
isLoading: true,
ipInfo: null,
),
);
@@ -28,7 +29,6 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
bool? _preIsStart;
Timer? _setTimeoutTimer;
CancelToken? cancelToken;
Completer? checkedCompleter;
@override
void initState() {
@@ -37,11 +37,14 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
_startCheck();
}
});
if (!_networkDetectionState.value.isTesting &&
_networkDetectionState.value.isLoading) {
_startCheck();
}
super.initState();
}
_startCheck() async {
await checkedCompleter?.future;
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
@@ -64,7 +67,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
}
_clearSetTimeoutTimer();
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: true,
isLoading: true,
ipInfo: null,
);
_preIsStart = isStart;
@@ -74,16 +77,16 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
}
cancelToken = CancelToken();
try {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: true,
);
final ipInfo = await request.checkIp(cancelToken: cancelToken);
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: false,
);
if (ipInfo != null) {
checkedCompleter = Completer();
checkedCompleter?.complete(
Future.delayed(
Duration(milliseconds: 3000),
),
);
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: false,
isLoading: false,
ipInfo: ipInfo,
);
return;
@@ -91,14 +94,14 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
_clearSetTimeoutTimer();
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: false,
isLoading: false,
ipInfo: null,
);
});
} catch (e) {
if (e.toString() == "cancelled") {
_networkDetectionState.value = _networkDetectionState.value.copyWith(
isTesting: true,
isLoading: true,
ipInfo: null,
);
}
@@ -136,7 +139,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
valueListenable: _networkDetectionState,
builder: (_, state, __) {
final ipInfo = state.ipInfo;
final isTesting = state.isTesting;
final isLoading = state.isLoading;
return CommonCard(
onPressed: () {},
child: Column(
@@ -218,7 +221,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
),
child: SizedBox(
height: globalState.measure.bodyMediumHeight + 2,
child: FadeBox(
child: FadeThroughBox(
child: ipInfo != null
? TooltipText(
text: Text(
@@ -229,8 +232,8 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
overflow: TextOverflow.ellipsis,
),
)
: FadeBox(
child: isTesting == false && ipInfo == null
: FadeThroughBox(
child: isLoading == false && ipInfo == null
? Text(
"timeout",
style: context.textTheme.bodyMedium

View File

@@ -41,7 +41,7 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
@override
Widget build(BuildContext context) {
final color = context.colorScheme.onSurfaceVariant.toLight;
final color = context.colorScheme.onSurfaceVariant.opacity80;
return SizedBox(
height: getWidgetHeight(2),
child: CommonCard(

View File

@@ -38,7 +38,7 @@ class OutboundMode extends StatelessWidget {
for (final item in Mode.values)
Flexible(
child: ListItem.radio(
prue: true,
dense: true,
horizontalTitleGap: 4,
padding: const EdgeInsets.only(
left: 12,

View File

@@ -16,13 +16,20 @@ class TUNButton extends StatelessWidget {
onPressed: () {
showSheet(
context: context,
body: generateListView(generateSection(
items: [
if (system.isDesktop) const TUNItem(),
const TunStackItem(),
],
)),
title: appLocalizations.tun,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: generateListView(
generateSection(
items: [
if (system.isDesktop) const TUNItem(),
const TunStackItem(),
],
),
),
title: appLocalizations.tun,
);
},
);
},
info: Info(
@@ -89,15 +96,20 @@ class SystemProxyButton extends StatelessWidget {
onPressed: () {
showSheet(
context: context,
body: generateListView(
generateSection(
items: [
SystemProxyItem(),
BypassDomainItem(),
],
),
),
title: appLocalizations.systemProxy,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: generateListView(
generateSection(
items: [
SystemProxyItem(),
BypassDomainItem(),
],
),
),
title: appLocalizations.systemProxy,
);
},
);
},
info: Info(

View File

@@ -51,10 +51,8 @@ class TrafficUsage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final primaryColor =
context.colorScheme.surfaceContainer.blendDarken(context, factor: 0.2);
final secondaryColor =
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3);
final primaryColor = globalState.theme.darken3PrimaryContainer;
final secondaryColor = globalState.theme.darken2SecondaryContainer;
return SizedBox(
height: getWidgetHeight(2),
child: CommonCard(

View File

@@ -4,6 +4,7 @@ import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -156,9 +157,28 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(IntlExt.actionMessage((widget.hotKeyAction.action.name))),
content: ValueListenableBuilder(
return CommonDialog(
title: IntlExt.actionMessage(widget.hotKeyAction.action.name),
actions: [
TextButton(
onPressed: () {
_handleRemove();
},
child: Text(appLocalizations.remove),
),
const SizedBox(
width: 8,
),
TextButton(
onPressed: () {
_handleConfirm();
},
child: Text(
appLocalizations.confirm,
),
),
],
child: ValueListenableBuilder(
valueListenable: hotKeyActionNotifier,
builder: (_, hotKeyAction, ___) {
final key = hotKeyAction.key;
@@ -191,25 +211,6 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
);
},
),
actions: [
TextButton(
onPressed: () {
_handleRemove();
},
child: Text(appLocalizations.remove),
),
const SizedBox(
width: 8,
),
TextButton(
onPressed: () {
_handleConfirm();
},
child: Text(
appLocalizations.confirm,
),
),
],
);
}
}

View File

@@ -22,8 +22,8 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
final _scrollController = ScrollController(
initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite,
);
final FixedMap<String, double?> _cacheDynamicHeightMap = FixedMap(1000);
double _currentMaxWidth = 0;
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
List<Log> _logs = [];
@@ -90,14 +90,13 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
void dispose() {
_logsStateNotifier.dispose();
_scrollController.dispose();
_cacheDynamicHeightMap.clear();
super.dispose();
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_cacheDynamicHeightMap.clear();
_key.currentState?.clearCache();
}
}
@@ -116,27 +115,19 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
);
}
double _calcCacheHeight(String text) {
final cacheHeight = _cacheDynamicHeightMap.get(text);
if (cacheHeight != null) {
return cacheHeight;
}
final size = globalState.measure.computeTextSize(
Text(
text,
style: globalState.appController.context.textTheme.bodyLarge,
),
maxWidth: _currentMaxWidth,
);
_cacheDynamicHeightMap.put(text, size.height);
return size.height;
}
double _getItemHeight(Log log) {
final measure = globalState.measure;
final bodySmallHeight = measure.bodySmallHeight;
final bodyMediumHeight = measure.bodyMediumHeight;
final height = _calcCacheHeight(log.payload ?? "");
final height = globalState.measure
.computeTextSize(
Text(
log.payload ?? "",
style: globalState.appController.context.textTheme.bodyLarge,
),
maxWidth: _currentMaxWidth,
)
.height;
return height + bodySmallHeight + 8 + bodyMediumHeight + 40;
}
@@ -196,7 +187,8 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
},
child: CommonScrollBar(
controller: _scrollController,
child: ListView.builder(
child: CacheItemExtentListView(
key: _key,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
@@ -204,7 +196,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
itemBuilder: (_, index) {
return items[index];
},
itemExtentBuilder: (index, __) {
itemExtentBuilder: (index) {
final item = items[index];
if (item.runtimeType == Divider) {
return 0;
@@ -213,6 +205,14 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
return _getItemHeight(log);
},
itemCount: items.length,
keyBuilder: (int index) {
final item = items[index];
if (item.runtimeType == Divider) {
return "divider";
}
final log = logs[(index / 2).floor()];
return log.payload ?? "";
},
),
),
);
@@ -272,11 +272,3 @@ class LogItem extends StatelessWidget {
);
}
}
class NoGlowScrollBehavior extends ScrollBehavior {
@override
Widget buildOverscrollIndicator(
BuildContext context, Widget child, ScrollableDetails details) {
return child; // 禁用过度滚动效果
}
}

View File

@@ -50,19 +50,19 @@ class AddProfile extends StatelessWidget {
return ListView(
children: [
ListItem(
leading: const Icon(Icons.qr_code),
leading: const Icon(Icons.qr_code_sharp),
title: Text(appLocalizations.qrcode),
subtitle: Text(appLocalizations.qrcodeDesc),
onTap: _toScan,
),
ListItem(
leading: const Icon(Icons.upload_file),
leading: const Icon(Icons.upload_file_sharp),
title: Text(appLocalizations.file),
subtitle: Text(appLocalizations.fileDesc),
onTap: _handleAddProfileFormFile,
),
ListItem(
leading: const Icon(Icons.cloud_download),
leading: const Icon(Icons.cloud_download_sharp),
title: Text(appLocalizations.url),
subtitle: Text(appLocalizations.urlDesc),
onTap: _toAdd,
@@ -90,9 +90,15 @@ class _URLFormDialogState extends State<URLFormDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.importFromURL),
content: SizedBox(
return CommonDialog(
title: appLocalizations.importFromURL,
actions: [
TextButton(
onPressed: _handleAddProfileFormURL,
child: Text(appLocalizations.submit),
)
],
child: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
@@ -109,12 +115,6 @@ class _URLFormDialogState extends State<URLFormDialog> {
],
),
),
actions: [
TextButton(
onPressed: _handleAddProfileFormURL,
child: Text(appLocalizations.submit),
)
],
);
}
}

View File

@@ -282,7 +282,7 @@ class _EditProfileState extends State<EditProfile> {
ValueListenableBuilder<FileInfo?>(
valueListenable: fileInfoNotifier,
builder: (_, fileInfo, __) {
return FadeBox(
return FadeThroughBox(
child: fileInfo == null
? Container()
: ListItem(
@@ -324,15 +324,13 @@ class _EditProfileState extends State<EditProfile> {
},
),
];
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, __) {
if (didPop) return;
return CommonPopScope(
onPop: () {
if (fileData == null) {
Navigator.of(context).pop();
return;
return true;
}
_handleBack();
return false;
},
child: FloatLayout(
floatingWidget: FloatWrapper(

View File

@@ -1,87 +0,0 @@
import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
class GenProfile extends StatefulWidget {
final String profileId;
const GenProfile({
super.key,
required this.profileId,
});
@override
State<GenProfile> createState() => _GenProfileState();
}
class _GenProfileState extends State<GenProfile> {
final _currentClashConfigNotifier = ValueNotifier<ClashConfigSnippet?>(null);
@override
void initState() {
super.initState();
_initCurrentClashConfig();
}
_initCurrentClashConfig() async {
final currentProfileId = globalState.config.currentProfileId;
if (currentProfileId == null) {
return;
}
_currentClashConfigNotifier.value =
await clashCore.getProfile(currentProfileId);
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
body: ValueListenableBuilder(
valueListenable: _currentClashConfigNotifier,
builder: (_, clashConfig, ___) {
if (clashConfig == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return Padding(
padding: EdgeInsets.all(16),
child: CustomScrollView(
slivers: [
SliverGrid.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100,
mainAxisExtent: 50,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
itemCount: clashConfig.proxyGroups.length,
itemBuilder: (BuildContext context, int index) {
return CommonCard(
onPressed: () {},
child: Text(
clashConfig.proxyGroups[index].name,
),
);
},
),
SliverList.builder(
itemBuilder: (BuildContext context, int index) {
final rule = clashConfig.rule[index];
return Text(
rule,
);
},
itemCount: clashConfig.rule.length,
)
],
),
);
},
),
title: "自定义",
);
}
}

View File

@@ -0,0 +1,957 @@
import 'dart:ui';
import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/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';
class OverrideProfile extends StatefulWidget {
final String profileId;
const OverrideProfile({
super.key,
required this.profileId,
});
@override
State<OverrideProfile> createState() => _OverrideProfileState();
}
class _OverrideProfileState extends State<OverrideProfile> {
final GlobalKey<CacheItemExtentListViewState> _ruleListKey = GlobalKey();
final _controller = ScrollController();
double _currentMaxWidth = 0;
_initState(WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(Duration(milliseconds: 300), () async {
final snippet = await clashCore.getProfile(widget.profileId);
final overrideData = ref.read(
getProfileOverrideDataProvider(widget.profileId),
);
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(
snippet: snippet,
overrideData: overrideData,
),
);
});
});
}
_handleSave(WidgetRef ref, OverrideData overrideData) {
ref.read(profilesProvider.notifier).updateProfile(
widget.profileId,
(state) => state.copyWith(
overrideData: overrideData,
),
);
}
_handleDelete(WidgetRef ref) async {
final res = await globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(text: appLocalizations.deleteRuleTip),
);
if (res != true) {
return;
}
final selectedRules = ref.read(
profileOverrideStateProvider.select(
(state) => state.selectedRules,
),
);
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) {
final overrideRule = state.overrideData!.rule.updateRules(
(rules) => List.from(
rules.where(
(item) => !selectedRules.contains(item.id),
),
),
);
return state.copyWith.overrideData!(
rule: overrideRule,
);
},
);
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(isEdit: false, selectedRules: {}),
);
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_ruleListKey.currentState?.clearCache();
}
}
_buildContent() {
return Consumer(
builder: (_, ref, child) {
final isInit = ref.watch(
profileOverrideStateProvider.select(
(state) => state.snippet != null && state.overrideData != null,
),
);
if (!isInit) {
return Center(
child: CircularProgressIndicator(),
);
}
return FadeBox(
child: !isInit
? Center(
child: CircularProgressIndicator(),
)
: child!,
);
},
child: LayoutBuilder(
builder: (_, constraints) {
_handleTryClearCache(constraints.maxWidth - 104);
return CommonAutoHiddenScrollBar(
controller: _controller,
child: CustomScrollView(
controller: _controller,
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 16,
),
),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: OverrideSwitch(),
),
),
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(
left: 8,
right: 8,
),
child: RuleTitle(
profileId: widget.profileId,
),
),
),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0),
sliver: RuleContent(
maxWidth: _currentMaxWidth,
ruleListKey: _ruleListKey,
),
),
SliverToBoxAdapter(
child: SizedBox(
height: 16,
),
),
],
),
);
},
),
);
}
@override
Widget build(BuildContext context) {
return ProviderScope(
overrides: [
profileOverrideStateProvider.overrideWith(() => ProfileOverrideState()),
],
child: Consumer(
builder: (_, ref, child) {
_initState(ref);
return child!;
},
child: Consumer(
builder: (_, ref, ___) {
final vm2 = ref.watch(
profileOverrideStateProvider.select(
(state) => VM2(
a: state.isEdit,
b: state.selectedRules.length,
),
),
);
final isEdit = vm2.a;
final editCount = vm2.b;
return CommonScaffold(
title: appLocalizations.override,
body: _buildContent(),
actions: [
if (!isEdit)
Consumer(
builder: (_, ref, child) {
final overrideData = ref.watch(
getProfileOverrideDataProvider(widget.profileId));
final newOverrideData = ref.watch(
profileOverrideStateProvider.select(
(state) => state.overrideData,
),
);
final equals = overrideData == newOverrideData;
if (equals || newOverrideData == null) {
return SizedBox();
}
return CommonPopScope(
onPop: () async {
if (equals) {
return true;
}
final res = await globalState.showMessage(
message: TextSpan(
text: appLocalizations.saveChanges,
),
confirmText: appLocalizations.save,
);
if (!context.mounted || res != true) {
return true;
}
_handleSave(ref, newOverrideData);
return true;
},
child: IconButton(
onPressed: () async {
final res = await globalState.showMessage(
message: TextSpan(
text: appLocalizations.saveTip,
),
confirmText: appLocalizations.tip,
);
if (res != true) {
return;
}
_handleSave(ref, newOverrideData);
},
icon: Icon(
Icons.save,
),
),
);
},
),
if (editCount == 1)
IconButton(
onPressed: () {
final rule = ref.read(profileOverrideStateProvider.select(
(state) {
return state.overrideData?.rule.rules.firstWhere(
(item) => item.id == state.selectedRules.first,
);
},
));
if (rule == null) {
return;
}
globalState.appController.handleAddOrUpdate(
ref,
rule,
);
},
icon: Icon(
Icons.edit,
),
),
if (editCount > 0)
IconButton(
onPressed: () {
_handleDelete(ref);
},
icon: Icon(
Icons.delete,
),
)
],
appBarEditState: AppBarEditState(
isEdit: isEdit,
editCount: editCount,
onExit: () {
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(
isEdit: false,
selectedRules: {},
),
);
},
),
);
},
),
),
);
}
}
class OverrideSwitch extends ConsumerWidget {
const OverrideSwitch({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final enable = ref.watch(
profileOverrideStateProvider.select(
(state) => state.overrideData?.enable,
),
);
return CommonCard(
onPressed: () {},
type: CommonCardType.filled,
radius: 18,
child: ListItem.switchItem(
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 4,
bottom: 4,
),
title: Text(appLocalizations.enableOverride),
delegate: SwitchDelegate(
value: enable ?? false,
onChanged: (value) {
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith.overrideData!(
enable: value,
),
);
},
),
),
);
}
}
class RuleTitle extends ConsumerWidget {
final String profileId;
const RuleTitle({
super.key,
required this.profileId,
});
_handleChangeType(WidgetRef ref, isOverrideRule) {
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith.overrideData!.rule(
type: isOverrideRule
? OverrideRuleType.added
: OverrideRuleType.override,
),
);
}
@override
Widget build(BuildContext context, ref) {
final vm3 = ref.watch(
profileOverrideStateProvider.select(
(state) {
final overrideRule = state.overrideData?.rule;
return VM3(
a: state.isEdit,
b: state.selectedRules.containsAll(
overrideRule?.rules.map((item) => item.id).toSet() ?? {},
),
c: overrideRule?.type == OverrideRuleType.override,
);
},
),
);
final isEdit = vm3.a;
final isSelectAll = vm3.b;
final isOverrideRule = vm3.c;
return FilledButtonTheme(
data: FilledButtonThemeData(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(
horizontal: 8,
)),
visualDensity: VisualDensity.compact,
),
),
child: IconButtonTheme(
data: IconButtonThemeData(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
visualDensity: VisualDensity.compact,
iconSize: WidgetStatePropertyAll(20),
),
),
child: ListHeader(
title: appLocalizations.rule,
subTitle: isOverrideRule
? appLocalizations.overrideOriginRules
: appLocalizations.addedOriginRules,
space: 8,
actions: [
if (!isEdit)
IconButton.filledTonal(
icon: Icon(
isOverrideRule ? Icons.edit_document : Icons.note_add,
),
onPressed: () {
_handleChangeType(
ref,
isOverrideRule,
);
},
),
!isEdit
? FilledButton.tonal(
onPressed: () {
globalState.appController.handleAddOrUpdate(ref);
},
child: Text(appLocalizations.add),
)
: isSelectAll
? FilledButton(
onPressed: () {
ref
.read(profileOverrideStateProvider.notifier)
.updateState(
(state) => state.copyWith(
selectedRules: {},
),
);
},
child: Text(appLocalizations.selectAll),
)
: FilledButton.tonal(
onPressed: () {
ref
.read(profileOverrideStateProvider.notifier)
.updateState(
(state) => state.copyWith(
selectedRules: state.overrideData?.rule.rules
.map((item) => item.id)
.toSet() ??
{},
),
);
},
child: Text(appLocalizations.selectAll),
),
],
),
),
);
}
}
class RuleContent extends ConsumerWidget {
final Key ruleListKey;
final double maxWidth;
const RuleContent({
super.key,
required this.ruleListKey,
required this.maxWidth,
});
Widget _proxyDecorator(
Widget child,
int index,
Animation<double> animation,
) {
return AnimatedBuilder(
animation: animation,
builder: (_, Widget? child) {
final double animValue = Curves.easeInOut.transform(animation.value);
final double scale = lerpDouble(1, 1.02, animValue)!;
return Transform.scale(
scale: scale,
child: child,
);
},
child: child,
);
}
Widget _buildItem(Rule rule, int index) {
return Consumer(
builder: (context, ref, ___) {
final vm2 = ref.watch(profileOverrideStateProvider.select(
(item) => VM2(
a: item.isEdit,
b: item.selectedRules.contains(rule.id),
),
));
final isEdit = vm2.a;
final isSelected = vm2.b;
return Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.symmetric(
vertical: 4,
),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isSelected
? context.colorScheme.secondaryContainer.opacity80
: context.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(18),
),
clipBehavior: Clip.hardEdge,
child: ListTile(
minTileHeight: 0,
minVerticalPadding: 0,
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
trailing: SizedBox(
width: 24,
height: 24,
child: !isEdit
? ReorderableDragStartListener(
index: index,
child: const Icon(Icons.drag_handle),
)
: CommonCheckBox(
value: isSelected,
isCircle: true,
onChanged: (_) {
_handleSelect(ref, rule);
},
),
),
title: Text(rule.value),
),
),
);
},
);
}
_handleSelect(WidgetRef ref, ruleId) {
if (!ref.read(profileOverrideStateProvider).isEdit) {
return;
}
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) {
final newSelectedRules = Set<String>.from(state.selectedRules);
if (newSelectedRules.contains(ruleId)) {
newSelectedRules.remove(ruleId);
} else {
newSelectedRules.add(ruleId);
}
return state.copyWith(
selectedRules: newSelectedRules,
);
},
);
}
@override
Widget build(BuildContext context, ref) {
final vm2 = ref.watch(
profileOverrideStateProvider.select(
(state) {
final overrideRule = state.overrideData?.rule;
return VM2(
a: overrideRule?.rules ?? [],
b: overrideRule?.type ?? OverrideRuleType.added,
);
},
),
);
final rules = vm2.a;
final type = vm2.b;
if (rules.isEmpty) {
return SliverToBoxAdapter(
child: SizedBox(
height: 300,
child: Center(
child: type == OverrideRuleType.added
? Text(
appLocalizations.noData,
)
: FilledButton(
onPressed: () {
final rules = ref.read(
profileOverrideStateProvider.select(
(state) => state.snippet?.rule ?? [],
),
);
ref
.read(profileOverrideStateProvider.notifier)
.updateState(
(state) {
return state.copyWith.overrideData!.rule(
overrideRules: rules,
);
},
);
},
child: Text(appLocalizations.getOriginRules),
),
),
),
);
}
return CacheItemExtentSliverReorderableList(
key: ruleListKey,
itemBuilder: (context, index) {
final rule = rules[index];
return GestureDetector(
key: ObjectKey(rule),
child: _buildItem(
rule,
index,
),
onTap: () {
_handleSelect(ref, rule.id);
},
onLongPress: () {
if (ref.read(profileOverrideStateProvider).isEdit) {
return;
}
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(
isEdit: true,
selectedRules: {
rule.id,
},
),
);
},
);
},
proxyDecorator: _proxyDecorator,
itemCount: rules.length,
onReorder: (oldIndex, newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final newRules = List<Rule>.from(rules);
final item = newRules.removeAt(oldIndex);
newRules.insert(newIndex, item);
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith.overrideData!(
rule: state.overrideData!.rule.updateRules((_) => newRules),
),
);
},
keyBuilder: (int index) {
return rules[index].value;
},
itemExtentBuilder: (index) {
final rule = rules[index];
return 40 +
globalState.measure
.computeTextSize(
Text(
rule.value,
style: context.textTheme.bodyMedium?.toJetBrainsMono,
),
maxWidth: maxWidth,
)
.height;
},
);
}
}
class AddRuleDialog extends StatefulWidget {
final ClashConfigSnippet snippet;
final Rule? rule;
const AddRuleDialog({
super.key,
required this.snippet,
this.rule,
});
@override
State<AddRuleDialog> createState() => _AddRuleDialogState();
}
class _AddRuleDialogState extends State<AddRuleDialog> {
late RuleAction _ruleAction;
final _ruleTargetController = TextEditingController();
final _contentController = TextEditingController();
final _ruleProviderController = TextEditingController();
final _subRuleController = TextEditingController();
bool _noResolve = false;
bool _src = false;
List<DropdownMenuEntry> _targetItems = [];
List<DropdownMenuEntry> _ruleProviderItems = [];
List<DropdownMenuEntry> _subRuleItems = [];
final _formKey = GlobalKey<FormState>();
@override
void initState() {
_initState();
super.initState();
}
_initState() {
_targetItems = [
...widget.snippet.proxyGroups.map(
(item) => DropdownMenuEntry(
value: item.name,
label: item.name,
),
),
...RuleTarget.values.map(
(item) => DropdownMenuEntry(
value: item.name,
label: item.name,
),
),
];
_ruleProviderItems = [
...widget.snippet.ruleProvider.map(
(item) => DropdownMenuEntry(
value: item.name,
label: item.name,
),
),
];
_subRuleItems = [
...widget.snippet.subRules.map(
(item) => DropdownMenuEntry(
value: item.name,
label: item.name,
),
),
];
if (widget.rule != null) {
final parsedRule = ParsedRule.parseString(widget.rule!.value);
_ruleAction = parsedRule.ruleAction;
_contentController.text = parsedRule.content ?? "";
_ruleTargetController.text = parsedRule.ruleTarget ?? "";
_ruleProviderController.text = parsedRule.ruleProvider ?? "";
_subRuleController.text = parsedRule.subRule ?? "";
_noResolve = parsedRule.noResolve;
_src = parsedRule.src;
return;
}
_ruleAction = RuleAction.values.first;
if (_targetItems.isNotEmpty) {
_ruleTargetController.text = _targetItems.first.value;
}
if (_ruleProviderItems.isNotEmpty) {
_ruleProviderController.text = _ruleProviderItems.first.value;
}
if (_subRuleItems.isNotEmpty) {
_subRuleController.text = _subRuleItems.first.value;
}
}
@override
void didUpdateWidget(AddRuleDialog oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.rule != widget.rule) {
_initState();
}
}
_handleSubmit() {
final res = _formKey.currentState?.validate();
if (res == false) {
return;
}
final parsedRule = ParsedRule(
ruleAction: _ruleAction,
content: _contentController.text,
ruleProvider: _ruleProviderController.text,
ruleTarget: _ruleTargetController.text,
subRule: _subRuleController.text,
noResolve: _noResolve,
src: _src,
);
final rule = widget.rule != null
? widget.rule!.copyWith(value: parsedRule.value)
: Rule.value(
parsedRule.value,
);
Navigator.of(context).pop(rule);
}
@override
Widget build(BuildContext context) {
return CommonDialog(
title: appLocalizations.addRule,
actions: [
TextButton(
onPressed: _handleSubmit,
child: Text(
appLocalizations.confirm,
),
),
],
child: DropdownMenuTheme(
data: DropdownMenuThemeData(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(),
labelStyle: context.textTheme.bodyLarge
?.copyWith(overflow: TextOverflow.ellipsis),
),
),
child: Form(
key: _formKey,
child: LayoutBuilder(
builder: (_, constraints) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FilledButton.tonal(
onPressed: () async {
_ruleAction =
await globalState.showCommonDialog<RuleAction>(
child: OptionsDialog<RuleAction>(
title: appLocalizations.ruleName,
options: RuleAction.values,
textBuilder: (item) => item.value,
value: _ruleAction,
),
) ??
_ruleAction;
setState(() {});
},
child: Text(_ruleAction.name),
),
SizedBox(
height: 24,
),
_ruleAction == RuleAction.RULE_SET
? FormField(
validator: (_) {
if (_ruleProviderController.text.isEmpty) {
return appLocalizations.ruleProviderEmptyTip;
}
return null;
},
builder: (field) {
return DropdownMenu(
expandedInsets: EdgeInsets.zero,
controller: _ruleProviderController,
label: Text(appLocalizations.ruleProviders),
menuHeight: 250,
errorText: field.errorText,
dropdownMenuEntries: _ruleProviderItems,
);
},
)
: TextFormField(
controller: _contentController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: appLocalizations.content,
),
validator: (_) {
if (_contentController.text.isEmpty) {
return appLocalizations.contentEmptyTip;
}
return null;
},
),
SizedBox(
height: 24,
),
_ruleAction == RuleAction.SUB_RULE
? FormField(
validator: (_) {
if (_subRuleController.text.isEmpty) {
return appLocalizations.subRuleEmptyTip;
}
return null;
},
builder: (filed) {
return DropdownMenu(
width: 200,
controller: _subRuleController,
label: Text(appLocalizations.subRule),
menuHeight: 250,
dropdownMenuEntries: _subRuleItems,
);
},
)
: FormField<String>(
validator: (_) {
if (_ruleTargetController.text.isEmpty) {
return appLocalizations.ruleTargetEmptyTip;
}
return null;
},
builder: (filed) {
return DropdownMenu(
controller: _ruleTargetController,
initialSelection: filed.value,
label: Text(appLocalizations.ruleTarget),
width: 200,
menuHeight: 250,
enableFilter: true,
dropdownMenuEntries: _targetItems,
errorText: filed.errorText,
);
},
),
if (_ruleAction.hasParams) ...[
SizedBox(
height: 20,
),
Wrap(
spacing: 8,
children: [
CommonCard(
radius: 8,
isSelected: _src,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
child: Text(
appLocalizations.sourceIp,
style: context.textTheme.bodyMedium,
),
),
onPressed: () {
setState(() {
_src = !_src;
});
},
),
CommonCard(
radius: 8,
isSelected: _noResolve,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
child: Text(
appLocalizations.noResolve,
style: context.textTheme.bodyMedium,
),
),
onPressed: () {
setState(() {
_noResolve = !_noResolve;
});
},
)
],
),
],
SizedBox(
height: 20,
),
],
);
},
),
),
),
);
}
}

View File

@@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
import 'package:fl_clash/fragments/profiles/override_profile.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
@@ -11,7 +12,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'add_profile.dart';
import 'gen_profile.dart';
class ProfilesFragment extends StatefulWidget {
const ProfilesFragment({super.key});
@@ -24,12 +24,17 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
Function? applyConfigDebounce;
_handleShowAddExtendPage() {
showExtendPage(
showExtend(
globalState.navigatorKey.currentState!.context,
body: AddProfile(
context: globalState.navigatorKey.currentState!.context,
),
title: "${appLocalizations.add}${appLocalizations.profile}",
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: AddProfile(
context: globalState.navigatorKey.currentState!.context,
),
title: "${appLocalizations.add}${appLocalizations.profile}",
);
},
);
}
@@ -81,12 +86,13 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
onPressed: () {
final profiles = globalState.config.profiles;
showSheet(
title: appLocalizations.profilesSort,
context: context,
body: SizedBox(
height: 400,
child: ReorderableProfiles(profiles: profiles),
),
builder: (_, type) {
return ReorderableProfilesSheet(
type: type,
profiles: profiles,
);
},
);
},
icon: const Icon(Icons.sort),
@@ -205,13 +211,18 @@ class ProfileItem extends StatelessWidget {
}
_handleShowEditExtendPage(BuildContext context) {
showExtendPage(
showExtend(
context,
body: EditProfile(
profile: profile,
context: context,
),
title: "${appLocalizations.edit}${appLocalizations.profile}",
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: EditProfile(
profile: profile,
context: context,
),
title: "${appLocalizations.edit}${appLocalizations.profile}",
);
},
);
}
@@ -277,7 +288,7 @@ class ProfileItem extends StatelessWidget {
_handlePushGenProfilePage(BuildContext context, String id) {
BaseNavigator.push(
context,
GenProfile(
OverrideProfile(
profileId: id,
),
);
@@ -285,7 +296,6 @@ class ProfileItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final key = GlobalKey<CommonPopupBoxState>();
return CommonCard(
isSelected: profile.id == groupValue,
onPressed: () {
@@ -298,17 +308,16 @@ class ProfileItem extends StatelessWidget {
trailing: SizedBox(
height: 40,
width: 40,
child: FadeBox(
child: FadeThroughBox(
child: profile.isUpdating
? const Padding(
padding: EdgeInsets.all(8),
child: CircularProgressIndicator(),
)
: CommonPopupBox(
key: key,
popup: CommonPopupMenu(
items: [
ActionItemData(
PopupMenuItemData(
icon: Icons.edit_outlined,
label: appLocalizations.edit,
onPressed: () {
@@ -316,52 +325,47 @@ class ProfileItem extends StatelessWidget {
},
),
if (profile.type == ProfileType.url) ...[
ActionItemData(
PopupMenuItemData(
icon: Icons.sync_alt_sharp,
label: appLocalizations.sync,
onPressed: () {
updateProfile();
},
),
// ActionItemData(
// icon: Icons.copy,
// label: appLocalizations.copyLink,
// onPressed: () {
// _handleCopyLink(context);
// },
// ),
],
// ActionItemData(
// icon: Icons.extension_outlined,
// label: "自定义",
// onPressed: () {
// _handlePushGenProfilePage(context, profile.id);
// },
// ),
ActionItemData(
PopupMenuItemData(
icon: Icons.extension_outlined,
label: appLocalizations.override,
onPressed: () {
_handlePushGenProfilePage(context, profile.id);
},
),
PopupMenuItemData(
icon: Icons.file_copy_outlined,
label: appLocalizations.exportFile,
onPressed: () {
_handleExportFile(context);
},
),
ActionItemData(
PopupMenuItemData(
icon: Icons.delete_outlined,
iconSize: 20,
label: appLocalizations.delete,
onPressed: () {
_handleDeleteProfile(context);
},
type: ActionType.danger,
type: PopupMenuItemType.danger,
),
],
),
target: IconButton(
onPressed: () {
key.currentState?.pop();
},
icon: Icon(Icons.more_vert),
),
targetBuilder: (open) {
return IconButton(
onPressed: () {
open();
},
icon: Icon(Icons.more_vert),
);
},
),
),
),
@@ -397,19 +401,22 @@ class ProfileItem extends StatelessWidget {
}
}
class ReorderableProfiles extends StatefulWidget {
class ReorderableProfilesSheet extends StatefulWidget {
final List<Profile> profiles;
final SheetType type;
const ReorderableProfiles({
const ReorderableProfilesSheet({
super.key,
required this.profiles,
required this.type,
});
@override
State<ReorderableProfiles> createState() => _ReorderableProfilesState();
State<ReorderableProfilesSheet> createState() =>
_ReorderableProfilesSheetState();
}
class _ReorderableProfilesState extends State<ReorderableProfiles> {
class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
late List<Profile> profiles;
@override
@@ -453,74 +460,61 @@ class _ReorderableProfilesState extends State<ReorderableProfiles> {
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: ReorderableListView.builder(
buildDefaultDragHandles: false,
padding: const EdgeInsets.symmetric(horizontal: 12),
proxyDecorator: proxyDecorator,
onReorder: (oldIndex, newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final profile = profiles.removeAt(oldIndex);
profiles.insert(newIndex, profile);
});
},
itemBuilder: (_, index) {
final profile = profiles[index];
return Container(
key: Key(profile.id),
padding: const EdgeInsets.symmetric(vertical: 4),
child: CommonCard(
type: CommonCardType.filled,
child: ListTile(
contentPadding: const EdgeInsets.only(
right: 16,
left: 16,
),
title: Text(profile.label ?? profile.id),
trailing: ReorderableDragStartListener(
index: index,
child: const Icon(Icons.drag_handle),
),
return AdaptiveSheetScaffold(
type: widget.type,
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pop();
globalState.appController.setProfiles(profiles);
},
icon: Icon(
Icons.save,
),
)
],
body: Padding(
padding: EdgeInsets.only(bottom: 32),
child: ReorderableListView.builder(
buildDefaultDragHandles: false,
padding: const EdgeInsets.symmetric(
horizontal: 12,
),
proxyDecorator: proxyDecorator,
onReorder: (oldIndex, newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final profile = profiles.removeAt(oldIndex);
profiles.insert(newIndex, profile);
});
},
itemBuilder: (_, index) {
final profile = profiles[index];
return Container(
key: Key(profile.id),
padding: const EdgeInsets.symmetric(vertical: 4),
child: CommonCard(
type: CommonCardType.filled,
child: ListTile(
contentPadding: const EdgeInsets.only(
right: 16,
left: 16,
),
title: Text(profile.label ?? profile.id),
trailing: ReorderableDragStartListener(
index: index,
child: const Icon(Icons.drag_handle),
),
),
);
},
itemCount: profiles.length,
),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 24,
),
child: FilledButton.tonal(
onPressed: () {
Navigator.of(context).pop();
globalState.appController.setProfiles(profiles);
},
style: ButtonStyle(
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(vertical: 8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
appLocalizations.confirm,
),
],
),
),
);
},
itemCount: profiles.length,
),
],
),
title: appLocalizations.profilesSort,
);
}
}

View File

@@ -178,7 +178,7 @@ class ProxyCard extends StatelessWidget {
style: context.textTheme.bodySmall?.copyWith(
overflow: TextOverflow.ellipsis,
color:
context.textTheme.bodySmall?.color?.toLight,
context.textTheme.bodySmall?.color?.opacity80,
),
),
),
@@ -221,7 +221,7 @@ class _ProxyDesc extends ConsumerWidget {
desc,
overflow: TextOverflow.ellipsis,
style: context.textTheme.bodySmall?.copyWith(
color: context.textTheme.bodySmall?.color?.toLight,
color: context.textTheme.bodySmall?.color?.opacity80,
),
);
}

View File

@@ -467,10 +467,6 @@ class _ListHeaderState extends State<ListHeader>
return CommonCard(
enterAnimated: widget.enterAnimated,
key: widget.key,
borderSide: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(
context.colorScheme.surfaceContainer,
),
radius: 14,
type: CommonCardType.filled,
child: Padding(
@@ -556,6 +552,7 @@ class _ListHeaderState extends State<ListHeader>
children: [
if (isExpand) ...[
IconButton(
visualDensity: VisualDensity.standard,
onPressed: () {
widget.onScrollToSelected(groupName);
},
@@ -565,12 +562,13 @@ class _ListHeaderState extends State<ListHeader>
),
IconButton(
onPressed: _delayTest,
visualDensity: VisualDensity.standard,
icon: const Icon(
Icons.network_ping,
),
),
const SizedBox(
width: 4,
width: 6,
),
],
AnimatedBuilder(

View File

@@ -13,8 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
typedef UpdatingMap = Map<String, bool>;
class ProvidersView extends ConsumerStatefulWidget {
final SheetType type;
const ProvidersView({
super.key,
required this.type,
});
@override
@@ -22,25 +25,6 @@ class ProvidersView extends ConsumerStatefulWidget {
}
class _ProvidersViewState extends ConsumerState<ProvidersView> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(
(_) {
globalState.appController.updateProviders();
context.commonScaffoldState?.actions = [
IconButton(
onPressed: () {
_updateProviders();
},
icon: const Icon(
Icons.sync,
),
)
];
},
);
}
_updateProviders() async {
final providers = ref.read(providersProvider);
@@ -102,10 +86,24 @@ class _ProvidersViewState extends ConsumerState<ProvidersView> {
title: appLocalizations.ruleProviders,
items: ruleProviders,
);
return generateListView([
...proxySection,
...ruleSection,
]);
return AdaptiveSheetScaffold(
actions: [
IconButton(
onPressed: () {
_updateProviders();
},
icon: const Icon(
Icons.sync,
),
)
],
type: widget.type,
body: generateListView([
...proxySection,
...ruleSection,
]),
title: appLocalizations.providers,
);
}
}
@@ -222,7 +220,7 @@ class ProviderItem extends StatelessWidget {
trailing: SizedBox(
height: 48,
width: 48,
child: FadeBox(
child: FadeThroughBox(
child: provider.isUpdating
? const Padding(
padding: EdgeInsets.all(8),

View File

@@ -28,12 +28,13 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
if (_hasProviders)
IconButton(
onPressed: () {
showExtendPage(
isScaffold: true,
extendPageWidth: 360,
showExtend(
context,
body: const ProvidersView(),
title: appLocalizations.providers,
builder: (_, type) {
return ProvidersView(
type: type,
);
},
);
},
icon: const Icon(
@@ -51,11 +52,15 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
)
: IconButton(
onPressed: () {
showExtendPage(
showExtend(
context,
extendPageWidth: 360,
title: appLocalizations.iconConfiguration,
body: _IconConfigView(),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const _IconConfigView(),
title: appLocalizations.iconConfiguration,
);
},
);
},
icon: const Icon(
@@ -65,9 +70,17 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
IconButton(
onPressed: () {
showSheet(
title: appLocalizations.proxiesSetting,
context: context,
body: const ProxiesSetting(),
props: SheetProps(
isScrollControlled: true,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: const ProxiesSetting(),
title: appLocalizations.proxiesSetting,
);
},
);
},
icon: const Icon(
@@ -128,13 +141,11 @@ class _IconConfigView extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final iconMap =
ref.watch(proxiesStyleSettingProvider.select((state) => state.iconMap));
final entries = iconMap.entries.toList();
return ListPage(
return MapInputPage(
title: appLocalizations.iconConfiguration,
items: entries,
map: iconMap,
keyLabel: appLocalizations.regExp,
valueLabel: appLocalizations.icon,
keyBuilder: (item) => Key(item.key),
titleBuilder: (item) => Text(item.key),
leadingBuilder: (item) => Container(
decoration: BoxDecoration(
@@ -151,10 +162,10 @@ class _IconConfigView extends ConsumerWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
onChange: (entries) {
onChange: (value) {
ref.read(proxiesStyleSettingProvider.notifier).updateState(
(state) => state.copyWith(
iconMap: Map.fromEntries(entries),
iconMap: value,
),
);
},

View File

@@ -248,8 +248,8 @@ class ProxiesSetting extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 32),
return SingleChildScrollView(
padding: EdgeInsets.only(bottom: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -268,6 +268,7 @@ class ProxiesSetting extends StatelessWidget {
return Container();
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
..._buildGroupStyleSetting(),

View File

@@ -49,7 +49,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
_buildMoreButton() {
return Consumer(
builder: (_, ref, ___) {
final isMobileView = ref.watch(viewWidthProvider.notifier).isMobileView;
final isMobileView = ref.watch(isMobileViewProvider);
return IconButton(
onPressed: _showMoreMenu,
icon: isMobileView
@@ -67,42 +67,48 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
_showMoreMenu() {
showSheet(
context: context,
width: 380,
isScrollControlled: false,
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Consumer(
builder: (_, ref, __) {
final state = ref.watch(proxiesSelectorStateProvider);
return SizedBox(
width: double.infinity,
child: Wrap(
alignment: WrapAlignment.center,
runSpacing: 8,
spacing: 8,
children: [
for (final groupName in state.groupNames)
SettingTextCard(
groupName,
onPressed: () {
final index = state.groupNames.indexWhere(
(item) => item == groupName,
);
if (index == -1) return;
_tabController?.animateTo(index);
globalState.appController
.updateCurrentGroupName(groupName);
Navigator.of(context).pop();
},
isSelected: groupName == state.currentGroupName,
)
],
),
);
},
),
props: SheetProps(
isScrollControlled: false,
),
title: appLocalizations.proxyGroup,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Consumer(
builder: (_, ref, __) {
final state = ref.watch(proxiesSelectorStateProvider);
return SizedBox(
width: double.infinity,
child: Wrap(
alignment: WrapAlignment.center,
runSpacing: 8,
spacing: 8,
children: [
for (final groupName in state.groupNames)
SettingTextCard(
groupName,
onPressed: () {
final index = state.groupNames.indexWhere(
(item) => item == groupName,
);
if (index == -1) return;
_tabController?.animateTo(index);
globalState.appController
.updateCurrentGroupName(groupName);
Navigator.of(context).pop();
},
isSelected: groupName == state.currentGroupName,
)
],
),
);
},
),
),
title: appLocalizations.proxyGroup,
);
},
);
}
@@ -238,7 +244,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
context.colorScheme.surface.withOpacity(0.1),
context.colorScheme.surface.opacity10,
context.colorScheme.surface,
],
stops: const [
@@ -319,32 +325,35 @@ class ProxyGroupViewState extends ConsumerState<ProxyGroupView> {
);
return Align(
alignment: Alignment.topCenter,
child: GridView.builder(
child: CommonAutoHiddenScrollBar(
controller: _controller,
padding: const EdgeInsets.only(
top: 16,
left: 16,
right: 16,
bottom: 96,
child: GridView.builder(
controller: _controller,
padding: const EdgeInsets.only(
top: 16,
left: 16,
right: 16,
bottom: 96,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: getItemHeight(proxyCardType),
),
itemCount: sortedProxies.length,
itemBuilder: (_, index) {
final proxy = sortedProxies[index];
return ProxyCard(
testUrl: state.testUrl,
groupType: state.groupType,
type: proxyCardType,
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: getItemHeight(proxyCardType),
),
itemCount: sortedProxies.length,
itemBuilder: (_, index) {
final proxy = sortedProxies[index];
return ProxyCard(
testUrl: state.testUrl,
groupType: state.groupType,
type: proxyCardType,
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
),
);
}

View File

@@ -149,7 +149,7 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
builder: (_, snapshot) {
return SizedBox(
height: 24,
child: FadeBox(
child: FadeThroughBox(
key: Key("fade_box_${geoItem.label}"),
child: snapshot.data == null
? const SizedBox(
@@ -248,7 +248,7 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
child: ValueListenableBuilder(
valueListenable: isUpdating,
builder: (_, isUpdating, ___) {
return FadeBox(
return FadeThroughBox(
child: isUpdating
? const Padding(
padding: EdgeInsets.all(8),
@@ -299,24 +299,8 @@ class _UpdateGeoUrlFormDialogState extends State<UpdateGeoUrlFormDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.title),
content: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
children: [
TextField(
maxLines: 5,
minLines: 1,
controller: urlController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
],
),
),
return CommonDialog(
title: widget.title,
actions: [
if (widget.defaultValue != null &&
urlController.value.text != widget.defaultValue) ...[
@@ -333,6 +317,19 @@ class _UpdateGeoUrlFormDialogState extends State<UpdateGeoUrlFormDialog> {
child: Text(appLocalizations.submit),
)
],
child: Wrap(
runSpacing: 16,
children: [
TextField(
maxLines: 5,
minLines: 1,
controller: urlController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
],
),
);
}
}

View File

@@ -291,12 +291,12 @@ class _PrimaryColorItem extends ConsumerWidget {
itemBuilder: (_, index) {
final color = primaryColors[index];
return ColorSchemeBox(
isSelected: color?.value == primaryColor,
isSelected: color?.toARGB32() == primaryColor,
primaryColor: color,
onPressed: () {
ref.read(themeSettingProvider.notifier).updateState(
(state) => state.copyWith(
primaryColor: color?.value,
primaryColor: color?.toARGB32(),
),
);
},

View File

@@ -35,7 +35,6 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
delegate: OpenDelegate(
title: Intl.message(navigationItem.label.name),
widget: navigationItem.fragment,
extendPageWidth: 360,
),
);
}
@@ -65,7 +64,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
);
}
List<Widget> _getSettingList() {
_getSettingList() {
return generateSection(
title: appLocalizations.settings,
items: [
@@ -75,7 +74,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
if (system.isDesktop) _HotkeyItem(),
if (Platform.isWindows) _LoopbackItem(),
if (Platform.isAndroid) _AccessItem(),
_OverrideItem(),
_ConfigItem(),
_SettingItem(),
],
);
@@ -155,7 +154,6 @@ class _ThemeItem extends StatelessWidget {
delegate: OpenDelegate(
title: appLocalizations.theme,
widget: const ThemeFragment(),
extendPageWidth: 360,
),
);
}
@@ -231,15 +229,15 @@ class _AccessItem extends StatelessWidget {
}
}
class _OverrideItem extends StatelessWidget {
const _OverrideItem();
class _ConfigItem extends StatelessWidget {
const _ConfigItem();
@override
Widget build(BuildContext context) {
return ListItem.open(
leading: const Icon(Icons.edit),
title: Text(appLocalizations.override),
subtitle: Text(appLocalizations.overrideDesc),
title: Text(appLocalizations.basicConfig),
subtitle: Text(appLocalizations.basicConfigDesc),
delegate: OpenDelegate(
title: appLocalizations.override,
widget: const ConfigFragment(),

View File

@@ -123,7 +123,6 @@
"project": "Project",
"core": "Core",
"tabAnimation": "Tab animation",
"tabAnimationDesc": "When enabled, the home tab will add a toggle animation",
"desc": "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
"startVpn": "Starting VPN...",
"stopVpn": "Stopping VPN...",
@@ -181,7 +180,6 @@
"requests": "Requests",
"requestsDesc": "View recently request records",
"findProcessMode": "Find process",
"findProcessModeDesc": "There is a risk of flashback after opening",
"init": "Init",
"infiniteTime": "Long term effective",
"expirationTime": "Expiration time",
@@ -249,7 +247,6 @@
"stop": "Stop",
"appDesc": "Processing app related settings",
"vpnDesc": "Modify VPN related settings",
"generalDesc": "Overwrite general settings",
"dnsDesc": "Update DNS related settings",
"key": "Key",
"value": "Value",
@@ -346,5 +343,34 @@
"exportFile": "Export file",
"cacheCorrupt": "The cache is corrupt. Do you want to clear it?",
"detectionTip": "Relying on third-party api is for reference only",
"listen": "Listen"
"listen": "Listen",
"keyExists": "The current key already exists",
"valueExists": "The current value already exists",
"undo": "undo",
"redo": "redo",
"none": "none",
"basicConfig": "Basic configuration",
"basicConfigDesc": "Modify the basic configuration globally",
"selectedCountTitle": "{count} items have been selected",
"addRule": "Add rule",
"ruleProviderEmptyTip": "Rule provider cannot be empty",
"ruleName": "Rule name",
"content": "Content",
"contentEmptyTip": "Content cannot be empty",
"subRule": "Sub rule",
"subRuleEmptyTip": "Sub rule content cannot be empty",
"ruleTarget": "Rule target",
"ruleTargetEmptyTip": "Rule target cannot be empty",
"sourceIp": "Source IP",
"noResolve": "No resolve IP",
"getOriginRules": "Get original rules",
"overrideOriginRules": "Override the original rule",
"addedOriginRules": "Attach on the original rules",
"enableOverride": "Enable override",
"deleteRuleTip": "Are you sure you want to delete the selected rule?",
"saveChanges": "Do you want to save the changes?",
"generalDesc": "Modify general settings",
"findProcessModeDesc": "There is a certain performance loss after opening",
"tabAnimationDesc": "Effective only in mobile view",
"saveTip": "Are you sure you want to save?"
}

View File

@@ -123,7 +123,6 @@
"project": "プロジェクト",
"core": "コア",
"tabAnimation": "タブアニメーション",
"tabAnimationDesc": "有効化するとホームタブに切り替えアニメーションを追加",
"desc": "ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
"startVpn": "VPNを開始中...",
"stopVpn": "VPNを停止中...",
@@ -181,7 +180,6 @@
"requests": "リクエスト",
"requestsDesc": "最近のリクエスト記録を表示",
"findProcessMode": "プロセス検出",
"findProcessModeDesc": "有効化するとフラッシュバックのリスクあり",
"init": "初期化",
"infiniteTime": "長期有効",
"expirationTime": "有効期限",
@@ -249,7 +247,6 @@
"stop": "停止",
"appDesc": "アプリ関連設定の処理",
"vpnDesc": "VPN関連設定の変更",
"generalDesc": "一般設定の上書き",
"dnsDesc": "DNS関連設定の更新",
"key": "キー",
"value": "値",
@@ -346,5 +343,34 @@
"exportFile": "ファイルをエクスポート",
"cacheCorrupt": "キャッシュが破損しています。クリアしますか?",
"detectionTip": "サードパーティAPIに依存参考値",
"listen": "リスン"
"listen": "リスン",
"keyExists": "現在のキーは既に存在します",
"valueExists": "現在の値は既に存在します",
"undo": "元に戻す",
"redo": "やり直す",
"none": "なし",
"basicConfig": "基本設定",
"basicConfigDesc": "基本設定をグローバルに変更",
"selectedCountTitle": "{count} 項目が選択されています",
"addRule": "ルールを追加",
"ruleProviderEmptyTip": "ルールプロバイダーは必須です",
"ruleName": "ルール名",
"content": "内容",
"contentEmptyTip": "内容は必須です",
"subRule": "サブルール",
"subRuleEmptyTip": "サブルールの内容は必須です",
"ruleTarget": "ルール対象",
"ruleTargetEmptyTip": "ルール対象は必須です",
"sourceIp": "送信元IP",
"noResolve": "IPを解決しない",
"getOriginRules": "元のルールを取得",
"overrideOriginRules": "元のルールを上書き",
"addedOriginRules": "元のルールに追加",
"enableOverride": "上書きを有効化",
"deleteRuleTip": "選択したルールを削除しますか?",
"saveChanges": "変更を保存しますか?",
"generalDesc": "一般設定を変更",
"findProcessModeDesc": "有効化するとパフォーマンスが若干低下します",
"tabAnimationDesc": "モバイル表示でのみ有効",
"saveTip": "保存してもよろしいですか?"
}

View File

@@ -123,7 +123,6 @@
"project": "Проект",
"core": "Ядро",
"tabAnimation": "Анимация вкладок",
"tabAnimationDesc": "При включении домашняя вкладка добавит анимацию переключения",
"desc": "Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
"startVpn": "Запуск VPN...",
"stopVpn": "Остановка VPN...",
@@ -181,7 +180,6 @@
"requests": "Запросы",
"requestsDesc": "Просмотр последних записей запросов",
"findProcessMode": "Режим поиска процесса",
"findProcessModeDesc": "Есть риск сбоя после включения",
"init": "Инициализация",
"infiniteTime": "Долгосрочное действие",
"expirationTime": "Время истечения",
@@ -249,7 +247,6 @@
"stop": "Стоп",
"appDesc": "Обработка настроек, связанных с приложением",
"vpnDesc": "Изменение настроек, связанных с VPN",
"generalDesc": "Переопределение общих настроек",
"dnsDesc": "Обновление настроек, связанных с DNS",
"key": "Ключ",
"value": "Значение",
@@ -346,5 +343,34 @@
"exportFile": "Экспорт файла",
"cacheCorrupt": "Кэш поврежден. Хотите очистить его?",
"detectionTip": "Опирается на сторонний API, только для справки",
"listen": "Слушать"
"listen": "Слушать",
"keyExists": "Текущий ключ уже существует",
"valueExists": "Текущее значение уже существует",
"undo": "Отменить",
"redo": "Повторить",
"none": "Нет",
"basicConfig": "Базовая конфигурация",
"basicConfigDesc": "Глобальное изменение базовых настроек",
"selectedCountTitle": "Выбрано {count} элементов",
"addRule": "Добавить правило",
"ruleProviderEmptyTip": "Поставщик правил не может быть пустым",
"ruleName": "Название правила",
"content": "Содержание",
"contentEmptyTip": "Содержание не может быть пустым",
"subRule": "Подправило",
"subRuleEmptyTip": "Содержание подправила не может быть пустым",
"ruleTarget": "Цель правила",
"ruleTargetEmptyTip": "Цель правила не может быть пустой",
"sourceIp": "Исходный IP",
"noResolve": "Не разрешать IP",
"getOriginRules": "Получить оригинальные правила",
"overrideOriginRules": "Переопределить оригинальное правило",
"addedOriginRules": "Добавить к оригинальным правилам",
"enableOverride": "Включить переопределение",
"deleteRuleTip": "Вы уверены, что хотите удалить выбранное правило?",
"saveChanges": "Сохранить изменения?",
"generalDesc": "Изменение общих настроек",
"findProcessModeDesc": "При включении возможны небольшие потери производительности",
"tabAnimationDesc": "Действительно только в мобильном виде",
"saveTip": "Вы уверены, что хотите сохранить?"
}

View File

@@ -123,7 +123,6 @@
"project": "项目",
"core": "内核",
"tabAnimation": "选项卡动画",
"tabAnimationDesc": "开启后,主页选项卡将添加切换动画",
"desc": "基于ClashMeta的多平台代理客户端简单易用开源无广告。",
"startVpn": "正在启动VPN...",
"stopVpn": "正在停止VPN...",
@@ -169,7 +168,7 @@
"externalControllerDesc": "开启后将可以通过9090端口控制Clash内核",
"ipv6Desc": "开启后将可以接收IPv6流量",
"app": "应用",
"general": "基础",
"general": "常规",
"vpnSystemProxyDesc": "为VpnService附加HTTP代理",
"systemProxyDesc": "设置系统代理",
"unifiedDelay": "统一延迟",
@@ -181,7 +180,6 @@
"requests": "请求",
"requestsDesc": "查看最近请求记录",
"findProcessMode": "查找进程",
"findProcessModeDesc": "开启后存在闪退风险",
"init": "初始化",
"infiniteTime": "长期有效",
"expirationTime": "到期时间",
@@ -249,7 +247,6 @@
"stop": "暂停",
"appDesc": "处理应用相关设置",
"vpnDesc": "修改VPN相关设置",
"generalDesc": "覆写基础设置",
"dnsDesc": "更新DNS相关设置",
"key": "键",
"value": "值",
@@ -346,5 +343,34 @@
"exportFile": "导出文件",
"cacheCorrupt": "缓存已损坏,是否清空?",
"detectionTip": "依赖第三方api仅供参考",
"listen": "监听"
"listen": "监听",
"keyExists": "当前键已存在",
"valueExists": "当前值已存在",
"undo": "撤销",
"redo": "重做",
"none": "无",
"basicConfig": "基本配置",
"basicConfigDesc": "全局修改基本配置",
"selectedCountTitle": "已选择 {count} 项",
"addRule": "添加规则",
"ruleProviderEmptyTip": "规则提供者不能为空",
"ruleName": "规则名称",
"content": "内容",
"contentEmptyTip": "内容不能为空",
"subRule": "子规则",
"subRuleEmptyTip": "子规则内容不能为空",
"ruleTarget": "规则目标",
"ruleTargetEmptyTip": "规则目标不能为空",
"sourceIp": "源IP",
"noResolve": "不解析IP",
"getOriginRules": "获取原始规则",
"overrideOriginRules": "覆盖原始规则",
"addedOriginRules": "附加到原始规则",
"enableOverride": "启用覆写",
"deleteRuleTip": "确定要删除选中的规则吗?",
"saveChanges": "是否保存更改?",
"generalDesc": "修改通用设置",
"findProcessModeDesc": "开启后会有一定性能损耗",
"tabAnimationDesc": "仅在移动视图中有效",
"saveTip": "确定要保存吗?"
}

View File

@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
static String m0(count) => "${count} items have been selected";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"about": MessageLookupByLibrary.simpleMessage("About"),
@@ -44,6 +46,10 @@ class MessageLookup extends MessageLookupByLibrary {
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
"action_view": MessageLookupByLibrary.simpleMessage("Show/Hide"),
"add": MessageLookupByLibrary.simpleMessage("Add"),
"addRule": MessageLookupByLibrary.simpleMessage("Add rule"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
"Attach on the original rules",
),
"address": MessageLookupByLibrary.simpleMessage("Address"),
"addressHelp": MessageLookupByLibrary.simpleMessage(
"WebDAV server address",
@@ -114,6 +120,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Sync data via WebDAV or file",
),
"backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"),
"basicConfig": MessageLookupByLibrary.simpleMessage("Basic configuration"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
"Modify the basic configuration globally",
),
"bind": MessageLookupByLibrary.simpleMessage("Bind"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"),
@@ -149,6 +159,10 @@ class MessageLookup extends MessageLookupByLibrary {
"View current connections data",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"content": MessageLookupByLibrary.simpleMessage("Content"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Content cannot be empty",
),
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
"Copying environment variables",
@@ -177,6 +191,9 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"Sure you want to delete the current profile?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to delete the selected rule?",
),
"desc": MessageLookupByLibrary.simpleMessage(
"A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
),
@@ -205,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("Download"),
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
"en": MessageLookupByLibrary.simpleMessage("English"),
"enableOverride": MessageLookupByLibrary.simpleMessage("Enable override"),
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
"exclude": MessageLookupByLibrary.simpleMessage("Hidden from recent tasks"),
"excludeDesc": MessageLookupByLibrary.simpleMessage(
@@ -243,13 +261,13 @@ class MessageLookup extends MessageLookupByLibrary {
),
"findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
"There is a risk of flashback after opening",
"There is a certain performance loss after opening",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"),
"fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"),
"general": MessageLookupByLibrary.simpleMessage("General"),
"generalDesc": MessageLookupByLibrary.simpleMessage(
"Overwrite general settings",
"Modify general settings",
),
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
"geodataLoader": MessageLookupByLibrary.simpleMessage(
@@ -259,6 +277,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Enabling will use the Geo low memory loader",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"),
"getOriginRules": MessageLookupByLibrary.simpleMessage(
"Get original rules",
),
"global": MessageLookupByLibrary.simpleMessage("Global"),
"go": MessageLookupByLibrary.simpleMessage("Go"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
@@ -302,6 +323,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Tcp keep alive interval",
),
"key": MessageLookupByLibrary.simpleMessage("Key"),
"keyExists": MessageLookupByLibrary.simpleMessage(
"The current key already exists",
),
"language": MessageLookupByLibrary.simpleMessage("Language"),
"layout": MessageLookupByLibrary.simpleMessage("Layout"),
"light": MessageLookupByLibrary.simpleMessage("Light"),
@@ -366,6 +390,8 @@ class MessageLookup extends MessageLookupByLibrary {
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Please create a profile or add a valid profile",
),
"noResolve": MessageLookupByLibrary.simpleMessage("No resolve IP"),
"none": MessageLookupByLibrary.simpleMessage("none"),
"notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"The current proxy group cannot be selected.",
@@ -407,6 +433,9 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Turning it on will override the DNS options in the profile",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Override the original rule",
),
"password": MessageLookupByLibrary.simpleMessage("Password"),
"passwordTip": MessageLookupByLibrary.simpleMessage(
"Password cannot be empty",
@@ -483,6 +512,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Only recovery profiles",
),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"),
"redo": MessageLookupByLibrary.simpleMessage("redo"),
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
"remote": MessageLookupByLibrary.simpleMessage("Remote"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
@@ -517,12 +547,27 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"),
"ru": MessageLookupByLibrary.simpleMessage("Russian"),
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
"ruleName": MessageLookupByLibrary.simpleMessage("Rule name"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"Rule provider cannot be empty",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Rule target"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
"Rule target cannot be empty",
),
"save": MessageLookupByLibrary.simpleMessage("Save"),
"saveChanges": MessageLookupByLibrary.simpleMessage(
"Do you want to save the changes?",
),
"saveTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to save?",
),
"search": MessageLookupByLibrary.simpleMessage("Search"),
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
"selectedCountTitle": m0,
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
"show": MessageLookupByLibrary.simpleMessage("Show"),
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
@@ -533,6 +578,7 @@ class MessageLookup extends MessageLookupByLibrary {
"size": MessageLookupByLibrary.simpleMessage("Size"),
"sort": MessageLookupByLibrary.simpleMessage("Sort"),
"source": MessageLookupByLibrary.simpleMessage("Source"),
"sourceIp": MessageLookupByLibrary.simpleMessage("Source IP"),
"stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"),
"standard": MessageLookupByLibrary.simpleMessage("Standard"),
"start": MessageLookupByLibrary.simpleMessage("Start"),
@@ -544,6 +590,10 @@ class MessageLookup extends MessageLookupByLibrary {
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Style"),
"subRule": MessageLookupByLibrary.simpleMessage("Sub rule"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
"Sub rule content cannot be empty",
),
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
"system": MessageLookupByLibrary.simpleMessage("System"),
@@ -555,7 +605,7 @@ class MessageLookup extends MessageLookupByLibrary {
"tab": MessageLookupByLibrary.simpleMessage("Tab"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("Tab animation"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"When enabled, the home tab will add a toggle animation",
"Effective only in mobile view",
),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
@@ -583,6 +633,7 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"unable to update current profile",
),
"undo": MessageLookupByLibrary.simpleMessage("undo"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
"Remove extra delays such as handshaking",
@@ -597,6 +648,9 @@ class MessageLookup extends MessageLookupByLibrary {
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
"value": MessageLookupByLibrary.simpleMessage("Value"),
"valueExists": MessageLookupByLibrary.simpleMessage(
"The current value already exists",
),
"view": MessageLookupByLibrary.simpleMessage("View"),
"vpnDesc": MessageLookupByLibrary.simpleMessage(
"Modify VPN related settings",

View File

@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ja';
static String m0(count) => "${count} 項目が選択されています";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"about": MessageLookupByLibrary.simpleMessage("について"),
@@ -42,6 +44,8 @@ class MessageLookup extends MessageLookupByLibrary {
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
"action_view": MessageLookupByLibrary.simpleMessage("表示/非表示"),
"add": MessageLookupByLibrary.simpleMessage("追加"),
"addRule": MessageLookupByLibrary.simpleMessage("ルールを追加"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage("元のルールに追加"),
"address": MessageLookupByLibrary.simpleMessage("アドレス"),
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAVサーバーアドレス"),
"addressTip": MessageLookupByLibrary.simpleMessage("有効なWebDAVアドレスを入力"),
@@ -82,6 +86,8 @@ class MessageLookup extends MessageLookupByLibrary {
"WebDAVまたはファイルでデータを同期",
),
"backupSuccess": MessageLookupByLibrary.simpleMessage("バックアップ成功"),
"basicConfig": MessageLookupByLibrary.simpleMessage("基本設定"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("基本設定をグローバルに変更"),
"bind": MessageLookupByLibrary.simpleMessage("バインド"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("ブラックリストモード"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("バイパスドメイン"),
@@ -109,6 +115,8 @@ class MessageLookup extends MessageLookupByLibrary {
"connections": MessageLookupByLibrary.simpleMessage("接続"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容は必須です"),
"copy": MessageLookupByLibrary.simpleMessage("コピー"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage("環境変数をコピー"),
"copyLink": MessageLookupByLibrary.simpleMessage("リンクをコピー"),
@@ -133,6 +141,7 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"現在のプロファイルを削除しますか?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("選択したルールを削除しますか?"),
"desc": MessageLookupByLibrary.simpleMessage(
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
),
@@ -151,6 +160,7 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("ダウンロード"),
"edit": MessageLookupByLibrary.simpleMessage("編集"),
"en": MessageLookupByLibrary.simpleMessage("英語"),
"enableOverride": MessageLookupByLibrary.simpleMessage("上書きを有効化"),
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
"exclude": MessageLookupByLibrary.simpleMessage("最近のタスクから非表示"),
"excludeDesc": MessageLookupByLibrary.simpleMessage(
@@ -181,18 +191,19 @@ class MessageLookup extends MessageLookupByLibrary {
"filterSystemApp": MessageLookupByLibrary.simpleMessage("システムアプリを除外"),
"findProcessMode": MessageLookupByLibrary.simpleMessage("プロセス検出"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとフラッシュバックのリスクあり",
"有効化するとパフォーマンスが若干低下します",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("フォントファミリー"),
"fourColumns": MessageLookupByLibrary.simpleMessage("4列"),
"general": MessageLookupByLibrary.simpleMessage("一般"),
"generalDesc": MessageLookupByLibrary.simpleMessage("一般設定の上書き"),
"generalDesc": MessageLookupByLibrary.simpleMessage("一般設定を変更"),
"geoData": MessageLookupByLibrary.simpleMessage("地域データ"),
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低メモリモード"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとGeo低メモリローダーを使用",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("GeoIPコード"),
"getOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを取得"),
"global": MessageLookupByLibrary.simpleMessage("グローバル"),
"go": MessageLookupByLibrary.simpleMessage("移動"),
"goDownload": MessageLookupByLibrary.simpleMessage("ダウンロードへ"),
@@ -222,6 +233,7 @@ class MessageLookup extends MessageLookupByLibrary {
"TCPキープアライブ間隔",
),
"key": MessageLookupByLibrary.simpleMessage("キー"),
"keyExists": MessageLookupByLibrary.simpleMessage("現在のキーは既に存在します"),
"language": MessageLookupByLibrary.simpleMessage("言語"),
"layout": MessageLookupByLibrary.simpleMessage("レイアウト"),
"light": MessageLookupByLibrary.simpleMessage("ライト"),
@@ -270,6 +282,8 @@ class MessageLookup extends MessageLookupByLibrary {
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルを作成するか、有効なプロファイルを追加してください",
),
"noResolve": MessageLookupByLibrary.simpleMessage("IPを解決しない"),
"none": MessageLookupByLibrary.simpleMessage("なし"),
"notEmpty": MessageLookupByLibrary.simpleMessage("空欄不可"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"現在のプロキシグループは選択できません",
@@ -299,6 +313,7 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとプロファイルのDNS設定を上書き",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを上書き"),
"password": MessageLookupByLibrary.simpleMessage("パスワード"),
"passwordTip": MessageLookupByLibrary.simpleMessage("パスワードは必須です"),
"paste": MessageLookupByLibrary.simpleMessage("貼り付け"),
@@ -359,6 +374,7 @@ class MessageLookup extends MessageLookupByLibrary {
"recoveryAll": MessageLookupByLibrary.simpleMessage("全データ復元"),
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("プロファイルのみ復元"),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("復元成功"),
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
"remote": MessageLookupByLibrary.simpleMessage("リモート"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
@@ -387,12 +403,21 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("設定を使用"),
"ru": MessageLookupByLibrary.simpleMessage("ロシア語"),
"rule": MessageLookupByLibrary.simpleMessage("ルール"),
"ruleName": MessageLookupByLibrary.simpleMessage("ルール名"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"ルールプロバイダーは必須です",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("ルールプロバイダー"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("ルール対象"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("ルール対象は必須です"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("変更を保存しますか?"),
"saveTip": MessageLookupByLibrary.simpleMessage("保存してもよろしいですか?"),
"search": MessageLookupByLibrary.simpleMessage("検索"),
"seconds": MessageLookupByLibrary.simpleMessage(""),
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
"selectedCountTitle": m0,
"settings": MessageLookupByLibrary.simpleMessage("設定"),
"show": MessageLookupByLibrary.simpleMessage("表示"),
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
@@ -401,6 +426,7 @@ class MessageLookup extends MessageLookupByLibrary {
"size": MessageLookupByLibrary.simpleMessage("サイズ"),
"sort": MessageLookupByLibrary.simpleMessage("並び替え"),
"source": MessageLookupByLibrary.simpleMessage("ソース"),
"sourceIp": MessageLookupByLibrary.simpleMessage("送信元IP"),
"stackMode": MessageLookupByLibrary.simpleMessage("スタックモード"),
"standard": MessageLookupByLibrary.simpleMessage("標準"),
"start": MessageLookupByLibrary.simpleMessage("開始"),
@@ -410,6 +436,8 @@ class MessageLookup extends MessageLookupByLibrary {
"stop": MessageLookupByLibrary.simpleMessage("停止"),
"stopVpn": MessageLookupByLibrary.simpleMessage("VPNを停止中..."),
"style": MessageLookupByLibrary.simpleMessage("スタイル"),
"subRule": MessageLookupByLibrary.simpleMessage("サブルール"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("サブルールの内容は必須です"),
"submit": MessageLookupByLibrary.simpleMessage("送信"),
"sync": MessageLookupByLibrary.simpleMessage("同期"),
"system": MessageLookupByLibrary.simpleMessage("システム"),
@@ -420,9 +448,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"tab": MessageLookupByLibrary.simpleMessage("タブ"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("タブアニメーション"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとホームタブに切り替えアニメーションを追加",
),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("モバイル表示でのみ有効"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
@@ -443,6 +469,7 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"現在のプロファイルを更新できません",
),
"undo": MessageLookupByLibrary.simpleMessage("元に戻す"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("統一遅延"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
"ハンドシェイクなどの余分な遅延を削除",
@@ -455,6 +482,7 @@ class MessageLookup extends MessageLookupByLibrary {
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
"value": MessageLookupByLibrary.simpleMessage(""),
"valueExists": MessageLookupByLibrary.simpleMessage("現在の値は既に存在します"),
"view": MessageLookupByLibrary.simpleMessage("表示"),
"vpnDesc": MessageLookupByLibrary.simpleMessage("VPN関連設定の変更"),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ru';
static String m0(count) => "Выбрано ${count} элементов";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"about": MessageLookupByLibrary.simpleMessage("О программе"),
@@ -44,6 +46,10 @@ class MessageLookup extends MessageLookupByLibrary {
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
"action_view": MessageLookupByLibrary.simpleMessage("Показать/Скрыть"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"addRule": MessageLookupByLibrary.simpleMessage("Добавить правило"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
"Добавить к оригинальным правилам",
),
"address": MessageLookupByLibrary.simpleMessage("Адрес"),
"addressHelp": MessageLookupByLibrary.simpleMessage("Адрес сервера WebDAV"),
"addressTip": MessageLookupByLibrary.simpleMessage(
@@ -114,6 +120,10 @@ class MessageLookup extends MessageLookupByLibrary {
"backupSuccess": MessageLookupByLibrary.simpleMessage(
"Резервное копирование успешно",
),
"basicConfig": MessageLookupByLibrary.simpleMessage("Базовая конфигурация"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
"Глобальное изменение базовых настроек",
),
"bind": MessageLookupByLibrary.simpleMessage("Привязать"),
"blacklistMode": MessageLookupByLibrary.simpleMessage(
"Режим черного списка",
@@ -155,6 +165,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Просмотр текущих данных о соединениях",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Содержание не может быть пустым",
),
"copy": MessageLookupByLibrary.simpleMessage("Копировать"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
"Копирование переменных окружения",
@@ -185,6 +199,9 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите удалить текущий профиль?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите удалить выбранное правило?",
),
"desc": MessageLookupByLibrary.simpleMessage(
"Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
),
@@ -215,6 +232,9 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("Скачивание"),
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
"en": MessageLookupByLibrary.simpleMessage("Английский"),
"enableOverride": MessageLookupByLibrary.simpleMessage(
"Включить переопределение",
),
"entries": MessageLookupByLibrary.simpleMessage(" записей"),
"exclude": MessageLookupByLibrary.simpleMessage(
"Скрыть из последних задач",
@@ -259,13 +279,13 @@ class MessageLookup extends MessageLookupByLibrary {
"Режим поиска процесса",
),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
"Есть риск сбоя после включения",
"При включении возможны небольшие потери производительности",
),
"fontFamily": MessageLookupByLibrary.simpleMessage("Семейство шрифтов"),
"fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"),
"general": MessageLookupByLibrary.simpleMessage("Общие"),
"generalDesc": MessageLookupByLibrary.simpleMessage(
"Переопределение общих настроек",
"Изменение общих настроек",
),
"geoData": MessageLookupByLibrary.simpleMessage("Геоданные"),
"geodataLoader": MessageLookupByLibrary.simpleMessage(
@@ -275,6 +295,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Включение будет использовать загрузчик геоданных с низким потреблением памяти",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("Код Geoip"),
"getOriginRules": MessageLookupByLibrary.simpleMessage(
"Получить оригинальные правила",
),
"global": MessageLookupByLibrary.simpleMessage("Глобальный"),
"go": MessageLookupByLibrary.simpleMessage("Перейти"),
"goDownload": MessageLookupByLibrary.simpleMessage("Перейти к загрузке"),
@@ -322,6 +345,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Интервал поддержания TCP-соединения",
),
"key": MessageLookupByLibrary.simpleMessage("Ключ"),
"keyExists": MessageLookupByLibrary.simpleMessage(
"Текущий ключ уже существует",
),
"language": MessageLookupByLibrary.simpleMessage("Язык"),
"layout": MessageLookupByLibrary.simpleMessage("Макет"),
"light": MessageLookupByLibrary.simpleMessage("Светлый"),
@@ -392,6 +418,8 @@ class MessageLookup extends MessageLookupByLibrary {
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, создайте профиль или добавьте действительный профиль",
),
"noResolve": MessageLookupByLibrary.simpleMessage("Не разрешать IP"),
"none": MessageLookupByLibrary.simpleMessage("Нет"),
"notEmpty": MessageLookupByLibrary.simpleMessage("Не может быть пустым"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"Текущая группа прокси не может быть выбрана.",
@@ -435,6 +463,9 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Включение переопределит настройки DNS в профиле",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Переопределить оригинальное правило",
),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"passwordTip": MessageLookupByLibrary.simpleMessage(
"Пароль не может быть пустым",
@@ -517,6 +548,7 @@ class MessageLookup extends MessageLookupByLibrary {
"recoverySuccess": MessageLookupByLibrary.simpleMessage(
"Восстановление успешно",
),
"redo": MessageLookupByLibrary.simpleMessage("Повторить"),
"regExp": MessageLookupByLibrary.simpleMessage("Регулярное выражение"),
"remote": MessageLookupByLibrary.simpleMessage("Удаленный"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
@@ -555,12 +587,25 @@ class MessageLookup extends MessageLookupByLibrary {
),
"ru": MessageLookupByLibrary.simpleMessage("Русский"),
"rule": MessageLookupByLibrary.simpleMessage("Правило"),
"ruleName": MessageLookupByLibrary.simpleMessage("Название правила"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"Поставщик правил не может быть пустым",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Провайдеры правил"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Цель правила"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
"Цель правила не может быть пустой",
),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"saveChanges": MessageLookupByLibrary.simpleMessage("Сохранить изменения?"),
"saveTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите сохранить?",
),
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
"selectedCountTitle": m0,
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"show": MessageLookupByLibrary.simpleMessage("Показать"),
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
@@ -571,6 +616,7 @@ class MessageLookup extends MessageLookupByLibrary {
"size": MessageLookupByLibrary.simpleMessage("Размер"),
"sort": MessageLookupByLibrary.simpleMessage("Сортировка"),
"source": MessageLookupByLibrary.simpleMessage("Источник"),
"sourceIp": MessageLookupByLibrary.simpleMessage("Исходный IP"),
"stackMode": MessageLookupByLibrary.simpleMessage("Режим стека"),
"standard": MessageLookupByLibrary.simpleMessage("Стандартный"),
"start": MessageLookupByLibrary.simpleMessage("Старт"),
@@ -582,6 +628,10 @@ class MessageLookup extends MessageLookupByLibrary {
"stop": MessageLookupByLibrary.simpleMessage("Стоп"),
"stopVpn": MessageLookupByLibrary.simpleMessage("Остановка VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Стиль"),
"subRule": MessageLookupByLibrary.simpleMessage("Подправило"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
"Содержание подправила не может быть пустым",
),
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
"system": MessageLookupByLibrary.simpleMessage("Система"),
@@ -593,7 +643,7 @@ class MessageLookup extends MessageLookupByLibrary {
"tab": MessageLookupByLibrary.simpleMessage("Вкладка"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("Анимация вкладок"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"При включении домашняя вкладка добавит анимацию переключения",
"Действительно только в мобильном виде",
),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP параллелизм"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
@@ -623,6 +673,7 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"невозможно обновить текущий профиль",
),
"undo": MessageLookupByLibrary.simpleMessage("Отменить"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage(
"Унифицированная задержка",
),
@@ -641,6 +692,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Использовать системные hosts",
),
"value": MessageLookupByLibrary.simpleMessage("Значение"),
"valueExists": MessageLookupByLibrary.simpleMessage(
"Текущее значение уже существует",
),
"view": MessageLookupByLibrary.simpleMessage("Просмотр"),
"vpnDesc": MessageLookupByLibrary.simpleMessage(
"Изменение настроек, связанных с VPN",

View File

@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'zh_CN';
static String m0(count) => "已选择 ${count}";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"about": MessageLookupByLibrary.simpleMessage("关于"),
@@ -40,6 +42,8 @@ class MessageLookup extends MessageLookupByLibrary {
"action_tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"),
"action_view": MessageLookupByLibrary.simpleMessage("显示/隐藏"),
"add": MessageLookupByLibrary.simpleMessage("添加"),
"addRule": MessageLookupByLibrary.simpleMessage("添加规则"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage("附加到原始规则"),
"address": MessageLookupByLibrary.simpleMessage("地址"),
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"),
"addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"),
@@ -76,6 +80,8 @@ class MessageLookup extends MessageLookupByLibrary {
"通过WebDAV或者文件同步数据",
),
"backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"),
"basicConfig": MessageLookupByLibrary.simpleMessage("基本配置"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("全局修改基本配置"),
"bind": MessageLookupByLibrary.simpleMessage("绑定"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"),
@@ -99,6 +105,8 @@ class MessageLookup extends MessageLookupByLibrary {
"connections": MessageLookupByLibrary.simpleMessage("连接"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容不能为空"),
"copy": MessageLookupByLibrary.simpleMessage("复制"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"),
"copyLink": MessageLookupByLibrary.simpleMessage("复制链接"),
@@ -119,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
"delete": MessageLookupByLibrary.simpleMessage("删除"),
"deleteProfileTip": MessageLookupByLibrary.simpleMessage("确定要删除当前配置吗?"),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("确定要删除选中的规则吗?"),
"desc": MessageLookupByLibrary.simpleMessage(
"基于ClashMeta的多平台代理客户端简单易用开源无广告。",
),
@@ -137,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("下载"),
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
"en": MessageLookupByLibrary.simpleMessage("英语"),
"enableOverride": MessageLookupByLibrary.simpleMessage("启用覆写"),
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
@@ -162,15 +172,16 @@ class MessageLookup extends MessageLookupByLibrary {
"fileIsUpdate": MessageLookupByLibrary.simpleMessage("文件有修改,是否保存修改"),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后存在闪退风险"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"),
"fontFamily": MessageLookupByLibrary.simpleMessage("字体"),
"fourColumns": MessageLookupByLibrary.simpleMessage("四列"),
"general": MessageLookupByLibrary.simpleMessage("基础"),
"generalDesc": MessageLookupByLibrary.simpleMessage("覆写基础设置"),
"general": MessageLookupByLibrary.simpleMessage("常规"),
"generalDesc": MessageLookupByLibrary.simpleMessage("修改通用设置"),
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"),
"getOriginRules": MessageLookupByLibrary.simpleMessage("获取原始规则"),
"global": MessageLookupByLibrary.simpleMessage("全局"),
"go": MessageLookupByLibrary.simpleMessage("前往"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
@@ -196,6 +207,7 @@ class MessageLookup extends MessageLookupByLibrary {
"just": MessageLookupByLibrary.simpleMessage("刚刚"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
"key": MessageLookupByLibrary.simpleMessage(""),
"keyExists": MessageLookupByLibrary.simpleMessage("当前键已存在"),
"language": MessageLookupByLibrary.simpleMessage("语言"),
"layout": MessageLookupByLibrary.simpleMessage("布局"),
"light": MessageLookupByLibrary.simpleMessage("浅色"),
@@ -238,6 +250,8 @@ class MessageLookup extends MessageLookupByLibrary {
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
"noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
"none": MessageLookupByLibrary.simpleMessage(""),
"notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"),
@@ -261,6 +275,7 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"),
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("覆盖原始规则"),
"password": MessageLookupByLibrary.simpleMessage("密码"),
"passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"),
"paste": MessageLookupByLibrary.simpleMessage("粘贴"),
@@ -313,6 +328,7 @@ class MessageLookup extends MessageLookupByLibrary {
"recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"),
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
"redo": MessageLookupByLibrary.simpleMessage("重做"),
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
"remote": MessageLookupByLibrary.simpleMessage("远程"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"),
@@ -335,12 +351,19 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"),
"ru": MessageLookupByLibrary.simpleMessage("俄语"),
"rule": MessageLookupByLibrary.simpleMessage("规则"),
"ruleName": MessageLookupByLibrary.simpleMessage("规则名称"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage("规则提供者不能为空"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("规则目标"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("规则目标不能为空"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("是否保存更改?"),
"saveTip": MessageLookupByLibrary.simpleMessage("确定要保存吗?"),
"search": MessageLookupByLibrary.simpleMessage("搜索"),
"seconds": MessageLookupByLibrary.simpleMessage(""),
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
"selectedCountTitle": m0,
"settings": MessageLookupByLibrary.simpleMessage("设置"),
"show": MessageLookupByLibrary.simpleMessage("显示"),
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
@@ -349,6 +372,7 @@ class MessageLookup extends MessageLookupByLibrary {
"size": MessageLookupByLibrary.simpleMessage("尺寸"),
"sort": MessageLookupByLibrary.simpleMessage("排序"),
"source": MessageLookupByLibrary.simpleMessage("来源"),
"sourceIp": MessageLookupByLibrary.simpleMessage("源IP"),
"stackMode": MessageLookupByLibrary.simpleMessage("栈模式"),
"standard": MessageLookupByLibrary.simpleMessage("标准"),
"start": MessageLookupByLibrary.simpleMessage("启动"),
@@ -358,6 +382,8 @@ class MessageLookup extends MessageLookupByLibrary {
"stop": MessageLookupByLibrary.simpleMessage("暂停"),
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
"style": MessageLookupByLibrary.simpleMessage("风格"),
"subRule": MessageLookupByLibrary.simpleMessage("子规则"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("子规则内容不能为空"),
"submit": MessageLookupByLibrary.simpleMessage("提交"),
"sync": MessageLookupByLibrary.simpleMessage("同步"),
"system": MessageLookupByLibrary.simpleMessage("系统"),
@@ -366,9 +392,7 @@ class MessageLookup extends MessageLookupByLibrary {
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
"tab": MessageLookupByLibrary.simpleMessage("标签页"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"开启后,主页选项卡将添加切换动画",
),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("仅在移动视图中有效"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
@@ -389,6 +413,7 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"无法更新当前配置文件",
),
"undo": MessageLookupByLibrary.simpleMessage("撤销"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"),
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
@@ -399,6 +424,7 @@ class MessageLookup extends MessageLookupByLibrary {
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
"value": MessageLookupByLibrary.simpleMessage(""),
"valueExists": MessageLookupByLibrary.simpleMessage("当前值已存在"),
"view": MessageLookupByLibrary.simpleMessage("查看"),
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -945,16 +945,6 @@ class AppLocalizations {
);
}
/// `When enabled, the home tab will add a toggle animation`
String get tabAnimationDesc {
return Intl.message(
'When enabled, the home tab will add a toggle animation',
name: 'tabAnimationDesc',
desc: '',
args: [],
);
}
/// `A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.`
String get desc {
return Intl.message(
@@ -1435,16 +1425,6 @@ class AppLocalizations {
);
}
/// `There is a risk of flashback after opening`
String get findProcessModeDesc {
return Intl.message(
'There is a risk of flashback after opening',
name: 'findProcessModeDesc',
desc: '',
args: [],
);
}
/// `Init`
String get init {
return Intl.message('Init', name: 'init', desc: '', args: []);
@@ -1930,16 +1910,6 @@ class AppLocalizations {
);
}
/// `Overwrite general settings`
String get generalDesc {
return Intl.message(
'Overwrite general settings',
name: 'generalDesc',
desc: '',
args: [],
);
}
/// `Update DNS related settings`
String get dnsDesc {
return Intl.message(
@@ -2694,6 +2664,246 @@ class AppLocalizations {
String get listen {
return Intl.message('Listen', name: 'listen', desc: '', args: []);
}
/// `The current key already exists`
String get keyExists {
return Intl.message(
'The current key already exists',
name: 'keyExists',
desc: '',
args: [],
);
}
/// `The current value already exists`
String get valueExists {
return Intl.message(
'The current value already exists',
name: 'valueExists',
desc: '',
args: [],
);
}
/// `undo`
String get undo {
return Intl.message('undo', name: 'undo', desc: '', args: []);
}
/// `redo`
String get redo {
return Intl.message('redo', name: 'redo', desc: '', args: []);
}
/// `none`
String get none {
return Intl.message('none', name: 'none', desc: '', args: []);
}
/// `Basic configuration`
String get basicConfig {
return Intl.message(
'Basic configuration',
name: 'basicConfig',
desc: '',
args: [],
);
}
/// `Modify the basic configuration globally`
String get basicConfigDesc {
return Intl.message(
'Modify the basic configuration globally',
name: 'basicConfigDesc',
desc: '',
args: [],
);
}
/// `{count} items have been selected`
String selectedCountTitle(Object count) {
return Intl.message(
'$count items have been selected',
name: 'selectedCountTitle',
desc: '',
args: [count],
);
}
/// `Add rule`
String get addRule {
return Intl.message('Add rule', name: 'addRule', desc: '', args: []);
}
/// `Rule provider cannot be empty`
String get ruleProviderEmptyTip {
return Intl.message(
'Rule provider cannot be empty',
name: 'ruleProviderEmptyTip',
desc: '',
args: [],
);
}
/// `Rule name`
String get ruleName {
return Intl.message('Rule name', name: 'ruleName', desc: '', args: []);
}
/// `Content`
String get content {
return Intl.message('Content', name: 'content', desc: '', args: []);
}
/// `Content cannot be empty`
String get contentEmptyTip {
return Intl.message(
'Content cannot be empty',
name: 'contentEmptyTip',
desc: '',
args: [],
);
}
/// `Sub rule`
String get subRule {
return Intl.message('Sub rule', name: 'subRule', desc: '', args: []);
}
/// `Sub rule content cannot be empty`
String get subRuleEmptyTip {
return Intl.message(
'Sub rule content cannot be empty',
name: 'subRuleEmptyTip',
desc: '',
args: [],
);
}
/// `Rule target`
String get ruleTarget {
return Intl.message('Rule target', name: 'ruleTarget', desc: '', args: []);
}
/// `Rule target cannot be empty`
String get ruleTargetEmptyTip {
return Intl.message(
'Rule target cannot be empty',
name: 'ruleTargetEmptyTip',
desc: '',
args: [],
);
}
/// `Source IP`
String get sourceIp {
return Intl.message('Source IP', name: 'sourceIp', desc: '', args: []);
}
/// `No resolve IP`
String get noResolve {
return Intl.message('No resolve IP', name: 'noResolve', desc: '', args: []);
}
/// `Get original rules`
String get getOriginRules {
return Intl.message(
'Get original rules',
name: 'getOriginRules',
desc: '',
args: [],
);
}
/// `Override the original rule`
String get overrideOriginRules {
return Intl.message(
'Override the original rule',
name: 'overrideOriginRules',
desc: '',
args: [],
);
}
/// `Attach on the original rules`
String get addedOriginRules {
return Intl.message(
'Attach on the original rules',
name: 'addedOriginRules',
desc: '',
args: [],
);
}
/// `Enable override`
String get enableOverride {
return Intl.message(
'Enable override',
name: 'enableOverride',
desc: '',
args: [],
);
}
/// `Are you sure you want to delete the selected rule?`
String get deleteRuleTip {
return Intl.message(
'Are you sure you want to delete the selected rule?',
name: 'deleteRuleTip',
desc: '',
args: [],
);
}
/// `Do you want to save the changes?`
String get saveChanges {
return Intl.message(
'Do you want to save the changes?',
name: 'saveChanges',
desc: '',
args: [],
);
}
/// `Modify general settings`
String get generalDesc {
return Intl.message(
'Modify general settings',
name: 'generalDesc',
desc: '',
args: [],
);
}
/// `There is a certain performance loss after opening`
String get findProcessModeDesc {
return Intl.message(
'There is a certain performance loss after opening',
name: 'findProcessModeDesc',
desc: '',
args: [],
);
}
/// `Effective only in mobile view`
String get tabAnimationDesc {
return Intl.message(
'Effective only in mobile view',
name: 'tabAnimationDesc',
desc: '',
args: [],
);
}
/// `Are you sure you want to save?`
String get saveTip {
return Intl.message(
'Are you sure you want to save?',
name: 'saveTip',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -27,6 +27,7 @@ Future<void> main() async {
await globalState.initApp(version);
await android?.init();
await window?.init(version);
globalState.isPre = const String.fromEnvironment("APP_ENV") != 'stable';
HttpOverrides.global = FlClashHttpOverrides();
runApp(ProviderScope(
child: const Application(),

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -19,6 +21,7 @@ class _AppStateManagerState extends State<AppStateManager>
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@@ -56,3 +59,24 @@ class _AppStateManagerState extends State<AppStateManager>
);
}
}
class AppEnvManager extends StatelessWidget {
final Widget child;
const AppEnvManager({
super.key,
required this.child,
});
@override
Widget build(BuildContext context) {
if (globalState.isPre) {
return Banner(
message: 'PRE',
location: BannerLocation.topEnd,
child: child,
);
}
return child;
}
}

View File

@@ -7,4 +7,5 @@ export 'app_state_manager.dart';
export 'vpn_manager.dart';
export 'proxy_manager.dart';
export 'connectivity_manager.dart';
export 'message_manager.dart';
export 'message_manager.dart';
export 'theme_manager.dart';

View File

@@ -0,0 +1,38 @@
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/common/measure.dart';
import 'package:fl_clash/common/theme.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
class ThemeManager extends StatelessWidget {
final Widget child;
const ThemeManager({
super.key,
required this.child,
});
@override
Widget build(BuildContext context) {
globalState.measure = Measure.of(context);
globalState.theme = CommonTheme.of(context);
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(
textScaleFactor,
),
),
child: LayoutBuilder(
builder: (_, container) {
globalState.appController.updateViewSize(
Size(
container.maxWidth,
container.maxHeight,
),
);
return child;
},
),
);
}
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:fl_clash/common/common.dart';
@@ -24,6 +25,7 @@ class WindowManager extends ConsumerStatefulWidget {
class _WindowContainerState extends ConsumerState<WindowManager>
with WindowListener, WindowExtListener {
@override
Widget build(BuildContext context) {
return widget.child;
@@ -271,7 +273,7 @@ class _WindowHeaderState extends State<WindowHeader> {
_updateMaximized();
},
child: Container(
color: context.colorScheme.secondary.toSoft,
color: context.colorScheme.secondary.opacity15,
alignment: Alignment.centerLeft,
height: kHeaderHeight,
),

View File

@@ -18,7 +18,7 @@ class AppState with _$AppState {
@Default([]) List<Package> packages,
@Default(ColorSchemes()) ColorSchemes colorSchemes,
@Default(0) int sortNum,
required double viewWidth,
required Size viewSize,
@Default({}) DelayMap delayMap,
@Default([]) List<Group> groups,
@Default(0) int checkIpNum,
@@ -35,7 +35,7 @@ class AppState with _$AppState {
}
extension AppStateExt on AppState {
ViewMode get viewMode => other.getViewMode(viewWidth);
ViewMode get viewMode => other.getViewMode(viewSize.width);
bool get isStart => runTime != null;
}

View File

@@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import '../enum/enum.dart';
part 'generated/clash_config.freezed.dart';
part 'generated/clash_config.g.dart';
typedef HostsMap = Map<String, String>;
@@ -122,7 +123,7 @@ class ProxyGroup with _$ProxyGroup {
String? filter,
@JsonKey(name: "expected-filter") String? excludeFilter,
@JsonKey(name: "exclude-type") String? excludeType,
@JsonKey(name: "expected-status") int? expectedStatus,
@JsonKey(name: "expected-status") dynamic expectedStatus,
bool? hidden,
String? icon,
}) = _ProxyGroup;
@@ -131,6 +132,16 @@ class ProxyGroup with _$ProxyGroup {
_$ProxyGroupFromJson(json);
}
@freezed
class RuleProvider with _$RuleProvider {
const factory RuleProvider({
required String name,
}) = _RuleProvider;
factory RuleProvider.fromJson(Map<String, Object?> json) =>
_$RuleProviderFromJson(json);
}
@freezed
class Tun with _$Tun {
const factory Tun({
@@ -273,11 +284,136 @@ class GeoXUrl with _$GeoXUrl {
}
}
@freezed
class ParsedRule with _$ParsedRule {
const factory ParsedRule({
required RuleAction ruleAction,
String? content,
String? ruleTarget,
String? ruleProvider,
String? subRule,
@Default(false) bool noResolve,
@Default(false) bool src,
}) = _ParsedRule;
factory ParsedRule.parseString(String value) {
final splits = value.split(",");
final shortSplits = splits
.where(
(item) => !item.contains("src") && !item.contains("no-resolve"),
)
.toList();
final ruleAction = RuleAction.values.firstWhere(
(item) => item.value == shortSplits.first,
orElse: () => RuleAction.DOMAIN,
);
String? subRule;
String? ruleTarget;
if (ruleAction == RuleAction.SUB_RULE) {
subRule = shortSplits.last;
} else {
ruleTarget = shortSplits.last;
}
String? content;
String? ruleProvider;
if (ruleAction == RuleAction.RULE_SET) {
ruleProvider = shortSplits.sublist(1, shortSplits.length - 1).join(",");
} else {
content = shortSplits.sublist(1, shortSplits.length - 1).join(",");
}
return ParsedRule(
ruleAction: ruleAction,
content: content,
src: splits.contains("src"),
ruleProvider: ruleProvider,
noResolve: splits.contains("no-resolve"),
subRule: subRule,
ruleTarget: ruleTarget,
);
}
}
extension ParsedRuleExt on ParsedRule {
String get value {
return [
ruleAction.value,
ruleAction == RuleAction.RULE_SET ? ruleProvider : content,
ruleAction == RuleAction.SUB_RULE ? subRule : ruleTarget,
if (ruleAction.hasParams) ...[
if (src) "src",
if (noResolve) "no-resolve",
]
].join(",");
}
}
@freezed
class Rule with _$Rule {
const factory Rule({
required String id,
required String value,
}) = _Rule;
factory Rule.value(String value) {
return Rule(
value: value,
id: other.uuidV4,
);
}
factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);
}
@freezed
class SubRule with _$SubRule {
const factory SubRule({
required String name,
}) = _SubRule;
factory SubRule.fromJson(Map<String, Object?> json) =>
_$SubRuleFromJson(json);
}
_genRule(List<dynamic>? rules) {
if (rules == null) {
return [];
}
return rules
.map(
(item) => Rule.value(item),
)
.toList();
}
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
return json.entries.map((entry) => RuleProvider(name: entry.key)).toList();
}
List<SubRule> _genSubRules(Map<String, dynamic> json) {
return json.entries
.map(
(entry) => SubRule(
name: entry.key,
),
)
.toList();
}
@freezed
class ClashConfigSnippet with _$ClashConfigSnippet {
const factory ClashConfigSnippet({
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@Default([]) List<String> rule,
@JsonKey(fromJson: _genRule) @Default([]) List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
@Default([])
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@Default([])
List<SubRule> subRules,
}) = _ClashConfigSnippet;
factory ClashConfigSnippet.fromJson(Map<String, Object?> json) =>

View File

@@ -382,7 +382,7 @@ extension ColorSchemesExt on ColorSchemes {
);
}
return lightColorScheme != null
? ColorScheme.fromSeed(seedColor: lightColorScheme!.primary)
? ColorScheme.fromSeed(seedColor: lightColorScheme!.primary,dynamicSchemeVariant: DynamicSchemeVariant.vibrant)
: ColorScheme.fromSeed(seedColor: defaultPrimaryColor);
}
}
@@ -481,13 +481,13 @@ class Field with _$Field {
}) = _Field;
}
enum ActionType {
enum PopupMenuItemType {
primary,
danger,
}
class ActionItemData {
const ActionItemData({
class PopupMenuItemData {
const PopupMenuItemData({
this.icon,
required this.label,
required this.onPressed,
@@ -497,7 +497,7 @@ class ActionItemData {
final double? iconSize;
final String label;
final VoidCallback onPressed;
final VoidCallback? onPressed;
final IconData? icon;
final ActionType? type;
final PopupMenuItemType? type;
}

View File

@@ -37,7 +37,7 @@ const defaultProxiesStyle = ProxiesStyle();
const defaultWindowProps = WindowProps();
const defaultAccessControl = AccessControl();
final defaultThemeProps = ThemeProps().copyWith(
primaryColor: defaultPrimaryColor.value,
primaryColor: defaultPrimaryColor.toARGB32(),
themeMode: ThemeMode.dark,
);
@@ -121,7 +121,7 @@ extension AccessControlExt on AccessControl {
@freezed
class WindowProps with _$WindowProps {
const factory WindowProps({
@Default(900) double width,
@Default(750) double width,
@Default(600) double height,
double? top,
double? left,

View File

@@ -62,6 +62,7 @@ class ConfigExtendedParams with _$ConfigExtendedParams {
@JsonKey(name: "is-patch") required bool isPatch,
@JsonKey(name: "selected-map") required SelectedMap selectedMap,
@JsonKey(name: "override-dns") required bool overrideDns,
@JsonKey(name: "override-rule") required bool overrideRule,
@JsonKey(name: "test-url") required String testUrl,
}) = _ConfigExtendedParams;

View File

@@ -21,7 +21,7 @@ mixin _$AppState {
List<Package> get packages => throw _privateConstructorUsedError;
ColorSchemes get colorSchemes => throw _privateConstructorUsedError;
int get sortNum => throw _privateConstructorUsedError;
double get viewWidth => throw _privateConstructorUsedError;
Size get viewSize => throw _privateConstructorUsedError;
Map<String, Map<String, int?>> get delayMap =>
throw _privateConstructorUsedError;
List<Group> get groups => throw _privateConstructorUsedError;
@@ -54,7 +54,7 @@ abstract class $AppStateCopyWith<$Res> {
List<Package> packages,
ColorSchemes colorSchemes,
int sortNum,
double viewWidth,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
List<Group> groups,
int checkIpNum,
@@ -91,7 +91,7 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
Object? packages = null,
Object? colorSchemes = null,
Object? sortNum = null,
Object? viewWidth = null,
Object? viewSize = null,
Object? delayMap = null,
Object? groups = null,
Object? checkIpNum = null,
@@ -126,10 +126,10 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
as int,
viewWidth: null == viewWidth
? _value.viewWidth
: viewWidth // ignore: cast_nullable_to_non_nullable
as double,
viewSize: null == viewSize
? _value.viewSize
: viewSize // ignore: cast_nullable_to_non_nullable
as Size,
delayMap: null == delayMap
? _value.delayMap
: delayMap // ignore: cast_nullable_to_non_nullable
@@ -206,7 +206,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
List<Package> packages,
ColorSchemes colorSchemes,
int sortNum,
double viewWidth,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
List<Group> groups,
int checkIpNum,
@@ -242,7 +242,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
Object? packages = null,
Object? colorSchemes = null,
Object? sortNum = null,
Object? viewWidth = null,
Object? viewSize = null,
Object? delayMap = null,
Object? groups = null,
Object? checkIpNum = null,
@@ -277,10 +277,10 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
as int,
viewWidth: null == viewWidth
? _value.viewWidth
: viewWidth // ignore: cast_nullable_to_non_nullable
as double,
viewSize: null == viewSize
? _value.viewSize
: viewSize // ignore: cast_nullable_to_non_nullable
as Size,
delayMap: null == delayMap
? _value._delayMap
: delayMap // ignore: cast_nullable_to_non_nullable
@@ -342,7 +342,7 @@ class _$AppStateImpl implements _AppState {
final List<Package> packages = const [],
this.colorSchemes = const ColorSchemes(),
this.sortNum = 0,
required this.viewWidth,
required this.viewSize,
final Map<String, Map<String, int?>> delayMap = const {},
final List<Group> groups = const [],
this.checkIpNum = 0,
@@ -382,7 +382,7 @@ class _$AppStateImpl implements _AppState {
@JsonKey()
final int sortNum;
@override
final double viewWidth;
final Size viewSize;
final Map<String, Map<String, int?>> _delayMap;
@override
@JsonKey()
@@ -432,7 +432,7 @@ class _$AppStateImpl implements _AppState {
@override
String toString() {
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, sortNum: $sortNum, viewWidth: $viewWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic)';
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic)';
}
@override
@@ -447,8 +447,8 @@ class _$AppStateImpl implements _AppState {
(identical(other.colorSchemes, colorSchemes) ||
other.colorSchemes == colorSchemes) &&
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
(identical(other.viewWidth, viewWidth) ||
other.viewWidth == viewWidth) &&
(identical(other.viewSize, viewSize) ||
other.viewSize == viewSize) &&
const DeepCollectionEquality().equals(other._delayMap, _delayMap) &&
const DeepCollectionEquality().equals(other._groups, _groups) &&
(identical(other.checkIpNum, checkIpNum) ||
@@ -477,7 +477,7 @@ class _$AppStateImpl implements _AppState {
const DeepCollectionEquality().hash(_packages),
colorSchemes,
sortNum,
viewWidth,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
@@ -507,7 +507,7 @@ abstract class _AppState implements AppState {
final List<Package> packages,
final ColorSchemes colorSchemes,
final int sortNum,
required final double viewWidth,
required final Size viewSize,
final Map<String, Map<String, int?>> delayMap,
final List<Group> groups,
final int checkIpNum,
@@ -532,7 +532,7 @@ abstract class _AppState implements AppState {
@override
int get sortNum;
@override
double get viewWidth;
Size get viewSize;
@override
Map<String, Map<String, int?>> get delayMap;
@override

View File

@@ -37,7 +37,7 @@ mixin _$ProxyGroup {
@JsonKey(name: "exclude-type")
String? get excludeType => throw _privateConstructorUsedError;
@JsonKey(name: "expected-status")
int? get expectedStatus => throw _privateConstructorUsedError;
dynamic get expectedStatus => throw _privateConstructorUsedError;
bool? get hidden => throw _privateConstructorUsedError;
String? get icon => throw _privateConstructorUsedError;
@@ -70,7 +70,7 @@ abstract class $ProxyGroupCopyWith<$Res> {
String? filter,
@JsonKey(name: "expected-filter") String? excludeFilter,
@JsonKey(name: "exclude-type") String? excludeType,
@JsonKey(name: "expected-status") int? expectedStatus,
@JsonKey(name: "expected-status") dynamic expectedStatus,
bool? hidden,
String? icon});
}
@@ -158,7 +158,7 @@ class _$ProxyGroupCopyWithImpl<$Res, $Val extends ProxyGroup>
expectedStatus: freezed == expectedStatus
? _value.expectedStatus
: expectedStatus // ignore: cast_nullable_to_non_nullable
as int?,
as dynamic,
hidden: freezed == hidden
? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable
@@ -192,7 +192,7 @@ abstract class _$$ProxyGroupImplCopyWith<$Res>
String? filter,
@JsonKey(name: "expected-filter") String? excludeFilter,
@JsonKey(name: "exclude-type") String? excludeType,
@JsonKey(name: "expected-status") int? expectedStatus,
@JsonKey(name: "expected-status") dynamic expectedStatus,
bool? hidden,
String? icon});
}
@@ -278,7 +278,7 @@ class __$$ProxyGroupImplCopyWithImpl<$Res>
expectedStatus: freezed == expectedStatus
? _value.expectedStatus
: expectedStatus // ignore: cast_nullable_to_non_nullable
as int?,
as dynamic,
hidden: freezed == hidden
? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable
@@ -362,7 +362,7 @@ class _$ProxyGroupImpl implements _ProxyGroup {
final String? excludeType;
@override
@JsonKey(name: "expected-status")
final int? expectedStatus;
final dynamic expectedStatus;
@override
final bool? hidden;
@override
@@ -394,8 +394,8 @@ class _$ProxyGroupImpl implements _ProxyGroup {
other.excludeFilter == excludeFilter) &&
(identical(other.excludeType, excludeType) ||
other.excludeType == excludeType) &&
(identical(other.expectedStatus, expectedStatus) ||
other.expectedStatus == expectedStatus) &&
const DeepCollectionEquality()
.equals(other.expectedStatus, expectedStatus) &&
(identical(other.hidden, hidden) || other.hidden == hidden) &&
(identical(other.icon, icon) || other.icon == icon));
}
@@ -416,7 +416,7 @@ class _$ProxyGroupImpl implements _ProxyGroup {
filter,
excludeFilter,
excludeType,
expectedStatus,
const DeepCollectionEquality().hash(expectedStatus),
hidden,
icon);
@@ -451,7 +451,7 @@ abstract class _ProxyGroup implements ProxyGroup {
final String? filter,
@JsonKey(name: "expected-filter") final String? excludeFilter,
@JsonKey(name: "exclude-type") final String? excludeType,
@JsonKey(name: "expected-status") final int? expectedStatus,
@JsonKey(name: "expected-status") final dynamic expectedStatus,
final bool? hidden,
final String? icon}) = _$ProxyGroupImpl;
@@ -488,7 +488,7 @@ abstract class _ProxyGroup implements ProxyGroup {
String? get excludeType;
@override
@JsonKey(name: "expected-status")
int? get expectedStatus;
dynamic get expectedStatus;
@override
bool? get hidden;
@override
@@ -502,6 +502,156 @@ abstract class _ProxyGroup implements ProxyGroup {
throw _privateConstructorUsedError;
}
RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) {
return _RuleProvider.fromJson(json);
}
/// @nodoc
mixin _$RuleProvider {
String get name => throw _privateConstructorUsedError;
/// Serializes this RuleProvider to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of RuleProvider
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$RuleProviderCopyWith<RuleProvider> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RuleProviderCopyWith<$Res> {
factory $RuleProviderCopyWith(
RuleProvider value, $Res Function(RuleProvider) then) =
_$RuleProviderCopyWithImpl<$Res, RuleProvider>;
@useResult
$Res call({String name});
}
/// @nodoc
class _$RuleProviderCopyWithImpl<$Res, $Val extends RuleProvider>
implements $RuleProviderCopyWith<$Res> {
_$RuleProviderCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of RuleProvider
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$RuleProviderImplCopyWith<$Res>
implements $RuleProviderCopyWith<$Res> {
factory _$$RuleProviderImplCopyWith(
_$RuleProviderImpl value, $Res Function(_$RuleProviderImpl) then) =
__$$RuleProviderImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String name});
}
/// @nodoc
class __$$RuleProviderImplCopyWithImpl<$Res>
extends _$RuleProviderCopyWithImpl<$Res, _$RuleProviderImpl>
implements _$$RuleProviderImplCopyWith<$Res> {
__$$RuleProviderImplCopyWithImpl(
_$RuleProviderImpl _value, $Res Function(_$RuleProviderImpl) _then)
: super(_value, _then);
/// Create a copy of RuleProvider
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
}) {
return _then(_$RuleProviderImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RuleProviderImpl implements _RuleProvider {
const _$RuleProviderImpl({required this.name});
factory _$RuleProviderImpl.fromJson(Map<String, dynamic> json) =>
_$$RuleProviderImplFromJson(json);
@override
final String name;
@override
String toString() {
return 'RuleProvider(name: $name)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RuleProviderImpl &&
(identical(other.name, name) || other.name == name));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, name);
/// Create a copy of RuleProvider
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$RuleProviderImplCopyWith<_$RuleProviderImpl> get copyWith =>
__$$RuleProviderImplCopyWithImpl<_$RuleProviderImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$RuleProviderImplToJson(
this,
);
}
}
abstract class _RuleProvider implements RuleProvider {
const factory _RuleProvider({required final String name}) =
_$RuleProviderImpl;
factory _RuleProvider.fromJson(Map<String, dynamic> json) =
_$RuleProviderImpl.fromJson;
@override
String get name;
/// Create a copy of RuleProvider
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$RuleProviderImplCopyWith<_$RuleProviderImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Tun _$TunFromJson(Map<String, dynamic> json) {
return _Tun.fromJson(json);
}
@@ -1834,6 +1984,571 @@ abstract class _GeoXUrl implements GeoXUrl {
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$ParsedRule {
RuleAction get ruleAction => throw _privateConstructorUsedError;
String? get content => throw _privateConstructorUsedError;
String? get ruleTarget => throw _privateConstructorUsedError;
String? get ruleProvider => throw _privateConstructorUsedError;
String? get subRule => throw _privateConstructorUsedError;
bool get noResolve => throw _privateConstructorUsedError;
bool get src => throw _privateConstructorUsedError;
/// Create a copy of ParsedRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ParsedRuleCopyWith<ParsedRule> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ParsedRuleCopyWith<$Res> {
factory $ParsedRuleCopyWith(
ParsedRule value, $Res Function(ParsedRule) then) =
_$ParsedRuleCopyWithImpl<$Res, ParsedRule>;
@useResult
$Res call(
{RuleAction ruleAction,
String? content,
String? ruleTarget,
String? ruleProvider,
String? subRule,
bool noResolve,
bool src});
}
/// @nodoc
class _$ParsedRuleCopyWithImpl<$Res, $Val extends ParsedRule>
implements $ParsedRuleCopyWith<$Res> {
_$ParsedRuleCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ParsedRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ruleAction = null,
Object? content = freezed,
Object? ruleTarget = freezed,
Object? ruleProvider = freezed,
Object? subRule = freezed,
Object? noResolve = null,
Object? src = null,
}) {
return _then(_value.copyWith(
ruleAction: null == ruleAction
? _value.ruleAction
: ruleAction // ignore: cast_nullable_to_non_nullable
as RuleAction,
content: freezed == content
? _value.content
: content // ignore: cast_nullable_to_non_nullable
as String?,
ruleTarget: freezed == ruleTarget
? _value.ruleTarget
: ruleTarget // ignore: cast_nullable_to_non_nullable
as String?,
ruleProvider: freezed == ruleProvider
? _value.ruleProvider
: ruleProvider // ignore: cast_nullable_to_non_nullable
as String?,
subRule: freezed == subRule
? _value.subRule
: subRule // ignore: cast_nullable_to_non_nullable
as String?,
noResolve: null == noResolve
? _value.noResolve
: noResolve // ignore: cast_nullable_to_non_nullable
as bool,
src: null == src
? _value.src
: src // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
/// @nodoc
abstract class _$$ParsedRuleImplCopyWith<$Res>
implements $ParsedRuleCopyWith<$Res> {
factory _$$ParsedRuleImplCopyWith(
_$ParsedRuleImpl value, $Res Function(_$ParsedRuleImpl) then) =
__$$ParsedRuleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{RuleAction ruleAction,
String? content,
String? ruleTarget,
String? ruleProvider,
String? subRule,
bool noResolve,
bool src});
}
/// @nodoc
class __$$ParsedRuleImplCopyWithImpl<$Res>
extends _$ParsedRuleCopyWithImpl<$Res, _$ParsedRuleImpl>
implements _$$ParsedRuleImplCopyWith<$Res> {
__$$ParsedRuleImplCopyWithImpl(
_$ParsedRuleImpl _value, $Res Function(_$ParsedRuleImpl) _then)
: super(_value, _then);
/// Create a copy of ParsedRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ruleAction = null,
Object? content = freezed,
Object? ruleTarget = freezed,
Object? ruleProvider = freezed,
Object? subRule = freezed,
Object? noResolve = null,
Object? src = null,
}) {
return _then(_$ParsedRuleImpl(
ruleAction: null == ruleAction
? _value.ruleAction
: ruleAction // ignore: cast_nullable_to_non_nullable
as RuleAction,
content: freezed == content
? _value.content
: content // ignore: cast_nullable_to_non_nullable
as String?,
ruleTarget: freezed == ruleTarget
? _value.ruleTarget
: ruleTarget // ignore: cast_nullable_to_non_nullable
as String?,
ruleProvider: freezed == ruleProvider
? _value.ruleProvider
: ruleProvider // ignore: cast_nullable_to_non_nullable
as String?,
subRule: freezed == subRule
? _value.subRule
: subRule // ignore: cast_nullable_to_non_nullable
as String?,
noResolve: null == noResolve
? _value.noResolve
: noResolve // ignore: cast_nullable_to_non_nullable
as bool,
src: null == src
? _value.src
: src // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
class _$ParsedRuleImpl implements _ParsedRule {
const _$ParsedRuleImpl(
{required this.ruleAction,
this.content,
this.ruleTarget,
this.ruleProvider,
this.subRule,
this.noResolve = false,
this.src = false});
@override
final RuleAction ruleAction;
@override
final String? content;
@override
final String? ruleTarget;
@override
final String? ruleProvider;
@override
final String? subRule;
@override
@JsonKey()
final bool noResolve;
@override
@JsonKey()
final bool src;
@override
String toString() {
return 'ParsedRule(ruleAction: $ruleAction, content: $content, ruleTarget: $ruleTarget, ruleProvider: $ruleProvider, subRule: $subRule, noResolve: $noResolve, src: $src)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ParsedRuleImpl &&
(identical(other.ruleAction, ruleAction) ||
other.ruleAction == ruleAction) &&
(identical(other.content, content) || other.content == content) &&
(identical(other.ruleTarget, ruleTarget) ||
other.ruleTarget == ruleTarget) &&
(identical(other.ruleProvider, ruleProvider) ||
other.ruleProvider == ruleProvider) &&
(identical(other.subRule, subRule) || other.subRule == subRule) &&
(identical(other.noResolve, noResolve) ||
other.noResolve == noResolve) &&
(identical(other.src, src) || other.src == src));
}
@override
int get hashCode => Object.hash(runtimeType, ruleAction, content, ruleTarget,
ruleProvider, subRule, noResolve, src);
/// Create a copy of ParsedRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ParsedRuleImplCopyWith<_$ParsedRuleImpl> get copyWith =>
__$$ParsedRuleImplCopyWithImpl<_$ParsedRuleImpl>(this, _$identity);
}
abstract class _ParsedRule implements ParsedRule {
const factory _ParsedRule(
{required final RuleAction ruleAction,
final String? content,
final String? ruleTarget,
final String? ruleProvider,
final String? subRule,
final bool noResolve,
final bool src}) = _$ParsedRuleImpl;
@override
RuleAction get ruleAction;
@override
String? get content;
@override
String? get ruleTarget;
@override
String? get ruleProvider;
@override
String? get subRule;
@override
bool get noResolve;
@override
bool get src;
/// Create a copy of ParsedRule
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ParsedRuleImplCopyWith<_$ParsedRuleImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Rule _$RuleFromJson(Map<String, dynamic> json) {
return _Rule.fromJson(json);
}
/// @nodoc
mixin _$Rule {
String get id => throw _privateConstructorUsedError;
String get value => throw _privateConstructorUsedError;
/// Serializes this Rule to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Rule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$RuleCopyWith<Rule> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RuleCopyWith<$Res> {
factory $RuleCopyWith(Rule value, $Res Function(Rule) then) =
_$RuleCopyWithImpl<$Res, Rule>;
@useResult
$Res call({String id, String value});
}
/// @nodoc
class _$RuleCopyWithImpl<$Res, $Val extends Rule>
implements $RuleCopyWith<$Res> {
_$RuleCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Rule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? value = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
value: null == value
? _value.value
: value // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$RuleImplCopyWith<$Res> implements $RuleCopyWith<$Res> {
factory _$$RuleImplCopyWith(
_$RuleImpl value, $Res Function(_$RuleImpl) then) =
__$$RuleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String id, String value});
}
/// @nodoc
class __$$RuleImplCopyWithImpl<$Res>
extends _$RuleCopyWithImpl<$Res, _$RuleImpl>
implements _$$RuleImplCopyWith<$Res> {
__$$RuleImplCopyWithImpl(_$RuleImpl _value, $Res Function(_$RuleImpl) _then)
: super(_value, _then);
/// Create a copy of Rule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? value = null,
}) {
return _then(_$RuleImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
value: null == value
? _value.value
: value // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RuleImpl implements _Rule {
const _$RuleImpl({required this.id, required this.value});
factory _$RuleImpl.fromJson(Map<String, dynamic> json) =>
_$$RuleImplFromJson(json);
@override
final String id;
@override
final String value;
@override
String toString() {
return 'Rule(id: $id, value: $value)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RuleImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.value, value) || other.value == value));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, value);
/// Create a copy of Rule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$RuleImplCopyWith<_$RuleImpl> get copyWith =>
__$$RuleImplCopyWithImpl<_$RuleImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$RuleImplToJson(
this,
);
}
}
abstract class _Rule implements Rule {
const factory _Rule({required final String id, required final String value}) =
_$RuleImpl;
factory _Rule.fromJson(Map<String, dynamic> json) = _$RuleImpl.fromJson;
@override
String get id;
@override
String get value;
/// Create a copy of Rule
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$RuleImplCopyWith<_$RuleImpl> get copyWith =>
throw _privateConstructorUsedError;
}
SubRule _$SubRuleFromJson(Map<String, dynamic> json) {
return _SubRule.fromJson(json);
}
/// @nodoc
mixin _$SubRule {
String get name => throw _privateConstructorUsedError;
/// Serializes this SubRule to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SubRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SubRuleCopyWith<SubRule> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SubRuleCopyWith<$Res> {
factory $SubRuleCopyWith(SubRule value, $Res Function(SubRule) then) =
_$SubRuleCopyWithImpl<$Res, SubRule>;
@useResult
$Res call({String name});
}
/// @nodoc
class _$SubRuleCopyWithImpl<$Res, $Val extends SubRule>
implements $SubRuleCopyWith<$Res> {
_$SubRuleCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SubRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$SubRuleImplCopyWith<$Res> implements $SubRuleCopyWith<$Res> {
factory _$$SubRuleImplCopyWith(
_$SubRuleImpl value, $Res Function(_$SubRuleImpl) then) =
__$$SubRuleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String name});
}
/// @nodoc
class __$$SubRuleImplCopyWithImpl<$Res>
extends _$SubRuleCopyWithImpl<$Res, _$SubRuleImpl>
implements _$$SubRuleImplCopyWith<$Res> {
__$$SubRuleImplCopyWithImpl(
_$SubRuleImpl _value, $Res Function(_$SubRuleImpl) _then)
: super(_value, _then);
/// Create a copy of SubRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
}) {
return _then(_$SubRuleImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SubRuleImpl implements _SubRule {
const _$SubRuleImpl({required this.name});
factory _$SubRuleImpl.fromJson(Map<String, dynamic> json) =>
_$$SubRuleImplFromJson(json);
@override
final String name;
@override
String toString() {
return 'SubRule(name: $name)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SubRuleImpl &&
(identical(other.name, name) || other.name == name));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, name);
/// Create a copy of SubRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SubRuleImplCopyWith<_$SubRuleImpl> get copyWith =>
__$$SubRuleImplCopyWithImpl<_$SubRuleImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SubRuleImplToJson(
this,
);
}
}
abstract class _SubRule implements SubRule {
const factory _SubRule({required final String name}) = _$SubRuleImpl;
factory _SubRule.fromJson(Map<String, dynamic> json) = _$SubRuleImpl.fromJson;
@override
String get name;
/// Create a copy of SubRule
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SubRuleImplCopyWith<_$SubRuleImpl> get copyWith =>
throw _privateConstructorUsedError;
}
ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
return _ClashConfigSnippet.fromJson(json);
}
@@ -1842,7 +2557,12 @@ ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
mixin _$ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
List<String> get rule => throw _privateConstructorUsedError;
@JsonKey(fromJson: _genRule)
List<Rule> get rule => throw _privateConstructorUsedError;
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> get ruleProvider => throw _privateConstructorUsedError;
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
List<SubRule> get subRules => throw _privateConstructorUsedError;
/// Serializes this ClashConfigSnippet to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1862,7 +2582,11 @@ abstract class $ClashConfigSnippetCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rule});
@JsonKey(fromJson: _genRule) List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
List<SubRule> subRules});
}
/// @nodoc
@@ -1882,6 +2606,8 @@ class _$ClashConfigSnippetCopyWithImpl<$Res, $Val extends ClashConfigSnippet>
$Res call({
Object? proxyGroups = null,
Object? rule = null,
Object? ruleProvider = null,
Object? subRules = null,
}) {
return _then(_value.copyWith(
proxyGroups: null == proxyGroups
@@ -1891,7 +2617,15 @@ class _$ClashConfigSnippetCopyWithImpl<$Res, $Val extends ClashConfigSnippet>
rule: null == rule
? _value.rule
: rule // ignore: cast_nullable_to_non_nullable
as List<String>,
as List<Rule>,
ruleProvider: null == ruleProvider
? _value.ruleProvider
: ruleProvider // ignore: cast_nullable_to_non_nullable
as List<RuleProvider>,
subRules: null == subRules
? _value.subRules
: subRules // ignore: cast_nullable_to_non_nullable
as List<SubRule>,
) as $Val);
}
}
@@ -1906,7 +2640,11 @@ abstract class _$$ClashConfigSnippetImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
List<String> rule});
@JsonKey(fromJson: _genRule) List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
List<SubRule> subRules});
}
/// @nodoc
@@ -1924,6 +2662,8 @@ class __$$ClashConfigSnippetImplCopyWithImpl<$Res>
$Res call({
Object? proxyGroups = null,
Object? rule = null,
Object? ruleProvider = null,
Object? subRules = null,
}) {
return _then(_$ClashConfigSnippetImpl(
proxyGroups: null == proxyGroups
@@ -1933,7 +2673,15 @@ class __$$ClashConfigSnippetImplCopyWithImpl<$Res>
rule: null == rule
? _value._rule
: rule // ignore: cast_nullable_to_non_nullable
as List<String>,
as List<Rule>,
ruleProvider: null == ruleProvider
? _value._ruleProvider
: ruleProvider // ignore: cast_nullable_to_non_nullable
as List<RuleProvider>,
subRules: null == subRules
? _value._subRules
: subRules // ignore: cast_nullable_to_non_nullable
as List<SubRule>,
));
}
}
@@ -1944,9 +2692,15 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
const _$ClashConfigSnippetImpl(
{@JsonKey(name: "proxy-groups")
final List<ProxyGroup> proxyGroups = const [],
final List<String> rule = const []})
@JsonKey(fromJson: _genRule) final List<Rule> rule = const [],
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider = const [],
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
final List<SubRule> subRules = const []})
: _proxyGroups = proxyGroups,
_rule = rule;
_rule = rule,
_ruleProvider = ruleProvider,
_subRules = subRules;
factory _$ClashConfigSnippetImpl.fromJson(Map<String, dynamic> json) =>
_$$ClashConfigSnippetImplFromJson(json);
@@ -1960,18 +2714,36 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
return EqualUnmodifiableListView(_proxyGroups);
}
final List<String> _rule;
final List<Rule> _rule;
@override
@JsonKey()
List<String> get rule {
@JsonKey(fromJson: _genRule)
List<Rule> get rule {
if (_rule is EqualUnmodifiableListView) return _rule;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_rule);
}
final List<RuleProvider> _ruleProvider;
@override
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> get ruleProvider {
if (_ruleProvider is EqualUnmodifiableListView) return _ruleProvider;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ruleProvider);
}
final List<SubRule> _subRules;
@override
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
List<SubRule> get subRules {
if (_subRules is EqualUnmodifiableListView) return _subRules;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_subRules);
}
@override
String toString() {
return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule)';
return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule, ruleProvider: $ruleProvider, subRules: $subRules)';
}
@override
@@ -1981,7 +2753,10 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
other is _$ClashConfigSnippetImpl &&
const DeepCollectionEquality()
.equals(other._proxyGroups, _proxyGroups) &&
const DeepCollectionEquality().equals(other._rule, _rule));
const DeepCollectionEquality().equals(other._rule, _rule) &&
const DeepCollectionEquality()
.equals(other._ruleProvider, _ruleProvider) &&
const DeepCollectionEquality().equals(other._subRules, _subRules));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -1989,7 +2764,9 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_proxyGroups),
const DeepCollectionEquality().hash(_rule));
const DeepCollectionEquality().hash(_rule),
const DeepCollectionEquality().hash(_ruleProvider),
const DeepCollectionEquality().hash(_subRules));
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.
@@ -2011,7 +2788,11 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
abstract class _ClashConfigSnippet implements ClashConfigSnippet {
const factory _ClashConfigSnippet(
{@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
final List<String> rule}) = _$ClashConfigSnippetImpl;
@JsonKey(fromJson: _genRule) final List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
final List<SubRule> subRules}) = _$ClashConfigSnippetImpl;
factory _ClashConfigSnippet.fromJson(Map<String, dynamic> json) =
_$ClashConfigSnippetImpl.fromJson;
@@ -2020,7 +2801,14 @@ abstract class _ClashConfigSnippet implements ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups;
@override
List<String> get rule;
@JsonKey(fromJson: _genRule)
List<Rule> get rule;
@override
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> get ruleProvider;
@override
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
List<SubRule> get subRules;
/// Create a copy of ClashConfigSnippet
/// with the given fields replaced by the non-null parameter values.

View File

@@ -21,7 +21,7 @@ _$ProxyGroupImpl _$$ProxyGroupImplFromJson(Map<String, dynamic> json) =>
filter: json['filter'] as String?,
excludeFilter: json['expected-filter'] as String?,
excludeType: json['exclude-type'] as String?,
expectedStatus: (json['expected-status'] as num?)?.toInt(),
expectedStatus: json['expected-status'],
hidden: json['hidden'] as bool?,
icon: json['icon'] as String?,
);
@@ -53,6 +53,16 @@ const _$GroupTypeEnumMap = {
GroupType.Relay: 'Relay',
};
_$RuleProviderImpl _$$RuleProviderImplFromJson(Map<String, dynamic> json) =>
_$RuleProviderImpl(
name: json['name'] as String,
);
Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
<String, dynamic>{
'name': instance.name,
};
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName,
@@ -206,6 +216,27 @@ Map<String, dynamic> _$$GeoXUrlImplToJson(_$GeoXUrlImpl instance) =>
'geosite': instance.geosite,
};
_$RuleImpl _$$RuleImplFromJson(Map<String, dynamic> json) => _$RuleImpl(
id: json['id'] as String,
value: json['value'] as String,
);
Map<String, dynamic> _$$RuleImplToJson(_$RuleImpl instance) =>
<String, dynamic>{
'id': instance.id,
'value': instance.value,
};
_$SubRuleImpl _$$SubRuleImplFromJson(Map<String, dynamic> json) =>
_$SubRuleImpl(
name: json['name'] as String,
);
Map<String, dynamic> _$$SubRuleImplToJson(_$SubRuleImpl instance) =>
<String, dynamic>{
'name': instance.name,
};
_$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
Map<String, dynamic> json) =>
_$ClashConfigSnippetImpl(
@@ -213,9 +244,13 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule:
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
const [],
rule: json['rule'] == null ? const [] : _genRule(json['rule'] as List?),
ruleProvider: json['rule-providers'] == null
? const []
: _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),
subRules: json['sub-rules'] == null
? const []
: _genSubRules(json['sub-rules'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
@@ -223,6 +258,8 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
<String, dynamic>{
'proxy-groups': instance.proxyGroups,
'rule': instance.rule,
'rule-providers': instance.ruleProvider,
'sub-rules': instance.subRules,
};
_$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>

View File

@@ -912,7 +912,7 @@ class __$$WindowPropsImplCopyWithImpl<$Res>
@JsonSerializable()
class _$WindowPropsImpl implements _WindowProps {
const _$WindowPropsImpl(
{this.width = 900, this.height = 600, this.top, this.left});
{this.width = 750, this.height = 600, this.top, this.left});
factory _$WindowPropsImpl.fromJson(Map<String, dynamic> json) =>
_$$WindowPropsImplFromJson(json);

View File

@@ -102,7 +102,7 @@ const _$AccessSortTypeEnumMap = {
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
_$WindowPropsImpl(
width: (json['width'] as num?)?.toDouble() ?? 900,
width: (json['width'] as num?)?.toDouble() ?? 750,
height: (json['height'] as num?)?.toDouble() ?? 600,
top: (json['top'] as num?)?.toDouble(),
left: (json['left'] as num?)?.toDouble(),

View File

@@ -667,6 +667,8 @@ mixin _$ConfigExtendedParams {
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
@JsonKey(name: "override-dns")
bool get overrideDns => throw _privateConstructorUsedError;
@JsonKey(name: "override-rule")
bool get overrideRule => throw _privateConstructorUsedError;
@JsonKey(name: "test-url")
String get testUrl => throw _privateConstructorUsedError;
@@ -690,6 +692,7 @@ abstract class $ConfigExtendedParamsCopyWith<$Res> {
{@JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
@JsonKey(name: "override-dns") bool overrideDns,
@JsonKey(name: "override-rule") bool overrideRule,
@JsonKey(name: "test-url") String testUrl});
}
@@ -712,6 +715,7 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
Object? isPatch = null,
Object? selectedMap = null,
Object? overrideDns = null,
Object? overrideRule = null,
Object? testUrl = null,
}) {
return _then(_value.copyWith(
@@ -727,6 +731,10 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
? _value.overrideDns
: overrideDns // ignore: cast_nullable_to_non_nullable
as bool,
overrideRule: null == overrideRule
? _value.overrideRule
: overrideRule // ignore: cast_nullable_to_non_nullable
as bool,
testUrl: null == testUrl
? _value.testUrl
: testUrl // ignore: cast_nullable_to_non_nullable
@@ -747,6 +755,7 @@ abstract class _$$ConfigExtendedParamsImplCopyWith<$Res>
{@JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
@JsonKey(name: "override-dns") bool overrideDns,
@JsonKey(name: "override-rule") bool overrideRule,
@JsonKey(name: "test-url") String testUrl});
}
@@ -766,6 +775,7 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
Object? isPatch = null,
Object? selectedMap = null,
Object? overrideDns = null,
Object? overrideRule = null,
Object? testUrl = null,
}) {
return _then(_$ConfigExtendedParamsImpl(
@@ -781,6 +791,10 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
? _value.overrideDns
: overrideDns // ignore: cast_nullable_to_non_nullable
as bool,
overrideRule: null == overrideRule
? _value.overrideRule
: overrideRule // ignore: cast_nullable_to_non_nullable
as bool,
testUrl: null == testUrl
? _value.testUrl
: testUrl // ignore: cast_nullable_to_non_nullable
@@ -797,6 +811,7 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
@JsonKey(name: "selected-map")
required final Map<String, String> selectedMap,
@JsonKey(name: "override-dns") required this.overrideDns,
@JsonKey(name: "override-rule") required this.overrideRule,
@JsonKey(name: "test-url") required this.testUrl})
: _selectedMap = selectedMap;
@@ -819,12 +834,15 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
@JsonKey(name: "override-dns")
final bool overrideDns;
@override
@JsonKey(name: "override-rule")
final bool overrideRule;
@override
@JsonKey(name: "test-url")
final String testUrl;
@override
String toString() {
return 'ConfigExtendedParams(isPatch: $isPatch, selectedMap: $selectedMap, overrideDns: $overrideDns, testUrl: $testUrl)';
return 'ConfigExtendedParams(isPatch: $isPatch, selectedMap: $selectedMap, overrideDns: $overrideDns, overrideRule: $overrideRule, testUrl: $testUrl)';
}
@override
@@ -837,13 +855,20 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
.equals(other._selectedMap, _selectedMap) &&
(identical(other.overrideDns, overrideDns) ||
other.overrideDns == overrideDns) &&
(identical(other.overrideRule, overrideRule) ||
other.overrideRule == overrideRule) &&
(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, isPatch,
const DeepCollectionEquality().hash(_selectedMap), overrideDns, testUrl);
int get hashCode => Object.hash(
runtimeType,
isPatch,
const DeepCollectionEquality().hash(_selectedMap),
overrideDns,
overrideRule,
testUrl);
/// Create a copy of ConfigExtendedParams
/// with the given fields replaced by the non-null parameter values.
@@ -869,6 +894,7 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
@JsonKey(name: "selected-map")
required final Map<String, String> selectedMap,
@JsonKey(name: "override-dns") required final bool overrideDns,
@JsonKey(name: "override-rule") required final bool overrideRule,
@JsonKey(name: "test-url") required final String testUrl}) =
_$ConfigExtendedParamsImpl;
@@ -885,6 +911,9 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
@JsonKey(name: "override-dns")
bool get overrideDns;
@override
@JsonKey(name: "override-rule")
bool get overrideRule;
@override
@JsonKey(name: "test-url")
String get testUrl;

View File

@@ -69,6 +69,7 @@ _$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson(
isPatch: json['is-patch'] as bool,
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
overrideDns: json['override-dns'] as bool,
overrideRule: json['override-rule'] as bool,
testUrl: json['test-url'] as String,
);
@@ -78,6 +79,7 @@ Map<String, dynamic> _$$ConfigExtendedParamsImplToJson(
'is-patch': instance.isPatch,
'selected-map': instance.selectedMap,
'override-dns': instance.overrideDns,
'override-rule': instance.overrideRule,
'test-url': instance.testUrl,
};

View File

@@ -238,6 +238,7 @@ mixin _$Profile {
bool get autoUpdate => throw _privateConstructorUsedError;
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
Set<String> get unfoldSet => throw _privateConstructorUsedError;
OverrideData get overrideData => throw _privateConstructorUsedError;
@JsonKey(includeToJson: false, includeFromJson: false)
bool get isUpdating => throw _privateConstructorUsedError;
@@ -266,9 +267,11 @@ abstract class $ProfileCopyWith<$Res> {
bool autoUpdate,
Map<String, String> selectedMap,
Set<String> unfoldSet,
OverrideData overrideData,
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
$OverrideDataCopyWith<$Res> get overrideData;
}
/// @nodoc
@@ -296,6 +299,7 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
Object? autoUpdate = null,
Object? selectedMap = null,
Object? unfoldSet = null,
Object? overrideData = null,
Object? isUpdating = null,
}) {
return _then(_value.copyWith(
@@ -339,6 +343,10 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
? _value.unfoldSet
: unfoldSet // ignore: cast_nullable_to_non_nullable
as Set<String>,
overrideData: null == overrideData
? _value.overrideData
: overrideData // ignore: cast_nullable_to_non_nullable
as OverrideData,
isUpdating: null == isUpdating
? _value.isUpdating
: isUpdating // ignore: cast_nullable_to_non_nullable
@@ -359,6 +367,16 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
return _then(_value.copyWith(subscriptionInfo: value) as $Val);
});
}
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$OverrideDataCopyWith<$Res> get overrideData {
return $OverrideDataCopyWith<$Res>(_value.overrideData, (value) {
return _then(_value.copyWith(overrideData: value) as $Val);
});
}
}
/// @nodoc
@@ -379,10 +397,13 @@ abstract class _$$ProfileImplCopyWith<$Res> implements $ProfileCopyWith<$Res> {
bool autoUpdate,
Map<String, String> selectedMap,
Set<String> unfoldSet,
OverrideData overrideData,
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
@override
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
@override
$OverrideDataCopyWith<$Res> get overrideData;
}
/// @nodoc
@@ -408,6 +429,7 @@ class __$$ProfileImplCopyWithImpl<$Res>
Object? autoUpdate = null,
Object? selectedMap = null,
Object? unfoldSet = null,
Object? overrideData = null,
Object? isUpdating = null,
}) {
return _then(_$ProfileImpl(
@@ -451,6 +473,10 @@ class __$$ProfileImplCopyWithImpl<$Res>
? _value._unfoldSet
: unfoldSet // ignore: cast_nullable_to_non_nullable
as Set<String>,
overrideData: null == overrideData
? _value.overrideData
: overrideData // ignore: cast_nullable_to_non_nullable
as OverrideData,
isUpdating: null == isUpdating
? _value.isUpdating
: isUpdating // ignore: cast_nullable_to_non_nullable
@@ -473,6 +499,7 @@ class _$ProfileImpl implements _Profile {
this.autoUpdate = true,
final Map<String, String> selectedMap = const {},
final Set<String> unfoldSet = const {},
this.overrideData = const OverrideData(),
@JsonKey(includeToJson: false, includeFromJson: false)
this.isUpdating = false})
: _selectedMap = selectedMap,
@@ -517,13 +544,16 @@ class _$ProfileImpl implements _Profile {
return EqualUnmodifiableSetView(_unfoldSet);
}
@override
@JsonKey()
final OverrideData overrideData;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
final bool isUpdating;
@override
String toString() {
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, isUpdating: $isUpdating)';
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, overrideData: $overrideData, isUpdating: $isUpdating)';
}
@override
@@ -548,6 +578,8 @@ class _$ProfileImpl implements _Profile {
.equals(other._selectedMap, _selectedMap) &&
const DeepCollectionEquality()
.equals(other._unfoldSet, _unfoldSet) &&
(identical(other.overrideData, overrideData) ||
other.overrideData == overrideData) &&
(identical(other.isUpdating, isUpdating) ||
other.isUpdating == isUpdating));
}
@@ -566,6 +598,7 @@ class _$ProfileImpl implements _Profile {
autoUpdate,
const DeepCollectionEquality().hash(_selectedMap),
const DeepCollectionEquality().hash(_unfoldSet),
overrideData,
isUpdating);
/// Create a copy of Profile
@@ -596,6 +629,7 @@ abstract class _Profile implements Profile {
final bool autoUpdate,
final Map<String, String> selectedMap,
final Set<String> unfoldSet,
final OverrideData overrideData,
@JsonKey(includeToJson: false, includeFromJson: false)
final bool isUpdating}) = _$ProfileImpl;
@@ -622,6 +656,8 @@ abstract class _Profile implements Profile {
@override
Set<String> get unfoldSet;
@override
OverrideData get overrideData;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
bool get isUpdating;
@@ -632,3 +668,398 @@ abstract class _Profile implements Profile {
_$$ProfileImplCopyWith<_$ProfileImpl> get copyWith =>
throw _privateConstructorUsedError;
}
OverrideData _$OverrideDataFromJson(Map<String, dynamic> json) {
return _OverrideData.fromJson(json);
}
/// @nodoc
mixin _$OverrideData {
bool get enable => throw _privateConstructorUsedError;
OverrideRule get rule => throw _privateConstructorUsedError;
/// Serializes this OverrideData to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$OverrideDataCopyWith<OverrideData> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $OverrideDataCopyWith<$Res> {
factory $OverrideDataCopyWith(
OverrideData value, $Res Function(OverrideData) then) =
_$OverrideDataCopyWithImpl<$Res, OverrideData>;
@useResult
$Res call({bool enable, OverrideRule rule});
$OverrideRuleCopyWith<$Res> get rule;
}
/// @nodoc
class _$OverrideDataCopyWithImpl<$Res, $Val extends OverrideData>
implements $OverrideDataCopyWith<$Res> {
_$OverrideDataCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? rule = null,
}) {
return _then(_value.copyWith(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
rule: null == rule
? _value.rule
: rule // ignore: cast_nullable_to_non_nullable
as OverrideRule,
) as $Val);
}
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$OverrideRuleCopyWith<$Res> get rule {
return $OverrideRuleCopyWith<$Res>(_value.rule, (value) {
return _then(_value.copyWith(rule: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$OverrideDataImplCopyWith<$Res>
implements $OverrideDataCopyWith<$Res> {
factory _$$OverrideDataImplCopyWith(
_$OverrideDataImpl value, $Res Function(_$OverrideDataImpl) then) =
__$$OverrideDataImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool enable, OverrideRule rule});
@override
$OverrideRuleCopyWith<$Res> get rule;
}
/// @nodoc
class __$$OverrideDataImplCopyWithImpl<$Res>
extends _$OverrideDataCopyWithImpl<$Res, _$OverrideDataImpl>
implements _$$OverrideDataImplCopyWith<$Res> {
__$$OverrideDataImplCopyWithImpl(
_$OverrideDataImpl _value, $Res Function(_$OverrideDataImpl) _then)
: super(_value, _then);
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? rule = null,
}) {
return _then(_$OverrideDataImpl(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
rule: null == rule
? _value.rule
: rule // ignore: cast_nullable_to_non_nullable
as OverrideRule,
));
}
}
/// @nodoc
@JsonSerializable()
class _$OverrideDataImpl implements _OverrideData {
const _$OverrideDataImpl(
{this.enable = false, this.rule = const OverrideRule()});
factory _$OverrideDataImpl.fromJson(Map<String, dynamic> json) =>
_$$OverrideDataImplFromJson(json);
@override
@JsonKey()
final bool enable;
@override
@JsonKey()
final OverrideRule rule;
@override
String toString() {
return 'OverrideData(enable: $enable, rule: $rule)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$OverrideDataImpl &&
(identical(other.enable, enable) || other.enable == enable) &&
(identical(other.rule, rule) || other.rule == rule));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, enable, rule);
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$OverrideDataImplCopyWith<_$OverrideDataImpl> get copyWith =>
__$$OverrideDataImplCopyWithImpl<_$OverrideDataImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$OverrideDataImplToJson(
this,
);
}
}
abstract class _OverrideData implements OverrideData {
const factory _OverrideData({final bool enable, final OverrideRule rule}) =
_$OverrideDataImpl;
factory _OverrideData.fromJson(Map<String, dynamic> json) =
_$OverrideDataImpl.fromJson;
@override
bool get enable;
@override
OverrideRule get rule;
/// Create a copy of OverrideData
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$OverrideDataImplCopyWith<_$OverrideDataImpl> get copyWith =>
throw _privateConstructorUsedError;
}
OverrideRule _$OverrideRuleFromJson(Map<String, dynamic> json) {
return _OverrideRule.fromJson(json);
}
/// @nodoc
mixin _$OverrideRule {
OverrideRuleType get type => throw _privateConstructorUsedError;
List<Rule> get overrideRules => throw _privateConstructorUsedError;
List<Rule> get addedRules => throw _privateConstructorUsedError;
/// Serializes this OverrideRule to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of OverrideRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$OverrideRuleCopyWith<OverrideRule> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $OverrideRuleCopyWith<$Res> {
factory $OverrideRuleCopyWith(
OverrideRule value, $Res Function(OverrideRule) then) =
_$OverrideRuleCopyWithImpl<$Res, OverrideRule>;
@useResult
$Res call(
{OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules});
}
/// @nodoc
class _$OverrideRuleCopyWithImpl<$Res, $Val extends OverrideRule>
implements $OverrideRuleCopyWith<$Res> {
_$OverrideRuleCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of OverrideRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
Object? overrideRules = null,
Object? addedRules = null,
}) {
return _then(_value.copyWith(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as OverrideRuleType,
overrideRules: null == overrideRules
? _value.overrideRules
: overrideRules // ignore: cast_nullable_to_non_nullable
as List<Rule>,
addedRules: null == addedRules
? _value.addedRules
: addedRules // ignore: cast_nullable_to_non_nullable
as List<Rule>,
) as $Val);
}
}
/// @nodoc
abstract class _$$OverrideRuleImplCopyWith<$Res>
implements $OverrideRuleCopyWith<$Res> {
factory _$$OverrideRuleImplCopyWith(
_$OverrideRuleImpl value, $Res Function(_$OverrideRuleImpl) then) =
__$$OverrideRuleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules});
}
/// @nodoc
class __$$OverrideRuleImplCopyWithImpl<$Res>
extends _$OverrideRuleCopyWithImpl<$Res, _$OverrideRuleImpl>
implements _$$OverrideRuleImplCopyWith<$Res> {
__$$OverrideRuleImplCopyWithImpl(
_$OverrideRuleImpl _value, $Res Function(_$OverrideRuleImpl) _then)
: super(_value, _then);
/// Create a copy of OverrideRule
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
Object? overrideRules = null,
Object? addedRules = null,
}) {
return _then(_$OverrideRuleImpl(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as OverrideRuleType,
overrideRules: null == overrideRules
? _value._overrideRules
: overrideRules // ignore: cast_nullable_to_non_nullable
as List<Rule>,
addedRules: null == addedRules
? _value._addedRules
: addedRules // ignore: cast_nullable_to_non_nullable
as List<Rule>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$OverrideRuleImpl implements _OverrideRule {
const _$OverrideRuleImpl(
{this.type = OverrideRuleType.added,
final List<Rule> overrideRules = const [],
final List<Rule> addedRules = const []})
: _overrideRules = overrideRules,
_addedRules = addedRules;
factory _$OverrideRuleImpl.fromJson(Map<String, dynamic> json) =>
_$$OverrideRuleImplFromJson(json);
@override
@JsonKey()
final OverrideRuleType type;
final List<Rule> _overrideRules;
@override
@JsonKey()
List<Rule> get overrideRules {
if (_overrideRules is EqualUnmodifiableListView) return _overrideRules;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_overrideRules);
}
final List<Rule> _addedRules;
@override
@JsonKey()
List<Rule> get addedRules {
if (_addedRules is EqualUnmodifiableListView) return _addedRules;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_addedRules);
}
@override
String toString() {
return 'OverrideRule(type: $type, overrideRules: $overrideRules, addedRules: $addedRules)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$OverrideRuleImpl &&
(identical(other.type, type) || other.type == type) &&
const DeepCollectionEquality()
.equals(other._overrideRules, _overrideRules) &&
const DeepCollectionEquality()
.equals(other._addedRules, _addedRules));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
type,
const DeepCollectionEquality().hash(_overrideRules),
const DeepCollectionEquality().hash(_addedRules));
/// Create a copy of OverrideRule
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$OverrideRuleImplCopyWith<_$OverrideRuleImpl> get copyWith =>
__$$OverrideRuleImplCopyWithImpl<_$OverrideRuleImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$OverrideRuleImplToJson(
this,
);
}
}
abstract class _OverrideRule implements OverrideRule {
const factory _OverrideRule(
{final OverrideRuleType type,
final List<Rule> overrideRules,
final List<Rule> addedRules}) = _$OverrideRuleImpl;
factory _OverrideRule.fromJson(Map<String, dynamic> json) =
_$OverrideRuleImpl.fromJson;
@override
OverrideRuleType get type;
@override
List<Rule> get overrideRules;
@override
List<Rule> get addedRules;
/// Create a copy of OverrideRule
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$OverrideRuleImplCopyWith<_$OverrideRuleImpl> get copyWith =>
throw _privateConstructorUsedError;
}

Some files were not shown because too many files have changed in this diff Show More