From 50e0ae721ea744205a162dbb5b7052b5b2ab00c9 Mon Sep 17 00:00:00 2001 From: chen08209 Date: Wed, 4 Mar 2026 18:47:56 +0800 Subject: [PATCH] cache --- lib/common/common.dart | 1 - lib/common/hive.dart | 0 lib/common/task.dart | 14 +++ lib/controller.dart | 4 + lib/database/database.dart | 57 ++++++++++++ lib/database/rules.dart | 1 + lib/models/clash_config.dart | 2 +- .../generated/clash_config.freezed.dart | 16 ++-- lib/models/generated/clash_config.g.dart | 4 +- lib/pages/editor.dart | 51 +---------- lib/views/profiles/overwrite/custom.dart | 90 ++++++++++++++++++- lib/widgets/input.dart | 49 ++++++++++ lib/widgets/list.dart | 21 +++++ 13 files changed, 245 insertions(+), 65 deletions(-) delete mode 100644 lib/common/hive.dart diff --git a/lib/common/common.dart b/lib/common/common.dart index e75514e..0b8dee3 100644 --- a/lib/common/common.dart +++ b/lib/common/common.dart @@ -10,7 +10,6 @@ export 'file.dart'; export 'fixed.dart'; export 'function.dart'; export 'future.dart'; -export 'hive.dart'; export 'http.dart'; export 'icons.dart'; export 'indexing.dart'; diff --git a/lib/common/hive.dart b/lib/common/hive.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/common/task.dart b/lib/common/task.dart index a8e0c29..8aa4bec 100644 --- a/lib/common/task.dart +++ b/lib/common/task.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -610,3 +611,16 @@ String _getScriptPath(String root, String fileName) { String _getProfilePath(String root, String fileName) { return join(root, 'profiles', '$fileName.yaml'); } + +Future> mapListTask(List results, T Function(S) mapper) async { + return await compute, T Function(S)>, List>( + _mapListTask, + VM2(results, mapper), + ); +} + +Future> _mapListTask(VM2, T Function(S)> vm2) async { + final results = vm2.a; + final mapper = vm2.b; + return results.map((item) => mapper(item)).toList(); +} diff --git a/lib/controller.dart b/lib/controller.dart index e0a72e1..5637d7f 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -210,6 +210,10 @@ extension StateControllerExt on AppController { return _ref.read(isMobileViewProvider); } + Size get viewSize { + return _ref.read(viewSizeProvider); + } + bool get isStart { return _ref.read(isStartProvider); } diff --git a/lib/database/database.dart b/lib/database/database.dart index 480416f..7f0728e 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -134,4 +135,60 @@ extension JoinedSelectStatementExt } } +// extension _AsyncMapPerSubscription on Stream { +// Stream asyncMapPerSubscription(FutureOr Function(S) mapper) { +// return Stream.multi((listener) { +// late StreamSubscription subscription; +// +// void onData(S original) { +// subscription.pause(); +// Future.sync(() => mapper(original)) +// .then(listener.addSync, onError: listener.addErrorSync) +// .whenComplete(subscription.resume); +// } +// +// subscription = listen( +// onData, +// onError: listener.addErrorSync, +// onDone: listener.closeSync, +// cancelOnError: false, // Determined by downstream subscription +// ); +// +// listener +// ..onPause = subscription.pause +// ..onResume = subscription.resume +// ..onCancel = subscription.cancel; +// }, isBroadcast: isBroadcast); +// } +// } + +// extension SelectableExt on Selectable { +// Selectable isolateMap(N Function(T) mapper) { +// return _IsolateMappedSelectable(this, mapper); +// } +// } +// +// class _IsolateMappedSelectable extends Selectable { +// final Selectable _source; +// final T Function(S) _mapper; +// +// _IsolateMappedSelectable(this._source, this._mapper); +// +// @override +// Future> get() { +// return _source.get().then(_mapResults); +// } +// +// @override +// Stream> watch() { +// return _AsyncMapPerSubscription( +// _source.watch(), +// ).asyncMapPerSubscription(_mapResults); +// } +// +// Future> _mapResults(List results) async { +// return mapListTask(results, _mapper); +// } +// } + final database = Database(); diff --git a/lib/database/rules.dart b/lib/database/rules.dart index 14acb83..36427d5 100644 --- a/lib/database/rules.dart +++ b/lib/database/rules.dart @@ -216,6 +216,7 @@ class RulesDao extends DatabaseAccessor with _$RulesDaoMixin { Selectable _get({int? profileId, RuleScene? scene}) { final query = _getSelectStatement(profileId: profileId, scene: scene); + return query.map((row) { return row.readTable(rules).toRule(row.read(profileRuleLinks.order)); }); diff --git a/lib/models/clash_config.dart b/lib/models/clash_config.dart index 7016c8d..390cfeb 100644 --- a/lib/models/clash_config.dart +++ b/lib/models/clash_config.dart @@ -113,7 +113,7 @@ abstract class ProxyGroup with _$ProxyGroup { int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, - @JsonKey(name: 'expected-filter') String? excludeFilter, + @JsonKey(name: 'exclude-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, diff --git a/lib/models/generated/clash_config.freezed.dart b/lib/models/generated/clash_config.freezed.dart index 7fbee5e..47973b8 100644 --- a/lib/models/generated/clash_config.freezed.dart +++ b/lib/models/generated/clash_config.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$ProxyGroup { - String get name;@JsonKey(fromJson: GroupType.parseProfileType) GroupType get type; List? get proxies; List? get use; int? get interval; bool? get lazy;@JsonKey(name: 'disable-udp') bool? get disableUDP; String? get url; int? get timeout;@JsonKey(name: 'max-failed-times') int? get maxFailedTimes; String? get filter;@JsonKey(name: 'expected-filter') String? get excludeFilter;@JsonKey(name: 'exclude-type') String? get excludeType;@JsonKey(name: 'expected-status') String? get expectedStatus;@JsonKey(name: 'include-all') bool? get includeAll;@JsonKey(name: 'include-all-proxies') bool? get includeAllProxies;@JsonKey(name: 'include-all-providers') bool? get includeAllProviders; bool? get hidden; String? get icon; String? get order; + String get name;@JsonKey(fromJson: GroupType.parseProfileType) GroupType get type; List? get proxies; List? get use; int? get interval; bool? get lazy;@JsonKey(name: 'disable-udp') bool? get disableUDP; String? get url; int? get timeout;@JsonKey(name: 'max-failed-times') int? get maxFailedTimes; String? get filter;@JsonKey(name: 'exclude-filter') String? get excludeFilter;@JsonKey(name: 'exclude-type') String? get excludeType;@JsonKey(name: 'expected-status') String? get expectedStatus;@JsonKey(name: 'include-all') bool? get includeAll;@JsonKey(name: 'include-all-proxies') bool? get includeAllProxies;@JsonKey(name: 'include-all-providers') bool? get includeAllProviders; bool? get hidden; String? get icon; String? get order; /// Create a copy of ProxyGroup /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -48,7 +48,7 @@ abstract mixin class $ProxyGroupCopyWith<$Res> { factory $ProxyGroupCopyWith(ProxyGroup value, $Res Function(ProxyGroup) _then) = _$ProxyGroupCopyWithImpl; @useResult $Res call({ - String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy,@JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout,@JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter,@JsonKey(name: 'expected-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') String? expectedStatus,@JsonKey(name: 'include-all') bool? includeAll,@JsonKey(name: 'include-all-proxies') bool? includeAllProxies,@JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order + String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy,@JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout,@JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter,@JsonKey(name: 'exclude-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') String? expectedStatus,@JsonKey(name: 'include-all') bool? includeAll,@JsonKey(name: 'include-all-proxies') bool? includeAllProxies,@JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order }); @@ -172,7 +172,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'expected-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'exclude-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _ProxyGroup() when $default != null: return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.disableUDP,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.includeAll,_that.includeAllProxies,_that.includeAllProviders,_that.hidden,_that.icon,_that.order);case _: @@ -193,7 +193,7 @@ return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_th /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'expected-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'exclude-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order) $default,) {final _that = this; switch (_that) { case _ProxyGroup(): return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.disableUDP,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.includeAll,_that.includeAllProxies,_that.includeAllProviders,_that.hidden,_that.icon,_that.order);case _: @@ -213,7 +213,7 @@ return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_th /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'expected-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy, @JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout, @JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter, @JsonKey(name: 'exclude-filter') String? excludeFilter, @JsonKey(name: 'exclude-type') String? excludeType, @JsonKey(name: 'expected-status') String? expectedStatus, @JsonKey(name: 'include-all') bool? includeAll, @JsonKey(name: 'include-all-proxies') bool? includeAllProxies, @JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order)? $default,) {final _that = this; switch (_that) { case _ProxyGroup() when $default != null: return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.disableUDP,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.includeAll,_that.includeAllProxies,_that.includeAllProviders,_that.hidden,_that.icon,_that.order);case _: @@ -228,7 +228,7 @@ return $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_th @JsonSerializable() class _ProxyGroup implements ProxyGroup { - const _ProxyGroup({required this.name, @JsonKey(fromJson: GroupType.parseProfileType) required this.type, final List? proxies, final List? use, this.interval, this.lazy, @JsonKey(name: 'disable-udp') this.disableUDP, this.url, this.timeout, @JsonKey(name: 'max-failed-times') this.maxFailedTimes, this.filter, @JsonKey(name: 'expected-filter') this.excludeFilter, @JsonKey(name: 'exclude-type') this.excludeType, @JsonKey(name: 'expected-status') this.expectedStatus, @JsonKey(name: 'include-all') this.includeAll, @JsonKey(name: 'include-all-proxies') this.includeAllProxies, @JsonKey(name: 'include-all-providers') this.includeAllProviders, this.hidden, this.icon, this.order}): _proxies = proxies,_use = use; + const _ProxyGroup({required this.name, @JsonKey(fromJson: GroupType.parseProfileType) required this.type, final List? proxies, final List? use, this.interval, this.lazy, @JsonKey(name: 'disable-udp') this.disableUDP, this.url, this.timeout, @JsonKey(name: 'max-failed-times') this.maxFailedTimes, this.filter, @JsonKey(name: 'exclude-filter') this.excludeFilter, @JsonKey(name: 'exclude-type') this.excludeType, @JsonKey(name: 'expected-status') this.expectedStatus, @JsonKey(name: 'include-all') this.includeAll, @JsonKey(name: 'include-all-proxies') this.includeAllProxies, @JsonKey(name: 'include-all-providers') this.includeAllProviders, this.hidden, this.icon, this.order}): _proxies = proxies,_use = use; factory _ProxyGroup.fromJson(Map json) => _$ProxyGroupFromJson(json); @override final String name; @@ -258,7 +258,7 @@ class _ProxyGroup implements ProxyGroup { @override final int? timeout; @override@JsonKey(name: 'max-failed-times') final int? maxFailedTimes; @override final String? filter; -@override@JsonKey(name: 'expected-filter') final String? excludeFilter; +@override@JsonKey(name: 'exclude-filter') final String? excludeFilter; @override@JsonKey(name: 'exclude-type') final String? excludeType; @override@JsonKey(name: 'expected-status') final String? expectedStatus; @override@JsonKey(name: 'include-all') final bool? includeAll; @@ -301,7 +301,7 @@ abstract mixin class _$ProxyGroupCopyWith<$Res> implements $ProxyGroupCopyWith<$ factory _$ProxyGroupCopyWith(_ProxyGroup value, $Res Function(_ProxyGroup) _then) = __$ProxyGroupCopyWithImpl; @override @useResult $Res call({ - String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy,@JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout,@JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter,@JsonKey(name: 'expected-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') String? expectedStatus,@JsonKey(name: 'include-all') bool? includeAll,@JsonKey(name: 'include-all-proxies') bool? includeAllProxies,@JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order + String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List? proxies, List? use, int? interval, bool? lazy,@JsonKey(name: 'disable-udp') bool? disableUDP, String? url, int? timeout,@JsonKey(name: 'max-failed-times') int? maxFailedTimes, String? filter,@JsonKey(name: 'exclude-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') String? expectedStatus,@JsonKey(name: 'include-all') bool? includeAll,@JsonKey(name: 'include-all-proxies') bool? includeAllProxies,@JsonKey(name: 'include-all-providers') bool? includeAllProviders, bool? hidden, String? icon, String? order }); diff --git a/lib/models/generated/clash_config.g.dart b/lib/models/generated/clash_config.g.dart index 7326b91..0ba348c 100644 --- a/lib/models/generated/clash_config.g.dart +++ b/lib/models/generated/clash_config.g.dart @@ -20,7 +20,7 @@ _ProxyGroup _$ProxyGroupFromJson(Map json) => _ProxyGroup( timeout: (json['timeout'] as num?)?.toInt(), maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(), filter: json['filter'] as String?, - excludeFilter: json['expected-filter'] as String?, + excludeFilter: json['exclude-filter'] as String?, excludeType: json['exclude-type'] as String?, expectedStatus: json['expected-status'] as String?, includeAll: json['include-all'] as bool?, @@ -44,7 +44,7 @@ Map _$ProxyGroupToJson(_ProxyGroup instance) => 'timeout': instance.timeout, 'max-failed-times': instance.maxFailedTimes, 'filter': instance.filter, - 'expected-filter': instance.excludeFilter, + 'exclude-filter': instance.excludeFilter, 'exclude-type': instance.excludeType, 'expected-status': instance.expectedStatus, 'include-all': instance.includeAll, diff --git a/lib/pages/editor.dart b/lib/pages/editor.dart index 0fe7a2d..9ea2a29 100644 --- a/lib/pages/editor.dart +++ b/lib/pages/editor.dart @@ -192,7 +192,7 @@ class _EditorPageState extends ConsumerState { enabled: widget.titleEditable, controller: _titleController, decoration: InputDecoration( - border: _NoInputBorder(), + border: NoInputBorder(), counter: SizedBox(), hintText: appLocalizations.unnamed, ), @@ -639,55 +639,6 @@ class ContextMenuControllerImpl implements SelectionToolbarController { } } -class _NoInputBorder extends InputBorder { - const _NoInputBorder() : super(borderSide: BorderSide.none); - - @override - _NoInputBorder copyWith({BorderSide? borderSide}) => const _NoInputBorder(); - - @override - bool get isOutline => false; - - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.zero; - - @override - _NoInputBorder scale(double t) => const _NoInputBorder(); - - @override - Path getInnerPath(Rect rect, {TextDirection? textDirection}) { - return Path()..addRect(rect); - } - - @override - Path getOuterPath(Rect rect, {TextDirection? textDirection}) { - return Path()..addRect(rect); - } - - @override - void paintInterior( - Canvas canvas, - Rect rect, - Paint paint, { - TextDirection? textDirection, - }) { - canvas.drawRect(rect, paint); - } - - @override - bool get preferPaintInterior => true; - - @override - void paint( - Canvas canvas, - Rect rect, { - double? gapStart, - double gapExtent = 0.0, - double gapPercentage = 0.0, - TextDirection? textDirection, - }) {} -} - class _ImportOptionsDialog extends StatefulWidget { const _ImportOptionsDialog(); diff --git a/lib/views/profiles/overwrite/custom.dart b/lib/views/profiles/overwrite/custom.dart index 28820a1..d705485 100644 --- a/lib/views/profiles/overwrite/custom.dart +++ b/lib/views/profiles/overwrite/custom.dart @@ -148,6 +148,20 @@ class _CustomProxyGroupsView extends ConsumerWidget { ref.read(proxyGroupsProvider(profileId).notifier).order(oldIndex, newIndex); } + void _handleEditProxyGroup(BuildContext context, ProxyGroup proxyGroup) { + showSheet( + context: context, + props: SheetProps(isScrollControlled: true), + builder: (_, type) { + return AdaptiveSheetScaffold( + type: type, + body: _EditCustomProxyGroupView(), + title: '编辑', + ); + }, + ); + } + @override Widget build(BuildContext context, WidgetRef ref) { final proxyGroups = ref.watch(proxyGroupsProvider(profileId)).value ?? []; @@ -165,7 +179,9 @@ class _CustomProxyGroupsView extends ConsumerWidget { child: CommonCard( radius: 16, padding: EdgeInsets.all(16), - onPressed: () {}, + onPressed: () { + _handleEditProxyGroup(context, proxyGroup); + }, child: ListTile( minTileHeight: 0, minVerticalPadding: 0, @@ -190,16 +206,79 @@ class _CustomProxyGroupsView extends ConsumerWidget { } } +class _EditCustomProxyGroupView extends ConsumerStatefulWidget { + const _EditCustomProxyGroupView(); + + @override + ConsumerState createState() => _EditCustomProxyGroupViewState(); +} + +class _EditCustomProxyGroupViewState + extends ConsumerState<_EditCustomProxyGroupView> { + @override + Widget build(BuildContext context) { + return Container( + height: appController.viewSize.height * 0.65, + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 16), + children: [ + generateSectionV3( + title: '通用', + items: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 16, + children: [ + Text('名称'), + Flexible( + child: TextFormField( + textAlign: TextAlign.end, + decoration: InputDecoration.collapsed( + border: NoInputBorder(), + hintText: '输入代理组名称', + ), + ), + ), + ], + ), + Text('类型'), + Text('图标'), + Text('从列表中隐藏'), + Text('禁用udp'), + ], + ), + generateSectionV3( + title: '节点', + items: [ + Text('手动选择代理'), + Text('包括所有代理'), + Text('包括所有远程代理集'), + Text('正则表达式过滤器'), + Text('排除类型'), + Text('预期状态'), + ], + ), + generateSectionV3( + title: '其他', + items: [Text('测试链接'), Text('最大失败时间'), Text('懒加载'), Text('测试间隔')], + ), + generateSectionV3(title: '操作', items: [Text('删除')]), + ], + ), + ); + } +} + class _CustomRulesView extends ConsumerStatefulWidget { final int profileId; const _CustomRulesView(this.profileId); @override - ConsumerState createState() => __CustomRulesViewState(); + ConsumerState createState() => _CustomRulesViewState(); } -class __CustomRulesViewState extends ConsumerState<_CustomRulesView> { +class _CustomRulesViewState extends ConsumerState<_CustomRulesView> { final _key = utils.id; void _handleReorder(int oldIndex, int newIndex) { @@ -246,6 +325,11 @@ class __CustomRulesViewState extends ConsumerState<_CustomRulesView> { ref.read(selectedItemsProvider(_key).notifier).value = {}; } + @override + void initState() { + super.initState(); + } + @override Widget build(context) { final rules = diff --git a/lib/widgets/input.dart b/lib/widgets/input.dart index 1ef8fb0..1ad8ae2 100644 --- a/lib/widgets/input.dart +++ b/lib/widgets/input.dart @@ -818,3 +818,52 @@ class _AddDialogState extends State { ); } } + +class NoInputBorder extends InputBorder { + const NoInputBorder() : super(borderSide: BorderSide.none); + + @override + NoInputBorder copyWith({BorderSide? borderSide}) => const NoInputBorder(); + + @override + bool get isOutline => false; + + @override + EdgeInsetsGeometry get dimensions => EdgeInsets.zero; + + @override + NoInputBorder scale(double t) => const NoInputBorder(); + + @override + Path getInnerPath(Rect rect, {TextDirection? textDirection}) { + return Path()..addRect(rect); + } + + @override + Path getOuterPath(Rect rect, {TextDirection? textDirection}) { + return Path()..addRect(rect); + } + + @override + void paintInterior( + Canvas canvas, + Rect rect, + Paint paint, { + TextDirection? textDirection, + }) { + canvas.drawRect(rect, paint); + } + + @override + bool get preferPaintInterior => true; + + @override + void paint( + Canvas canvas, + Rect rect, { + double? gapStart, + double gapExtent = 0.0, + double gapPercentage = 0.0, + TextDirection? textDirection, + }) {} +} diff --git a/lib/widgets/list.dart b/lib/widgets/list.dart index 97e88d6..5df44de 100644 --- a/lib/widgets/list.dart +++ b/lib/widgets/list.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/controller.dart'; import 'package:fl_clash/enum/enum.dart'; @@ -540,6 +541,26 @@ Widget generateSectionV2({ ); } +Widget generateSectionV3({ + String? title, + required Iterable items, + List? actions, + bool separated = true, +}) { + final genItems = items.mapIndexed((index, item) { + final isFirst = index == 0; + final isLast = index == items.length - 1; + return CommonInputListItem(title: item, isFirst: isFirst, isLast: isLast); + }); + return Column( + children: [ + if (items.isNotEmpty && title != null) + ListHeader(title: title, actions: actions), + Column(children: [...genItems]), + ], + ); +} + List generateInfoSection({ required Info info, required Iterable items,