cache
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user