Compare commits
5 Commits
v0.8.88-pr
...
v0.8.90-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fde588714 | ||
|
|
201062dc5d | ||
|
|
45b163184d | ||
|
|
2ab70f193a | ||
|
|
ed7868282a |
6
.github/workflows/build.yaml
vendored
6
.github/workflows/build.yaml
vendored
@@ -27,9 +27,9 @@ jobs:
|
||||
- platform: macos
|
||||
os: macos-latest
|
||||
arch: arm64
|
||||
- platform: windows
|
||||
os: windows-11-arm
|
||||
arch: arm64
|
||||
# - platform: windows
|
||||
# os: windows-11-arm
|
||||
# arch: arm64
|
||||
- platform: linux
|
||||
os: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -45,11 +45,19 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/android/**/.cxx
|
||||
/android/**/build
|
||||
/android/common/**/.**/
|
||||
/android/common/local.*
|
||||
/android/core/**/includes/
|
||||
/android/core/**/cmake-build-*/
|
||||
/android/core/**/jniLibs/
|
||||
|
||||
|
||||
|
||||
#libclash
|
||||
#FlClash
|
||||
/libclash/
|
||||
|
||||
#jniLibs
|
||||
/android/app/src/main/jniLibs/
|
||||
/services/helper/target
|
||||
/macos/**/Package.resolved
|
||||
devtools_options.yaml
|
||||
|
||||
|
||||
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,3 +1,29 @@
|
||||
## v0.8.89
|
||||
|
||||
- Fix some issues
|
||||
|
||||
- Optimize Windows service mode
|
||||
|
||||
- Update core
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.88
|
||||
|
||||
- Add android separates the core process
|
||||
|
||||
- Support core status check and force restart
|
||||
|
||||
- Optimize proxies page and access page
|
||||
|
||||
- Update flutter and pub dependencies
|
||||
|
||||
- Update go version
|
||||
|
||||
- Optimize more details
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.87
|
||||
|
||||
- Optimize desktop view
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.follow.clash
|
||||
import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.common.formatString
|
||||
import com.follow.clash.common.intent
|
||||
import com.follow.clash.service.IAckInterface
|
||||
import com.follow.clash.service.ICallbackInterface
|
||||
import com.follow.clash.service.IEventInterface
|
||||
import com.follow.clash.service.IRemoteInterface
|
||||
@@ -44,8 +45,11 @@ object Service {
|
||||
return delegate.useService {
|
||||
it.invokeAction(
|
||||
data, object : ICallbackInterface.Stub() {
|
||||
override fun onResult(result: ByteArray?, isSuccess: Boolean) {
|
||||
override fun onResult(
|
||||
result: ByteArray?, isSuccess: Boolean, ack: IAckInterface?
|
||||
) {
|
||||
res.add(result ?: byteArrayOf())
|
||||
ack?.onAck()
|
||||
if (isSuccess) {
|
||||
cb(res.formatString())
|
||||
}
|
||||
@@ -61,24 +65,24 @@ object Service {
|
||||
return delegate.useService {
|
||||
it.setEventListener(
|
||||
when (cb != null) {
|
||||
true -> object : IEventInterface.Stub() {
|
||||
override fun onEvent(
|
||||
id: String, data: ByteArray?, isSuccess: Boolean
|
||||
) {
|
||||
if (results[id] == null) {
|
||||
results[id] = mutableListOf()
|
||||
}
|
||||
results[id]?.add(data ?: byteArrayOf())
|
||||
if (isSuccess) {
|
||||
cb(results[id]?.formatString())
|
||||
results.remove(id)
|
||||
}
|
||||
true -> object : IEventInterface.Stub() {
|
||||
override fun onEvent(
|
||||
id: String, data: ByteArray?, isSuccess: Boolean, ack: IAckInterface?
|
||||
) {
|
||||
if (results[id] == null) {
|
||||
results[id] = mutableListOf()
|
||||
}
|
||||
results[id]?.add(data ?: byteArrayOf())
|
||||
ack?.onAck()
|
||||
if (isSuccess) {
|
||||
cb(results[id]?.formatString())
|
||||
results.remove(id)
|
||||
}
|
||||
}
|
||||
|
||||
false -> null
|
||||
}
|
||||
)
|
||||
|
||||
false -> null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +104,11 @@ object Service {
|
||||
|
||||
private suspend fun awaitIResultInterface(
|
||||
block: (IResultInterface) -> Unit
|
||||
): Unit = suspendCancellableCoroutine { continuation ->
|
||||
): Long = suspendCancellableCoroutine { continuation ->
|
||||
val callback = object : IResultInterface.Stub() {
|
||||
override fun onResult() {
|
||||
override fun onResult(time: Long) {
|
||||
if (continuation.isActive) {
|
||||
continuation.resume(Unit)
|
||||
continuation.resume(time)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,19 +123,25 @@ object Service {
|
||||
}
|
||||
|
||||
|
||||
suspend fun startService(options: VpnOptions) {
|
||||
delegate.useService {
|
||||
suspend fun startService(options: VpnOptions, runTime: Long): Long {
|
||||
return delegate.useService {
|
||||
awaitIResultInterface { callback ->
|
||||
it.startService(options, callback)
|
||||
it.startService(options, runTime, callback)
|
||||
}
|
||||
}
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
|
||||
suspend fun stopService() {
|
||||
delegate.useService {
|
||||
suspend fun stopService(): Long {
|
||||
return delegate.useService {
|
||||
awaitIResultInterface { callback ->
|
||||
it.stopService(callback)
|
||||
}
|
||||
}
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
|
||||
suspend fun getRunTime(): Long {
|
||||
return delegate.useService {
|
||||
it.runTime
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,18 @@ object State {
|
||||
action?.invoke()
|
||||
}
|
||||
|
||||
suspend fun handleSyncState() {
|
||||
runLock.withLock {
|
||||
Service.bind()
|
||||
runTime = Service.getRunTime()
|
||||
val runState = when (runTime == 0L) {
|
||||
true -> RunState.STOP
|
||||
false -> RunState.START
|
||||
}
|
||||
runStateFlow.tryEmit(runState)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun handleStartServiceAction() {
|
||||
tilePlugin?.handleStart()
|
||||
if (flutterEngine != null) {
|
||||
@@ -80,6 +92,7 @@ object State {
|
||||
|
||||
suspend fun destroyServiceEngine() {
|
||||
runLock.withLock {
|
||||
GlobalState.log("Destroy service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
runCatching {
|
||||
serviceFlutterEngine?.destroy()
|
||||
@@ -91,7 +104,12 @@ object State {
|
||||
|
||||
suspend fun startServiceWithEngine() {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
|
||||
return
|
||||
}
|
||||
GlobalState.log("Create service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
serviceFlutterEngine?.destroy()
|
||||
serviceFlutterEngine = FlutterEngine(GlobalState.application)
|
||||
serviceFlutterEngine?.plugins?.add(ServicePlugin())
|
||||
serviceFlutterEngine?.plugins?.add(AppPlugin())
|
||||
@@ -119,8 +137,7 @@ object State {
|
||||
return@launch
|
||||
}
|
||||
appPlugin?.prepare(options.enable) {
|
||||
Service.startService(options)
|
||||
runTime = System.currentTimeMillis()
|
||||
runTime = Service.startService(options, runTime)
|
||||
runStateFlow.tryEmit(RunState.START)
|
||||
}
|
||||
}
|
||||
@@ -135,9 +152,8 @@ object State {
|
||||
return@launch
|
||||
}
|
||||
runStateFlow.tryEmit(RunState.PENDING)
|
||||
Service.stopService()
|
||||
runTime = Service.stopService()
|
||||
runStateFlow.tryEmit(RunState.STOP)
|
||||
runTime = 0
|
||||
}
|
||||
destroyServiceEngine()
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class TileService : TileService() {
|
||||
scope?.cancel()
|
||||
scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
scope?.launch {
|
||||
State.handleSyncState()
|
||||
State.runStateFlow.collect {
|
||||
updateTile(it)
|
||||
}
|
||||
@@ -44,8 +45,7 @@ class TileService : TileService() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startActivityAndCollapse(pendingIntent)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
startActivityAndCollapse(intent)
|
||||
@Suppress("DEPRECATION") startActivityAndCollapse(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) = when (call.method) {
|
||||
"init" -> {
|
||||
handleInit(result)
|
||||
handleInit(call, result)
|
||||
}
|
||||
|
||||
"shutdown" -> {
|
||||
@@ -131,11 +131,16 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
}
|
||||
}
|
||||
|
||||
fun handleInit(result: MethodChannel.Result) {
|
||||
fun handleInit(call: MethodCall, result: MethodChannel.Result) {
|
||||
Service.bind()
|
||||
launch {
|
||||
Service.setEventListener {
|
||||
handleSendEvent(it)
|
||||
val needSetEventListener = call.arguments<Boolean>() ?: false
|
||||
when (needSetEventListener) {
|
||||
true -> Service.setEventListener {
|
||||
handleSendEvent(it)
|
||||
}
|
||||
|
||||
false -> Service.setEventListener(null)
|
||||
}.onSuccess {
|
||||
result.success("")
|
||||
}.onFailure {
|
||||
@@ -147,6 +152,9 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
}
|
||||
|
||||
private fun handleGetRunTime(result: MethodChannel.Result) {
|
||||
return result.success(State.runTime)
|
||||
launch {
|
||||
State.handleSyncState()
|
||||
result.success(State.runTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,6 @@ val Long.formatBytes: String
|
||||
fun String.chunkedForAidl(charset: Charset = Charsets.UTF_8): List<ByteArray> {
|
||||
val allBytes = toByteArray(charset)
|
||||
val total = allBytes.size
|
||||
|
||||
val maxBytes = when {
|
||||
total <= 100 * 1024 -> total
|
||||
total <= 1024 * 1024 -> 64 * 1024
|
||||
|
||||
@@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@@ -59,7 +60,9 @@ class ServiceDelegate<T>(
|
||||
withTimeout(timeoutMillis) {
|
||||
val state = serviceState.filterNotNull().first()
|
||||
state.first?.let {
|
||||
block(it)
|
||||
withContext(Dispatchers.Default) {
|
||||
block(it)
|
||||
}
|
||||
} ?: throw Exception(state.second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
android:process=":remote">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="service" />
|
||||
android:value="proxy" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// IAckInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface IAckInterface {
|
||||
oneway void onAck();
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// ICallbackInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface ICallbackInterface {
|
||||
oneway void onResult(in byte[] data,in boolean isSuccess);
|
||||
oneway void onResult(in byte[] data,in boolean isSuccess, in IAckInterface ack);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// IEventInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface IEventInterface {
|
||||
oneway void onEvent(in String id, in byte[] data,in boolean isSuccess);
|
||||
oneway void onEvent(in String id, in byte[] data,in boolean isSuccess, in IAckInterface ack);
|
||||
}
|
||||
@@ -10,8 +10,9 @@ import com.follow.clash.service.models.NotificationParams;
|
||||
interface IRemoteInterface {
|
||||
void invokeAction(in String data, in ICallbackInterface callback);
|
||||
void updateNotificationParams(in NotificationParams params);
|
||||
void startService(in VpnOptions options, in IResultInterface result);
|
||||
void startService(in VpnOptions options, in long runTime, in IResultInterface result);
|
||||
void stopService(in IResultInterface result);
|
||||
void setEventListener(in IEventInterface event);
|
||||
void setCrashlytics(in boolean enable);
|
||||
long getRunTime();
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
package com.follow.clash.service;
|
||||
|
||||
interface IResultInterface {
|
||||
oneway void onResult();
|
||||
oneway void onResult(in long runTime);
|
||||
}
|
||||
@@ -8,23 +8,22 @@ import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.common.chunkedForAidl
|
||||
import com.follow.clash.common.intent
|
||||
import com.follow.clash.core.Core
|
||||
import com.follow.clash.service.State.delegate
|
||||
import com.follow.clash.service.State.intent
|
||||
import com.follow.clash.service.State.runLock
|
||||
import com.follow.clash.service.models.NotificationParams
|
||||
import com.follow.clash.service.models.VpnOptions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.UUID
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class RemoteService : Service(),
|
||||
CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
|
||||
private var delegate: ServiceDelegate<IBaseService>? = null
|
||||
private var intent: Intent? = null
|
||||
|
||||
val runLock = Mutex()
|
||||
|
||||
private fun handleStopService(result: IResultInterface) {
|
||||
launch {
|
||||
runLock.withLock {
|
||||
@@ -32,7 +31,8 @@ class RemoteService : Service(),
|
||||
service.stop()
|
||||
delegate?.unbind()
|
||||
}
|
||||
result.onResult()
|
||||
State.runTime = 0
|
||||
result.onResult(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class RemoteService : Service(),
|
||||
delegate = null
|
||||
}
|
||||
|
||||
private fun handleStartService(result: IResultInterface) {
|
||||
private fun handleStartService(runTime: Long, result: IResultInterface) {
|
||||
launch {
|
||||
runLock.withLock {
|
||||
val nextIntent = when (State.options?.enable == true) {
|
||||
@@ -65,7 +65,11 @@ class RemoteService : Service(),
|
||||
delegate?.useService { service ->
|
||||
service.start()
|
||||
}
|
||||
result.onResult()
|
||||
State.runTime = when (runTime != 0L) {
|
||||
true -> runTime
|
||||
false -> System.currentTimeMillis()
|
||||
}
|
||||
result.onResult(State.runTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,11 +77,22 @@ class RemoteService : Service(),
|
||||
private val binder = object : IRemoteInterface.Stub() {
|
||||
override fun invokeAction(data: String, callback: ICallbackInterface) {
|
||||
Core.invokeAction(data) {
|
||||
runCatching {
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
val totalSize = chunks.size
|
||||
chunks.forEachIndexed { index, chunk ->
|
||||
callback.onResult(chunk, totalSize - 1 == index)
|
||||
launch {
|
||||
runCatching {
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
for ((index, chunk) in chunks.withIndex()) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
callback.onResult(
|
||||
chunk,
|
||||
index == chunks.lastIndex,
|
||||
object : IAckInterface.Stub() {
|
||||
override fun onAck() {
|
||||
cont.resume(Unit)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,12 +102,14 @@ class RemoteService : Service(),
|
||||
State.notificationParamsFlow.tryEmit(params)
|
||||
}
|
||||
|
||||
|
||||
override fun startService(
|
||||
options: VpnOptions,
|
||||
runtime: Long,
|
||||
result: IResultInterface,
|
||||
) {
|
||||
State.options = options
|
||||
handleStartService(result)
|
||||
handleStartService(runtime, result)
|
||||
}
|
||||
|
||||
override fun stopService(result: IResultInterface) {
|
||||
@@ -100,15 +117,27 @@ class RemoteService : Service(),
|
||||
}
|
||||
|
||||
override fun setEventListener(eventListener: IEventInterface?) {
|
||||
GlobalState.log("isRemoveEventListener is ${eventListener == null}")
|
||||
GlobalState.log("RemoveEventListener ${eventListener == null}")
|
||||
when (eventListener != null) {
|
||||
true -> Core.callSetEventListener {
|
||||
runCatching {
|
||||
val id = UUID.randomUUID().toString()
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
val totalSize = chunks.size
|
||||
chunks.forEachIndexed { index, chunk ->
|
||||
eventListener.onEvent(id, chunk, totalSize - 1 == index)
|
||||
launch {
|
||||
runCatching {
|
||||
val id = UUID.randomUUID().toString()
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
for ((index, chunk) in chunks.withIndex()) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
eventListener.onEvent(
|
||||
id,
|
||||
chunk,
|
||||
index == chunks.lastIndex,
|
||||
object : IAckInterface.Stub() {
|
||||
override fun onAck() {
|
||||
cont.resume(Unit)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,9 +149,18 @@ class RemoteService : Service(),
|
||||
override fun setCrashlytics(enable: Boolean) {
|
||||
GlobalState.setCrashlytics(enable)
|
||||
}
|
||||
|
||||
override fun getRunTime(): Long {
|
||||
return State.runTime
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
return binder
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
GlobalState.log("Remote service destroy")
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
package com.follow.clash.service
|
||||
|
||||
import android.content.Intent
|
||||
import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.service.models.NotificationParams
|
||||
import com.follow.clash.service.models.VpnOptions
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
||||
object State {
|
||||
var options: VpnOptions? = null
|
||||
var notificationParamsFlow: MutableStateFlow<NotificationParams?> = MutableStateFlow(
|
||||
NotificationParams()
|
||||
)
|
||||
|
||||
val runLock = Mutex()
|
||||
var runTime: Long = 0L
|
||||
|
||||
var delegate: ServiceDelegate<IBaseService>? = null
|
||||
|
||||
var intent: Intent? = null
|
||||
}
|
||||
@@ -213,6 +213,7 @@ class VpnService : SystemVpnService(), IBaseService,
|
||||
allowBypass()
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) {
|
||||
GlobalState.log("Open http proxy")
|
||||
setHttpProxy(
|
||||
ProxyInfo.buildDirectProxy(
|
||||
"127.0.0.1", options.port, options.bypassDomain
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.follow.clash.service.models
|
||||
|
||||
import com.follow.clash.common.GlobalState
|
||||
import com.follow.clash.common.formatBytes
|
||||
import com.follow.clash.core.Core
|
||||
import com.google.gson.Gson
|
||||
@@ -17,7 +18,8 @@ fun Core.getSpeedTrafficText(onlyStatisticsProxy: Boolean): String {
|
||||
val res = getTraffic(onlyStatisticsProxy)
|
||||
val traffic = Gson().fromJson(res, Traffic::class.java)
|
||||
return traffic.speedText
|
||||
} catch (_: Exception) {
|
||||
} catch (e: Exception) {
|
||||
GlobalState.log(e.message + "")
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,6 @@ class NotificationModule(private val service: Service) : Module() {
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
override fun onInstall() {
|
||||
State.notificationParamsFlow.value?.let {
|
||||
update(it.extended)
|
||||
}
|
||||
scope.launch {
|
||||
val screenFlow = service.receiveBroadcastFlow {
|
||||
addAction(Intent.ACTION_SCREEN_ON)
|
||||
@@ -69,6 +66,12 @@ class NotificationModule(private val service: Service) : Module() {
|
||||
.collect { (params, _) ->
|
||||
update(params!!)
|
||||
}
|
||||
|
||||
State.notificationParamsFlow.value?.let {
|
||||
update(it.extended)
|
||||
} ?: run {
|
||||
update(NotificationParams().extended)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Submodule core/Clash.Meta updated: 573489787b...168fc4232c
@@ -53,8 +53,8 @@ func handleAction(action *Action, result ActionResult) {
|
||||
result.success(handleShutdown())
|
||||
return
|
||||
case validateConfigMethod:
|
||||
data := []byte(action.Data.(string))
|
||||
result.success(handleValidateConfig(data))
|
||||
path := action.Data.(string)
|
||||
result.success(handleValidateConfig(path))
|
||||
return
|
||||
case updateConfigMethod:
|
||||
data := []byte(action.Data.(string))
|
||||
|
||||
42
core/go.mod
42
core/go.mod
@@ -1,6 +1,6 @@
|
||||
module core
|
||||
|
||||
go 1.20
|
||||
go 1.25
|
||||
|
||||
replace github.com/metacubex/mihomo => ./Clash.Meta
|
||||
|
||||
@@ -18,8 +18,8 @@ require (
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/coreos/go-iptables v0.8.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/enfein/mieru/v3 v3.19.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.0 // indirect
|
||||
github.com/enfein/mieru/v3 v3.20.0 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
@@ -34,43 +34,46 @@ require (
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.3.2 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/klauspost/reedsolomon v1.12.3 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a // indirect
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250902133113-a7f637c14281 // indirect
|
||||
github.com/metacubex/ascon v0.1.0 // indirect
|
||||
github.com/metacubex/bart v0.20.5 // indirect
|
||||
github.com/metacubex/bart v0.24.0 // indirect
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b // indirect
|
||||
github.com/metacubex/blake3 v0.1.0 // indirect
|
||||
github.com/metacubex/chacha v0.1.5 // indirect
|
||||
github.com/metacubex/fswatch v0.1.1 // indirect
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301 // indirect
|
||||
github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b // indirect
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
|
||||
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 // indirect
|
||||
github.com/metacubex/randv2 v0.2.0 // indirect
|
||||
github.com/metacubex/restls-client-go v0.1.7 // indirect
|
||||
github.com/metacubex/sing v0.5.5 // indirect
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb // indirect
|
||||
github.com/metacubex/sing v0.5.6 // indirect
|
||||
github.com/metacubex/sing-mux v0.3.4 // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 // indirect
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7 // indirect
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.7 // indirect
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.8 // indirect
|
||||
github.com/metacubex/sing-vmess v0.2.4 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 // indirect
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 // indirect
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 // indirect
|
||||
github.com/metacubex/utls v1.8.1 // indirect
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||
@@ -78,24 +81,19 @@ require (
|
||||
github.com/openacid/low v0.1.21 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/sagernet/cors v1.2.1 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
|
||||
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 // indirect
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
|
||||
100
core/go.sum
100
core/go.sum
@@ -1,6 +1,7 @@
|
||||
github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=
|
||||
github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
|
||||
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=
|
||||
github.com/RyuaNerin/testingutil v0.1.0/go.mod h1:yTqj6Ta/ycHMPJHRyO12Mz3VrvTloWOsy23WOZH19AA=
|
||||
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=
|
||||
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
@@ -22,15 +23,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I=
|
||||
github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
|
||||
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.20.0 h1:1ob7pCIVSH5FYFAfYvim8isLW1vBOS4cFOUF9exJS38=
|
||||
github.com/enfein/mieru/v3 v3.20.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
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/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/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
|
||||
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
|
||||
@@ -44,7 +46,7 @@ github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hH
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
@@ -59,16 +61,17 @@ github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
||||
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
||||
@@ -78,20 +81,22 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/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/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
|
||||
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a h1:c1QSGpacSeQdBdWcEKZKGuWLcqIG2wxHEygAcXuDwS4=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250902133113-a7f637c14281 h1:09EM0sOLb2kfL0KETGhHujsBLB5iy5U/2yHRHsxf/pI=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250902133113-a7f637c14281/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
|
||||
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
|
||||
github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=
|
||||
github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
|
||||
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bart v0.24.0 h1:EyNiPeVOlg0joSHTzi5oentI0j5M89utUq/5dd76pWM=
|
||||
github.com/metacubex/bart v0.24.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=
|
||||
github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=
|
||||
@@ -102,8 +107,10 @@ github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQux
|
||||
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM9MQ2ySuqu2aeBqcA8rtfWUYLZ8RtI=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||
github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301 h1:N5GExQJqYAH3gOCshpp2u/J3CtNYzMctmlb0xK9wtbQ=
|
||||
github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||
github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b h1:z7JLKjugnQ1qvDOAD8yMA5C8AlJY3bG+VrrgRntRlUY=
|
||||
github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
|
||||
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uSJAvmyCd4TjvDxs57xjb0WxEoaWafK5+qs=
|
||||
@@ -113,32 +120,34 @@ github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFq
|
||||
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
|
||||
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
|
||||
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
|
||||
github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA=
|
||||
github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c=
|
||||
github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0=
|
||||
github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 h1:dGvo7UahC/gYBQNBoictr14baJzBjAKUAorP63QFFtg=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
|
||||
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
|
||||
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 h1:WZepq4TOZa6WewB8tGAZrrL+bL2R2ivoBzuEgAeolWc=
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
|
||||
github.com/metacubex/sing-tun v0.4.8 h1:3PyiUKWXYi37yHptXskzL1723O3OUdyt0Aej4XHVikM=
|
||||
github.com/metacubex/sing-tun v0.4.8/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
|
||||
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
|
||||
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 h1:yN3mQ4cT9sPUciw/rO0Isc/8QlO86DB6g9SEMRgQ8Cw=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 h1:csEbKOzRAxJXffOeZnnS3/kA/F55JiTbKv5jcYqCXms=
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U=
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719 h1:T6qCCfolRDAVJKeaPW/mXwNLjnlo65AYN7WS2jrBNaM=
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.8.1 h1:RW8GeCGWAegjV0HW5nw9DoqNoeGAXXeYUP6AysmRvx4=
|
||||
github.com/metacubex/utls v1.8.1/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9xQt22kSA9HtnyTDmk5XjjQVCGHe2E=
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
||||
@@ -148,6 +157,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/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
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/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
|
||||
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
|
||||
@@ -160,8 +170,6 @@ github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
@@ -170,8 +178,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
|
||||
@@ -191,11 +197,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
@@ -209,8 +212,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
|
||||
@@ -240,20 +243,18 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
@@ -263,7 +264,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -33,6 +33,8 @@ var (
|
||||
)
|
||||
|
||||
func handleInitClash(paramsString string) bool {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
var params = InitParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), ¶ms)
|
||||
if err != nil {
|
||||
@@ -83,8 +85,9 @@ func handleShutdown() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func handleValidateConfig(bytes []byte) string {
|
||||
_, err := config.UnmarshalRawConfig(bytes)
|
||||
func handleValidateConfig(path string) string {
|
||||
buf, err := readFile(path)
|
||||
_, err = config.UnmarshalRawConfig(buf)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ type TunHandler struct {
|
||||
}
|
||||
|
||||
func (th *TunHandler) start(fd int, stack, address, dns string) {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
_ = th.limit.Acquire(context.TODO(), 4)
|
||||
defer th.limit.Release(4)
|
||||
th.initHook()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
|
||||
class Debouncer {
|
||||
@@ -36,16 +37,25 @@ class Throttler {
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
bool fire = false,
|
||||
}) {
|
||||
final timer = _operations[tag];
|
||||
if (timer != null) {
|
||||
return true;
|
||||
}
|
||||
_operations[tag] = Timer(duration, () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
if (fire) {
|
||||
Function.apply(func, args);
|
||||
});
|
||||
_operations[tag] = Timer(duration, () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
});
|
||||
} else {
|
||||
_operations[tag] = Timer(duration, () {
|
||||
Function.apply(func, args);
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,7 +69,7 @@ Future<T> retry<T>({
|
||||
required Future<T> Function() task,
|
||||
int maxAttempts = 3,
|
||||
required bool Function(T res) retryIf,
|
||||
Duration delay = Duration.zero,
|
||||
Duration delay = midDuration,
|
||||
}) async {
|
||||
int attempts = 0;
|
||||
while (attempts < maxAttempts) {
|
||||
@@ -69,7 +79,7 @@ Future<T> retry<T>({
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
throw 'unknown error';
|
||||
throw 'retry error';
|
||||
}
|
||||
|
||||
final debouncer = Debouncer();
|
||||
|
||||
@@ -134,11 +134,15 @@ class CommonPageTransition extends StatefulWidget {
|
||||
bool allowSnapshotting,
|
||||
Widget? child,
|
||||
) {
|
||||
final Animation<Offset> delegatedPositionAnimation = CurvedAnimation(
|
||||
final CurvedAnimation animation = CurvedAnimation(
|
||||
parent: secondaryAnimation,
|
||||
curve: Curves.linearToEaseOut,
|
||||
reverseCurve: Curves.easeInToLinear,
|
||||
).drive(_kMiddleLeftTween);
|
||||
);
|
||||
final Animation<Offset> delegatedPositionAnimation = animation.drive(
|
||||
_kMiddleLeftTween,
|
||||
);
|
||||
animation.dispose();
|
||||
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
|
||||
@@ -71,6 +71,11 @@ class AppPath {
|
||||
return join(homeDirPath, 'config.json');
|
||||
}
|
||||
|
||||
Future<String> get validateFilePath async {
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return join(homeDirPath, 'temp', 'validate${utils.id}.yaml');
|
||||
}
|
||||
|
||||
Future<String> get sharedPreferencesPath async {
|
||||
final directory = await dataDir.future;
|
||||
return join(directory.path, 'shared_preferences.json');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -12,14 +13,14 @@ class CommonPrint {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
void log(String? text) {
|
||||
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
|
||||
final payload = '[APP] $text';
|
||||
debugPrint(payload);
|
||||
if (!globalState.isInit) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log.app(payload),
|
||||
Log.app(payload).copyWith(logLevel: logLevel),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +73,13 @@ class Request {
|
||||
}
|
||||
|
||||
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
||||
'https://ipwho.is/': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com/': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json/': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json/': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
|
||||
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
|
||||
};
|
||||
|
||||
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
||||
@@ -92,11 +92,13 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
final future = dio.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final future = dio
|
||||
.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
future
|
||||
.then((res) {
|
||||
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
||||
|
||||
@@ -11,16 +11,15 @@ extension StringExtension on String {
|
||||
}
|
||||
|
||||
dynamic get splitByMultipleSeparators {
|
||||
final parts =
|
||||
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
|
||||
final parts = split(
|
||||
RegExp(r'[, ;]+'),
|
||||
).where((part) => part.isNotEmpty).toList();
|
||||
|
||||
return parts.length > 1 ? parts : this;
|
||||
}
|
||||
|
||||
int compareToLower(String other) {
|
||||
return toLowerCase().compareTo(
|
||||
other.toLowerCase(),
|
||||
);
|
||||
return toLowerCase().compareTo(other.toLowerCase());
|
||||
}
|
||||
|
||||
List<int> get encodeUtf16LeWithBom {
|
||||
@@ -66,9 +65,9 @@ extension StringExtension on String {
|
||||
return md5.convert(bytes).toString();
|
||||
}
|
||||
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
}
|
||||
|
||||
extension StringExtensionSafe on String? {
|
||||
|
||||
@@ -181,28 +181,31 @@ class Windows {
|
||||
calloc.free(argumentsPtr);
|
||||
calloc.free(operationPtr);
|
||||
|
||||
commonPrint.log('windows runas: $command $arguments resultCode:$result');
|
||||
commonPrint.log(
|
||||
'windows runas: $command $arguments resultCode:$result',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
|
||||
if (result < 42) {
|
||||
if (result <= 32) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _killProcess(int port) async {
|
||||
final result = await Process.run('netstat', ['-ano']);
|
||||
final lines = result.stdout.toString().trim().split('\n');
|
||||
for (final line in lines) {
|
||||
if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
continue;
|
||||
}
|
||||
final parts = line.trim().split(RegExp(r'\s+'));
|
||||
final pid = int.tryParse(parts.last);
|
||||
if (pid != null) {
|
||||
await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Future<void> _killProcess(int port) async {
|
||||
// final result = await Process.run('netstat', ['-ano']);
|
||||
// final lines = result.stdout.toString().trim().split('\n');
|
||||
// for (final line in lines) {
|
||||
// if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
// continue;
|
||||
// }
|
||||
// final parts = line.trim().split(RegExp(r'\s+'));
|
||||
// final pid = int.tryParse(parts.last);
|
||||
// if (pid != null) {
|
||||
// await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<WindowsHelperServiceStatus> checkService() async {
|
||||
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
|
||||
@@ -228,16 +231,18 @@ class Windows {
|
||||
return true;
|
||||
}
|
||||
|
||||
await _killProcess(helperPort);
|
||||
|
||||
final command = [
|
||||
'/c',
|
||||
if (status == WindowsHelperServiceStatus.presence) ...[
|
||||
'sc',
|
||||
'taskkill',
|
||||
'/F',
|
||||
'/IM',
|
||||
'$appHelperService.exe'
|
||||
' & '
|
||||
'sc',
|
||||
'delete',
|
||||
appHelperService,
|
||||
'/force',
|
||||
'&&',
|
||||
'&',
|
||||
],
|
||||
'sc',
|
||||
'create',
|
||||
@@ -253,8 +258,12 @@ class Windows {
|
||||
final res = runas('cmd.exe', command);
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
|
||||
return res;
|
||||
final retryStatus = await retry(
|
||||
task: checkService,
|
||||
retryIf: (status) => status == WindowsHelperServiceStatus.running,
|
||||
delay: commonDuration,
|
||||
);
|
||||
return res && retryStatus == WindowsHelperServiceStatus.running;
|
||||
}
|
||||
|
||||
Future<bool> registerTask(String appName) async {
|
||||
|
||||
@@ -322,12 +322,15 @@ class Utils {
|
||||
return SingleActivator(trigger, control: control, meta: !control);
|
||||
}
|
||||
|
||||
FutureOr<T> handleWatch<T>(Function function) async {
|
||||
FutureOr<T> handleWatch<T>({
|
||||
required Function function,
|
||||
required void Function(T data, int elapsedMilliseconds) onWatch,
|
||||
}) async {
|
||||
if (kDebugMode) {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final res = await function();
|
||||
stopwatch.stop();
|
||||
commonPrint.log('耗时:${stopwatch.elapsedMilliseconds} ms');
|
||||
onWatch(res, stopwatch.elapsedMilliseconds);
|
||||
return res;
|
||||
}
|
||||
return await function();
|
||||
|
||||
@@ -353,7 +353,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
commonPrint.log(e.toString());
|
||||
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,9 +466,6 @@ class AppController {
|
||||
Map<String, dynamic>? data,
|
||||
bool handleError = false,
|
||||
}) async {
|
||||
if (globalState.isPre) {
|
||||
return;
|
||||
}
|
||||
if (data != null) {
|
||||
final tagName = data['tag_name'];
|
||||
final body = data['body'];
|
||||
@@ -529,6 +526,7 @@ class AppController {
|
||||
FlutterError.onError = (details) {
|
||||
commonPrint.log(
|
||||
'exception: ${details.exception} stack: ${details.stack}',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
};
|
||||
updateTray(true);
|
||||
@@ -551,7 +549,12 @@ class AppController {
|
||||
|
||||
Future<void> _connectCore() async {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting;
|
||||
final message = await coreController.preload();
|
||||
final result = await Future.wait([
|
||||
coreController.preload(),
|
||||
if (!globalState.isService) Future.delayed(Duration(milliseconds: 300)),
|
||||
]);
|
||||
final String message = result[0];
|
||||
await Future.delayed(commonDuration);
|
||||
if (message.isNotEmpty) {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
if (context.mounted) {
|
||||
@@ -956,7 +959,7 @@ class AppController {
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$futureFunction ===> $e');
|
||||
commonPrint.log('$title===> $e', logLevel: LogLevel.warning);
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
|
||||
@@ -71,18 +71,37 @@ class CoreController {
|
||||
|
||||
FutureOr<bool> get isInit => _interface.isInit;
|
||||
|
||||
FutureOr<String> validateConfig(String data) {
|
||||
return _interface.validateConfig(data);
|
||||
Future<String> validateConfig(String data) async {
|
||||
final path = await appPath.validateFilePath;
|
||||
await globalState.genValidateFile(path, data);
|
||||
final res = await _interface.validateConfig(path);
|
||||
await File(path).delete();
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<String> validateConfigFormBytes(Uint8List bytes) async {
|
||||
final path = await appPath.validateFilePath;
|
||||
await globalState.genValidateFileFormBytes(path, bytes);
|
||||
final res = await _interface.validateConfig(path);
|
||||
await File(path).delete();
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await _interface.updateConfig(updateParams);
|
||||
}
|
||||
|
||||
Future<String> setupConfig(ClashConfig clashConfig) async {
|
||||
Future<String> setupConfig(
|
||||
ClashConfig clashConfig, {
|
||||
VoidCallback? preloadInvoke,
|
||||
}) async {
|
||||
await globalState.genConfigFile(clashConfig);
|
||||
final params = await globalState.getSetupParams();
|
||||
return await _interface.setupConfig(params);
|
||||
final res = _interface.setupConfig(params);
|
||||
if (preloadInvoke != null) {
|
||||
preloadInvoke();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<List<Group>> getProxiesGroups({
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:isolate';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
mixin CoreInterface {
|
||||
Future<bool> init(InitParams params);
|
||||
@@ -17,7 +18,7 @@ mixin CoreInterface {
|
||||
|
||||
Future<bool> forceGc();
|
||||
|
||||
Future<String> validateConfig(String data);
|
||||
Future<String> validateConfig(String path);
|
||||
|
||||
Future<Result> getConfig(String path);
|
||||
|
||||
@@ -86,7 +87,18 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
await connected;
|
||||
return invoke(method: method, data: data, timeout: timeout);
|
||||
if (kDebugMode) {
|
||||
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
|
||||
}
|
||||
|
||||
return utils.handleWatch(
|
||||
function: () async {
|
||||
return await invoke(method: method, data: data, timeout: timeout);
|
||||
},
|
||||
onWatch: (data, elapsedMilliseconds) {
|
||||
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<T?> invoke<T>({
|
||||
@@ -125,10 +137,10 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> validateConfig(String data) async {
|
||||
Future<String> validateConfig(String path) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.validateConfig,
|
||||
data: data,
|
||||
data: path,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
@@ -145,7 +157,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
|
||||
Result<Map<String, dynamic>>.success({});
|
||||
Result.success({});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -38,23 +38,29 @@ class CoreService extends CoreHandlerInterface {
|
||||
completer?.complete(data);
|
||||
}
|
||||
|
||||
void _initServer() {
|
||||
runZonedGuarded(
|
||||
() async {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
_serverCompleter.complete(server);
|
||||
await for (final socket in server) {
|
||||
await _attachSocket(socket);
|
||||
Future<void> _initServer() async {
|
||||
final server = await retry(
|
||||
task: () async {
|
||||
try {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
server.listen((socket) async {
|
||||
await _attachSocket(socket);
|
||||
});
|
||||
return server;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
(error, stack) async {
|
||||
commonPrint.log('Service error: $error');
|
||||
},
|
||||
retryIf: (server) => server == null,
|
||||
);
|
||||
if (server == null) {
|
||||
exit(0);
|
||||
}
|
||||
_serverCompleter.complete(server);
|
||||
}
|
||||
|
||||
Future<void> _attachSocket(Socket socket) async {
|
||||
@@ -97,7 +103,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
_process?.stderr.listen((e) {
|
||||
final error = utf8.decode(e);
|
||||
if (error.isNotEmpty) {
|
||||
commonPrint.log(error);
|
||||
commonPrint.log(error, logLevel: LogLevel.warning);
|
||||
}
|
||||
});
|
||||
await _socketCompleter.future;
|
||||
|
||||
@@ -22,6 +22,7 @@ Future<void> main() async {
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _service(List<String> flags) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
globalState.isService = true;
|
||||
await globalState.init();
|
||||
await coreController.preload();
|
||||
tile?.addListener(
|
||||
@@ -32,16 +33,18 @@ Future<void> _service(List<String> flags) async {
|
||||
},
|
||||
),
|
||||
);
|
||||
Future(() async {
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
await globalState.handleStart();
|
||||
await coreController.setupConfig(clashConfig);
|
||||
});
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
coreController.setupConfig(
|
||||
clashConfig,
|
||||
preloadInvoke: () {
|
||||
globalState.handleStart();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
||||
@@ -101,7 +101,7 @@ class MessageManagerState extends State<MessageManager> {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
child: Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(14),
|
||||
),
|
||||
@@ -127,6 +127,7 @@ class MessageManagerState extends State<MessageManager> {
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
IconButton(
|
||||
padding: EdgeInsets.all(2),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
_cancelMessage(messages.last.id);
|
||||
|
||||
@@ -87,13 +87,23 @@ class ThemeManager extends ConsumerWidget {
|
||||
top: padding.top > height * 0.3 ? 20.0 : padding.top,
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(container.maxWidth, container.maxHeight),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(container.maxWidth, container.maxHeight),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,11 +24,16 @@ class _VpnContainerState extends ConsumerState<VpnManager> {
|
||||
}
|
||||
|
||||
void showTip() {
|
||||
debouncer.call(FunctionTag.vpnTip, () {
|
||||
if (ref.read(isStartProvider)) {
|
||||
globalState.showNotifier(appLocalizations.vpnTip);
|
||||
}
|
||||
});
|
||||
throttler.call(
|
||||
FunctionTag.vpnTip,
|
||||
() {
|
||||
if (ref.read(isStartProvider)) {
|
||||
globalState.showNotifier(appLocalizations.vpnTip);
|
||||
}
|
||||
},
|
||||
duration: const Duration(seconds: 6),
|
||||
fire: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -271,9 +271,11 @@ class AppIcon extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
decoration: ShapeDecoration(
|
||||
color: context.colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Transform.translate(
|
||||
|
||||
@@ -192,7 +192,7 @@ extension ActionResultExt on ActionResult {
|
||||
if (code == ResultType.success) {
|
||||
return Result.success(data);
|
||||
} else {
|
||||
return Result.error(data);
|
||||
return Result.error('$data');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -174,7 +173,7 @@ extension ProfileExtension on Profile {
|
||||
}
|
||||
|
||||
Future<Profile> saveFile(Uint8List bytes) async {
|
||||
final message = await coreController.validateConfig(utf8.decode(bytes));
|
||||
final message = await coreController.validateConfigFormBytes(bytes);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
}
|
||||
@@ -182,14 +181,4 @@ extension ProfileExtension on Profile {
|
||||
await file.writeAsBytes(bytes);
|
||||
return copyWith(lastUpdateDate: DateTime.now());
|
||||
}
|
||||
|
||||
Future<Profile> saveFileWithString(String value) async {
|
||||
final message = await coreController.validateConfig(value);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
}
|
||||
final file = await getFile();
|
||||
await file.writeAsString(value);
|
||||
return copyWith(lastUpdateDate: DateTime.now());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ class ScannerOverlay extends CustomPainter {
|
||||
final backgroundPath = Path()..addRect(Rect.largest);
|
||||
|
||||
final cutoutPath = Path()
|
||||
..addRRect(
|
||||
RRect.fromRectAndCorners(
|
||||
..addRSuperellipse(
|
||||
RSuperellipse.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -195,7 +195,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 4.0;
|
||||
|
||||
final borderRect = RRect.fromRectAndCorners(
|
||||
final border = RSuperellipse.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -204,7 +204,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
);
|
||||
|
||||
canvas.drawPath(backgroundWithCutout, backgroundPaint);
|
||||
canvas.drawRRect(borderRect, borderPaint);
|
||||
canvas.drawRSuperellipse(border, borderPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -86,7 +86,11 @@ class Service {
|
||||
}
|
||||
|
||||
Future<String> init() async {
|
||||
return await methodChannel.invokeMethod<String>('init') ?? '';
|
||||
return await methodChannel.invokeMethod<String>(
|
||||
'init',
|
||||
!globalState.isService,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
Future<bool> shutdown() async {
|
||||
|
||||
@@ -48,6 +48,7 @@ class GlobalState {
|
||||
AppController? _appController;
|
||||
bool isInit = false;
|
||||
bool isUserDisconnected = false;
|
||||
bool isService = false;
|
||||
|
||||
bool get isStart => startTime != null && startTime!.isBeforeNow;
|
||||
|
||||
@@ -238,7 +239,7 @@ class GlobalState {
|
||||
return VpnOptions(
|
||||
stack: config.patchClashConfig.tun.stack.name,
|
||||
enable: vpnProps.enable,
|
||||
systemProxy: networkProps.systemProxy,
|
||||
systemProxy: vpnProps.systemProxy,
|
||||
port: port,
|
||||
ipv6: vpnProps.ipv6,
|
||||
dnsHijacking: vpnProps.dnsHijacking,
|
||||
@@ -303,7 +304,13 @@ class GlobalState {
|
||||
|
||||
Future<void> genConfigFile(ClashConfig pathConfig) async {
|
||||
final configFilePath = await appPath.configFilePath;
|
||||
final config = await patchRawConfig(patchConfig: pathConfig);
|
||||
var config = {};
|
||||
try {
|
||||
config = await patchRawConfig(patchConfig: pathConfig);
|
||||
} catch (e) {
|
||||
globalState.showNotifier(e.toString());
|
||||
config = {};
|
||||
}
|
||||
final res = await Isolate.run<String>(() async {
|
||||
try {
|
||||
final res = json.encode(config);
|
||||
@@ -322,6 +329,42 @@ class GlobalState {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> genValidateFile(String path, String data) async {
|
||||
final res = await Isolate.run<String>(() async {
|
||||
try {
|
||||
final file = File(path);
|
||||
if (!await file.exists()) {
|
||||
await file.create(recursive: true);
|
||||
}
|
||||
await file.writeAsString(data);
|
||||
return '';
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
});
|
||||
if (res.isNotEmpty) {
|
||||
throw res;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> genValidateFileFormBytes(String path, Uint8List bytes) async {
|
||||
final res = await Isolate.run<String>(() async {
|
||||
try {
|
||||
final file = File(path);
|
||||
if (!await file.exists()) {
|
||||
await file.create(recursive: true);
|
||||
}
|
||||
await file.writeAsBytes(bytes);
|
||||
return '';
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
});
|
||||
if (res.isNotEmpty) {
|
||||
throw res;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidState getAndroidState() {
|
||||
return AndroidState(
|
||||
currentProfileName: config.currentProfile?.label ?? '',
|
||||
|
||||
@@ -14,7 +14,7 @@ class StartButton extends ConsumerStatefulWidget {
|
||||
|
||||
class _StartButtonState extends ConsumerState<StartButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
AnimationController? _controller;
|
||||
late Animation<double> _animation;
|
||||
bool isStart = false;
|
||||
|
||||
@@ -28,7 +28,7 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
parent: _controller!,
|
||||
curve: Curves.easeOutBack,
|
||||
);
|
||||
ref.listenManual(runTimeProvider.select((state) => state != null), (
|
||||
@@ -44,7 +44,8 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_controller?.dispose();
|
||||
_controller = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -59,9 +60,9 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
void updateController() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isStart && mounted) {
|
||||
_controller.forward();
|
||||
_controller?.forward();
|
||||
} else {
|
||||
_controller.reverse();
|
||||
_controller?.reverse();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -74,12 +75,13 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
}
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
animation: _controller!.view,
|
||||
builder: (_, child) {
|
||||
final textWidth =
|
||||
globalState.measure
|
||||
|
||||
@@ -128,10 +128,11 @@ class TrafficUsage extends StatelessWidget {
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
decoration: ShapeDecoration(
|
||||
color: primaryColor,
|
||||
borderRadius: BorderRadius.circular(
|
||||
3,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -151,10 +152,11 @@ class TrafficUsage extends StatelessWidget {
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
decoration: ShapeDecoration(
|
||||
color: secondaryColor,
|
||||
borderRadius: BorderRadius.circular(
|
||||
3,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/app.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -19,12 +18,14 @@ class DeveloperView extends ConsumerWidget {
|
||||
items: [
|
||||
ListItem(
|
||||
title: Text(appLocalizations.messageTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
context.showNotifier(appLocalizations.messageTestTip);
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.logsTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
globalState.appController.addLog(
|
||||
@@ -37,6 +38,7 @@ class DeveloperView extends ConsumerWidget {
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.crashTest),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () {
|
||||
if (kDebugMode) {
|
||||
coreController.crash();
|
||||
@@ -45,18 +47,20 @@ class DeveloperView extends ConsumerWidget {
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.clearData),
|
||||
minVerticalPadding: 14,
|
||||
onTap: () async {
|
||||
await globalState.appController.handleClear();
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: Text('Loading'),
|
||||
onTap: () {
|
||||
ref.read(loadingProvider.notifier).value = !ref.read(
|
||||
loadingProvider,
|
||||
);
|
||||
},
|
||||
),
|
||||
// ListItem(
|
||||
// title: Text('Loading'),
|
||||
// minVerticalPadding: 14,
|
||||
// onTap: () {
|
||||
// ref.read(loadingProvider.notifier).value = !ref.read(
|
||||
// loadingProvider,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ class _ListHeaderState extends State<ListHeader> {
|
||||
return CommonCard(
|
||||
enterAnimated: widget.enterAnimated,
|
||||
key: widget.key,
|
||||
radius: 16.ap,
|
||||
radius: 18.ap,
|
||||
type: CommonCardType.filled,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
||||
@@ -147,7 +147,8 @@ class ProviderItem extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
Text(_buildProviderDesc()),
|
||||
if (provider.updateAt.microsecondsSinceEpoch > 0)
|
||||
Text(_buildProviderDesc()),
|
||||
const SizedBox(height: 4),
|
||||
if (provider.subscriptionInfo != null)
|
||||
SubscriptionInfoView(subscriptionInfo: provider.subscriptionInfo),
|
||||
|
||||
@@ -360,7 +360,7 @@ class DelayTestButton extends StatefulWidget {
|
||||
class _DelayTestButtonState extends State<DelayTestButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _scale;
|
||||
late Animation<double> _animation;
|
||||
|
||||
Future<void> _healthcheck() async {
|
||||
if (_controller.isAnimating) {
|
||||
@@ -378,10 +378,10 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration: const Duration(milliseconds: 400),
|
||||
);
|
||||
_scale = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: const Interval(0, 1)),
|
||||
_animation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.easeInOutBack),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -399,7 +399,10 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: Transform.scale(scale: _scale.value, child: child),
|
||||
child: FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: FloatingActionButton(
|
||||
|
||||
@@ -79,7 +79,7 @@ class CommonCard extends StatelessWidget {
|
||||
this.type = CommonCardType.plain,
|
||||
this.onPressed,
|
||||
this.selectWidget,
|
||||
this.radius = 12,
|
||||
this.radius = 14,
|
||||
required this.child,
|
||||
this.padding,
|
||||
this.enterAnimated = false,
|
||||
@@ -177,7 +177,9 @@ class CommonCard extends StatelessWidget {
|
||||
style: ButtonStyle(
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)),
|
||||
RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
),
|
||||
),
|
||||
iconColor: WidgetStatePropertyAll(context.colorScheme.primary),
|
||||
iconSize: WidgetStateProperty.all(20),
|
||||
|
||||
@@ -37,7 +37,7 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ClipRRect(
|
||||
child: ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(36),
|
||||
child: SizedBox(
|
||||
width: 72,
|
||||
@@ -47,22 +47,16 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
children: [
|
||||
GridItem(
|
||||
mainAxisCellCount: 2,
|
||||
child: Container(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
child: Container(color: colorScheme.primary),
|
||||
),
|
||||
GridItem(
|
||||
mainAxisCellCount: 1,
|
||||
child: Container(
|
||||
color: colorScheme.secondary,
|
||||
),
|
||||
child: Container(color: colorScheme.secondary),
|
||||
),
|
||||
GridItem(
|
||||
mainAxisCellCount: 1,
|
||||
child: Container(
|
||||
color: colorScheme.tertiary,
|
||||
),
|
||||
)
|
||||
child: Container(color: colorScheme.tertiary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -73,11 +67,8 @@ class ColorSchemeBox extends StatelessWidget {
|
||||
const Positioned(
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
child: Icon(
|
||||
Icons.colorize,
|
||||
size: 20,
|
||||
),
|
||||
)
|
||||
child: Icon(Icons.colorize, size: 20),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -112,9 +103,7 @@ class PrimaryColorBox extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
return Theme(
|
||||
data: themeData.copyWith(
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
data: themeData.copyWith(colorScheme: colorScheme),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,30 +18,21 @@ class RadioDelegate<T> extends Delegate {
|
||||
final T value;
|
||||
final void Function()? onTab;
|
||||
|
||||
const RadioDelegate({
|
||||
required this.value,
|
||||
this.onTab,
|
||||
});
|
||||
const RadioDelegate({required this.value, this.onTab});
|
||||
}
|
||||
|
||||
class SwitchDelegate<T> extends Delegate {
|
||||
final bool value;
|
||||
final ValueChanged<bool>? onChanged;
|
||||
|
||||
const SwitchDelegate({
|
||||
required this.value,
|
||||
this.onChanged,
|
||||
});
|
||||
const SwitchDelegate({required this.value, this.onChanged});
|
||||
}
|
||||
|
||||
class CheckboxDelegate<T> extends Delegate {
|
||||
final bool value;
|
||||
final ValueChanged<bool?>? onChanged;
|
||||
|
||||
const CheckboxDelegate({
|
||||
this.value = false,
|
||||
this.onChanged,
|
||||
});
|
||||
const CheckboxDelegate({this.value = false, this.onChanged});
|
||||
}
|
||||
|
||||
class OpenDelegate extends Delegate {
|
||||
@@ -129,6 +120,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
final double? horizontalTitleGap;
|
||||
final TextStyle? titleTextStyle;
|
||||
final TextStyle? subtitleTextStyle;
|
||||
final double minVerticalPadding;
|
||||
final void Function()? onTap;
|
||||
|
||||
const ListItem({
|
||||
@@ -143,6 +135,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.onTap,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : delegate = const Delegate();
|
||||
|
||||
@@ -158,6 +151,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -173,6 +167,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -188,6 +183,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -203,6 +199,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
@@ -217,9 +214,10 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
|
||||
const ListItem.switchItem({
|
||||
super.key,
|
||||
@@ -232,9 +230,10 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
}) : trailing = null,
|
||||
onTap = null;
|
||||
|
||||
const ListItem.radio({
|
||||
super.key,
|
||||
@@ -247,9 +246,10 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.minVerticalPadding = 12,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : leading = null,
|
||||
onTap = null;
|
||||
}) : leading = null,
|
||||
onTap = null;
|
||||
|
||||
Widget _buildListTile({
|
||||
void Function()? onTap,
|
||||
@@ -264,7 +264,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
leading: leading ?? this.leading,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
title: title,
|
||||
minVerticalPadding: 12,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
subtitle: subtitle,
|
||||
titleAlignment: tileTitleAlignment,
|
||||
onTap: onTap,
|
||||
@@ -282,7 +282,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
closedBuilder: (_, action) {
|
||||
openAction() {
|
||||
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
|
||||
if (!isMobile || system.isDesktop) {
|
||||
if (!isMobile) {
|
||||
showExtend(
|
||||
context,
|
||||
props: ExtendProps(
|
||||
@@ -306,9 +306,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
action();
|
||||
}
|
||||
|
||||
return _buildListTile(
|
||||
onTap: openAction,
|
||||
);
|
||||
return _buildListTile(onTap: openAction);
|
||||
},
|
||||
openBuilder: (_, action) {
|
||||
return openDelegate.wrap
|
||||
@@ -422,9 +420,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
return _buildListTile(
|
||||
onTap: onTap,
|
||||
);
|
||||
return _buildListTile(onTap: onTap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,13 +444,9 @@ class ListHeader extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: padding ??
|
||||
const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 8,
|
||||
top: 24,
|
||||
bottom: 8,
|
||||
),
|
||||
padding:
|
||||
padding ??
|
||||
const EdgeInsets.only(left: 16, right: 8, top: 24, bottom: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -466,19 +458,18 @@ class ListHeader extends StatelessWidget {
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant
|
||||
.opacity80,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.opacity80,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
if (subTitle != null)
|
||||
Text(
|
||||
subTitle!,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -486,12 +477,7 @@ class ListHeader extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
...genActions(
|
||||
actions,
|
||||
space: space,
|
||||
),
|
||||
],
|
||||
children: [...genActions(actions, space: space)],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -506,18 +492,11 @@ List<Widget> generateSection({
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = separated
|
||||
? items.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
? items.separated(const Divider(height: 0))
|
||||
: items;
|
||||
return [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
),
|
||||
ListHeader(title: title, actions: actions),
|
||||
...genItems,
|
||||
];
|
||||
}
|
||||
@@ -528,22 +507,26 @@ Widget generateSectionV2({
|
||||
List<Widget>? actions,
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = items
|
||||
.map<Widget>((item) {
|
||||
return ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
radius: 0,
|
||||
child: item,
|
||||
),
|
||||
);
|
||||
})
|
||||
.separated(const Divider(height: 2, color: Colors.transparent));
|
||||
return Column(
|
||||
children: [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
),
|
||||
CommonCard(
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
child: Column(
|
||||
children: [
|
||||
...items,
|
||||
],
|
||||
),
|
||||
)
|
||||
ListHeader(title: title, actions: actions),
|
||||
ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Column(children: [...genItems]),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -555,18 +538,10 @@ List<Widget> generateInfoSection({
|
||||
bool separated = true,
|
||||
}) {
|
||||
final genItems = separated
|
||||
? items.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
? items.separated(const Divider(height: 0))
|
||||
: items;
|
||||
return [
|
||||
if (items.isNotEmpty)
|
||||
InfoHeader(
|
||||
info: info,
|
||||
actions: actions,
|
||||
),
|
||||
if (items.isNotEmpty) InfoHeader(info: info, actions: actions),
|
||||
...genItems,
|
||||
];
|
||||
}
|
||||
@@ -575,8 +550,6 @@ Widget generateListView(List<Widget> items) {
|
||||
return ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (_, index) => items[index],
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 16,
|
||||
),
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,19 +2,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
typedef CloseContainerActionCallback<S> = void Function({S? returnValue});
|
||||
typedef OpenContainerBuilder<S> = Widget Function(
|
||||
BuildContext context,
|
||||
CloseContainerActionCallback<S> action,
|
||||
);
|
||||
typedef CloseContainerBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
VoidCallback action,
|
||||
);
|
||||
typedef OpenContainerBuilder<S> =
|
||||
Widget Function(
|
||||
BuildContext context,
|
||||
CloseContainerActionCallback<S> action,
|
||||
);
|
||||
typedef CloseContainerBuilder =
|
||||
Widget Function(BuildContext context, VoidCallback action);
|
||||
|
||||
enum ContainerTransitionType {
|
||||
fade,
|
||||
fadeThrough,
|
||||
}
|
||||
enum ContainerTransitionType { fade, fadeThrough }
|
||||
|
||||
typedef ClosedCallback<S> = void Function(S data);
|
||||
|
||||
@@ -56,20 +52,23 @@ class _OpenContainerState<T> extends State<OpenContainer<T?>> {
|
||||
Future<void> openContainer() async {
|
||||
final Color middleColor =
|
||||
widget.middleColor ?? Theme.of(context).canvasColor;
|
||||
final T? data = await Navigator.of(
|
||||
context,
|
||||
rootNavigator: widget.useRootNavigator,
|
||||
).push(_OpenContainerRoute<T>(
|
||||
middleColor: middleColor,
|
||||
closedBuilder: widget.closedBuilder,
|
||||
openBuilder: widget.openBuilder,
|
||||
hideableKey: _hideableKey,
|
||||
closedBuilderKey: _closedBuilderKey,
|
||||
transitionDuration: widget.transitionDuration,
|
||||
transitionType: widget.transitionType,
|
||||
useRootNavigator: widget.useRootNavigator,
|
||||
routeSettings: widget.routeSettings,
|
||||
));
|
||||
final T? data =
|
||||
await Navigator.of(
|
||||
context,
|
||||
rootNavigator: widget.useRootNavigator,
|
||||
).push(
|
||||
_OpenContainerRoute<T>(
|
||||
middleColor: middleColor,
|
||||
closedBuilder: widget.closedBuilder,
|
||||
openBuilder: widget.openBuilder,
|
||||
hideableKey: _hideableKey,
|
||||
closedBuilderKey: _closedBuilderKey,
|
||||
transitionDuration: widget.transitionDuration,
|
||||
transitionType: widget.transitionType,
|
||||
useRootNavigator: widget.useRootNavigator,
|
||||
routeSettings: widget.routeSettings,
|
||||
),
|
||||
);
|
||||
if (widget.onClosed != null) {
|
||||
widget.onClosed!(data);
|
||||
}
|
||||
@@ -97,10 +96,7 @@ class _OpenContainerState<T> extends State<OpenContainer<T?>> {
|
||||
}
|
||||
|
||||
class _Hideable extends StatefulWidget {
|
||||
const _Hideable({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const _Hideable({super.key, required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@@ -161,9 +157,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
required this.transitionType,
|
||||
required this.useRootNavigator,
|
||||
required RouteSettings? routeSettings,
|
||||
}) : _closedOpacityTween = _getClosedOpacityTween(transitionType),
|
||||
_openOpacityTween = _getOpenOpacityTween(transitionType),
|
||||
super(settings: routeSettings);
|
||||
}) : _closedOpacityTween = _getClosedOpacityTween(transitionType),
|
||||
_openOpacityTween = _getOpenOpacityTween(transitionType),
|
||||
super(settings: routeSettings);
|
||||
|
||||
static _FlippableTweenSequence<Color?> _getColorTween({
|
||||
required ContainerTransitionType transitionType,
|
||||
@@ -173,99 +169,89 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
}) {
|
||||
switch (transitionType) {
|
||||
case ContainerTransitionType.fade:
|
||||
return _FlippableTweenSequence<Color?>(
|
||||
<TweenSequenceItem<Color?>>[
|
||||
TweenSequenceItem<Color>(
|
||||
tween: ConstantTween<Color>(closedColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: closedColor, end: openColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color>(
|
||||
tween: ConstantTween<Color>(openColor),
|
||||
weight: 3 / 5,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<Color?>(<TweenSequenceItem<Color?>>[
|
||||
TweenSequenceItem<Color>(
|
||||
tween: ConstantTween<Color>(closedColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: closedColor, end: openColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color>(
|
||||
tween: ConstantTween<Color>(openColor),
|
||||
weight: 3 / 5,
|
||||
),
|
||||
]);
|
||||
case ContainerTransitionType.fadeThrough:
|
||||
return _FlippableTweenSequence<Color?>(
|
||||
<TweenSequenceItem<Color?>>[
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: closedColor, end: middleColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: middleColor, end: openColor),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<Color?>(<TweenSequenceItem<Color?>>[
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: closedColor, end: middleColor),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<Color?>(
|
||||
tween: ColorTween(begin: middleColor, end: openColor),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
static _FlippableTweenSequence<double> _getClosedOpacityTween(
|
||||
ContainerTransitionType transitionType) {
|
||||
ContainerTransitionType transitionType,
|
||||
) {
|
||||
switch (transitionType) {
|
||||
case ContainerTransitionType.fade:
|
||||
return _FlippableTweenSequence<double>(
|
||||
<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(1.0),
|
||||
weight: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(1.0),
|
||||
weight: 1,
|
||||
),
|
||||
]);
|
||||
case ContainerTransitionType.fadeThrough:
|
||||
return _FlippableTweenSequence<double>(
|
||||
<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 1.0, end: 0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 1.0, end: 0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
static _FlippableTweenSequence<double> _getOpenOpacityTween(
|
||||
ContainerTransitionType transitionType) {
|
||||
ContainerTransitionType transitionType,
|
||||
) {
|
||||
switch (transitionType) {
|
||||
case ContainerTransitionType.fade:
|
||||
return _FlippableTweenSequence<double>(
|
||||
<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(1.0),
|
||||
weight: 3 / 5,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(1.0),
|
||||
weight: 3 / 5,
|
||||
),
|
||||
]);
|
||||
case ContainerTransitionType.fadeThrough:
|
||||
return _FlippableTweenSequence<double>(
|
||||
<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
],
|
||||
);
|
||||
return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[
|
||||
TweenSequenceItem<double>(
|
||||
tween: ConstantTween<double>(0.0),
|
||||
weight: 1 / 5,
|
||||
),
|
||||
TweenSequenceItem<double>(
|
||||
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||
weight: 4 / 5,
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,8 +311,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (hideableKey.currentState?.isVisible == false) {
|
||||
SchedulerBinding.instance
|
||||
.addPostFrameCallback((Duration d) => _toggleHideable(hide: false));
|
||||
SchedulerBinding.instance.addPostFrameCallback(
|
||||
(Duration d) => _toggleHideable(hide: false),
|
||||
);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
@@ -343,10 +330,12 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
required BuildContext navigatorContext,
|
||||
bool delayForSourceRoute = false,
|
||||
}) {
|
||||
final RenderBox navigator = Navigator.of(
|
||||
navigatorContext,
|
||||
rootNavigator: useRootNavigator,
|
||||
).context.findRenderObject()! as RenderBox;
|
||||
final RenderBox navigator =
|
||||
Navigator.of(
|
||||
navigatorContext,
|
||||
rootNavigator: useRootNavigator,
|
||||
).context.findRenderObject()!
|
||||
as RenderBox;
|
||||
final Size navSize = _getSize(navigator);
|
||||
_rectTween.end = Offset.zero & navSize;
|
||||
|
||||
@@ -359,8 +348,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
}
|
||||
|
||||
if (delayForSourceRoute) {
|
||||
SchedulerBinding.instance
|
||||
.addPostFrameCallback(takeMeasurementsInSourceRoute);
|
||||
SchedulerBinding.instance.addPostFrameCallback(
|
||||
takeMeasurementsInSourceRoute,
|
||||
);
|
||||
} else {
|
||||
takeMeasurementsInSourceRoute();
|
||||
}
|
||||
@@ -451,8 +441,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
final Animation<double> curvedAnimation = CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
reverseCurve:
|
||||
_transitionWasInterrupted ? null : Curves.fastOutSlowIn.flipped,
|
||||
reverseCurve: _transitionWasInterrupted
|
||||
? null
|
||||
: Curves.fastOutSlowIn.flipped,
|
||||
);
|
||||
TweenSequence<Color?>? colorTween;
|
||||
TweenSequence<double>? closedOpacityTween, openOpacityTween;
|
||||
@@ -508,8 +499,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
child: (hideableKey.currentState?.isInTree ?? false)
|
||||
? null
|
||||
: FadeTransition(
|
||||
opacity:
|
||||
closedOpacityTween!.animate(animation),
|
||||
opacity: closedOpacityTween!.animate(
|
||||
animation,
|
||||
),
|
||||
child: Builder(
|
||||
key: closedBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
@@ -521,22 +513,18 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Open child fading in.
|
||||
FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
OverflowBox(
|
||||
maxWidth: _rectTween.end!.width,
|
||||
maxHeight: _rectTween.end!.height,
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: _rectTween.end!.width,
|
||||
height: _rectTween.end!.height,
|
||||
child: FadeTransition(
|
||||
opacity: openOpacityTween!.animate(animation),
|
||||
child: Builder(
|
||||
key: _openBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
return openBuilder(context, closeContainer);
|
||||
},
|
||||
),
|
||||
child: FadeTransition(
|
||||
opacity: openOpacityTween!.animate(animation),
|
||||
child: Builder(
|
||||
key: _openBuilderKey,
|
||||
builder: (BuildContext context) {
|
||||
return openBuilder(context, closeContainer);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -578,10 +566,12 @@ class _FlippableTweenSequence<T> extends TweenSequence<T> {
|
||||
if (_flipped == null) {
|
||||
final List<TweenSequenceItem<T>> newItems = <TweenSequenceItem<T>>[];
|
||||
for (int i = 0; i < _items.length; i++) {
|
||||
newItems.add(TweenSequenceItem<T>(
|
||||
tween: _items[i].tween,
|
||||
weight: _items[_items.length - 1 - i].weight,
|
||||
));
|
||||
newItems.add(
|
||||
TweenSequenceItem<T>(
|
||||
tween: _items[i].tween,
|
||||
weight: _items[_items.length - 1 - i].weight,
|
||||
),
|
||||
);
|
||||
}
|
||||
_flipped = _FlippableTweenSequence<T>(newItems);
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ class _ShadePainter extends CustomPainter {
|
||||
effectiveSquareRadius * 2,
|
||||
effectiveSquareRadius * 2,
|
||||
);
|
||||
final RRect rRect = RRect.fromRectAndRadius(
|
||||
final RSuperellipse rSuperellipse = RSuperellipse.fromRectAndRadius(
|
||||
rectBox,
|
||||
Radius.circular(trackBorderRadius),
|
||||
);
|
||||
@@ -254,8 +254,8 @@ class _ShadePainter extends CustomPainter {
|
||||
HSVColor.fromAHSV(1, colorHue, 1, 1).toColor(),
|
||||
],
|
||||
).createShader(rectBox);
|
||||
canvas.drawRRect(
|
||||
rRect,
|
||||
canvas.drawRSuperellipse(
|
||||
rSuperellipse,
|
||||
Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..shader = horizontal,
|
||||
@@ -266,8 +266,8 @@ class _ShadePainter extends CustomPainter {
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[Colors.transparent, Colors.black],
|
||||
).createShader(rectBox);
|
||||
canvas.drawRRect(
|
||||
rRect,
|
||||
canvas.drawRSuperellipse(
|
||||
rSuperellipse,
|
||||
Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..shader = vertical,
|
||||
|
||||
@@ -38,10 +38,9 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
Widget child,
|
||||
) {
|
||||
final align = Alignment.topRight;
|
||||
final animationValue = CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeIn,
|
||||
).value;
|
||||
final curveAnimation = animation
|
||||
.drive(Tween(begin: 0.0, end: 1.0))
|
||||
.drive(CurveTween(curve: Curves.easeOutBack));
|
||||
return SafeArea(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: offsetNotifier,
|
||||
@@ -58,15 +57,17 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (_, Widget? child) {
|
||||
return Opacity(
|
||||
opacity: 0.1 + 0.9 * animationValue,
|
||||
child: Transform.scale(
|
||||
builder: (_, child) {
|
||||
return FadeTransition(
|
||||
opacity: curveAnimation,
|
||||
child: ScaleTransition(
|
||||
alignment: align,
|
||||
scale: 0.7 + 0.3 * animationValue,
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, -10) * (1 - animationValue),
|
||||
child: child!,
|
||||
scale: curveAnimation,
|
||||
child: SlideTransition(
|
||||
position: curveAnimation.drive(
|
||||
Tween(begin: const Offset(0, -0.02), end: Offset.zero),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -78,7 +79,7 @@ class CommonPopupRoute<T> extends PopupRoute<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 150);
|
||||
Duration get transitionDuration => const Duration(milliseconds: 250);
|
||||
}
|
||||
|
||||
class PopupController extends ValueNotifier<bool> {
|
||||
@@ -270,8 +271,8 @@ class CommonPopupMenu extends StatelessWidget {
|
||||
elevation: 12,
|
||||
color: context.colorScheme.surfaceContainer,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -18,7 +18,6 @@ class CommonScaffold extends StatefulWidget {
|
||||
final Widget body;
|
||||
final Color? backgroundColor;
|
||||
final String? title;
|
||||
final Widget? leading;
|
||||
final List<Widget>? actions;
|
||||
final bool? centerTitle;
|
||||
final Widget? floatingActionButton;
|
||||
@@ -31,7 +30,6 @@ class CommonScaffold extends StatefulWidget {
|
||||
this.appBar,
|
||||
required this.body,
|
||||
this.backgroundColor,
|
||||
this.leading,
|
||||
this.title,
|
||||
this.actions,
|
||||
this.centerTitle,
|
||||
@@ -163,7 +161,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
_keywordsNotifier.value = keywords;
|
||||
}
|
||||
|
||||
Widget? _buildLeading() {
|
||||
Widget? _buildLeading(VoidCallback? backAction) {
|
||||
if (_isEdit) {
|
||||
return IconButton(
|
||||
onPressed: _appBarState.value.editState?.onExit,
|
||||
@@ -176,7 +174,16 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
icon: Icon(Icons.arrow_back),
|
||||
);
|
||||
}
|
||||
return widget.leading;
|
||||
return backAction != null
|
||||
? BackButton(
|
||||
onPressed: () {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
backAction();
|
||||
},
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
Widget _buildTitle(AppBarSearchState? startState) {
|
||||
@@ -251,7 +258,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
PreferredSizeWidget _buildAppBar(VoidCallback? backAction) {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
@@ -263,8 +270,11 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
builder: (_, state, _) {
|
||||
return _buildAppBarWrap(
|
||||
AppBar(
|
||||
automaticallyImplyLeading: backAction != null
|
||||
? false
|
||||
: true,
|
||||
centerTitle: widget.centerTitle ?? false,
|
||||
leading: _buildLeading(),
|
||||
leading: _buildLeading(backAction),
|
||||
title: _buildTitle(state.searchState),
|
||||
actions: _buildActions(
|
||||
state.searchState != null,
|
||||
@@ -285,6 +295,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(widget.appBar != null || widget.title != null);
|
||||
final backActionProvider = CommonScaffoldBackActionProvider.of(context);
|
||||
final body = SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -327,7 +338,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
),
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(),
|
||||
appBar: _buildAppBar(backActionProvider?.backAction),
|
||||
body: body,
|
||||
resizeToAvoidBottomInset: true,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
@@ -349,3 +360,23 @@ List<Widget> genActions(List<Widget> actions, {double? space}) {
|
||||
SizedBox(width: 8),
|
||||
];
|
||||
}
|
||||
|
||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||
final VoidCallback? backAction;
|
||||
|
||||
const CommonScaffoldBackActionProvider({
|
||||
super.key,
|
||||
required this.backAction,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static CommonScaffoldBackActionProvider? of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<CommonScaffoldBackActionProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CommonScaffoldBackActionProvider oldWidget) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,7 @@ class ExtendProps {
|
||||
});
|
||||
}
|
||||
|
||||
enum SheetType {
|
||||
page,
|
||||
bottomSheet,
|
||||
sideSheet,
|
||||
}
|
||||
enum SheetType { page, bottomSheet, sideSheet }
|
||||
|
||||
typedef SheetBuilder = Widget Function(BuildContext context, SheetType type);
|
||||
|
||||
@@ -55,28 +51,24 @@ Future<T?> showSheet<T>({
|
||||
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
|
||||
return switch (isMobile) {
|
||||
true => showModalBottomSheet<T>(
|
||||
context: context,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
builder: (_) {
|
||||
return SafeArea(
|
||||
child: builder(context, SheetType.bottomSheet),
|
||||
);
|
||||
},
|
||||
showDragHandle: false,
|
||||
useSafeArea: props.useSafeArea,
|
||||
),
|
||||
context: context,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
builder: (_) {
|
||||
return SafeArea(child: builder(context, SheetType.bottomSheet));
|
||||
},
|
||||
showDragHandle: false,
|
||||
useSafeArea: props.useSafeArea,
|
||||
),
|
||||
false => showModalSideSheet<T>(
|
||||
useSafeArea: props.useSafeArea,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
context: context,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: props.maxWidth ?? 360,
|
||||
),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (_) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
useSafeArea: props.useSafeArea,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (_) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,21 +79,16 @@ Future<T?> showExtend<T>(
|
||||
}) {
|
||||
final isMobile = globalState.appState.viewMode == ViewMode.mobile;
|
||||
return switch (isMobile || props.forceFull) {
|
||||
true => BaseNavigator.push(
|
||||
context,
|
||||
builder(context, SheetType.page),
|
||||
),
|
||||
true => BaseNavigator.push(context, builder(context, SheetType.page)),
|
||||
false => showModalSideSheet<T>(
|
||||
useSafeArea: props.useSafeArea,
|
||||
context: context,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: props.maxWidth ?? 360,
|
||||
),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (context) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
useSafeArea: props.useSafeArea,
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (context) {
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -134,13 +121,11 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
automaticallyImplyLeading: bottomSheet
|
||||
? false
|
||||
: widget.actions.isEmpty && sideSheet
|
||||
? false
|
||||
: true,
|
||||
? false
|
||||
: true,
|
||||
centerTitle: bottomSheet,
|
||||
backgroundColor: backgroundColor,
|
||||
title: Text(
|
||||
widget.title,
|
||||
),
|
||||
title: Text(widget.title),
|
||||
actions: genActions([
|
||||
if (widget.actions.isEmpty && sideSheet) CloseButton(),
|
||||
...widget.actions,
|
||||
@@ -150,9 +135,11 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
final handleSize = Size(32, 4);
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
decoration: ShapeDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -163,17 +150,16 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
alignment: Alignment.center,
|
||||
height: handleSize.height,
|
||||
width: handleSize.width,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(handleSize.height / 2),
|
||||
decoration: ShapeDecoration(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(handleSize.height / 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
appBar,
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: widget.body,
|
||||
)
|
||||
Flexible(flex: 1, child: widget.body),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -82,12 +82,12 @@ class _SideSheetState extends State<SideSheet> {
|
||||
final Color surfaceTintColor = colorScheme.surfaceTint;
|
||||
final Color shadowColor = widget.shadowColor ?? Colors.transparent;
|
||||
final double elevation = widget.elevation ?? 0;
|
||||
final ShapeBorder shape = widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
);
|
||||
final ShapeBorder shape =
|
||||
widget.shape ??
|
||||
RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(0));
|
||||
|
||||
final BoxConstraints constraints = widget.constraints ??
|
||||
final BoxConstraints constraints =
|
||||
widget.constraints ??
|
||||
const BoxConstraints(maxWidth: 320, minWidth: 320);
|
||||
|
||||
final Clip clipBehavior = widget.clipBehavior ?? Clip.none;
|
||||
@@ -103,10 +103,7 @@ class _SideSheetState extends State<SideSheet> {
|
||||
child: widget.builder(context),
|
||||
);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: constraints,
|
||||
child: sideSheet,
|
||||
);
|
||||
return ConstrainedBox(constraints: constraints, child: sideSheet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +125,8 @@ class _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
|
||||
|
||||
@override
|
||||
_RenderSideSheetLayoutWithSizeListener createRenderObject(
|
||||
BuildContext context) {
|
||||
BuildContext context,
|
||||
) {
|
||||
return _RenderSideSheetLayoutWithSizeListener(
|
||||
onChildSizeChanged: onChildSizeChanged,
|
||||
animationValue: animationValue,
|
||||
@@ -138,8 +136,10 @@ class _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context,
|
||||
_RenderSideSheetLayoutWithSizeListener renderObject) {
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderSideSheetLayoutWithSizeListener renderObject,
|
||||
) {
|
||||
renderObject.onChildSizeChanged = onChildSizeChanged;
|
||||
renderObject.animationValue = animationValue;
|
||||
renderObject.isScrollControlled = isScrollControlled;
|
||||
@@ -155,12 +155,12 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
required double animationValue,
|
||||
required bool isScrollControlled,
|
||||
required double scrollControlDisabledMaxHeightRatio,
|
||||
}) : _onChildSizeChanged = onChildSizeChanged,
|
||||
_animationValue = animationValue,
|
||||
_isScrollControlled = isScrollControlled,
|
||||
_scrollControlDisabledMaxHeightRatio =
|
||||
scrollControlDisabledMaxHeightRatio,
|
||||
super(child);
|
||||
}) : _onChildSizeChanged = onChildSizeChanged,
|
||||
_animationValue = animationValue,
|
||||
_isScrollControlled = isScrollControlled,
|
||||
_scrollControlDisabledMaxHeightRatio =
|
||||
scrollControlDisabledMaxHeightRatio,
|
||||
super(child);
|
||||
|
||||
Size _lastSize = Size.zero;
|
||||
|
||||
@@ -219,8 +219,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
final double width =
|
||||
_getSize(BoxConstraints.tightForFinite(height: height)).width;
|
||||
final double width = _getSize(
|
||||
BoxConstraints.tightForFinite(height: height),
|
||||
).width;
|
||||
if (width.isFinite) {
|
||||
return width;
|
||||
}
|
||||
@@ -229,8 +230,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicWidth(double height) {
|
||||
final double width =
|
||||
_getSize(BoxConstraints.tightForFinite(height: height)).width;
|
||||
final double width = _getSize(
|
||||
BoxConstraints.tightForFinite(height: height),
|
||||
).width;
|
||||
if (width.isFinite) {
|
||||
return width;
|
||||
}
|
||||
@@ -239,8 +241,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
final double height =
|
||||
_getSize(BoxConstraints.tightForFinite(width: width)).height;
|
||||
final double height = _getSize(
|
||||
BoxConstraints.tightForFinite(width: width),
|
||||
).height;
|
||||
if (height.isFinite) {
|
||||
return height;
|
||||
}
|
||||
@@ -249,8 +252,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicHeight(double width) {
|
||||
final double height =
|
||||
_getSize(BoxConstraints.tightForFinite(width: width)).height;
|
||||
final double height = _getSize(
|
||||
BoxConstraints.tightForFinite(width: width),
|
||||
).height;
|
||||
if (height.isFinite) {
|
||||
return height;
|
||||
}
|
||||
@@ -263,9 +267,7 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
}
|
||||
|
||||
BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {
|
||||
return BoxConstraints(
|
||||
maxHeight: constraints.maxHeight,
|
||||
);
|
||||
return BoxConstraints(maxHeight: constraints.maxHeight);
|
||||
}
|
||||
|
||||
Offset _getPositionForChild(Size size, Size childSize) {
|
||||
@@ -276,8 +278,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
void performLayout() {
|
||||
size = _getSize(constraints);
|
||||
if (child != null) {
|
||||
final BoxConstraints childConstraints =
|
||||
_getConstraintsForChild(constraints);
|
||||
final BoxConstraints childConstraints = _getConstraintsForChild(
|
||||
constraints,
|
||||
);
|
||||
assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
|
||||
child!.layout(
|
||||
childConstraints,
|
||||
@@ -288,8 +291,9 @@ class _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {
|
||||
size,
|
||||
childConstraints.isTight ? childConstraints.smallest : child!.size,
|
||||
);
|
||||
final Size childSize =
|
||||
childConstraints.isTight ? childConstraints.smallest : child!.size;
|
||||
final Size childSize = childConstraints.isTight
|
||||
? childConstraints.smallest
|
||||
: child!.size;
|
||||
|
||||
if (_lastSize != childSize) {
|
||||
_lastSize = childSize;
|
||||
@@ -354,8 +358,9 @@ class _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
final MaterialLocalizations localizations =
|
||||
MaterialLocalizations.of(context);
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(
|
||||
context,
|
||||
);
|
||||
final String routeLabel = _getRouteLabel(localizations);
|
||||
|
||||
return AnimatedBuilder(
|
||||
@@ -406,26 +411,27 @@ class _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {
|
||||
}
|
||||
|
||||
class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
ModalSideSheetRoute(
|
||||
{required this.builder,
|
||||
this.capturedThemes,
|
||||
this.barrierLabel,
|
||||
this.barrierOnTapHint,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
this.clipBehavior,
|
||||
this.constraints,
|
||||
this.modalBarrierColor,
|
||||
this.isDismissible = true,
|
||||
this.isScrollControlled = false,
|
||||
this.scrollControlDisabledMaxHeightRatio =
|
||||
_defaultScrollControlDisabledMaxHeightRatio,
|
||||
super.settings,
|
||||
this.transitionAnimationController,
|
||||
this.anchorPoint,
|
||||
this.useSafeArea = false,
|
||||
super.filter});
|
||||
ModalSideSheetRoute({
|
||||
required this.builder,
|
||||
this.capturedThemes,
|
||||
this.barrierLabel,
|
||||
this.barrierOnTapHint,
|
||||
this.backgroundColor,
|
||||
this.elevation,
|
||||
this.shape,
|
||||
this.clipBehavior,
|
||||
this.constraints,
|
||||
this.modalBarrierColor,
|
||||
this.isDismissible = true,
|
||||
this.isScrollControlled = false,
|
||||
this.scrollControlDisabledMaxHeightRatio =
|
||||
_defaultScrollControlDisabledMaxHeightRatio,
|
||||
super.settings,
|
||||
this.transitionAnimationController,
|
||||
this.anchorPoint,
|
||||
this.useSafeArea = false,
|
||||
super.filter,
|
||||
});
|
||||
|
||||
final WidgetBuilder builder;
|
||||
|
||||
@@ -504,8 +510,11 @@ class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
Widget buildPage(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
final Widget content = DisplayFeatureSubScreen(
|
||||
anchorPoint: anchorPoint,
|
||||
child: Builder(
|
||||
@@ -539,9 +548,7 @@ class ModalSideSheetRoute<T> extends PopupRoute<T> {
|
||||
ColorTween(
|
||||
begin: barrierColor.opacity0,
|
||||
end: barrierColor,
|
||||
).chain(
|
||||
CurveTween(curve: barrierCurve),
|
||||
),
|
||||
).chain(CurveTween(curve: barrierCurve)),
|
||||
);
|
||||
return AnimatedModalBarrier(
|
||||
color: color,
|
||||
@@ -587,32 +594,39 @@ Future<T?> showModalSideSheet<T>({
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
final NavigatorState navigator =
|
||||
Navigator.of(context, rootNavigator: useRootNavigator);
|
||||
final NavigatorState navigator = Navigator.of(
|
||||
context,
|
||||
rootNavigator: useRootNavigator,
|
||||
);
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
return navigator.push(ModalSideSheetRoute<T>(
|
||||
builder: builder,
|
||||
filter: filter,
|
||||
capturedThemes:
|
||||
InheritedTheme.capture(from: context, to: navigator.context),
|
||||
isScrollControlled: isScrollControlled,
|
||||
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
|
||||
barrierLabel: barrierLabel ?? localizations.scrimLabel,
|
||||
barrierOnTapHint:
|
||||
localizations.scrimOnTapHint(localizations.bottomSheetLabel),
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
isDismissible: isDismissible,
|
||||
modalBarrierColor:
|
||||
barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||
settings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
useSafeArea: useSafeArea,
|
||||
));
|
||||
return navigator.push(
|
||||
ModalSideSheetRoute<T>(
|
||||
builder: builder,
|
||||
filter: filter,
|
||||
capturedThemes: InheritedTheme.capture(
|
||||
from: context,
|
||||
to: navigator.context,
|
||||
),
|
||||
isScrollControlled: isScrollControlled,
|
||||
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
|
||||
barrierLabel: barrierLabel ?? localizations.scrimLabel,
|
||||
barrierOnTapHint: localizations.scrimOnTapHint(
|
||||
localizations.bottomSheetLabel,
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
isDismissible: isDismissible,
|
||||
modalBarrierColor:
|
||||
barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||
settings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
useSafeArea: useSafeArea,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// class ModalAppBar extends StatelessWidget {
|
||||
|
||||
@@ -8,8 +8,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
const EdgeInsetsGeometry _kHorizontalItemPadding =
|
||||
EdgeInsets.symmetric(vertical: 2, horizontal: 3);
|
||||
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 3,
|
||||
);
|
||||
|
||||
const Radius _kCornerRadius = Radius.circular(9);
|
||||
|
||||
@@ -63,11 +65,11 @@ class CommonTabBar<T extends Object> extends StatefulWidget {
|
||||
this.padding = _kHorizontalItemPadding,
|
||||
this.backgroundColor,
|
||||
this.proportionalWidth = false,
|
||||
}) : assert(children.length >= 2),
|
||||
assert(
|
||||
groupValue == null || children.keys.contains(groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
);
|
||||
}) : assert(children.length >= 2),
|
||||
assert(
|
||||
groupValue == null || children.keys.contains(groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
);
|
||||
final Map<T, Widget> children;
|
||||
final Set<T> disabledChildren;
|
||||
final T? groupValue;
|
||||
@@ -190,8 +192,9 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
void _playThumbScaleAnimation({required bool isExpanding}) {
|
||||
thumbScaleAnimation = thumbScaleController.drive(
|
||||
Tween<double>(
|
||||
begin: thumbScaleAnimation.value,
|
||||
end: isExpanding ? 1 : _kMinThumbScale),
|
||||
begin: thumbScaleAnimation.value,
|
||||
end: isExpanding ? 1 : _kMinThumbScale,
|
||||
),
|
||||
);
|
||||
thumbScaleController.animateWith(_kThumbSpringAnimationSimulation);
|
||||
}
|
||||
@@ -231,8 +234,9 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
void onDown(DragDownDetails details) {
|
||||
final T touchDownSegment = segmentForXPosition(details.localPosition.dx);
|
||||
_startedOnSelectedSegment = touchDownSegment == highlighted;
|
||||
_startedOnDisabledSegment =
|
||||
widget.disabledChildren.contains(touchDownSegment);
|
||||
_startedOnDisabledSegment = widget.disabledChildren.contains(
|
||||
touchDownSegment,
|
||||
);
|
||||
if (widget.disabledChildren.contains(touchDownSegment)) {
|
||||
return;
|
||||
}
|
||||
@@ -373,8 +377,10 @@ class _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
padding: widget.padding.resolve(Directionality.of(context)),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(_kCornerRadius),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: const BorderRadius.all(_kCornerRadius),
|
||||
),
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
@@ -455,8 +461,9 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
end: widget.shouldScaleContent ? _kMinThumbScale : 1.0,
|
||||
),
|
||||
);
|
||||
highlightPressScaleController
|
||||
.animateWith(_kThumbSpringAnimationSimulation);
|
||||
highlightPressScaleController.animateWith(
|
||||
_kThumbSpringAnimationSimulation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,20 +487,21 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
AnimatedOpacity(
|
||||
opacity:
|
||||
widget.shouldFadeoutContent ? _kContentPressedMinOpacity : 1,
|
||||
opacity: widget.shouldFadeoutContent
|
||||
? _kContentPressedMinOpacity
|
||||
: 1,
|
||||
duration: _kOpacityAnimationDuration,
|
||||
curve: Curves.ease,
|
||||
child: AnimatedDefaultTextStyle(
|
||||
style: DefaultTextStyle.of(context).style.merge(
|
||||
TextStyle(
|
||||
fontWeight: widget.highlighted
|
||||
? _kHighlightedFontWeight
|
||||
: _kFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
color: widget.enabled ? null : _kDisabledContentColor,
|
||||
),
|
||||
),
|
||||
TextStyle(
|
||||
fontWeight: widget.highlighted
|
||||
? _kHighlightedFontWeight
|
||||
: _kFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
color: widget.enabled ? null : _kDisabledContentColor,
|
||||
),
|
||||
),
|
||||
duration: _kHighlightAnimationDuration,
|
||||
curve: Curves.ease,
|
||||
child: ScaleTransition(
|
||||
@@ -505,7 +513,9 @@ class _SegmentState<T> extends State<_Segment<T>>
|
||||
),
|
||||
DefaultTextStyle.merge(
|
||||
style: const TextStyle(
|
||||
fontWeight: _kHighlightedFontWeight, fontSize: _kFontSize),
|
||||
fontWeight: _kHighlightedFontWeight,
|
||||
fontSize: _kFontSize,
|
||||
),
|
||||
child: widget.child,
|
||||
),
|
||||
],
|
||||
@@ -570,9 +580,7 @@ class _SegmentSeparatorState extends State<_SegmentSeparator>
|
||||
return Padding(
|
||||
padding: _kSeparatorInset,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.transparent),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
@@ -612,7 +620,9 @@ class _CommonTabBarRenderWidget<T extends Object>
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context, _RenderSegmentedControl<T> renderObject) {
|
||||
BuildContext context,
|
||||
_RenderSegmentedControl<T> renderObject,
|
||||
) {
|
||||
assert(renderObject.state == state);
|
||||
renderObject
|
||||
..thumbColor = thumbColor
|
||||
@@ -629,20 +639,24 @@ enum _SegmentLocation { leftmost, rightmost, inbetween }
|
||||
|
||||
class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
with
|
||||
ContainerRenderObjectMixin<RenderBox,
|
||||
ContainerBoxParentData<RenderBox>>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox,
|
||||
ContainerBoxParentData<RenderBox>> {
|
||||
ContainerRenderObjectMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
>,
|
||||
RenderBoxContainerDefaultsMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
> {
|
||||
_RenderSegmentedControl({
|
||||
required int? highlightedIndex,
|
||||
required Color thumbColor,
|
||||
required double thumbScale,
|
||||
required bool proportionalWidth,
|
||||
required this.state,
|
||||
}) : _highlightedIndex = highlightedIndex,
|
||||
_thumbColor = thumbColor,
|
||||
_thumbScale = thumbScale,
|
||||
_proportionalWidth = proportionalWidth;
|
||||
}) : _highlightedIndex = highlightedIndex,
|
||||
_thumbColor = thumbColor,
|
||||
_thumbScale = thumbScale,
|
||||
_proportionalWidth = proportionalWidth;
|
||||
|
||||
final _CommonTabBarState<T> state;
|
||||
|
||||
@@ -839,7 +853,9 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
child = nonSeparatorChildAfter(child);
|
||||
}
|
||||
return math.min(
|
||||
childWidth, (constraints.maxWidth - totalSeparatorWidth) / childCount);
|
||||
childWidth,
|
||||
(constraints.maxWidth - totalSeparatorWidth) / childCount,
|
||||
);
|
||||
}
|
||||
|
||||
List<double> _getChildWidths(BoxConstraints constraints) {
|
||||
@@ -873,20 +889,28 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
}
|
||||
|
||||
Size _computeOverallSize(BoxConstraints constraints) {
|
||||
final double maxChildHeight =
|
||||
_getMaxChildHeight(constraints, constraints.maxWidth);
|
||||
final double maxChildHeight = _getMaxChildHeight(
|
||||
constraints,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
return constraints.constrain(
|
||||
Size(_getChildWidths(constraints).sum + totalSeparatorWidth,
|
||||
maxChildHeight),
|
||||
Size(
|
||||
_getChildWidths(constraints).sum + totalSeparatorWidth,
|
||||
maxChildHeight,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDryBaseline(
|
||||
covariant BoxConstraints constraints, TextBaseline baseline) {
|
||||
covariant BoxConstraints constraints,
|
||||
TextBaseline baseline,
|
||||
) {
|
||||
final List<double> segmentWidths = _getChildWidths(constraints);
|
||||
final double childHeight =
|
||||
_getMaxChildHeight(constraints, constraints.maxWidth);
|
||||
final double childHeight = _getMaxChildHeight(
|
||||
constraints,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
|
||||
int index = 0;
|
||||
BaselineOffset baselineOffset = BaselineOffset.noBaseline;
|
||||
@@ -928,8 +952,10 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final BoxConstraints childConstraints = BoxConstraints.tight(
|
||||
Size(segmentWidths[index ~/ 2], childHeight),
|
||||
);
|
||||
child.layout(index.isEven ? childConstraints : separatorConstraints,
|
||||
parentUsesSize: true);
|
||||
child.layout(
|
||||
index.isEven ? childConstraints : separatorConstraints,
|
||||
parentUsesSize: true,
|
||||
);
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final Offset childOffset = Offset(start, 0);
|
||||
@@ -959,9 +985,9 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final double leftMost = firstChildOffset.dx;
|
||||
final double rightMost =
|
||||
(children.last.parentData! as _SegmentedControlContainerBoxParentData)
|
||||
.offset
|
||||
.dx +
|
||||
children.last.size.width;
|
||||
.offset
|
||||
.dx +
|
||||
children.last.size.width;
|
||||
assert(rightMost > leftMost);
|
||||
return Rect.fromLTRB(
|
||||
math.max(thumbRect.left, leftMost - _kThumbInsets.left),
|
||||
@@ -992,8 +1018,10 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
if (thumbTween == null) {
|
||||
final Rect startingRect =
|
||||
moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;
|
||||
state.thumbAnimatable =
|
||||
RectTween(begin: startingRect, end: newThumbRect);
|
||||
state.thumbAnimatable = RectTween(
|
||||
begin: startingRect,
|
||||
end: newThumbRect,
|
||||
);
|
||||
} else if (newThumbRect != thumbTween.transform(1)) {
|
||||
final Rect startingRect =
|
||||
moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;
|
||||
@@ -1008,7 +1036,7 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
|
||||
final Rect unscaledThumbRect =
|
||||
state.thumbAnimatable?.evaluate(state.thumbController) ??
|
||||
newThumbRect;
|
||||
newThumbRect;
|
||||
currentThumbRect = unscaledThumbRect;
|
||||
|
||||
final _SegmentLocation childLocation;
|
||||
@@ -1045,7 +1073,10 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
final Paint separatorPaint = Paint();
|
||||
|
||||
void _paintSeparator(
|
||||
PaintingContext context, Offset offset, RenderBox child) {
|
||||
PaintingContext context,
|
||||
Offset offset,
|
||||
RenderBox child,
|
||||
) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
context.paintChild(child, offset + childParentData.offset);
|
||||
@@ -1058,23 +1089,20 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
}
|
||||
|
||||
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {
|
||||
// const List<BoxShadow> thumbShadow = <BoxShadow>[
|
||||
// BoxShadow(color: Color(0x1F000000), offset: Offset(0, 3), blurRadius: 8),
|
||||
// BoxShadow(color: Color(0x0A000000), offset: Offset(0, 3), blurRadius: 1),
|
||||
// ];
|
||||
final RSuperellipse thumbRSuperellipse = RSuperellipse.fromRectAndRadius(
|
||||
thumbRect.shift(offset),
|
||||
_kThumbRadius,
|
||||
);
|
||||
|
||||
final RRect thumbRRect =
|
||||
RRect.fromRectAndRadius(thumbRect.shift(offset), _kThumbRadius);
|
||||
context.canvas.drawRSuperellipse(
|
||||
thumbRSuperellipse.inflate(0.5),
|
||||
Paint()..color = const Color(0x0A000000),
|
||||
);
|
||||
|
||||
// for (final BoxShadow shadow in thumbShadow) {
|
||||
// context.canvas
|
||||
// .drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
|
||||
// }
|
||||
|
||||
context.canvas.drawRRect(
|
||||
thumbRRect.inflate(0.5), Paint()..color = const Color(0x0A000000));
|
||||
|
||||
context.canvas.drawRRect(thumbRRect, Paint()..color = thumbColor);
|
||||
context.canvas.drawRSuperellipse(
|
||||
thumbRSuperellipse,
|
||||
Paint()..color = thumbColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
export 'activate_box.dart';
|
||||
export 'animate_grid.dart';
|
||||
export 'pop_scope.dart';
|
||||
export 'builder.dart';
|
||||
export 'card.dart';
|
||||
export 'chip.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
export 'container.dart';
|
||||
export 'dialog.dart';
|
||||
export 'disabled_mask.dart';
|
||||
export 'donut_chart.dart';
|
||||
export 'effect.dart';
|
||||
export 'fade_box.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'grid.dart';
|
||||
@@ -13,23 +17,19 @@ export 'input.dart';
|
||||
export 'keep_scope.dart';
|
||||
export 'line_chart.dart';
|
||||
export 'list.dart';
|
||||
export 'notification.dart';
|
||||
export 'null_status.dart';
|
||||
export 'open_container.dart';
|
||||
export 'palette.dart';
|
||||
export 'pop_scope.dart';
|
||||
export 'popup.dart';
|
||||
export 'scaffold.dart';
|
||||
export 'scroll.dart';
|
||||
export 'setting.dart';
|
||||
export 'sheet.dart';
|
||||
export 'side_sheet.dart';
|
||||
export 'subscription_info_view.dart';
|
||||
export 'text.dart';
|
||||
export 'super_grid.dart';
|
||||
export 'donut_chart.dart';
|
||||
export 'activate_box.dart';
|
||||
export 'wave.dart';
|
||||
export 'scroll.dart';
|
||||
export 'dialog.dart';
|
||||
export 'effect.dart';
|
||||
export 'palette.dart';
|
||||
export 'tab.dart';
|
||||
export 'container.dart';
|
||||
export 'notification.dart';
|
||||
export 'text.dart';
|
||||
export 'wave.dart';
|
||||
|
||||
12
pubspec.lock
12
pubspec.lock
@@ -666,10 +666,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: a45bef33deb24839a51fb85a4d9e504ead2b1ad1c4779d02d09bf6a8857cdd52
|
||||
sha256: "8dfe08ea7fcf7467dbaf6889e72eebd5e0d6711caae201fdac780eb45232cd02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.13+2"
|
||||
version: "0.8.13+3"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1010,10 +1010,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
version: "1.5.2"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1471,10 +1471,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "07cffecb7d68cbc6437cd803d5f11a86fe06736735c3dfe46ff73bcb0f958eed"
|
||||
sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.21"
|
||||
version: "6.3.22"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.88+2025091902
|
||||
version: 0.8.90+2025100201
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ SolidCompression=yes
|
||||
SetupIconFile={{SETUP_ICON_FILE}}
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired={{PRIVILEGES_REQUIRED}}
|
||||
ArchitecturesAllowed={{ARCH}}
|
||||
ArchitecturesInstallIn64BitMode={{ARCH}}
|
||||
ArchitecturesAllowed=x64 arm64
|
||||
ArchitecturesInstallIn64BitMode=x64 arm64
|
||||
|
||||
[Code]
|
||||
procedure KillProcesses;
|
||||
|
||||
Reference in New Issue
Block a user