This commit is contained in:
chen08209
2026-03-15 01:10:43 +08:00
parent b41ca491ae
commit e5c18d97cf
7 changed files with 233 additions and 369 deletions

View File

@@ -333,17 +333,6 @@ class ProxyGroups extends _$ProxyGroups with AsyncNotifierMixin {
List<ProxyGroup> get value => state.value ?? [];
}
@Riverpod(name: 'proxyGroupProvider')
class ProxyGroupProvider extends _$ProxyGroupProvider with AsyncNotifierMixin {
@override
Stream<ProxyGroup> build(int profileId, String name) {
return database.proxyGroupsDao.get(profileId, name).watchSingle();
}
@override
ProxyGroup get value => state.requireValue;
}
@riverpod
class ProfileDisabledRuleIds extends _$ProfileDisabledRuleIds
with AsyncNotifierMixin {

View File

@@ -749,98 +749,6 @@ abstract class _$ProxyGroups extends $StreamNotifier<List<ProxyGroup>> {
}
}
@ProviderFor(ProxyGroupProvider)
const proxyGroupProvider = ProxyGroupProviderFamily._();
final class ProxyGroupProviderProvider
extends $StreamNotifierProvider<ProxyGroupProvider, ProxyGroup> {
const ProxyGroupProviderProvider._({
required ProxyGroupProviderFamily super.from,
required (int, String) super.argument,
}) : super(
retry: null,
name: r'proxyGroupProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$proxyGroupProviderHash();
@override
String toString() {
return r'proxyGroupProvider'
''
'$argument';
}
@$internal
@override
ProxyGroupProvider create() => ProxyGroupProvider();
@override
bool operator ==(Object other) {
return other is ProxyGroupProviderProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$proxyGroupProviderHash() =>
r'60ec3f8a2a09e3ed1ac73d724b1ade295309897d';
final class ProxyGroupProviderFamily extends $Family
with
$ClassFamilyOverride<
ProxyGroupProvider,
AsyncValue<ProxyGroup>,
ProxyGroup,
Stream<ProxyGroup>,
(int, String)
> {
const ProxyGroupProviderFamily._()
: super(
retry: null,
name: r'proxyGroupProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
ProxyGroupProviderProvider call(int profileId, String name) =>
ProxyGroupProviderProvider._(argument: (profileId, name), from: this);
@override
String toString() => r'proxyGroupProvider';
}
abstract class _$ProxyGroupProvider extends $StreamNotifier<ProxyGroup> {
late final _$args = ref.$arg as (int, String);
int get profileId => _$args.$1;
String get name => _$args.$2;
Stream<ProxyGroup> build(int profileId, String name);
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args.$1, _$args.$2);
final ref = this.ref as $Ref<AsyncValue<ProxyGroup>, ProxyGroup>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<ProxyGroup>, ProxyGroup>,
AsyncValue<ProxyGroup>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@ProviderFor(ProfileDisabledRuleIds)
const profileDisabledRuleIdsProvider = ProfileDisabledRuleIdsFamily._();

View File

@@ -2645,3 +2645,57 @@ abstract class _$AccessControlState extends $Notifier<AccessControlProps> {
element.handleValue(ref, created);
}
}
@ProviderFor(ProxyGroupProvider)
const proxyGroupProvider = ProxyGroupProviderProvider._();
final class ProxyGroupProviderProvider
extends $NotifierProvider<ProxyGroupProvider, ProxyGroup> {
const ProxyGroupProviderProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'proxyGroupProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$proxyGroupProviderHash();
@$internal
@override
ProxyGroupProvider create() => ProxyGroupProvider();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ProxyGroup value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ProxyGroup>(value),
);
}
}
String _$proxyGroupProviderHash() =>
r'095a5f094ee2bfac3cc2c382de0c2f8e9ad5b2b7';
abstract class _$ProxyGroupProvider extends $Notifier<ProxyGroup> {
ProxyGroup build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ProxyGroup, ProxyGroup>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ProxyGroup, ProxyGroup>,
ProxyGroup,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

View File

@@ -727,3 +727,11 @@ class AccessControlState extends _$AccessControlState
@override
AccessControlProps build() => AccessControlProps();
}
@Riverpod(name: 'proxyGroupProvider')
class ProxyGroupProvider extends _$ProxyGroupProvider {
@override
ProxyGroup build() {
return throw 'Initialization proxyGroupProvider error';
}
}

View File

@@ -25,8 +25,10 @@ class _CustomProxyGroupsView extends ConsumerWidget {
builder: (context) {
return ProfileIdProvider(
profileId: profileId,
child: ProxyGroupProvider(
proxyGroup: proxyGroup,
child: ProviderScope(
overrides: [
proxyGroupProvider.overrideWithBuild((_, __) => proxyGroup),
],
child: _EditProxyGroupNestedSheet(),
),
);
@@ -39,6 +41,7 @@ class _CustomProxyGroupsView extends ConsumerWidget {
required ProxyGroup proxyGroup,
required int index,
required int total,
required VoidCallback onPressed,
}) {
final position = ItemPosition.get(index, total);
return ItemPositionProvider(
@@ -47,12 +50,10 @@ class _CustomProxyGroupsView extends ConsumerWidget {
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: DecorationListItem(
onPressed: onPressed,
minVerticalPadding: 8,
title: Text(proxyGroup.name),
subtitle: Text(proxyGroup.type.name),
onPressed: () {
_handleEditProxyGroup(context, proxyGroup);
},
trailing: ReorderableDelayedDragStartListener(
index: index,
child: Icon(Icons.drag_handle),
@@ -77,6 +78,9 @@ class _CustomProxyGroupsView extends ConsumerWidget {
proxyGroup: proxyGroup,
total: proxyGroups.length,
index: index,
onPressed: () {
_handleEditProxyGroup(context, proxyGroup);
},
);
},
itemCount: proxyGroups.length,
@@ -186,69 +190,19 @@ class _EditProxyGroupView extends ConsumerStatefulWidget {
}
class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
late ProxyGroup _proxyGroup;
final _nameController = TextEditingController();
final _hideController = ValueNotifier<bool>(false);
final _disableUDPController = ValueNotifier<bool>(false);
final _proxiesController = ValueNotifier<List<String>>([]);
final _useController = ValueNotifier<List<String>>([]);
final _typeController = ValueNotifier<GroupType>(GroupType.Selector);
final _allProxiesController = ValueNotifier<bool>(false);
final _allProviderController = ValueNotifier<bool>(false);
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_nameController.text = _proxyGroup.name;
_hideController.value = _proxyGroup.hidden ?? false;
_disableUDPController.value = _proxyGroup.disableUDP ?? false;
_typeController.value = _proxyGroup.type;
_proxiesController.value = _proxyGroup.proxies ?? [];
_useController.value = _proxyGroup.use ?? [];
if (_proxyGroup.includeAll == true) {
_allProxiesController.value = true;
_allProviderController.value = true;
} else {
_allProxiesController.value = _proxyGroup.includeAllProxies ?? false;
_allProviderController.value = _proxyGroup.includeAllProviders ?? false;
}
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_proxyGroup = ProxyGroupProvider.of(context)!.proxyGroup;
}
@override
void dispose() {
_nameController.dispose();
_hideController.dispose();
_disableUDPController.dispose();
_typeController.dispose();
_proxiesController.dispose();
_useController.dispose();
_allProxiesController.dispose();
_allProviderController.dispose();
super.dispose();
}
Future<void> _showTypeOptions() async {
final value = await globalState.showCommonDialog<GroupType>(
child: OptionsDialog<GroupType>(
title: '类型',
options: GroupType.values,
textBuilder: (item) => item.name,
value: _typeController.value,
),
);
if (value == null) {
return;
}
_typeController.value = value;
// final value = await globalState.showCommonDialog<GroupType>(
// child: OptionsDialog<GroupType>(
// title: '类型',
// options: GroupType.values,
// textBuilder: (item) => item.name,
// value: _typeController.value,
// ),
// );
// if (value == null) {
// return;
// }
// _typeController.value = value;
}
Widget _buildItem({
@@ -283,125 +237,137 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
}
void _handleToProxiesView() {
Navigator.of(context).push(
PagedSheetRoute(
builder: (context) => _EditProxiesView(_proxyGroup.proxies ?? []),
),
);
Navigator.of(
context,
).push(PagedSheetRoute(builder: (context) => _EditProxiesView()));
}
void _handleToProvidersView() {}
Widget _buildProvidersItem() {
Widget _buildProvidersItem(bool includeAllProviders, List<String> use) {
return _buildItem(
title: Text('选择代理集'),
trailing: ValueListenableBuilder(
valueListenable: _allProviderController,
builder: (_, allProviders, _) {
return ValueListenableBuilder(
valueListenable: _useController,
builder: (_, use, _) {
return Row(
mainAxisSize: MainAxisSize.min,
spacing: 2,
children: [
!allProviders
? Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Container(
constraints: BoxConstraints(minWidth: 32),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 4,
vertical: 3,
),
child: Text(
textAlign: TextAlign.center,
'${use.length}',
style: context.textTheme.bodySmall,
),
),
),
)
: Icon(
Icons.check_circle_outline,
size: 20,
color: Colors.greenAccent.shade200,
),
Icon(Icons.arrow_forward_ios),
],
);
},
);
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
spacing: 2,
children: [
!includeAllProviders
? Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Container(
constraints: BoxConstraints(minWidth: 32),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 3),
child: Text(
textAlign: TextAlign.center,
'${use.length}',
style: context.textTheme.bodySmall,
),
),
),
)
: Icon(
Icons.check_circle_outline,
size: 20,
color: Colors.greenAccent.shade200,
),
Icon(Icons.arrow_forward_ios),
],
),
onPressed: _handleToProvidersView,
);
}
Widget _buildProxiesItem() {
Widget _buildProxiesItem(bool includeAllProxies, List<String> proxies) {
return _buildItem(
title: Text('选择代理'),
trailing: ValueListenableBuilder(
valueListenable: _allProxiesController,
builder: (_, allProxies, _) {
return ValueListenableBuilder(
valueListenable: _proxiesController,
builder: (_, proxies, _) {
return Row(
mainAxisSize: MainAxisSize.min,
spacing: 2,
children: [
!allProxies
? Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Container(
constraints: BoxConstraints(minWidth: 32),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 4,
vertical: 3,
),
child: Text(
textAlign: TextAlign.center,
'${proxies.length}',
style: context.textTheme.bodySmall,
),
),
),
)
: Icon(
Icons.check_circle_outline,
size: 20,
color: Colors.greenAccent.shade200,
),
Icon(Icons.arrow_forward_ios),
],
);
},
);
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
spacing: 2,
children: [
!includeAllProxies
? Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Container(
constraints: BoxConstraints(minWidth: 32),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 3),
child: Text(
textAlign: TextAlign.center,
'${proxies.length}',
style: context.textTheme.bodySmall,
),
),
),
)
: Icon(
Icons.check_circle_outline,
size: 20,
color: Colors.greenAccent.shade200,
),
Icon(Icons.arrow_forward_ios),
],
),
onPressed: _handleToProxiesView,
);
}
Widget _buildGroupTypeItem() {
Widget _buildTypeItem(GroupType type) {
return _buildItem(
title: Text('类型'),
onPressed: () {
_showTypeOptions();
},
trailing: ValueListenableBuilder(
valueListenable: _typeController,
builder: (_, type, _) {
return Text(type.name);
trailing: Text(type.name),
);
}
Widget _buildNameItem(String name) {
return _buildItem(
title: Text('名称'),
trailing: TextFormField(
initialValue: name,
onChanged: (value) {},
textAlign: TextAlign.end,
decoration: InputDecoration.collapsed(
border: NoInputBorder(),
hintText: '输入策略组名称',
),
),
);
}
Widget _buildHiddenItem(bool hidden) {
return _buildItem(
title: Text('从列表中隐藏'),
onPressed: () {
// _hideController.value = !_hideController.value;
},
trailing: Switch(
value: hidden,
onChanged: (value) {
// _hideController.value = value;
},
),
);
}
Widget _buildDisableUDPItem(bool disableUDP) {
return _buildItem(
title: Text('禁用UDP'),
onPressed: () {
// _disableUDPController.value = !_disableUDPController.value;
},
trailing: Switch(
value: disableUDP,
onChanged: (value) {
// _disableUDPController.value = value;
},
),
);
@@ -409,9 +375,9 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
@override
Widget build(BuildContext context) {
_proxyGroup = ProxyGroupProvider.of(context)!.proxyGroup;
final isBottomSheet =
SheetProvider.of(context)?.type == SheetType.bottomSheet;
final proxyGroup = ref.watch(proxyGroupProvider);
return AdaptiveSheetScaffold(
sheetTransparentToolBar: true,
actions: [IconButtonData(icon: Icons.check, onPressed: () {})],
@@ -427,60 +393,24 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
generateSectionV3(
title: '通用',
items: [
_buildItem(
title: Text('名称'),
trailing: TextFormField(
controller: _nameController,
textAlign: TextAlign.end,
decoration: InputDecoration.collapsed(
border: NoInputBorder(),
hintText: '输入策略组名称',
),
),
),
_buildGroupTypeItem(),
_buildNameItem(proxyGroup.name),
_buildTypeItem(proxyGroup.type),
_buildItem(title: Text('图标')),
_buildItem(
title: Text('从列表中隐藏'),
onPressed: () {
_hideController.value = !_hideController.value;
},
trailing: ValueListenableBuilder(
valueListenable: _hideController,
builder: (_, value, _) {
return Switch(
value: value,
onChanged: (value) {
_hideController.value = value;
},
);
},
),
),
_buildItem(
title: Text('禁用UDP'),
onPressed: () {
_disableUDPController.value = !_disableUDPController.value;
},
trailing: ValueListenableBuilder(
valueListenable: _disableUDPController,
builder: (_, value, _) {
return Switch(
value: value,
onChanged: (value) {
_disableUDPController.value = value;
},
);
},
),
),
_buildHiddenItem(proxyGroup.hidden ?? false),
_buildDisableUDPItem(proxyGroup.disableUDP ?? false),
],
),
generateSectionV3(
title: '节点',
items: [
_buildProxiesItem(),
_buildProvidersItem(),
_buildProxiesItem(
proxyGroup.includeAllProxies ?? false,
proxyGroup.proxies ?? [],
),
_buildProvidersItem(
proxyGroup.includeAllProviders ?? false,
proxyGroup.use ?? [],
),
_buildItem(
title: Text('节点过滤器'),
trailing: TextFormField(
@@ -564,14 +494,7 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
),
generateSectionV3(
title: '操作',
items: [
_buildItem(
title: Text('删除'),
onPressed: () {
_disableUDPController.value = !_disableUDPController.value;
},
),
],
items: [_buildItem(title: Text('删除'), onPressed: () {})],
),
],
),
@@ -582,9 +505,7 @@ class _EditProxyGroupViewState extends ConsumerState<_EditProxyGroupView> {
}
class _EditProxiesView extends ConsumerStatefulWidget {
final List<String> proxyNames;
const _EditProxiesView(this.proxyNames);
const _EditProxiesView();
@override
ConsumerState<_EditProxiesView> createState() => _EditProxiesViewState();
@@ -592,12 +513,9 @@ class _EditProxiesView extends ConsumerStatefulWidget {
class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
void _handleToAddProxiesView() {
Navigator.of(context).push(
PagedSheetRoute(
builder: (context) =>
_AddProxiesView(addedProxyNames: widget.proxyNames),
),
);
Navigator.of(
context,
).push(PagedSheetRoute(builder: (context) => _AddProxiesView()));
}
Widget _buildItem({
@@ -635,7 +553,9 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
@override
Widget build(BuildContext context) {
final profileId = ProfileIdProvider.of(context)!.profileId;
final proxyNames = widget.proxyNames;
final proxyNames = ref.watch(
proxyGroupProvider.select((state) => state.proxies ?? []),
);
final proxyTypeMap =
ref.watch(
clashConfigProvider(
@@ -714,9 +634,7 @@ class _EditProxiesViewState extends ConsumerState<_EditProxiesView> {
}
class _AddProxiesView extends ConsumerWidget {
final List<String> addedProxyNames;
const _AddProxiesView({required this.addedProxyNames});
const _AddProxiesView();
Widget _buildItem({
required String title,
@@ -744,20 +662,26 @@ class _AddProxiesView extends ConsumerWidget {
final isBottomSheet =
SheetProvider.of(context)?.type == SheetType.bottomSheet;
final profileId = ProfileIdProvider.of(context)!.profileId;
final currentGroupName = ProxyGroupProvider.of(context)!.proxyGroup.name;
final proxiesAndGroupsMap = ref.watch(
final allProxiesAndProxyGroups = ref.watch(
clashConfigProvider(profileId).select(
(state) =>
VM2(state.value?.proxies ?? [], state.value?.proxyGroups ?? []),
),
);
final allProxies = proxiesAndGroupsMap.a;
final allProxyGroups = proxiesAndGroupsMap.b;
final allProxies = allProxiesAndProxyGroups.a;
final allProxyGroups = allProxiesAndProxyGroups.b;
final proxyNamesAndName = ref.watch(
proxyGroupProvider.select(
(state) => VM2(state.name, state.proxies ?? []),
),
);
final groupName = proxyNamesAndName.a;
final proxyNames = proxyNamesAndName.b;
final proxies = allProxies
.where((item) => !addedProxyNames.contains(item.name))
.where((item) => !proxyNames.contains(item.name))
.toList();
final groups = allProxyGroups
.where((item) => currentGroupName != item.name)
final proxyGroups = allProxyGroups
.where((item) => groupName != item.name)
.toList();
return SizedBox(
height: isBottomSheet
@@ -771,7 +695,7 @@ class _AddProxiesView extends ConsumerWidget {
SliverToBoxAdapter(
child: SizedBox(height: context.sheetTopPadding),
),
if (groups.isNotEmpty) ...[
if (proxyGroups.isNotEmpty) ...[
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
@@ -780,11 +704,11 @@ class _AddProxiesView extends ConsumerWidget {
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
final group = groups[index];
final position = ItemPosition.get(index, groups.length);
final proxyGroup = proxyGroups[index];
final position = ItemPosition.get(index, proxyGroups.length);
return _buildItem(
title: group.name,
subtitle: group.type.value,
title: proxyGroup.name,
subtitle: proxyGroup.type.value,
position: position,
trailing: CommonMinIconButtonTheme(
child: IconButton.filledTonal(
@@ -794,7 +718,7 @@ class _AddProxiesView extends ConsumerWidget {
),
),
);
}, childCount: groups.length),
}, childCount: proxyGroups.length),
),
SliverToBoxAdapter(child: SizedBox(height: 8)),
],

View File

@@ -13,12 +13,11 @@ import 'package:fl_clash/views/config/scripts.dart';
import 'package:fl_clash/views/profiles/preview.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:smooth_sheets/smooth_sheets.dart';
part 'custom.dart';
part 'custom_proxies.dart';
part 'custom_groups.dart';
part 'script.dart';
part 'standard.dart';
part 'widgets.dart';

View File

@@ -47,21 +47,3 @@ class ProfileIdProvider extends InheritedWidget {
bool updateShouldNotify(ProfileIdProvider oldWidget) =>
profileId != oldWidget.profileId;
}
class ProxyGroupProvider extends InheritedWidget {
final ProxyGroup proxyGroup;
const ProxyGroupProvider({
super.key,
required this.proxyGroup,
required super.child,
});
static ProxyGroupProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ProxyGroupProvider>();
}
@override
bool updateShouldNotify(ProxyGroupProvider oldWidget) =>
proxyGroup != oldWidget.proxyGroup;
}