Compare commits

..

52 Commits

Author SHA1 Message Date
chen08209
52d61b15fd Fix the problem that the download of remote resources failed after GeodataMode was turned on, which caused the application to flash back.
Fix edit profile error
2024-06-06 10:15:51 +08:00
chen08209
fea3c14608 Fix quickStart change proxy error 2024-06-05 17:59:53 +08:00
chen08209
84be01a38a Fix core version 2024-06-05 17:59:50 +08:00
chen08209
93da242148 Fix core version 2024-06-05 14:13:54 +08:00
chen08209
bb7e44da30 Update file_picker
Add resources page

Optimize more detail
2024-06-05 13:44:11 +08:00
chen08209
01f1b2d72f Add access selected sorted 2024-06-05 13:43:44 +08:00
chen08209
3074b1299e Fix notification duplicate creation issue
Fix AccessControl click issue
2024-06-03 11:48:50 +08:00
chen08209
bd5470b863 Fix Workflow 2024-05-31 22:34:46 +08:00
chen08209
83fafa4b68 Fix Linux unable to open 2024-05-31 22:12:41 +08:00
chen08209
b7fb969301 Update README.md 3 2024-05-31 15:41:08 +08:00
chen08209
3ef3190785 Create LICENSE 2024-05-31 15:11:15 +08:00
chen08209
b1c763fcfa Update README.md 2 2024-05-31 15:08:51 +08:00
chen08209
9452ffa514 Update README.md 2024-05-31 15:02:18 +08:00
chen08209
c9291e5027 Optimize workFlow 2024-05-31 14:25:15 +08:00
chen08209
3ba86dc9c2 optimize checkUpdate 2024-05-31 10:07:51 +08:00
chen08209
fd3040283c Fix submit error 2024-05-30 17:22:23 +08:00
chen08209
91d30c0f0e add WebDAV
add Auto check updates

Optimize more details
2024-05-30 17:11:15 +08:00
chen08209
c4b470ffaf optimize delayTest 2024-05-17 19:54:57 +08:00
chen08209
9a07c785f2 upgrade flutter version 2024-05-15 20:34:59 +08:00
chen08209
a134c32493 Update kernel
Add import profile via QR code image
2024-05-15 20:21:02 +08:00
chen08209
472cea9037 Add compatibility mode and adapt clash scheme. 2024-05-11 14:10:06 +08:00
chen08209
08d07498b9 update Version 2024-05-07 18:32:21 +08:00
chen08209
d5aa09949a Reconstruction application proxy logic 2024-05-07 18:31:14 +08:00
chen08209
fd1dfe5c60 Fix Tab destroy error 2024-05-06 19:05:27 +08:00
chen08209
9f89fe8b29 Optimize repeat healthcheck 2024-05-06 17:17:26 +08:00
chen08209
78081a12e8 Optimize Direct mode ui 2024-05-06 15:27:37 +08:00
chen08209
6896837f28 Optimize Healthcheck 2024-05-06 14:32:23 +08:00
chen08209
85eb903402 Remove proxies position animation, improve performance
Add Telegram Link
2024-05-06 14:32:22 +08:00
chen08209
9aa9180f1f Update healthcheck policy 2024-05-06 14:32:21 +08:00
chen08209
feb9688a29 New Check URLTest 2024-05-06 14:32:21 +08:00
chen08209
5c71992174 Fix the problem of invalid auto-selection 2024-05-05 16:14:34 +08:00
chen08209
74c3d0ae25 New Async UpdateConfig 2024-05-05 03:13:52 +08:00
chen08209
ecd1bcafd5 add changeProfileDebounce 2024-05-05 03:13:51 +08:00
chen08209
184d2d117a Update Workflow 2024-05-05 03:13:50 +08:00
chen08209
89e6f17794 Fix ChangeProfile block 2024-05-05 03:13:49 +08:00
chen08209
aef50fe0e3 Fix Release Message Error 2024-05-04 16:14:03 +08:00
chen08209
fc0767ed25 Update Selector 2 2024-05-04 01:14:56 +08:00
chen08209
dbf1724cca Update Version 2024-05-04 00:14:34 +08:00
chen08209
909aa4038e Fix Proxies Select Error 2024-05-04 00:14:07 +08:00
chen08209
2d0a7d8d46 Fix the problem that the proxy group is empty in global mode. 2024-05-03 23:08:06 +08:00
chen08209
ca96cd1d82 Fix the problem that the proxy group is empty in global mode. 2024-05-03 23:07:38 +08:00
chen08209
91ab1e5dac Add ProxyProvider2 2024-05-03 21:48:22 +08:00
chen08209
b3a5f74df8 Add ProxyProvider 2024-05-03 21:28:22 +08:00
chen08209
1f98be8ad8 Update Version 2024-05-03 15:33:46 +08:00
chen08209
453c7c98d0 Update ProxyGroup Sort 2024-05-03 15:33:45 +08:00
chen08209
91faed35c0 Fix Android quickStart VpnService some problems 2024-05-02 00:32:11 +08:00
chen08209
07bbaf6b6f Update version 2024-05-01 23:40:04 +08:00
chen08209
e8feb7c431 Set Android notification low importance 2024-05-01 23:40:03 +08:00
chen08209
4d16820526 Fix the issue that VpnService can't be closed correctly in special cases 2024-05-01 23:40:00 +08:00
chen08209
92294b49c6 Fix the problem that TileService is not destroyed correctly in some cases
Adjust tab animation defaults
2024-05-01 23:39:59 +08:00
chen08209
8a188a37c9 Add Telegram in README_zh_CN.md 2024-05-01 21:52:07 +08:00
chen08209
48af16c265 Add Telegram 2024-05-01 21:50:26 +08:00
48 changed files with 726 additions and 1033 deletions

View File

@@ -3,7 +3,7 @@ name: build
on: on:
push: push:
tags: tags:
- '*' - 'v*'
jobs: jobs:
build: build:
@@ -82,7 +82,6 @@ jobs:
upload-release: upload-release:
if: ${{ !endsWith(github.ref, '-debug') }}
permissions: write-all permissions: write-all
needs: [ build ] needs: [ build ]
runs-on: ubuntu-latest runs-on: ubuntu-latest

2
.gitignore vendored
View File

@@ -31,7 +31,6 @@ migrate_working_dir/
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
/dist/
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
@@ -45,7 +44,6 @@ app.*.map.json
/android/app/release /android/app/release
#libclash #libclash
/libclash/ /libclash/

View File

@@ -34,6 +34,8 @@ on Mobile:
💡 Based on Material You Design, [Surfboard](https://github.com/getsurfboard/surfboard)-like UI 💡 Based on Material You Design, [Surfboard](https://github.com/getsurfboard/surfboard)-like UI
☁️ Supports data sync via WebDAV
✨ Support subscription link, Dark mode ✨ Support subscription link, Dark mode
## Contact ## Contact

View File

@@ -34,6 +34,8 @@ on Mobile:
💡 基本 Material You 设计, 类[Surfboard](https://github.com/getsurfboard/surfboard)用户界面 💡 基本 Material You 设计, 类[Surfboard](https://github.com/getsurfboard/surfboard)用户界面
☁️ 支持通过WebDAV同步数据
✨ 支持一键导入订阅, 深色模式 ✨ 支持一键导入订阅, 深色模式
## Contact ## Contact

View File

@@ -353,6 +353,7 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.RuleProvider = make(map[string]map[string]any) targetConfig.RuleProvider = make(map[string]map[string]any)
generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule) generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
} }
} }
func patchConfig(general *config.General) { func patchConfig(general *config.General) {
@@ -411,6 +412,6 @@ func applyConfig(isPatch bool) {
patchConfig(cfg.General) patchConfig(cfg.General)
} else { } else {
executor.ApplyConfig(cfg, true) executor.ApplyConfig(cfg, true)
hcCompatibleProvider(tunnel.Providers()) healthcheck()
} }
} }

View File

@@ -1,6 +1,6 @@
module core module core
go 1.20 go 1.21.0
replace github.com/metacubex/mihomo => ./Clash.Meta replace github.com/metacubex/mihomo => ./Clash.Meta

View File

@@ -35,16 +35,19 @@ github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIF
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg= github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c/go.mod h1:ETASDWf/FmEb6Ysrtd1QhjNedUU/ZQxBCRLh60bQ/UI=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -60,6 +63,7 @@ github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -69,6 +73,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -83,7 +88,9 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
@@ -125,6 +132,7 @@ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:U
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo= github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0= github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
@@ -146,6 +154,7 @@ github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
@@ -249,6 +258,7 @@ golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
@@ -263,6 +273,7 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -181,8 +181,7 @@ func getTraffic() *C.char {
} }
//export asyncTestDelay //export asyncTestDelay
func asyncTestDelay(s *C.char, port C.longlong) { func asyncTestDelay(s *C.char) {
i := int64(port)
go func() { go func() {
paramsString := C.GoString(s) paramsString := C.GoString(s)
var params = &TestDelayParams{} var params = &TestDelayParams{}
@@ -206,25 +205,26 @@ func asyncTestDelay(s *C.char, port C.longlong) {
Name: params.ProxyName, Name: params.ProxyName,
} }
message := bridge.Message{
Type: bridge.Delay,
Data: delayData,
}
if proxy == nil { if proxy == nil {
delayData.Value = -1 delayData.Value = -1
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return return
} }
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus) delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
if err != nil || delay == 0 { if err != nil || delay == 0 {
delayData.Value = -1 delayData.Value = -1
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return return
} }
delayData.Value = int32(delay) delayData.Value = int32(delay)
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return
}() }()
} }
@@ -374,6 +374,11 @@ func updateExternalProvider(providerName *C.char, providerType *C.char, port C.l
}() }()
} }
//export healthcheck
func healthcheck() {
hcCompatibleProvider(tunnel.Providers())
}
//export initNativeApiBridge //export initNativeApiBridge
func initNativeApiBridge(api unsafe.Pointer, port C.longlong) { func initNativeApiBridge(api unsafe.Pointer, port C.longlong) {
bridge.InitDartApi(api) bridge.InitDartApi(api)

View File

@@ -17,35 +17,36 @@ var tunLock sync.Mutex
var tun *t.Tun var tun *t.Tun
//export startTUN //export startTUN
func startTUN(fd C.int) { func startTUN(fd C.int) bool {
go func() { tunLock.Lock()
tunLock.Lock() defer tunLock.Unlock()
defer tunLock.Unlock()
if tun != nil { if tun != nil {
tun.Close() tun.Close()
tun = nil tun = nil
} }
f := int(fd) f := int(fd)
gateway := "172.16.0.1/30" gateway := "172.16.0.1/30"
portal := "172.16.0.2" portal := "172.16.0.2"
dns := "0.0.0.0" dns := "0.0.0.0"
tempTun := &t.Tun{Closed: false, Limit: semaphore.NewWeighted(4)} tempTun := &t.Tun{Closed: false, Limit: semaphore.NewWeighted(4)}
closer, err := t.Start(f, gateway, portal, dns) closer, err := t.Start(f, gateway, portal, dns)
applyConfig(true) applyConfig(true)
if err != nil { if err != nil {
log.Errorln("startTUN error: %v", err) log.Errorln("startTUN error: %v", err)
tempTun.Close() tempTun.Close()
} return false
}
tempTun.Closer = closer tempTun.Closer = closer
tun = tempTun tun = tempTun
}()
return true
} }
//export updateMarkSocketPort //export updateMarkSocketPort
@@ -60,16 +61,14 @@ func updateMarkSocketPort(markSocketPort C.longlong) bool {
//export stopTun //export stopTun
func stopTun() { func stopTun() {
go func() { tunLock.Lock()
tunLock.Lock() defer tunLock.Unlock()
defer tunLock.Unlock()
if tun != nil { if tun != nil {
tun.Close() tun.Close()
applyConfig(true) applyConfig(true)
tun = nil tun = nil
} }
}()
} }
func init() { func init() {

View File

@@ -160,9 +160,8 @@ class ApplicationState extends State<Application> {
AppLocalizations.delegate.supportedLocales, AppLocalizations.delegate.supportedLocales,
themeMode: state.themeMode, themeMode: state.themeMode,
theme: ThemeData( theme: ThemeData(
useMaterial3: true,
fontFamily: '',
pageTransitionsTheme: _pageTransitionsTheme, pageTransitionsTheme: _pageTransitionsTheme,
useMaterial3: true,
colorScheme: _getAppColorScheme( colorScheme: _getAppColorScheme(
brightness: Brightness.light, brightness: Brightness.light,
systemColorSchemes: systemColorSchemes, systemColorSchemes: systemColorSchemes,
@@ -171,7 +170,6 @@ class ApplicationState extends State<Application> {
), ),
darkTheme: ThemeData( darkTheme: ThemeData(
useMaterial3: true, useMaterial3: true,
fontFamily: '',
pageTransitionsTheme: _pageTransitionsTheme, pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme( colorScheme: _getAppColorScheme(
brightness: Brightness.dark, brightness: Brightness.dark,

View File

@@ -156,36 +156,23 @@ class ClashCore {
return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1; return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1;
} }
Future<Delay> getDelay(String proxyName) { bool delay(String proxyName) {
final delayParams = { final delayParams = {
"proxy-name": proxyName, "proxy-name": proxyName,
"timeout": httpTimeoutDuration.inMilliseconds, "timeout": httpTimeoutDuration.inMilliseconds,
}; };
final completer = Completer<Delay>(); clashFFI.asyncTestDelay(json.encode(delayParams).toNativeUtf8().cast());
final receiver = ReceivePort(); return true;
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(Delay.fromJson(json.decode(message)));
receiver.close();
}
});
clashFFI.asyncTestDelay(
json.encode(delayParams).toNativeUtf8().cast(),
receiver.sendPort.nativePort,
);
Future.delayed(httpTimeoutDuration + moreDuration, () {
receiver.close();
completer.complete(
Delay(name: proxyName, value: -1),
);
});
return completer.future;
} }
clearEffect(String path) { clearEffect(String path) {
clashFFI.clearEffect(path.toNativeUtf8().cast()); clashFFI.clearEffect(path.toNativeUtf8().cast());
} }
healthcheck() {
clashFFI.healthcheck();
}
VersionInfo getVersionInfo() { VersionInfo getVersionInfo() {
final versionInfoRaw = clashFFI.getVersionInfo(); final versionInfoRaw = clashFFI.getVersionInfo();
final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString()); final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString());

View File

@@ -977,20 +977,17 @@ class ClashFFI {
void asyncTestDelay( void asyncTestDelay(
ffi.Pointer<ffi.Char> s, ffi.Pointer<ffi.Char> s,
int port,
) { ) {
return _asyncTestDelay( return _asyncTestDelay(
s, s,
port,
); );
} }
late final _asyncTestDelayPtr = _lookup< late final _asyncTestDelayPtr =
ffi.NativeFunction< _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
ffi.Void Function( 'asyncTestDelay');
ffi.Pointer<ffi.Char>, ffi.LongLong)>>('asyncTestDelay'); late final _asyncTestDelay =
late final _asyncTestDelay = _asyncTestDelayPtr _asyncTestDelayPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
ffi.Pointer<ffi.Char> getVersionInfo() { ffi.Pointer<ffi.Char> getVersionInfo() {
return _getVersionInfo(); return _getVersionInfo();
@@ -1089,6 +1086,14 @@ class ClashFFI {
late final _updateExternalProvider = _updateExternalProviderPtr.asFunction< late final _updateExternalProvider = _updateExternalProviderPtr.asFunction<
void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)>(); void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)>();
void healthcheck() {
return _healthcheck();
}
late final _healthcheckPtr =
_lookup<ffi.NativeFunction<ffi.Void Function()>>('healthcheck');
late final _healthcheck = _healthcheckPtr.asFunction<void Function()>();
void initNativeApiBridge( void initNativeApiBridge(
ffi.Pointer<ffi.Void> api, ffi.Pointer<ffi.Void> api,
int port, int port,

View File

@@ -35,8 +35,4 @@ extension DateTimeExtension on DateTime {
} }
return appLocalizations.just; return appLocalizations.just;
} }
}
String get show {
return toIso8601String().substring(0, 10);
}
}

View File

@@ -23,11 +23,10 @@ class Measure {
double? _bodyMediumHeight; double? _bodyMediumHeight;
double? _bodySmallHeight; double? _bodySmallHeight;
double? _labelSmallHeight; double? _labelSmallHeight;
double? _labelMediumHeight;
double? _titleLargeHeight; double? _titleLargeHeight;
double? _titleMediumHeight;
double get bodyMediumHeight {
double get bodyMediumHeight{
_bodyMediumHeight ??= computeTextSize( _bodyMediumHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -37,7 +36,7 @@ class Measure {
return _bodyMediumHeight!; return _bodyMediumHeight!;
} }
double get bodySmallHeight { double get bodySmallHeight{
_bodySmallHeight ??= computeTextSize( _bodySmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -47,7 +46,7 @@ class Measure {
return _bodySmallHeight!; return _bodySmallHeight!;
} }
double get labelSmallHeight { double get labelSmallHeight{
_labelSmallHeight ??= computeTextSize( _labelSmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -57,17 +56,7 @@ class Measure {
return _labelSmallHeight!; return _labelSmallHeight!;
} }
double get labelMediumHeight { double get titleLargeHeight{
_labelMediumHeight ??= computeTextSize(
Text(
"",
style: context.textTheme.labelMedium,
),
).height;
return _labelMediumHeight!;
}
double get titleLargeHeight {
_titleLargeHeight ??= computeTextSize( _titleLargeHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -76,14 +65,4 @@ class Measure {
).height; ).height;
return _titleLargeHeight!; return _titleLargeHeight!;
} }
double get titleMediumHeight {
_titleMediumHeight ??= computeTextSize(
Text(
"",
style: context.textTheme.titleMedium,
),
).height;
return _titleMediumHeight!;
}
} }

View File

@@ -3,17 +3,18 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart'; import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ip.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
class Request { class Request {
late final Dio _dio; late final Dio _dio;
int? _port; int? _port;
bool _isStart = false;
Request() { Request() {
_dio = Dio( _dio = Dio(
BaseOptions( BaseOptions(
connectTimeout: httpTimeoutDuration,
sendTimeout: httpTimeoutDuration,
receiveTimeout: httpTimeoutDuration,
headers: {"User-Agent": coreName}, headers: {"User-Agent": coreName},
), ),
); );
@@ -25,16 +26,13 @@ class Request {
)); ));
} }
_syncProxy() { _syncProxy(){
final port = globalState.appController.clashConfig.mixedPort; final port = globalState.appController.clashConfig.mixedPort;
final isStart = globalState.appController.appState.isStart; if (_port != port) {
if (_port != port || isStart != _isStart) {
_port = port; _port = port;
_isStart = isStart;
_dio.httpClientAdapter = IOHttpClientAdapter( _dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () { createHttpClient: () {
final client = HttpClient(); final client = HttpClient();
if (!_isStart) return client;
client.findProxy = (url) { client.findProxy = (url) {
return "PROXY localhost:$_port;DIRECT"; return "PROXY localhost:$_port;DIRECT";
}; };
@@ -47,14 +45,14 @@ class Request {
Future<Response> getFileResponseForUrl(String url) async { Future<Response> getFileResponseForUrl(String url) async {
final response = await _dio final response = await _dio
.get( .get(
url, url,
options: Options( options: Options(
responseType: ResponseType.bytes, responseType: ResponseType.bytes,
), ),
) )
.timeout( .timeout(
httpTimeoutDuration * 2, httpTimeoutDuration,
); );
return response; return response;
} }
@@ -75,31 +73,6 @@ class Request {
if (!hasUpdate) return null; if (!hasUpdate) return null;
return data; return data;
} }
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
"https://ipwho.is/": IpInfo.fromIpwhoIsJson,
"https://api.ip.sb/geoip/": IpInfo.fromIpSbJson,
"https://ipapi.co/json/": IpInfo.fromIpApiCoJson,
"https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson,
};
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)
.timeout(
httpTimeoutDuration,
);
if (response.statusCode == 200 && response.data != null) {
return source.value(response.data!);
}
} catch (e) {
continue;
}
}
return null;
}
} }
final request = Request(); final request = Request();

View File

@@ -6,11 +6,6 @@ extension TextStyleExtension on TextStyle {
return copyWith(color: color?.toLight()); return copyWith(color: color?.toLight());
} }
toLighter() {
return copyWith(color: color?.toLighter());
}
toSoftBold() { toSoftBold() {
return copyWith(fontWeight: FontWeight.w500); return copyWith(fontWeight: FontWeight.w500);
} }

View File

@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -40,6 +39,8 @@ class AppController {
updateRunTime, updateRunTime,
updateTraffic, updateTraffic,
]; ];
clearShowProxyDelay();
testShowProxyDelay();
} else { } else {
await globalState.stopSystemProxy(); await globalState.stopSystemProxy();
appState.traffics = []; appState.traffics = [];
@@ -97,7 +98,7 @@ class AppController {
} }
} }
Future<void> updateProfile(String id) async { updateProfile(String id) async {
final profile = config.getCurrentProfileForId(id); final profile = config.getCurrentProfileForId(id);
if (profile != null) { if (profile != null) {
final tempProfile = profile.copyWith(); final tempProfile = profile.copyWith();
@@ -136,25 +137,16 @@ class AppController {
autoUpdateProfiles() async { autoUpdateProfiles() async {
for (final profile in config.profiles) { for (final profile in config.profiles) {
if (!profile.autoUpdate) continue; if (!profile.autoUpdate) return;
final isNotNeedUpdate = profile.lastUpdateDate final isNotNeedUpdate = profile.lastUpdateDate
?.add( ?.add(
profile.autoUpdateDuration, profile.autoUpdateDuration,
) )
.isBeforeNow; .isBeforeNow;
if (isNotNeedUpdate == false || profile.type == ProfileType.file) { if (isNotNeedUpdate == false ||
continue; profile.url == null ||
} profile.url!.isEmpty) continue;
await updateProfile(profile.id); await profile.update();
}
}
updateProfiles() async {
for (final profile in config.profiles) {
if (profile.type == ProfileType.file) {
continue;
}
await updateProfile(profile.id);
} }
} }
@@ -271,6 +263,19 @@ class AppController {
autoCheckUpdate(); autoCheckUpdate();
} }
healthcheck() {
if (globalState.healthcheckLock) return;
for (final delay in appState.delayMap.entries) {
setDelay(
Delay(
name: delay.key,
value: 0,
),
);
}
clashCore.healthcheck();
}
setDelay(Delay delay) { setDelay(Delay delay) {
appState.setDelay(delay); appState.setDelay(delay);
} }
@@ -380,6 +385,22 @@ class AppController {
addProfileFormURL(url); addProfileFormURL(url);
} }
clearShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
appState.setDelay(
Delay(name: showProxyDelay, value: null),
);
}
}
testShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
globalState.updateCurrentDelay(showProxyDelay);
}
}
updateViewWidth(double width) { updateViewWidth(double width) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
appState.viewWidth = width; appState.viewWidth = width;

View File

@@ -61,4 +61,4 @@ enum MessageType { log, tun, delay, process, now }
enum RecoveryOption { enum RecoveryOption {
all, all,
onlyProfiles, onlyProfiles,
} }

View File

@@ -86,25 +86,25 @@ class _ConfigFragmentState extends State<ConfigFragment> {
); );
}, },
), ),
// if (system.isDesktop) if (system.isDesktop)
// Selector<ClashConfig, bool>( Selector<ClashConfig, bool>(
// selector: (_, clashConfig) => clashConfig.tun.enable, selector: (_, clashConfig) => clashConfig.tun.enable,
// builder: (_, tunEnable, __) { builder: (_, tunEnable, __) {
// return ListItem.switchItem( return ListItem.switchItem(
// leading: const Icon(Icons.support), leading: const Icon(Icons.support),
// title: Text(appLocalizations.tun), title: Text(appLocalizations.tun),
// subtitle: Text(appLocalizations.tunDesc), subtitle: Text(appLocalizations.tunDesc),
// delegate: SwitchDelegate( delegate: SwitchDelegate(
// value: tunEnable, value: tunEnable,
// onChanged: (bool value) async { onChanged: (bool value) async {
// final clashConfig = context.read<ClashConfig>(); final clashConfig = context.read<ClashConfig>();
// clashConfig.tun = Tun(enable: value); clashConfig.tun = Tun(enable: value);
// globalState.appController.updateClashConfigDebounce(); globalState.appController.updateClashConfigDebounce();
// }, },
// ), ),
// ); );
// }, },
// ), ),
Selector<Config, bool>( Selector<Config, bool>(
selector: (_, config) => config.isCompatible, selector: (_, config) => config.isCompatible,
builder: (_, isCompatible, __) { builder: (_, isCompatible, __) {

View File

@@ -1,5 +1,3 @@
import 'package:country_flags/country_flags.dart';
import 'package:dio/dio.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
@@ -15,159 +13,118 @@ class NetworkDetection extends StatefulWidget {
} }
class _NetworkDetectionState extends State<NetworkDetection> { class _NetworkDetectionState extends State<NetworkDetection> {
final ipInfoNotifier = ValueNotifier<IpInfo?>(null); Widget _buildDescription(String? currentProxyName, int? delay) {
final timeoutNotifier = ValueNotifier<bool>(false); if (currentProxyName == null) {
bool? _preIsStart; return TooltipText(
CancelToken? cancelToken; text: Text(
appLocalizations.noProxyDesc,
_checkIp( style: Theme.of(context).textTheme.titleMedium?.copyWith(
bool isInit, color: Theme.of(context).colorScheme.secondary,
bool isStart, ),
) async { overflow: TextOverflow.ellipsis,
if (!isInit) return; ),
if (_preIsStart == false && _preIsStart == isStart) return; );
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
} }
ipInfoNotifier.value = null; if (delay == 0 || delay == null) {
final ipInfo = await request.checkIp(cancelToken); return const AspectRatio(
if (ipInfo == null) { aspectRatio: 1,
timeoutNotifier.value = true; child: CircularProgressIndicator(
return; strokeCap: StrokeCap.round,
} else { ),
timeoutNotifier.value = false; );
} }
ipInfoNotifier.value = ipInfo; if (delay > 0) {
} return Row(
mainAxisAlignment: MainAxisAlignment.start,
_checkIpContainer(Widget child) { crossAxisAlignment: CrossAxisAlignment.center,
return Selector2<AppState, Config, CheckIpSelectorState>( children: [
selector: (_, appState, config) { TooltipText(
return CheckIpSelectorState( text: Text(
isInit: appState.isInit, "$delay",
selectedMap: appState.selectedMap, overflow: TextOverflow.ellipsis,
isStart: appState.isStart, maxLines: 1,
); style: context.textTheme.titleLarge
}, ?.copyWith(
builder: (_, state, __) { color: context.colorScheme.primary,
_checkIp(state.isInit, state.isStart); )
return child; .toSoftBold(),
}, ),
child: child, ),
const Flexible(
child: SizedBox(
width: 4,
),
),
Flexible(
flex: 0,
child: Text(
'ms',
style: Theme.of(context).textTheme.bodyMedium?.toLight(),
),
),
],
);
}
return Text(
"Timeout",
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.red,
),
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _checkIpContainer( return CommonCard(
ValueListenableBuilder<IpInfo?>( info: Info(
valueListenable: ipInfoNotifier, iconData: Icons.network_check,
builder: (_, ipInfo, __) { label: appLocalizations.networkDetection,
return CommonCard( ),
child: Selector<AppState, NetworkDetectionSelectorState>(
selector: (_, appState) {
return NetworkDetectionSelectorState(
currentProxyName: appState.showProxyName,
delay: appState.getDelay(
appState.showProxyName,
),
);
},
builder: (_, state, __) {
return Container(
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Flexible( Flexible(
flex: 0, flex: 0,
child: Container( child: TooltipText(
padding: const EdgeInsets.all(16), text: Text(
child: Row( state.currentProxyName ?? appLocalizations.noProxy,
children: [ overflow: TextOverflow.ellipsis,
Icon( maxLines: 1,
Icons.network_check, style: Theme.of(context)
color: Theme.of(context).colorScheme.primary, .textTheme
), .titleMedium
const SizedBox( ?.toSoftBold(),
width: 8,
),
Flexible(
flex: 1,
child: FadeBox(
child: ipInfo != null
? CountryFlag.fromCountryCode(
ipInfo.countryCode,
width: 24,
height: 24,
)
: ValueListenableBuilder(
valueListenable: timeoutNotifier,
builder: (_, timeout, __) {
if (timeout) {
return Text(
appLocalizations.checkError,
style: Theme.of(context)
.textTheme
.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
return TooltipText(
text: Text(
appLocalizations.checking,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleMedium,
),
);
},
),
),
),
],
), ),
), ),
), ),
Container( const SizedBox(
height: height: 8,
globalState.appController.measure.titleLargeHeight + 24, ),
alignment: Alignment.centerLeft, Flexible(
padding: const EdgeInsets.all(16).copyWith(top: 0), child: Container(
child: FadeBox( height: globalState.appController.measure.titleLargeHeight,
child: ipInfo != null alignment: Alignment.centerLeft,
? Column( child: FadeBox(
crossAxisAlignment: CrossAxisAlignment.start, child: _buildDescription(
mainAxisSize: MainAxisSize.min, state.currentProxyName,
children: [ state.delay,
Flexible( ),
flex: 1, ),
child: TooltipText(
text: Text(
ipInfo.ip,
style: context.textTheme.titleLarge
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
)
: ValueListenableBuilder(
valueListenable: timeoutNotifier,
builder: (_, timeout, __) {
if (timeout) {
return Text(
appLocalizations.ipCheckTimeout,
style: context.textTheme.titleLarge
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
return Container(
padding: const EdgeInsets.all(2),
child: const AspectRatio(
aspectRatio: 1,
child: CircularProgressIndicator(),
),
);
},
),
), ),
) ),
], ],
), ),
); );

View File

@@ -19,7 +19,7 @@ class _StartButtonState extends State<StartButton>
@override @override
void initState() { void initState() {
isStart = globalState.appController.appState.isStart; isStart = context.read<AppState>().runTime != null;
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
value: isStart ? 1 : 0, value: isStart ? 1 : 0,
@@ -48,11 +48,9 @@ class _StartButtonState extends State<StartButton>
} }
} }
updateSystemProxy() { updateSystemProxy() async {
WidgetsBinding.instance.addPostFrameCallback((_) async { final appController = globalState.appController;
final appController = globalState.appController; await appController.updateSystemProxy(isStart);
await appController.updateSystemProxy(isStart);
});
} }
@override @override

View File

@@ -56,9 +56,6 @@ class _LogsFragmentState extends State<LogsFragment> {
); );
}, },
icon: const Icon(Icons.search), icon: const Icon(Icons.search),
),
const SizedBox(
width: 8,
) )
]; ];
}); });
@@ -142,9 +139,6 @@ class LogsSearchDelegate extends SearchDelegate {
}, },
icon: const Icon(Icons.clear), icon: const Icon(Icons.clear),
), ),
const SizedBox(
width: 8,
)
]; ];
} }

View File

@@ -1,5 +1,4 @@
import 'package:fl_clash/common/common.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/models/models.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
@@ -83,7 +82,7 @@ class _EditProfileState extends State<EditProfile> {
}, },
), ),
), ),
if (widget.profile.type == ProfileType.url)...[ if (widget.profile.url != null && widget.profile.url!.isNotEmpty == true)...[
ListItem( ListItem(
title: TextFormField( title: TextFormField(
controller: urlController, controller: urlController,

View File

@@ -17,9 +17,24 @@ enum ProfileActions {
delete, delete,
} }
class ProfilesFragment extends StatelessWidget { class ProfilesFragment extends StatefulWidget {
const ProfilesFragment({super.key}); const ProfilesFragment({super.key});
@override
State<ProfilesFragment> createState() => _ProfilesFragmentState();
}
class _ProfilesFragmentState extends State<ProfilesFragment> {
_handleDeleteProfile(String id) async {
globalState.appController.deleteProfile(id);
}
_handleUpdateProfile(String id) async {
context.findAncestorStateOfType<CommonScaffoldState>()?.loadingRun(
() => globalState.appController.updateProfile(id),
);
}
_handleShowAddExtendPage() { _handleShowAddExtendPage() {
showExtendPage( showExtendPage(
globalState.navigatorKey.currentState!.context, globalState.navigatorKey.currentState!.context,
@@ -30,6 +45,76 @@ class ProfilesFragment extends StatelessWidget {
); );
} }
_handleShowEditExtendPage(Profile profile) {
showExtendPage(
context,
body: EditProfile(
profile: profile.copyWith(),
context: context,
),
title: "${appLocalizations.edit}${appLocalizations.profile}",
);
}
_buildGrid({
required ProfilesSelectorState state,
int crossAxisCount = 1,
}) {
return SingleChildScrollView(
padding: crossAxisCount > 1
? const EdgeInsets.symmetric(horizontal: 16)
: EdgeInsets.zero,
child: Grid.baseGap(
crossAxisCount: crossAxisCount,
children: [
for (final profile in state.profiles)
GridItem(
child: ProfileItem(
profile: profile,
commonPopupMenu: CommonPopupMenu<ProfileActions>(
items: [
CommonPopupMenuItem(
action: ProfileActions.edit,
label: appLocalizations.edit,
iconData: Icons.edit,
),
if (profile.url != null)
CommonPopupMenuItem(
action: ProfileActions.update,
label: appLocalizations.update,
iconData: Icons.sync,
),
CommonPopupMenuItem(
action: ProfileActions.delete,
label: appLocalizations.delete,
iconData: Icons.delete,
),
],
onSelected: (ProfileActions? action) async {
switch (action) {
case ProfileActions.edit:
_handleShowEditExtendPage(profile);
break;
case ProfileActions.delete:
_handleDeleteProfile(profile.id);
break;
case ProfileActions.update:
_handleUpdateProfile(profile.id);
break;
case null:
break;
}
},
),
groupValue: state.currentProfileId,
onChanged: globalState.appController.changeProfile,
),
),
],
),
);
}
_getColumns(ViewMode viewMode) { _getColumns(ViewMode viewMode) {
switch (viewMode) { switch (viewMode) {
case ViewMode.mobile: case ViewMode.mobile:
@@ -41,47 +126,17 @@ class ProfilesFragment extends StatelessWidget {
} }
} }
_initScaffoldState(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
final commonScaffoldState =
context.findAncestorStateOfType<CommonScaffoldState>();
commonScaffoldState?.actions = [
IconButton(
onPressed: () {
commonScaffoldState.loadingRun<void>(
() async {
await globalState.appController.updateProfiles();
},
);
},
icon: const Icon(Icons.download),
),
const SizedBox(
width: 8,
)
];
commonScaffoldState?.floatingActionButton = FloatingActionButton(
heroTag: null,
onPressed: _handleShowAddExtendPage,
child: const Icon(
Icons.add,
),
);
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<AppState, bool>( return FloatLayout(
selector: (_, appState) => appState.currentLabel == 'profiles', floatingWidget: Container(
builder: (_, isCurrent, child) { margin: const EdgeInsets.all(kFloatingActionButtonMargin),
if (isCurrent) { child: FloatingActionButton(
_initScaffoldState(context); heroTag: null,
} onPressed: _handleShowAddExtendPage,
return child!; child: const Icon(Icons.add),
}, ),
),
child: Selector2<AppState, Config, ProfilesSelectorState>( child: Selector2<AppState, Config, ProfilesSelectorState>(
selector: (_, appState, config) => ProfilesSelectorState( selector: (_, appState, config) => ProfilesSelectorState(
profiles: config.profiles, profiles: config.profiles,
@@ -94,32 +149,11 @@ class ProfilesFragment extends StatelessWidget {
label: appLocalizations.nullProfileDesc, label: appLocalizations.nullProfileDesc,
); );
} }
final columns = _getColumns(state.viewMode);
final isMobile = state.viewMode == ViewMode.mobile;
return Align( return Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: SingleChildScrollView( child: _buildGrid(
padding: !isMobile state: state,
? const EdgeInsets.symmetric( crossAxisCount: _getColumns(state.viewMode),
horizontal: 16,
vertical: 16,
)
: EdgeInsets.zero,
child: Grid(
mainAxisSpacing: isMobile ? 8 : 16,
crossAxisSpacing: 16,
crossAxisCount: columns,
children: [
for (final profile in state.profiles)
GridItem(
child: ProfileItem(
profile: profile,
groupValue: state.currentProfileId,
onChanged: globalState.appController.changeProfile,
),
),
],
),
), ),
); );
}, },
@@ -128,188 +162,92 @@ class ProfilesFragment extends StatelessWidget {
} }
} }
class ProfileItem extends StatefulWidget { class ProfileItem extends StatelessWidget {
final Profile profile; final Profile profile;
final String? groupValue; final String? groupValue;
final CommonPopupMenu commonPopupMenu;
final void Function(String? value) onChanged; final void Function(String? value) onChanged;
const ProfileItem({ const ProfileItem({
super.key, super.key,
required this.profile, required this.profile,
required this.commonPopupMenu,
required this.groupValue, required this.groupValue,
required this.onChanged, required this.onChanged,
}); });
@override @override
State<ProfileItem> createState() => _ProfileItemState(); Widget build(BuildContext context) {
} String useShow;
String totalShow;
class _ProfileItemState extends State<ProfileItem> { double progress;
final isUpdating = ValueNotifier<bool>(false); final userInfo = profile.userInfo;
if (userInfo == null) {
_handleDeleteProfile(String id) async { useShow = "Infinite";
globalState.appController.deleteProfile(id); totalShow = "Infinite";
} progress = 1;
} else {
_handleUpdateProfile(String id) async { final use = userInfo.upload + userInfo.download;
isUpdating.value = true; final total = userInfo.total;
await globalState.safeRun<void>(() async { useShow = TrafficValue(value: use).show;
await globalState.appController.updateProfile(id); totalShow = TrafficValue(value: total).show;
}); progress = total == 0 ? 0.0 : use / total;
isUpdating.value = false; }
} return ListItem.radio(
horizontalTitleGap: 16,
_handleShowEditExtendPage( delegate: RadioDelegate<String?>(
Profile profile, value: profile.id,
) { groupValue: groupValue,
showExtendPage( onChanged: onChanged,
context,
body: EditProfile(
profile: profile.copyWith(),
context: context,
), ),
title: "${appLocalizations.edit}${appLocalizations.profile}", padding: const EdgeInsets.symmetric(horizontal: 16),
); trailing: commonPopupMenu,
} title: Column(
mainAxisSize: MainAxisSize.min,
_buildTitle(Profile profile) {
final textTheme = context.textTheme;
final userInfo = profile.userInfo ?? UserInfo();
final use = userInfo.upload + userInfo.download;
final total = userInfo.total;
final useShow = TrafficValue(value: use).show;
final totalShow = TrafficValue(value: total).show;
final progress = total == 0 ? 0.0 : use / total;
final expireShow = userInfo.expire == 0
? "长期有效"
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000).show;
return Container(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Row( Flexible(
mainAxisSize: MainAxisSize.max, child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max,
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text( children: [
profile.label ?? profile.id, Flexible(
style: textTheme.titleMedium, child: Text(
maxLines: 1, profile.label ?? profile.id,
overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.titleMedium,
), maxLines: 1,
Text( overflow: TextOverflow.ellipsis,
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '', ),
style: textTheme.labelMedium?.toLight(), ),
), Flexible(
], child: Text(
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '',
style: Theme.of(context).textTheme.labelMedium?.toLight(),
),
),
],
),
), ),
Column( Flexible(
mainAxisSize: MainAxisSize.min, child: Container(
crossAxisAlignment: CrossAxisAlignment.start, margin: const EdgeInsets.symmetric(
mainAxisAlignment: MainAxisAlignment.center, vertical: 8,
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 8,
),
child: LinearProgressIndicator(
minHeight: 6,
value: progress,
),
), ),
Text( child: LinearProgressIndicator(
"$useShow / $totalShow", minHeight: 6,
style: textTheme.labelMedium?.toLight(), value: progress,
), ),
const SizedBox( ),
height: 2, ),
), Flexible(
Row( child: Text(
children: [ "$useShow / $totalShow",
Text( style: Theme.of(context).textTheme.labelMedium?.toLight(),
"到期时间:", ),
style: textTheme.labelMedium?.toLighter(),
),
const SizedBox(
width: 4,
),
Text(
expireShow,
style: textTheme.labelMedium?.toLighter(),
),
],
)
],
), ),
], ],
), ),
); );
} }
@override
Widget build(BuildContext context) {
final profile = widget.profile;
final groupValue = widget.groupValue;
final onChanged = widget.onChanged;
return Selector<AppState, ViewMode>(
selector: (_, appState) => appState.viewMode,
builder: (_, viewMode, child) {
if (viewMode == ViewMode.mobile) {
return child!;
}
return CommonCard(
child: child!,
);
},
child: ListItem.radio(
key: Key(profile.id),
horizontalTitleGap: 16,
delegate: RadioDelegate<String?>(
value: profile.id,
groupValue: groupValue,
onChanged: onChanged,
),
padding: const EdgeInsets.symmetric(horizontal: 16),
trailing: CommonPopupMenu<ProfileActions>(
items: [
CommonPopupMenuItem(
action: ProfileActions.edit,
label: appLocalizations.edit,
iconData: Icons.edit,
),
if (profile.type == ProfileType.url)
CommonPopupMenuItem(
action: ProfileActions.update,
label: appLocalizations.update,
iconData: Icons.sync,
),
CommonPopupMenuItem(
action: ProfileActions.delete,
label: appLocalizations.delete,
iconData: Icons.delete,
),
],
onSelected: (ProfileActions? action) async {
switch (action) {
case ProfileActions.edit:
_handleShowEditExtendPage(profile);
break;
case ProfileActions.delete:
_handleDeleteProfile(profile.id);
break;
case ProfileActions.update:
_handleUpdateProfile(profile.id);
break;
case null:
break;
}
},
),
title: _buildTitle(profile),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
),
);
}
} }

View File

@@ -1,4 +1,3 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -51,9 +50,6 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
selectedValue: proxiesSortType, selectedValue: proxiesSortType,
); );
}, },
),
const SizedBox(
width: 8,
) )
]; ];
}); });
@@ -61,69 +57,72 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<AppState, bool>( return DelayTestButtonContainer(
selector: (_, appState) => appState.currentLabel == 'proxies', child: Selector<AppState, bool>(
builder: (_, isCurrent, child) { selector: (_, appState) => appState.currentLabel == 'proxies',
if (isCurrent) { builder: (_, isCurrent, child) {
_initActions(); if (isCurrent) {
} _initActions();
return child!;
},
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
selector: (_, appState, config, clashConfig) {
final currentGroups = appState.currentGroups;
final groupNames = currentGroups.map((e) => e.name).toList();
return ProxiesSelectorState(
groupNames: groupNames,
);
},
shouldRebuild: (prev, next) {
if (prev.groupNames.length != next.groupNames.length) {
_tabController?.dispose();
_tabController = null;
} }
return prev != next; return child!;
}, },
builder: (_, state, __) { child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
_tabController ??= TabController( selector: (_, appState, config, clashConfig) {
length: state.groupNames.length, final currentGroups = appState.currentGroups;
vsync: this, final groupNames = currentGroups.map((e) => e.name).toList();
); return ProxiesSelectorState(
return Column( groupNames: groupNames,
mainAxisAlignment: MainAxisAlignment.start, );
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ shouldRebuild: (prev, next) {
TabBar( if (prev.groupNames.length != next.groupNames.length) {
controller: _tabController, _tabController?.dispose();
padding: const EdgeInsets.symmetric(horizontal: 16), _tabController = null;
dividerColor: Colors.transparent, }
isScrollable: true, return prev != next;
tabAlignment: TabAlignment.start, },
overlayColor: const WidgetStatePropertyAll(Colors.transparent), builder: (_, state, __) {
tabs: [ _tabController ??= TabController(
for (final groupName in state.groupNames) length: state.groupNames.length,
Tab( vsync: this,
text: groupName, );
), return Column(
], mainAxisAlignment: MainAxisAlignment.start,
), crossAxisAlignment: CrossAxisAlignment.start,
Expanded( children: [
child: TabBarView( TabBar(
controller: _tabController, controller: _tabController,
children: [ padding: const EdgeInsets.symmetric(horizontal: 16),
dividerColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
overlayColor:
const WidgetStatePropertyAll(Colors.transparent),
tabs: [
for (final groupName in state.groupNames) for (final groupName in state.groupNames)
KeepContainer( Tab(
key: ObjectKey(groupName), text: groupName,
child: ProxiesTabView(
groupName: groupName,
),
), ),
], ],
), ),
) Expanded(
], child: TabBarView(
); controller: _tabController,
}, children: [
for (final groupName in state.groupNames)
KeepContainer(
key: ObjectKey(groupName),
child: ProxiesTabView(
groupName: groupName,
),
),
],
),
)
],
);
},
),
), ),
); );
} }
@@ -197,18 +196,6 @@ class ProxiesTabView extends StatelessWidget {
} }
} }
_delayTest(List<Proxy> proxies) async {
for (final proxy in proxies) {
globalState.appController.setDelay(
Delay(name: proxy.name, value: 0),
);
clashCore.getDelay(proxy.name).then((delay) {
globalState.appController.setDelay(delay);
});
}
await Future.delayed(httpTimeoutDuration + moreDuration);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector2<AppState, Config, ProxiesTabViewSelectorState>( return Selector2<AppState, Config, ProxiesTabViewSelectorState>(
@@ -226,32 +213,25 @@ class ProxiesTabView extends StatelessWidget {
state.group.all, state.group.all,
state.proxiesSortType, state.proxiesSortType,
); );
return DelayTestButtonContainer( return Align(
onClick: () async { alignment: Alignment.topCenter,
await _delayTest( child: GridView.builder(
state.group.all, padding: const EdgeInsets.all(16),
); gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
}, crossAxisCount: _getColumns(state.viewMode),
child: Align( mainAxisSpacing: 8,
alignment: Alignment.topCenter, crossAxisSpacing: 8,
child: GridView.builder( mainAxisExtent: _getItemHeight(context),
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _getColumns(state.viewMode),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: _getItemHeight(context),
),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
); );
}, },
@@ -404,12 +384,10 @@ class ProxyCard extends StatelessWidget {
class DelayTestButtonContainer extends StatefulWidget { class DelayTestButtonContainer extends StatefulWidget {
final Widget child; final Widget child;
final Future Function() onClick;
const DelayTestButtonContainer({ const DelayTestButtonContainer({
super.key, super.key,
required this.child, required this.child,
required this.onClick,
}); });
@override @override
@@ -423,9 +401,12 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
late Animation<double> _scale; late Animation<double> _scale;
_healthcheck() async { _healthcheck() async {
if (globalState.healthcheckLock) return;
_controller.forward(); _controller.forward();
await widget.onClick(); globalState.appController.healthcheck();
_controller.reverse(); Future.delayed(httpTimeoutDuration + moreDuration, () {
_controller.reverse();
});
} }
@override @override
@@ -446,6 +427,7 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
curve: const Interval( curve: const Interval(
0, 0,
1, 1,
curve: Curves.elasticInOut,
), ),
), ),
); );
@@ -459,7 +441,6 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_controller.reverse();
return FloatLayout( return FloatLayout(
floatingWidget: FloatWrapper( floatingWidget: FloatWrapper(
child: AnimatedBuilder( child: AnimatedBuilder(

View File

@@ -156,9 +156,5 @@
"goDownload": "Go to download", "goDownload": "Go to download",
"unknown": "Unknown", "unknown": "Unknown",
"geoData": "GeoData", "geoData": "GeoData",
"externalResources": "External resources", "externalResources": "External resources"
"checking": "Checking...",
"country": "Country",
"checkError": "Check error",
"ipCheckTimeout": "Ip check timeout"
} }

View File

@@ -156,9 +156,5 @@
"goDownload": "前往下载", "goDownload": "前往下载",
"unknown": "未知", "unknown": "未知",
"geoData": "地理数据", "geoData": "地理数据",
"externalResources": "外部资源", "externalResources": "外部资源"
"checking": "检测中...",
"country": "区域",
"checkError": "检测失败",
"ipCheckTimeout": "Ip检测超时"
} }

View File

@@ -76,12 +76,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Cancel filter system app"), MessageLookupByLibrary.simpleMessage("Cancel filter system app"),
"cancelSelectAll": "cancelSelectAll":
MessageLookupByLibrary.simpleMessage("Cancel select all"), MessageLookupByLibrary.simpleMessage("Cancel select all"),
"checkError": MessageLookupByLibrary.simpleMessage("Check error"),
"checkUpdate": "checkUpdate":
MessageLookupByLibrary.simpleMessage("Check for updates"), MessageLookupByLibrary.simpleMessage("Check for updates"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage( "checkUpdateError": MessageLookupByLibrary.simpleMessage(
"The current application is already the latest version"), "The current application is already the latest version"),
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
"compatible": "compatible":
MessageLookupByLibrary.simpleMessage("Compatibility mode"), MessageLookupByLibrary.simpleMessage("Compatibility mode"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage( "compatibleDesc": MessageLookupByLibrary.simpleMessage(
@@ -90,7 +88,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"), "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"core": MessageLookupByLibrary.simpleMessage("Core"), "core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"create": MessageLookupByLibrary.simpleMessage("Create"), "create": MessageLookupByLibrary.simpleMessage("Create"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"), "dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
@@ -125,8 +122,6 @@ class MessageLookup extends MessageLookupByLibrary {
"hours": MessageLookupByLibrary.simpleMessage("Hours"), "hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL": "importFromURL":
MessageLookupByLibrary.simpleMessage("Import from URL"), MessageLookupByLibrary.simpleMessage("Import from URL"),
"ipCheckTimeout":
MessageLookupByLibrary.simpleMessage("Ip check timeout"),
"just": MessageLookupByLibrary.simpleMessage("Just"), "just": MessageLookupByLibrary.simpleMessage("Just"),
"language": MessageLookupByLibrary.simpleMessage("Language"), "language": MessageLookupByLibrary.simpleMessage("Language"),
"light": MessageLookupByLibrary.simpleMessage("Light"), "light": MessageLookupByLibrary.simpleMessage("Light"),

View File

@@ -63,10 +63,8 @@ class MessageLookup extends MessageLookupByLibrary {
"cancelFilterSystemApp": "cancelFilterSystemApp":
MessageLookupByLibrary.simpleMessage("取消过滤系统应用"), MessageLookupByLibrary.simpleMessage("取消过滤系统应用"),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"),
"checkError": MessageLookupByLibrary.simpleMessage("检测失败"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
"compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"),
"compatibleDesc": "compatibleDesc":
MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"), MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"),
@@ -74,7 +72,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"core": MessageLookupByLibrary.simpleMessage("内核"), "core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"create": MessageLookupByLibrary.simpleMessage("创建"), "create": MessageLookupByLibrary.simpleMessage("创建"),
"dark": MessageLookupByLibrary.simpleMessage("深色"), "dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
@@ -102,7 +99,6 @@ class MessageLookup extends MessageLookupByLibrary {
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"), "hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"ipCheckTimeout": MessageLookupByLibrary.simpleMessage("Ip检测超时"),
"just": MessageLookupByLibrary.simpleMessage("刚刚"), "just": MessageLookupByLibrary.simpleMessage("刚刚"),
"language": MessageLookupByLibrary.simpleMessage("语言"), "language": MessageLookupByLibrary.simpleMessage("语言"),
"light": MessageLookupByLibrary.simpleMessage("浅色"), "light": MessageLookupByLibrary.simpleMessage("浅色"),

View File

@@ -1629,46 +1629,6 @@ class AppLocalizations {
args: [], args: [],
); );
} }
/// `Checking...`
String get checking {
return Intl.message(
'Checking...',
name: 'checking',
desc: '',
args: [],
);
}
/// `Country`
String get country {
return Intl.message(
'Country',
name: 'country',
desc: '',
args: [],
);
}
/// `Check error`
String get checkError {
return Intl.message(
'Check error',
name: 'checkError',
desc: '',
args: [],
);
}
/// `Ip check timeout`
String get ipCheckTimeout {
return Intl.message(
'Ip check timeout',
name: 'ipCheckTimeout',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> { class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -89,8 +89,6 @@ class AppState with ChangeNotifier {
} }
} }
bool get isStart => _runTime != null;
int? get runTime => _runTime; int? get runTime => _runTime;
set runTime(int? value) { set runTime(int? value) {
@@ -168,7 +166,7 @@ class AppState with ChangeNotifier {
addLog(Log log) { addLog(Log log) {
_logs.add(log); _logs.add(log);
if (_logs.length > 60) { if(_logs.length > 60){
_logs = _logs.sublist(_logs.length - 60); _logs = _logs.sublist(_logs.length - 60);
} }
notifyListeners(); notifyListeners();

View File

@@ -16,7 +16,7 @@ class Tun with _$Tun {
const factory Tun({ const factory Tun({
@Default(false) bool enable, @Default(false) bool enable,
@Default(appName) String device, @Default(appName) String device,
@Default(TunStack.mixed) TunStack stack, @Default(TunStack.gvisor) TunStack stack,
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack, @JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
}) = _Tun; }) = _Tun;

View File

@@ -135,7 +135,7 @@ class _$TunImpl implements _Tun {
const _$TunImpl( const _$TunImpl(
{this.enable = false, {this.enable = false,
this.device = appName, this.device = appName,
this.stack = TunStack.mixed, this.stack = TunStack.gvisor,
@JsonKey(name: "dns-hijack") @JsonKey(name: "dns-hijack")
final List<String> dnsHijack = const ["any:53"]}) final List<String> dnsHijack = const ["any:53"]})
: _dnsHijack = dnsHijack; : _dnsHijack = dnsHijack;

View File

@@ -79,7 +79,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false, enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName, device: json['device'] as String? ?? appName,
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
TunStack.mixed, TunStack.gvisor,
dnsHijack: (json['dns-hijack'] as List<dynamic>?) dnsHijack: (json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toList() ?? .toList() ??

View File

@@ -31,7 +31,7 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
? null ? null
: DAV.fromJson(json['dav'] as Map<String, dynamic>) : DAV.fromJson(json['dav'] as Map<String, dynamic>)
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
..isCompatible = json['isCompatible'] as bool? ?? true ..isCompatible = json['isCompatible'] as bool? ?? false
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true; ..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{

View File

@@ -157,30 +157,34 @@ abstract class _StartButtonSelectorState implements StartButtonSelectorState {
} }
/// @nodoc /// @nodoc
mixin _$CheckIpSelectorState { mixin _$UpdateCurrentDelaySelectorState {
String? get currentProxyName => throw _privateConstructorUsedError;
bool get isCurrent => throw _privateConstructorUsedError;
int? get delay => throw _privateConstructorUsedError;
bool get isInit => throw _privateConstructorUsedError; bool get isInit => throw _privateConstructorUsedError;
bool get isStart => throw _privateConstructorUsedError;
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$CheckIpSelectorStateCopyWith<CheckIpSelectorState> get copyWith => $UpdateCurrentDelaySelectorStateCopyWith<UpdateCurrentDelaySelectorState>
throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }
/// @nodoc /// @nodoc
abstract class $CheckIpSelectorStateCopyWith<$Res> { abstract class $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
factory $CheckIpSelectorStateCopyWith(CheckIpSelectorState value, factory $UpdateCurrentDelaySelectorStateCopyWith(
$Res Function(CheckIpSelectorState) then) = UpdateCurrentDelaySelectorState value,
_$CheckIpSelectorStateCopyWithImpl<$Res, CheckIpSelectorState>; $Res Function(UpdateCurrentDelaySelectorState) then) =
_$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
UpdateCurrentDelaySelectorState>;
@useResult @useResult
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap}); $Res call(
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class _$CheckIpSelectorStateCopyWithImpl<$Res, class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
$Val extends CheckIpSelectorState> $Val extends UpdateCurrentDelaySelectorState>
implements $CheckIpSelectorStateCopyWith<$Res> { implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
_$CheckIpSelectorStateCopyWithImpl(this._value, this._then); _$UpdateCurrentDelaySelectorStateCopyWithImpl(this._value, this._then);
// ignore: unused_field // ignore: unused_field
final $Val _value; final $Val _value;
@@ -190,136 +194,154 @@ class _$CheckIpSelectorStateCopyWithImpl<$Res,
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value.selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
) as $Val); ) as $Val);
} }
} }
/// @nodoc /// @nodoc
abstract class _$$CheckIpSelectorStateImplCopyWith<$Res> abstract class _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res>
implements $CheckIpSelectorStateCopyWith<$Res> { implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
factory _$$CheckIpSelectorStateImplCopyWith(_$CheckIpSelectorStateImpl value, factory _$$UpdateCurrentDelaySelectorStateImplCopyWith(
$Res Function(_$CheckIpSelectorStateImpl) then) = _$UpdateCurrentDelaySelectorStateImpl value,
__$$CheckIpSelectorStateImplCopyWithImpl<$Res>; $Res Function(_$UpdateCurrentDelaySelectorStateImpl) then) =
__$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap}); $Res call(
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class __$$CheckIpSelectorStateImplCopyWithImpl<$Res> class __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>
extends _$CheckIpSelectorStateCopyWithImpl<$Res, _$CheckIpSelectorStateImpl> extends _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
implements _$$CheckIpSelectorStateImplCopyWith<$Res> { _$UpdateCurrentDelaySelectorStateImpl>
__$$CheckIpSelectorStateImplCopyWithImpl(_$CheckIpSelectorStateImpl _value, implements _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> {
$Res Function(_$CheckIpSelectorStateImpl) _then) __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl(
_$UpdateCurrentDelaySelectorStateImpl _value,
$Res Function(_$UpdateCurrentDelaySelectorStateImpl) _then)
: super(_value, _then); : super(_value, _then);
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_$CheckIpSelectorStateImpl( return _then(_$UpdateCurrentDelaySelectorStateImpl(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value._selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
)); ));
} }
} }
/// @nodoc /// @nodoc
class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState { class _$UpdateCurrentDelaySelectorStateImpl
const _$CheckIpSelectorStateImpl( implements _UpdateCurrentDelaySelectorState {
{required this.isInit, const _$UpdateCurrentDelaySelectorStateImpl(
required this.isStart, {required this.currentProxyName,
required final Map<String, String> selectedMap}) required this.isCurrent,
: _selectedMap = selectedMap; required this.delay,
required this.isInit});
@override
final String? currentProxyName;
@override
final bool isCurrent;
@override
final int? delay;
@override @override
final bool isInit; final bool isInit;
@override
final bool isStart;
final Map<String, String> _selectedMap;
@override
Map<String, String> get selectedMap {
if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_selectedMap);
}
@override @override
String toString() { String toString() {
return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap)'; return 'UpdateCurrentDelaySelectorState(currentProxyName: $currentProxyName, isCurrent: $isCurrent, delay: $delay, isInit: $isInit)';
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$CheckIpSelectorStateImpl && other is _$UpdateCurrentDelaySelectorStateImpl &&
(identical(other.isInit, isInit) || other.isInit == isInit) && (identical(other.currentProxyName, currentProxyName) ||
(identical(other.isStart, isStart) || other.isStart == isStart) && other.currentProxyName == currentProxyName) &&
const DeepCollectionEquality() (identical(other.isCurrent, isCurrent) ||
.equals(other._selectedMap, _selectedMap)); other.isCurrent == isCurrent) &&
(identical(other.delay, delay) || other.delay == delay) &&
(identical(other.isInit, isInit) || other.isInit == isInit));
} }
@override @override
int get hashCode => Object.hash(runtimeType, isInit, isStart, int get hashCode =>
const DeepCollectionEquality().hash(_selectedMap)); Object.hash(runtimeType, currentProxyName, isCurrent, delay, isInit);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> _$$UpdateCurrentDelaySelectorStateImplCopyWith<
get copyWith => _$UpdateCurrentDelaySelectorStateImpl>
__$$CheckIpSelectorStateImplCopyWithImpl<_$CheckIpSelectorStateImpl>( get copyWith => __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<
this, _$identity); _$UpdateCurrentDelaySelectorStateImpl>(this, _$identity);
} }
abstract class _CheckIpSelectorState implements CheckIpSelectorState { abstract class _UpdateCurrentDelaySelectorState
const factory _CheckIpSelectorState( implements UpdateCurrentDelaySelectorState {
{required final bool isInit, const factory _UpdateCurrentDelaySelectorState(
required final bool isStart, {required final String? currentProxyName,
required final Map<String, String> selectedMap}) = required final bool isCurrent,
_$CheckIpSelectorStateImpl; required final int? delay,
required final bool isInit}) = _$UpdateCurrentDelaySelectorStateImpl;
@override
String? get currentProxyName;
@override
bool get isCurrent;
@override
int? get delay;
@override @override
bool get isInit; bool get isInit;
@override @override
bool get isStart;
@override
Map<String, String> get selectedMap;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> _$$UpdateCurrentDelaySelectorStateImplCopyWith<
_$UpdateCurrentDelaySelectorStateImpl>
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }

View File

@@ -1,70 +0,0 @@
class IpInfo {
final String ip;
final String countryCode;
const IpInfo({
required this.ip,
required this.countryCode,
});
static IpInfo fromIpInfoIoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country": final String country,
} =>
IpInfo(
ip: ip,
countryCode: country,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpApiCoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpSbJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
@override
String toString() {
return 'IpInfo{ip: $ip, countryCode: $countryCode}';
}
}

View File

@@ -12,5 +12,4 @@ export 'package.dart';
export 'ffi.dart'; export 'ffi.dart';
export 'selector.dart'; export 'selector.dart';
export 'navigation.dart'; export 'navigation.dart';
export 'dav.dart'; export 'dav.dart';
export 'ip.dart';

View File

@@ -16,17 +16,16 @@ class UserInfo {
int upload; int upload;
int download; int download;
int total; int total;
int expire; int? expire;
UserInfo({ UserInfo({
int? upload, int? upload,
int? download, int? download,
int? total, int? total,
int? expire, this.expire,
}) : upload = upload ?? 0, }) : upload = upload ?? 0,
download = download ?? 0, download = download ?? 0,
total = total ?? 0, total = total ?? 0;
expire = expire ?? 0;
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$UserInfoToJson(this); return _$UserInfoToJson(this);
@@ -38,10 +37,10 @@ class UserInfo {
factory UserInfo.formHString(String? info) { factory UserInfo.formHString(String? info) {
if (info == null) return UserInfo(); if (info == null) return UserInfo();
final list = info.split(";"); var list = info.split(";");
Map<String, int?> map = {}; Map<String, int?> map = {};
for (final i in list) { for (var i in list) {
final keyValue = i.trim().split("="); var keyValue = i.trim().split("=");
map[keyValue[0]] = int.tryParse(keyValue[1]); map[keyValue[0]] = int.tryParse(keyValue[1]);
} }
return UserInfo( return UserInfo(
@@ -84,8 +83,7 @@ class Profile {
autoUpdateDuration = autoUpdateDuration ?? defaultUpdateDuration, autoUpdateDuration = autoUpdateDuration ?? defaultUpdateDuration,
selectedMap = selectedMap ?? {}; selectedMap = selectedMap ?? {};
ProfileType get type => ProfileType get type => url == null ? ProfileType.file : ProfileType.url;
url == null || url?.isEmpty == true ? ProfileType.file : ProfileType.url;
Future<void> checkAndUpdate() async { Future<void> checkAndUpdate() async {
final isExists = await check(); final isExists = await check();

View File

@@ -1,7 +1,10 @@
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'config.dart';
import 'navigation.dart';
import 'profile.dart';
import 'proxy.dart';
part 'generated/selector.freezed.dart'; part 'generated/selector.freezed.dart';
@@ -14,12 +17,13 @@ class StartButtonSelectorState with _$StartButtonSelectorState {
} }
@freezed @freezed
class CheckIpSelectorState with _$CheckIpSelectorState { class UpdateCurrentDelaySelectorState with _$UpdateCurrentDelaySelectorState {
const factory CheckIpSelectorState({ const factory UpdateCurrentDelaySelectorState({
required String? currentProxyName,
required bool isCurrent,
required int? delay,
required bool isInit, required bool isInit,
required bool isStart, }) = _UpdateCurrentDelaySelectorState;
required SelectedMap selectedMap,
}) = _CheckIpSelectorState;
} }
@freezed @freezed
@@ -49,23 +53,24 @@ class ApplicationSelectorState with _$ApplicationSelectorState {
} }
@freezed @freezed
class TrayContainerSelectorState with _$TrayContainerSelectorState { class TrayContainerSelectorState with _$TrayContainerSelectorState{
const factory TrayContainerSelectorState({ const factory TrayContainerSelectorState({
required Mode mode, required Mode mode,
required bool autoLaunch, required bool autoLaunch,
required bool isRun, required bool isRun,
required String? locale, required String? locale,
}) = _TrayContainerSelectorState; })=_TrayContainerSelectorState;
} }
@freezed @freezed
class UpdateNavigationsSelector with _$UpdateNavigationsSelector { class UpdateNavigationsSelector with _$UpdateNavigationsSelector{
const factory UpdateNavigationsSelector({ const factory UpdateNavigationsSelector({
required bool openLogs, required bool openLogs,
required bool hasProxies, required bool hasProxies,
}) = _UpdateNavigationsSelector; }) = _UpdateNavigationsSelector;
} }
@freezed @freezed
class HomeSelectorState with _$HomeSelectorState { class HomeSelectorState with _$HomeSelectorState {
const factory HomeSelectorState({ const factory HomeSelectorState({
@@ -84,21 +89,21 @@ class HomeBodySelectorState with _$HomeBodySelectorState {
} }
@freezed @freezed
class ProxiesCardSelectorState with _$ProxiesCardSelectorState { class ProxiesCardSelectorState with _$ProxiesCardSelectorState{
const factory ProxiesCardSelectorState({ const factory ProxiesCardSelectorState({
required bool isSelected, required bool isSelected,
}) = _ProxiesCardSelectorState; }) = _ProxiesCardSelectorState;
} }
@freezed @freezed
class ProxiesSelectorState with _$ProxiesSelectorState { class ProxiesSelectorState with _$ProxiesSelectorState{
const factory ProxiesSelectorState({ const factory ProxiesSelectorState({
required List<String> groupNames, required List<String> groupNames,
}) = _ProxiesSelectorState; }) = _ProxiesSelectorState;
} }
@freezed @freezed
class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState { class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState{
const factory ProxiesTabViewSelectorState({ const factory ProxiesTabViewSelectorState({
required ProxiesSortType proxiesSortType, required ProxiesSortType proxiesSortType,
required num sortNum, required num sortNum,
@@ -120,4 +125,4 @@ class PackageListSelectorState with _$PackageListSelectorState {
required AccessControl accessControl, required AccessControl accessControl,
required bool isAccessControl, required bool isAccessControl,
}) = _PackageListSelectorState; }) = _PackageListSelectorState;
} }

View File

@@ -13,6 +13,7 @@ import 'common/common.dart';
class GlobalState { class GlobalState {
Timer? timer; Timer? timer;
Function? healthcheckLockDebounce;
Timer? groupsUpdateTimer; Timer? groupsUpdateTimer;
Function? updateCurrentDelayDebounce; Function? updateCurrentDelayDebounce;
PageController? pageController; PageController? pageController;
@@ -21,6 +22,7 @@ class GlobalState {
late AppController appController; late AppController appController;
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey(); GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
List<Function> updateFunctionLists = []; List<Function> updateFunctionLists = [];
bool healthcheckLock = false;
startListenUpdate() { startListenUpdate() {
if (timer != null && timer!.isActive == true) return; if (timer != null && timer!.isActive == true) return;
@@ -251,6 +253,20 @@ class GlobalState {
); );
} }
void updateCurrentDelay(
String? proxyName,
) {
updateCurrentDelayDebounce ??= debounce<Function(String?)>((proxyName) {
if (proxyName != null) {
debugPrint("[delay]=====> $proxyName");
clashCore.delay(
proxyName,
);
}
});
updateCurrentDelayDebounce!([proxyName]);
}
Future<T?> safeRun<T>( Future<T?> safeRun<T>(
FutureOr<T> Function() futureFunction, { FutureOr<T> Function() futureFunction, {
String? title, String? title,

View File

@@ -38,8 +38,16 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
@override @override
void onDelay(Delay delay) { void onDelay(Delay delay) {
globalState.healthcheckLock = true;
final appController = globalState.appController; final appController = globalState.appController;
appController.setDelay(delay); appController.setDelay(delay);
globalState.healthcheckLockDebounce ??= debounce<Function()>(
() async {
globalState.healthcheckLock = false;
},
milliseconds: 5000,
);
globalState.healthcheckLockDebounce!();
super.onDelay(delay); super.onDelay(delay);
} }

View File

@@ -71,7 +71,6 @@ class ListItem<T> extends StatelessWidget {
final Widget title; final Widget title;
final Widget? subtitle; final Widget? subtitle;
final EdgeInsets padding; final EdgeInsets padding;
final ListTileTitleAlignment tileTitleAlignment;
final bool? prue; final bool? prue;
final Widget? trailing; final Widget? trailing;
final Delegate delegate; final Delegate delegate;
@@ -88,7 +87,6 @@ class ListItem<T> extends StatelessWidget {
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.onTab, this.onTab,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : delegate = const Delegate(); }) : delegate = const Delegate();
const ListItem.open({ const ListItem.open({
@@ -101,7 +99,6 @@ class ListItem<T> extends StatelessWidget {
required OpenDelegate this.delegate, required OpenDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null; }) : onTab = null;
const ListItem.next({ const ListItem.next({
@@ -114,7 +111,6 @@ class ListItem<T> extends StatelessWidget {
required NextDelegate this.delegate, required NextDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null; }) : onTab = null;
const ListItem.checkbox({ const ListItem.checkbox({
@@ -126,7 +122,6 @@ class ListItem<T> extends StatelessWidget {
required CheckboxDelegate this.delegate, required CheckboxDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null, }) : trailing = null,
onTab = null; onTab = null;
@@ -139,7 +134,6 @@ class ListItem<T> extends StatelessWidget {
required SwitchDelegate this.delegate, required SwitchDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null, }) : trailing = null,
onTab = null; onTab = null;
@@ -152,7 +146,6 @@ class ListItem<T> extends StatelessWidget {
required RadioDelegate<T> this.delegate, required RadioDelegate<T> this.delegate,
this.horizontalTitleGap = 8, this.horizontalTitleGap = 8,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : leading = null, }) : leading = null,
onTab = null; onTab = null;
@@ -200,7 +193,6 @@ class ListItem<T> extends StatelessWidget {
horizontalTitleGap: horizontalTitleGap, horizontalTitleGap: horizontalTitleGap,
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,
titleAlignment: tileTitleAlignment,
onTap: onTab, onTap: onTab,
trailing: trailing ?? this.trailing, trailing: trailing ?? this.trailing,
contentPadding: padding, contentPadding: padding,

View File

@@ -49,7 +49,6 @@ class CommonScaffold extends StatefulWidget {
class CommonScaffoldState extends State<CommonScaffold> { class CommonScaffoldState extends State<CommonScaffold> {
final ValueNotifier<List<Widget>> _actions = ValueNotifier([]); final ValueNotifier<List<Widget>> _actions = ValueNotifier([]);
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
final ValueNotifier<bool> _loading = ValueNotifier(false); final ValueNotifier<bool> _loading = ValueNotifier(false);
@@ -59,16 +58,11 @@ class CommonScaffoldState extends State<CommonScaffold> {
} }
} }
set floatingActionButton(Widget? actions) {
if (_floatingActionButton.value != actions) {
_floatingActionButton.value = actions;
}
}
Future<T?> loadingRun<T>( Future<T?> loadingRun<T>(
Future<T> Function() futureFunction, { Future<T> Function() futureFunction, {
String? title, String? title,
}) async { }) async {
if (_loading.value == true) return null;
_loading.value = true; _loading.value = true;
try { try {
final res = await futureFunction(); final res = await futureFunction();
@@ -91,7 +85,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (oldWidget.title != widget.title) { if (oldWidget.title != widget.title) {
_actions.value = []; _actions.value = [];
_floatingActionButton.value = null;
} }
} }
@@ -116,13 +109,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _platformContainer( return _platformContainer(
child: Scaffold( child: Scaffold(
floatingActionButton: ValueListenableBuilder(
valueListenable: _floatingActionButton,
builder: (_, floatingActionButton, __) {
return floatingActionButton ?? Container();
},
),
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight), preferredSize: const Size.fromHeight(kToolbarHeight),
child: Stack( child: Stack(

View File

@@ -37,10 +37,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.6.1" version: "3.5.1"
args: args:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -85,10 +85,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_daemon name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.0.2" version: "4.0.1"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
@@ -101,18 +101,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.11" version: "2.4.9"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "7.3.1" version: "7.3.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -193,14 +193,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
country_flags:
dependency: "direct main"
description:
name: country_flags
sha256: dbc4f76e7c801619b2d841023e0327191ba00663f1f1b4770394e9bc6632c444
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@@ -343,10 +335,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e sha256: "592dc01a18961a51c24ae5d963b724b2b7fa4a95c100fe8eb6ca8a5a4732cadf"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.20" version: "2.0.18"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -361,10 +353,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: freezed name: freezed
sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 sha256: "91bce569d4805ea5bad6619a3e8690df8ad062a235165af4c0c5d928dda15eaf"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.5.2" version: "2.5.1"
freezed_annotation: freezed_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -433,10 +425,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: image name: image
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.2.0" version: "4.1.7"
image_picker: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -449,26 +441,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "4161e1f843d8480d2e9025ee22411778c3c9eb7e40076dcf2da23d8242b7b51c" sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12+3" version: "0.8.12"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" sha256: "6a1704fdd75022272e7e7a897a9068e9c2ff3cd6a66820bf3ded810633eac954"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.4" version: "3.0.3"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12" version: "0.8.11"
image_picker_linux: image_picker_linux:
dependency: transitive dependency: transitive
description: description:
@@ -517,22 +509,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
jovial_misc:
dependency: transitive
description:
name: jovial_misc
sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.5"
jovial_svg:
dependency: transitive
description:
name: jovial_svg
sha256: "000cafe183bdeba28f76d65bf93fc9b636d6f7eaa7e2a33e80c4345a28866ea8"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.21"
js: js:
dependency: transitive dependency: transitive
description: description:
@@ -705,18 +681,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.2.5" version: "2.2.3"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.0" version: "2.3.2"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -765,14 +741,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.9.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@@ -808,10 +776,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: pubspec_parse name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0" version: "1.2.3"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
@@ -840,18 +808,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.2.3" version: "2.2.1"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.0" version: "2.3.5"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@@ -896,10 +864,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.0" version: "1.0.4"
shortid: shortid:
dependency: transitive dependency: transitive
description: description:
@@ -997,10 +965,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: tray_manager name: tray_manager
sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 sha256: e0ac9a88b2700f366b8629b97e8663b6ef450a2f169560a685dc167bfe9c9c29
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.3" version: "0.2.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -1013,26 +981,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.0" version: "6.2.6"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.3" version: "6.3.0"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.0" version: "6.2.5"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@@ -1045,10 +1013,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.2.0" version: "3.1.0"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -1061,10 +1029,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.3.1" version: "2.3.0"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@@ -1105,22 +1073,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.5.1" version: "0.5.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.5"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.0" version: "2.4.5"
webdav_client: webdav_client:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1133,26 +1093,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "5.5.1" version: "5.5.0"
win32_registry: win32_registry:
dependency: "direct main" dependency: "direct main"
description: description:
name: win32_registry name: win32_registry
sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.3" version: "1.1.2"
window_manager: window_manager:
dependency: "direct main" dependency: "direct main"
description: description:
name: window_manager name: window_manager
sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" sha256: b3c895bdf936c77b83c5254bec2e6b3f066710c1f89c38b20b8acc382b525494
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.3.9" version: "0.3.8"
windows_single_instance: windows_single_instance:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1202,5 +1162,5 @@ packages:
source: hosted source: hosted
version: "0.2.3" version: "0.2.3"
sdks: sdks:
dart: ">=3.4.0 <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.19.0"

View File

@@ -1,7 +1,7 @@
name: fl_clash name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none' publish_to: 'none'
version: 0.8.16 version: 0.8.12
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'
@@ -38,7 +38,6 @@ dependencies:
image: ^4.1.7 image: ^4.1.7
webdav_client: ^1.2.2 webdav_client: ^1.2.2
dio: ^5.4.3+1 dio: ^5.4.3+1
country_flags: ^2.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View File

@@ -1,5 +1,4 @@
// ignore_for_file: avoid_print // ignore_for_file: avoid_print
void main() async { void main() async {
String input = """ String input = """