Compare commits
1 Commits
release/v0
...
v0.8.92-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbd8fd89fe |
@@ -475,6 +475,5 @@
|
||||
"restoreFromFileDesc": "Restore data via file",
|
||||
"restoreOnlyConfig": "Restore configuration files only",
|
||||
"restoreAllData": "Restore all data",
|
||||
"addProfile": "Add Profile",
|
||||
"delayTest": "Delay Test"
|
||||
"addProfile": "Add Profile"
|
||||
}
|
||||
@@ -476,6 +476,5 @@
|
||||
"restoreFromFileDesc": "ファイルを介してデータを復元する",
|
||||
"restoreOnlyConfig": "設定ファイルのみを復元する",
|
||||
"restoreAllData": "すべてのデータを復元する",
|
||||
"addProfile": "プロファイルを追加",
|
||||
"delayTest": "遅延テスト"
|
||||
"addProfile": "プロファイルを追加"
|
||||
}
|
||||
@@ -484,6 +484,5 @@
|
||||
"restoreFromFileDesc": "Восстановить данные из файла",
|
||||
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
|
||||
"restoreAllData": "Восстановить все данные",
|
||||
"addProfile": "Добавить профиль",
|
||||
"delayTest": "Тест задержки"
|
||||
"addProfile": "Добавить профиль"
|
||||
}
|
||||
@@ -476,6 +476,5 @@
|
||||
"restoreFromFileDesc": "通过文件恢复数据",
|
||||
"restoreOnlyConfig": "仅恢复配置文件",
|
||||
"restoreAllData": "恢复所有数据",
|
||||
"addProfile": "添加配置",
|
||||
"delayTest": "延迟测试"
|
||||
"addProfile": "添加配置"
|
||||
}
|
||||
|
||||
@@ -532,9 +532,6 @@ func handleDelFile(path string, result ActionResult) {
|
||||
}
|
||||
|
||||
func handleSetupConfig(bytes []byte) string {
|
||||
if !isInit {
|
||||
return "not initialized"
|
||||
}
|
||||
var params = defaultSetupParams()
|
||||
err := UnmarshalJson(bytes, params)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,14 +20,14 @@ const helperPort = 47890;
|
||||
const maxTextScale = 1.4;
|
||||
const minTextScale = 0.8;
|
||||
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16.mAp,
|
||||
horizontal: 16.mAp,
|
||||
vertical: 16.ap,
|
||||
horizontal: 16.ap,
|
||||
);
|
||||
final listHeaderPadding = EdgeInsets.only(
|
||||
left: 16.mAp,
|
||||
right: 8.mAp,
|
||||
top: 24.mAp,
|
||||
bottom: 8.mAp,
|
||||
left: 16.ap,
|
||||
right: 8.ap,
|
||||
top: 24.ap,
|
||||
bottom: 8.ap,
|
||||
);
|
||||
|
||||
const watchExecution = true;
|
||||
@@ -102,8 +102,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
|
||||
const defaultPrimaryColor = 0XFFD8C0C3;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
final space = 14.mAp;
|
||||
return max(lines * (80.ap + space) - space, 0);
|
||||
return max(lines * 80 + (lines - 1) * 16, 0).ap;
|
||||
}
|
||||
|
||||
const maxLength = 1000;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -22,10 +20,6 @@ extension NumExt on num {
|
||||
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 {
|
||||
final units = TrafficUnit.values;
|
||||
var size = toDouble();
|
||||
@@ -57,7 +51,7 @@ extension NumExt on num {
|
||||
|
||||
extension DoubleExt on double {
|
||||
bool moreOrEqual(double value) {
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 1;
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -653,14 +653,9 @@ extension SetupControllerExt on AppController {
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
}) async {
|
||||
if (!force && !await needSetup()) {
|
||||
return;
|
||||
}
|
||||
await loadingRun(
|
||||
() async {
|
||||
await _setupConfig(preloadInvoke);
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
await _applyProfile(force, preloadInvoke);
|
||||
},
|
||||
silence: true,
|
||||
tag: !silence ? LoadingTag.proxies : null,
|
||||
@@ -730,7 +725,13 @@ extension SetupControllerExt on AppController {
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> _setupConfig([VoidCallback? preloadInvoke]) async {
|
||||
Future<void> _setupConfig([
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
]) async {
|
||||
if (!force && !await needSetup()) {
|
||||
return;
|
||||
}
|
||||
commonPrint.log('setup ===>');
|
||||
var profile = _ref.read(currentProfileProvider);
|
||||
final nextProfile = await profile?.checkAndUpdateAndCopy();
|
||||
@@ -749,6 +750,9 @@ extension SetupControllerExt on AppController {
|
||||
globalState.lastSetupState = setupState;
|
||||
if (system.isAndroid) {
|
||||
globalState.lastVpnState = _ref.read(vpnStateProvider);
|
||||
}
|
||||
|
||||
if (system.isAndroid) {
|
||||
preferences.saveShareState(this.sharedState);
|
||||
}
|
||||
final config = await getProfile(
|
||||
@@ -768,6 +772,15 @@ extension SetupControllerExt on AppController {
|
||||
}
|
||||
addCheckIp();
|
||||
}
|
||||
|
||||
Future _applyProfile([
|
||||
bool force = false,
|
||||
VoidCallback? preloadInvoke,
|
||||
]) async {
|
||||
await _setupConfig(force, preloadInvoke);
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
}
|
||||
}
|
||||
|
||||
extension CoreControllerExt on AppController {
|
||||
@@ -863,9 +876,9 @@ extension SystemControllerExt on AppController {
|
||||
if (needSave) preferences.saveConfig(config),
|
||||
if (macOS != null) macOS!.updateDns(true),
|
||||
if (proxy != null) proxy!.stopProxy(),
|
||||
coreController.destroy(),
|
||||
if (tray != null) tray!.destroy(),
|
||||
]);
|
||||
await coreController.destroy();
|
||||
commonPrint.log('exit');
|
||||
} finally {
|
||||
system.exit();
|
||||
|
||||
@@ -86,7 +86,9 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
try {
|
||||
await completer.future.timeout(const Duration(seconds: 10));
|
||||
if (!completer.isCompleted) {
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
commonPrint.log(
|
||||
'Invoke pre ${method.name} timeout $e',
|
||||
|
||||
@@ -270,7 +270,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("Default"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("Delay Test"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -205,7 +205,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("デフォルト"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("遅延テスト"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("削除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -277,7 +277,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("По умолчанию"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("Тест задержки"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -185,7 +185,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"defaultText": MessageLookupByLibrary.simpleMessage("默认"),
|
||||
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
||||
"delayTest": MessageLookupByLibrary.simpleMessage("延迟测试"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
|
||||
@@ -3743,11 +3743,6 @@ class AppLocalizations {
|
||||
String get addProfile {
|
||||
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> {
|
||||
|
||||
@@ -228,7 +228,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
Widget build(BuildContext context) {
|
||||
final dashboardState = ref.watch(dashboardStateProvider);
|
||||
final columns = max(4 * ((dashboardState.contentWidth / 280).ceil()), 8);
|
||||
final spacing = 14.mAp;
|
||||
final spacing = 14.ap;
|
||||
final children = [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
|
||||
@@ -41,54 +41,45 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
final color = context.colorScheme.onSurfaceVariant.opacity80;
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
child: RepaintBoundary(
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final traffics = ref.watch(trafficsProvider).list;
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(bottom: 0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: InfoHeader(
|
||||
padding: EdgeInsets.zero,
|
||||
info: Info(
|
||||
label: appLocalizations.networkSpeed,
|
||||
iconData: Icons.speed_sharp,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
_getLastTraffic(traffics).speedText,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.networkSpeed,
|
||||
iconData: Icons.speed_sharp,
|
||||
),
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final traffics = ref.watch(trafficsProvider).list;
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
).copyWith(bottom: 0, left: 0, right: 0),
|
||||
child: LineChart(
|
||||
gradient: true,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
points: _getPoints(traffics),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
16,
|
||||
).copyWith(bottom: 0, left: 0, right: 0),
|
||||
child: LineChart(
|
||||
gradient: true,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
points: _getPoints(traffics),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Transform.translate(
|
||||
offset: Offset(-16, -20),
|
||||
child: Text(
|
||||
_getLastTraffic(traffics).speedText,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -23,7 +23,6 @@ class ProfilesView extends StatefulWidget {
|
||||
|
||||
class _ProfilesViewState extends State<ProfilesView> {
|
||||
Function? applyConfigDebounce;
|
||||
bool _isUpdating = false;
|
||||
|
||||
void _handleShowAddExtendPage() {
|
||||
showExtend(
|
||||
@@ -41,10 +40,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
}
|
||||
|
||||
Future<void> _updateProfiles(List<Profile> profiles) async {
|
||||
if (_isUpdating == true) {
|
||||
return;
|
||||
}
|
||||
_isUpdating = true;
|
||||
final List<UpdatingMessage> messages = [];
|
||||
final updateProfiles = profiles.map<Future>((profile) async {
|
||||
if (profile.type == ProfileType.file) return;
|
||||
@@ -60,7 +55,6 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
if (messages.isNotEmpty) {
|
||||
globalState.showAllUpdatingMessagesDialog(messages);
|
||||
}
|
||||
_isUpdating = false;
|
||||
}
|
||||
|
||||
List<Widget> _buildActions(List<Profile> profiles) {
|
||||
@@ -92,10 +86,10 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
}
|
||||
|
||||
Widget _buildFAB() {
|
||||
return CommonFloatingActionButton(
|
||||
return FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
icon: const Icon(Icons.add),
|
||||
label: context.appLocalizations.addProfile,
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,7 +99,7 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
builder: (_, ref, _) {
|
||||
final isLoading = ref.watch(loadingProvider(LoadingTag.profiles));
|
||||
final state = ref.watch(profilesStateProvider);
|
||||
final spacing = 14.mAp;
|
||||
final spacing = 14.ap;
|
||||
return CommonScaffold(
|
||||
isLoading: isLoading,
|
||||
title: appLocalizations.profiles,
|
||||
|
||||
@@ -410,15 +410,19 @@ class _DelayTestButtonState extends State<DelayTestButton>
|
||||
return AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: FadeTransition(
|
||||
opacity: _animation,
|
||||
child: ScaleTransition(scale: _animation, child: child),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: CommonFloatingActionButton(
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _healthcheck,
|
||||
label: appLocalizations.delayTest,
|
||||
icon: const Icon(Icons.network_ping),
|
||||
child: const Icon(Icons.network_ping),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScrollOverBuilder extends StatefulWidget {
|
||||
@@ -36,18 +35,58 @@ class _ScrollOverBuilderState extends State<ScrollOverBuilder> {
|
||||
}
|
||||
}
|
||||
|
||||
class FloatingActionButtonExtendedBuilder extends StatelessWidget {
|
||||
final Widget Function(bool isExtend) builder;
|
||||
// class ProxiesActionsBuilder extends StatelessWidget {
|
||||
// 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});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isExtended =
|
||||
CommonScaffoldFabExtendedProvider.of(context)?.isExtended ?? false;
|
||||
return builder(isExtended);
|
||||
}
|
||||
}
|
||||
// class ActiveBuilder extends StatelessWidget {
|
||||
// final String label;
|
||||
// final StateAndChildWidgetBuilder<bool> builder;
|
||||
// final Widget? child;
|
||||
//
|
||||
// const ActiveBuilder({
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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.0,
|
||||
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) {
|
||||
EdgeInsetsGeometry nextPadding = (padding ?? baseInfoEdgeInsets);
|
||||
if (actions.isNotEmpty) {
|
||||
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.mAp));
|
||||
nextPadding = nextPadding.subtract(EdgeInsets.symmetric(vertical: 8.ap));
|
||||
}
|
||||
return Padding(
|
||||
padding: nextPadding,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
typedef WrapBuilder = Widget Function(Widget child);
|
||||
|
||||
@@ -28,10 +27,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
TextDirection? textDirection,
|
||||
this.mainAxisExtent,
|
||||
List<Widget>? children,
|
||||
}) : crossAxisCount = crossAxisCount ?? 1,
|
||||
axisDirection = axisDirection ?? AxisDirection.down,
|
||||
textDirection = textDirection ?? TextDirection.ltr,
|
||||
super(children: children ?? const []);
|
||||
}) : crossAxisCount = crossAxisCount ?? 1,
|
||||
axisDirection = axisDirection ?? AxisDirection.down,
|
||||
textDirection = textDirection ?? TextDirection.ltr,
|
||||
super(children: children ?? const []);
|
||||
|
||||
const Grid.baseGap({
|
||||
Key? key,
|
||||
@@ -43,15 +42,15 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
double? mainAxisExtent,
|
||||
List<Widget>? children,
|
||||
}) : this(
|
||||
key: key,
|
||||
mainAxisSpacing: mainAxisSpacing,
|
||||
crossAxisSpacing: crossAxisSpacing,
|
||||
crossAxisCount: crossAxisCount,
|
||||
axisDirection: axisDirection,
|
||||
textDirection: textDirection,
|
||||
mainAxisExtent: mainAxisExtent,
|
||||
children: children,
|
||||
);
|
||||
key: key,
|
||||
mainAxisSpacing: mainAxisSpacing,
|
||||
crossAxisSpacing: crossAxisSpacing,
|
||||
crossAxisCount: crossAxisCount,
|
||||
axisDirection: axisDirection,
|
||||
textDirection: textDirection,
|
||||
mainAxisExtent: mainAxisExtent,
|
||||
children: children,
|
||||
);
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
@@ -66,7 +65,10 @@ class Grid extends MultiChildRenderObjectWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderGrid renderObject) {
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
RenderGrid renderObject,
|
||||
) {
|
||||
renderObject
|
||||
..mainAxisSpacing = mainAxisSpacing
|
||||
..mainAxisExtent = mainAxisExtent
|
||||
@@ -88,12 +90,12 @@ class RenderGrid extends RenderBox
|
||||
required AxisDirection axisDirection,
|
||||
required TextDirection textDirection,
|
||||
double? mainAxisExtent,
|
||||
}) : _crossAxisCount = crossAxisCount,
|
||||
_crossAxisSpacing = crossAxisSpacing,
|
||||
_mainAxisSpacing = mainAxisSpacing,
|
||||
_axisDirection = axisDirection,
|
||||
_textDirection = textDirection,
|
||||
_mainAxisExtent = mainAxisExtent;
|
||||
}) : _crossAxisCount = crossAxisCount,
|
||||
_crossAxisSpacing = crossAxisSpacing,
|
||||
_mainAxisSpacing = mainAxisSpacing,
|
||||
_axisDirection = axisDirection,
|
||||
_textDirection = textDirection,
|
||||
_mainAxisExtent = mainAxisExtent;
|
||||
|
||||
int _crossAxisCount;
|
||||
|
||||
@@ -212,10 +214,15 @@ class RenderGrid extends RenderBox
|
||||
GridParentData childParentData,
|
||||
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
|
||||
? constraints.maxWidth
|
||||
: constraints.maxHeight;
|
||||
@@ -238,13 +245,11 @@ class RenderGrid extends RenderBox
|
||||
? BoxConstraints.tightFor(width: crossAxisExtent)
|
||||
: BoxConstraints.tightFor(height: crossAxisExtent);
|
||||
_layoutChild(child, childConstraints, parentUsesSize: true);
|
||||
mainAxisExtent = mainAxis == Axis.vertical
|
||||
? child.size.height
|
||||
: child.size.width;
|
||||
mainAxisExtent =
|
||||
mainAxis == Axis.vertical ? child.size.height : child.size.width;
|
||||
} else {
|
||||
final mainAxisCellCount = childParentData.mainAxisCellCount ?? 1;
|
||||
mainAxisExtent =
|
||||
(this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
||||
mainAxisExtent = (this.mainAxisExtent ?? stride) * mainAxisCellCount -
|
||||
mainAxisSpacing;
|
||||
childParentData.realMainAxisExtent = mainAxisExtent;
|
||||
final childSize = mainAxis == Axis.vertical
|
||||
@@ -260,7 +265,9 @@ class RenderGrid extends RenderBox
|
||||
? Offset(crossAxisOffset, mainAxisOffset)
|
||||
: Offset(mainAxisOffset, crossAxisOffset);
|
||||
childParentData.offset = offset;
|
||||
|
||||
final nextOffset = mainAxisOffset + mainAxisExtent + mainAxisSpacing;
|
||||
|
||||
for (int i = 0; i < crossAxisCellCount; i++) {
|
||||
offsets[origin.crossAxisIndex + i] = nextOffset;
|
||||
}
|
||||
@@ -274,8 +281,7 @@ class RenderGrid extends RenderBox
|
||||
final childParentData = _getParentData(child);
|
||||
final offset = childParentData.offset;
|
||||
final crossAxisOffset = offset.getCrossAxisOffset(mainAxis);
|
||||
final mainAxisOffset =
|
||||
mainAxisExtent -
|
||||
final mainAxisOffset = mainAxisExtent -
|
||||
offset.getMainAxisOffset(mainAxis) -
|
||||
childParentData.realMainAxisExtent!;
|
||||
final newOffset = mainAxis == Axis.vertical
|
||||
@@ -359,11 +365,15 @@ class GridItem extends ParentDataWidget<GridParentData> {
|
||||
@override
|
||||
Type get debugTypicalAncestorWidgetClass => GridItem;
|
||||
|
||||
GridItem wrap({required WrapBuilder builder}) {
|
||||
GridItem wrap({
|
||||
required WrapBuilder builder,
|
||||
}) {
|
||||
return GridItem(
|
||||
mainAxisCellCount: mainAxisCellCount,
|
||||
crossAxisCellCount: crossAxisCellCount,
|
||||
child: builder(child),
|
||||
child: builder(
|
||||
child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -385,13 +395,11 @@ _Origin _getOrigin(List<double> offsets, int crossAxisCount) {
|
||||
}
|
||||
int start = 0;
|
||||
int span = 0;
|
||||
for (
|
||||
int j = 0;
|
||||
span < crossAxisCount &&
|
||||
j < length &&
|
||||
length - j >= crossAxisCount - span;
|
||||
j++
|
||||
) {
|
||||
for (int j = 0;
|
||||
span < crossAxisCount &&
|
||||
j < length &&
|
||||
length - j >= crossAxisCount - span;
|
||||
j++) {
|
||||
if (offset.moreOrEqual(offsets[j])) {
|
||||
span++;
|
||||
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/widgets/pop_scope.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
import 'chip.dart';
|
||||
import 'inherited.dart';
|
||||
|
||||
typedef OnKeywordsUpdateCallback = void Function(List<String> keywords);
|
||||
|
||||
@@ -49,8 +47,8 @@ class CommonScaffold extends StatefulWidget {
|
||||
|
||||
class CommonScaffoldState extends State<CommonScaffold> {
|
||||
late final ValueNotifier<AppBarState> _appBarState;
|
||||
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
|
||||
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _isFabExtendedNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);
|
||||
final _textController = TextEditingController();
|
||||
|
||||
@@ -85,6 +83,12 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
_updateSearchState((state) => state?.copyWith(query: ''));
|
||||
}
|
||||
|
||||
set floatingActionButton(Widget? floatingActionButton) {
|
||||
if (_floatingActionButton.value != floatingActionButton) {
|
||||
_floatingActionButton.value = floatingActionButton;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSearchingAppBarTheme(Widget child) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
@@ -152,7 +156,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
void dispose() {
|
||||
_appBarState.dispose();
|
||||
_textController.dispose();
|
||||
_isFabExtendedNotifier.dispose();
|
||||
_floatingActionButton.dispose();
|
||||
_loadingNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -346,31 +350,17 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(backActionProvider?.backAction),
|
||||
body: NotificationListener<UserScrollNotification>(
|
||||
child: body,
|
||||
onNotification: (notification) {
|
||||
if (notification.direction == ScrollDirection.reverse) {
|
||||
_isFabExtendedNotifier.value = false;
|
||||
} else if (notification.direction == ScrollDirection.forward) {
|
||||
_isFabExtendedNotifier.value = true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
),
|
||||
body: body,
|
||||
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
floatingActionButton: widget.floatingActionButton != null
|
||||
? ValueListenableBuilder<bool>(
|
||||
valueListenable: _isFabExtendedNotifier,
|
||||
builder: (_, isExtended, child) {
|
||||
return CommonScaffoldFabExtendedProvider(
|
||||
isExtended: isExtended,
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
child: widget.floatingActionButton,
|
||||
)
|
||||
: null,
|
||||
floatingActionButton:
|
||||
widget.floatingActionButton ??
|
||||
ValueListenableBuilder<Widget?>(
|
||||
valueListenable: _floatingActionButton,
|
||||
builder: (_, value, _) {
|
||||
return value ?? SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
final String title;
|
||||
final List<Widget> actions;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export 'activate_box.dart';
|
||||
export 'animate_grid.dart';
|
||||
export 'builder.dart';
|
||||
export 'button.dart';
|
||||
export 'card.dart';
|
||||
export 'chip.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
@@ -14,7 +13,6 @@ export 'fade_box.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'grid.dart';
|
||||
export 'icon.dart';
|
||||
export 'inherited.dart';
|
||||
export 'input.dart';
|
||||
export 'keep_scope.dart';
|
||||
export 'line_chart.dart';
|
||||
|
||||
@@ -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.92+2026012701
|
||||
version: 0.8.92+2026012504
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user