Compare commits
5 Commits
v0.8.83-pr
...
v0.8.84
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c9f08d4a | ||
|
|
f83a8e0cce | ||
|
|
f5544f1af7 | ||
|
|
eeb543780a | ||
|
|
676f2d058a |
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
os: windows-latest
|
||||
arch: amd64
|
||||
- platform: linux
|
||||
os: ubuntu-latest
|
||||
os: ubuntu-22.04
|
||||
arch: amd64
|
||||
- platform: macos
|
||||
os: macos-13
|
||||
@@ -201,6 +201,7 @@ jobs:
|
||||
env:
|
||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
40
CHANGELOG.md
40
CHANGELOG.md
@@ -1,3 +1,43 @@
|
||||
## v0.8.84
|
||||
|
||||
- Fix windows service verify issues
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.83
|
||||
|
||||
- Add windows server mode start process verify
|
||||
|
||||
- Add linux deb dependencies
|
||||
|
||||
- Add backup recovery strategy select
|
||||
|
||||
- Support custom text scaling
|
||||
|
||||
- Optimize the display of different text scale
|
||||
|
||||
- Optimize windows setup experience
|
||||
|
||||
- Optimize startTun performance
|
||||
|
||||
- Optimize android tv experience
|
||||
|
||||
- Optimize default option
|
||||
|
||||
- Optimize computed text size
|
||||
|
||||
- Optimize hyperOS freeform window
|
||||
|
||||
- Add developer mode
|
||||
|
||||
- Update core
|
||||
|
||||
- Optimize more details
|
||||
|
||||
- Add issues template
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.82
|
||||
|
||||
- Optimize android vpn performance
|
||||
|
||||
@@ -41,8 +41,8 @@ on Mobile:
|
||||
⚠️ Make sure to install the following dependencies before using them
|
||||
|
||||
```bash
|
||||
sudo apt-get install appindicator3-0.1 libappindicator3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
sudo apt-get install libayatana-appindicator3-dev
|
||||
sudo apt-get install libkeybinder-3.0-dev
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
@@ -41,8 +41,8 @@ on Mobile:
|
||||
⚠️ 使用前请确保安装以下依赖
|
||||
|
||||
```bash
|
||||
sudo apt-get install appindicator3-0.1 libappindicator3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
sudo apt-get install libayatana-appindicator3-dev
|
||||
sudo apt-get install libkeybinder-3.0-dev
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.follow.clash.models
|
||||
data class Package(
|
||||
val packageName: String,
|
||||
val label: String,
|
||||
val isSystem: Boolean,
|
||||
val system: Boolean,
|
||||
val internet: Boolean,
|
||||
val lastUpdateTime: Long,
|
||||
)
|
||||
|
||||
@@ -293,19 +293,17 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
if (packages.isNotEmpty()) return packages
|
||||
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS)
|
||||
?.filter {
|
||||
it.packageName != FlClashApplication.getAppContext().packageName && (
|
||||
it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|
||||
|| it.packageName == "android"
|
||||
)
|
||||
it.packageName != FlClashApplication.getAppContext().packageName || it.packageName == "android"
|
||||
|
||||
}?.map {
|
||||
Package(
|
||||
packageName = it.packageName,
|
||||
label = it.applicationInfo?.loadLabel(packageManager).toString(),
|
||||
isSystem = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) == 1,
|
||||
lastUpdateTime = it.lastUpdateTime
|
||||
)
|
||||
}?.let { packages.addAll(it) }
|
||||
Package(
|
||||
packageName = it.packageName,
|
||||
label = it.applicationInfo?.loadLabel(packageManager).toString(),
|
||||
system = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) == 1,
|
||||
lastUpdateTime = it.lastUpdateTime,
|
||||
internet = it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|
||||
)
|
||||
}?.let { packages.addAll(it) }
|
||||
return packages
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ fun Service.startForeground(notification: Notification) {
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("[FlClash]","startForeground===>")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
try {
|
||||
startForeground(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.follow.clash.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.net.ProxyInfo
|
||||
import android.net.VpnService
|
||||
@@ -34,6 +33,10 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
if (options.ipv4Address.isNotEmpty()) {
|
||||
val cidr = options.ipv4Address.toCIDR()
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
Log.d(
|
||||
"addAddress",
|
||||
"address: ${cidr.address} prefixLength:${cidr.prefixLength}"
|
||||
)
|
||||
val routeAddress = options.getIpv4RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
try {
|
||||
@@ -50,26 +53,39 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
} else {
|
||||
addRoute("0.0.0.0", 0)
|
||||
}
|
||||
} else {
|
||||
addRoute("0.0.0.0", 0)
|
||||
}
|
||||
if (options.ipv6Address.isNotEmpty()) {
|
||||
val cidr = options.ipv6Address.toCIDR()
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
val routeAddress = options.getIpv6RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
try {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d(
|
||||
"addRoute6",
|
||||
"address: ${i.address} prefixLength:${i.prefixLength}"
|
||||
)
|
||||
addRoute(i.address, i.prefixLength)
|
||||
try {
|
||||
if (options.ipv6Address.isNotEmpty()) {
|
||||
val cidr = options.ipv6Address.toCIDR()
|
||||
Log.d(
|
||||
"addAddress6",
|
||||
"address: ${cidr.address} prefixLength:${cidr.prefixLength}"
|
||||
)
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
val routeAddress = options.getIpv6RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
try {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d(
|
||||
"addRoute6",
|
||||
"address: ${i.address} prefixLength:${i.prefixLength}"
|
||||
)
|
||||
addRoute(i.address, i.prefixLength)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
addRoute("::", 0)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
} else {
|
||||
addRoute("::", 0)
|
||||
}
|
||||
} else {
|
||||
addRoute("::", 0)
|
||||
}
|
||||
}catch (_:Exception){
|
||||
Log.d(
|
||||
"addAddress6",
|
||||
"IPv6 is not supported."
|
||||
)
|
||||
}
|
||||
addDnsServer(options.dnsServerAddress)
|
||||
setMtu(9000)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import com.android.build.gradle.tasks.MergeSourceSetFolders
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
@@ -37,13 +35,17 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation("androidx.annotation:annotation-jvm:1.9.1")
|
||||
}
|
||||
|
||||
val copyNativeLibs by tasks.register<Copy>("copyNativeLibs") {
|
||||
@@ -58,8 +60,4 @@ afterEvaluate {
|
||||
tasks.named("preBuild") {
|
||||
dependsOn(copyNativeLibs)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.16.0")
|
||||
}
|
||||
@@ -21,6 +21,8 @@ if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
-Wl,--strip-all
|
||||
-Wl,--exclude-libs=ALL
|
||||
)
|
||||
|
||||
add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)
|
||||
endif ()
|
||||
|
||||
set(LIB_CLASH_PATH "${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libclash.so")
|
||||
|
||||
@@ -391,5 +391,14 @@
|
||||
"messageTest": "Message test",
|
||||
"messageTestTip": "This is a message.",
|
||||
"crashTest": "Crash test",
|
||||
"clearData": "Clear Data"
|
||||
"clearData": "Clear Data",
|
||||
"textScale": "Text Scaling",
|
||||
"internet": "Internet",
|
||||
"systemApp": "System APP",
|
||||
"noNetworkApp": "No network APP",
|
||||
"contactMe": "Contact me",
|
||||
"recoveryStrategy": "Recovery strategy",
|
||||
"recoveryStrategy_override": "Override",
|
||||
"recoveryStrategy_compatible": "Compatible",
|
||||
"logsTest": "Logs test"
|
||||
}
|
||||
@@ -391,5 +391,15 @@
|
||||
"messageTest": "メッセージテスト",
|
||||
"messageTestTip": "これはメッセージです。",
|
||||
"crashTest": "クラッシュテスト",
|
||||
"clearData": "データを消去"
|
||||
"clearData": "データを消去",
|
||||
"zoom": "ズーム",
|
||||
"textScale": "テキストスケーリング",
|
||||
"internet": "インターネット",
|
||||
"systemApp": "システムアプリ",
|
||||
"noNetworkApp": "ネットワークなしアプリ",
|
||||
"contactMe": "連絡する",
|
||||
"recoveryStrategy": "リカバリー戦略",
|
||||
"recoveryStrategy_override": "オーバーライド",
|
||||
"recoveryStrategy_compatible": "互換性",
|
||||
"logsTest": "ログテスト"
|
||||
}
|
||||
@@ -391,5 +391,15 @@
|
||||
"messageTest": "Тестирование сообщения",
|
||||
"messageTestTip": "Это сообщение.",
|
||||
"crashTest": "Тест на сбои",
|
||||
"clearData": "Очистить данные"
|
||||
"clearData": "Очистить данные",
|
||||
"zoom": "Масштаб",
|
||||
"textScale": "Масштабирование текста",
|
||||
"internet": "Интернет",
|
||||
"systemApp": "Системное приложение",
|
||||
"noNetworkApp": "Приложение без сети",
|
||||
"contactMe": "Свяжитесь со мной",
|
||||
"recoveryStrategy": "Стратегия восстановления",
|
||||
"recoveryStrategy_override": "Переопределение",
|
||||
"recoveryStrategy_compatible": "Совместимый",
|
||||
"logsTest": "Тест журналов"
|
||||
}
|
||||
@@ -391,5 +391,15 @@
|
||||
"messageTest": "消息测试",
|
||||
"messageTestTip": "这是一条消息。",
|
||||
"crashTest": "崩溃测试",
|
||||
"clearData": "清除数据"
|
||||
"clearData": "清除数据",
|
||||
"zoom": "缩放",
|
||||
"textScale": "文本缩放",
|
||||
"internet": "互联网",
|
||||
"systemApp": "系统应用",
|
||||
"noNetworkApp": "无网络应用",
|
||||
"contactMe": "联系我",
|
||||
"recoveryStrategy": "恢复策略",
|
||||
"recoveryStrategy_override": "覆盖",
|
||||
"recoveryStrategy_compatible": "兼容",
|
||||
"logsTest": "日志测试"
|
||||
}
|
||||
Submodule core/Clash.Meta updated: 88a1848dfb...6eac0e5d3d
@@ -98,13 +98,13 @@ func handleStopTun() {
|
||||
}
|
||||
}
|
||||
|
||||
func handleStartTun(fd int, callback unsafe.Pointer) bool {
|
||||
func handleStartTun(fd int, callback unsafe.Pointer) {
|
||||
handleStopTun()
|
||||
tunLock.Lock()
|
||||
defer tunLock.Unlock()
|
||||
now := time.Now()
|
||||
runTime = &now
|
||||
if fd != 0 {
|
||||
tunLock.Lock()
|
||||
defer tunLock.Unlock()
|
||||
tunHandler = &TunHandler{
|
||||
callback: callback,
|
||||
limit: semaphore.NewWeighted(4),
|
||||
@@ -113,13 +113,11 @@ func handleStartTun(fd int, callback unsafe.Pointer) bool {
|
||||
tunListener, _ := t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack)
|
||||
if tunListener != nil {
|
||||
log.Infoln("TUN address: %v", tunListener.Address())
|
||||
tunHandler.listener = tunListener
|
||||
} else {
|
||||
removeTunHook()
|
||||
return false
|
||||
}
|
||||
tunHandler.listener = tunListener
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleGetRunTime() string {
|
||||
@@ -228,7 +226,10 @@ func quickStart(initParamsChar *C.char, paramsChar *C.char, stateParamsChar *C.c
|
||||
|
||||
//export startTUN
|
||||
func startTUN(fd C.int, callback unsafe.Pointer) bool {
|
||||
return handleStartTun(int(fd), callback)
|
||||
go func() {
|
||||
handleStartTun(int(fd), callback)
|
||||
}()
|
||||
return true
|
||||
}
|
||||
|
||||
//export getRunTime
|
||||
@@ -238,7 +239,9 @@ func getRunTime() *C.char {
|
||||
|
||||
//export stopTun
|
||||
func stopTun() {
|
||||
handleStopTun()
|
||||
go func() {
|
||||
handleStopTun()
|
||||
}()
|
||||
}
|
||||
|
||||
//export getCurrentProfileName
|
||||
|
||||
@@ -24,7 +24,6 @@ type AccessControl struct {
|
||||
Mode string `json:"mode"`
|
||||
AcceptList []string `json:"acceptList"`
|
||||
RejectList []string `json:"rejectList"`
|
||||
IsFilterSystemApp bool `json:"isFilterSystemApp"`
|
||||
}
|
||||
|
||||
type AndroidVpnRawOptions struct {
|
||||
|
||||
@@ -245,7 +245,6 @@ abstract class ClashHandlerInterface with ClashInterface {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<bool> crash() {
|
||||
return invoke<bool>(
|
||||
|
||||
@@ -71,9 +71,9 @@ class ClashService extends ClashHandlerInterface {
|
||||
}
|
||||
}, (error, stack) {
|
||||
commonPrint.log(error.toString());
|
||||
if(error is SocketException){
|
||||
if (error is SocketException) {
|
||||
globalState.showNotifier(error.toString());
|
||||
globalState.appController.restartCore();
|
||||
// globalState.appController.restartCore();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -92,12 +92,11 @@ class ClashService extends ClashHandlerInterface {
|
||||
final arg = Platform.isWindows
|
||||
? "${serverSocket.port}"
|
||||
: serverSocket.address.address;
|
||||
bool isSuccess = false;
|
||||
if (Platform.isWindows && await system.checkIsAdmin()) {
|
||||
isSuccess = await request.startCoreByHelper(arg);
|
||||
}
|
||||
if (isSuccess) {
|
||||
return;
|
||||
final isSuccess = await request.startCoreByHelper(arg);
|
||||
if (isSuccess) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
process = await Process.start(
|
||||
appPath.corePath,
|
||||
|
||||
@@ -12,7 +12,7 @@ export 'iterable.dart';
|
||||
export 'keyboard.dart';
|
||||
export 'launch.dart';
|
||||
export 'link.dart';
|
||||
export 'list.dart';
|
||||
export 'fixed.dart';
|
||||
export 'lock.dart';
|
||||
export 'measure.dart';
|
||||
export 'navigation.dart';
|
||||
|
||||
@@ -16,16 +16,14 @@ const browserUa =
|
||||
const packageName = "com.follow.clash";
|
||||
final unixSocketPath = "/tmp/FlClashSocket_${Random().nextInt(10000)}.sock";
|
||||
const helperPort = 47890;
|
||||
const helperTag = "2024125";
|
||||
const baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
const maxTextScale = 1.4;
|
||||
const minTextScale = 0.8;
|
||||
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16.ap,
|
||||
horizontal: 16.ap,
|
||||
);
|
||||
|
||||
double textScaleFactor = min(
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
|
||||
1.2,
|
||||
);
|
||||
final defaultTextScaleFactor = WidgetsBinding.instance.platformDispatcher.textScaleFactor;
|
||||
const httpTimeoutDuration = Duration(milliseconds: 5000);
|
||||
const moreDuration = Duration(milliseconds: 100);
|
||||
const animateDuration = Duration(milliseconds: 100);
|
||||
@@ -44,7 +42,6 @@ const profilesDirectoryName = "profiles";
|
||||
const localhost = "127.0.0.1";
|
||||
const clashConfigKey = "clash_config";
|
||||
const configKey = "config";
|
||||
const listItemPadding = EdgeInsets.symmetric(horizontal: 16);
|
||||
const double dialogCommonWidth = 300;
|
||||
const repository = "chen08209/FlClash";
|
||||
const defaultExternalController = "127.0.0.1:9090";
|
||||
@@ -60,6 +57,7 @@ final commonFilter = ImageFilter.blur(
|
||||
const navigationItemListEquality = ListEquality<NavigationItem>();
|
||||
const connectionListEquality = ListEquality<Connection>();
|
||||
const stringListEquality = ListEquality<String>();
|
||||
const intListEquality = ListEquality<int>();
|
||||
const logListEquality = ListEquality<Log>();
|
||||
const groupListEquality = ListEquality<Group>();
|
||||
const externalProviderListEquality = ListEquality<ExternalProvider>();
|
||||
@@ -78,22 +76,24 @@ const viewModeColumnsMap = {
|
||||
ViewMode.desktop: [4, 3],
|
||||
};
|
||||
|
||||
const defaultPrimaryColor = 0xFF795548;
|
||||
const defaultPrimaryColor = 0XFFD8C0C3;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
return max(lines * 84 * textScaleFactor + (lines - 1) * 16, 0);
|
||||
return max(lines * 84 + (lines - 1) * 16, 0).ap;
|
||||
}
|
||||
|
||||
const maxLength = 150;
|
||||
|
||||
final mainIsolate = "FlClashMainIsolate";
|
||||
|
||||
final serviceIsolate = "FlClashServiceIsolate";
|
||||
|
||||
const defaultPrimaryColors = [
|
||||
defaultPrimaryColor,
|
||||
0xFF795548,
|
||||
0xFF03A9F4,
|
||||
0xFFFFFF00,
|
||||
0XFFBBC9CC,
|
||||
0XFFABD397,
|
||||
0XFFD8C0C3,
|
||||
defaultPrimaryColor,
|
||||
0XFF665390,
|
||||
];
|
||||
|
||||
79
lib/common/fixed.dart
Normal file
79
lib/common/fixed.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'iterable.dart';
|
||||
|
||||
class FixedList<T> {
|
||||
final int maxLength;
|
||||
final List<T> _list;
|
||||
|
||||
FixedList(this.maxLength, {List<T>? list})
|
||||
: _list = (list ?? [])..truncate(maxLength);
|
||||
|
||||
add(T item) {
|
||||
_list.add(item);
|
||||
_list.truncate(maxLength);
|
||||
}
|
||||
|
||||
clear() {
|
||||
_list.clear();
|
||||
}
|
||||
|
||||
List<T> get list => List.unmodifiable(_list);
|
||||
|
||||
int get length => _list.length;
|
||||
|
||||
T operator [](int index) => _list[index];
|
||||
|
||||
FixedList<T> copyWith() {
|
||||
return FixedList(
|
||||
maxLength,
|
||||
list: _list,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FixedMap<K, V> {
|
||||
int maxLength;
|
||||
late Map<K, V> _map;
|
||||
|
||||
FixedMap(this.maxLength, {Map<K, V>? map}) {
|
||||
_map = map ?? {};
|
||||
}
|
||||
|
||||
updateCacheValue(K key, V Function() callback) {
|
||||
final realValue = _map.updateCacheValue(
|
||||
key,
|
||||
callback,
|
||||
);
|
||||
_adjustMap();
|
||||
return realValue;
|
||||
}
|
||||
|
||||
clear() {
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
updateMaxLength(int size) {
|
||||
maxLength = size;
|
||||
_adjustMap();
|
||||
}
|
||||
|
||||
updateMap(Map<K, V> map) {
|
||||
_map = map;
|
||||
_adjustMap();
|
||||
}
|
||||
|
||||
_adjustMap() {
|
||||
if (_map.length > maxLength) {
|
||||
_map = Map.fromEntries(
|
||||
map.entries.toList()..truncate(maxLength),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
V? get(K key) => _map[key];
|
||||
|
||||
bool containsKey(K key) => _map.containsKey(key);
|
||||
|
||||
int get length => _map.length;
|
||||
|
||||
Map<K, V> get map => Map.unmodifiable(_map);
|
||||
}
|
||||
@@ -38,6 +38,43 @@ extension IterableExt<T> on Iterable<T> {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<T> takeLast({int count = 50}) {
|
||||
if (count <= 0) return Iterable.empty();
|
||||
return count >= length ? this : toList().skip(length - count);
|
||||
}
|
||||
}
|
||||
|
||||
extension ListExt<T> on List<T> {
|
||||
void truncate(int maxLength) {
|
||||
assert(maxLength > 0);
|
||||
if (length > maxLength) {
|
||||
removeRange(0, length - maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
List<T> intersection(List<T> list) {
|
||||
return where((item) => list.contains(item)).toList();
|
||||
}
|
||||
|
||||
List<List<T>> batch(int maxConcurrent) {
|
||||
final batches = (length / maxConcurrent).ceil();
|
||||
final List<List<T>> res = [];
|
||||
for (int i = 0; i < batches; i++) {
|
||||
if (i != batches - 1) {
|
||||
res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1)));
|
||||
} else {
|
||||
res.add(sublist(i * maxConcurrent, length));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
List<T> safeSublist(int start) {
|
||||
if (start <= 0) return this;
|
||||
if (start > length) return [];
|
||||
return sublist(start);
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleListExt on List<double> {
|
||||
@@ -67,9 +104,9 @@ extension DoubleListExt on List<double> {
|
||||
}
|
||||
|
||||
extension MapExt<K, V> on Map<K, V> {
|
||||
getCacheValue(K key, V defaultValue) {
|
||||
updateCacheValue(K key, V Function() callback) {
|
||||
if (this[key] == null) {
|
||||
this[key] = defaultValue;
|
||||
this[key] = callback();
|
||||
}
|
||||
return this[key];
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import 'dart:collection';
|
||||
|
||||
class FixedList<T> {
|
||||
final int maxLength;
|
||||
final List<T> _list;
|
||||
|
||||
FixedList(this.maxLength, {List<T>? list}) : _list = list ?? [];
|
||||
|
||||
add(T item) {
|
||||
if (_list.length == maxLength) {
|
||||
_list.removeAt(0);
|
||||
}
|
||||
_list.add(item);
|
||||
}
|
||||
|
||||
clear() {
|
||||
_list.clear();
|
||||
}
|
||||
|
||||
List<T> get list => List.unmodifiable(_list);
|
||||
|
||||
int get length => _list.length;
|
||||
|
||||
T operator [](int index) => _list[index];
|
||||
|
||||
FixedList<T> copyWith() {
|
||||
return FixedList(
|
||||
maxLength,
|
||||
list: _list,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FixedMap<K, V> {
|
||||
int maxSize;
|
||||
final Map<K, V> _map = {};
|
||||
final Queue<K> _queue = Queue<K>();
|
||||
|
||||
FixedMap(this.maxSize);
|
||||
|
||||
put(K key, V value) {
|
||||
if (_map.length == maxSize) {
|
||||
final oldestKey = _queue.removeFirst();
|
||||
_map.remove(oldestKey);
|
||||
}
|
||||
_map[key] = value;
|
||||
_queue.add(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
clear() {
|
||||
_map.clear();
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
updateMaxSize(int size){
|
||||
maxSize = size;
|
||||
}
|
||||
|
||||
V? get(K key) => _map[key];
|
||||
|
||||
|
||||
bool containsKey(K key) => _map.containsKey(key);
|
||||
|
||||
int get length => _map.length;
|
||||
|
||||
Map<K, V> get map => Map.unmodifiable(_map);
|
||||
}
|
||||
|
||||
extension ListExtension<T> on List<T> {
|
||||
List<T> intersection(List<T> list) {
|
||||
return where((item) => list.contains(item)).toList();
|
||||
}
|
||||
|
||||
List<List<T>> batch(int maxConcurrent) {
|
||||
final batches = (length / maxConcurrent).ceil();
|
||||
final List<List<T>> res = [];
|
||||
for (int i = 0; i < batches; i++) {
|
||||
if (i != batches - 1) {
|
||||
res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1)));
|
||||
} else {
|
||||
res.add(sublist(i * maxConcurrent, length));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
List<T> safeSublist(int start) {
|
||||
if (start <= 0) return this;
|
||||
if (start > length) return [];
|
||||
return sublist(start);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Measure {
|
||||
final TextScaler _textScale;
|
||||
final TextScaler _textScaler;
|
||||
final BuildContext context;
|
||||
final Map<String, dynamic> _measureMap;
|
||||
|
||||
Measure.of(this.context)
|
||||
: _textScale = TextScaler.linear(
|
||||
Measure.of(this.context, double textScaleFactor)
|
||||
: _measureMap = {},
|
||||
_textScaler = TextScaler.linear(
|
||||
textScaleFactor,
|
||||
);
|
||||
|
||||
@@ -21,7 +23,7 @@ class Measure {
|
||||
style: text.style,
|
||||
),
|
||||
maxLines: text.maxLines,
|
||||
textScaler: _textScale,
|
||||
textScaler: _textScaler,
|
||||
textDirection: text.textDirection ?? TextDirection.ltr,
|
||||
)..layout(
|
||||
maxWidth: maxWidth,
|
||||
@@ -29,81 +31,87 @@ class Measure {
|
||||
return textPainter.size;
|
||||
}
|
||||
|
||||
double? _bodyMediumHeight;
|
||||
Size? _bodyLargeSize;
|
||||
double? _bodySmallHeight;
|
||||
double? _labelSmallHeight;
|
||||
double? _labelMediumHeight;
|
||||
double? _titleLargeHeight;
|
||||
double? _titleMediumHeight;
|
||||
|
||||
double get bodyMediumHeight {
|
||||
_bodyMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
).height;
|
||||
return _bodyMediumHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"bodyMediumHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
Size get bodyLargeSize {
|
||||
_bodyLargeSize ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
double get bodyLargeHeight {
|
||||
return _measureMap.updateCacheValue(
|
||||
"bodyLargeHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
return _bodyLargeSize!;
|
||||
}
|
||||
|
||||
double get bodySmallHeight {
|
||||
_bodySmallHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
).height;
|
||||
return _bodySmallHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"bodySmallHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get labelSmallHeight {
|
||||
_labelSmallHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelSmall,
|
||||
),
|
||||
).height;
|
||||
return _labelSmallHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"labelSmallHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelSmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get labelMediumHeight {
|
||||
_labelMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelMedium,
|
||||
),
|
||||
).height;
|
||||
return _labelMediumHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"labelMediumHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get titleLargeHeight {
|
||||
_titleLargeHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleLarge,
|
||||
),
|
||||
).height;
|
||||
return _titleLargeHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"titleLargeHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleLarge,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get titleMediumHeight {
|
||||
_titleMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
).height;
|
||||
return _titleMediumHeight!;
|
||||
return _measureMap.updateCacheValue(
|
||||
"titleMediumHeight",
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ class Navigation {
|
||||
const NavigationItem(
|
||||
icon: Icon(Icons.space_dashboard),
|
||||
label: PageLabel.dashboard,
|
||||
keep: false,
|
||||
fragment: DashboardFragment(
|
||||
key: GlobalObjectKey(PageLabel.dashboard),
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -12,6 +13,10 @@ extension NumExt on num {
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
double get ap {
|
||||
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleExt on double {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
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';
|
||||
@@ -20,10 +19,7 @@ class CommonPrint {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.app,
|
||||
payload: payload,
|
||||
),
|
||||
Log.app(payload),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class Request {
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
return false;
|
||||
}
|
||||
return (response.data as String) == helperTag;
|
||||
return (response.data as String) == globalState.coreSHA256;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,18 +55,24 @@ class System {
|
||||
}
|
||||
|
||||
Future<AuthorizeCode> authorizeCore() async {
|
||||
if (Platform.isAndroid) {
|
||||
return AuthorizeCode.none;
|
||||
}
|
||||
final corePath = appPath.corePath.replaceAll(' ', '\\\\ ');
|
||||
final isAdmin = await checkIsAdmin();
|
||||
if (isAdmin) {
|
||||
return AuthorizeCode.none;
|
||||
}
|
||||
|
||||
if (Platform.isWindows) {
|
||||
final result = await windows?.registerService();
|
||||
if (result == true) {
|
||||
return AuthorizeCode.success;
|
||||
}
|
||||
return AuthorizeCode.error;
|
||||
} else if (Platform.isMacOS) {
|
||||
}
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
final shell = 'chown root:admin $corePath; chmod +sx $corePath';
|
||||
final arguments = [
|
||||
"-e",
|
||||
|
||||
@@ -4,36 +4,43 @@ import 'package:flutter/material.dart';
|
||||
class CommonTheme {
|
||||
final BuildContext context;
|
||||
final Map<String, Color> _colorMap;
|
||||
final double textScaleFactor;
|
||||
|
||||
CommonTheme.of(this.context) : _colorMap = {};
|
||||
CommonTheme.of(
|
||||
this.context,
|
||||
this.textScaleFactor,
|
||||
) : _colorMap = {};
|
||||
|
||||
Color get darkenSecondaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
return _colorMap.updateCacheValue(
|
||||
"darkenSecondaryContainer",
|
||||
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.1),
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darkenSecondaryContainerLighter {
|
||||
return _colorMap.getCacheValue(
|
||||
return _colorMap.updateCacheValue(
|
||||
"darkenSecondaryContainerLighter",
|
||||
context.colorScheme.secondaryContainer
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1)
|
||||
.opacity60,
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken2SecondaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
return _colorMap.updateCacheValue(
|
||||
"darken2SecondaryContainer",
|
||||
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.2),
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.2),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken3PrimaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
return _colorMap.updateCacheValue(
|
||||
"darken3PrimaryContainer",
|
||||
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3),
|
||||
() => context.colorScheme.primaryContainer
|
||||
.blendDarken(context, factor: 0.3),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +260,7 @@ class AppController {
|
||||
final patchConfig = _ref.read(patchClashConfigProvider);
|
||||
final appSetting = _ref.read(appSettingProvider);
|
||||
bool enableTun = patchConfig.tun.enable;
|
||||
if (enableTun != lastTunEnable &&
|
||||
lastTunEnable == false &&
|
||||
!Platform.isAndroid) {
|
||||
if (enableTun != lastTunEnable && lastTunEnable == false) {
|
||||
final code = await system.authorizeCore();
|
||||
switch (code) {
|
||||
case AuthorizeCode.none:
|
||||
@@ -314,6 +312,10 @@ class AppController {
|
||||
handleChangeProfile() {
|
||||
_ref.read(delayDataSourceProvider.notifier).value = {};
|
||||
applyProfile();
|
||||
_ref.read(logsProvider.notifier).value = FixedList(500);
|
||||
_ref.read(requestsProvider.notifier).value = FixedList(500);
|
||||
globalState.cacheHeightMap = {};
|
||||
globalState.cacheScrollPosition = {};
|
||||
}
|
||||
|
||||
updateBrightness(Brightness brightness) {
|
||||
@@ -334,12 +336,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
_ref.read(logsProvider.notifier).addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.info,
|
||||
payload: e.toString(),
|
||||
),
|
||||
);
|
||||
commonPrint.log(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,9 +398,9 @@ class AppController {
|
||||
handleExit() async {
|
||||
try {
|
||||
await updateStatus(false);
|
||||
await proxy?.stopProxy();
|
||||
await clashCore.shutdown();
|
||||
await clashService?.destroy();
|
||||
await proxy?.stopProxy();
|
||||
await savePreferences();
|
||||
} finally {
|
||||
system.exit();
|
||||
@@ -492,10 +489,10 @@ class AppController {
|
||||
Future<void> _initCore() async {
|
||||
final isInit = await clashCore.isInit;
|
||||
if (!isInit) {
|
||||
await clashCore.init();
|
||||
await clashCore.setState(
|
||||
globalState.getCoreState(),
|
||||
);
|
||||
await clashCore.init();
|
||||
}
|
||||
await applyProfile();
|
||||
}
|
||||
@@ -945,30 +942,39 @@ class AppController {
|
||||
}
|
||||
|
||||
_recovery(Config config, RecoveryOption recoveryOption) {
|
||||
final recoveryStrategy = _ref.read(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
final profiles = config.profiles;
|
||||
for (final profile in profiles) {
|
||||
_ref.read(profilesProvider.notifier).setProfile(profile);
|
||||
if (recoveryStrategy == RecoveryStrategy.override) {
|
||||
_ref.read(profilesProvider.notifier).value = profiles;
|
||||
} else {
|
||||
for (final profile in profiles) {
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
profile,
|
||||
);
|
||||
}
|
||||
}
|
||||
final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
|
||||
if (onlyProfiles) {
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile != null) {
|
||||
_ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
|
||||
}
|
||||
return;
|
||||
if (!onlyProfiles) {
|
||||
_ref.read(patchClashConfigProvider.notifier).value =
|
||||
config.patchClashConfig;
|
||||
_ref.read(appSettingProvider.notifier).value = config.appSetting;
|
||||
_ref.read(currentProfileIdProvider.notifier).value =
|
||||
config.currentProfileId;
|
||||
_ref.read(appDAVSettingProvider.notifier).value = config.dav;
|
||||
_ref.read(themeSettingProvider.notifier).value = config.themeProps;
|
||||
_ref.read(windowSettingProvider.notifier).value = config.windowProps;
|
||||
_ref.read(vpnSettingProvider.notifier).value = config.vpnProps;
|
||||
_ref.read(proxiesStyleSettingProvider.notifier).value =
|
||||
config.proxiesStyle;
|
||||
_ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
|
||||
_ref.read(networkSettingProvider.notifier).value = config.networkProps;
|
||||
_ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
|
||||
}
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile == null) {
|
||||
_ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
|
||||
}
|
||||
_ref.read(patchClashConfigProvider.notifier).value =
|
||||
config.patchClashConfig;
|
||||
_ref.read(appSettingProvider.notifier).value = config.appSetting;
|
||||
_ref.read(currentProfileIdProvider.notifier).value =
|
||||
config.currentProfileId;
|
||||
_ref.read(appDAVSettingProvider.notifier).value = config.dav;
|
||||
_ref.read(themeSettingProvider.notifier).value = config.themeProps;
|
||||
_ref.read(windowSettingProvider.notifier).value = config.windowProps;
|
||||
_ref.read(vpnSettingProvider.notifier).value = config.vpnProps;
|
||||
_ref.read(proxiesStyleSettingProvider.notifier).value = config.proxiesStyle;
|
||||
_ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
|
||||
_ref.read(networkSettingProvider.notifier).value = config.networkProps;
|
||||
_ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +293,7 @@ enum WindowsHelperServiceStatus {
|
||||
|
||||
enum DebounceTag {
|
||||
updateClashConfig,
|
||||
updateStatus,
|
||||
updateGroups,
|
||||
addCheckIpNum,
|
||||
applyProfile,
|
||||
@@ -347,6 +348,15 @@ enum DashboardWidget {
|
||||
),
|
||||
platforms: desktopPlatforms,
|
||||
),
|
||||
vpnButton(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: VpnButton(),
|
||||
),
|
||||
platforms: [
|
||||
SupportPlatform.Android,
|
||||
],
|
||||
),
|
||||
systemProxyButton(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
@@ -461,3 +471,14 @@ enum RuleTarget {
|
||||
DIRECT,
|
||||
REJECT,
|
||||
}
|
||||
|
||||
enum RecoveryStrategy {
|
||||
compatible,
|
||||
override,
|
||||
}
|
||||
|
||||
enum CacheTag {
|
||||
logs,
|
||||
rules,
|
||||
requests,
|
||||
}
|
||||
|
||||
@@ -47,6 +47,15 @@ class AboutFragment extends StatelessWidget {
|
||||
_checkUpdate(context);
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.contactMe),
|
||||
onTap: () {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.contactMe,
|
||||
message: TextSpan(text: "chen08209@gmail.com"),
|
||||
);
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: const Text("Telegram"),
|
||||
onTap: () {
|
||||
|
||||
@@ -113,15 +113,11 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
|
||||
}
|
||||
|
||||
_intelligentSelected() async {
|
||||
final appState = globalState.appState;
|
||||
final config = globalState.config;
|
||||
final accessControl = config.vpnProps.accessControl;
|
||||
final packageNames = appState.packages
|
||||
.where(
|
||||
(item) =>
|
||||
accessControl.isFilterSystemApp ? item.isSystem == false : true,
|
||||
)
|
||||
.map((item) => item.packageName);
|
||||
final packageNames = ref.read(
|
||||
packageListSelectorStateProvider.select(
|
||||
(state) => state.list.map((item) => item.packageName),
|
||||
),
|
||||
);
|
||||
final commonScaffoldState = context.commonScaffoldState;
|
||||
if (commonScaffoldState?.mounted != true) return;
|
||||
final selectedPackageNames =
|
||||
@@ -194,7 +190,7 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
|
||||
final state = ref.watch(packageListSelectorStateProvider);
|
||||
final accessControl = state.accessControl;
|
||||
final accessControlMode = accessControl.mode;
|
||||
final packages = state.getList(
|
||||
final packages = state.getSortList(
|
||||
accessControlMode == AccessControlMode.acceptSelected
|
||||
? acceptList
|
||||
: rejectList,
|
||||
@@ -482,14 +478,20 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
final lowQuery = query.toLowerCase();
|
||||
return Consumer(
|
||||
builder: (context, ref, __) {
|
||||
final state = ref.watch(packageListSelectorStateProvider);
|
||||
final accessControl = state.accessControl;
|
||||
final accessControlMode = accessControl.mode;
|
||||
final packages = state.getList(
|
||||
accessControlMode == AccessControlMode.acceptSelected
|
||||
? acceptList
|
||||
: rejectList,
|
||||
final vm3 = ref.watch(
|
||||
packageListSelectorStateProvider.select(
|
||||
(state) => VM3(
|
||||
a: state.getSortList(
|
||||
state.accessControl.mode == AccessControlMode.acceptSelected
|
||||
? acceptList
|
||||
: rejectList,
|
||||
),
|
||||
b: state.accessControl.enable,
|
||||
c: state.accessControl.currentList,
|
||||
),
|
||||
),
|
||||
);
|
||||
final packages = vm3.a;
|
||||
final queryPackages = packages
|
||||
.where(
|
||||
(package) =>
|
||||
@@ -497,8 +499,8 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
package.packageName.contains(lowQuery),
|
||||
)
|
||||
.toList();
|
||||
final isAccessControl = state.accessControl.enable;
|
||||
final currentList = accessControl.currentList;
|
||||
final isAccessControl = vm3.b;
|
||||
final currentList = vm3.c;
|
||||
final packageNameList = packages.map((e) => e.packageName).toList();
|
||||
final valueList = currentList.intersection(packageNameList);
|
||||
return DisabledMask(
|
||||
@@ -579,13 +581,6 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
};
|
||||
}
|
||||
|
||||
String _getTextWithIsFilterSystemApp(bool isFilterSystemApp) {
|
||||
return switch (isFilterSystemApp) {
|
||||
true => appLocalizations.onlyOtherApps,
|
||||
false => appLocalizations.allApps,
|
||||
};
|
||||
}
|
||||
|
||||
List<Widget> _buildModeSetting() {
|
||||
return generateSection(
|
||||
title: appLocalizations.mode,
|
||||
@@ -673,25 +668,39 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final isFilterSystemApp = ref.watch(
|
||||
vpnSettingProvider
|
||||
.select((state) => state.accessControl.isFilterSystemApp),
|
||||
final vm2 = ref.watch(
|
||||
vpnSettingProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.accessControl.isFilterSystemApp,
|
||||
b: state.accessControl.isFilterNonInternetApp,
|
||||
),
|
||||
),
|
||||
);
|
||||
return Wrap(
|
||||
spacing: 16,
|
||||
children: [
|
||||
for (final item in [false, true])
|
||||
SettingTextCard(
|
||||
_getTextWithIsFilterSystemApp(item),
|
||||
isSelected: isFilterSystemApp == item,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
isFilterSystemApp: item,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
SettingTextCard(
|
||||
appLocalizations.systemApp,
|
||||
isSelected: vm2.a == false,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
isFilterSystemApp: !vm2.a,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SettingTextCard(
|
||||
appLocalizations.noNetworkApp,
|
||||
isSelected: vm2.b == false,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
isFilterNonInternetApp: !vm2.b,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -8,10 +8,12 @@ import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/fade_box.dart';
|
||||
import 'package:fl_clash/widgets/input.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:fl_clash/widgets/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class BackupAndRecovery extends ConsumerWidget {
|
||||
const BackupAndRecovery({super.key});
|
||||
@@ -134,6 +136,30 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
_handleUpdateRecoveryStrategy(WidgetRef ref) async {
|
||||
final recoveryStrategy = ref.read(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
final res = await globalState.showCommonDialog(
|
||||
child: OptionsDialog<RecoveryStrategy>(
|
||||
title: appLocalizations.recoveryStrategy,
|
||||
options: RecoveryStrategy.values,
|
||||
textBuilder: (mode) => Intl.message(
|
||||
"recoveryStrategy_${mode.name}",
|
||||
),
|
||||
value: recoveryStrategy,
|
||||
),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
recoveryStrategy: res,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final dav = ref.watch(appDAVSettingProvider);
|
||||
@@ -256,6 +282,26 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
title: Text(appLocalizations.recovery),
|
||||
subtitle: Text(appLocalizations.localRecoveryDesc),
|
||||
),
|
||||
ListHeader(title: appLocalizations.options),
|
||||
Consumer(builder: (_, ref, __) {
|
||||
final recoveryStrategy = ref.watch(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
return ListItem(
|
||||
onTap: () {
|
||||
_handleUpdateRecoveryStrategy(ref);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryStrategy),
|
||||
trailing: FilledButton(
|
||||
onPressed: () {
|
||||
_handleUpdateRecoveryStrategy(ref);
|
||||
},
|
||||
child: Text(
|
||||
Intl.message("recoveryStrategy_${recoveryStrategy.name}"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -301,8 +301,11 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
title: appLocalizations.routeAddress,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final routeAddress = ref.watch(patchClashConfigProvider
|
||||
.select((state) => state.tun.routeAddress));
|
||||
final routeAddress = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.tun.routeAddress,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.routeAddress,
|
||||
items: routeAddress,
|
||||
@@ -371,7 +374,9 @@ class NetworkListView extends ConsumerWidget {
|
||||
return;
|
||||
}
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => defaultVpnProps,
|
||||
(state) => defaultVpnProps.copyWith(
|
||||
accessControl: state.accessControl,
|
||||
),
|
||||
);
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
|
||||
@@ -20,12 +20,13 @@ class RequestsFragment extends ConsumerStatefulWidget {
|
||||
|
||||
class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
with PageMixin {
|
||||
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
|
||||
final _requestsStateNotifier =
|
||||
ValueNotifier<ConnectionsState>(const ConnectionsState());
|
||||
final _requestsStateNotifier = ValueNotifier<ConnectionsState>(
|
||||
const ConnectionsState(loading: true),
|
||||
);
|
||||
List<Connection> _requests = [];
|
||||
final _cacheKey = ValueKey("requests_list");
|
||||
final _tag = CacheTag.requests;
|
||||
late ScrollController _scrollController;
|
||||
bool _isLoad = false;
|
||||
|
||||
double _currentMaxWidth = 0;
|
||||
|
||||
@@ -45,12 +46,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final preOffset = globalState.cacheScrollPosition[_cacheKey] ?? -1;
|
||||
final preOffset = globalState.cacheScrollPosition[_tag] ?? -1;
|
||||
_scrollController = ScrollController(
|
||||
initialScrollOffset: preOffset > 0 ? preOffset : double.maxFinite,
|
||||
);
|
||||
_requests = globalState.appState.requests.list;
|
||||
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
|
||||
connections: globalState.appState.requests.list,
|
||||
connections: _requests,
|
||||
);
|
||||
ref.listenManual(
|
||||
isCurrentPageProvider(
|
||||
@@ -73,7 +75,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
updateRequestsThrottler();
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -98,14 +99,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
final lines = (chainSize.height / baseHeight).round();
|
||||
final computerHeight =
|
||||
size.height + chainSize.height + 24 + 24 * (lines - 1);
|
||||
return computerHeight;
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_key.currentState?.clearCache();
|
||||
}
|
||||
return computerHeight + 8 + 32 + globalState.measure.bodyMediumHeight;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -133,6 +127,42 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
}, duration: commonDuration);
|
||||
}
|
||||
|
||||
_preLoad() {
|
||||
if (_isLoad == true) {
|
||||
return;
|
||||
}
|
||||
_isLoad = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final isMobileView = ref.read(isMobileViewProvider);
|
||||
if (isMobileView) {
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
}
|
||||
final parts = _requests.batch(10);
|
||||
globalState.cacheHeightMap[_tag] ??= FixedMap(
|
||||
_requests.length,
|
||||
);
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
final part = parts[i];
|
||||
await Future(
|
||||
() {
|
||||
for (final request in part) {
|
||||
globalState.cacheHeightMap[_tag]?.updateCacheValue(
|
||||
request.id,
|
||||
() => _calcCacheHeight(request),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
|
||||
loading: false,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
@@ -146,75 +176,86 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
Platform.isAndroid,
|
||||
),
|
||||
);
|
||||
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
|
||||
_currentMaxWidth = constraints.maxWidth - 40 - (value ? 60 : 0);
|
||||
return child!;
|
||||
},
|
||||
child: ValueListenableBuilder<ConnectionsState>(
|
||||
valueListenable: _requestsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
final connections = state.list;
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
child: TextScaleNotification(
|
||||
child: ValueListenableBuilder<ConnectionsState>(
|
||||
valueListenable: _requestsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
_preLoad();
|
||||
final connections = state.list;
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
);
|
||||
}
|
||||
final items = connections
|
||||
.map<Widget>(
|
||||
(connection) => ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
final content = connections.isEmpty
|
||||
? NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
)
|
||||
: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ScrollToEndBox(
|
||||
controller: _scrollController,
|
||||
tag: _tag,
|
||||
dataSource: connections,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: CacheItemExtentListView(
|
||||
tag: _tag,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemExtentBuilder: (index) {
|
||||
if (index.isOdd) {
|
||||
return 0;
|
||||
}
|
||||
return _calcCacheHeight(
|
||||
connections[index ~/ 2]);
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
if (index.isOdd) {
|
||||
return "divider";
|
||||
}
|
||||
return connections[index ~/ 2].id;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return FadeBox(
|
||||
child: state.loading
|
||||
? Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: content,
|
||||
);
|
||||
}
|
||||
final items = connections
|
||||
.map<Widget>(
|
||||
(connection) => ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ScrollToEndBox(
|
||||
controller: _scrollController,
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: connections,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemExtentBuilder: (index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final measure = globalState.measure;
|
||||
final bodyMediumHeight = measure.bodyMediumHeight;
|
||||
final connection = connections[(index / 2).floor()];
|
||||
final height = _calcCacheHeight(connection);
|
||||
return height + bodyMediumHeight + 32;
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final connection = connections[(index / 2).floor()];
|
||||
return connection.id;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
onNotification: (_) {
|
||||
globalState.cacheHeightMap[_tag]?.clear();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -103,8 +103,8 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16.ap,
|
||||
mainAxisSpacing: 16.ap,
|
||||
children: [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -57,37 +58,43 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
onPressed: () {
|
||||
clashCore.requestGc();
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _memoryInfoStateNotifier,
|
||||
builder: (_, trafficValue, __) {
|
||||
return Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(
|
||||
bottom: 0,
|
||||
top: 12,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
trafficValue.showValue,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
padding: baseInfoEdgeInsets.copyWith(
|
||||
top: 0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: globalState.measure.bodyMediumHeight + 2,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _memoryInfoStateNotifier,
|
||||
builder: (_, trafficValue, __) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
trafficValue.showValue,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -206,7 +206,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
size: 16,
|
||||
size: 16.ap,
|
||||
Icons.info_outline,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
||||
@@ -17,56 +17,65 @@ class OutboundMode extends StatelessWidget {
|
||||
height: height,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final mode =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.mode));
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.outboundMode,
|
||||
iconData: Icons.call_split_sharp,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
for (final item in Mode.values)
|
||||
Flexible(
|
||||
child: ListItem.radio(
|
||||
dense: true,
|
||||
horizontalTitleGap: 4,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 16,
|
||||
),
|
||||
delegate: RadioDelegate(
|
||||
value: item,
|
||||
groupValue: mode,
|
||||
onChanged: (value) async {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.changeMode(value);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
final mode = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.mode,
|
||||
),
|
||||
);
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent),
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.outboundMode,
|
||||
iconData: Icons.call_split_sharp,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (final item in Mode.values)
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: ListItem.radio(
|
||||
dense: true,
|
||||
horizontalTitleGap: 4,
|
||||
padding: EdgeInsets.only(
|
||||
left: 12.ap,
|
||||
right: 16.ap,
|
||||
),
|
||||
delegate: RadioDelegate(
|
||||
value: item,
|
||||
groupValue: mode,
|
||||
onChanged: (value) async {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.changeMode(value);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -76,12 +85,21 @@ class OutboundMode extends StatelessWidget {
|
||||
class OutboundModeV2 extends StatelessWidget {
|
||||
const OutboundModeV2({super.key});
|
||||
|
||||
Color _getTextColor(BuildContext context, Mode mode) {
|
||||
return switch (mode) {
|
||||
Mode.rule => context.colorScheme.onSecondaryContainer,
|
||||
Mode.global => context.colorScheme.onPrimaryContainer,
|
||||
Mode.direct => context.colorScheme.onTertiaryContainer,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final height = getWidgetHeight(0.72);
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final mode = ref.watch(
|
||||
@@ -102,14 +120,24 @@ class OutboundModeV2 extends StatelessWidget {
|
||||
(item) => MapEntry(
|
||||
item,
|
||||
Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(),
|
||||
height: height - 16,
|
||||
child: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall
|
||||
?.adjustSize(1),
|
||||
?.adjustSize(1)
|
||||
.copyWith(
|
||||
color: item == mode
|
||||
? _getTextColor(
|
||||
context,
|
||||
item,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -165,3 +165,87 @@ class SystemProxyButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VpnButton extends StatelessWidget {
|
||||
const VpnButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(1),
|
||||
child: CommonCard(
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
const VPNItem(),
|
||||
const VpnSystemProxyItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: "VPN",
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
info: Info(
|
||||
label: "VPN",
|
||||
iconData: Icons.stacked_line_chart,
|
||||
),
|
||||
child: Container(
|
||||
padding: baseInfoEdgeInsets.copyWith(
|
||||
top: 4,
|
||||
bottom: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: TooltipText(
|
||||
text: Text(
|
||||
appLocalizations.options,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall
|
||||
?.adjustSize(-2)
|
||||
.toLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final enable = ref.watch(
|
||||
vpnSettingProvider.select(
|
||||
(state) => state.enable,
|
||||
),
|
||||
);
|
||||
return Switch(
|
||||
value: enable,
|
||||
onChanged: (value) {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -35,11 +35,15 @@ class _StartButtonState extends State<StartButton>
|
||||
}
|
||||
|
||||
handleSwitchStart() {
|
||||
if (isStart == globalState.appState.isStart) {
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
globalState.appController.updateStatus(isStart);
|
||||
}
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
debouncer.call(
|
||||
DebounceTag.updateStatus,
|
||||
() {
|
||||
globalState.appController.updateStatus(isStart);
|
||||
},
|
||||
duration: moreDuration,
|
||||
);
|
||||
}
|
||||
|
||||
updateController() {
|
||||
@@ -126,9 +130,11 @@ class _StartButtonState extends State<StartButton>
|
||||
final text = utils.getTimeText(runTime);
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold.copyWith(
|
||||
color: context.colorScheme.onPrimaryContainer
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.toSoftBold
|
||||
.copyWith(color: context.colorScheme.onPrimaryContainer),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../providers/app.dart';
|
||||
|
||||
class DeveloperView extends ConsumerWidget {
|
||||
const DeveloperView({super.key});
|
||||
|
||||
Widget _getDeveloperList(BuildContext context) {
|
||||
Widget _getDeveloperList(BuildContext context, WidgetRef ref) {
|
||||
return generateSectionV2(
|
||||
title: appLocalizations.options,
|
||||
items: [
|
||||
ListItem(
|
||||
leading: Icon(Icons.ac_unit),
|
||||
title: Text(appLocalizations.messageTest),
|
||||
onTap: () {
|
||||
context.showNotifier(
|
||||
@@ -24,14 +26,46 @@ class DeveloperView extends ConsumerWidget {
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
leading: Icon(Icons.heart_broken),
|
||||
title: Text(appLocalizations.logsTest),
|
||||
onTap: () {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
ref.read(requestsProvider.notifier).addRequest(Connection(
|
||||
id: utils.id,
|
||||
start: DateTime.now(),
|
||||
metadata: Metadata(
|
||||
uid: i * i,
|
||||
network: utils.generateRandomString(
|
||||
maxLength: 1000,
|
||||
minLength: 20,
|
||||
),
|
||||
sourceIP: '',
|
||||
sourcePort: '',
|
||||
destinationIP: '',
|
||||
destinationPort: '',
|
||||
host: '',
|
||||
process: '',
|
||||
remoteDestination: "",
|
||||
),
|
||||
chains: ["chains"],
|
||||
));
|
||||
globalState.appController.addLog(
|
||||
Log.app(
|
||||
utils.generateRandomString(
|
||||
maxLength: 200,
|
||||
minLength: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.crashTest),
|
||||
onTap: () {
|
||||
clashCore.clashInterface.crash();
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
leading: Icon(Icons.delete_forever),
|
||||
title: Text(appLocalizations.clearData),
|
||||
onTap: () async {
|
||||
await globalState.appController.handleClear();
|
||||
@@ -78,7 +112,7 @@ class DeveloperView extends ConsumerWidget {
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
_getDeveloperList(context)
|
||||
_getDeveloperList(context, ref)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
@@ -16,36 +18,27 @@ class LogsFragment extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
final _logsStateNotifier = ValueNotifier<LogsState>(LogsState());
|
||||
final _cacheKey = ValueKey("logs_list");
|
||||
final _logsStateNotifier = ValueNotifier<LogsState>(
|
||||
LogsState(loading: true),
|
||||
);
|
||||
late ScrollController _scrollController;
|
||||
|
||||
double _currentMaxWidth = 0;
|
||||
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
|
||||
final _tag = CacheTag.rules;
|
||||
bool _isLoad = false;
|
||||
|
||||
List<Log> _logs = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final preOffset = globalState.cacheScrollPosition[_cacheKey] ?? -1;
|
||||
final position = globalState.cacheScrollPosition[_tag] ?? -1;
|
||||
_scrollController = ScrollController(
|
||||
initialScrollOffset: preOffset > 0 ? preOffset : double.maxFinite,
|
||||
initialScrollOffset: position > 0 ? position : double.maxFinite,
|
||||
);
|
||||
_logs = globalState.appState.logs.list;
|
||||
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
|
||||
logs: globalState.appState.logs.list,
|
||||
);
|
||||
ref.listenManual(
|
||||
logsProvider.select((state) => state.list),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
final isEquality = logListEquality.equals(prev, next);
|
||||
if (!isEquality) {
|
||||
_logs = next;
|
||||
updateLogsThrottler();
|
||||
}
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
logs: _logs,
|
||||
);
|
||||
ref.listenManual(
|
||||
isCurrentPageProvider(
|
||||
@@ -60,6 +53,18 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(
|
||||
logsProvider.select((state) => state.list),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
final isEquality = logListEquality.equals(prev, next);
|
||||
if (!isEquality) {
|
||||
_logs = next;
|
||||
updateLogsThrottler();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -94,13 +99,6 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_key.currentState?.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
_handleExport() async {
|
||||
final commonScaffoldState = context.commonScaffoldState;
|
||||
final res = await commonScaffoldState?.loadingRun<bool>(
|
||||
@@ -123,13 +121,13 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
final height = globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
log.payload ?? "",
|
||||
style: globalState.appController.context.textTheme.bodyLarge,
|
||||
log.payload,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
maxWidth: _currentMaxWidth,
|
||||
)
|
||||
.height;
|
||||
return height + bodySmallHeight + 8 + bodyMediumHeight + 40;
|
||||
return height + bodySmallHeight + 8 + bodyMediumHeight + 40 + 8;
|
||||
}
|
||||
|
||||
updateLogsThrottler() {
|
||||
@@ -142,82 +140,123 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
|
||||
logs: _logs,
|
||||
);
|
||||
if (mounted) {
|
||||
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
|
||||
logs: _logs,
|
||||
);
|
||||
}
|
||||
});
|
||||
}, duration: commonDuration);
|
||||
}
|
||||
|
||||
_preLoad() {
|
||||
if (_isLoad == true) {
|
||||
return;
|
||||
}
|
||||
_isLoad = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final isMobileView = ref.read(isMobileViewProvider);
|
||||
if (isMobileView) {
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
}
|
||||
final parts = _logs.batch(10);
|
||||
globalState.cacheHeightMap[_tag] ??= FixedMap(
|
||||
_logs.length,
|
||||
);
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
final part = parts[i];
|
||||
await Future(
|
||||
() {
|
||||
for (final log in part) {
|
||||
globalState.cacheHeightMap[_tag]?.updateCacheValue(
|
||||
log.payload,
|
||||
() => _getItemHeight(log),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
|
||||
loading: false,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
_handleTryClearCache(constraints.maxWidth - 40);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ValueListenableBuilder<LogsState>(
|
||||
valueListenable: _logsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
final logs = state.list;
|
||||
if (logs.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullLogsDesc,
|
||||
);
|
||||
}
|
||||
final items = logs
|
||||
.map<Widget>(
|
||||
(log) => LogItem(
|
||||
key: Key(log.dateTime.toString()),
|
||||
log: log,
|
||||
onClick: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return ScrollToEndBox<Log>(
|
||||
controller: _scrollController,
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: logs,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemExtentBuilder: (index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return _getItemHeight(log);
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return log.payload ?? "";
|
||||
_currentMaxWidth = constraints.maxWidth - 40;
|
||||
return ValueListenableBuilder<LogsState>(
|
||||
valueListenable: _logsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
_preLoad();
|
||||
final logs = state.list;
|
||||
final items = logs
|
||||
.map<Widget>(
|
||||
(log) => LogItem(
|
||||
key: Key(log.dateTime),
|
||||
log: log,
|
||||
onClick: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
final content = logs.isEmpty
|
||||
? NullStatus(
|
||||
label: appLocalizations.nullLogsDesc,
|
||||
)
|
||||
: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: ScrollToEndBox(
|
||||
controller: _scrollController,
|
||||
tag: _tag,
|
||||
dataSource: logs,
|
||||
child: CacheItemExtentListView(
|
||||
tag: _tag,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemExtentBuilder: (index) {
|
||||
if (index.isOdd) {
|
||||
return 0;
|
||||
}
|
||||
return _getItemHeight(logs[index ~/ 2]);
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
if (index.isOdd) {
|
||||
return "divider";
|
||||
}
|
||||
return logs[index ~/ 2].payload;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return FadeBox(
|
||||
child: state.loading
|
||||
? Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: content,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -242,14 +281,14 @@ class LogItem extends StatelessWidget {
|
||||
vertical: 4,
|
||||
),
|
||||
title: SelectableText(
|
||||
log.payload ?? '',
|
||||
log.payload,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SelectableText(
|
||||
"${log.dateTime}",
|
||||
log.dateTime,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
|
||||
@@ -23,7 +23,6 @@ class OverrideProfile extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _OverrideProfileState extends State<OverrideProfile> {
|
||||
final GlobalKey<CacheItemExtentListViewState> _ruleListKey = GlobalKey();
|
||||
final _controller = ScrollController();
|
||||
double _currentMaxWidth = 0;
|
||||
|
||||
@@ -86,13 +85,6 @@ class _OverrideProfileState extends State<OverrideProfile> {
|
||||
);
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_ruleListKey.currentState?.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
_buildContent() {
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
@@ -116,7 +108,7 @@ class _OverrideProfileState extends State<OverrideProfile> {
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
_handleTryClearCache(constraints.maxWidth - 104);
|
||||
_currentMaxWidth = constraints.maxWidth - 104;
|
||||
return CommonAutoHiddenScrollBar(
|
||||
controller: _controller,
|
||||
child: CustomScrollView(
|
||||
@@ -148,7 +140,6 @@ class _OverrideProfileState extends State<OverrideProfile> {
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0),
|
||||
sliver: RuleContent(
|
||||
maxWidth: _currentMaxWidth,
|
||||
ruleListKey: _ruleListKey,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
@@ -228,7 +219,7 @@ class _OverrideProfileState extends State<OverrideProfile> {
|
||||
message: TextSpan(
|
||||
text: appLocalizations.saveTip,
|
||||
),
|
||||
confirmText: appLocalizations.tip,
|
||||
confirmText: appLocalizations.save,
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
@@ -449,12 +440,10 @@ class RuleTitle extends ConsumerWidget {
|
||||
}
|
||||
|
||||
class RuleContent extends ConsumerWidget {
|
||||
final Key ruleListKey;
|
||||
final double maxWidth;
|
||||
|
||||
const RuleContent({
|
||||
super.key,
|
||||
required this.ruleListKey,
|
||||
required this.maxWidth,
|
||||
});
|
||||
|
||||
@@ -602,7 +591,7 @@ class RuleContent extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
return CacheItemExtentSliverReorderableList(
|
||||
key: ruleListKey,
|
||||
tag: CacheTag.rules,
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
return GestureDetector(
|
||||
|
||||
@@ -370,7 +370,7 @@ class ProfileItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
title: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:fl_clash/state.dart';
|
||||
|
||||
double get listHeaderHeight {
|
||||
final measure = globalState.measure;
|
||||
return 24 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
|
||||
return 28 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
|
||||
}
|
||||
|
||||
double getItemHeight(ProxyCardType proxyCardType) {
|
||||
|
||||
@@ -414,7 +414,10 @@ class _ListHeaderState extends State<ListHeader>
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final iconStyle = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.iconStyle));
|
||||
proxiesStyleSettingProvider.select(
|
||||
(state) => state.iconStyle,
|
||||
),
|
||||
);
|
||||
final icon = ref.watch(proxiesStyleSettingProvider.select((state) {
|
||||
final iconMapEntryList = state.iconMap.entries.toList();
|
||||
final index = iconMapEntryList.indexWhere((item) {
|
||||
@@ -430,30 +433,44 @@ class _ListHeaderState extends State<ListHeader>
|
||||
return this.icon;
|
||||
}));
|
||||
return switch (iconStyle) {
|
||||
ProxiesIconStyle.standard => Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
margin: const EdgeInsets.only(
|
||||
right: 16,
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: CommonTargetIcon(
|
||||
src: icon,
|
||||
size: 32,
|
||||
),
|
||||
ProxiesIconStyle.standard => LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(
|
||||
right: 16,
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
height: constraints.maxHeight,
|
||||
width: constraints.maxWidth,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(6.ap),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: CommonTargetIcon(
|
||||
src: icon,
|
||||
size: constraints.maxHeight - 12.ap,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ProxiesIconStyle.icon => Container(
|
||||
margin: const EdgeInsets.only(
|
||||
right: 16,
|
||||
),
|
||||
child: CommonTargetIcon(
|
||||
src: icon,
|
||||
size: 42,
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return CommonTargetIcon(
|
||||
src: icon,
|
||||
size: constraints.maxHeight - 8,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
ProxiesIconStyle.none => Container(),
|
||||
|
||||
@@ -78,7 +78,7 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: const ProxiesSetting(),
|
||||
title: appLocalizations.proxiesSetting,
|
||||
title: appLocalizations.settings,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -123,8 +123,13 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final proxiesType =
|
||||
ref.watch(proxiesStyleSettingProvider.select((state) => state.type));
|
||||
final proxiesType = ref.watch(
|
||||
proxiesStyleSettingProvider.select(
|
||||
(state) => state.type,
|
||||
),
|
||||
);
|
||||
|
||||
ref.watch(themeSettingProvider.select((state) => state.textScale));
|
||||
return switch (proxiesType) {
|
||||
ProxiesType.tab => ProxiesTabFragment(
|
||||
key: _proxiesTabKey,
|
||||
|
||||
@@ -147,22 +147,21 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
|
||||
FutureBuilder<FileInfo>(
|
||||
future: _getGeoFileLastModified(geoItem.fileName),
|
||||
builder: (_, snapshot) {
|
||||
final height = globalState.measure.bodyMediumHeight;
|
||||
return SizedBox(
|
||||
height: 24,
|
||||
child: FadeThroughBox(
|
||||
key: Key("fade_box_${geoItem.label}"),
|
||||
child: snapshot.data == null
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
snapshot.data!.desc,
|
||||
height: height,
|
||||
child: snapshot.data == null
|
||||
? SizedBox(
|
||||
width: height,
|
||||
height: height,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
snapshot.data!.desc,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
@@ -39,7 +41,20 @@ class ThemeFragment extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(child: ThemeColorsBox());
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
_ThemeModeItem(),
|
||||
_PrimaryColorItem(),
|
||||
_PrueBlackItem(),
|
||||
_TextScaleFactorItem(),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,42 +72,14 @@ class ItemCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
InfoHeader(
|
||||
info: info,
|
||||
actions: actions,
|
||||
),
|
||||
child,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeColorsBox extends ConsumerStatefulWidget {
|
||||
const ThemeColorsBox({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ThemeColorsBox> createState() => _ThemeColorsBoxState();
|
||||
}
|
||||
|
||||
class _ThemeColorsBoxState extends ConsumerState<ThemeColorsBox> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
return Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
_ThemeModeItem(),
|
||||
_PrimaryColorItem(),
|
||||
_PrueBlackItem(),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
InfoHeader(
|
||||
info: info,
|
||||
actions: actions,
|
||||
),
|
||||
child,
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -296,18 +283,21 @@ class _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final vm3 = ref.watch(
|
||||
final vm4 = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
(state) => VM3(
|
||||
(state) => VM4(
|
||||
a: state.primaryColor,
|
||||
b: state.primaryColors,
|
||||
c: state.schemeVariant,
|
||||
d: state.primaryColor == defaultPrimaryColor &&
|
||||
intListEquality.equals(state.primaryColors, defaultPrimaryColors),
|
||||
),
|
||||
),
|
||||
);
|
||||
final primaryColor = vm3.a;
|
||||
final primaryColors = [null, ...vm3.b];
|
||||
final schemeVariant = vm3.c;
|
||||
final primaryColor = vm4.a;
|
||||
final primaryColors = [null, ...vm4.b];
|
||||
final schemeVariant = vm4.c;
|
||||
final isEquals = vm4.d;
|
||||
|
||||
return CommonPopScope(
|
||||
onPop: () {
|
||||
@@ -334,33 +324,32 @@ class _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {
|
||||
onPressed: _handleChangeSchemeVariant,
|
||||
child: Text(Intl.message("${schemeVariant.name}Scheme")),
|
||||
),
|
||||
_removablePrimaryColor != null
|
||||
? FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
},
|
||||
child: Text(appLocalizations.cancel),
|
||||
)
|
||||
: IconButton.filledTonal(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(4),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: _handleReset,
|
||||
icon: Icon(Icons.replay),
|
||||
)
|
||||
if (_removablePrimaryColor != null)
|
||||
FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
},
|
||||
child: Text(appLocalizations.cancel),
|
||||
),
|
||||
if (_removablePrimaryColor == null && !isEquals)
|
||||
IconButton.filledTonal(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(4),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: _handleReset,
|
||||
icon: Icon(Icons.replay),
|
||||
)
|
||||
],
|
||||
space: 8,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
@@ -452,33 +441,118 @@ class _PrueBlackItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final prueBlack =
|
||||
ref.watch(themeSettingProvider.select((state) => state.pureBlack));
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: ListItem.switchItem(
|
||||
leading: Icon(
|
||||
Icons.contrast,
|
||||
),
|
||||
horizontalTitleGap: 12,
|
||||
title: Text(
|
||||
appLocalizations.pureBlackMode,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
delegate: SwitchDelegate(
|
||||
value: prueBlack,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
pureBlack: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
final prueBlack = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
(state) => state.pureBlack,
|
||||
),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: Icon(
|
||||
Icons.contrast,
|
||||
),
|
||||
horizontalTitleGap: 12,
|
||||
title: Text(
|
||||
appLocalizations.pureBlackMode,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
delegate: SwitchDelegate(
|
||||
value: prueBlack,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
pureBlack: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TextScaleFactorItem extends ConsumerWidget {
|
||||
const _TextScaleFactorItem();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final textScale = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
(state) => state.textScale,
|
||||
),
|
||||
);
|
||||
final String process = "${((textScale.scale * 100) as double).round()}%";
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 8),
|
||||
child: ListItem.switchItem(
|
||||
leading: Icon(
|
||||
Icons.text_fields,
|
||||
),
|
||||
horizontalTitleGap: 12,
|
||||
title: Text(
|
||||
appLocalizations.textScale,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
delegate: SwitchDelegate(
|
||||
value: textScale.enable,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.textScale(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
spacing: 32,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DisabledMask(
|
||||
status: !textScale.enable,
|
||||
child: ActivateBox(
|
||||
active: textScale.enable,
|
||||
child: SliderTheme(
|
||||
data: _SliderDefaultsM3(context),
|
||||
child: Slider(
|
||||
padding: EdgeInsets.zero,
|
||||
min: minTextScale,
|
||||
max: maxTextScale,
|
||||
value: textScale.scale,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.textScale(
|
||||
scale: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 4),
|
||||
child: Text(
|
||||
process,
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,3 +618,112 @@ class _PaletteDialogState extends State<_PaletteDialog> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SliderDefaultsM3 extends SliderThemeData {
|
||||
_SliderDefaultsM3(this.context) : super(trackHeight: 16.0);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
Color? get activeTrackColor => _colors.primary;
|
||||
|
||||
@override
|
||||
Color? get inactiveTrackColor => _colors.secondaryContainer;
|
||||
|
||||
@override
|
||||
Color? get secondaryActiveTrackColor => _colors.primary.withOpacity(0.54);
|
||||
|
||||
@override
|
||||
Color? get disabledActiveTrackColor => _colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get disabledInactiveTrackColor => _colors.onSurface.withOpacity(0.12);
|
||||
|
||||
@override
|
||||
Color? get disabledSecondaryActiveTrackColor =>
|
||||
_colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get activeTickMarkColor => _colors.onPrimary.withOpacity(1.0);
|
||||
|
||||
@override
|
||||
Color? get inactiveTickMarkColor =>
|
||||
_colors.onSecondaryContainer.withOpacity(1.0);
|
||||
|
||||
@override
|
||||
Color? get disabledActiveTickMarkColor => _colors.onInverseSurface;
|
||||
|
||||
@override
|
||||
Color? get disabledInactiveTickMarkColor => _colors.onSurface;
|
||||
|
||||
@override
|
||||
Color? get thumbColor => _colors.primary;
|
||||
|
||||
@override
|
||||
Color? get disabledThumbColor => _colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get overlayColor =>
|
||||
WidgetStateColor.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.dragged)) {
|
||||
return _colors.primary.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return _colors.primary.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return _colors.primary.withOpacity(0.1);
|
||||
}
|
||||
|
||||
return Colors.transparent;
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get valueIndicatorTextStyle =>
|
||||
Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||
color: _colors.onInverseSurface,
|
||||
);
|
||||
|
||||
@override
|
||||
Color? get valueIndicatorColor => _colors.inverseSurface;
|
||||
|
||||
@override
|
||||
SliderComponentShape? get valueIndicatorShape =>
|
||||
const RoundedRectSliderValueIndicatorShape();
|
||||
|
||||
@override
|
||||
SliderComponentShape? get thumbShape => const HandleThumbShape();
|
||||
|
||||
@override
|
||||
SliderTrackShape? get trackShape => const GappedSliderTrackShape();
|
||||
|
||||
@override
|
||||
SliderComponentShape? get overlayShape => const RoundSliderOverlayShape();
|
||||
|
||||
@override
|
||||
SliderTickMarkShape? get tickMarkShape =>
|
||||
const RoundSliderTickMarkShape(tickMarkRadius: 4.0 / 2);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Size?>? get thumbSize {
|
||||
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return const Size(4.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return const Size(4.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return const Size(2.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return const Size(2.0, 44.0);
|
||||
}
|
||||
return const Size(4.0, 44.0);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
double? get trackGap => 6.0;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ class _DeveloperItem extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListItem.open(
|
||||
leading: const Icon(Icons.developer_mode),
|
||||
leading: const Icon(Icons.developer_board),
|
||||
title: Text(appLocalizations.developerMode),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.developerMode,
|
||||
|
||||
@@ -164,6 +164,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"View current connections data",
|
||||
),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"),
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("Contact me"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Content"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Content cannot be empty",
|
||||
@@ -326,6 +327,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
|
||||
"Intelligent selection",
|
||||
),
|
||||
"internet": MessageLookupByLibrary.simpleMessage("Internet"),
|
||||
"intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"),
|
||||
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
|
||||
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -362,6 +364,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"logs": MessageLookupByLibrary.simpleMessage("Logs"),
|
||||
"logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"),
|
||||
"logsTest": MessageLookupByLibrary.simpleMessage("Logs test"),
|
||||
"loopback": MessageLookupByLibrary.simpleMessage("Loopback unlock tool"),
|
||||
"loopbackDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Used for UWP loopback unlocking",
|
||||
@@ -409,6 +412,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("No info"),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("No network"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("No network APP"),
|
||||
"noProxy": MessageLookupByLibrary.simpleMessage("No proxy"),
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Please create a profile or add a valid profile",
|
||||
@@ -536,6 +540,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage(
|
||||
"Only recovery profiles",
|
||||
),
|
||||
"recoveryStrategy": MessageLookupByLibrary.simpleMessage(
|
||||
"Recovery strategy",
|
||||
),
|
||||
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage(
|
||||
"Compatible",
|
||||
),
|
||||
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
|
||||
"Override",
|
||||
),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("redo"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
|
||||
@@ -622,6 +635,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("System"),
|
||||
"systemApp": MessageLookupByLibrary.simpleMessage("System APP"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("System font"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -637,6 +651,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Enabling it will allow TCP concurrency",
|
||||
),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("Test url"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("Text Scaling"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -118,6 +118,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"connections": MessageLookupByLibrary.simpleMessage("接続"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("連絡する"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容は必須です"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("コンテンツテーマ"),
|
||||
@@ -236,6 +237,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"init": MessageLookupByLibrary.simpleMessage("初期化"),
|
||||
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("正しいホットキーを入力"),
|
||||
"intelligentSelected": MessageLookupByLibrary.simpleMessage("インテリジェント選択"),
|
||||
"internet": MessageLookupByLibrary.simpleMessage("インターネット"),
|
||||
"intranetIP": MessageLookupByLibrary.simpleMessage("イントラネットIP"),
|
||||
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
|
||||
"ipv6Desc": MessageLookupByLibrary.simpleMessage("有効化するとIPv6トラフィックを受信可能"),
|
||||
@@ -260,6 +262,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"logcatDesc": MessageLookupByLibrary.simpleMessage("無効化するとログエントリを非表示"),
|
||||
"logs": MessageLookupByLibrary.simpleMessage("ログ"),
|
||||
"logsDesc": MessageLookupByLibrary.simpleMessage("ログキャプチャ記録"),
|
||||
"logsTest": MessageLookupByLibrary.simpleMessage("ログテスト"),
|
||||
"loopback": MessageLookupByLibrary.simpleMessage("ループバック解除ツール"),
|
||||
"loopbackDesc": MessageLookupByLibrary.simpleMessage("UWPループバック解除用"),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("疎"),
|
||||
@@ -295,6 +298,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("情報なし"),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("追加情報なし"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("ネットワークなし"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("ネットワークなしアプリ"),
|
||||
"noProxy": MessageLookupByLibrary.simpleMessage("プロキシなし"),
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"プロファイルを作成するか、有効なプロファイルを追加してください",
|
||||
@@ -392,6 +396,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recovery": MessageLookupByLibrary.simpleMessage("復元"),
|
||||
"recoveryAll": MessageLookupByLibrary.simpleMessage("全データ復元"),
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("プロファイルのみ復元"),
|
||||
"recoveryStrategy": MessageLookupByLibrary.simpleMessage("リカバリー戦略"),
|
||||
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage("互換性"),
|
||||
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
|
||||
"オーバーライド",
|
||||
),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("復元成功"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
|
||||
@@ -460,6 +469,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"submit": MessageLookupByLibrary.simpleMessage("送信"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("同期"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("システム"),
|
||||
"systemApp": MessageLookupByLibrary.simpleMessage("システムアプリ"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("システムフォント"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("システムプロキシ"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -471,6 +481,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("テキストスケーリング"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("テーマ"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("テーマカラー"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage("ダークモードの設定、色の調整"),
|
||||
|
||||
@@ -170,6 +170,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Просмотр текущих данных о соединениях",
|
||||
),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("Свяжитесь со мной"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Содержание не может быть пустым",
|
||||
@@ -348,6 +349,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
|
||||
"Интеллектуальный выбор",
|
||||
),
|
||||
"internet": MessageLookupByLibrary.simpleMessage("Интернет"),
|
||||
"intranetIP": MessageLookupByLibrary.simpleMessage("Внутренний IP"),
|
||||
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
|
||||
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -384,6 +386,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"logs": MessageLookupByLibrary.simpleMessage("Логи"),
|
||||
"logsDesc": MessageLookupByLibrary.simpleMessage("Записи захвата логов"),
|
||||
"logsTest": MessageLookupByLibrary.simpleMessage("Тест журналов"),
|
||||
"loopback": MessageLookupByLibrary.simpleMessage(
|
||||
"Инструмент разблокировки Loopback",
|
||||
),
|
||||
@@ -437,6 +440,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Нет дополнительной информации",
|
||||
),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("Нет сети"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("Приложение без сети"),
|
||||
"noProxy": MessageLookupByLibrary.simpleMessage("Нет прокси"),
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Пожалуйста, создайте профиль или добавьте действительный профиль",
|
||||
@@ -570,6 +574,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage(
|
||||
"Только восстановление профилей",
|
||||
),
|
||||
"recoveryStrategy": MessageLookupByLibrary.simpleMessage(
|
||||
"Стратегия восстановления",
|
||||
),
|
||||
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage(
|
||||
"Совместимый",
|
||||
),
|
||||
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
|
||||
"Переопределение",
|
||||
),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage(
|
||||
"Восстановление успешно",
|
||||
),
|
||||
@@ -660,6 +673,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("Система"),
|
||||
"systemApp": MessageLookupByLibrary.simpleMessage("Системное приложение"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("Системный шрифт"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("Системный прокси"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -675,6 +689,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Включение позволит использовать параллелизм TCP",
|
||||
),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("Тест URL"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("Масштабирование текста"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Тема"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("Цвет темы"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -108,6 +108,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"connections": MessageLookupByLibrary.simpleMessage("连接"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("联系我"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容不能为空"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("内容主题"),
|
||||
@@ -210,6 +211,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"init": MessageLookupByLibrary.simpleMessage("初始化"),
|
||||
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"),
|
||||
"intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"),
|
||||
"internet": MessageLookupByLibrary.simpleMessage("互联网"),
|
||||
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
|
||||
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
|
||||
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
|
||||
@@ -232,6 +234,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"),
|
||||
"logs": MessageLookupByLibrary.simpleMessage("日志"),
|
||||
"logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"),
|
||||
"logsTest": MessageLookupByLibrary.simpleMessage("日志测试"),
|
||||
"loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"),
|
||||
"loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("宽松"),
|
||||
@@ -263,6 +266,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("无网络应用"),
|
||||
"noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"),
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
|
||||
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
|
||||
@@ -344,6 +348,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recovery": MessageLookupByLibrary.simpleMessage("恢复"),
|
||||
"recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"),
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
|
||||
"recoveryStrategy": MessageLookupByLibrary.simpleMessage("恢复策略"),
|
||||
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage("兼容"),
|
||||
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage("覆盖"),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("重做"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
|
||||
@@ -404,6 +411,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"submit": MessageLookupByLibrary.simpleMessage("提交"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("同步"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("系统"),
|
||||
"systemApp": MessageLookupByLibrary.simpleMessage("系统应用"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("系统字体"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
|
||||
@@ -413,6 +421,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("文本缩放"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("主题"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
|
||||
|
||||
@@ -3054,6 +3054,71 @@ class AppLocalizations {
|
||||
String get clearData {
|
||||
return Intl.message('Clear Data', name: 'clearData', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Text Scaling`
|
||||
String get textScale {
|
||||
return Intl.message('Text Scaling', name: 'textScale', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Internet`
|
||||
String get internet {
|
||||
return Intl.message('Internet', name: 'internet', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `System APP`
|
||||
String get systemApp {
|
||||
return Intl.message('System APP', name: 'systemApp', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `No network APP`
|
||||
String get noNetworkApp {
|
||||
return Intl.message(
|
||||
'No network APP',
|
||||
name: 'noNetworkApp',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Contact me`
|
||||
String get contactMe {
|
||||
return Intl.message('Contact me', name: 'contactMe', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Recovery strategy`
|
||||
String get recoveryStrategy {
|
||||
return Intl.message(
|
||||
'Recovery strategy',
|
||||
name: 'recoveryStrategy',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Override`
|
||||
String get recoveryStrategy_override {
|
||||
return Intl.message(
|
||||
'Override',
|
||||
name: 'recoveryStrategy_override',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Compatible`
|
||||
String get recoveryStrategy_compatible {
|
||||
return Intl.message(
|
||||
'Compatible',
|
||||
name: 'recoveryStrategy_compatible',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Logs test`
|
||||
String get logsTest {
|
||||
return Intl.message('Logs test', name: 'logsTest', desc: '', args: []);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -21,12 +21,14 @@ import 'common/common.dart';
|
||||
Future<void> main() async {
|
||||
globalState.isService = false;
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterError.onError = (details) {
|
||||
commonPrint.log(details.stack.toString());
|
||||
};
|
||||
final version = await system.version;
|
||||
await clashCore.preload();
|
||||
await globalState.initApp(version);
|
||||
await android?.init();
|
||||
await window?.init(version);
|
||||
globalState.isPre = const String.fromEnvironment("APP_ENV") != 'stable';
|
||||
HttpOverrides.global = FlClashHttpOverrides();
|
||||
runApp(ProviderScope(
|
||||
child: const Application(),
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class AppStateManager extends StatefulWidget {
|
||||
class AppStateManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppStateManager({
|
||||
@@ -14,15 +16,22 @@ class AppStateManager extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<AppStateManager> createState() => _AppStateManagerState();
|
||||
ConsumerState<AppStateManager> createState() => _AppStateManagerState();
|
||||
}
|
||||
|
||||
class _AppStateManagerState extends State<AppStateManager>
|
||||
class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
ref.listenManual(layoutChangeProvider, (prev, next) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (prev != next) {
|
||||
globalState.cacheHeightMap = {};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -71,22 +71,22 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
|
||||
@override
|
||||
void onLog(Log log) {
|
||||
ref.watch(logsProvider.notifier).addLog(log);
|
||||
ref.read(logsProvider.notifier).addLog(log);
|
||||
if (log.logLevel == LogLevel.error) {
|
||||
globalState.showNotifier(log.payload ?? '');
|
||||
globalState.showNotifier(log.payload);
|
||||
}
|
||||
super.onLog(log);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRequest(Connection connection) async {
|
||||
ref.watch(requestsProvider.notifier).addRequest(connection);
|
||||
ref.read(requestsProvider.notifier).addRequest(connection);
|
||||
super.onRequest(connection);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoaded(String providerName) async {
|
||||
ref.watch(providersProvider.notifier).setProvider(
|
||||
ref.read(providersProvider.notifier).setProvider(
|
||||
await clashCore.getExternalProvider(
|
||||
providerName,
|
||||
),
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'dart:math';
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/measure.dart';
|
||||
import 'package:fl_clash/common/theme.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ThemeManager extends StatelessWidget {
|
||||
class ThemeManager extends ConsumerWidget {
|
||||
final Widget child;
|
||||
|
||||
const ThemeManager({
|
||||
@@ -13,9 +16,20 @@ class ThemeManager extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
globalState.measure = Measure.of(context);
|
||||
globalState.theme = CommonTheme.of(context);
|
||||
Widget build(BuildContext context, ref) {
|
||||
final textScale = ref.read(
|
||||
themeSettingProvider.select((state) => state.textScale),
|
||||
);
|
||||
final double textScaleFactor = max(
|
||||
min(
|
||||
textScale.enable ? textScale.scale : defaultTextScaleFactor,
|
||||
maxTextScale,
|
||||
),
|
||||
minTextScale,
|
||||
);
|
||||
|
||||
globalState.measure = Measure.of(context, textScaleFactor);
|
||||
globalState.theme = CommonTheme.of(context, textScaleFactor);
|
||||
final padding = MediaQuery.of(context).padding;
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
return MediaQuery(
|
||||
|
||||
@@ -25,7 +25,6 @@ class WindowManager extends ConsumerStatefulWidget {
|
||||
|
||||
class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
with WindowListener, WindowExtListener {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
@@ -183,19 +182,23 @@ class _WindowHeaderState extends State<WindowHeader> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_updateMaximized() {
|
||||
isMaximizedNotifier.value = !isMaximizedNotifier.value;
|
||||
switch (isMaximizedNotifier.value) {
|
||||
_updateMaximized() async {
|
||||
final isMaximized = await windowManager.isMaximized();
|
||||
switch (isMaximized) {
|
||||
case true:
|
||||
windowManager.maximize();
|
||||
await windowManager.unmaximize();
|
||||
break;
|
||||
case false:
|
||||
windowManager.unmaximize();
|
||||
await windowManager.maximize();
|
||||
break;
|
||||
}
|
||||
isMaximizedNotifier.value = await windowManager.isMaximized();
|
||||
}
|
||||
|
||||
_updatePin() {
|
||||
isPinNotifier.value = !isPinNotifier.value;
|
||||
windowManager.setAlwaysOnTop(isPinNotifier.value);
|
||||
_updatePin() async {
|
||||
final isAlwaysOnTop = await windowManager.isAlwaysOnTop();
|
||||
await windowManager.setAlwaysOnTop(!isAlwaysOnTop);
|
||||
isPinNotifier.value = await windowManager.isAlwaysOnTop();
|
||||
}
|
||||
|
||||
_buildActions() {
|
||||
|
||||
@@ -147,7 +147,8 @@ class Tun with _$Tun {
|
||||
const factory Tun({
|
||||
@Default(false) bool enable,
|
||||
@Default(appName) String device,
|
||||
@Default(TunStack.gvisor) TunStack stack,
|
||||
@JsonKey(name: "auto-route") @Default(false) bool autoRoute,
|
||||
@Default(TunStack.mixed) TunStack stack,
|
||||
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
|
||||
@JsonKey(name: "route-address") @Default([]) List<String> routeAddress,
|
||||
}) = _Tun;
|
||||
|
||||
@@ -30,7 +30,8 @@ class Package with _$Package {
|
||||
const factory Package({
|
||||
required String packageName,
|
||||
required String label,
|
||||
required bool isSystem,
|
||||
required bool system,
|
||||
required bool internet,
|
||||
required int lastUpdateTime,
|
||||
}) = _Package;
|
||||
|
||||
@@ -84,33 +85,33 @@ extension ConnectionExt on Connection {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Log {
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel logLevel;
|
||||
@JsonKey(name: "Payload")
|
||||
String? payload;
|
||||
DateTime _dateTime;
|
||||
String _logDateTime(_) {
|
||||
return DateTime.now().toString();
|
||||
}
|
||||
|
||||
Log({
|
||||
required this.logLevel,
|
||||
this.payload,
|
||||
}) : _dateTime = DateTime.now();
|
||||
// String _logId(_) {
|
||||
// return utils.id;
|
||||
// }
|
||||
|
||||
DateTime get dateTime => _dateTime;
|
||||
@freezed
|
||||
class Log with _$Log {
|
||||
const factory Log({
|
||||
@JsonKey(name: "LogLevel") @Default(LogLevel.app) LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") @Default("") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) required String dateTime,
|
||||
}) = _Log;
|
||||
|
||||
factory Log.fromJson(Map<String, dynamic> json) {
|
||||
return _$LogFromJson(json);
|
||||
factory Log.app(
|
||||
String payload,
|
||||
) {
|
||||
return Log(
|
||||
payload: payload,
|
||||
dateTime: _logDateTime(null),
|
||||
// id: _logId(null),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LogToJson(this);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Log{logLevel: $logLevel, payload: $payload, dateTime: $dateTime}';
|
||||
}
|
||||
factory Log.fromJson(Map<String, Object?> json) => _$LogFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -119,6 +120,7 @@ class LogsState with _$LogsState {
|
||||
@Default([]) List<Log> logs,
|
||||
@Default([]) List<String> keywords,
|
||||
@Default("") String query,
|
||||
@Default(false) bool loading,
|
||||
}) = _LogsState;
|
||||
}
|
||||
|
||||
@@ -127,11 +129,10 @@ extension LogsStateExt on LogsState {
|
||||
final lowQuery = query.toLowerCase();
|
||||
return logs.where(
|
||||
(log) {
|
||||
final payload = log.payload?.toLowerCase();
|
||||
final payload = log.payload.toLowerCase();
|
||||
final logLevelName = log.logLevel.name;
|
||||
return {logLevelName}.containsAll(keywords) &&
|
||||
((payload?.contains(lowQuery) ?? false) ||
|
||||
logLevelName.contains(lowQuery));
|
||||
((payload.contains(lowQuery)) || logLevelName.contains(lowQuery));
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
@@ -143,6 +144,7 @@ class ConnectionsState with _$ConnectionsState {
|
||||
@Default([]) List<Connection> connections,
|
||||
@Default([]) List<String> keywords,
|
||||
@Default("") String query,
|
||||
@Default(false) bool loading,
|
||||
}) = _ConnectionsState;
|
||||
}
|
||||
|
||||
@@ -512,3 +514,17 @@ class PopupMenuItemData {
|
||||
final IconData? icon;
|
||||
final PopupMenuItemType? type;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TextPainterParams with _$TextPainterParams {
|
||||
const factory TextPainterParams({
|
||||
required String? text,
|
||||
required double? fontSize,
|
||||
required double textScaleFactor,
|
||||
@Default(double.infinity) double maxWidth,
|
||||
int? maxLines,
|
||||
}) = _TextPainterParams;
|
||||
|
||||
factory TextPainterParams.fromJson(Map<String, Object?> json) =>
|
||||
_$TextPainterParamsFromJson(json);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class AppSettingProps with _$AppSettingProps {
|
||||
@Default(false) bool autoLaunch,
|
||||
@Default(false) bool silentLaunch,
|
||||
@Default(false) bool autoRun,
|
||||
@Default(true) bool openLogs,
|
||||
@Default(false) bool openLogs,
|
||||
@Default(true) bool closeConnections,
|
||||
@Default(defaultTestUrl) String testUrl,
|
||||
@Default(true) bool isAnimateToPage,
|
||||
@@ -85,6 +85,7 @@ class AppSettingProps with _$AppSettingProps {
|
||||
@Default(true) bool minimizeOnExit,
|
||||
@Default(false) bool hidden,
|
||||
@Default(false) bool developerMode,
|
||||
@Default(RecoveryStrategy.compatible) RecoveryStrategy recoveryStrategy,
|
||||
}) = _AppSettingProps;
|
||||
|
||||
factory AppSettingProps.fromJson(Map<String, Object?> json) =>
|
||||
@@ -106,6 +107,7 @@ class AccessControl with _$AccessControl {
|
||||
@Default([]) List<String> rejectList,
|
||||
@Default(AccessSortType.none) AccessSortType sort,
|
||||
@Default(true) bool isFilterSystemApp,
|
||||
@Default(true) bool isFilterNonInternetApp,
|
||||
}) = _AccessControl;
|
||||
|
||||
factory AccessControl.fromJson(Map<String, Object?> json) =>
|
||||
@@ -173,14 +175,26 @@ class ProxiesStyle with _$ProxiesStyle {
|
||||
json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TextScale with _$TextScale {
|
||||
const factory TextScale({
|
||||
@Default(false) enable,
|
||||
@Default(1.0) scale,
|
||||
}) = _TextScale;
|
||||
|
||||
factory TextScale.fromJson(Map<String, Object?> json) =>
|
||||
_$TextScaleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ThemeProps with _$ThemeProps {
|
||||
const factory ThemeProps({
|
||||
int? primaryColor,
|
||||
@Default(defaultPrimaryColors) List<int> primaryColors,
|
||||
@Default(ThemeMode.dark) ThemeMode themeMode,
|
||||
@Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant,
|
||||
@Default(DynamicSchemeVariant.content) DynamicSchemeVariant schemeVariant,
|
||||
@Default(false) bool pureBlack,
|
||||
@Default(TextScale()) TextScale textScale,
|
||||
}) = _ThemeProps;
|
||||
|
||||
factory ThemeProps.fromJson(Map<String, Object?> json) =>
|
||||
|
||||
@@ -660,6 +660,8 @@ Tun _$TunFromJson(Map<String, dynamic> json) {
|
||||
mixin _$Tun {
|
||||
bool get enable => throw _privateConstructorUsedError;
|
||||
String get device => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "auto-route")
|
||||
bool get autoRoute => throw _privateConstructorUsedError;
|
||||
TunStack get stack => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "dns-hijack")
|
||||
List<String> get dnsHijack => throw _privateConstructorUsedError;
|
||||
@@ -683,6 +685,7 @@ abstract class $TunCopyWith<$Res> {
|
||||
$Res call(
|
||||
{bool enable,
|
||||
String device,
|
||||
@JsonKey(name: "auto-route") bool autoRoute,
|
||||
TunStack stack,
|
||||
@JsonKey(name: "dns-hijack") List<String> dnsHijack,
|
||||
@JsonKey(name: "route-address") List<String> routeAddress});
|
||||
@@ -704,6 +707,7 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> {
|
||||
$Res call({
|
||||
Object? enable = null,
|
||||
Object? device = null,
|
||||
Object? autoRoute = null,
|
||||
Object? stack = null,
|
||||
Object? dnsHijack = null,
|
||||
Object? routeAddress = null,
|
||||
@@ -717,6 +721,10 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> {
|
||||
? _value.device
|
||||
: device // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
autoRoute: null == autoRoute
|
||||
? _value.autoRoute
|
||||
: autoRoute // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
stack: null == stack
|
||||
? _value.stack
|
||||
: stack // ignore: cast_nullable_to_non_nullable
|
||||
@@ -742,6 +750,7 @@ abstract class _$$TunImplCopyWith<$Res> implements $TunCopyWith<$Res> {
|
||||
$Res call(
|
||||
{bool enable,
|
||||
String device,
|
||||
@JsonKey(name: "auto-route") bool autoRoute,
|
||||
TunStack stack,
|
||||
@JsonKey(name: "dns-hijack") List<String> dnsHijack,
|
||||
@JsonKey(name: "route-address") List<String> routeAddress});
|
||||
@@ -760,6 +769,7 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl>
|
||||
$Res call({
|
||||
Object? enable = null,
|
||||
Object? device = null,
|
||||
Object? autoRoute = null,
|
||||
Object? stack = null,
|
||||
Object? dnsHijack = null,
|
||||
Object? routeAddress = null,
|
||||
@@ -773,6 +783,10 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl>
|
||||
? _value.device
|
||||
: device // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
autoRoute: null == autoRoute
|
||||
? _value.autoRoute
|
||||
: autoRoute // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
stack: null == stack
|
||||
? _value.stack
|
||||
: stack // ignore: cast_nullable_to_non_nullable
|
||||
@@ -795,7 +809,8 @@ class _$TunImpl implements _Tun {
|
||||
const _$TunImpl(
|
||||
{this.enable = false,
|
||||
this.device = appName,
|
||||
this.stack = TunStack.gvisor,
|
||||
@JsonKey(name: "auto-route") this.autoRoute = false,
|
||||
this.stack = TunStack.mixed,
|
||||
@JsonKey(name: "dns-hijack")
|
||||
final List<String> dnsHijack = const ["any:53"],
|
||||
@JsonKey(name: "route-address")
|
||||
@@ -813,6 +828,9 @@ class _$TunImpl implements _Tun {
|
||||
@JsonKey()
|
||||
final String device;
|
||||
@override
|
||||
@JsonKey(name: "auto-route")
|
||||
final bool autoRoute;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TunStack stack;
|
||||
final List<String> _dnsHijack;
|
||||
@@ -835,7 +853,7 @@ class _$TunImpl implements _Tun {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Tun(enable: $enable, device: $device, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)';
|
||||
return 'Tun(enable: $enable, device: $device, autoRoute: $autoRoute, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -845,6 +863,8 @@ class _$TunImpl implements _Tun {
|
||||
other is _$TunImpl &&
|
||||
(identical(other.enable, enable) || other.enable == enable) &&
|
||||
(identical(other.device, device) || other.device == device) &&
|
||||
(identical(other.autoRoute, autoRoute) ||
|
||||
other.autoRoute == autoRoute) &&
|
||||
(identical(other.stack, stack) || other.stack == stack) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._dnsHijack, _dnsHijack) &&
|
||||
@@ -858,6 +878,7 @@ class _$TunImpl implements _Tun {
|
||||
runtimeType,
|
||||
enable,
|
||||
device,
|
||||
autoRoute,
|
||||
stack,
|
||||
const DeepCollectionEquality().hash(_dnsHijack),
|
||||
const DeepCollectionEquality().hash(_routeAddress));
|
||||
@@ -882,6 +903,7 @@ abstract class _Tun implements Tun {
|
||||
const factory _Tun(
|
||||
{final bool enable,
|
||||
final String device,
|
||||
@JsonKey(name: "auto-route") final bool autoRoute,
|
||||
final TunStack stack,
|
||||
@JsonKey(name: "dns-hijack") final List<String> dnsHijack,
|
||||
@JsonKey(name: "route-address") final List<String> routeAddress}) =
|
||||
@@ -894,6 +916,9 @@ abstract class _Tun implements Tun {
|
||||
@override
|
||||
String get device;
|
||||
@override
|
||||
@JsonKey(name: "auto-route")
|
||||
bool get autoRoute;
|
||||
@override
|
||||
TunStack get stack;
|
||||
@override
|
||||
@JsonKey(name: "dns-hijack")
|
||||
|
||||
@@ -66,8 +66,9 @@ Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
|
||||
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
device: json['device'] as String? ?? appName,
|
||||
autoRoute: json['auto-route'] as bool? ?? false,
|
||||
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
|
||||
TunStack.gvisor,
|
||||
TunStack.mixed,
|
||||
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
@@ -81,6 +82,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
||||
Map<String, dynamic> _$$TunImplToJson(_$TunImpl instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'device': instance.device,
|
||||
'auto-route': instance.autoRoute,
|
||||
'stack': _$TunStackEnumMap[instance.stack]!,
|
||||
'dns-hijack': instance.dnsHijack,
|
||||
'route-address': instance.routeAddress,
|
||||
|
||||
@@ -289,7 +289,8 @@ Package _$PackageFromJson(Map<String, dynamic> json) {
|
||||
mixin _$Package {
|
||||
String get packageName => throw _privateConstructorUsedError;
|
||||
String get label => throw _privateConstructorUsedError;
|
||||
bool get isSystem => throw _privateConstructorUsedError;
|
||||
bool get system => throw _privateConstructorUsedError;
|
||||
bool get internet => throw _privateConstructorUsedError;
|
||||
int get lastUpdateTime => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Package to a JSON map.
|
||||
@@ -307,7 +308,11 @@ abstract class $PackageCopyWith<$Res> {
|
||||
_$PackageCopyWithImpl<$Res, Package>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String packageName, String label, bool isSystem, int lastUpdateTime});
|
||||
{String packageName,
|
||||
String label,
|
||||
bool system,
|
||||
bool internet,
|
||||
int lastUpdateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -327,7 +332,8 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package>
|
||||
$Res call({
|
||||
Object? packageName = null,
|
||||
Object? label = null,
|
||||
Object? isSystem = null,
|
||||
Object? system = null,
|
||||
Object? internet = null,
|
||||
Object? lastUpdateTime = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -339,9 +345,13 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package>
|
||||
? _value.label
|
||||
: label // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isSystem: null == isSystem
|
||||
? _value.isSystem
|
||||
: isSystem // ignore: cast_nullable_to_non_nullable
|
||||
system: null == system
|
||||
? _value.system
|
||||
: system // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
internet: null == internet
|
||||
? _value.internet
|
||||
: internet // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
lastUpdateTime: null == lastUpdateTime
|
||||
? _value.lastUpdateTime
|
||||
@@ -359,7 +369,11 @@ abstract class _$$PackageImplCopyWith<$Res> implements $PackageCopyWith<$Res> {
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String packageName, String label, bool isSystem, int lastUpdateTime});
|
||||
{String packageName,
|
||||
String label,
|
||||
bool system,
|
||||
bool internet,
|
||||
int lastUpdateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -377,7 +391,8 @@ class __$$PackageImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? packageName = null,
|
||||
Object? label = null,
|
||||
Object? isSystem = null,
|
||||
Object? system = null,
|
||||
Object? internet = null,
|
||||
Object? lastUpdateTime = null,
|
||||
}) {
|
||||
return _then(_$PackageImpl(
|
||||
@@ -389,9 +404,13 @@ class __$$PackageImplCopyWithImpl<$Res>
|
||||
? _value.label
|
||||
: label // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isSystem: null == isSystem
|
||||
? _value.isSystem
|
||||
: isSystem // ignore: cast_nullable_to_non_nullable
|
||||
system: null == system
|
||||
? _value.system
|
||||
: system // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
internet: null == internet
|
||||
? _value.internet
|
||||
: internet // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
lastUpdateTime: null == lastUpdateTime
|
||||
? _value.lastUpdateTime
|
||||
@@ -407,7 +426,8 @@ class _$PackageImpl implements _Package {
|
||||
const _$PackageImpl(
|
||||
{required this.packageName,
|
||||
required this.label,
|
||||
required this.isSystem,
|
||||
required this.system,
|
||||
required this.internet,
|
||||
required this.lastUpdateTime});
|
||||
|
||||
factory _$PackageImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -418,13 +438,15 @@ class _$PackageImpl implements _Package {
|
||||
@override
|
||||
final String label;
|
||||
@override
|
||||
final bool isSystem;
|
||||
final bool system;
|
||||
@override
|
||||
final bool internet;
|
||||
@override
|
||||
final int lastUpdateTime;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Package(packageName: $packageName, label: $label, isSystem: $isSystem, lastUpdateTime: $lastUpdateTime)';
|
||||
return 'Package(packageName: $packageName, label: $label, system: $system, internet: $internet, lastUpdateTime: $lastUpdateTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -435,16 +457,17 @@ class _$PackageImpl implements _Package {
|
||||
(identical(other.packageName, packageName) ||
|
||||
other.packageName == packageName) &&
|
||||
(identical(other.label, label) || other.label == label) &&
|
||||
(identical(other.isSystem, isSystem) ||
|
||||
other.isSystem == isSystem) &&
|
||||
(identical(other.system, system) || other.system == system) &&
|
||||
(identical(other.internet, internet) ||
|
||||
other.internet == internet) &&
|
||||
(identical(other.lastUpdateTime, lastUpdateTime) ||
|
||||
other.lastUpdateTime == lastUpdateTime));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, packageName, label, isSystem, lastUpdateTime);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, packageName, label, system, internet, lastUpdateTime);
|
||||
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -466,7 +489,8 @@ abstract class _Package implements Package {
|
||||
const factory _Package(
|
||||
{required final String packageName,
|
||||
required final String label,
|
||||
required final bool isSystem,
|
||||
required final bool system,
|
||||
required final bool internet,
|
||||
required final int lastUpdateTime}) = _$PackageImpl;
|
||||
|
||||
factory _Package.fromJson(Map<String, dynamic> json) = _$PackageImpl.fromJson;
|
||||
@@ -476,7 +500,9 @@ abstract class _Package implements Package {
|
||||
@override
|
||||
String get label;
|
||||
@override
|
||||
bool get isSystem;
|
||||
bool get system;
|
||||
@override
|
||||
bool get internet;
|
||||
@override
|
||||
int get lastUpdateTime;
|
||||
|
||||
@@ -1092,11 +1118,209 @@ abstract class _Connection implements Connection {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Log _$LogFromJson(Map<String, dynamic> json) {
|
||||
return _Log.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Log {
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel get logLevel => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "Payload")
|
||||
String get payload => throw _privateConstructorUsedError;
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
String get dateTime => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Log to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LogCopyWith<Log> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $LogCopyWith<$Res> {
|
||||
factory $LogCopyWith(Log value, $Res Function(Log) then) =
|
||||
_$LogCopyWithImpl<$Res, Log>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "LogLevel") LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) String dateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LogCopyWithImpl<$Res, $Val extends Log> implements $LogCopyWith<$Res> {
|
||||
_$LogCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? logLevel = null,
|
||||
Object? payload = null,
|
||||
Object? dateTime = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
logLevel: null == logLevel
|
||||
? _value.logLevel
|
||||
: logLevel // ignore: cast_nullable_to_non_nullable
|
||||
as LogLevel,
|
||||
payload: null == payload
|
||||
? _value.payload
|
||||
: payload // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
dateTime: null == dateTime
|
||||
? _value.dateTime
|
||||
: dateTime // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LogImplCopyWith<$Res> implements $LogCopyWith<$Res> {
|
||||
factory _$$LogImplCopyWith(_$LogImpl value, $Res Function(_$LogImpl) then) =
|
||||
__$$LogImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "LogLevel") LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) String dateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LogImplCopyWithImpl<$Res> extends _$LogCopyWithImpl<$Res, _$LogImpl>
|
||||
implements _$$LogImplCopyWith<$Res> {
|
||||
__$$LogImplCopyWithImpl(_$LogImpl _value, $Res Function(_$LogImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? logLevel = null,
|
||||
Object? payload = null,
|
||||
Object? dateTime = null,
|
||||
}) {
|
||||
return _then(_$LogImpl(
|
||||
logLevel: null == logLevel
|
||||
? _value.logLevel
|
||||
: logLevel // ignore: cast_nullable_to_non_nullable
|
||||
as LogLevel,
|
||||
payload: null == payload
|
||||
? _value.payload
|
||||
: payload // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
dateTime: null == dateTime
|
||||
? _value.dateTime
|
||||
: dateTime // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$LogImpl implements _Log {
|
||||
const _$LogImpl(
|
||||
{@JsonKey(name: "LogLevel") this.logLevel = LogLevel.app,
|
||||
@JsonKey(name: "Payload") this.payload = "",
|
||||
@JsonKey(fromJson: _logDateTime) required this.dateTime});
|
||||
|
||||
factory _$LogImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$LogImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "LogLevel")
|
||||
final LogLevel logLevel;
|
||||
@override
|
||||
@JsonKey(name: "Payload")
|
||||
final String payload;
|
||||
@override
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
final String dateTime;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Log(logLevel: $logLevel, payload: $payload, dateTime: $dateTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$LogImpl &&
|
||||
(identical(other.logLevel, logLevel) ||
|
||||
other.logLevel == logLevel) &&
|
||||
(identical(other.payload, payload) || other.payload == payload) &&
|
||||
(identical(other.dateTime, dateTime) ||
|
||||
other.dateTime == dateTime));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, logLevel, payload, dateTime);
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
|
||||
__$$LogImplCopyWithImpl<_$LogImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$LogImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Log implements Log {
|
||||
const factory _Log(
|
||||
{@JsonKey(name: "LogLevel") final LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") final String payload,
|
||||
@JsonKey(fromJson: _logDateTime) required final String dateTime}) =
|
||||
_$LogImpl;
|
||||
|
||||
factory _Log.fromJson(Map<String, dynamic> json) = _$LogImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel get logLevel;
|
||||
@override
|
||||
@JsonKey(name: "Payload")
|
||||
String get payload;
|
||||
@override
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
String get dateTime;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LogsState {
|
||||
List<Log> get logs => throw _privateConstructorUsedError;
|
||||
List<String> get keywords => throw _privateConstructorUsedError;
|
||||
String get query => throw _privateConstructorUsedError;
|
||||
bool get loading => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of LogsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1110,7 +1334,8 @@ abstract class $LogsStateCopyWith<$Res> {
|
||||
factory $LogsStateCopyWith(LogsState value, $Res Function(LogsState) then) =
|
||||
_$LogsStateCopyWithImpl<$Res, LogsState>;
|
||||
@useResult
|
||||
$Res call({List<Log> logs, List<String> keywords, String query});
|
||||
$Res call(
|
||||
{List<Log> logs, List<String> keywords, String query, bool loading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1131,6 +1356,7 @@ class _$LogsStateCopyWithImpl<$Res, $Val extends LogsState>
|
||||
Object? logs = null,
|
||||
Object? keywords = null,
|
||||
Object? query = null,
|
||||
Object? loading = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
logs: null == logs
|
||||
@@ -1145,6 +1371,10 @@ class _$LogsStateCopyWithImpl<$Res, $Val extends LogsState>
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1157,7 +1387,8 @@ abstract class _$$LogsStateImplCopyWith<$Res>
|
||||
__$$LogsStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<Log> logs, List<String> keywords, String query});
|
||||
$Res call(
|
||||
{List<Log> logs, List<String> keywords, String query, bool loading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1176,6 +1407,7 @@ class __$$LogsStateImplCopyWithImpl<$Res>
|
||||
Object? logs = null,
|
||||
Object? keywords = null,
|
||||
Object? query = null,
|
||||
Object? loading = null,
|
||||
}) {
|
||||
return _then(_$LogsStateImpl(
|
||||
logs: null == logs
|
||||
@@ -1190,6 +1422,10 @@ class __$$LogsStateImplCopyWithImpl<$Res>
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1200,7 +1436,8 @@ class _$LogsStateImpl implements _LogsState {
|
||||
const _$LogsStateImpl(
|
||||
{final List<Log> logs = const [],
|
||||
final List<String> keywords = const [],
|
||||
this.query = ""})
|
||||
this.query = "",
|
||||
this.loading = false})
|
||||
: _logs = logs,
|
||||
_keywords = keywords;
|
||||
|
||||
@@ -1225,10 +1462,13 @@ class _$LogsStateImpl implements _LogsState {
|
||||
@override
|
||||
@JsonKey()
|
||||
final String query;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool loading;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LogsState(logs: $logs, keywords: $keywords, query: $query)';
|
||||
return 'LogsState(logs: $logs, keywords: $keywords, query: $query, loading: $loading)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1238,7 +1478,8 @@ class _$LogsStateImpl implements _LogsState {
|
||||
other is _$LogsStateImpl &&
|
||||
const DeepCollectionEquality().equals(other._logs, _logs) &&
|
||||
const DeepCollectionEquality().equals(other._keywords, _keywords) &&
|
||||
(identical(other.query, query) || other.query == query));
|
||||
(identical(other.query, query) || other.query == query) &&
|
||||
(identical(other.loading, loading) || other.loading == loading));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1246,7 +1487,8 @@ class _$LogsStateImpl implements _LogsState {
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_logs),
|
||||
const DeepCollectionEquality().hash(_keywords),
|
||||
query);
|
||||
query,
|
||||
loading);
|
||||
|
||||
/// Create a copy of LogsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1261,7 +1503,8 @@ abstract class _LogsState implements LogsState {
|
||||
const factory _LogsState(
|
||||
{final List<Log> logs,
|
||||
final List<String> keywords,
|
||||
final String query}) = _$LogsStateImpl;
|
||||
final String query,
|
||||
final bool loading}) = _$LogsStateImpl;
|
||||
|
||||
@override
|
||||
List<Log> get logs;
|
||||
@@ -1269,6 +1512,8 @@ abstract class _LogsState implements LogsState {
|
||||
List<String> get keywords;
|
||||
@override
|
||||
String get query;
|
||||
@override
|
||||
bool get loading;
|
||||
|
||||
/// Create a copy of LogsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1283,6 +1528,7 @@ mixin _$ConnectionsState {
|
||||
List<Connection> get connections => throw _privateConstructorUsedError;
|
||||
List<String> get keywords => throw _privateConstructorUsedError;
|
||||
String get query => throw _privateConstructorUsedError;
|
||||
bool get loading => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ConnectionsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1298,7 +1544,10 @@ abstract class $ConnectionsStateCopyWith<$Res> {
|
||||
_$ConnectionsStateCopyWithImpl<$Res, ConnectionsState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<Connection> connections, List<String> keywords, String query});
|
||||
{List<Connection> connections,
|
||||
List<String> keywords,
|
||||
String query,
|
||||
bool loading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1319,6 +1568,7 @@ class _$ConnectionsStateCopyWithImpl<$Res, $Val extends ConnectionsState>
|
||||
Object? connections = null,
|
||||
Object? keywords = null,
|
||||
Object? query = null,
|
||||
Object? loading = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
connections: null == connections
|
||||
@@ -1333,6 +1583,10 @@ class _$ConnectionsStateCopyWithImpl<$Res, $Val extends ConnectionsState>
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1346,7 +1600,10 @@ abstract class _$$ConnectionsStateImplCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<Connection> connections, List<String> keywords, String query});
|
||||
{List<Connection> connections,
|
||||
List<String> keywords,
|
||||
String query,
|
||||
bool loading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1365,6 +1622,7 @@ class __$$ConnectionsStateImplCopyWithImpl<$Res>
|
||||
Object? connections = null,
|
||||
Object? keywords = null,
|
||||
Object? query = null,
|
||||
Object? loading = null,
|
||||
}) {
|
||||
return _then(_$ConnectionsStateImpl(
|
||||
connections: null == connections
|
||||
@@ -1379,6 +1637,10 @@ class __$$ConnectionsStateImplCopyWithImpl<$Res>
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1389,7 +1651,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
|
||||
const _$ConnectionsStateImpl(
|
||||
{final List<Connection> connections = const [],
|
||||
final List<String> keywords = const [],
|
||||
this.query = ""})
|
||||
this.query = "",
|
||||
this.loading = false})
|
||||
: _connections = connections,
|
||||
_keywords = keywords;
|
||||
|
||||
@@ -1414,10 +1677,13 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
|
||||
@override
|
||||
@JsonKey()
|
||||
final String query;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool loading;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ConnectionsState(connections: $connections, keywords: $keywords, query: $query)';
|
||||
return 'ConnectionsState(connections: $connections, keywords: $keywords, query: $query, loading: $loading)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1428,7 +1694,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._connections, _connections) &&
|
||||
const DeepCollectionEquality().equals(other._keywords, _keywords) &&
|
||||
(identical(other.query, query) || other.query == query));
|
||||
(identical(other.query, query) || other.query == query) &&
|
||||
(identical(other.loading, loading) || other.loading == loading));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1436,7 +1703,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_connections),
|
||||
const DeepCollectionEquality().hash(_keywords),
|
||||
query);
|
||||
query,
|
||||
loading);
|
||||
|
||||
/// Create a copy of ConnectionsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1452,7 +1720,8 @@ abstract class _ConnectionsState implements ConnectionsState {
|
||||
const factory _ConnectionsState(
|
||||
{final List<Connection> connections,
|
||||
final List<String> keywords,
|
||||
final String query}) = _$ConnectionsStateImpl;
|
||||
final String query,
|
||||
final bool loading}) = _$ConnectionsStateImpl;
|
||||
|
||||
@override
|
||||
List<Connection> get connections;
|
||||
@@ -1460,6 +1729,8 @@ abstract class _ConnectionsState implements ConnectionsState {
|
||||
List<String> get keywords;
|
||||
@override
|
||||
String get query;
|
||||
@override
|
||||
bool get loading;
|
||||
|
||||
/// Create a copy of ConnectionsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -2955,3 +3226,243 @@ abstract class _Field implements Field {
|
||||
_$$FieldImplCopyWith<_$FieldImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
TextPainterParams _$TextPainterParamsFromJson(Map<String, dynamic> json) {
|
||||
return _TextPainterParams.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TextPainterParams {
|
||||
String? get text => throw _privateConstructorUsedError;
|
||||
double? get fontSize => throw _privateConstructorUsedError;
|
||||
double get textScaleFactor => throw _privateConstructorUsedError;
|
||||
double get maxWidth => throw _privateConstructorUsedError;
|
||||
int? get maxLines => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this TextPainterParams to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of TextPainterParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TextPainterParamsCopyWith<TextPainterParams> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TextPainterParamsCopyWith<$Res> {
|
||||
factory $TextPainterParamsCopyWith(
|
||||
TextPainterParams value, $Res Function(TextPainterParams) then) =
|
||||
_$TextPainterParamsCopyWithImpl<$Res, TextPainterParams>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? text,
|
||||
double? fontSize,
|
||||
double textScaleFactor,
|
||||
double maxWidth,
|
||||
int? maxLines});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TextPainterParamsCopyWithImpl<$Res, $Val extends TextPainterParams>
|
||||
implements $TextPainterParamsCopyWith<$Res> {
|
||||
_$TextPainterParamsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of TextPainterParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? text = freezed,
|
||||
Object? fontSize = freezed,
|
||||
Object? textScaleFactor = null,
|
||||
Object? maxWidth = null,
|
||||
Object? maxLines = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
text: freezed == text
|
||||
? _value.text
|
||||
: text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
fontSize: freezed == fontSize
|
||||
? _value.fontSize
|
||||
: fontSize // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
textScaleFactor: null == textScaleFactor
|
||||
? _value.textScaleFactor
|
||||
: textScaleFactor // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
maxWidth: null == maxWidth
|
||||
? _value.maxWidth
|
||||
: maxWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
maxLines: freezed == maxLines
|
||||
? _value.maxLines
|
||||
: maxLines // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TextPainterParamsImplCopyWith<$Res>
|
||||
implements $TextPainterParamsCopyWith<$Res> {
|
||||
factory _$$TextPainterParamsImplCopyWith(_$TextPainterParamsImpl value,
|
||||
$Res Function(_$TextPainterParamsImpl) then) =
|
||||
__$$TextPainterParamsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? text,
|
||||
double? fontSize,
|
||||
double textScaleFactor,
|
||||
double maxWidth,
|
||||
int? maxLines});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TextPainterParamsImplCopyWithImpl<$Res>
|
||||
extends _$TextPainterParamsCopyWithImpl<$Res, _$TextPainterParamsImpl>
|
||||
implements _$$TextPainterParamsImplCopyWith<$Res> {
|
||||
__$$TextPainterParamsImplCopyWithImpl(_$TextPainterParamsImpl _value,
|
||||
$Res Function(_$TextPainterParamsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of TextPainterParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? text = freezed,
|
||||
Object? fontSize = freezed,
|
||||
Object? textScaleFactor = null,
|
||||
Object? maxWidth = null,
|
||||
Object? maxLines = freezed,
|
||||
}) {
|
||||
return _then(_$TextPainterParamsImpl(
|
||||
text: freezed == text
|
||||
? _value.text
|
||||
: text // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
fontSize: freezed == fontSize
|
||||
? _value.fontSize
|
||||
: fontSize // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
textScaleFactor: null == textScaleFactor
|
||||
? _value.textScaleFactor
|
||||
: textScaleFactor // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
maxWidth: null == maxWidth
|
||||
? _value.maxWidth
|
||||
: maxWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
maxLines: freezed == maxLines
|
||||
? _value.maxLines
|
||||
: maxLines // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$TextPainterParamsImpl implements _TextPainterParams {
|
||||
const _$TextPainterParamsImpl(
|
||||
{required this.text,
|
||||
required this.fontSize,
|
||||
required this.textScaleFactor,
|
||||
this.maxWidth = double.infinity,
|
||||
this.maxLines});
|
||||
|
||||
factory _$TextPainterParamsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TextPainterParamsImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String? text;
|
||||
@override
|
||||
final double? fontSize;
|
||||
@override
|
||||
final double textScaleFactor;
|
||||
@override
|
||||
@JsonKey()
|
||||
final double maxWidth;
|
||||
@override
|
||||
final int? maxLines;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TextPainterParams(text: $text, fontSize: $fontSize, textScaleFactor: $textScaleFactor, maxWidth: $maxWidth, maxLines: $maxLines)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TextPainterParamsImpl &&
|
||||
(identical(other.text, text) || other.text == text) &&
|
||||
(identical(other.fontSize, fontSize) ||
|
||||
other.fontSize == fontSize) &&
|
||||
(identical(other.textScaleFactor, textScaleFactor) ||
|
||||
other.textScaleFactor == textScaleFactor) &&
|
||||
(identical(other.maxWidth, maxWidth) ||
|
||||
other.maxWidth == maxWidth) &&
|
||||
(identical(other.maxLines, maxLines) ||
|
||||
other.maxLines == maxLines));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, text, fontSize, textScaleFactor, maxWidth, maxLines);
|
||||
|
||||
/// Create a copy of TextPainterParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TextPainterParamsImplCopyWith<_$TextPainterParamsImpl> get copyWith =>
|
||||
__$$TextPainterParamsImplCopyWithImpl<_$TextPainterParamsImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TextPainterParamsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TextPainterParams implements TextPainterParams {
|
||||
const factory _TextPainterParams(
|
||||
{required final String? text,
|
||||
required final double? fontSize,
|
||||
required final double textScaleFactor,
|
||||
final double maxWidth,
|
||||
final int? maxLines}) = _$TextPainterParamsImpl;
|
||||
|
||||
factory _TextPainterParams.fromJson(Map<String, dynamic> json) =
|
||||
_$TextPainterParamsImpl.fromJson;
|
||||
|
||||
@override
|
||||
String? get text;
|
||||
@override
|
||||
double? get fontSize;
|
||||
@override
|
||||
double get textScaleFactor;
|
||||
@override
|
||||
double get maxWidth;
|
||||
@override
|
||||
int? get maxLines;
|
||||
|
||||
/// Create a copy of TextPainterParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TextPainterParamsImplCopyWith<_$TextPainterParamsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -6,30 +6,12 @@ part of '../common.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Log _$LogFromJson(Map<String, dynamic> json) => Log(
|
||||
logLevel: $enumDecode(_$LogLevelEnumMap, json['LogLevel']),
|
||||
payload: json['Payload'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LogToJson(Log instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
LogLevel.info: 'info',
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PackageImpl(
|
||||
packageName: json['packageName'] as String,
|
||||
label: json['label'] as String,
|
||||
isSystem: json['isSystem'] as bool,
|
||||
system: json['system'] as bool,
|
||||
internet: json['internet'] as bool,
|
||||
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
|
||||
);
|
||||
|
||||
@@ -37,7 +19,8 @@ Map<String, dynamic> _$$PackageImplToJson(_$PackageImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'packageName': instance.packageName,
|
||||
'label': instance.label,
|
||||
'isSystem': instance.isSystem,
|
||||
'system': instance.system,
|
||||
'internet': instance.internet,
|
||||
'lastUpdateTime': instance.lastUpdateTime,
|
||||
};
|
||||
|
||||
@@ -88,6 +71,28 @@ Map<String, dynamic> _$$ConnectionImplToJson(_$ConnectionImpl instance) =>
|
||||
'chains': instance.chains,
|
||||
};
|
||||
|
||||
_$LogImpl _$$LogImplFromJson(Map<String, dynamic> json) => _$LogImpl(
|
||||
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??
|
||||
LogLevel.app,
|
||||
payload: json['Payload'] as String? ?? "",
|
||||
dateTime: _logDateTime(json['dateTime']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$LogImplToJson(_$LogImpl instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
'dateTime': instance.dateTime,
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
LogLevel.info: 'info',
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
_$DAVImpl _$$DAVImplFromJson(Map<String, dynamic> json) => _$DAVImpl(
|
||||
uri: json['uri'] as String,
|
||||
user: json['user'] as String,
|
||||
@@ -193,3 +198,23 @@ const _$KeyboardModifierEnumMap = {
|
||||
KeyboardModifier.meta: 'meta',
|
||||
KeyboardModifier.shift: 'shift',
|
||||
};
|
||||
|
||||
_$TextPainterParamsImpl _$$TextPainterParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$TextPainterParamsImpl(
|
||||
text: json['text'] as String?,
|
||||
fontSize: (json['fontSize'] as num?)?.toDouble(),
|
||||
textScaleFactor: (json['textScaleFactor'] as num).toDouble(),
|
||||
maxWidth: (json['maxWidth'] as num?)?.toDouble() ?? double.infinity,
|
||||
maxLines: (json['maxLines'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TextPainterParamsImplToJson(
|
||||
_$TextPainterParamsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'text': instance.text,
|
||||
'fontSize': instance.fontSize,
|
||||
'textScaleFactor': instance.textScaleFactor,
|
||||
'maxWidth': instance.maxWidth,
|
||||
'maxLines': instance.maxLines,
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ mixin _$AppSettingProps {
|
||||
bool get minimizeOnExit => throw _privateConstructorUsedError;
|
||||
bool get hidden => throw _privateConstructorUsedError;
|
||||
bool get developerMode => throw _privateConstructorUsedError;
|
||||
RecoveryStrategy get recoveryStrategy => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AppSettingProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -72,7 +73,8 @@ abstract class $AppSettingPropsCopyWith<$Res> {
|
||||
bool disclaimerAccepted,
|
||||
bool minimizeOnExit,
|
||||
bool hidden,
|
||||
bool developerMode});
|
||||
bool developerMode,
|
||||
RecoveryStrategy recoveryStrategy});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -106,6 +108,7 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
|
||||
Object? minimizeOnExit = null,
|
||||
Object? hidden = null,
|
||||
Object? developerMode = null,
|
||||
Object? recoveryStrategy = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
locale: freezed == locale
|
||||
@@ -172,6 +175,10 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
|
||||
? _value.developerMode
|
||||
: developerMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
recoveryStrategy: null == recoveryStrategy
|
||||
? _value.recoveryStrategy
|
||||
: recoveryStrategy // ignore: cast_nullable_to_non_nullable
|
||||
as RecoveryStrategy,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -201,7 +208,8 @@ abstract class _$$AppSettingPropsImplCopyWith<$Res>
|
||||
bool disclaimerAccepted,
|
||||
bool minimizeOnExit,
|
||||
bool hidden,
|
||||
bool developerMode});
|
||||
bool developerMode,
|
||||
RecoveryStrategy recoveryStrategy});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -233,6 +241,7 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
|
||||
Object? minimizeOnExit = null,
|
||||
Object? hidden = null,
|
||||
Object? developerMode = null,
|
||||
Object? recoveryStrategy = null,
|
||||
}) {
|
||||
return _then(_$AppSettingPropsImpl(
|
||||
locale: freezed == locale
|
||||
@@ -299,6 +308,10 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
|
||||
? _value.developerMode
|
||||
: developerMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
recoveryStrategy: null == recoveryStrategy
|
||||
? _value.recoveryStrategy
|
||||
: recoveryStrategy // ignore: cast_nullable_to_non_nullable
|
||||
as RecoveryStrategy,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -314,7 +327,7 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
this.autoLaunch = false,
|
||||
this.silentLaunch = false,
|
||||
this.autoRun = false,
|
||||
this.openLogs = true,
|
||||
this.openLogs = false,
|
||||
this.closeConnections = true,
|
||||
this.testUrl = defaultTestUrl,
|
||||
this.isAnimateToPage = true,
|
||||
@@ -323,7 +336,8 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
this.disclaimerAccepted = false,
|
||||
this.minimizeOnExit = true,
|
||||
this.hidden = false,
|
||||
this.developerMode = false})
|
||||
this.developerMode = false,
|
||||
this.recoveryStrategy = RecoveryStrategy.compatible})
|
||||
: _dashboardWidgets = dashboardWidgets;
|
||||
|
||||
factory _$AppSettingPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -383,10 +397,13 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool developerMode;
|
||||
@override
|
||||
@JsonKey()
|
||||
final RecoveryStrategy recoveryStrategy;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode)';
|
||||
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, recoveryStrategy: $recoveryStrategy)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -421,7 +438,9 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
other.minimizeOnExit == minimizeOnExit) &&
|
||||
(identical(other.hidden, hidden) || other.hidden == hidden) &&
|
||||
(identical(other.developerMode, developerMode) ||
|
||||
other.developerMode == developerMode));
|
||||
other.developerMode == developerMode) &&
|
||||
(identical(other.recoveryStrategy, recoveryStrategy) ||
|
||||
other.recoveryStrategy == recoveryStrategy));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -443,7 +462,8 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
disclaimerAccepted,
|
||||
minimizeOnExit,
|
||||
hidden,
|
||||
developerMode);
|
||||
developerMode,
|
||||
recoveryStrategy);
|
||||
|
||||
/// Create a copy of AppSettingProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -480,7 +500,8 @@ abstract class _AppSettingProps implements AppSettingProps {
|
||||
final bool disclaimerAccepted,
|
||||
final bool minimizeOnExit,
|
||||
final bool hidden,
|
||||
final bool developerMode}) = _$AppSettingPropsImpl;
|
||||
final bool developerMode,
|
||||
final RecoveryStrategy recoveryStrategy}) = _$AppSettingPropsImpl;
|
||||
|
||||
factory _AppSettingProps.fromJson(Map<String, dynamic> json) =
|
||||
_$AppSettingPropsImpl.fromJson;
|
||||
@@ -518,6 +539,8 @@ abstract class _AppSettingProps implements AppSettingProps {
|
||||
bool get hidden;
|
||||
@override
|
||||
bool get developerMode;
|
||||
@override
|
||||
RecoveryStrategy get recoveryStrategy;
|
||||
|
||||
/// Create a copy of AppSettingProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -539,6 +562,7 @@ mixin _$AccessControl {
|
||||
List<String> get rejectList => throw _privateConstructorUsedError;
|
||||
AccessSortType get sort => throw _privateConstructorUsedError;
|
||||
bool get isFilterSystemApp => throw _privateConstructorUsedError;
|
||||
bool get isFilterNonInternetApp => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AccessControl to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -562,7 +586,8 @@ abstract class $AccessControlCopyWith<$Res> {
|
||||
List<String> acceptList,
|
||||
List<String> rejectList,
|
||||
AccessSortType sort,
|
||||
bool isFilterSystemApp});
|
||||
bool isFilterSystemApp,
|
||||
bool isFilterNonInternetApp});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -586,6 +611,7 @@ class _$AccessControlCopyWithImpl<$Res, $Val extends AccessControl>
|
||||
Object? rejectList = null,
|
||||
Object? sort = null,
|
||||
Object? isFilterSystemApp = null,
|
||||
Object? isFilterNonInternetApp = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
enable: null == enable
|
||||
@@ -612,6 +638,10 @@ class _$AccessControlCopyWithImpl<$Res, $Val extends AccessControl>
|
||||
? _value.isFilterSystemApp
|
||||
: isFilterSystemApp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isFilterNonInternetApp: null == isFilterNonInternetApp
|
||||
? _value.isFilterNonInternetApp
|
||||
: isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -630,7 +660,8 @@ abstract class _$$AccessControlImplCopyWith<$Res>
|
||||
List<String> acceptList,
|
||||
List<String> rejectList,
|
||||
AccessSortType sort,
|
||||
bool isFilterSystemApp});
|
||||
bool isFilterSystemApp,
|
||||
bool isFilterNonInternetApp});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -652,6 +683,7 @@ class __$$AccessControlImplCopyWithImpl<$Res>
|
||||
Object? rejectList = null,
|
||||
Object? sort = null,
|
||||
Object? isFilterSystemApp = null,
|
||||
Object? isFilterNonInternetApp = null,
|
||||
}) {
|
||||
return _then(_$AccessControlImpl(
|
||||
enable: null == enable
|
||||
@@ -678,6 +710,10 @@ class __$$AccessControlImplCopyWithImpl<$Res>
|
||||
? _value.isFilterSystemApp
|
||||
: isFilterSystemApp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isFilterNonInternetApp: null == isFilterNonInternetApp
|
||||
? _value.isFilterNonInternetApp
|
||||
: isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -691,7 +727,8 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
final List<String> acceptList = const [],
|
||||
final List<String> rejectList = const [],
|
||||
this.sort = AccessSortType.none,
|
||||
this.isFilterSystemApp = true})
|
||||
this.isFilterSystemApp = true,
|
||||
this.isFilterNonInternetApp = true})
|
||||
: _acceptList = acceptList,
|
||||
_rejectList = rejectList;
|
||||
|
||||
@@ -728,10 +765,13 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFilterSystemApp;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFilterNonInternetApp;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp)';
|
||||
return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp, isFilterNonInternetApp: $isFilterNonInternetApp)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -747,7 +787,9 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
.equals(other._rejectList, _rejectList) &&
|
||||
(identical(other.sort, sort) || other.sort == sort) &&
|
||||
(identical(other.isFilterSystemApp, isFilterSystemApp) ||
|
||||
other.isFilterSystemApp == isFilterSystemApp));
|
||||
other.isFilterSystemApp == isFilterSystemApp) &&
|
||||
(identical(other.isFilterNonInternetApp, isFilterNonInternetApp) ||
|
||||
other.isFilterNonInternetApp == isFilterNonInternetApp));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -759,7 +801,8 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
const DeepCollectionEquality().hash(_acceptList),
|
||||
const DeepCollectionEquality().hash(_rejectList),
|
||||
sort,
|
||||
isFilterSystemApp);
|
||||
isFilterSystemApp,
|
||||
isFilterNonInternetApp);
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -784,7 +827,8 @@ abstract class _AccessControl implements AccessControl {
|
||||
final List<String> acceptList,
|
||||
final List<String> rejectList,
|
||||
final AccessSortType sort,
|
||||
final bool isFilterSystemApp}) = _$AccessControlImpl;
|
||||
final bool isFilterSystemApp,
|
||||
final bool isFilterNonInternetApp}) = _$AccessControlImpl;
|
||||
|
||||
factory _AccessControl.fromJson(Map<String, dynamic> json) =
|
||||
_$AccessControlImpl.fromJson;
|
||||
@@ -801,6 +845,8 @@ abstract class _AccessControl implements AccessControl {
|
||||
AccessSortType get sort;
|
||||
@override
|
||||
bool get isFilterSystemApp;
|
||||
@override
|
||||
bool get isFilterNonInternetApp;
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1740,6 +1786,170 @@ abstract class _ProxiesStyle implements ProxiesStyle {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
TextScale _$TextScaleFromJson(Map<String, dynamic> json) {
|
||||
return _TextScale.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TextScale {
|
||||
dynamic get enable => throw _privateConstructorUsedError;
|
||||
dynamic get scale => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this TextScale to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TextScaleCopyWith<TextScale> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TextScaleCopyWith<$Res> {
|
||||
factory $TextScaleCopyWith(TextScale value, $Res Function(TextScale) then) =
|
||||
_$TextScaleCopyWithImpl<$Res, TextScale>;
|
||||
@useResult
|
||||
$Res call({dynamic enable, dynamic scale});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TextScaleCopyWithImpl<$Res, $Val extends TextScale>
|
||||
implements $TextScaleCopyWith<$Res> {
|
||||
_$TextScaleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = freezed,
|
||||
Object? scale = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
enable: freezed == enable
|
||||
? _value.enable
|
||||
: enable // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
scale: freezed == scale
|
||||
? _value.scale
|
||||
: scale // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TextScaleImplCopyWith<$Res>
|
||||
implements $TextScaleCopyWith<$Res> {
|
||||
factory _$$TextScaleImplCopyWith(
|
||||
_$TextScaleImpl value, $Res Function(_$TextScaleImpl) then) =
|
||||
__$$TextScaleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({dynamic enable, dynamic scale});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TextScaleImplCopyWithImpl<$Res>
|
||||
extends _$TextScaleCopyWithImpl<$Res, _$TextScaleImpl>
|
||||
implements _$$TextScaleImplCopyWith<$Res> {
|
||||
__$$TextScaleImplCopyWithImpl(
|
||||
_$TextScaleImpl _value, $Res Function(_$TextScaleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = freezed,
|
||||
Object? scale = freezed,
|
||||
}) {
|
||||
return _then(_$TextScaleImpl(
|
||||
enable: freezed == enable ? _value.enable! : enable,
|
||||
scale: freezed == scale ? _value.scale! : scale,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$TextScaleImpl implements _TextScale {
|
||||
const _$TextScaleImpl({this.enable = false, this.scale = 1.0});
|
||||
|
||||
factory _$TextScaleImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TextScaleImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final dynamic enable;
|
||||
@override
|
||||
@JsonKey()
|
||||
final dynamic scale;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TextScale(enable: $enable, scale: $scale)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TextScaleImpl &&
|
||||
const DeepCollectionEquality().equals(other.enable, enable) &&
|
||||
const DeepCollectionEquality().equals(other.scale, scale));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(enable),
|
||||
const DeepCollectionEquality().hash(scale));
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
|
||||
__$$TextScaleImplCopyWithImpl<_$TextScaleImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TextScaleImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TextScale implements TextScale {
|
||||
const factory _TextScale({final dynamic enable, final dynamic scale}) =
|
||||
_$TextScaleImpl;
|
||||
|
||||
factory _TextScale.fromJson(Map<String, dynamic> json) =
|
||||
_$TextScaleImpl.fromJson;
|
||||
|
||||
@override
|
||||
dynamic get enable;
|
||||
@override
|
||||
dynamic get scale;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) {
|
||||
return _ThemeProps.fromJson(json);
|
||||
}
|
||||
@@ -1751,6 +1961,7 @@ mixin _$ThemeProps {
|
||||
ThemeMode get themeMode => throw _privateConstructorUsedError;
|
||||
DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError;
|
||||
bool get pureBlack => throw _privateConstructorUsedError;
|
||||
TextScale get textScale => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ThemeProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -1773,7 +1984,10 @@ abstract class $ThemePropsCopyWith<$Res> {
|
||||
List<int> primaryColors,
|
||||
ThemeMode themeMode,
|
||||
DynamicSchemeVariant schemeVariant,
|
||||
bool pureBlack});
|
||||
bool pureBlack,
|
||||
TextScale textScale});
|
||||
|
||||
$TextScaleCopyWith<$Res> get textScale;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1796,6 +2010,7 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
|
||||
Object? themeMode = null,
|
||||
Object? schemeVariant = null,
|
||||
Object? pureBlack = null,
|
||||
Object? textScale = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
primaryColor: freezed == primaryColor
|
||||
@@ -1818,8 +2033,22 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
|
||||
? _value.pureBlack
|
||||
: pureBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
textScale: null == textScale
|
||||
? _value.textScale
|
||||
: textScale // ignore: cast_nullable_to_non_nullable
|
||||
as TextScale,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$TextScaleCopyWith<$Res> get textScale {
|
||||
return $TextScaleCopyWith<$Res>(_value.textScale, (value) {
|
||||
return _then(_value.copyWith(textScale: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1835,7 +2064,11 @@ abstract class _$$ThemePropsImplCopyWith<$Res>
|
||||
List<int> primaryColors,
|
||||
ThemeMode themeMode,
|
||||
DynamicSchemeVariant schemeVariant,
|
||||
bool pureBlack});
|
||||
bool pureBlack,
|
||||
TextScale textScale});
|
||||
|
||||
@override
|
||||
$TextScaleCopyWith<$Res> get textScale;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1856,6 +2089,7 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
Object? themeMode = null,
|
||||
Object? schemeVariant = null,
|
||||
Object? pureBlack = null,
|
||||
Object? textScale = null,
|
||||
}) {
|
||||
return _then(_$ThemePropsImpl(
|
||||
primaryColor: freezed == primaryColor
|
||||
@@ -1878,6 +2112,10 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
? _value.pureBlack
|
||||
: pureBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
textScale: null == textScale
|
||||
? _value.textScale
|
||||
: textScale // ignore: cast_nullable_to_non_nullable
|
||||
as TextScale,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1889,8 +2127,9 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
{this.primaryColor,
|
||||
final List<int> primaryColors = defaultPrimaryColors,
|
||||
this.themeMode = ThemeMode.dark,
|
||||
this.schemeVariant = DynamicSchemeVariant.tonalSpot,
|
||||
this.pureBlack = false})
|
||||
this.schemeVariant = DynamicSchemeVariant.content,
|
||||
this.pureBlack = false,
|
||||
this.textScale = const TextScale()})
|
||||
: _primaryColors = primaryColors;
|
||||
|
||||
factory _$ThemePropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -1916,10 +2155,13 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool pureBlack;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TextScale textScale;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack)';
|
||||
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack, textScale: $textScale)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1936,7 +2178,9 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
(identical(other.schemeVariant, schemeVariant) ||
|
||||
other.schemeVariant == schemeVariant) &&
|
||||
(identical(other.pureBlack, pureBlack) ||
|
||||
other.pureBlack == pureBlack));
|
||||
other.pureBlack == pureBlack) &&
|
||||
(identical(other.textScale, textScale) ||
|
||||
other.textScale == textScale));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -1947,7 +2191,8 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
const DeepCollectionEquality().hash(_primaryColors),
|
||||
themeMode,
|
||||
schemeVariant,
|
||||
pureBlack);
|
||||
pureBlack,
|
||||
textScale);
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1971,7 +2216,8 @@ abstract class _ThemeProps implements ThemeProps {
|
||||
final List<int> primaryColors,
|
||||
final ThemeMode themeMode,
|
||||
final DynamicSchemeVariant schemeVariant,
|
||||
final bool pureBlack}) = _$ThemePropsImpl;
|
||||
final bool pureBlack,
|
||||
final TextScale textScale}) = _$ThemePropsImpl;
|
||||
|
||||
factory _ThemeProps.fromJson(Map<String, dynamic> json) =
|
||||
_$ThemePropsImpl.fromJson;
|
||||
@@ -1986,6 +2232,8 @@ abstract class _ThemeProps implements ThemeProps {
|
||||
DynamicSchemeVariant get schemeVariant;
|
||||
@override
|
||||
bool get pureBlack;
|
||||
@override
|
||||
TextScale get textScale;
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -17,7 +17,7 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
|
||||
autoLaunch: json['autoLaunch'] as bool? ?? false,
|
||||
silentLaunch: json['silentLaunch'] as bool? ?? false,
|
||||
autoRun: json['autoRun'] as bool? ?? false,
|
||||
openLogs: json['openLogs'] as bool? ?? true,
|
||||
openLogs: json['openLogs'] as bool? ?? false,
|
||||
closeConnections: json['closeConnections'] as bool? ?? true,
|
||||
testUrl: json['testUrl'] as String? ?? defaultTestUrl,
|
||||
isAnimateToPage: json['isAnimateToPage'] as bool? ?? true,
|
||||
@@ -27,6 +27,9 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
|
||||
minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,
|
||||
hidden: json['hidden'] as bool? ?? false,
|
||||
developerMode: json['developerMode'] as bool? ?? false,
|
||||
recoveryStrategy: $enumDecodeNullable(
|
||||
_$RecoveryStrategyEnumMap, json['recoveryStrategy']) ??
|
||||
RecoveryStrategy.compatible,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
@@ -50,8 +53,14 @@ Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
'minimizeOnExit': instance.minimizeOnExit,
|
||||
'hidden': instance.hidden,
|
||||
'developerMode': instance.developerMode,
|
||||
'recoveryStrategy': _$RecoveryStrategyEnumMap[instance.recoveryStrategy]!,
|
||||
};
|
||||
|
||||
const _$RecoveryStrategyEnumMap = {
|
||||
RecoveryStrategy.compatible: 'compatible',
|
||||
RecoveryStrategy.override: 'override',
|
||||
};
|
||||
|
||||
const _$DashboardWidgetEnumMap = {
|
||||
DashboardWidget.networkSpeed: 'networkSpeed',
|
||||
DashboardWidget.outboundModeV2: 'outboundModeV2',
|
||||
@@ -59,6 +68,7 @@ const _$DashboardWidgetEnumMap = {
|
||||
DashboardWidget.trafficUsage: 'trafficUsage',
|
||||
DashboardWidget.networkDetection: 'networkDetection',
|
||||
DashboardWidget.tunButton: 'tunButton',
|
||||
DashboardWidget.vpnButton: 'vpnButton',
|
||||
DashboardWidget.systemProxyButton: 'systemProxyButton',
|
||||
DashboardWidget.intranetIp: 'intranetIp',
|
||||
DashboardWidget.memoryInfo: 'memoryInfo',
|
||||
@@ -80,6 +90,7 @@ _$AccessControlImpl _$$AccessControlImplFromJson(Map<String, dynamic> json) =>
|
||||
sort: $enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
|
||||
AccessSortType.none,
|
||||
isFilterSystemApp: json['isFilterSystemApp'] as bool? ?? true,
|
||||
isFilterNonInternetApp: json['isFilterNonInternetApp'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
|
||||
@@ -90,6 +101,7 @@ Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
|
||||
'rejectList': instance.rejectList,
|
||||
'sort': _$AccessSortTypeEnumMap[instance.sort]!,
|
||||
'isFilterSystemApp': instance.isFilterSystemApp,
|
||||
'isFilterNonInternetApp': instance.isFilterNonInternetApp,
|
||||
};
|
||||
|
||||
const _$AccessControlModeEnumMap = {
|
||||
@@ -222,6 +234,18 @@ const _$ProxyCardTypeEnumMap = {
|
||||
ProxyCardType.min: 'min',
|
||||
};
|
||||
|
||||
_$TextScaleImpl _$$TextScaleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$TextScaleImpl(
|
||||
enable: json['enable'] ?? false,
|
||||
scale: json['scale'] ?? 1.0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TextScaleImplToJson(_$TextScaleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'scale': instance.scale,
|
||||
};
|
||||
|
||||
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ThemePropsImpl(
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
@@ -233,8 +257,11 @@ _$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
ThemeMode.dark,
|
||||
schemeVariant: $enumDecodeNullable(
|
||||
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
|
||||
DynamicSchemeVariant.tonalSpot,
|
||||
DynamicSchemeVariant.content,
|
||||
pureBlack: json['pureBlack'] as bool? ?? false,
|
||||
textScale: json['textScale'] == null
|
||||
? const TextScale()
|
||||
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
|
||||
@@ -244,6 +271,7 @@ Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
|
||||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||
'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,
|
||||
'pureBlack': instance.pureBlack,
|
||||
'textScale': instance.textScale,
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
|
||||
@@ -327,6 +327,193 @@ abstract class _VM3<A, B, C> implements VM3<A, B, C> {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$VM4<A, B, C, D> {
|
||||
A get a => throw _privateConstructorUsedError;
|
||||
B get b => throw _privateConstructorUsedError;
|
||||
C get c => throw _privateConstructorUsedError;
|
||||
D get d => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of VM4
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$VM4CopyWith<A, B, C, D, VM4<A, B, C, D>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $VM4CopyWith<A, B, C, D, $Res> {
|
||||
factory $VM4CopyWith(
|
||||
VM4<A, B, C, D> value, $Res Function(VM4<A, B, C, D>) then) =
|
||||
_$VM4CopyWithImpl<A, B, C, D, $Res, VM4<A, B, C, D>>;
|
||||
@useResult
|
||||
$Res call({A a, B b, C c, D d});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$VM4CopyWithImpl<A, B, C, D, $Res, $Val extends VM4<A, B, C, D>>
|
||||
implements $VM4CopyWith<A, B, C, D, $Res> {
|
||||
_$VM4CopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of VM4
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? a = freezed,
|
||||
Object? b = freezed,
|
||||
Object? c = freezed,
|
||||
Object? d = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
a: freezed == a
|
||||
? _value.a
|
||||
: a // ignore: cast_nullable_to_non_nullable
|
||||
as A,
|
||||
b: freezed == b
|
||||
? _value.b
|
||||
: b // ignore: cast_nullable_to_non_nullable
|
||||
as B,
|
||||
c: freezed == c
|
||||
? _value.c
|
||||
: c // ignore: cast_nullable_to_non_nullable
|
||||
as C,
|
||||
d: freezed == d
|
||||
? _value.d
|
||||
: d // ignore: cast_nullable_to_non_nullable
|
||||
as D,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$VM4ImplCopyWith<A, B, C, D, $Res>
|
||||
implements $VM4CopyWith<A, B, C, D, $Res> {
|
||||
factory _$$VM4ImplCopyWith(_$VM4Impl<A, B, C, D> value,
|
||||
$Res Function(_$VM4Impl<A, B, C, D>) then) =
|
||||
__$$VM4ImplCopyWithImpl<A, B, C, D, $Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({A a, B b, C c, D d});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$VM4ImplCopyWithImpl<A, B, C, D, $Res>
|
||||
extends _$VM4CopyWithImpl<A, B, C, D, $Res, _$VM4Impl<A, B, C, D>>
|
||||
implements _$$VM4ImplCopyWith<A, B, C, D, $Res> {
|
||||
__$$VM4ImplCopyWithImpl(
|
||||
_$VM4Impl<A, B, C, D> _value, $Res Function(_$VM4Impl<A, B, C, D>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of VM4
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? a = freezed,
|
||||
Object? b = freezed,
|
||||
Object? c = freezed,
|
||||
Object? d = freezed,
|
||||
}) {
|
||||
return _then(_$VM4Impl<A, B, C, D>(
|
||||
a: freezed == a
|
||||
? _value.a
|
||||
: a // ignore: cast_nullable_to_non_nullable
|
||||
as A,
|
||||
b: freezed == b
|
||||
? _value.b
|
||||
: b // ignore: cast_nullable_to_non_nullable
|
||||
as B,
|
||||
c: freezed == c
|
||||
? _value.c
|
||||
: c // ignore: cast_nullable_to_non_nullable
|
||||
as C,
|
||||
d: freezed == d
|
||||
? _value.d
|
||||
: d // ignore: cast_nullable_to_non_nullable
|
||||
as D,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$VM4Impl<A, B, C, D> implements _VM4<A, B, C, D> {
|
||||
const _$VM4Impl(
|
||||
{required this.a, required this.b, required this.c, required this.d});
|
||||
|
||||
@override
|
||||
final A a;
|
||||
@override
|
||||
final B b;
|
||||
@override
|
||||
final C c;
|
||||
@override
|
||||
final D d;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'VM4<$A, $B, $C, $D>(a: $a, b: $b, c: $c, d: $d)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$VM4Impl<A, B, C, D> &&
|
||||
const DeepCollectionEquality().equals(other.a, a) &&
|
||||
const DeepCollectionEquality().equals(other.b, b) &&
|
||||
const DeepCollectionEquality().equals(other.c, c) &&
|
||||
const DeepCollectionEquality().equals(other.d, d));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(a),
|
||||
const DeepCollectionEquality().hash(b),
|
||||
const DeepCollectionEquality().hash(c),
|
||||
const DeepCollectionEquality().hash(d));
|
||||
|
||||
/// Create a copy of VM4
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$VM4ImplCopyWith<A, B, C, D, _$VM4Impl<A, B, C, D>> get copyWith =>
|
||||
__$$VM4ImplCopyWithImpl<A, B, C, D, _$VM4Impl<A, B, C, D>>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _VM4<A, B, C, D> implements VM4<A, B, C, D> {
|
||||
const factory _VM4(
|
||||
{required final A a,
|
||||
required final B b,
|
||||
required final C c,
|
||||
required final D d}) = _$VM4Impl<A, B, C, D>;
|
||||
|
||||
@override
|
||||
A get a;
|
||||
@override
|
||||
B get b;
|
||||
@override
|
||||
C get c;
|
||||
@override
|
||||
D get d;
|
||||
|
||||
/// Create a copy of VM4
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$VM4ImplCopyWith<A, B, C, D, _$VM4Impl<A, B, C, D>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$StartButtonSelectorState {
|
||||
bool get isInit => throw _privateConstructorUsedError;
|
||||
@@ -3335,6 +3522,7 @@ mixin _$ClashConfigState {
|
||||
bool get overrideDns => throw _privateConstructorUsedError;
|
||||
ClashConfig get clashConfig => throw _privateConstructorUsedError;
|
||||
OverrideData get overrideData => throw _privateConstructorUsedError;
|
||||
RouteMode get routeMode => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -3350,7 +3538,10 @@ abstract class $ClashConfigStateCopyWith<$Res> {
|
||||
_$ClashConfigStateCopyWithImpl<$Res, ClashConfigState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool overrideDns, ClashConfig clashConfig, OverrideData overrideData});
|
||||
{bool overrideDns,
|
||||
ClashConfig clashConfig,
|
||||
OverrideData overrideData,
|
||||
RouteMode routeMode});
|
||||
|
||||
$ClashConfigCopyWith<$Res> get clashConfig;
|
||||
$OverrideDataCopyWith<$Res> get overrideData;
|
||||
@@ -3374,6 +3565,7 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
|
||||
Object? overrideDns = null,
|
||||
Object? clashConfig = null,
|
||||
Object? overrideData = null,
|
||||
Object? routeMode = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
overrideDns: null == overrideDns
|
||||
@@ -3388,6 +3580,10 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
|
||||
? _value.overrideData
|
||||
: overrideData // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideData,
|
||||
routeMode: null == routeMode
|
||||
? _value.routeMode
|
||||
: routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -3421,7 +3617,10 @@ abstract class _$$ClashConfigStateImplCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool overrideDns, ClashConfig clashConfig, OverrideData overrideData});
|
||||
{bool overrideDns,
|
||||
ClashConfig clashConfig,
|
||||
OverrideData overrideData,
|
||||
RouteMode routeMode});
|
||||
|
||||
@override
|
||||
$ClashConfigCopyWith<$Res> get clashConfig;
|
||||
@@ -3445,6 +3644,7 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
|
||||
Object? overrideDns = null,
|
||||
Object? clashConfig = null,
|
||||
Object? overrideData = null,
|
||||
Object? routeMode = null,
|
||||
}) {
|
||||
return _then(_$ClashConfigStateImpl(
|
||||
overrideDns: null == overrideDns
|
||||
@@ -3459,6 +3659,10 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
|
||||
? _value.overrideData
|
||||
: overrideData // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideData,
|
||||
routeMode: null == routeMode
|
||||
? _value.routeMode
|
||||
: routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -3469,7 +3673,8 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
|
||||
const _$ClashConfigStateImpl(
|
||||
{required this.overrideDns,
|
||||
required this.clashConfig,
|
||||
required this.overrideData});
|
||||
required this.overrideData,
|
||||
required this.routeMode});
|
||||
|
||||
@override
|
||||
final bool overrideDns;
|
||||
@@ -3477,10 +3682,12 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
|
||||
final ClashConfig clashConfig;
|
||||
@override
|
||||
final OverrideData overrideData;
|
||||
@override
|
||||
final RouteMode routeMode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData)';
|
||||
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData, routeMode: $routeMode)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -3493,12 +3700,14 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
|
||||
(identical(other.clashConfig, clashConfig) ||
|
||||
other.clashConfig == clashConfig) &&
|
||||
(identical(other.overrideData, overrideData) ||
|
||||
other.overrideData == overrideData));
|
||||
other.overrideData == overrideData) &&
|
||||
(identical(other.routeMode, routeMode) ||
|
||||
other.routeMode == routeMode));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, overrideDns, clashConfig, overrideData);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, overrideDns, clashConfig, overrideData, routeMode);
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -3514,7 +3723,8 @@ abstract class _ClashConfigState implements ClashConfigState {
|
||||
const factory _ClashConfigState(
|
||||
{required final bool overrideDns,
|
||||
required final ClashConfig clashConfig,
|
||||
required final OverrideData overrideData}) = _$ClashConfigStateImpl;
|
||||
required final OverrideData overrideData,
|
||||
required final RouteMode routeMode}) = _$ClashConfigStateImpl;
|
||||
|
||||
@override
|
||||
bool get overrideDns;
|
||||
@@ -3522,6 +3732,8 @@ abstract class _ClashConfigState implements ClashConfigState {
|
||||
ClashConfig get clashConfig;
|
||||
@override
|
||||
OverrideData get overrideData;
|
||||
@override
|
||||
RouteMode get routeMode;
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -24,6 +24,17 @@ class VM3<A, B, C> with _$VM3<A, B, C> {
|
||||
}) = _VM3;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VM4<A, B, C,D> with _$VM4<A, B, C,D> {
|
||||
const factory VM4({
|
||||
required A a,
|
||||
required B b,
|
||||
required C c,
|
||||
required D d,
|
||||
}) = _VM4;
|
||||
}
|
||||
|
||||
|
||||
@freezed
|
||||
class StartButtonSelectorState with _$StartButtonSelectorState {
|
||||
const factory StartButtonSelectorState({
|
||||
@@ -146,12 +157,21 @@ class PackageListSelectorState with _$PackageListSelectorState {
|
||||
}
|
||||
|
||||
extension PackageListSelectorStateExt on PackageListSelectorState {
|
||||
List<Package> getList(List<String> selectedList) {
|
||||
List<Package> get list {
|
||||
final isFilterSystemApp = accessControl.isFilterSystemApp;
|
||||
final sort = accessControl.sort;
|
||||
final isFilterNonInternetApp = accessControl.isFilterNonInternetApp;
|
||||
return packages
|
||||
.where((item) => isFilterSystemApp ? item.isSystem == false : true)
|
||||
.sorted(
|
||||
.where(
|
||||
(item) =>
|
||||
(isFilterSystemApp ? item.system == false : true) &&
|
||||
(isFilterNonInternetApp ? item.internet == true : true),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<Package> getSortList(List<String> selectedList) {
|
||||
final sort = accessControl.sort;
|
||||
return list.sorted(
|
||||
(a, b) {
|
||||
return switch (sort) {
|
||||
AccessSortType.none => 0,
|
||||
@@ -208,6 +228,7 @@ class ClashConfigState with _$ClashConfigState {
|
||||
required bool overrideDns,
|
||||
required ClashConfig clashConfig,
|
||||
required OverrideData overrideData,
|
||||
required RouteMode routeMode,
|
||||
}) = _ClashConfigState;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
);
|
||||
},
|
||||
popup: CommonPopupMenu(
|
||||
minWidth: 180,
|
||||
items: [
|
||||
PopupMenuItemData(
|
||||
icon: Icons.search,
|
||||
@@ -189,7 +190,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
|
||||
controller: _controller,
|
||||
style: CodeEditorStyle(
|
||||
fontSize: 14,
|
||||
fontSize: 14.ap,
|
||||
fontFamily: FontFamily.jetBrainsMono.value,
|
||||
codeTheme: CodeHighlightTheme(
|
||||
languages: {
|
||||
|
||||
@@ -190,43 +190,46 @@ class CommonNavigationBar extends ConsumerWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: IntrinsicHeight(
|
||||
child: NavigationRail(
|
||||
backgroundColor: context.colorScheme.surfaceContainer,
|
||||
selectedIconTheme: IconThemeData(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
unselectedIconTheme: IconThemeData(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
selectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
unselectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: e.icon,
|
||||
label: Text(
|
||||
Intl.message(e.label.name),
|
||||
child: ScrollConfiguration(
|
||||
behavior: HiddenBarScrollBehavior(),
|
||||
child: SingleChildScrollView(
|
||||
child: IntrinsicHeight(
|
||||
child: NavigationRail(
|
||||
backgroundColor: context.colorScheme.surfaceContainer,
|
||||
selectedIconTheme: IconThemeData(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
unselectedIconTheme: IconThemeData(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
selectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
unselectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: e.icon,
|
||||
label: Text(
|
||||
Intl.message(e.label.name),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController
|
||||
.toPage(navigationItems[index].label);
|
||||
},
|
||||
extended: false,
|
||||
selectedIndex: currentIndex,
|
||||
labelType: showLabel
|
||||
? NavigationRailLabelType.all
|
||||
: NavigationRailLabelType.none,
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController
|
||||
.toPage(navigationItems[index].label);
|
||||
},
|
||||
extended: false,
|
||||
selectedIndex: currentIndex,
|
||||
labelType: showLabel
|
||||
? NavigationRailLabelType.all
|
||||
: NavigationRailLabelType.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -78,7 +78,7 @@ final coreStateProvider = AutoDisposeProvider<CoreState>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef CoreStateRef = AutoDisposeProviderRef<CoreState>;
|
||||
String _$clashConfigStateHash() => r'848f6b2f734d99fb11ec05f73d614be415e9658f';
|
||||
String _$clashConfigStateHash() => r'fbbcd7221b0b9b18db523e59c9021e8e56e119ca';
|
||||
|
||||
/// See also [clashConfigState].
|
||||
@ProviderFor(clashConfigState)
|
||||
@@ -1765,6 +1765,22 @@ class _GetProfileOverrideDataProviderElement
|
||||
String get profileId => (origin as GetProfileOverrideDataProvider).profileId;
|
||||
}
|
||||
|
||||
String _$layoutChangeHash() => r'f25182e1dfaf3c70000404d7635bb1e1db09efbb';
|
||||
|
||||
/// See also [layoutChange].
|
||||
@ProviderFor(layoutChange)
|
||||
final layoutChangeProvider = AutoDisposeProvider<VM2?>.internal(
|
||||
layoutChange,
|
||||
name: r'layoutChangeProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$layoutChangeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef LayoutChangeRef = AutoDisposeProviderRef<VM2?>;
|
||||
String _$genColorSchemeHash() => r'b18f15c938a8132ee4ed02cdfc02f3b9f01724e2';
|
||||
|
||||
/// See also [genColorScheme].
|
||||
|
||||
@@ -75,13 +75,15 @@ CoreState coreState(Ref ref) {
|
||||
ClashConfigState clashConfigState(Ref ref) {
|
||||
final clashConfig = ref.watch(patchClashConfigProvider);
|
||||
final overrideDns = ref.watch(overrideDnsProvider);
|
||||
final overrideData = ref.watch(currentProfileProvider.select(
|
||||
(state) => state?.overrideData,
|
||||
));
|
||||
final overrideData =
|
||||
ref.watch(currentProfileProvider.select((state) => state?.overrideData));
|
||||
final routeMode =
|
||||
ref.watch(networkSettingProvider.select((state) => state.routeMode));
|
||||
return ClashConfigState(
|
||||
overrideDns: overrideDns,
|
||||
clashConfig: clashConfig,
|
||||
overrideData: overrideData ?? OverrideData(),
|
||||
routeMode: routeMode,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -508,6 +510,17 @@ OverrideData? getProfileOverrideData(Ref ref, String profileId) {
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
VM2? layoutChange(Ref ref) {
|
||||
final viewWidth = ref.watch(viewWidthProvider);
|
||||
final textScale =
|
||||
ref.watch(themeSettingProvider.select((state) => state.textScale));
|
||||
return VM2(
|
||||
a: viewWidth,
|
||||
b: textScale,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
ColorScheme genColorScheme(
|
||||
Ref ref,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
@@ -22,13 +21,15 @@ typedef UpdateTasks = List<FutureOr Function()>;
|
||||
|
||||
class GlobalState {
|
||||
static GlobalState? _instance;
|
||||
Map<Key, double> cacheScrollPosition = {};
|
||||
Map<CacheTag, double> cacheScrollPosition = {};
|
||||
Map<CacheTag, FixedMap<String, double>> cacheHeightMap = {};
|
||||
bool isService = false;
|
||||
Timer? timer;
|
||||
Timer? groupsUpdateTimer;
|
||||
late Config config;
|
||||
late AppState appState;
|
||||
bool isPre = true;
|
||||
String? coreSHA256;
|
||||
late PackageInfo packageInfo;
|
||||
Function? updateCurrentDelayDebounce;
|
||||
late Measure measure;
|
||||
@@ -51,11 +52,13 @@ class GlobalState {
|
||||
}
|
||||
|
||||
initApp(int version) async {
|
||||
coreSHA256 = const String.fromEnvironment("CORE_SHA256");
|
||||
isPre = const String.fromEnvironment("APP_ENV") != 'stable';
|
||||
appState = AppState(
|
||||
version: version,
|
||||
viewSize: Size.zero,
|
||||
requests: FixedList(1000),
|
||||
logs: FixedList(1000),
|
||||
requests: FixedList(maxLength),
|
||||
logs: FixedList(maxLength),
|
||||
traffics: FixedList(30),
|
||||
totalTraffic: Traffic(),
|
||||
);
|
||||
@@ -66,7 +69,8 @@ class GlobalState {
|
||||
_initDynamicColor() async {
|
||||
try {
|
||||
corePalette = await DynamicColorPlugin.getCorePalette();
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ?? Color(defaultPrimaryColor);
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ??
|
||||
Color(defaultPrimaryColor);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -256,14 +260,17 @@ class GlobalState {
|
||||
getUpdateConfigParams([bool? isPatch]) {
|
||||
final currentProfile = config.currentProfile;
|
||||
final clashConfig = config.patchClashConfig;
|
||||
final routeAddress =
|
||||
config.networkProps.routeMode == RouteMode.bypassPrivate
|
||||
? defaultBypassPrivateRouteAddress
|
||||
: clashConfig.tun.routeAddress;
|
||||
return UpdateConfigParams(
|
||||
profileId: config.currentProfileId ?? "",
|
||||
config: clashConfig.copyWith(
|
||||
globalUa: ua,
|
||||
tun: clashConfig.tun.copyWith(
|
||||
routeAddress: config.networkProps.routeMode == RouteMode.bypassPrivate
|
||||
? defaultBypassPrivateRouteAddress
|
||||
: clashConfig.tun.routeAddress,
|
||||
autoRoute: routeAddress.isEmpty ? true : false,
|
||||
routeAddress: routeAddress,
|
||||
),
|
||||
rule: currentProfile?.overrideData.runningRule ?? [],
|
||||
),
|
||||
|
||||
@@ -137,7 +137,7 @@ class DonutChartPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
const strokeWidth = 10.0;
|
||||
final strokeWidth = 10.0.ap;
|
||||
final radius = min(size.width / 2, size.height / 2) - strokeWidth / 2;
|
||||
|
||||
final gapAngle = 2 * asin(strokeWidth * 1 / (2 * radius)) * 1.2;
|
||||
|
||||
@@ -62,6 +62,22 @@ class OpenDelegate extends Delegate {
|
||||
});
|
||||
}
|
||||
|
||||
class NextDelegate extends Delegate {
|
||||
final Widget widget;
|
||||
final String title;
|
||||
final double? maxWidth;
|
||||
final Widget? action;
|
||||
final bool blur;
|
||||
|
||||
const NextDelegate({
|
||||
required this.title,
|
||||
required this.widget,
|
||||
this.maxWidth,
|
||||
this.action,
|
||||
this.blur = true,
|
||||
});
|
||||
}
|
||||
|
||||
class OptionsDelegate<T> extends Delegate {
|
||||
final List<T> options;
|
||||
final String title;
|
||||
@@ -138,6 +154,21 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
const ListItem.next({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 16),
|
||||
this.trailing,
|
||||
required NextDelegate this.delegate,
|
||||
this.horizontalTitleGap,
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
const ListItem.options({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -226,6 +257,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
leading: leading ?? this.leading,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
title: title,
|
||||
minVerticalPadding: 12,
|
||||
subtitle: subtitle,
|
||||
titleAlignment: tileTitleAlignment,
|
||||
onTap: onTap,
|
||||
@@ -285,6 +317,34 @@ class ListItem<T> extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
if (delegate is NextDelegate) {
|
||||
final nextDelegate = delegate as NextDelegate;
|
||||
final child = SafeArea(
|
||||
child: nextDelegate.widget,
|
||||
);
|
||||
|
||||
return _buildListTile(
|
||||
onTap: () {
|
||||
showExtend(
|
||||
context,
|
||||
props: ExtendProps(
|
||||
blur: nextDelegate.blur,
|
||||
maxWidth: nextDelegate.maxWidth,
|
||||
),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
actions: [
|
||||
if (nextDelegate.action != null) nextDelegate.action!,
|
||||
],
|
||||
type: type,
|
||||
body: child,
|
||||
title: nextDelegate.title,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (delegate is OptionsDelegate) {
|
||||
final optionsDelegate = delegate as OptionsDelegate<T>;
|
||||
return _buildListTile(
|
||||
@@ -353,14 +413,11 @@ class ListItem<T> extends StatelessWidget {
|
||||
radioDelegate.onChanged!(radioDelegate.value);
|
||||
}
|
||||
},
|
||||
leading: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: Radio<T>(
|
||||
value: radioDelegate.value,
|
||||
groupValue: radioDelegate.groupValue,
|
||||
onChanged: radioDelegate.onChanged,
|
||||
),
|
||||
leading: Radio<T>(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
value: radioDelegate.value,
|
||||
groupValue: radioDelegate.groupValue,
|
||||
onChanged: radioDelegate.onChanged,
|
||||
),
|
||||
trailing: trailing,
|
||||
);
|
||||
|
||||
33
lib/widgets/notification.dart
Normal file
33
lib/widgets/notification.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:fl_clash/models/config.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class TextScaleNotification extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Function(TextScale textScale) onNotification;
|
||||
|
||||
const TextScaleNotification({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.onNotification,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
ref.listen(
|
||||
themeSettingProvider.select((state) => state.textScale),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
onNotification(next);
|
||||
}
|
||||
},
|
||||
);
|
||||
return child!;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import 'package:flutter/services.dart';
|
||||
import 'chip.dart';
|
||||
|
||||
class CommonScaffold extends StatefulWidget {
|
||||
final PreferredSizeWidget? appBar;
|
||||
final AppBar? appBar;
|
||||
final Widget body;
|
||||
final Widget? bottomNavigationBar;
|
||||
final Widget? sideNavigationBar;
|
||||
@@ -125,25 +125,25 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
}
|
||||
}
|
||||
|
||||
ThemeData _appBarTheme(BuildContext context) {
|
||||
Widget _buildSearchingAppBarTheme(Widget child) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
return theme.copyWith(
|
||||
appBarTheme: AppBarTheme(
|
||||
systemOverlayStyle: colorScheme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
backgroundColor: colorScheme.brightness == Brightness.dark
|
||||
? Colors.grey[900]
|
||||
: Colors.white,
|
||||
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: InputBorder.none,
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
appBarTheme: theme.appBarTheme.copyWith(
|
||||
backgroundColor: colorScheme.brightness == Brightness.dark
|
||||
? Colors.grey[900]
|
||||
: Colors.white,
|
||||
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -318,72 +318,66 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
child: appBar,
|
||||
);
|
||||
}
|
||||
return _isSearch
|
||||
? Theme(
|
||||
data: _appBarTheme(context),
|
||||
child: CommonPopScope(
|
||||
onPop: () {
|
||||
if (_isSearch) {
|
||||
_handleExitSearching();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: appBar,
|
||||
),
|
||||
)
|
||||
: appBar;
|
||||
return _isSearch ? _buildSearchingAppBarTheme(appBar) : appBar;
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ValueListenableBuilder<AppBarState>(
|
||||
valueListenable: _appBarState,
|
||||
builder: (_, state, __) {
|
||||
return _buildAppBarWrap(
|
||||
AppBar(
|
||||
centerTitle: widget.centerTitle ?? false,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||
leading: _buildLeading(),
|
||||
title: _buildTitle(state.searchState),
|
||||
actions: _buildActions(
|
||||
state.searchState != null,
|
||||
state.actions.isNotEmpty
|
||||
? state.actions
|
||||
: widget.actions ?? [],
|
||||
),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
appBarTheme: AppBarTheme(
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
widget.appBar ??
|
||||
ValueListenableBuilder<AppBarState>(
|
||||
valueListenable: _appBarState,
|
||||
builder: (_, state, __) {
|
||||
return _buildAppBarWrap(
|
||||
AppBar(
|
||||
centerTitle: widget.centerTitle ?? false,
|
||||
automaticallyImplyLeading:
|
||||
widget.automaticallyImplyLeading,
|
||||
leading: _buildLeading(),
|
||||
title: _buildTitle(state.searchState),
|
||||
actions: _buildActions(
|
||||
state.searchState != null,
|
||||
state.actions.isNotEmpty
|
||||
? state.actions
|
||||
: widget.actions ?? [],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -391,49 +385,51 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(widget.appBar != null || widget.title != null);
|
||||
final body = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _keywordsNotifier,
|
||||
builder: (_, keywords, __) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_onKeywordsUpdate != null) {
|
||||
_onKeywordsUpdate!(keywords);
|
||||
final body = SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _keywordsNotifier,
|
||||
builder: (_, keywords, __) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_onKeywordsUpdate != null) {
|
||||
_onKeywordsUpdate!(keywords);
|
||||
}
|
||||
});
|
||||
if (keywords.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
});
|
||||
if (keywords.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final keyword in keywords)
|
||||
CommonChip(
|
||||
label: keyword,
|
||||
type: ChipType.delete,
|
||||
onPressed: () {
|
||||
_deleteKeyword(keyword);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: widget.body,
|
||||
),
|
||||
],
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final keyword in keywords)
|
||||
CommonChip(
|
||||
label: keyword,
|
||||
type: ChipType.delete,
|
||||
onPressed: () {
|
||||
_deleteKeyword(keyword);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: widget.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
final scaffold = Scaffold(
|
||||
appBar: widget.appBar ?? _buildAppBar(),
|
||||
appBar: _buildAppBar(),
|
||||
body: body,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
floatingActionButton: ValueListenableBuilder<Widget?>(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/common/list.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -54,13 +54,13 @@ class ScrollToEndBox<T> extends StatefulWidget {
|
||||
final ScrollController controller;
|
||||
final List<T> dataSource;
|
||||
final Widget child;
|
||||
final Key cacheKey;
|
||||
final CacheTag tag;
|
||||
|
||||
const ScrollToEndBox({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.controller,
|
||||
required this.cacheKey,
|
||||
required this.tag,
|
||||
required this.dataSource,
|
||||
});
|
||||
|
||||
@@ -73,8 +73,7 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
|
||||
|
||||
_handleTryToEnd() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final double offset =
|
||||
globalState.cacheScrollPosition[widget.cacheKey] ?? -1;
|
||||
final double offset = globalState.cacheScrollPosition[widget.tag] ?? -1;
|
||||
if (offset < 0) {
|
||||
widget.controller.animateTo(
|
||||
duration: kThemeAnimationDuration,
|
||||
@@ -85,12 +84,6 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
globalState.cacheScrollPosition[widget.cacheKey] = -1;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(ScrollToEndBox<T> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
@@ -101,13 +94,12 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NotificationListener<ScrollNotification>(
|
||||
return NotificationListener<UserScrollNotification>(
|
||||
onNotification: (details) {
|
||||
double offset =
|
||||
globalState.cacheScrollPosition[widget.tag] =
|
||||
details.metrics.pixels == details.metrics.maxScrollExtent
|
||||
? -1
|
||||
: details.metrics.pixels;
|
||||
globalState.cacheScrollPosition[widget.cacheKey] = offset;
|
||||
return false;
|
||||
},
|
||||
child: widget.child,
|
||||
@@ -124,6 +116,7 @@ class CacheItemExtentListView extends StatefulWidget {
|
||||
final bool shrinkWrap;
|
||||
final bool reverse;
|
||||
final ScrollController controller;
|
||||
final CacheTag tag;
|
||||
|
||||
const CacheItemExtentListView({
|
||||
super.key,
|
||||
@@ -135,6 +128,7 @@ class CacheItemExtentListView extends StatefulWidget {
|
||||
required this.keyBuilder,
|
||||
required this.itemCount,
|
||||
required this.itemExtentBuilder,
|
||||
required this.tag,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -143,21 +137,19 @@ class CacheItemExtentListView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class CacheItemExtentListViewState extends State<CacheItemExtentListView> {
|
||||
late final FixedMap<String, double> _cacheHeightMap;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_cacheHeightMap = FixedMap(widget.itemCount);
|
||||
_updateCacheHeightMap();
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
_cacheHeightMap.clear();
|
||||
_updateCacheHeightMap() {
|
||||
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
|
||||
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_cacheHeightMap.updateMaxSize(widget.itemCount);
|
||||
return ListView.builder(
|
||||
itemBuilder: widget.itemBuilder,
|
||||
itemCount: widget.itemCount,
|
||||
@@ -166,20 +158,14 @@ class CacheItemExtentListViewState extends State<CacheItemExtentListView> {
|
||||
shrinkWrap: widget.shrinkWrap,
|
||||
controller: widget.controller,
|
||||
itemExtentBuilder: (index, __) {
|
||||
final key = widget.keyBuilder(index);
|
||||
if (_cacheHeightMap.containsKey(key)) {
|
||||
return _cacheHeightMap.get(key);
|
||||
}
|
||||
return _cacheHeightMap.put(key, widget.itemExtentBuilder(index));
|
||||
_updateCacheHeightMap();
|
||||
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
|
||||
widget.keyBuilder(index),
|
||||
() => widget.itemExtentBuilder(index),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cacheHeightMap.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class CacheItemExtentSliverReorderableList extends StatefulWidget {
|
||||
@@ -189,6 +175,7 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
|
||||
final double Function(int index) itemExtentBuilder;
|
||||
final ReorderCallback onReorder;
|
||||
final ReorderItemProxyDecorator? proxyDecorator;
|
||||
final CacheTag tag;
|
||||
|
||||
const CacheItemExtentSliverReorderableList({
|
||||
super.key,
|
||||
@@ -198,6 +185,7 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
|
||||
required this.itemExtentBuilder,
|
||||
required this.onReorder,
|
||||
this.proxyDecorator,
|
||||
required this.tag,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -207,30 +195,24 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
|
||||
|
||||
class CacheItemExtentSliverReorderableListState
|
||||
extends State<CacheItemExtentSliverReorderableList> {
|
||||
late final FixedMap<String, double> _cacheHeightMap;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_cacheHeightMap = FixedMap(widget.itemCount);
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
_cacheHeightMap.clear();
|
||||
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
|
||||
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_cacheHeightMap.updateMaxSize(widget.itemCount);
|
||||
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
|
||||
return SliverReorderableList(
|
||||
itemBuilder: widget.itemBuilder,
|
||||
itemCount: widget.itemCount,
|
||||
itemExtentBuilder: (index, __) {
|
||||
final key = widget.keyBuilder(index);
|
||||
if (_cacheHeightMap.containsKey(key)) {
|
||||
return _cacheHeightMap.get(key);
|
||||
}
|
||||
return _cacheHeightMap.put(key, widget.itemExtentBuilder(index));
|
||||
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
|
||||
widget.keyBuilder(index),
|
||||
() => widget.itemExtentBuilder(index),
|
||||
);
|
||||
},
|
||||
onReorder: widget.onReorder,
|
||||
proxyDecorator: widget.proxyDecorator,
|
||||
@@ -239,7 +221,6 @@ class CacheItemExtentSliverReorderableListState
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cacheHeightMap.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,18 +1058,18 @@ 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),
|
||||
];
|
||||
// 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 RRect thumbRRect =
|
||||
RRect.fromRectAndRadius(thumbRect.shift(offset), _kThumbRadius);
|
||||
|
||||
for (final BoxShadow shadow in thumbShadow) {
|
||||
context.canvas
|
||||
.drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
|
||||
}
|
||||
// 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));
|
||||
|
||||
@@ -84,6 +84,7 @@ class EmojiText extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RichText(
|
||||
textScaler: MediaQuery.of(context).textScaler,
|
||||
maxLines: maxLines,
|
||||
overflow: overflow ?? TextOverflow.clip,
|
||||
text: TextSpan(
|
||||
|
||||
@@ -32,3 +32,4 @@ export 'effect.dart';
|
||||
export 'palette.dart';
|
||||
export 'tab.dart';
|
||||
export 'container.dart';
|
||||
export 'notification.dart';
|
||||
|
||||
@@ -10,7 +10,6 @@ keywords:
|
||||
|
||||
generic_name: FlClash
|
||||
|
||||
|
||||
categories:
|
||||
- Network
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ installed_size: 6604
|
||||
essential: false
|
||||
icon: ./assets/images/icon.png
|
||||
|
||||
dependencies:
|
||||
- libayatana-appindicator3-dev
|
||||
- libkeybinder-3.0-dev
|
||||
|
||||
keywords:
|
||||
- FlClash
|
||||
|
||||
Submodule plugins/flutter_distributor updated: c5c06ee67d...cdeeef2d8f
@@ -279,7 +279,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
|
||||
@@ -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.83+202504221
|
||||
version: 0.8.84+202505013
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -66,6 +66,7 @@ dev_dependencies:
|
||||
riverpod_generator: ^2.6.3
|
||||
custom_lint: ^0.7.0
|
||||
riverpod_lint: ^2.6.3
|
||||
crypto: ^3.0.3
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
@@ -93,5 +94,5 @@ ffigen:
|
||||
flutter_intl:
|
||||
enabled: true
|
||||
class_name: AppLocalizations
|
||||
arb_dir: lib/l10n/arb
|
||||
arb_dir: arb
|
||||
output_dir: lib/l10n
|
||||
@@ -4,6 +4,7 @@ import requests
|
||||
|
||||
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
|
||||
TAG = os.getenv("TAG")
|
||||
RUN_ID = os.getenv("RUN_ID")
|
||||
|
||||
IS_STABLE = "-" not in TAG
|
||||
|
||||
@@ -45,7 +46,8 @@ if TAG:
|
||||
|
||||
if IS_STABLE:
|
||||
text += f"\nhttps://github.com/chen08209/FlClash/releases/tag/{TAG}\n"
|
||||
|
||||
else:
|
||||
text += f"\nhttps://github.com/chen08209/FlClash/actions/runs/{RUN_ID}\n"
|
||||
|
||||
if os.path.exists(release):
|
||||
text += "\n"
|
||||
|
||||
14
services/helper/Cargo.lock
generated
14
services/helper/Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@@ -284,6 +284,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"sha2",
|
||||
"tokio",
|
||||
"warp",
|
||||
"windows-service",
|
||||
@@ -822,6 +823,17 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
|
||||
@@ -14,10 +14,11 @@ anyhow = "1.0.93"
|
||||
warp = "0.3.7"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
once_cell = "1.20.2"
|
||||
sha2 = "0.10.8"
|
||||
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = "s"
|
||||
opt-level = "s"
|
||||
|
||||
4
services/helper/build.rs
Normal file
4
services/helper/build.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
let version = std::env::var("TOKEN").unwrap_or_default();
|
||||
println!("cargo:rustc-env=TOKEN={}", version);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::VecDeque;
|
||||
use std::{io, thread};
|
||||
use std::io::BufRead;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, Error, Read};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{io, thread};
|
||||
use warp::{Filter, Reply};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
const LISTEN_PORT: u16 = 47890;
|
||||
|
||||
@@ -15,10 +17,32 @@ pub struct StartParams {
|
||||
pub arg: String,
|
||||
}
|
||||
|
||||
static LOGS: Lazy<Arc<Mutex<VecDeque<String>>>> = Lazy::new(|| Arc::new(Mutex::new(VecDeque::with_capacity(100))));
|
||||
static PROCESS: Lazy<Arc<Mutex<Option<std::process::Child>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||
fn sha256_file(path: &str) -> Result<String, Error> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut hasher = Sha256::new();
|
||||
let mut buffer = [0; 4096];
|
||||
|
||||
loop {
|
||||
let bytes_read = file.read(&mut buffer)?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buffer[..bytes_read]);
|
||||
}
|
||||
|
||||
Ok(format!("{:x}", hasher.finalize()))
|
||||
}
|
||||
|
||||
static LOGS: Lazy<Arc<Mutex<VecDeque<String>>>> =
|
||||
Lazy::new(|| Arc::new(Mutex::new(VecDeque::with_capacity(100))));
|
||||
static PROCESS: Lazy<Arc<Mutex<Option<std::process::Child>>>> =
|
||||
Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||
|
||||
fn start(start_params: StartParams) -> impl Reply {
|
||||
let sha256 = sha256_file(start_params.path.as_str()).unwrap_or("".to_string());
|
||||
if sha256 != env!("TOKEN") {
|
||||
return format!("The SHA256 hash of the program requesting execution is: {}. The helper program only allows execution of applications with the SHA256 hash: {}.", sha256, env!("TOKEN"),);
|
||||
}
|
||||
stop();
|
||||
let mut process = PROCESS.lock().unwrap();
|
||||
match Command::new(&start_params.path)
|
||||
@@ -73,38 +97,29 @@ fn log_message(message: String) {
|
||||
|
||||
fn get_logs() -> impl Reply {
|
||||
let log_buffer = LOGS.lock().unwrap();
|
||||
let value = log_buffer.iter().cloned().collect::<Vec<String>>().join("\n");
|
||||
let value = log_buffer
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
warp::reply::with_header(value, "Content-Type", "text/plain")
|
||||
}
|
||||
|
||||
pub async fn run_service() -> anyhow::Result<()> {
|
||||
let api_ping = warp::get()
|
||||
.and(warp::path("ping"))
|
||||
.map(|| "2024125");
|
||||
let api_ping = warp::get().and(warp::path("ping")).map(|| env!("TOKEN"));
|
||||
|
||||
let api_start = warp::post()
|
||||
.and(warp::path("start"))
|
||||
.and(warp::body::json())
|
||||
.map(|start_params: StartParams| {
|
||||
start(start_params)
|
||||
});
|
||||
.map(|start_params: StartParams| start(start_params));
|
||||
|
||||
let api_stop = warp::post()
|
||||
.and(warp::path("stop"))
|
||||
.map(|| stop());
|
||||
let api_stop = warp::post().and(warp::path("stop")).map(|| stop());
|
||||
|
||||
let api_logs = warp::get()
|
||||
.and(warp::path("logs"))
|
||||
.map(|| get_logs());
|
||||
let api_logs = warp::get().and(warp::path("logs")).map(|| get_logs());
|
||||
|
||||
warp::serve(
|
||||
api_ping
|
||||
.or(api_start)
|
||||
.or(api_stop)
|
||||
.or(api_logs)
|
||||
)
|
||||
warp::serve(api_ping.or(api_start).or(api_stop).or(api_logs))
|
||||
.run(([127, 0, 0, 1], LISTEN_PORT))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
62
setup.dart
62
setup.dart
@@ -5,6 +5,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
enum Target {
|
||||
windows,
|
||||
@@ -195,7 +196,16 @@ class Build {
|
||||
if (exitCode != 0 && name != null) throw "$name error";
|
||||
}
|
||||
|
||||
static buildCore({
|
||||
static Future<String> calcSha256(String filePath) async {
|
||||
final file = File(filePath);
|
||||
if (!await file.exists()) {
|
||||
throw "File not exists";
|
||||
}
|
||||
final stream = file.openRead();
|
||||
return sha256.convert(await stream.reduce((a, b) => a + b)).toString();
|
||||
}
|
||||
|
||||
static Future<List<String>> buildCore({
|
||||
required Mode mode,
|
||||
required Target target,
|
||||
Arch? arch,
|
||||
@@ -209,6 +219,8 @@ class Build {
|
||||
},
|
||||
).toList();
|
||||
|
||||
final List<String> corePaths = [];
|
||||
|
||||
for (final item in items) {
|
||||
final outFileDir = join(
|
||||
outDir,
|
||||
@@ -228,6 +240,7 @@ class Build {
|
||||
outFileDir,
|
||||
fileName,
|
||||
);
|
||||
corePaths.add(outPath);
|
||||
|
||||
final Map<String, String> env = {};
|
||||
env["GOOS"] = item.target.os;
|
||||
@@ -258,9 +271,11 @@ class Build {
|
||||
workingDirectory: _coreDir,
|
||||
);
|
||||
}
|
||||
|
||||
return corePaths;
|
||||
}
|
||||
|
||||
static buildHelper(Target target) async {
|
||||
static buildHelper(Target target, String token) async {
|
||||
await exec(
|
||||
[
|
||||
"cargo",
|
||||
@@ -269,6 +284,9 @@ class Build {
|
||||
"--features",
|
||||
"windows-service",
|
||||
],
|
||||
environment: {
|
||||
"TOKEN": token,
|
||||
},
|
||||
name: "build helper",
|
||||
workingDirectory: _servicesDir,
|
||||
);
|
||||
@@ -278,13 +296,15 @@ class Build {
|
||||
"release",
|
||||
"helper${target.executableExtensionName}",
|
||||
);
|
||||
final targetPath = join(outDir, target.name,
|
||||
"FlClashHelperService${target.executableExtensionName}");
|
||||
final targetPath = join(
|
||||
outDir,
|
||||
target.name,
|
||||
"FlClashHelperService${target.executableExtensionName}",
|
||||
);
|
||||
await File(outPath).copy(targetPath);
|
||||
}
|
||||
|
||||
static List<String> getExecutable(String command) {
|
||||
print(command);
|
||||
return command.split(" ");
|
||||
}
|
||||
|
||||
@@ -402,7 +422,8 @@ class BuildCommand extends Command {
|
||||
await Build.exec(
|
||||
Build.getExecutable("sudo apt install -y libfuse2"),
|
||||
);
|
||||
final downloadName = arch == Arch.amd64 ? "x86_64" : "aarch_64";
|
||||
|
||||
final downloadName = arch == Arch.amd64 ? "x86_64" : "aarch64";
|
||||
await Build.exec(
|
||||
Build.getExecutable(
|
||||
"wget -O appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$downloadName.AppImage",
|
||||
@@ -413,12 +434,12 @@ class BuildCommand extends Command {
|
||||
"chmod +x appimagetool",
|
||||
),
|
||||
);
|
||||
await Build.exec(
|
||||
Build.getExecutable(
|
||||
"sudo mv appimagetool /usr/local/bin/",
|
||||
),
|
||||
);
|
||||
}
|
||||
await Build.exec(
|
||||
Build.getExecutable(
|
||||
"sudo mv appimagetool /usr/local/bin/",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_getMacosDependencies() async {
|
||||
@@ -466,26 +487,27 @@ class BuildCommand extends Command {
|
||||
throw "Invalid arch parameter";
|
||||
}
|
||||
|
||||
await Build.buildCore(
|
||||
final corePaths = await Build.buildCore(
|
||||
target: target,
|
||||
arch: arch,
|
||||
mode: mode,
|
||||
);
|
||||
|
||||
if (target == Target.windows) {
|
||||
await Build.buildHelper(target);
|
||||
}
|
||||
|
||||
if (out != "app") {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
case Target.windows:
|
||||
final token = target != Target.android
|
||||
? await Build.calcSha256(corePaths.first)
|
||||
: null;
|
||||
Build.buildHelper(target, token!);
|
||||
_buildDistributor(
|
||||
target: target,
|
||||
targets: "exe,zip",
|
||||
args: " --description $archName",
|
||||
args:
|
||||
" --description $archName --build-dart-define=CORE_SHA256=$token",
|
||||
env: env,
|
||||
);
|
||||
return;
|
||||
@@ -496,10 +518,8 @@ class BuildCommand extends Command {
|
||||
};
|
||||
final targets = [
|
||||
"deb",
|
||||
if (arch == Arch.amd64) ...[
|
||||
"appimage",
|
||||
"rpm",
|
||||
],
|
||||
if (arch == Arch.amd64) "appimage",
|
||||
if (arch == Arch.amd64) "rpm",
|
||||
].join(",");
|
||||
final defaultTarget = targetMap[arch];
|
||||
await _getLinuxDependencies(arch!);
|
||||
|
||||
83
windows/packaging/exe/inno_setup.iss
Normal file
83
windows/packaging/exe/inno_setup.iss
Normal file
@@ -0,0 +1,83 @@
|
||||
[Setup]
|
||||
AppId={{APP_ID}}
|
||||
AppVersion={{APP_VERSION}}
|
||||
AppName={{DISPLAY_NAME}}
|
||||
AppPublisher={{PUBLISHER_NAME}}
|
||||
AppPublisherURL={{PUBLISHER_URL}}
|
||||
AppSupportURL={{PUBLISHER_URL}}
|
||||
AppUpdatesURL={{PUBLISHER_URL}}
|
||||
DefaultDirName={{INSTALL_DIR_NAME}}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=.
|
||||
OutputBaseFilename={{OUTPUT_BASE_FILENAME}}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
SetupIconFile={{SETUP_ICON_FILE}}
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired={{PRIVILEGES_REQUIRED}}
|
||||
ArchitecturesAllowed={{ARCH}}
|
||||
ArchitecturesInstallIn64BitMode={{ARCH}}
|
||||
|
||||
[Code]
|
||||
procedure KillProcesses;
|
||||
var
|
||||
Processes: TArrayOfString;
|
||||
i: Integer;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Processes := ['FlClash.exe', 'FlClashCore.exe', 'FlClashHelperService.exe'];
|
||||
|
||||
for i := 0 to GetArrayLength(Processes)-1 do
|
||||
begin
|
||||
Exec('taskkill', '/f /im ' + Processes[i], '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
KillProcesses;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
[Languages]
|
||||
{% for locale in LOCALES %}
|
||||
{% if locale.lang == 'en' %}Name: "english"; MessagesFile: "compiler:Default.isl"{% endif %}
|
||||
{% if locale.lang == 'hy' %}Name: "armenian"; MessagesFile: "compiler:Languages\\Armenian.isl"{% endif %}
|
||||
{% if locale.lang == 'bg' %}Name: "bulgarian"; MessagesFile: "compiler:Languages\\Bulgarian.isl"{% endif %}
|
||||
{% if locale.lang == 'ca' %}Name: "catalan"; MessagesFile: "compiler:Languages\\Catalan.isl"{% endif %}
|
||||
{% if locale.lang == 'zh' %}
|
||||
Name: "chineseSimplified"; MessagesFile: {% if locale.file %}{{ locale.file }}{% else %}"compiler:Languages\\ChineseSimplified.isl"{% endif %}
|
||||
{% endif %}
|
||||
{% if locale.lang == 'co' %}Name: "corsican"; MessagesFile: "compiler:Languages\\Corsican.isl"{% endif %}
|
||||
{% if locale.lang == 'cs' %}Name: "czech"; MessagesFile: "compiler:Languages\\Czech.isl"{% endif %}
|
||||
{% if locale.lang == 'da' %}Name: "danish"; MessagesFile: "compiler:Languages\\Danish.isl"{% endif %}
|
||||
{% if locale.lang == 'nl' %}Name: "dutch"; MessagesFile: "compiler:Languages\\Dutch.isl"{% endif %}
|
||||
{% if locale.lang == 'fi' %}Name: "finnish"; MessagesFile: "compiler:Languages\\Finnish.isl"{% endif %}
|
||||
{% if locale.lang == 'fr' %}Name: "french"; MessagesFile: "compiler:Languages\\French.isl"{% endif %}
|
||||
{% if locale.lang == 'de' %}Name: "german"; MessagesFile: "compiler:Languages\\German.isl"{% endif %}
|
||||
{% if locale.lang == 'he' %}Name: "hebrew"; MessagesFile: "compiler:Languages\\Hebrew.isl"{% endif %}
|
||||
{% if locale.lang == 'is' %}Name: "icelandic"; MessagesFile: "compiler:Languages\\Icelandic.isl"{% endif %}
|
||||
{% if locale.lang == 'it' %}Name: "italian"; MessagesFile: "compiler:Languages\\Italian.isl"{% endif %}
|
||||
{% if locale.lang == 'ja' %}Name: "japanese"; MessagesFile: "compiler:Languages\\Japanese.isl"{% endif %}
|
||||
{% if locale.lang == 'no' %}Name: "norwegian"; MessagesFile: "compiler:Languages\\Norwegian.isl"{% endif %}
|
||||
{% if locale.lang == 'pl' %}Name: "polish"; MessagesFile: "compiler:Languages\\Polish.isl"{% endif %}
|
||||
{% if locale.lang == 'pt' %}Name: "portuguese"; MessagesFile: "compiler:Languages\\Portuguese.isl"{% endif %}
|
||||
{% if locale.lang == 'ru' %}Name: "russian"; MessagesFile: "compiler:Languages\\Russian.isl"{% endif %}
|
||||
{% if locale.lang == 'sk' %}Name: "slovak"; MessagesFile: "compiler:Languages\\Slovak.isl"{% endif %}
|
||||
{% if locale.lang == 'sl' %}Name: "slovenian"; MessagesFile: "compiler:Languages\\Slovenian.isl"{% endif %}
|
||||
{% if locale.lang == 'es' %}Name: "spanish"; MessagesFile: "compiler:Languages\\Spanish.isl"{% endif %}
|
||||
{% if locale.lang == 'tr' %}Name: "turkish"; MessagesFile: "compiler:Languages\\Turkish.isl"{% endif %}
|
||||
{% if locale.lang == 'uk' %}Name: "ukrainian"; MessagesFile: "compiler:Languages\\Ukrainian.isl"{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}
|
||||
[Files]
|
||||
Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"
|
||||
Name: "{autodesktop}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: desktopicon
|
||||
[Run]
|
||||
Filename: "{app}\\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user