This commit is contained in:
chen08209
2026-03-04 18:47:56 +08:00
parent b61d985657
commit 50e0ae721e
13 changed files with 245 additions and 65 deletions

View File

@@ -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';

View File

View File

@@ -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<List<T>> mapListTask<T, S>(List<S> results, T Function(S) mapper) async {
return await compute<VM2<List<S>, T Function(S)>, List<T>>(
_mapListTask,
VM2(results, mapper),
);
}
Future<List<T>> _mapListTask<T, S>(VM2<List<S>, T Function(S)> vm2) async {
final results = vm2.a;
final mapper = vm2.b;
return results.map((item) => mapper(item)).toList();
}

View File

@@ -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);
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -134,4 +135,60 @@ extension JoinedSelectStatementExt<T extends HasResultSet, D>
}
}
// extension _AsyncMapPerSubscription<S> on Stream<S> {
// Stream<T> asyncMapPerSubscription<T>(FutureOr<T> Function(S) mapper) {
// return Stream.multi((listener) {
// late StreamSubscription<S> 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<T> on Selectable<T> {
// Selectable<N> isolateMap<N>(N Function(T) mapper) {
// return _IsolateMappedSelectable<T, N>(this, mapper);
// }
// }
//
// class _IsolateMappedSelectable<S, T> extends Selectable<T> {
// final Selectable<S> _source;
// final T Function(S) _mapper;
//
// _IsolateMappedSelectable(this._source, this._mapper);
//
// @override
// Future<List<T>> get() {
// return _source.get().then(_mapResults);
// }
//
// @override
// Stream<List<T>> watch() {
// return _AsyncMapPerSubscription(
// _source.watch(),
// ).asyncMapPerSubscription(_mapResults);
// }
//
// Future<List<T>> _mapResults(List<S> results) async {
// return mapListTask(results, _mapper);
// }
// }
final database = Database();

View File

@@ -216,6 +216,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
Selectable<Rule> _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));
});

View File

@@ -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,

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ProxyGroup {
String get name;@JsonKey(fromJson: GroupType.parseProfileType) GroupType get type; List<String>? get proxies; List<String>? 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<String>? get proxies; List<String>? 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<String>? proxies, List<String>? 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<String>? proxies, List<String>? 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 extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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 extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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 extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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 extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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 extends Object?>(TResult? Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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 extends Object?>(TResult? Function( String name, @JsonKey(fromJson: GroupType.parseProfileType) GroupType type, List<String>? proxies, List<String>? 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<String>? proxies, final List<String>? 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<String>? proxies, final List<String>? 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<String, dynamic> 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<String>? proxies, List<String>? 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<String>? proxies, List<String>? 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
});

View File

@@ -20,7 +20,7 @@ _ProxyGroup _$ProxyGroupFromJson(Map<String, dynamic> 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<String, dynamic> _$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,

View File

@@ -192,7 +192,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
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();

View File

@@ -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 =

View File

@@ -818,3 +818,52 @@ class _AddDialogState extends State<AddDialog> {
);
}
}
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,
}) {}
}

View File

@@ -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<Widget> items,
List<Widget>? actions,
bool separated = true,
}) {
final genItems = items.mapIndexed<Widget>((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<Widget> generateInfoSection({
required Info info,
required Iterable<Widget> items,