Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
e7f53cfaae Support override script
Support proxies search

Support svg display

Optimize config persistence

Add some scenes auto close connections

Update core

Optimize more details
2025-06-06 10:42:57 +08:00
17 changed files with 422 additions and 322 deletions

View File

@@ -214,10 +214,7 @@ class ClashCore {
final profilePath = await appPath.getProfilePath(id);
final res = await clashInterface.getConfig(profilePath);
if (res.isSuccess) {
final data = (res.data ?? {}) as Map<String, dynamic>;
data["rules"] = data["rule"];
data.remove("rule");
return data;
return res.data as Map<String, dynamic>;
} else {
throw res.message;
}

View File

@@ -249,8 +249,6 @@ class ClashLibHandler {
return {};
}
final config = json.decode(configString);
config["rules"] = config["rule"];
config.remove("rule");
malloc.free(pathChar);
clashFFI.freeCString(configRaw);
return config;

View File

@@ -41,6 +41,11 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
},
fireImmediately: true,
);
ref.listenManual(configStateProvider, (prev, next) {
if (prev != next) {
globalState.appController.savePreferencesDebounce();
}
});
}
@override

View File

@@ -102,9 +102,10 @@ class _WindowContainerState extends ConsumerState<WindowManager>
}
@override
Future<void> onTaskbarCreated() async {
// globalState.appController.updateTray(true);
super.onTaskbarCreated();
void onWindowRestore() {
commonPrint.log("restore");
render?.resume();
super.onWindowRestore();
}
@override

View File

@@ -285,6 +285,10 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
currentId: nextId,
);
}
isExits(String label) {
return state.scripts.indexWhere((item) => item.label == label) != -1;
}
}
@riverpod

View File

@@ -178,7 +178,7 @@ final proxiesStyleSettingProvider =
);
typedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;
String _$scriptStateHash() => r'16d669009ffb233d95b2cb206cf771342ebc1cc1';
String _$scriptStateHash() => r'884581c71fd5afa3c9d34f31625d967cf561cdbe';
/// See also [ScriptState].
@ProviderFor(ScriptState)

View File

@@ -6,6 +6,22 @@ part of '../state.dart';
// RiverpodGenerator
// **************************************************************************
String _$configStateHash() => r'1f4ea3cc8f6461ba734e7e0c5d7295bfa4fd5afb';
/// See also [configState].
@ProviderFor(configState)
final configStateProvider = AutoDisposeProvider<Config>.internal(
configState,
name: r'configStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$configStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ConfigStateRef = AutoDisposeProviderRef<Config>;
String _$currentGroupsStateHash() =>
r'6222c006e1970e7435268d32903b9019cf1a4351';

View File

@@ -12,6 +12,38 @@ import 'config.dart';
part 'generated/state.g.dart';
@riverpod
Config configState(Ref ref) {
final themeProps = ref.watch(themeSettingProvider);
final patchClashConfig = ref.watch(patchClashConfigProvider);
final appSetting = ref.watch(appSettingProvider);
final profiles = ref.watch(profilesProvider);
final currentProfileId = ref.watch(currentProfileIdProvider);
final overrideDns = ref.watch(overrideDnsProvider);
final networkProps = ref.watch(networkSettingProvider);
final vpnProps = ref.watch(vpnSettingProvider);
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
final scriptProps = ref.watch(scriptStateProvider);
final hotKeyActions = ref.watch(hotKeyActionsProvider);
final dav = ref.watch(appDAVSettingProvider);
final windowProps = ref.watch(windowSettingProvider);
return Config(
dav: dav,
windowProps: windowProps,
hotKeyActions: hotKeyActions,
scriptProps: scriptProps,
proxiesStyle: proxiesStyle,
vpnProps: vpnProps,
networkProps: networkProps,
overrideDns: overrideDns,
currentProfileId: currentProfileId,
profiles: profiles,
appSetting: appSetting,
themeProps: themeProps,
patchClashConfig: patchClashConfig,
);
}
@riverpod
GroupsState currentGroupsState(Ref ref) {
final mode =

View File

@@ -313,10 +313,7 @@ class GlobalState {
return {};
}
final profileId = profile.id;
final configMap = await switch (clashLibHandler != null) {
true => clashLibHandler!.getConfig(profileId),
false => clashCore.getConfig(profileId),
};
final configMap = await getProfileConfig(profileId);
final rawConfig = await handleEvaluate(configMap);
final routeAddress =
config.networkProps.routeMode == RouteMode.bypassPrivate
@@ -443,6 +440,16 @@ class GlobalState {
return rawConfig;
}
Future<Map<String, dynamic>> getProfileConfig(String profileId) async {
final configMap = await switch (clashLibHandler != null) {
true => clashLibHandler!.getConfig(profileId),
false => clashCore.getConfig(profileId),
};
configMap["rules"] = configMap["rule"];
configMap.remove("rule");
return configMap;
}
Future<Map<String, dynamic>> handleEvaluate(
Map<String, dynamic> config,
) async {

View File

@@ -47,15 +47,6 @@ class AboutView extends StatelessWidget {
_checkUpdate(context);
},
),
// ListItem(
// title: Text(appLocalizations.contactMe),
// onTap: () {
// globalState.showMessage(
// title: appLocalizations.contactMe,
// message: TextSpan(text: "chen08209@gmail.com"),
// );
// },
// ),
ListItem(
title: const Text("Telegram"),
onTap: () {

View File

@@ -1,5 +1,6 @@
import 'dart:math';
import 'package:defer_pointer/defer_pointer.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/providers.dart';
@@ -9,6 +10,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'widgets/start_button.dart';
typedef _IsEditWidgetBuilder = Widget Function(bool isEdit);
class DashboardView extends ConsumerStatefulWidget {
const DashboardView({super.key});
@@ -18,6 +21,8 @@ class DashboardView extends ConsumerStatefulWidget {
class _DashboardViewState extends ConsumerState<DashboardView> with PageMixin {
final key = GlobalKey<SuperGridState>();
final _isEditNotifier = ValueNotifier<bool>(false);
final _addedWidgetsNotifier = ValueNotifier<List<GridItem>>([]);
@override
initState() {
@@ -33,115 +38,282 @@ class _DashboardViewState extends ConsumerState<DashboardView> with PageMixin {
return super.initState();
}
@override
dispose() {
_isEditNotifier.dispose();
super.dispose();
}
@override
Widget? get floatingActionButton => const StartButton();
Widget _buildIsEdit(_IsEditWidgetBuilder builder) {
return ValueListenableBuilder(
valueListenable: _isEditNotifier,
builder: (_, isEdit, ___) {
return builder(isEdit);
},
);
}
@override
List<Widget> get actions => [
ValueListenableBuilder(
valueListenable: key.currentState!.addedChildrenNotifier,
builder: (_, addedChildren, child) {
return ValueListenableBuilder(
valueListenable: key.currentState!.isEditNotifier,
builder: (_, isEdit, child) {
if (!isEdit || addedChildren.isEmpty) {
return Container();
}
return child!;
},
child: child,
);
},
child: IconButton(
onPressed: () {
key.currentState!.showAddModal();
},
icon: Icon(
Icons.add_circle,
),
),
),
_buildIsEdit((isEdit) {
return isEdit
? ValueListenableBuilder(
valueListenable: _addedWidgetsNotifier,
builder: (_, addedChildren, child) {
if (addedChildren.isEmpty) {
return Container();
}
return child!;
},
child: IconButton(
onPressed: () {
_showAddWidgetsModal();
},
icon: Icon(
Icons.add_circle,
),
),
)
: SizedBox();
}),
IconButton(
icon: ValueListenableBuilder(
valueListenable: key.currentState!.isEditNotifier,
builder: (_, isEdit, ___) {
return isEdit
? SystemBackBlock(
child: CommonPopScope(
child: Icon(Icons.save),
onPop: () {
key.currentState!.isEditNotifier.value =
!key.currentState!.isEditNotifier.value;
return false;
},
),
)
: Icon(
Icons.edit,
);
},
),
onPressed: () {
key.currentState!.isEditNotifier.value =
!key.currentState!.isEditNotifier.value;
},
icon: _buildIsEdit((isEdit) {
return isEdit
? Icon(Icons.save)
: Icon(
Icons.edit,
);
}),
onPressed: _handleUpdateIsEdit,
),
];
_handleSave(List<GridItem> girdItems, WidgetRef ref) {
final dashboardWidgets = girdItems
.map(
(item) => DashboardWidget.getDashboardWidget(item),
)
.toList();
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(dashboardWidgets: dashboardWidgets),
_showAddWidgetsModal() {
showSheet(
builder: (_, type) {
return ValueListenableBuilder(
valueListenable: _addedWidgetsNotifier,
builder: (_, value, __) {
return AdaptiveSheetScaffold(
type: type,
body: _AddDashboardWidgetModal(
items: value,
onAdd: (gridItem) {
key.currentState?.handleAdd(gridItem);
},
),
title: appLocalizations.add,
);
},
);
},
context: context,
);
}
_handleUpdateIsEdit() {
if (_isEditNotifier.value == true) {
_handleSave();
}
_isEditNotifier.value = !_isEditNotifier.value;
}
_handleSave() {
final children = key.currentState?.children;
if (children == null) {
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
final dashboardWidgets = children
.map(
(item) => DashboardWidget.getDashboardWidget(item),
)
.toList();
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(dashboardWidgets: dashboardWidgets),
);
});
}
@override
Widget build(BuildContext context) {
final dashboardState = ref.watch(dashboardStateProvider);
final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);
final spacing = 16.ap;
final children = [
...dashboardState.dashboardWidgets
.where(
(item) => item.platforms.contains(
SupportPlatform.currentPlatform,
),
)
.map(
(item) => item.widget,
),
];
WidgetsBinding.instance.addPostFrameCallback((_) {
_addedWidgetsNotifier.value = DashboardWidget.values
.where(
(item) =>
!children.contains(item.widget) &&
item.platforms.contains(
SupportPlatform.currentPlatform,
),
)
.map((item) => item.widget)
.toList();
});
return Align(
alignment: Alignment.topCenter,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16).copyWith(
bottom: 88,
),
child: SuperGrid(
key: key,
crossAxisCount: columns,
crossAxisSpacing: 16.ap,
mainAxisSpacing: 16.ap,
children: [
...dashboardState.dashboardWidgets
.where(
(item) => item.platforms.contains(
SupportPlatform.currentPlatform,
),
)
.map(
(item) => item.widget,
),
],
onSave: (girdItems) {
_handleSave(girdItems, ref);
},
addedItemsBuilder: (girdItems) {
return DashboardWidget.values
.where(
(item) =>
!girdItems.contains(item.widget) &&
item.platforms.contains(
SupportPlatform.currentPlatform,
padding: const EdgeInsets.all(16).copyWith(
bottom: 88,
),
child: _buildIsEdit((isEdit) {
return isEdit
? SystemBackBlock(
child: CommonPopScope(
child: SuperGrid(
key: key,
crossAxisCount: columns,
crossAxisSpacing: spacing,
mainAxisSpacing: spacing,
children: [
...dashboardState.dashboardWidgets
.where(
(item) => item.platforms.contains(
SupportPlatform.currentPlatform,
),
)
.map(
(item) => item.widget,
),
],
onUpdate: () {
_handleSave();
},
),
)
.map((item) => item.widget)
.toList();
},
onPop: () {
_handleUpdateIsEdit();
return false;
},
),
)
: Grid(
crossAxisCount: columns,
crossAxisSpacing: spacing,
mainAxisSpacing: spacing,
children: children,
);
})),
);
}
}
class _AddDashboardWidgetModal extends StatelessWidget {
final List<GridItem> items;
final Function(GridItem item) onAdd;
const _AddDashboardWidgetModal({
required this.items,
required this.onAdd,
});
@override
Widget build(BuildContext context) {
return DeferredPointerHandler(
child: SingleChildScrollView(
padding: EdgeInsets.all(
16,
),
child: Grid(
crossAxisCount: 8,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: items
.map(
(item) => item.wrap(
builder: (child) {
return _AddedContainer(
onAdd: () {
onAdd(item);
},
child: child,
);
},
),
)
.toList(),
),
),
);
}
}
class _AddedContainer extends StatefulWidget {
final Widget child;
final VoidCallback onAdd;
const _AddedContainer({
required this.child,
required this.onAdd,
});
@override
State<_AddedContainer> createState() => _AddedContainerState();
}
class _AddedContainerState extends State<_AddedContainer> {
@override
void initState() {
super.initState();
}
@override
void didUpdateWidget(_AddedContainer oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.child != widget.child) {}
}
_handleAdd() async {
widget.onAdd();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
ActivateBox(
child: widget.child,
),
Positioned(
top: -8,
right: -8,
child: DeferPointer(
child: SizedBox(
width: 24,
height: 24,
child: IconButton.filled(
iconSize: 20,
padding: EdgeInsets.all(2),
onPressed: _handleAdd,
icon: Icon(
Icons.add,
),
),
),
),
)
],
);
}
}

View File

@@ -136,12 +136,9 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
return appLocalizations.emptyTip(appLocalizations.name);
}
if (value != script?.label) {
final index = ref
.read(scriptStateProvider.select((state) => state.scripts))
.indexWhere(
(item) => item.label == value,
);
if (index != -1) {
final isExits =
ref.read(scriptStateProvider.notifier).isExits(value);
if (isExits) {
return appLocalizations.existsTip(
appLocalizations.name,
);
@@ -156,6 +153,18 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
}
newScript = newScript.copyWith(label: res);
}
if (newScript.label != script?.label) {
final isExits =
ref.read(scriptStateProvider.notifier).isExits(newScript.label);
if (isExits) {
globalState.showMessage(
message: TextSpan(
text: appLocalizations.existsTip(appLocalizations.name),
),
);
return;
}
}
ref.read(scriptStateProvider.notifier).setScript(newScript);
if (mounted) {
Navigator.of(context).pop();

View File

@@ -7,7 +7,6 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/widgets/activate_box.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/grid.dart';
import 'package:fl_clash/widgets/sheet.dart';
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
@@ -18,8 +17,7 @@ class SuperGrid extends StatefulWidget {
final double mainAxisSpacing;
final double crossAxisSpacing;
final int crossAxisCount;
final void Function(List<GridItem> newChildren)? onSave;
final List<GridItem> Function(List<GridItem> newChildren)? addedItemsBuilder;
final VoidCallback? onUpdate;
const SuperGrid({
super.key,
@@ -27,8 +25,7 @@ class SuperGrid extends StatefulWidget {
this.crossAxisCount = 1,
this.mainAxisSpacing = 0,
this.crossAxisSpacing = 0,
this.onSave,
this.addedItemsBuilder,
this.onUpdate,
});
@override
@@ -37,7 +34,7 @@ class SuperGrid extends StatefulWidget {
class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
final ValueNotifier<List<GridItem>> _childrenNotifier = ValueNotifier([]);
final ValueNotifier<List<GridItem>> addedChildrenNotifier = ValueNotifier([]);
List<GridItem> children = [];
int get length => _childrenNotifier.value.length;
List<int> _tempIndexList = [];
@@ -49,8 +46,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
List<Offset> _offsets = [];
Offset _parentOffset = Offset.zero;
EdgeDraggingAutoScroller? _edgeDraggingAutoScroller;
final ValueNotifier<bool> isEditNotifier = ValueNotifier(false);
Map<int, Tween<Offset>> _transformTweenMap = {};
final ValueNotifier<bool> _animating = ValueNotifier(false);
@@ -95,35 +90,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_containerSize = context.size!;
}
showAddModal() {
if (!isEditNotifier.value) {
return;
}
showSheet(
builder: (_, type) {
return ValueListenableBuilder(
valueListenable: addedChildrenNotifier,
builder: (_, value, __) {
return AdaptiveSheetScaffold(
type: type,
body: _AddedWidgetsModal(
items: value,
onAdd: (gridItem) {
_childrenNotifier.value = List.from(_childrenNotifier.value)
..add(
gridItem,
);
},
),
title: appLocalizations.add,
);
},
);
},
context: context,
);
}
_initState() {
_transformController.value = 0;
_sizes = List.generate(length, (index) => Size.zero);
@@ -139,22 +105,18 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_targetIndex = -1;
}
_handleChildrenNotifierChange() {
addedChildrenNotifier.value = widget.addedItemsBuilder != null
? widget.addedItemsBuilder!(_childrenNotifier.value)
: [];
}
@override
void initState() {
super.initState();
_childrenNotifier.addListener(() {
children = _childrenNotifier.value;
if (widget.onUpdate != null) {
widget.onUpdate!();
}
});
_childrenNotifier.value = widget.children;
_childrenNotifier.addListener(_handleChildrenNotifierChange);
isEditNotifier.addListener(_handleIsEditChange);
_fakeDragWidgetController = AnimationController.unbounded(
vsync: this,
duration: commonDuration,
@@ -164,6 +126,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
vsync: this,
duration: Duration(milliseconds: 120),
);
_shakeAnimation = Tween<double>(
begin: -0.012,
end: 0.012,
@@ -182,15 +145,11 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_initState();
}
_handleIsEditChange() async {
_handleChildrenNotifierChange();
if (isEditNotifier.value == false) {
if (widget.onSave != null) {
await _transformCompleter?.future;
await Future.delayed(commonDuration);
widget.onSave!(_childrenNotifier.value);
}
}
handleAdd(GridItem gridItem) {
_childrenNotifier.value = List.from(_childrenNotifier.value)
..add(
gridItem,
);
}
@override
@@ -305,6 +264,9 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
}
_handleDragEnd(DraggableDetails details) async {
final children = List<GridItem>.from(_childrenNotifier.value);
children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));
this.children = children;
debouncer.cancel(FunctionTag.handleWill);
if (_targetIndex == -1) {
return;
@@ -334,8 +296,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_fakeDragWidgetAnimation = null;
_transformTweenMap.clear();
_transformAnimationMap.clear();
final children = List<GridItem>.from(_childrenNotifier.value);
children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));
_childrenNotifier.value = children;
_initState();
}
@@ -385,17 +345,15 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_initState();
}
Widget _wrapTransform(Widget rawChild, int index) {
Widget _buildTransform(Widget rawChild, int index) {
return ValueListenableBuilder(
valueListenable: _animating,
builder: (_, animating, child) {
if (animating) {
if (_dragIndexNotifier.value == index) {
return _sizeBoxWrap(
Container(),
index,
);
}
if (animating && _dragIndexNotifier.value == index) {
return _buildSizeBox(
Container(),
index,
);
}
return child!;
},
@@ -442,7 +400,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
return nextOffset;
}
Widget _sizeBoxWrap(Widget child, int index) {
Widget _buildSizeBox(Widget child, int index) {
return ValueListenableBuilder(
valueListenable: _dragWidgetSizeNotifier,
builder: (_, size, child) {
@@ -455,7 +413,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
);
}
Widget _ignoreWrap(Widget child) {
Widget _buildInactivate(Widget child) {
return ValueListenableBuilder(
valueListenable: _animating,
builder: (_, animating, child) {
@@ -471,7 +429,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
);
}
Widget _shakeWrap(Widget child) {
Widget _buildShake(Widget child) {
final random = 0.7 + Random().nextDouble() * 0.3;
_shakeController.stop();
_shakeController.repeat(reverse: true);
@@ -487,7 +445,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
);
}
Widget _draggableWrap({
Widget _buildDraggable({
required Widget childWhenDragging,
required Widget feedback,
required Widget item,
@@ -523,7 +481,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
if (dragIndex == index) {
return child!;
}
return _shakeWrap(
return _buildShake(
_DeletableContainer(
onDelete: () {
_handleDelete(index);
@@ -566,16 +524,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
},
child: shakeTarget,
);
return ValueListenableBuilder(
valueListenable: isEditNotifier,
builder: (_, isEdit, child) {
if (!isEdit) {
return item;
}
return child!;
},
child: draggableChild,
);
return draggableChild;
}
Widget _builderItem(int index) {
@@ -590,7 +539,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
final childWhenDragging = ActivateBox(
child: Opacity(
opacity: 0.6,
child: _sizeBoxWrap(
child: _buildSizeBox(
CommonCard(
child: child,
),
@@ -599,7 +548,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
),
);
final feedback = ActivateBox(
child: _sizeBoxWrap(
child: _buildSizeBox(
CommonCard(
child: Material(
elevation: 6,
@@ -609,8 +558,8 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
index,
),
);
return _wrapTransform(
_draggableWrap(
return _buildTransform(
_buildDraggable(
childWhenDragging: childWhenDragging,
feedback: feedback,
item: child,
@@ -631,7 +580,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
if (!animating || _fakeDragWidgetAnimation == null || index == -1) {
return Container();
}
return _sizeBoxWrap(
return _buildSizeBox(
AnimatedBuilder(
animation: _fakeDragWidgetAnimation!,
builder: (_, child) {
@@ -658,10 +607,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_transformController.dispose();
_dragIndexNotifier.dispose();
_animating.dispose();
_childrenNotifier.removeListener(_handleChildrenNotifierChange);
_childrenNotifier.dispose();
isEditNotifier.removeListener(_handleIsEditChange);
isEditNotifier.dispose();
super.dispose();
}
@@ -670,7 +616,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
return DeferredPointerHandler(
child: Stack(
children: [
_ignoreWrap(
_buildInactivate(
ValueListenableBuilder(
valueListenable: _childrenNotifier,
builder: (_, children, __) {
@@ -694,46 +640,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
}
}
class _AddedWidgetsModal extends StatelessWidget {
final List<GridItem> items;
final Function(GridItem item) onAdd;
const _AddedWidgetsModal({
required this.items,
required this.onAdd,
});
@override
Widget build(BuildContext context) {
return DeferredPointerHandler(
child: SingleChildScrollView(
padding: EdgeInsets.all(
16,
),
child: Grid(
crossAxisCount: 8,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: items
.map(
(item) => item.wrap(
builder: (child) {
return _AddedContainer(
onAdd: () {
onAdd(item);
},
child: child,
);
},
),
)
.toList(),
),
),
);
}
}
class _DeletableContainer extends StatefulWidget {
final Widget child;
final VoidCallback onDelete;
@@ -841,68 +747,3 @@ class _DeletableContainerState extends State<_DeletableContainer>
);
}
}
class _AddedContainer extends StatefulWidget {
final Widget child;
final VoidCallback onAdd;
const _AddedContainer({
required this.child,
required this.onAdd,
});
@override
State<_AddedContainer> createState() => _AddedContainerState();
}
class _AddedContainerState extends State<_AddedContainer> {
@override
void initState() {
super.initState();
}
@override
void didUpdateWidget(_AddedContainer oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.child != widget.child) {}
}
_handleAdd() async {
widget.onAdd();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
ActivateBox(
child: widget.child,
),
Positioned(
top: -8,
right: -8,
child: DeferPointer(
child: SizedBox(
width: 24,
height: 24,
child: IconButton.filled(
iconSize: 20,
padding: EdgeInsets.all(2),
onPressed: _handleAdd,
icon: Icon(
Icons.add,
),
),
),
),
)
],
);
}
}

View File

@@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch
return TRUE;
}
// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application startup.
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}
// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application shutdown.
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
@@ -91,12 +109,21 @@ static void my_application_dispose(GObject* object) {
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
nullptr)); }
// Set the program name to the application ID, which helps various systems
// like GTK and desktop environments map this running application to its
// corresponding .desktop file. This ensures better integration by allowing
// the application to be recognized beyond its binary name.
g_set_prgname(APPLICATION_ID);
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
nullptr));
}

View File

@@ -40,7 +40,7 @@ PODS:
- FlutterMacOS
- window_ext (0.0.1):
- FlutterMacOS
- window_manager (0.2.0):
- window_manager (0.5.0):
- FlutterMacOS
DEPENDENCIES:
@@ -128,7 +128,7 @@ SPEC CHECKSUMS:
tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
window_ext: 4afef727fe428b30c68ce800ba92e890fd329f63
window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c
window_manager: b729e31d38fb04905235df9ea896128991cad99e
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367

View File

@@ -1070,7 +1070,7 @@ packages:
source: hosted
version: "2.6.4"
riverpod_lint:
dependency: "direct main"
dependency: "direct dev"
description:
name: riverpod_lint
sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693
@@ -1565,10 +1565,10 @@ packages:
dependency: "direct main"
description:
name: window_manager
sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059"
sha256: "51d50168ab267d344b975b15390426b1243600d436770d3f13de67e55b05ec16"
url: "https://pub.dev"
source: hosted
version: "0.4.3"
version: "0.5.0"
xdg_directories:
dependency: transitive
description:

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.85+202506021
version: 0.8.85+202506062
environment:
sdk: '>=3.1.0 <4.0.0'
@@ -14,7 +14,7 @@ dependencies:
path_provider: ^2.1.0
path: ^1.9.0
shared_preferences: ^2.5.3
window_manager: ^0.4.3
window_manager: ^0.5.0
dynamic_color: ^1.7.0
proxy:
path: plugins/proxy
@@ -59,7 +59,6 @@ dependencies:
# yaml: ^3.1.3
flutter_svg: ^2.1.0
flutter_cache_manager: ^3.4.1
riverpod_lint: ^2.6.3
crypto: ^3.0.3
dev_dependencies:
flutter_test:
@@ -71,6 +70,7 @@ dev_dependencies:
args: ^2.4.2
freezed: ^2.5.1
riverpod_generator: ^2.6.3
riverpod_lint: ^2.6.3
custom_lint: ^0.7.0
flutter: