Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-06-21 12:46:34 +08:00
parent 5f3f158932
commit 604d78ad6a
161 changed files with 4873 additions and 4770 deletions

View File

@@ -0,0 +1,658 @@
import 'dart:io';
import 'package:PiliPlus/common/widgets/custom_toast.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
import 'package:PiliPlus/models/common/msg/msg_unread_type.dart';
import 'package:PiliPlus/models/common/nav_bar_config.dart';
import 'package:PiliPlus/models/common/settings_type.dart';
import 'package:PiliPlus/models/common/theme/theme_type.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/pages/setting/models/model.dart';
import 'package:PiliPlus/pages/setting/pages/color_select.dart';
import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart';
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
import 'package:PiliPlus/router/app_pages.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:auto_orientation/auto_orientation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
List<SettingsModel> get styleSettings => [
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '横屏适配',
subtitle: '启用横屏布局与逻辑,平板、折叠屏等可开启;建议全屏方向设为【不改变当前方向】',
leading: const Icon(Icons.phonelink_outlined),
setKey: SettingBoxKey.horizontalScreen,
defaultVal: Pref.horizontalScreen,
onChanged: (value) {
if (value) {
autoScreen();
} else {
AutoOrientation.portraitUpMode();
}
}),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '改用侧边栏',
subtitle: '开启后底栏与顶栏被替换,且相关设置失效',
leading: const Icon(Icons.chrome_reader_mode_outlined),
setKey: SettingBoxKey.useSideBar,
defaultVal: false,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: 'App字体字重',
subtitle: '点击设置',
setKey: SettingBoxKey.appFontWeight,
defaultVal: false,
onTap: () {
showDialog<double>(
context: Get.context!,
builder: (context) {
return SlideDialog(
title: 'App字体字重',
value: Pref.appFontWeight.toDouble() + 1,
min: 1,
max: FontWeight.values.length.toDouble(),
divisions: FontWeight.values.length - 1,
);
},
).then((res) async {
if (res != null) {
await GStorage.setting
.put(SettingBoxKey.appFontWeight, res.toInt() - 1);
Get.forceAppUpdate();
}
});
},
leading: const Icon(Icons.text_fields),
onChanged: (value) {
Get.forceAppUpdate();
},
),
SettingsModel(
settingsType: SettingsType.normal,
title: '页面过渡动画',
leading: const Icon(Icons.animation),
getSubtitle: () => '当前:${CustomGetPage.pageTransition.name}',
onTap: (setState) async {
Transition? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<Transition>(
title: '页面过渡动画',
value: CustomGetPage.pageTransition,
values: Transition.values.map((e) {
return (e, e.name);
}).toList(),
);
},
);
if (result != null) {
CustomGetPage.pageTransition = result;
await GStorage.setting
.put(SettingBoxKey.pageTransition, result.index);
SmartDialog.showToast('重启生效');
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '优化平板导航栏',
leading: const Icon(MdiIcons.soundbar),
setKey: SettingBoxKey.optTabletNav,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: 'MD3样式底栏',
subtitle: 'Material You设计规范底栏关闭可变窄',
leading: const Icon(Icons.design_services_outlined),
setKey: SettingBoxKey.enableMYBar,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
double? result = await showDialog(
context: Get.context!,
builder: (context) {
return SlideDialog(
title: '列表最大列宽度默认240dp',
value: Pref.smallCardWidth,
min: 150.0,
max: 500.0,
divisions: 35,
suffix: 'dp',
);
});
if (result != null) {
await GStorage.setting.put(SettingBoxKey.smallCardWidth, result);
SmartDialog.showToast('重启生效');
setState();
}
},
leading: const Icon(Icons.calendar_view_week_outlined),
title: '列表宽度dp限制',
getSubtitle: () =>
'当前:${Pref.smallCardWidth.toInt()}dp屏幕宽度:${Get.mediaQuery.size.width.toPrecision(2)}dp。宽度越小列数越多。',
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '视频播放页使用深色主题',
leading: const Icon(Icons.dark_mode_outlined),
setKey: SettingBoxKey.darkVideoPage,
defaultVal: false,
onChanged: (value) {
if (value && MyApp.darkThemeData == null) {
Get.forceAppUpdate();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '播放页移除安全边距',
subtitle: '隐藏状态栏、撑满屏幕,但播放控件仍处于安全域内',
leading: const Icon(Icons.fit_screen_outlined),
setKey: SettingBoxKey.videoPlayerRemoveSafeArea,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '动态页启用瀑布流',
subtitle: '关闭会显示为单列',
leading: const Icon(Icons.view_array_outlined),
setKey: SettingBoxKey.dynamicsWaterfallFlow,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.normal,
title: '动态页UP主显示位置',
leading: const Icon(Icons.person_outlined),
getSubtitle: () => '当前:${Pref.upPanelPosition.label}',
onTap: (setState) async {
UpPanelPosition? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<UpPanelPosition>(
title: '动态页UP主显示位置',
value: Pref.upPanelPosition,
values: UpPanelPosition.values.map((e) {
return (e, e.label);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting
.put(SettingBoxKey.upPanelPosition, result.index);
SmartDialog.showToast('重启生效');
setState();
}
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '动态页显示所有已关注UP主',
subtitle: '并以最常访问排序UP',
leading: const Icon(Icons.people_alt_outlined),
setKey: SettingBoxKey.dynamicsShowAllFollowedUp,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '动态页展开正在直播UP列表',
leading: const Icon(Icons.live_tv),
setKey: SettingBoxKey.expandDynLivePanel,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
DynamicBadgeMode? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<DynamicBadgeMode>(
title: '动态未读标记',
value: Pref.dynamicBadgeType,
values: DynamicBadgeMode.values.map((e) {
return (e, e.desc);
}).toList(),
);
},
);
if (result != null) {
MainController mainController = Get.put(MainController())
..dynamicBadgeMode = DynamicBadgeMode.values[result.index];
if (mainController.dynamicBadgeMode != DynamicBadgeMode.hidden) {
mainController.getUnreadDynamic();
}
await GStorage.setting
.put(SettingBoxKey.dynamicBadgeMode, result.index);
SmartDialog.showToast('设置成功');
setState();
}
},
title: '动态未读标记',
leading: const Icon(Icons.motion_photos_on_outlined),
getSubtitle: () => '当前标记样式:${Pref.dynamicBadgeType.desc}',
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
DynamicBadgeMode? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<DynamicBadgeMode>(
title: '消息未读标记',
value: Pref.msgBadgeMode,
values: DynamicBadgeMode.values.map((e) {
return (e, e.desc);
}).toList(),
);
},
);
if (result != null) {
MainController mainController = Get.put(MainController())
..msgBadgeMode = DynamicBadgeMode.values[result.index];
if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) {
mainController.queryUnreadMsg(true);
} else {
mainController.msgUnReadCount.value = '';
}
await GStorage.setting
.put(SettingBoxKey.msgBadgeMode, result.index);
SmartDialog.showToast('设置成功');
setState();
}
},
title: '消息未读标记',
leading: const Icon(MdiIcons.bellBadgeOutline),
getSubtitle: () => '当前标记样式:${Pref.msgBadgeMode.desc}',
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
final result = await showDialog<Set<MsgUnReadType>>(
context: Get.context!,
builder: (context) {
return MultiSelectDialog<MsgUnReadType>(
title: '消息未读类型',
initValues: Pref.msgUnReadTypeV2,
values: {for (var i in MsgUnReadType.values) i: i.title},
);
},
);
if (result != null) {
MainController mainController = Get.put(MainController())
..msgUnReadTypes = result;
if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) {
mainController.queryUnreadMsg();
}
await GStorage.setting.put(SettingBoxKey.msgUnReadTypeV2,
result.map((item) => item.index).toList()..sort());
SmartDialog.showToast('设置成功');
setState();
}
},
title: '消息未读类型',
leading: const Icon(MdiIcons.bellCogOutline),
getSubtitle: () =>
'当前消息类型:${Pref.msgUnReadTypeV2.map((item) => item.title).join('')}',
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '首页顶栏收起',
subtitle: '首页列表滑动时,收起顶栏',
leading: const Icon(Icons.vertical_align_top_outlined),
setKey: SettingBoxKey.hideSearchBar,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '首页底栏收起',
subtitle: '首页列表滑动时,收起底栏',
leading: const Icon(Icons.vertical_align_bottom_outlined),
setKey: SettingBoxKey.hideTabBar,
defaultVal: true,
needReboot: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '降低收起/展开顶/底栏频率',
leading: const Icon(Icons.vertical_distribute),
setKey: SettingBoxKey.navSearchStreamDebounce,
defaultVal: false,
onChanged: (value) {
try {
Get.find<MainController>().navSearchStreamDebounce = value;
Get.forceAppUpdate();
} catch (_) {}
},
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
_showQualityDialog(
context: Get.context!,
title: '图片质量',
initValue: Pref.picQuality,
callback: (picQuality) async {
GlobalData().imgQuality = picQuality;
await GStorage.setting
.put(SettingBoxKey.defaultPicQa, picQuality);
setState();
},
);
},
title: '图片质量',
subtitle: '选择合适的图片清晰度上限100%',
leading: const Icon(Icons.image_outlined),
getTrailing: () => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
'${Pref.picQuality}%',
style: Get.theme.textTheme.titleSmall,
),
),
),
// preview quality
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) {
_showQualityDialog(
context: Get.context!,
title: '查看大图质量',
initValue: Pref.previewQ,
callback: (picQuality) async {
await GStorage.setting
.put(SettingBoxKey.previewQuality, picQuality);
setState();
},
);
},
title: '查看大图质量',
subtitle: '选择合适的图片清晰度上限100%',
leading: const Icon(Icons.image_outlined),
getTrailing: () => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
'${Pref.previewQ}%',
style: Get.theme.textTheme.titleSmall,
),
),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
double? result = await showDialog(
context: Get.context!,
builder: (context) {
return SlideDialog(
title: 'Toast不透明度',
value: CustomToast.toastOpacity,
min: 0.0,
max: 1.0,
divisions: 10,
);
},
);
if (result != null) {
CustomToast.toastOpacity = result;
await GStorage.setting.put(SettingBoxKey.defaultToastOp, result);
SmartDialog.showToast('设置成功');
setState();
}
},
leading: const Icon(Icons.opacity_outlined),
title: '气泡提示不透明度',
subtitle: '自定义气泡提示(Toast)不透明度',
getTrailing: () => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
CustomToast.toastOpacity.toStringAsFixed(1),
style: Get.theme.textTheme.titleSmall,
),
),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
ThemeType? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<ThemeType>(
title: '主题模式',
value: Pref.themeType,
values: ThemeType.values.map(
(e) {
return (e, e.desc);
},
).toList());
},
);
if (result != null) {
try {
Get.find<MineController>().themeType.value = result;
} catch (_) {}
GStorage.setting.put(SettingBoxKey.themeMode, result.index);
Get.put(ColorSelectController()).themeType.value = result;
Get.changeThemeMode(result.toThemeMode);
setState();
}
},
leading: const Icon(Icons.flashlight_on_outlined),
title: '主题模式',
getSubtitle: () => '当前模式:${Pref.themeType.desc}',
),
SettingsModel(
settingsType: SettingsType.sw1tch,
leading: const Icon(Icons.invert_colors),
title: '纯黑主题',
setKey: SettingBoxKey.isPureBlackTheme,
defaultVal: false,
onChanged: (value) {
if (Get.theme.brightness == Brightness.dark || Pref.darkVideoPage) {
Get.forceAppUpdate();
}
},
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/colorSetting'),
leading: const Icon(Icons.color_lens_outlined),
title: '应用主题',
getSubtitle: () =>
'当前主题:${Get.put(ColorSelectController()).type.value == 0 ? '动态取色' : '指定颜色'}',
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
int? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<int>(
title: '首页启动页',
value: Pref.defaultHomePage,
values: NavigationBarType.values.map((e) {
return (e.index, e.label);
}).toList(),
);
},
);
if (result != null) {
await GStorage.setting.put(SettingBoxKey.defaultHomePage, result);
SmartDialog.showToast('设置成功,重启生效');
setState();
}
},
leading: const Icon(Icons.home_outlined),
title: '默认启动页',
getSubtitle: () =>
'当前启动页:${NavigationBarType.values.firstWhere((e) => e.index == Pref.defaultHomePage).label}',
),
SettingsModel(
settingsType: SettingsType.normal,
title: '滑动动画弹簧参数',
leading: const Icon(Icons.chrome_reader_mode_outlined),
onTap: (setState) {
final numberRegExp = RegExp(r'[\d\.]+');
List<String> springDescription = CustomSpringDescription
.springDescription
.map((i) => i.toString())
.toList();
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('弹簧参数'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: List.generate(
3,
(index) => TextFormField(
autofocus: index == 0,
initialValue: springDescription[index],
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
onChanged: (value) {
springDescription[index] = value;
},
inputFormatters: [
FilteringTextInputFormatter.allow(numberRegExp)
],
decoration: InputDecoration(
labelText: const [
'mass',
'stiffness',
'damping'
][index],
),
),
),
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () async {
Get.back();
await GStorage.setting.put(
SettingBoxKey.springDescription,
List<double>.generate(
3,
(i) =>
double.tryParse(springDescription[i]) ??
CustomSpringDescription.springDescription[i],
),
);
SmartDialog.showToast('设置成功,重启生效');
setState();
},
child: const Text('确定'),
)
],
);
},
);
},
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
var result = await Get.toNamed('/fontSizeSetting');
if (result != null) {
Get.put(ColorSelectController()).currentTextScale.value = result;
}
},
title: '字体大小',
leading: const Icon(Icons.format_size_outlined),
getSubtitle: () =>
Get.put(ColorSelectController()).currentTextScale.value == 1.0
? '默认'
: Get.put(ColorSelectController())
.currentTextScale
.value
.toString(),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/barSetting', arguments: {
'key': SettingBoxKey.tabBarSort,
'defaultBars': HomeTabType.values,
'title': '首页标签页'
}),
title: '首页标签页',
subtitle: '删除或调换首页标签页',
leading: const Icon(Icons.toc_outlined),
),
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/barSetting', arguments: {
'key': SettingBoxKey.navBarSort,
'defaultBars': NavigationBarType.values,
'title': 'Navbar'
}),
title: 'Navbar编辑',
subtitle: '删除或调换Navbar',
leading: const Icon(Icons.toc_outlined),
),
if (Platform.isAndroid)
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) => Get.toNamed('/displayModeSetting'),
title: '屏幕帧率',
leading: const Icon(Icons.autofps_select_outlined),
),
];
void _showQualityDialog({
required BuildContext context,
required String title,
required int initValue,
required ValueChanged<int> callback,
}) {
showDialog<double>(
context: context,
builder: (context) => SlideDialog(
value: initValue.toDouble(),
title: title,
min: 10,
max: 100,
divisions: 9,
suffix: '%',
precise: 0),
).then((result) {
if (result != null) {
SmartDialog.showToast('设置成功');
callback(result.toInt());
}
});
}