Compare commits
1 Commits
main
...
v0.8.92-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8bd7fedd4 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,23 +1,3 @@
|
|||||||
## v0.8.92
|
|
||||||
|
|
||||||
- Add sqlite store
|
|
||||||
|
|
||||||
- Optimize android quick action
|
|
||||||
|
|
||||||
- Optimize backup and restore
|
|
||||||
|
|
||||||
- Optimize more details
|
|
||||||
|
|
||||||
## v0.8.91
|
|
||||||
|
|
||||||
- Fix windows some issues
|
|
||||||
|
|
||||||
- Optimize overwrite handle
|
|
||||||
|
|
||||||
- Optimize access control page
|
|
||||||
|
|
||||||
- Optimize some details
|
|
||||||
|
|
||||||
## v0.8.90
|
## v0.8.90
|
||||||
|
|
||||||
- Fix android tile service
|
- Fix android tile service
|
||||||
|
|||||||
@@ -475,6 +475,5 @@
|
|||||||
"restoreFromFileDesc": "Restore data via file",
|
"restoreFromFileDesc": "Restore data via file",
|
||||||
"restoreOnlyConfig": "Restore configuration files only",
|
"restoreOnlyConfig": "Restore configuration files only",
|
||||||
"restoreAllData": "Restore all data",
|
"restoreAllData": "Restore all data",
|
||||||
"addProfile": "Add Profile",
|
"addProfile": "Add Profile"
|
||||||
"delayTest": "Delay Test"
|
|
||||||
}
|
}
|
||||||
@@ -476,6 +476,5 @@
|
|||||||
"restoreFromFileDesc": "ファイルを介してデータを復元する",
|
"restoreFromFileDesc": "ファイルを介してデータを復元する",
|
||||||
"restoreOnlyConfig": "設定ファイルのみを復元する",
|
"restoreOnlyConfig": "設定ファイルのみを復元する",
|
||||||
"restoreAllData": "すべてのデータを復元する",
|
"restoreAllData": "すべてのデータを復元する",
|
||||||
"addProfile": "プロファイルを追加",
|
"addProfile": "プロファイルを追加"
|
||||||
"delayTest": "遅延テスト"
|
|
||||||
}
|
}
|
||||||
@@ -484,6 +484,5 @@
|
|||||||
"restoreFromFileDesc": "Восстановить данные из файла",
|
"restoreFromFileDesc": "Восстановить данные из файла",
|
||||||
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
|
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
|
||||||
"restoreAllData": "Восстановить все данные",
|
"restoreAllData": "Восстановить все данные",
|
||||||
"addProfile": "Добавить профиль",
|
"addProfile": "Добавить профиль"
|
||||||
"delayTest": "Тест задержки"
|
|
||||||
}
|
}
|
||||||
@@ -476,6 +476,5 @@
|
|||||||
"restoreFromFileDesc": "通过文件恢复数据",
|
"restoreFromFileDesc": "通过文件恢复数据",
|
||||||
"restoreOnlyConfig": "仅恢复配置文件",
|
"restoreOnlyConfig": "仅恢复配置文件",
|
||||||
"restoreAllData": "恢复所有数据",
|
"restoreAllData": "恢复所有数据",
|
||||||
"addProfile": "添加配置",
|
"addProfile": "添加配置"
|
||||||
"delayTest": "延迟测试"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -532,9 +532,6 @@ func handleDelFile(path string, result ActionResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleSetupConfig(bytes []byte) string {
|
func handleSetupConfig(bytes []byte) string {
|
||||||
if !isInit {
|
|
||||||
return "not initialized"
|
|
||||||
}
|
|
||||||
var params = defaultSetupParams()
|
var params = defaultSetupParams()
|
||||||
err := UnmarshalJson(bytes, params)
|
err := UnmarshalJson(bytes, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ const helperPort = 47890;
|
|||||||
const maxTextScale = 1.4;
|
const maxTextScale = 1.4;
|
||||||
const minTextScale = 0.8;
|
const minTextScale = 0.8;
|
||||||
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||||
vertical: 16.mAp,
|
vertical: 16.ap,
|
||||||
horizontal: 16.mAp,
|
horizontal: 16.ap,
|
||||||
);
|
);
|
||||||
final listHeaderPadding = EdgeInsets.only(
|
final listHeaderPadding = EdgeInsets.only(
|
||||||
left: 16.mAp,
|
left: 16.ap,
|
||||||
right: 8.mAp,
|
right: 8.ap,
|
||||||
top: 24.mAp,
|
top: 24.ap,
|
||||||
bottom: 8.mAp,
|
bottom: 8.ap,
|
||||||
);
|
);
|
||||||
|
|
||||||
const watchExecution = true;
|
const watchExecution = true;
|
||||||
@@ -102,8 +102,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
|
|||||||
const defaultPrimaryColor = 0XFFD8C0C3;
|
const defaultPrimaryColor = 0XFFD8C0C3;
|
||||||
|
|
||||||
double getWidgetHeight(num lines) {
|
double getWidgetHeight(num lines) {
|
||||||
final space = 14.mAp;
|
return max(lines * 80 + (lines - 1) * 16, 0).ap;
|
||||||
return max(lines * (80.ap + space) - space, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxLength = 1000;
|
const maxLength = 1000;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:fl_clash/enum/enum.dart';
|
import 'package:fl_clash/enum/enum.dart';
|
||||||
import 'package:fl_clash/models/common.dart';
|
import 'package:fl_clash/models/common.dart';
|
||||||
import 'package:fl_clash/state.dart';
|
import 'package:fl_clash/state.dart';
|
||||||
@@ -22,10 +20,6 @@ extension NumExt on num {
|
|||||||
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
double get mAp {
|
|
||||||
return this * min((1 + (globalState.theme.textScaleFactor - 1) * 0.5), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TrafficShow get traffic {
|
TrafficShow get traffic {
|
||||||
final units = TrafficUnit.values;
|
final units = TrafficUnit.values;
|
||||||
var size = toDouble();
|
var size = toDouble();
|
||||||
@@ -57,7 +51,7 @@ extension NumExt on num {
|
|||||||
|
|
||||||
extension DoubleExt on double {
|
extension DoubleExt on double {
|
||||||
bool moreOrEqual(double value) {
|
bool moreOrEqual(double value) {
|
||||||
return this > value || (value - this).abs() < precisionErrorTolerance + 1;
|
return this > value || (value - this).abs() < precisionErrorTolerance + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ class Tray {
|
|||||||
return system.isWindows ? 'ico' : 'png';
|
return system.isWindows ? 'ico' : 'png';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> destroy() async {
|
|
||||||
await trayManager.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTryIcon({required bool isStart, required bool tunEnable}) {
|
String getTryIcon({required bool isStart, required bool tunEnable}) {
|
||||||
if (system.isMacOS || !isStart) {
|
if (system.isMacOS || !isStart) {
|
||||||
return 'assets/images/icon/status_1.$trayIconSuffix';
|
return 'assets/images/icon/status_1.$trayIconSuffix';
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ class Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await windowManager.close();
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,11 +138,6 @@ extension InitControllerExt on AppController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initStatus() async {
|
Future<void> _initStatus() async {
|
||||||
if (!globalState.needInitStatus) {
|
|
||||||
commonPrint.log('init status cancel');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
commonPrint.log('init status');
|
|
||||||
if (system.isAndroid) {
|
if (system.isAndroid) {
|
||||||
await globalState.updateStartTime();
|
await globalState.updateStartTime();
|
||||||
}
|
}
|
||||||
@@ -555,6 +550,9 @@ extension SetupControllerExt on AppController {
|
|||||||
|
|
||||||
Future<void> updateStatus(bool isStart, {bool isInit = false}) async {
|
Future<void> updateStatus(bool isStart, {bool isInit = false}) async {
|
||||||
if (isStart) {
|
if (isStart) {
|
||||||
|
_ref.read(runTimeProvider.notifier).update((state) {
|
||||||
|
return state ?? 0;
|
||||||
|
});
|
||||||
if (!isInit) {
|
if (!isInit) {
|
||||||
final res = await tryStartCore(true);
|
final res = await tryStartCore(true);
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -566,7 +564,6 @@ extension SetupControllerExt on AppController {
|
|||||||
await globalState.handleStart([updateRunTime, updateTraffic]);
|
await globalState.handleStart([updateRunTime, updateTraffic]);
|
||||||
applyProfileDebounce(force: true, silence: true);
|
applyProfileDebounce(force: true, silence: true);
|
||||||
} else {
|
} else {
|
||||||
globalState.needInitStatus = false;
|
|
||||||
await applyProfile(
|
await applyProfile(
|
||||||
force: true,
|
force: true,
|
||||||
preloadInvoke: () async {
|
preloadInvoke: () async {
|
||||||
@@ -653,14 +650,9 @@ extension SetupControllerExt on AppController {
|
|||||||
bool force = false,
|
bool force = false,
|
||||||
VoidCallback? preloadInvoke,
|
VoidCallback? preloadInvoke,
|
||||||
}) async {
|
}) async {
|
||||||
if (!force && !await needSetup()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await loadingRun(
|
await loadingRun(
|
||||||
() async {
|
() async {
|
||||||
await _setupConfig(preloadInvoke);
|
await _applyProfile(force, preloadInvoke);
|
||||||
await updateGroups();
|
|
||||||
await updateProviders();
|
|
||||||
},
|
},
|
||||||
silence: true,
|
silence: true,
|
||||||
tag: !silence ? LoadingTag.proxies : null,
|
tag: !silence ? LoadingTag.proxies : null,
|
||||||
@@ -730,8 +722,13 @@ extension SetupControllerExt on AppController {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setupConfig([VoidCallback? preloadInvoke]) async {
|
Future<void> _setupConfig([
|
||||||
commonPrint.log('setup ===>');
|
bool force = false,
|
||||||
|
VoidCallback? preloadInvoke,
|
||||||
|
]) async {
|
||||||
|
if (!force && !await needSetup()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var profile = _ref.read(currentProfileProvider);
|
var profile = _ref.read(currentProfileProvider);
|
||||||
final nextProfile = await profile?.checkAndUpdateAndCopy();
|
final nextProfile = await profile?.checkAndUpdateAndCopy();
|
||||||
if (nextProfile != null) {
|
if (nextProfile != null) {
|
||||||
@@ -749,6 +746,9 @@ extension SetupControllerExt on AppController {
|
|||||||
globalState.lastSetupState = setupState;
|
globalState.lastSetupState = setupState;
|
||||||
if (system.isAndroid) {
|
if (system.isAndroid) {
|
||||||
globalState.lastVpnState = _ref.read(vpnStateProvider);
|
globalState.lastVpnState = _ref.read(vpnStateProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system.isAndroid) {
|
||||||
preferences.saveShareState(this.sharedState);
|
preferences.saveShareState(this.sharedState);
|
||||||
}
|
}
|
||||||
final config = await getProfile(
|
final config = await getProfile(
|
||||||
@@ -768,6 +768,15 @@ extension SetupControllerExt on AppController {
|
|||||||
}
|
}
|
||||||
addCheckIp();
|
addCheckIp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future _applyProfile([
|
||||||
|
bool force = false,
|
||||||
|
VoidCallback? preloadInvoke,
|
||||||
|
]) async {
|
||||||
|
await _setupConfig(force, preloadInvoke);
|
||||||
|
await updateGroups();
|
||||||
|
await updateProviders();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CoreControllerExt on AppController {
|
extension CoreControllerExt on AppController {
|
||||||
@@ -833,7 +842,7 @@ extension CoreControllerExt on AppController {
|
|||||||
if (coreController.isCompleted) {
|
if (coreController.isCompleted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await restartCore(start);
|
await restartCore();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,12 +868,11 @@ extension SystemControllerExt on AppController {
|
|||||||
system.exit();
|
system.exit();
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await Future.wait([
|
if (needSave) {
|
||||||
if (needSave) preferences.saveConfig(config),
|
await preferences.saveConfig(config);
|
||||||
if (macOS != null) macOS!.updateDns(true),
|
}
|
||||||
if (proxy != null) proxy!.stopProxy(),
|
await proxy?.stopProxy();
|
||||||
if (tray != null) tray!.destroy(),
|
await macOS?.updateDns(true);
|
||||||
]);
|
|
||||||
await coreController.destroy();
|
await coreController.destroy();
|
||||||
commonPrint.log('exit');
|
commonPrint.log('exit');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ abstract class CoreHandlerInterface with CoreInterface {
|
|||||||
Duration? timeout,
|
Duration? timeout,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
await completer.future.timeout(const Duration(seconds: 10));
|
if (!completer.isCompleted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
commonPrint.log(
|
commonPrint.log(
|
||||||
'Invoke pre ${method.name} timeout $e',
|
'Invoke pre ${method.name} timeout $e',
|
||||||
|
|||||||
@@ -270,7 +270,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"defaultText": MessageLookupByLibrary.simpleMessage("Default"),
|
"defaultText": MessageLookupByLibrary.simpleMessage("Default"),
|
||||||
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
|
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
|
||||||
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
|
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
|
||||||
"delayTest": MessageLookupByLibrary.simpleMessage("Delay Test"),
|
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||||
"deleteMultipTip": m1,
|
"deleteMultipTip": m1,
|
||||||
"deleteTip": m2,
|
"deleteTip": m2,
|
||||||
|
|||||||
@@ -205,7 +205,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"defaultText": MessageLookupByLibrary.simpleMessage("デフォルト"),
|
"defaultText": MessageLookupByLibrary.simpleMessage("デフォルト"),
|
||||||
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
|
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
|
||||||
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
|
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
|
||||||
"delayTest": MessageLookupByLibrary.simpleMessage("遅延テスト"),
|
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("削除"),
|
"delete": MessageLookupByLibrary.simpleMessage("削除"),
|
||||||
"deleteMultipTip": m1,
|
"deleteMultipTip": m1,
|
||||||
"deleteTip": m2,
|
"deleteTip": m2,
|
||||||
|
|||||||
@@ -277,7 +277,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"defaultText": MessageLookupByLibrary.simpleMessage("По умолчанию"),
|
"defaultText": MessageLookupByLibrary.simpleMessage("По умолчанию"),
|
||||||
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
|
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
|
||||||
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
|
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
|
||||||
"delayTest": MessageLookupByLibrary.simpleMessage("Тест задержки"),
|
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
||||||
"deleteMultipTip": m1,
|
"deleteMultipTip": m1,
|
||||||
"deleteTip": m2,
|
"deleteTip": m2,
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"defaultText": MessageLookupByLibrary.simpleMessage("默认"),
|
"defaultText": MessageLookupByLibrary.simpleMessage("默认"),
|
||||||
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
|
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
|
||||||
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
||||||
"delayTest": MessageLookupByLibrary.simpleMessage("延迟测试"),
|
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||||
"deleteMultipTip": m1,
|
"deleteMultipTip": m1,
|
||||||
"deleteTip": m2,
|
"deleteTip": m2,
|
||||||
|
|||||||
@@ -3743,11 +3743,6 @@ class AppLocalizations {
|
|||||||
String get addProfile {
|
String get addProfile {
|
||||||
return Intl.message('Add Profile', name: 'addProfile', desc: '', args: []);
|
return Intl.message('Add Profile', name: 'addProfile', desc: '', args: []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Delay Test`
|
|
||||||
String get delayTest {
|
|
||||||
return Intl.message('Delay Test', name: 'delayTest', desc: '', args: []);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:fl_clash/common/common.dart';
|
import 'package:fl_clash/common/common.dart';
|
||||||
import 'package:fl_clash/enum/enum.dart';
|
import 'package:fl_clash/enum/enum.dart';
|
||||||
import 'package:fl_clash/models/common.dart';
|
import 'package:fl_clash/models/common.dart';
|
||||||
@@ -127,7 +125,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
|||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final res = utf8.decode(file.bytes?.toList() ?? []);
|
final res = String.fromCharCodes(file.bytes?.toList() ?? []);
|
||||||
_controller.text = res;
|
_controller.text = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:fl_clash/common/color.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
@@ -50,9 +49,9 @@ class InitErrorScreen extends StatelessWidget {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorScheme.errorContainer.opacity50,
|
color: colorScheme.errorContainer.withOpacity(0.5),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: Border.all(color: colorScheme.error.opacity50),
|
border: Border.all(color: colorScheme.error.withOpacity(0.5)),
|
||||||
),
|
),
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
error.toString(),
|
error.toString(),
|
||||||
@@ -72,7 +71,7 @@ class InitErrorScreen extends StatelessWidget {
|
|||||||
? Colors.grey[900]
|
? Colors.grey[900]
|
||||||
: Colors.grey[200],
|
: Colors.grey[200],
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: Border.all(color: Colors.grey.opacity50),
|
border: Border.all(color: Colors.grey.withOpacity(0.5)),
|
||||||
),
|
),
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
stack.toString(),
|
stack.toString(),
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class GlobalState {
|
|||||||
late Measure measure;
|
late Measure measure;
|
||||||
late CommonTheme theme;
|
late CommonTheme theme;
|
||||||
late Color accentColor;
|
late Color accentColor;
|
||||||
bool needInitStatus = true;
|
|
||||||
CorePalette? corePalette;
|
CorePalette? corePalette;
|
||||||
DateTime? startTime;
|
DateTime? startTime;
|
||||||
UpdateTasks tasks = [];
|
UpdateTasks tasks = [];
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final dashboardState = ref.watch(dashboardStateProvider);
|
final dashboardState = ref.watch(dashboardStateProvider);
|
||||||
final columns = max(4 * ((dashboardState.contentWidth / 280).ceil()), 8);
|
final columns = max(4 * ((dashboardState.contentWidth / 280).ceil()), 8);
|
||||||
final spacing = 14.mAp;
|
final spacing = 14.ap;
|
||||||
final children = [
|
final children = [
|
||||||
...dashboardState.dashboardWidgets
|
...dashboardState.dashboardWidgets
|
||||||
.where(
|
.where(
|
||||||
|
|||||||
@@ -41,54 +41,45 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
|||||||
final color = context.colorScheme.onSurfaceVariant.opacity80;
|
final color = context.colorScheme.onSurfaceVariant.opacity80;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: getWidgetHeight(2),
|
height: getWidgetHeight(2),
|
||||||
child: RepaintBoundary(
|
child: CommonCard(
|
||||||
child: CommonCard(
|
onPressed: () {},
|
||||||
onPressed: () {},
|
info: Info(
|
||||||
child: Consumer(
|
label: appLocalizations.networkSpeed,
|
||||||
builder: (_, ref, _) {
|
iconData: Icons.speed_sharp,
|
||||||
final traffics = ref.watch(trafficsProvider).list;
|
),
|
||||||
return Column(
|
child: Consumer(
|
||||||
children: [
|
builder: (_, ref, _) {
|
||||||
Padding(
|
final traffics = ref.watch(trafficsProvider).list;
|
||||||
padding: baseInfoEdgeInsets.copyWith(bottom: 0),
|
return Stack(
|
||||||
child: Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Positioned.fill(
|
||||||
children: [
|
child: Padding(
|
||||||
Flexible(
|
padding: EdgeInsets.all(
|
||||||
child: InfoHeader(
|
16,
|
||||||
padding: EdgeInsets.zero,
|
).copyWith(bottom: 0, left: 0, right: 0),
|
||||||
info: Info(
|
child: LineChart(
|
||||||
label: appLocalizations.networkSpeed,
|
gradient: true,
|
||||||
iconData: Icons.speed_sharp,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
points: _getPoints(traffics),
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
_getLastTraffic(traffics).speedText,
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
),
|
||||||
child: Padding(
|
Positioned(
|
||||||
padding: EdgeInsets.all(
|
top: 0,
|
||||||
16,
|
right: 0,
|
||||||
).copyWith(bottom: 0, left: 0, right: 0),
|
child: Transform.translate(
|
||||||
child: LineChart(
|
offset: Offset(-16, -20),
|
||||||
gradient: true,
|
child: Text(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
_getLastTraffic(traffics).speedText,
|
||||||
points: _getPoints(traffics),
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class _StartButtonState extends ConsumerState<StartButton>
|
|||||||
parent: _controller!,
|
parent: _controller!,
|
||||||
curve: Curves.easeOutBack,
|
curve: Curves.easeOutBack,
|
||||||
);
|
);
|
||||||
ref.listenManual(isStartProvider, (prev, next) {
|
ref.listenManual(runTimeProvider.select((state) => state != null), (
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
) {
|
||||||
if (next != isStart) {
|
if (next != isStart) {
|
||||||
isStart = next;
|
isStart = next;
|
||||||
updateController();
|
updateController();
|
||||||
@@ -52,7 +55,7 @@ class _StartButtonState extends ConsumerState<StartButton>
|
|||||||
isStart = !isStart;
|
isStart = !isStart;
|
||||||
updateController();
|
updateController();
|
||||||
debouncer.call(FunctionTag.updateStatus, () {
|
debouncer.call(FunctionTag.updateStatus, () {
|
||||||
appController.updateStatus(isStart, isInit: !ref.read(initProvider));
|
appController.updateStatus(isStart);
|
||||||
}, duration: commonDuration);
|
}, duration: commonDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:fl_clash/controller.dart';
|
|||||||
import 'package:fl_clash/enum/enum.dart';
|
import 'package:fl_clash/enum/enum.dart';
|
||||||
import 'package:fl_clash/features/overwrite/rule.dart';
|
import 'package:fl_clash/features/overwrite/rule.dart';
|
||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
import 'package:fl_clash/pages/editor.dart';
|
|
||||||
import 'package:fl_clash/providers/database.dart';
|
import 'package:fl_clash/providers/database.dart';
|
||||||
import 'package:fl_clash/providers/providers.dart';
|
import 'package:fl_clash/providers/providers.dart';
|
||||||
import 'package:fl_clash/state.dart';
|
import 'package:fl_clash/state.dart';
|
||||||
@@ -29,33 +28,10 @@ class _OverwriteViewState extends ConsumerState<OverwriteView> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handlePreview() async {
|
|
||||||
final profile = ref.read(profileProvider(widget.profileId));
|
|
||||||
if (profile == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final configMap = await appController.getProfileWithId(profile.id);
|
|
||||||
final content = await encodeYamlTask(configMap);
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final previewPage = EditorPage(title: profile.realLabel, content: content);
|
|
||||||
BaseNavigator.push<String>(context, previewPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CommonScaffold(
|
return CommonScaffold(
|
||||||
title: appLocalizations.override,
|
title: appLocalizations.override,
|
||||||
actions: [
|
|
||||||
CommonMinFilledButtonTheme(
|
|
||||||
child: FilledButton(
|
|
||||||
onPressed: _handlePreview,
|
|
||||||
child: Text(appLocalizations.preview),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
],
|
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
slivers: [_Title(widget.profileId), _Content(widget.profileId)],
|
slivers: [_Title(widget.profileId), _Content(widget.profileId)],
|
||||||
),
|
),
|
||||||
@@ -365,9 +341,7 @@ class _ScriptContent extends ConsumerWidget {
|
|||||||
|
|
||||||
void _handleChange(WidgetRef ref, int scriptId) {
|
void _handleChange(WidgetRef ref, int scriptId) {
|
||||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||||
return state.copyWith(
|
return state.copyWith(scriptId: scriptId);
|
||||||
scriptId: state.scriptId == scriptId ? null : scriptId,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class ProfilesView extends StatefulWidget {
|
|||||||
|
|
||||||
class _ProfilesViewState extends State<ProfilesView> {
|
class _ProfilesViewState extends State<ProfilesView> {
|
||||||
Function? applyConfigDebounce;
|
Function? applyConfigDebounce;
|
||||||
bool _isUpdating = false;
|
|
||||||
|
|
||||||
void _handleShowAddExtendPage() {
|
void _handleShowAddExtendPage() {
|
||||||
showExtend(
|
showExtend(
|
||||||
@@ -41,10 +40,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateProfiles(List<Profile> profiles) async {
|
Future<void> _updateProfiles(List<Profile> profiles) async {
|
||||||
if (_isUpdating == true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_isUpdating = true;
|
|
||||||
final List<UpdatingMessage> messages = [];
|
final List<UpdatingMessage> messages = [];
|
||||||
final updateProfiles = profiles.map<Future>((profile) async {
|
final updateProfiles = profiles.map<Future>((profile) async {
|
||||||
if (profile.type == ProfileType.file) return;
|
if (profile.type == ProfileType.file) return;
|
||||||
@@ -60,7 +55,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
|||||||
if (messages.isNotEmpty) {
|
if (messages.isNotEmpty) {
|
||||||
globalState.showAllUpdatingMessagesDialog(messages);
|
globalState.showAllUpdatingMessagesDialog(messages);
|
||||||
}
|
}
|
||||||
_isUpdating = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildActions(List<Profile> profiles) {
|
List<Widget> _buildActions(List<Profile> profiles) {
|
||||||
@@ -92,10 +86,10 @@ class _ProfilesViewState extends State<ProfilesView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFAB() {
|
Widget _buildFAB() {
|
||||||
return CommonFloatingActionButton(
|
return FloatingActionButton(
|
||||||
|
heroTag: null,
|
||||||
onPressed: _handleShowAddExtendPage,
|
onPressed: _handleShowAddExtendPage,
|
||||||
icon: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
label: context.appLocalizations.addProfile,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +99,7 @@ class _ProfilesViewState extends State<ProfilesView> {
|
|||||||
builder: (_, ref, _) {
|
builder: (_, ref, _) {
|
||||||
final isLoading = ref.watch(loadingProvider(LoadingTag.profiles));
|
final isLoading = ref.watch(loadingProvider(LoadingTag.profiles));
|
||||||
final state = ref.watch(profilesStateProvider);
|
final state = ref.watch(profilesStateProvider);
|
||||||
final spacing = 14.mAp;
|
final spacing = 14.ap;
|
||||||
return CommonScaffold(
|
return CommonScaffold(
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
title: appLocalizations.profiles,
|
title: appLocalizations.profiles,
|
||||||
|
|||||||
@@ -410,15 +410,19 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
|||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: _controller.view,
|
animation: _controller.view,
|
||||||
builder: (_, child) {
|
builder: (_, child) {
|
||||||
return FadeTransition(
|
return SizedBox(
|
||||||
opacity: _animation,
|
width: 56,
|
||||||
child: ScaleTransition(scale: _animation, child: child),
|
height: 56,
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _animation,
|
||||||
|
child: ScaleTransition(scale: _animation, child: child),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: CommonFloatingActionButton(
|
child: FloatingActionButton(
|
||||||
|
heroTag: null,
|
||||||
onPressed: _healthcheck,
|
onPressed: _healthcheck,
|
||||||
label: appLocalizations.delayTest,
|
child: const Icon(Icons.network_ping),
|
||||||
icon: const Icon(Icons.network_ping),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:fl_clash/widgets/inherited.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ScrollOverBuilder extends StatefulWidget {
|
class ScrollOverBuilder extends StatefulWidget {
|
||||||
@@ -36,18 +35,58 @@ class _ScrollOverBuilderState extends State<ScrollOverBuilder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FloatingActionButtonExtendedBuilder extends StatelessWidget {
|
// class ProxiesActionsBuilder extends StatelessWidget {
|
||||||
final Widget Function(bool isExtend) builder;
|
// final Widget? child;
|
||||||
|
// final Widget Function(
|
||||||
|
// ProxiesActionsState state,
|
||||||
|
// Widget? child,
|
||||||
|
// ) builder;
|
||||||
|
//
|
||||||
|
// const ProxiesActionsBuilder({
|
||||||
|
// super.key,
|
||||||
|
// required this.child,
|
||||||
|
// required this.builder,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return Selector<AppState, ProxiesActionsState>(
|
||||||
|
// selector: (_, appState) => ProxiesActionsState(
|
||||||
|
// isCurrent: appState.currentLabel == "proxies",
|
||||||
|
// hasProvider: appState.providers.isNotEmpty,
|
||||||
|
// ),
|
||||||
|
// builder: (_, state, child) => builder(state, child),
|
||||||
|
// child: child,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
const FloatingActionButtonExtendedBuilder({super.key, required this.builder});
|
// class ActiveBuilder extends StatelessWidget {
|
||||||
|
// final String label;
|
||||||
@override
|
// final StateAndChildWidgetBuilder<bool> builder;
|
||||||
Widget build(BuildContext context) {
|
// final Widget? child;
|
||||||
final isExtended =
|
//
|
||||||
CommonScaffoldFabExtendedProvider.of(context)?.isExtended ?? true;
|
// const ActiveBuilder({
|
||||||
return builder(isExtended);
|
// super.key,
|
||||||
}
|
// required this.label,
|
||||||
}
|
// required this.builder,
|
||||||
|
// required this.child,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return Selector<AppState, bool>(
|
||||||
|
// selector: (_, appState) => appState.currentLabel == label,
|
||||||
|
// builder: (_, state, child) {
|
||||||
|
// return builder(
|
||||||
|
// state,
|
||||||
|
// child,
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// child: child,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
typedef StateWidgetBuilder<T> = Widget Function(T state);
|
typedef StateWidgetBuilder<T> = Widget Function(T state);
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import 'package:fl_clash/common/common.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'builder.dart';
|
|
||||||
|
|
||||||
class CommonFloatingActionButton extends StatelessWidget {
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
final Icon icon;
|
|
||||||
final String label;
|
|
||||||
|
|
||||||
const CommonFloatingActionButton({
|
|
||||||
super.key,
|
|
||||||
this.onPressed,
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Theme(
|
|
||||||
data: Theme.of(context).copyWith(
|
|
||||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
|
||||||
.copyWith(
|
|
||||||
extendedIconLabelSpacing: 0,
|
|
||||||
extendedPadding: EdgeInsets.all(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: FloatingActionButtonExtendedBuilder(
|
|
||||||
builder: (isExtended) {
|
|
||||||
return FloatingActionButton.extended(
|
|
||||||
heroTag: null,
|
|
||||||
icon: icon,
|
|
||||||
onPressed: onPressed,
|
|
||||||
isExtended: true,
|
|
||||||
label: AnimatedSize(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
duration: midDuration,
|
|
||||||
curve: Curves.easeOutBack,
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
duration: midDuration,
|
|
||||||
opacity: isExtended ? 1.0 : 0.4,
|
|
||||||
curve: Curves.linear,
|
|
||||||
child: isExtended
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: Text(label, softWrap: false),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,7 @@ class InfoHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
EdgeInsetsGeometry nextPadding = (padding ?? baseInfoEdgeInsets);
|
EdgeInsetsGeometry nextPadding = (padding ?? baseInfoEdgeInsets);
|
||||||
if (actions.isNotEmpty) {
|
if (actions.isNotEmpty) {
|
||||||
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.mAp));
|
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.ap));
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: nextPadding,
|
padding: nextPadding,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:fl_clash/common/common.dart';
|
import 'package:fl_clash/common/common.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
typedef WrapBuilder = Widget Function(Widget child);
|
typedef WrapBuilder = Widget Function(Widget child);
|
||||||
|
|
||||||
@@ -28,10 +27,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
|||||||
TextDirection? textDirection,
|
TextDirection? textDirection,
|
||||||
this.mainAxisExtent,
|
this.mainAxisExtent,
|
||||||
List<Widget>? children,
|
List<Widget>? children,
|
||||||
}) : crossAxisCount = crossAxisCount ?? 1,
|
}) : crossAxisCount = crossAxisCount ?? 1,
|
||||||
axisDirection = axisDirection ?? AxisDirection.down,
|
axisDirection = axisDirection ?? AxisDirection.down,
|
||||||
textDirection = textDirection ?? TextDirection.ltr,
|
textDirection = textDirection ?? TextDirection.ltr,
|
||||||
super(children: children ?? const []);
|
super(children: children ?? const []);
|
||||||
|
|
||||||
const Grid.baseGap({
|
const Grid.baseGap({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -43,15 +42,15 @@ class Grid extends MultiChildRenderObjectWidget {
|
|||||||
double? mainAxisExtent,
|
double? mainAxisExtent,
|
||||||
List<Widget>? children,
|
List<Widget>? children,
|
||||||
}) : this(
|
}) : this(
|
||||||
key: key,
|
key: key,
|
||||||
mainAxisSpacing: mainAxisSpacing,
|
mainAxisSpacing: mainAxisSpacing,
|
||||||
crossAxisSpacing: crossAxisSpacing,
|
crossAxisSpacing: crossAxisSpacing,
|
||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
axisDirection: axisDirection,
|
axisDirection: axisDirection,
|
||||||
textDirection: textDirection,
|
textDirection: textDirection,
|
||||||
mainAxisExtent: mainAxisExtent,
|
mainAxisExtent: mainAxisExtent,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
@@ -66,7 +65,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, RenderGrid renderObject) {
|
void updateRenderObject(
|
||||||
|
BuildContext context,
|
||||||
|
RenderGrid renderObject,
|
||||||
|
) {
|
||||||
renderObject
|
renderObject
|
||||||
..mainAxisSpacing = mainAxisSpacing
|
..mainAxisSpacing = mainAxisSpacing
|
||||||
..mainAxisExtent = mainAxisExtent
|
..mainAxisExtent = mainAxisExtent
|
||||||
@@ -88,12 +90,12 @@ class RenderGrid extends RenderBox
|
|||||||
required AxisDirection axisDirection,
|
required AxisDirection axisDirection,
|
||||||
required TextDirection textDirection,
|
required TextDirection textDirection,
|
||||||
double? mainAxisExtent,
|
double? mainAxisExtent,
|
||||||
}) : _crossAxisCount = crossAxisCount,
|
}) : _crossAxisCount = crossAxisCount,
|
||||||
_crossAxisSpacing = crossAxisSpacing,
|
_crossAxisSpacing = crossAxisSpacing,
|
||||||
_mainAxisSpacing = mainAxisSpacing,
|
_mainAxisSpacing = mainAxisSpacing,
|
||||||
_axisDirection = axisDirection,
|
_axisDirection = axisDirection,
|
||||||
_textDirection = textDirection,
|
_textDirection = textDirection,
|
||||||
_mainAxisExtent = mainAxisExtent;
|
_mainAxisExtent = mainAxisExtent;
|
||||||
|
|
||||||
int _crossAxisCount;
|
int _crossAxisCount;
|
||||||
|
|
||||||
@@ -212,10 +214,15 @@ class RenderGrid extends RenderBox
|
|||||||
GridParentData childParentData,
|
GridParentData childParentData,
|
||||||
int crossAxisCount,
|
int crossAxisCount,
|
||||||
) {
|
) {
|
||||||
return math.min(childParentData.crossAxisCellCount ?? 1, crossAxisCount);
|
return math.min(
|
||||||
|
childParentData.crossAxisCellCount ?? 1,
|
||||||
|
crossAxisCount,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Size _computeSize({required BoxConstraints constraints}) {
|
Size _computeSize({
|
||||||
|
required BoxConstraints constraints,
|
||||||
|
}) {
|
||||||
final crossAxisExtent = mainAxis == Axis.vertical
|
final crossAxisExtent = mainAxis == Axis.vertical
|
||||||
? constraints.maxWidth
|
? constraints.maxWidth
|
||||||
: constraints.maxHeight;
|
: constraints.maxHeight;
|
||||||
@@ -238,13 +245,11 @@ class RenderGrid extends RenderBox
|
|||||||
? BoxConstraints.tightFor(width: crossAxisExtent)
|
? BoxConstraints.tightFor(width: crossAxisExtent)
|
||||||
: BoxConstraints.tightFor(height: crossAxisExtent);
|
: BoxConstraints.tightFor(height: crossAxisExtent);
|
||||||
_layoutChild(child, childConstraints, parentUsesSize: true);
|
_layoutChild(child, childConstraints, parentUsesSize: true);
|
||||||
mainAxisExtent = mainAxis == Axis.vertical
|
mainAxisExtent =
|
||||||
? child.size.height
|
mainAxis == Axis.vertical ? child.size.height : child.size.width;
|
||||||
: child.size.width;
|
|
||||||
} else {
|
} else {
|
||||||
final mainAxisCellCount = childParentData.mainAxisCellCount ?? 1;
|
final mainAxisCellCount = childParentData.mainAxisCellCount ?? 1;
|
||||||
mainAxisExtent =
|
mainAxisExtent = (this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
||||||
(this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
|
||||||
mainAxisSpacing;
|
mainAxisSpacing;
|
||||||
childParentData.realMainAxisExtent = mainAxisExtent;
|
childParentData.realMainAxisExtent = mainAxisExtent;
|
||||||
final childSize = mainAxis == Axis.vertical
|
final childSize = mainAxis == Axis.vertical
|
||||||
@@ -260,7 +265,9 @@ class RenderGrid extends RenderBox
|
|||||||
? Offset(crossAxisOffset, mainAxisOffset)
|
? Offset(crossAxisOffset, mainAxisOffset)
|
||||||
: Offset(mainAxisOffset, crossAxisOffset);
|
: Offset(mainAxisOffset, crossAxisOffset);
|
||||||
childParentData.offset = offset;
|
childParentData.offset = offset;
|
||||||
|
|
||||||
final nextOffset = mainAxisOffset + mainAxisExtent + mainAxisSpacing;
|
final nextOffset = mainAxisOffset + mainAxisExtent + mainAxisSpacing;
|
||||||
|
|
||||||
for (int i = 0; i < crossAxisCellCount; i++) {
|
for (int i = 0; i < crossAxisCellCount; i++) {
|
||||||
offsets[origin.crossAxisIndex + i] = nextOffset;
|
offsets[origin.crossAxisIndex + i] = nextOffset;
|
||||||
}
|
}
|
||||||
@@ -274,8 +281,7 @@ class RenderGrid extends RenderBox
|
|||||||
final childParentData = _getParentData(child);
|
final childParentData = _getParentData(child);
|
||||||
final offset = childParentData.offset;
|
final offset = childParentData.offset;
|
||||||
final crossAxisOffset = offset.getCrossAxisOffset(mainAxis);
|
final crossAxisOffset = offset.getCrossAxisOffset(mainAxis);
|
||||||
final mainAxisOffset =
|
final mainAxisOffset = mainAxisExtent -
|
||||||
mainAxisExtent -
|
|
||||||
offset.getMainAxisOffset(mainAxis) -
|
offset.getMainAxisOffset(mainAxis) -
|
||||||
childParentData.realMainAxisExtent!;
|
childParentData.realMainAxisExtent!;
|
||||||
final newOffset = mainAxis == Axis.vertical
|
final newOffset = mainAxis == Axis.vertical
|
||||||
@@ -359,11 +365,15 @@ class GridItem extends ParentDataWidget<GridParentData> {
|
|||||||
@override
|
@override
|
||||||
Type get debugTypicalAncestorWidgetClass => GridItem;
|
Type get debugTypicalAncestorWidgetClass => GridItem;
|
||||||
|
|
||||||
GridItem wrap({required WrapBuilder builder}) {
|
GridItem wrap({
|
||||||
|
required WrapBuilder builder,
|
||||||
|
}) {
|
||||||
return GridItem(
|
return GridItem(
|
||||||
mainAxisCellCount: mainAxisCellCount,
|
mainAxisCellCount: mainAxisCellCount,
|
||||||
crossAxisCellCount: crossAxisCellCount,
|
crossAxisCellCount: crossAxisCellCount,
|
||||||
child: builder(child),
|
child: builder(
|
||||||
|
child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,13 +395,11 @@ _Origin _getOrigin(List<double> offsets, int crossAxisCount) {
|
|||||||
}
|
}
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int span = 0;
|
int span = 0;
|
||||||
for (
|
for (int j = 0;
|
||||||
int j = 0;
|
span < crossAxisCount &&
|
||||||
span < crossAxisCount &&
|
j < length &&
|
||||||
j < length &&
|
length - j >= crossAxisCount - span;
|
||||||
length - j >= crossAxisCount - span;
|
j++) {
|
||||||
j++
|
|
||||||
) {
|
|
||||||
if (offset.moreOrEqual(offsets[j])) {
|
if (offset.moreOrEqual(offsets[j])) {
|
||||||
span++;
|
span++;
|
||||||
if (span == crossAxisCount) {
|
if (span == crossAxisCount) {
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
|
||||||
final VoidCallback? backAction;
|
|
||||||
|
|
||||||
const CommonScaffoldBackActionProvider({
|
|
||||||
super.key,
|
|
||||||
required this.backAction,
|
|
||||||
required super.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
static CommonScaffoldBackActionProvider? of(BuildContext context) {
|
|
||||||
return context
|
|
||||||
.dependOnInheritedWidgetOfExactType<CommonScaffoldBackActionProvider>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(CommonScaffoldBackActionProvider oldWidget) =>
|
|
||||||
backAction != oldWidget.backAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CommonScaffoldFabExtendedProvider extends InheritedWidget {
|
|
||||||
final bool isExtended;
|
|
||||||
|
|
||||||
const CommonScaffoldFabExtendedProvider({
|
|
||||||
super.key,
|
|
||||||
required this.isExtended,
|
|
||||||
required super.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
static CommonScaffoldFabExtendedProvider? of(BuildContext context) {
|
|
||||||
return context
|
|
||||||
.dependOnInheritedWidgetOfExactType<
|
|
||||||
CommonScaffoldFabExtendedProvider
|
|
||||||
>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(CommonScaffoldFabExtendedProvider oldWidget) =>
|
|
||||||
isExtended != oldWidget.isExtended;
|
|
||||||
}
|
|
||||||
@@ -3,10 +3,8 @@ import 'package:fl_clash/enum/enum.dart';
|
|||||||
import 'package:fl_clash/models/models.dart';
|
import 'package:fl_clash/models/models.dart';
|
||||||
import 'package:fl_clash/widgets/pop_scope.dart';
|
import 'package:fl_clash/widgets/pop_scope.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
|
|
||||||
import 'chip.dart';
|
import 'chip.dart';
|
||||||
import 'inherited.dart';
|
|
||||||
|
|
||||||
typedef OnKeywordsUpdateCallback = void Function(List<String> keywords);
|
typedef OnKeywordsUpdateCallback = void Function(List<String> keywords);
|
||||||
|
|
||||||
@@ -49,8 +47,8 @@ class CommonScaffold extends StatefulWidget {
|
|||||||
|
|
||||||
class CommonScaffoldState extends State<CommonScaffold> {
|
class CommonScaffoldState extends State<CommonScaffold> {
|
||||||
late final ValueNotifier<AppBarState> _appBarState;
|
late final ValueNotifier<AppBarState> _appBarState;
|
||||||
|
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
|
||||||
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
|
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
|
||||||
final ValueNotifier<bool> _isFabExtendedNotifier = ValueNotifier(true);
|
|
||||||
final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);
|
final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);
|
||||||
final _textController = TextEditingController();
|
final _textController = TextEditingController();
|
||||||
|
|
||||||
@@ -85,6 +83,12 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
|||||||
_updateSearchState((state) => state?.copyWith(query: ''));
|
_updateSearchState((state) => state?.copyWith(query: ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set floatingActionButton(Widget? floatingActionButton) {
|
||||||
|
if (_floatingActionButton.value != floatingActionButton) {
|
||||||
|
_floatingActionButton.value = floatingActionButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildSearchingAppBarTheme(Widget child) {
|
Widget _buildSearchingAppBarTheme(Widget child) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
final ColorScheme colorScheme = theme.colorScheme;
|
final ColorScheme colorScheme = theme.colorScheme;
|
||||||
@@ -152,7 +156,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_appBarState.dispose();
|
_appBarState.dispose();
|
||||||
_textController.dispose();
|
_textController.dispose();
|
||||||
_isFabExtendedNotifier.dispose();
|
_floatingActionButton.dispose();
|
||||||
_loadingNotifier.dispose();
|
_loadingNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@@ -346,31 +350,17 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
|||||||
);
|
);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: _buildAppBar(backActionProvider?.backAction),
|
appBar: _buildAppBar(backActionProvider?.backAction),
|
||||||
body: NotificationListener<UserScrollNotification>(
|
body: body,
|
||||||
child: body,
|
|
||||||
onNotification: (notification) {
|
|
||||||
if (notification.direction == ScrollDirection.reverse) {
|
|
||||||
_isFabExtendedNotifier.value = false;
|
|
||||||
} else if (notification.direction == ScrollDirection.forward) {
|
|
||||||
_isFabExtendedNotifier.value = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
|
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
|
||||||
backgroundColor: widget.backgroundColor,
|
backgroundColor: widget.backgroundColor,
|
||||||
floatingActionButton: widget.floatingActionButton != null
|
floatingActionButton:
|
||||||
? ValueListenableBuilder<bool>(
|
widget.floatingActionButton ??
|
||||||
valueListenable: _isFabExtendedNotifier,
|
ValueListenableBuilder<Widget?>(
|
||||||
builder: (_, isExtended, child) {
|
valueListenable: _floatingActionButton,
|
||||||
return CommonScaffoldFabExtendedProvider(
|
builder: (_, value, _) {
|
||||||
isExtended: isExtended,
|
return value ?? SizedBox();
|
||||||
child: child!,
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
child: widget.floatingActionButton,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,6 +372,25 @@ List<Widget> genActions(List<Widget> actions, {double? space}) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||||
|
final VoidCallback? backAction;
|
||||||
|
|
||||||
|
const CommonScaffoldBackActionProvider({
|
||||||
|
super.key,
|
||||||
|
required this.backAction,
|
||||||
|
required super.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
static CommonScaffoldBackActionProvider? of(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.dependOnInheritedWidgetOfExactType<CommonScaffoldBackActionProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(CommonScaffoldBackActionProvider oldWidget) =>
|
||||||
|
backAction != oldWidget.backAction;
|
||||||
|
}
|
||||||
|
|
||||||
class BaseScaffold extends StatelessWidget {
|
class BaseScaffold extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Widget> actions;
|
final List<Widget> actions;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export 'activate_box.dart';
|
export 'activate_box.dart';
|
||||||
export 'animate_grid.dart';
|
export 'animate_grid.dart';
|
||||||
export 'builder.dart';
|
export 'builder.dart';
|
||||||
export 'button.dart';
|
|
||||||
export 'card.dart';
|
export 'card.dart';
|
||||||
export 'chip.dart';
|
export 'chip.dart';
|
||||||
export 'color_scheme_box.dart';
|
export 'color_scheme_box.dart';
|
||||||
@@ -14,7 +13,6 @@ export 'fade_box.dart';
|
|||||||
export 'float_layout.dart';
|
export 'float_layout.dart';
|
||||||
export 'grid.dart';
|
export 'grid.dart';
|
||||||
export 'icon.dart';
|
export 'icon.dart';
|
||||||
export 'inherited.dart';
|
|
||||||
export 'input.dart';
|
export 'input.dart';
|
||||||
export 'keep_scope.dart';
|
export 'keep_scope.dart';
|
||||||
export 'line_chart.dart';
|
export 'line_chart.dart';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: fl_clash
|
name: fl_clash
|
||||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.8.92+2026020201
|
version: 0.8.92+2026012502
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.8.0 <4.0.0'
|
sdk: '>=3.8.0 <4.0.0'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user