Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
dbd8fd89fe Add sqlite store
Optimize android quick action

Optimize backup and restore

Optimize more details
2026-01-26 01:31:32 +08:00
24 changed files with 219 additions and 280 deletions

View File

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

View File

@@ -476,6 +476,5 @@
"restoreFromFileDesc": "ファイルを介してデータを復元する",
"restoreOnlyConfig": "設定ファイルのみを復元する",
"restoreAllData": "すべてのデータを復元する",
"addProfile": "プロファイルを追加",
"delayTest": "遅延テスト"
"addProfile": "プロファイルを追加"
}

View File

@@ -484,6 +484,5 @@
"restoreFromFileDesc": "Восстановить данные из файла",
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
"restoreAllData": "Восстановить все данные",
"addProfile": "Добавить профиль",
"delayTest": "Тест задержки"
"addProfile": "Добавить профиль"
}

View File

@@ -476,6 +476,5 @@
"restoreFromFileDesc": "通过文件恢复数据",
"restoreOnlyConfig": "仅恢复配置文件",
"restoreAllData": "恢复所有数据",
"addProfile": "添加配置",
"delayTest": "延迟测试"
"addProfile": "添加配置"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.92+2026012602
version: 0.8.92+2026012504
environment:
sdk: '>=3.8.0 <4.0.0'