Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
676f2d058a 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
2025-05-01 00:02:29 +08:00
99 changed files with 3296 additions and 1174 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,
)

View File

@@ -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
}

View File

@@ -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(

View File

@@ -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)

View File

@@ -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")
}

View File

@@ -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")

View File

@@ -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"
}

View File

@@ -391,5 +391,15 @@
"messageTest": "メッセージテスト",
"messageTestTip": "これはメッセージです。",
"crashTest": "クラッシュテスト",
"clearData": "データを消去"
"clearData": "データを消去",
"zoom": "ズーム",
"textScale": "テキストスケーリング",
"internet": "インターネット",
"systemApp": "システムアプリ",
"noNetworkApp": "ネットワークなしアプリ",
"contactMe": "連絡する",
"recoveryStrategy": "リカバリー戦略",
"recoveryStrategy_override": "オーバーライド",
"recoveryStrategy_compatible": "互換性",
"logsTest": "ログテスト"
}

View File

@@ -391,5 +391,15 @@
"messageTest": "Тестирование сообщения",
"messageTestTip": "Это сообщение.",
"crashTest": "Тест на сбои",
"clearData": "Очистить данные"
"clearData": "Очистить данные",
"zoom": "Масштаб",
"textScale": "Масштабирование текста",
"internet": "Интернет",
"systemApp": "Системное приложение",
"noNetworkApp": "Приложение без сети",
"contactMe": "Свяжитесь со мной",
"recoveryStrategy": "Стратегия восстановления",
"recoveryStrategy_override": "Переопределение",
"recoveryStrategy_compatible": "Совместимый",
"logsTest": "Тест журналов"
}

View File

@@ -391,5 +391,15 @@
"messageTest": "消息测试",
"messageTestTip": "这是一条消息。",
"crashTest": "崩溃测试",
"clearData": "清除数据"
"clearData": "清除数据",
"zoom": "缩放",
"textScale": "文本缩放",
"internet": "互联网",
"systemApp": "系统应用",
"noNetworkApp": "无网络应用",
"contactMe": "联系我",
"recoveryStrategy": "恢复策略",
"recoveryStrategy_override": "覆盖",
"recoveryStrategy_compatible": "兼容",
"logsTest": "日志测试"
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -245,7 +245,6 @@ abstract class ClashHandlerInterface with ClashInterface {
);
}
@override
Future<bool> crash() {
return invoke<bool>(

View File

@@ -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,

View File

@@ -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';

View File

@@ -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
View 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);
}

View File

@@ -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];
}

View File

@@ -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);
}
}

View File

@@ -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,
);
}
}

View File

@@ -14,7 +14,6 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
keep: false,
fragment: DashboardFragment(
key: GlobalObjectKey(PageLabel.dashboard),
),

View File

@@ -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 {

View File

@@ -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),
);
}
}

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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),
);
}
}

View File

@@ -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;
}
}

View File

@@ -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,
}

View File

@@ -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: () {

View File

@@ -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,
),
);
},
)
],
);
},

View File

@@ -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}"),
),
),
);
}),
],
);
}

View File

@@ -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(

View File

@@ -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();
},
),
);

View File

@@ -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(

View File

@@ -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),
)
],
);
},
),
)
],
),
),
),
);

View File

@@ -206,7 +206,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
);
},
icon: Icon(
size: 16,
size: 16.ap,
Icons.info_outline,
color: context.colorScheme.onSurfaceVariant,
),

View File

@@ -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,
),
),
),
),

View File

@@ -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,
),
);
},
);
},
)
],
),
),
),
);
}
}

View File

@@ -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),
);
},
),

View File

@@ -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)
],
),
);

View File

@@ -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,
),

View File

@@ -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(

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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,
),
);
},
),

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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("ダークモードの設定、色の調整"),

View File

@@ -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(

View File

@@ -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("设置深色模式,调整色彩"),

View File

@@ -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> {

View File

@@ -21,12 +21,16 @@ 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';
globalState.coreSHA256 = const String.fromEnvironment("CORE_SHA256");
HttpOverrides.global = FlClashHttpOverrides();
runApp(ProviderScope(
child: const Application(),

View File

@@ -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

View File

@@ -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,
),

View File

@@ -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(

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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) =>

View File

@@ -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")

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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,
};

View File

@@ -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.

View File

@@ -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 = {

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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: {

View File

@@ -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,
),
),
),
),

View File

@@ -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].

View File

@@ -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,

View File

@@ -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;
@@ -54,8 +55,8 @@ class GlobalState {
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 +67,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 +258,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 ?? [],
),

View File

@@ -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;

View File

@@ -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,
);

View 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,
);
}
}

View File

@@ -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?>(

View File

@@ -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();
}
}

View File

@@ -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));

View File

@@ -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(

View File

@@ -32,3 +32,4 @@ export 'effect.dart';
export 'palette.dart';
export 'tab.dart';
export 'container.dart';
export 'notification.dart';

View File

@@ -10,7 +10,6 @@ keywords:
generic_name: FlClash
categories:
- Network

View File

@@ -10,6 +10,9 @@ installed_size: 6604
essential: false
icon: ./assets/images/icon.png
dependencies:
- libayatana-appindicator3-dev
- libkeybinder-3.0-dev
keywords:
- FlClash

View File

@@ -279,7 +279,7 @@ packages:
source: hosted
version: "0.3.4+2"
crypto:
dependency: transitive
dependency: "direct dev"
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.83+202504221
version: 0.8.83+202505011
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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
View File

@@ -0,0 +1,4 @@
fn main() {
let version = std::env::var("TOKEN").unwrap_or_default();
println!("cargo:rustc-env=TOKEN={}", version);
}

View File

@@ -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,31 @@ 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 {
if sha256_file(start_params.path.as_str()).unwrap_or("".to_string()) != env!("TOKEN") {
return "Only FlClashCore is allowed to run.".to_string();
}
stop();
let mut process = PROCESS.lock().unwrap();
match Command::new(&start_params.path)
@@ -73,38 +96,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(())
}
}

View File

@@ -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!);

View 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

View File

@@ -1,3 +1,4 @@
script_template: inno_setup.iss
app_id: 728B3532-C74B-4870-9068-BE70FE12A3E6
app_name: FlClash
publisher: chen08209
@@ -9,4 +10,5 @@ setup_icon_file: ..\windows\runner\resources\app_icon.ico
locales:
- lang: zh
file: ..\windows\packaging\exe\ChineseSimplified.isl
- lang: en
- lang: en
privileges_required: admin