This commit is contained in:
chen08209
2026-03-19 18:19:37 +08:00
parent 2023cecdfc
commit ce3093c80b
5 changed files with 249 additions and 215 deletions

View File

@@ -28,7 +28,7 @@ class RuleItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CommonSelectedListItem(
return SelectedDecorationListItem(
isSelected: isSelected,
onSelected: () {
onSelected();

View File

@@ -117,124 +117,3 @@ class _CustomContent extends ConsumerWidget {
);
}
}
class _CustomRulesView extends ConsumerStatefulWidget {
final int profileId;
const _CustomRulesView(this.profileId);
@override
ConsumerState createState() => _CustomRulesViewState();
}
class _CustomRulesViewState extends ConsumerState<_CustomRulesView> {
final _key = utils.id;
int get _profileId => widget.profileId;
@override
void initState() {
super.initState();
}
void _handleReorder(int oldIndex, int newIndex) {
ref
.read(profileCustomRulesProvider(_profileId).notifier)
.order(oldIndex, newIndex);
}
void _handleSelected(int ruleId) {
ref.read(itemsProvider(_key).notifier).update((selectedRules) {
final newSelectedRules = Set<int>.from(selectedRules)
..addOrRemove(ruleId);
return newSelectedRules;
});
}
void _handleSelectAll() {
final ids =
ref
.read(profileCustomRulesProvider(_profileId))
.value
?.map((item) => item.id)
.toSet() ??
{};
ref.read(itemsProvider(_key).notifier).update((selected) {
return selected.containsAll(ids) ? {} : ids;
});
}
Future<void> _handleDelete() async {
final res = await globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(
text: appLocalizations.deleteMultipTip(appLocalizations.rule),
),
);
if (res != true) {
return;
}
final selectedRules = ref.read(itemsProvider(_key));
ref
.read(profileCustomRulesProvider(_profileId).notifier)
.delAll(selectedRules.cast<int>());
ref.read(itemsProvider(_key).notifier).value = {};
}
@override
Widget build(context) {
final rules = ref.watch(profileCustomRulesProvider(_profileId)).value ?? [];
final selectedRules = ref.watch(itemsProvider(_key));
return CommonScaffold(
title: appLocalizations.rule,
actions: [
if (selectedRules.isNotEmpty) ...[
CommonMinIconButtonTheme(
child: IconButton.filledTonal(
onPressed: _handleDelete,
icon: Icon(Icons.delete),
),
),
SizedBox(width: 2),
],
CommonMinFilledButtonTheme(
child: selectedRules.isNotEmpty
? FilledButton(
onPressed: _handleSelectAll,
child: Text(appLocalizations.selectAll),
)
: FilledButton.tonal(
onPressed: () {
// _handleAddOrUpdate();
},
child: Text(appLocalizations.add),
),
),
SizedBox(width: 8),
],
body: ReorderableListView.builder(
buildDefaultDragHandles: false,
itemBuilder: (_, index) {
final rule = rules[index];
return ReorderableDelayedDragStartListener(
key: ObjectKey(rule),
index: index,
child: RuleItem(
isEditing: selectedRules.isNotEmpty,
isSelected: selectedRules.contains(rule.id),
rule: rule,
onSelected: () {
_handleSelected(rule.id);
},
onEdit: (rule) {
// _handleAddOrUpdate(rule);
},
),
);
},
itemCount: rules.length,
onReorder: _handleReorder,
),
);
}
}

View File

@@ -330,12 +330,20 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
);
}
void _handleChangeName(String value) {
ref
.read(proxyGroupProvider.notifier)
.update((state) => state.copyWith(name: value));
}
Widget _buildNameItem(String name) {
return _buildItem(
title: Text('名称'),
trailing: TextFormField(
initialValue: name,
onChanged: (value) {},
onChanged: (value) {
_handleChangeName(value);
},
textAlign: TextAlign.end,
decoration: InputDecoration.collapsed(
border: NoInputBorder(),
@@ -385,6 +393,8 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
);
}
void _handleDelete() {}
@override
Widget build(BuildContext context) {
final isBottomSheet =
@@ -506,7 +516,17 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
),
generateSectionV3(
title: '操作',
items: [_buildItem(title: Text('删除'), onPressed: () {})],
items: [
_buildItem(
title: Text(
'删除',
style: context.textTheme.bodyLarge?.copyWith(
color: context.colorScheme.error,
),
),
onPressed: _handleDelete,
),
],
),
],
),
@@ -523,9 +543,8 @@ class _EditProxiesView extends ConsumerStatefulWidget {
ConsumerState<_EditProxiesView> createState() => _EditProxiesViewState();
}
class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
final _dismissItemController = ValueNotifier<String?>(null);
class _EditProxiesViewState extends ConsumerState<_EditProxiesView>
with UniqueKeyStateMixin {
void _handleToAddProxiesView() {
Navigator.of(
context,
@@ -538,10 +557,11 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
}
void _handleRemove(String proxyName) {
if (_dismissItemController.value != null) {
final dismissItem = ref.read(itemProvider(key));
if (dismissItem != null) {
return;
}
_dismissItemController.value = proxyName;
ref.read(itemProvider(key).notifier).value = proxyName;
}
void _handleRealRemove(String proxyName) {
@@ -551,7 +571,7 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
return state.copyWith(proxies: newProxies);
});
if (mounted) {
_dismissItemController.value = null;
ref.read(itemProvider(key).notifier).value = null;
}
}
@@ -633,6 +653,7 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
(state) => VM2(state.includeAllProxies ?? false, state.proxies ?? []),
),
);
final dismissItem = ref.watch(itemProvider(key));
final includeAllProxies = vm2.a;
final proxyNames = vm2.b;
final proxyTypeMap =
@@ -692,32 +713,30 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
),
),
),
ValueListenableBuilder(
valueListenable: _dismissItemController,
builder: (_, dismissItem, _) {
return 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];
return _buildItem(
dismiss: dismissItem == proxyName,
proxyName: proxyName,
proxyType: proxyTypeMap[proxyName],
index: index,
length: proxyNames.length,
);
},
itemCount: proxyNames.length,
onReorder: (int oldIndex, int newIndex) {
_handleReorder(oldIndex, newIndex);
},
);
},
),
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];
return _buildItem(
dismiss: dismissItem == proxyName,
proxyName: proxyName,
proxyType: proxyTypeMap[proxyName],
index: index,
length: proxyNames.length,
);
},
itemCount: proxyNames.length,
onReorder: (int oldIndex, int newIndex) {
_handleReorder(oldIndex, newIndex);
},
)
else
SliverFillRemaining(child: NullStatus(label: '代理为空')),
SliverToBoxAdapter(child: SizedBox(height: 16)),
],
),
@@ -817,66 +836,74 @@ class _AddProxiesViewState extends ConsumerState<_AddProxiesView>
child: AdaptiveSheetScaffold(
sheetTransparentToolBar: true,
title: '添加代理',
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: SizedBox(height: context.sheetTopPadding),
),
if (proxyGroups.isNotEmpty) ...[
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: InfoHeader(info: Info(label: '策略组')),
),
body: proxies.isEmpty && proxyGroups.isEmpty
? NullStatus(label: appLocalizations.noData)
: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: SizedBox(height: context.sheetTopPadding),
),
if (proxyGroups.isNotEmpty) ...[
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: InfoHeader(info: Info(label: '策略组')),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
final proxyGroup = proxyGroups[index];
final position = ItemPosition.get(
index,
proxyGroups.length,
);
return _buildItem(
title: proxyGroup.name,
subtitle: proxyGroup.type.value,
position: position,
dismiss: dismissItem == proxyGroup.name,
onAdd: () {
_handleAdd(proxyGroup.name);
},
onDismissed: () {
_handleRealAdd(proxyGroup.name);
},
);
}, childCount: proxyGroups.length),
),
SliverToBoxAdapter(child: SizedBox(height: 8)),
],
if (proxies.isNotEmpty) ...[
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: InfoHeader(info: Info(label: '代理')),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
final proxy = proxies[index];
final position = ItemPosition.get(
index,
proxies.length,
);
return _buildItem(
title: proxy.name,
subtitle: proxy.type,
position: position,
dismiss: dismissItem == proxy.name,
onAdd: () {
_handleAdd(proxy.name);
},
onDismissed: () {
_handleRealAdd(proxy.name);
},
);
}, childCount: proxies.length),
),
],
],
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
final proxyGroup = proxyGroups[index];
final position = ItemPosition.get(index, proxyGroups.length);
return _buildItem(
title: proxyGroup.name,
subtitle: proxyGroup.type.value,
position: position,
dismiss: dismissItem == proxyGroup.name,
onAdd: () {
_handleAdd(proxyGroup.name);
},
onDismissed: () {
_handleRealAdd(proxyGroup.name);
},
);
}, childCount: proxyGroups.length),
),
SliverToBoxAdapter(child: SizedBox(height: 8)),
],
if (proxies.isNotEmpty) ...[
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: InfoHeader(info: Info(label: '代理')),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
final proxy = proxies[index];
final position = ItemPosition.get(index, proxies.length);
return _buildItem(
title: proxy.name,
subtitle: proxy.type,
position: position,
dismiss: dismissItem == proxy.name,
onAdd: () {
_handleAdd(proxy.name);
},
onDismissed: () {
_handleRealAdd(proxy.name);
},
);
}, childCount: proxies.length),
),
],
],
),
),
);
}

View File

@@ -0,0 +1,127 @@
part of 'overwrite.dart';
class _CustomRulesView extends ConsumerStatefulWidget {
final int profileId;
const _CustomRulesView(this.profileId);
@override
ConsumerState createState() => _CustomRulesViewState();
}
class _CustomRulesViewState extends ConsumerState<_CustomRulesView>
with UniqueKeyStateMixin {
int get _profileId => widget.profileId;
@override
void initState() {
super.initState();
}
void _handleReorder(int oldIndex, int newIndex) {
ref
.read(profileCustomRulesProvider(_profileId).notifier)
.order(oldIndex, newIndex);
}
void _handleSelected(int ruleId) {
ref.read(itemsProvider(key).notifier).update((selectedRules) {
final newSelectedRules = Set<int>.from(selectedRules)
..addOrRemove(ruleId);
return newSelectedRules;
});
}
void _handleSelectAll() {
final ids =
ref
.read(profileCustomRulesProvider(_profileId))
.value
?.map((item) => item.id)
.toSet() ??
{};
ref.read(itemsProvider(key).notifier).update((selected) {
return selected.containsAll(ids) ? {} : ids;
});
}
Future<void> _handleDelete() async {
final res = await globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(
text: appLocalizations.deleteMultipTip(appLocalizations.rule),
),
);
if (res != true) {
return;
}
final selectedRules = ref.read(itemsProvider(key));
ref
.read(profileCustomRulesProvider(_profileId).notifier)
.delAll(selectedRules.cast<int>());
ref.read(itemsProvider(key).notifier).value = {};
}
@override
Widget build(context) {
final rules = ref.watch(profileCustomRulesProvider(_profileId)).value ?? [];
final selectedRules = ref.watch(itemsProvider(key));
return CommonScaffold(
title: appLocalizations.rule,
actions: [
if (selectedRules.isNotEmpty) ...[
CommonMinIconButtonTheme(
child: IconButton.filledTonal(
onPressed: _handleDelete,
icon: Icon(Icons.delete),
),
),
SizedBox(width: 2),
],
CommonMinFilledButtonTheme(
child: selectedRules.isNotEmpty
? FilledButton(
onPressed: _handleSelectAll,
child: Text(appLocalizations.selectAll),
)
: FilledButton.tonal(
onPressed: () {
// _handleAddOrUpdate();
},
child: Text(appLocalizations.add),
),
),
SizedBox(width: 8),
],
body: ReorderableListView.builder(
buildDefaultDragHandles: false,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
itemBuilder: (_, index) {
final rule = rules[index];
final position = ItemPosition.get(index, rules.length);
return ReorderableDelayedDragStartListener(
key: ValueKey(rule),
index: index,
child: ItemPositionProvider(
position: position,
child: RuleItem(
isEditing: selectedRules.isNotEmpty,
isSelected: selectedRules.contains(rule.id),
rule: rule,
onSelected: () {
_handleSelected(rule.id);
},
onEdit: (rule) {
// _handleAddOrUpdate(rule);
},
),
),
);
},
itemExtent: globalState.measure.bodyMediumHeight * 2 + 12,
itemCount: rules.length,
onReorder: _handleReorder,
),
);
}
}

View File

@@ -18,6 +18,7 @@ import 'package:smooth_sheets/smooth_sheets.dart';
part 'custom.dart';
part 'custom_groups.dart';
part 'custom_rules.dart';
part 'script.dart';
part 'standard.dart';
part 'widgets.dart';