cache
This commit is contained in:
@@ -6,16 +6,15 @@ class CommonTheme {
|
||||
final Map<String, Color> _colorMap;
|
||||
final double textScaleFactor;
|
||||
|
||||
CommonTheme.of(
|
||||
this.context,
|
||||
this.textScaleFactor,
|
||||
) : _colorMap = {};
|
||||
CommonTheme.of(this.context, this.textScaleFactor) : _colorMap = {};
|
||||
|
||||
Color get darkenSecondaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darkenSecondaryContainer',
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1),
|
||||
() => context.colorScheme.secondaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,16 +30,20 @@ class CommonTheme {
|
||||
Color get darken2SecondaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darken2SecondaryContainer',
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.2),
|
||||
() => context.colorScheme.secondaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken3PrimaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darken3PrimaryContainer',
|
||||
() => context.colorScheme.primaryContainer
|
||||
.blendDarken(context, factor: 0.3),
|
||||
() => context.colorScheme.primaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.3,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,3 +430,5 @@ enum LoadingTag { profiles, backup_restore, access, proxies }
|
||||
enum CoreStatus { connecting, connected, disconnected }
|
||||
|
||||
enum RuleScene { added, disabled, custom }
|
||||
|
||||
enum ItemPosition { start, middle, end, startAndEnd }
|
||||
|
||||
@@ -347,6 +347,14 @@ class ProfileDisabledRuleIds extends _$ProfileDisabledRuleIds
|
||||
.watch();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(
|
||||
AsyncValue<List<int>> previous,
|
||||
AsyncValue<List<int>> next,
|
||||
) {
|
||||
return !intListEquality.equals(previous.value, next.value);
|
||||
}
|
||||
|
||||
void _put(int ruleId) {
|
||||
var newList = List<int>.from(value);
|
||||
final index = newList.indexWhere((item) => item == ruleId);
|
||||
|
||||
@@ -792,7 +792,7 @@ final class ProfileDisabledRuleIdsProvider
|
||||
}
|
||||
|
||||
String _$profileDisabledRuleIdsHash() =>
|
||||
r'22d6e68bcee55b42fbb909e7f66e5c7095935224';
|
||||
r'5093cc1d77ec69a2c1db6efa86a3f5916475d4f0';
|
||||
|
||||
final class ProfileDisabledRuleIdsFamily extends $Family
|
||||
with
|
||||
|
||||
@@ -215,54 +215,148 @@ class _EditCustomProxyGroupView extends ConsumerStatefulWidget {
|
||||
|
||||
class _EditCustomProxyGroupViewState
|
||||
extends ConsumerState<_EditCustomProxyGroupView> {
|
||||
Widget _buildItem({required Widget title, Widget? trailing}) {
|
||||
return CommonInputListItem(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: 16,
|
||||
children: [
|
||||
title,
|
||||
if (trailing != null)
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
height: globalState.measure.bodyLargeHeight + 6,
|
||||
child: trailing,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
height: appController.viewSize.height * 0.65,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 24),
|
||||
children: [
|
||||
generateSectionV3(
|
||||
title: '通用',
|
||||
items: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text('名称'),
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '输入代理组名称',
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('名称'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '输入代理组名称',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildItem(title: Text('类型')),
|
||||
_buildItem(title: Text('图标')),
|
||||
_buildItem(
|
||||
title: Text('从列表中隐藏'),
|
||||
trailing: Switch(value: false, onChanged: (_) {}),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('禁用UDP'),
|
||||
trailing: Switch(value: false, onChanged: (_) {}),
|
||||
),
|
||||
Text('类型'),
|
||||
Text('图标'),
|
||||
Text('从列表中隐藏'),
|
||||
Text('禁用udp'),
|
||||
],
|
||||
),
|
||||
generateSectionV3(
|
||||
title: '节点',
|
||||
items: [
|
||||
Text('手动选择代理'),
|
||||
Text('包括所有代理'),
|
||||
Text('包括所有远程代理集'),
|
||||
Text('正则表达式过滤器'),
|
||||
Text('排除类型'),
|
||||
Text('预期状态'),
|
||||
_buildItem(title: Text('选择代理')),
|
||||
_buildItem(title: Text('选择代理集')),
|
||||
_buildItem(
|
||||
title: Text('节点过滤器'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('排除过滤器'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('排除类型'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('预期状态'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
generateSectionV3(
|
||||
title: '其他',
|
||||
items: [Text('测试链接'), Text('最大失败时间'), Text('懒加载'), Text('测试间隔')],
|
||||
items: [
|
||||
_buildItem(
|
||||
title: Text('测速链接'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('最大失败次数'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('使用时测速'),
|
||||
trailing: Switch(value: false, onChanged: (_) {}),
|
||||
),
|
||||
_buildItem(
|
||||
title: Text('测速间隔'),
|
||||
trailing: TextFormField(
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration.collapsed(
|
||||
border: NoInputBorder(),
|
||||
hintText: '可选',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
generateSectionV3(
|
||||
title: '操作',
|
||||
items: [_buildItem(title: Text('删除'))],
|
||||
),
|
||||
generateSectionV3(title: '操作', items: [Text('删除')]),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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/core/controller.dart';
|
||||
|
||||
@@ -449,19 +449,26 @@ class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
|
||||
}
|
||||
|
||||
Widget _buildItem(int index, [bool isDecorator = false]) {
|
||||
final isLast = index == profiles.length - 1;
|
||||
final isFirst = index == 0;
|
||||
ItemPosition position = ItemPosition.middle;
|
||||
if (profiles.length == 1) {
|
||||
position = ItemPosition.startAndEnd;
|
||||
} else if (index == profiles.length - 1) {
|
||||
position = ItemPosition.end;
|
||||
} else if (index == 0) {
|
||||
position = ItemPosition.start;
|
||||
}
|
||||
final profile = profiles[index];
|
||||
return CommonInputListItem(
|
||||
return ItemPositionProvider(
|
||||
key: Key(profile.id.toString()),
|
||||
trailing: ReorderableDelayedDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
position: position,
|
||||
child: CommonInputListItem(
|
||||
trailing: ReorderableDelayedDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
title: Text(profile.realLabel),
|
||||
isDecorator: isDecorator,
|
||||
),
|
||||
title: Text(profile.realLabel),
|
||||
isFirst: isFirst,
|
||||
isLast: isLast,
|
||||
isDecorator: isDecorator,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||
@@ -39,3 +40,21 @@ class CommonScaffoldFabExtendedProvider extends InheritedWidget {
|
||||
bool updateShouldNotify(CommonScaffoldFabExtendedProvider oldWidget) =>
|
||||
isExtended != oldWidget.isExtended;
|
||||
}
|
||||
|
||||
class ItemPositionProvider extends InheritedWidget {
|
||||
final ItemPosition position;
|
||||
|
||||
const ItemPositionProvider({
|
||||
super.key,
|
||||
required this.position,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static ItemPositionProvider? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ItemPositionProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ItemPositionProvider oldWidget) =>
|
||||
position != oldWidget.position;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:fl_clash/widgets/null_status.dart';
|
||||
import 'package:fl_clash/widgets/pop_scope.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
@@ -324,30 +326,37 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
required bool isEditing,
|
||||
isDecorator = false,
|
||||
}) {
|
||||
final isFirst = index == 0;
|
||||
final isLast = index == totalLength - 1;
|
||||
ItemPosition position = ItemPosition.middle;
|
||||
if (totalLength == 1) {
|
||||
position = ItemPosition.startAndEnd;
|
||||
} else if (index == totalLength - 1) {
|
||||
position = ItemPosition.end;
|
||||
} else if (index == 0) {
|
||||
position = ItemPosition.start;
|
||||
}
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey(value),
|
||||
index: index,
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
isLast: isLast,
|
||||
isFirst: isFirst,
|
||||
title: widget.titleBuilder(value),
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
title: widget.titleBuilder(value),
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -577,30 +586,37 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
required bool isEditing,
|
||||
isDecorator = false,
|
||||
}) {
|
||||
final isFirst = index == 0;
|
||||
final isLast = index == totalLength - 1;
|
||||
ItemPosition position = ItemPosition.middle;
|
||||
if (totalLength == 1) {
|
||||
position = ItemPosition.startAndEnd;
|
||||
} else if (index == totalLength - 1) {
|
||||
position = ItemPosition.end;
|
||||
} else if (index == 0) {
|
||||
position = ItemPosition.start;
|
||||
}
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey(value),
|
||||
index: index,
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
isLast: isLast,
|
||||
isFirst: isFirst,
|
||||
title: widget.titleBuilder(value),
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
title: widget.titleBuilder(value),
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -545,12 +546,19 @@ 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);
|
||||
if (items.length == 1) {
|
||||
return ItemPositionProvider(
|
||||
position: ItemPosition.startAndEnd,
|
||||
child: item,
|
||||
);
|
||||
} else if (index == 0) {
|
||||
return ItemPositionProvider(position: ItemPosition.start, child: item);
|
||||
} else if (index == items.length - 1) {
|
||||
return ItemPositionProvider(position: ItemPosition.end, child: item);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return Column(
|
||||
children: [
|
||||
@@ -643,8 +651,6 @@ class CommonSelectedListItem extends StatelessWidget {
|
||||
|
||||
class CommonInputListItem extends StatelessWidget {
|
||||
final bool isDecorator;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final Widget? title;
|
||||
final Widget? subtitle;
|
||||
final Widget? leading;
|
||||
@@ -655,8 +661,6 @@ class CommonInputListItem extends StatelessWidget {
|
||||
const CommonInputListItem({
|
||||
super.key,
|
||||
this.isDecorator = false,
|
||||
this.isFirst = false,
|
||||
this.isLast = false,
|
||||
this.title,
|
||||
this.leading,
|
||||
this.trailing,
|
||||
@@ -667,6 +671,15 @@ class CommonInputListItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final position = ItemPositionProvider.of(context)?.position;
|
||||
final isStart = [
|
||||
ItemPosition.start,
|
||||
ItemPosition.startAndEnd,
|
||||
].contains(position);
|
||||
final isEnd = [
|
||||
ItemPosition.end,
|
||||
ItemPosition.startAndEnd,
|
||||
].contains(position);
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: ShapeDecoration(
|
||||
@@ -674,8 +687,8 @@ class CommonInputListItem extends StatelessWidget {
|
||||
? LinearBorder.none
|
||||
: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: isFirst ? Radius.circular(24) : Radius.zero,
|
||||
bottom: isLast ? Radius.circular(24) : Radius.zero,
|
||||
top: isStart ? Radius.circular(24) : Radius.zero,
|
||||
bottom: isEnd ? Radius.circular(24) : Radius.zero,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -697,7 +710,7 @@ class CommonInputListItem extends StatelessWidget {
|
||||
trailing: trailing,
|
||||
),
|
||||
),
|
||||
if (isDecorator != true && !isLast)
|
||||
if (isDecorator != true && !isEnd)
|
||||
Divider(height: 0, indent: 14, endIndent: 14),
|
||||
],
|
||||
),
|
||||
@@ -713,8 +726,6 @@ class CommonSelectedInputListItem extends StatelessWidget {
|
||||
final Widget? subtitle;
|
||||
final VoidCallback onSelected;
|
||||
final VoidCallback onPressed;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final bool isDecorator;
|
||||
final Widget? leading;
|
||||
|
||||
@@ -725,8 +736,6 @@ class CommonSelectedInputListItem extends StatelessWidget {
|
||||
this.isEditing = false,
|
||||
required this.title,
|
||||
required this.onPressed,
|
||||
this.isFirst = false,
|
||||
this.isLast = false,
|
||||
this.isDecorator = false,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
@@ -738,8 +747,6 @@ class CommonSelectedInputListItem extends StatelessWidget {
|
||||
title: title,
|
||||
isDecorator: isDecorator,
|
||||
isSelected: isSelected,
|
||||
isFirst: isFirst,
|
||||
isLast: isLast,
|
||||
leading: leading,
|
||||
onPressed: isDecorator
|
||||
? null
|
||||
|
||||
Reference in New Issue
Block a user