This commit is contained in:
chen08209
2026-03-22 15:16:12 +08:00
parent d30eb6a7c6
commit 5d3122f5f4
4 changed files with 91 additions and 67 deletions

View File

@@ -449,4 +449,25 @@ enum ItemPosition {
}
return position;
}
static ItemPosition calculateVisualPosition<T>(
int currentIndex,
List<T> items,
Set<T> deletedItems,
) {
final currentItem = items[currentIndex];
if (deletedItems.contains(currentItem)) {
return ItemPosition.middle;
}
final int visualLength = items.length - deletedItems.length;
if (visualLength <= 0) return ItemPosition.middle;
int deletedCountBeforeMe = 0;
for (int i = 0; i < currentIndex; i++) {
if (deletedItems.contains(items[i])) {
deletedCountBeforeMe++;
}
}
final int visualIndex = currentIndex - deletedCountBeforeMe;
return ItemPosition.get(visualIndex, visualLength);
}
}

View File

@@ -560,34 +560,54 @@ class _EditProxiesView extends ConsumerStatefulWidget {
class _EditProxiesViewState extends ConsumerState<_EditProxiesView>
with UniqueKeyStateMixin {
bool _isComputing = false;
@override
void initState() {
super.initState();
ref.listenManual(itemsProvider(key), (prev, next) {
if (!SetEquality().equals(prev, next)) {
_handleRealRemove();
}
});
}
void _handleToAddProxiesView() {
Navigator.of(
context,
).push(PagedSheetRoute(builder: (context) => _AddProxiesView()));
}
@override
void initState() {
super.initState();
}
void _handleRemove(String proxyName) {
final dismissItem = ref.read(itemProvider(key));
if (dismissItem != null) {
if (_isComputing) {
return;
}
ref.read(itemProvider(key).notifier).value = proxyName;
ref.read(itemsProvider(key).notifier).update((state) {
final newSet = Set.from(state);
newSet.add(proxyName);
return newSet;
});
}
void _handleRealRemove(String proxyName) {
ref.read(proxyGroupProvider.notifier).update((state) {
final newProxies = List<String>.from(state.proxies ?? []);
newProxies.remove(proxyName);
return state.copyWith(proxies: newProxies);
});
if (mounted) {
ref.read(itemProvider(key).notifier).value = null;
}
void _handleRealRemove() {
debouncer.call(
'EditProxiesViewState_handleRealRemove',
() {
_isComputing = true;
if (!ref.context.mounted) {
return;
}
final dismissItems = ref.read(itemsProvider(key));
ref.read(proxyGroupProvider.notifier).update((state) {
final newProxies = List<String>.from(state.proxies ?? []);
newProxies.removeWhere((state) => dismissItems.contains(state));
return state.copyWith(proxies: newProxies);
});
ref.read(itemsProvider(key).notifier).update((state) => <dynamic>{});
_isComputing = false;
},
duration: Duration(milliseconds: 1000),
);
}
Widget _buildItem({
@@ -595,16 +615,13 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView>
required String? proxyType,
required int index,
required int length,
required ItemPosition position,
required bool dismiss,
}) {
final position = ItemPosition.get(index, length);
return ExternalDismissible(
dismiss: dismiss,
effect: ExternalDismissibleEffect.normal,
key: ValueKey(proxyName),
onDismissed: () {
_handleRealRemove(proxyName);
},
onDismissed: _handleRealRemove,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: ItemPositionProvider(
@@ -668,7 +685,7 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView>
(state) => VM2(state.includeAllProxies ?? false, state.proxies ?? []),
),
);
final dismissItem = ref.watch(itemProvider(key));
final dismissItems = ref.watch(itemsProvider(key));
final includeAllProxies = vm2.a;
final proxyNames = vm2.b;
final proxyTypeMap =
@@ -730,15 +747,16 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView>
),
if (proxyNames.isNotEmpty)
SliverReorderableList(
findChildIndexCallback: (Key key) {
final String keyValue = (key as dynamic).subKey?.value;
final index = proxyNames.indexOf(keyValue);
return index;
},
itemBuilder: (_, index) {
final proxyName = proxyNames[index];
final position = ItemPosition.calculateVisualPosition(
index,
proxyNames,
dismissItems,
);
return _buildItem(
dismiss: dismissItem == proxyName,
position: position,
dismiss: dismissItems.contains(proxyName),
proxyName: proxyName,
proxyType: proxyTypeMap[proxyName],
index: index,

View File

@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/database/database.dart';

View File

@@ -1,19 +1,15 @@
import 'package:flutter/material.dart';
enum ExternalDismissibleEffect { normal, resize }
class ExternalDismissible extends StatefulWidget {
final Widget child;
final VoidCallback onDismissed;
final VoidCallback? onDismissed;
final bool dismiss;
final ExternalDismissibleEffect effect;
const ExternalDismissible({
super.key,
required this.child,
required this.dismiss,
required this.onDismissed,
this.effect = ExternalDismissibleEffect.normal,
this.onDismissed,
});
@override
@@ -28,43 +24,33 @@ class _ExternalDismissibleState extends State<ExternalDismissible>
bool _isDismissing = false;
bool get isNormal => widget.effect == ExternalDismissibleEffect.normal;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: isNormal ? 300 : 160),
duration: Duration(milliseconds: 400),
);
_initAnimations();
if (widget.dismiss) {
_dismiss();
}
}
void _initAnimations() {
if (isNormal) {
_slideAnimation =
Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.6, curve: Curves.easeOut),
),
);
_resizeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.6, 1.0, curve: Curves.easeOut),
),
);
} else {
_slideAnimation = const AlwaysStoppedAnimation(Offset.zero);
_resizeAnimation = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
}
_slideAnimation =
Tween<Offset>(begin: Offset.zero, end: const Offset(1.0, 0.0)).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.6, curve: Curves.easeOut),
),
);
_resizeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.6, 1.0, curve: Curves.easeOut),
),
);
}
@override
@@ -86,8 +72,8 @@ class _ExternalDismissibleState extends State<ExternalDismissible>
await _controller.forward();
if (mounted) {
widget.onDismissed();
if (mounted && widget.onDismissed != null) {
widget.onDismissed!();
}
}
@@ -103,9 +89,7 @@ class _ExternalDismissibleState extends State<ExternalDismissible>
Widget content = widget.child;
if (isNormal) {
content = SlideTransition(position: _slideAnimation, child: content);
}
content = SlideTransition(position: _slideAnimation, child: content);
return SizeTransition(
axisAlignment: 0.5,