diff --git a/lib/enum/enum.dart b/lib/enum/enum.dart index c739586..9e4455f 100644 --- a/lib/enum/enum.dart +++ b/lib/enum/enum.dart @@ -449,4 +449,25 @@ enum ItemPosition { } return position; } + + static ItemPosition calculateVisualPosition( + int currentIndex, + List items, + Set 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); + } } diff --git a/lib/views/profiles/overwrite/custom_groups.dart b/lib/views/profiles/overwrite/custom_groups.dart index 7781e89..9bd5324 100644 --- a/lib/views/profiles/overwrite/custom_groups.dart +++ b/lib/views/profiles/overwrite/custom_groups.dart @@ -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.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.from(state.proxies ?? []); + newProxies.removeWhere((state) => dismissItems.contains(state)); + return state.copyWith(proxies: newProxies); + }); + ref.read(itemsProvider(key).notifier).update((state) => {}); + _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, diff --git a/lib/views/profiles/overwrite/overwrite.dart b/lib/views/profiles/overwrite/overwrite.dart index 2c7fd20..fb62ab4 100644 --- a/lib/views/profiles/overwrite/overwrite.dart +++ b/lib/views/profiles/overwrite/overwrite.dart @@ -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'; diff --git a/lib/widgets/dismissible.dart b/lib/widgets/dismissible.dart index 73db6cc..a994133 100644 --- a/lib/widgets/dismissible.dart +++ b/lib/widgets/dismissible.dart @@ -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 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( - 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(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( - begin: 1.0, - end: 0.0, - ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); - } + _slideAnimation = + Tween(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(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 await _controller.forward(); - if (mounted) { - widget.onDismissed(); + if (mounted && widget.onDismissed != null) { + widget.onDismissed!(); } } @@ -103,9 +89,7 @@ class _ExternalDismissibleState extends State Widget content = widget.child; - if (isNormal) { - content = SlideTransition(position: _slideAnimation, child: content); - } + content = SlideTransition(position: _slideAnimation, child: content); return SizeTransition( axisAlignment: 0.5,