Files
PiliPlus/lib/pages/setting/view.dart
My-Responsitories 9d94c72e95 tweaks (#2426)
* opt: danmaku weight

* opt: cache clean

* opt: level img

* opt: play icon

* opt: svg big-vip

* opt: webview ua

* opt: simple dialog

* feat: export vtt

* tweak

* opt: mapIndexed

* feat: more subtitle

* refa: settings page

* feat: codec list options

* drawPath

Signed-off-by: dom <githubaccount56556@proton.me>

* custom dialog option

Signed-off-by: dom <githubaccount56556@proton.me>

* update

Signed-off-by: dom <githubaccount56556@proton.me>

* Revert "drawPath"

This reverts commit e8a4b19f0f.

* opt: _initStreamIndex

* fix: avoid gap

* fix: scale [skip ci]

* fix: hide repost menu not login

* tweaks

Signed-off-by: dom <githubaccount56556@proton.me>

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-06-26 10:51:41 +08:00

324 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:PiliPlus/common/widgets/flutter/list_tile.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/http/login.dart';
import 'package:PiliPlus/models/common/setting_type.dart';
import 'package:PiliPlus/pages/about/view.dart';
import 'package:PiliPlus/pages/login/controller.dart';
import 'package:PiliPlus/pages/setting/common_setting.dart';
import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart';
import 'package:PiliPlus/pages/webdav/view.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/extension/size_ext.dart';
import 'package:flutter/material.dart' hide ListTile;
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';
class _SettingsModel {
final SettingType type;
final String? subtitle;
final Icon icon;
const _SettingsModel({
required this.type,
this.subtitle,
required this.icon,
});
}
class SettingPage extends StatefulWidget {
const SettingPage({super.key});
@override
State<SettingPage> createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
late SettingType _type = SettingType.privacySetting;
final RxBool _noAccount = Accounts.account.isEmpty.obs;
late bool _isPortrait;
late ThemeData theme;
static const List<_SettingsModel> _items = [
_SettingsModel(
type: SettingType.privacySetting,
subtitle: '黑名单',
icon: Icon(Icons.privacy_tip_outlined),
),
_SettingsModel(
type: SettingType.recommendSetting,
subtitle: '推荐来源web/app、刷新保留内容、过滤器',
icon: Icon(Icons.explore_outlined),
),
_SettingsModel(
type: SettingType.videoSetting,
subtitle: '画质、音质、解码、缓冲、音频输出等',
icon: Icon(Icons.video_settings_outlined),
),
_SettingsModel(
type: SettingType.playSetting,
subtitle: '双击/长按、全屏、后台播放、弹幕、字幕、底部进度条等',
icon: Icon(Icons.touch_app_outlined),
),
_SettingsModel(
type: SettingType.styleSetting,
subtitle: '横屏适配(平板)、侧栏、列宽、首页、动态红点、主题、字号、图片、帧率等',
icon: Icon(Icons.style_outlined),
),
_SettingsModel(
type: SettingType.extraSetting,
subtitle: '震动、搜索、收藏、ai、评论、动态、代理、更新检查等',
icon: Icon(Icons.extension_outlined),
),
_SettingsModel(
type: SettingType.webdavSetting,
icon: Icon(MdiIcons.databaseCogOutline),
),
_SettingsModel(
type: SettingType.about,
icon: Icon(Icons.info_outline),
),
];
@override
void didChangeDependencies() {
super.didChangeDependencies();
theme = Theme.of(context);
_isPortrait = MediaQuery.sizeOf(context).isPortrait;
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: _isPortrait ? const Text('设置') : Text(_type.title),
),
body: ViewSafeArea(
child: _isPortrait
? _buildList(theme)
: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 4,
child: _buildList(theme),
),
VerticalDivider(
width: 1,
color: theme.colorScheme.outline.withValues(alpha: 0.1),
),
Expanded(
flex: 6,
child: switch (_type) {
.privacySetting ||
.recommendSetting ||
.videoSetting ||
.playSetting ||
.styleSetting ||
.extraSetting => CommonSetting(
settingType: _type,
showAppBar: false,
),
.webdavSetting => const WebDavSettingPage(
showAppBar: false,
),
.about => const AboutPage(showAppBar: false),
},
),
],
),
),
);
}
@override
void dispose() {
_noAccount.close();
super.dispose();
}
void _toPage(SettingType type) {
if (_isPortrait) {
Get.to(
() => switch (type) {
.privacySetting ||
.recommendSetting ||
.videoSetting ||
.playSetting ||
.styleSetting ||
.extraSetting => CommonSetting(settingType: type),
.webdavSetting => const WebDavSettingPage(),
.about => const AboutPage(),
},
);
} else {
_type = type;
setState(() {});
}
}
Color? _getTileColor(ThemeData theme, SettingType type) {
if (_isPortrait) {
return null;
} else {
return type == _type ? theme.colorScheme.onInverseSurface : null;
}
}
Widget _buildList(ThemeData theme) {
final padding = MediaQuery.viewPaddingOf(context);
TextStyle titleStyle = theme.textTheme.titleMedium!;
TextStyle subTitleStyle = theme.textTheme.labelMedium!.copyWith(
color: theme.colorScheme.outline,
);
return ListView(
padding: EdgeInsets.only(bottom: padding.bottom + 100),
children: [
_buildSearchItem(theme),
..._items
.take(_items.length - 1)
.map(
(item) => ListTile(
tileColor: _getTileColor(theme, item.type),
onTap: () => _toPage(item.type),
leading: item.icon,
title: Text(item.type.title, style: titleStyle),
subtitle: item.subtitle == null
? null
: Text(item.subtitle!, style: subTitleStyle),
),
),
ListTile(
onTap: () => LoginPageController.switchAccountDialog(context),
leading: const Icon(Icons.switch_account_outlined),
title: Text('切换账号', style: titleStyle),
),
Obx(
() => _noAccount.value
? const SizedBox.shrink()
: ListTile(
leading: const Icon(Icons.logout_outlined),
onTap: () => _logoutDialog(context),
title: Text('退出登录', style: titleStyle),
),
),
ListTile(
tileColor: _getTileColor(theme, _items.last.type),
onTap: () => _toPage(_items.last.type),
leading: _items.last.icon,
title: Text(_items.last.type.title, style: titleStyle),
),
],
);
}
Future<void> _logoutDialog(BuildContext context) async {
final result = await showDialog<Set<LoginAccount>>(
context: context,
builder: (context) => MultiSelectDialog<LoginAccount>(
title: '选择要登出的账号uid',
initValues: const Iterable.empty(),
values: {
for (final i in Accounts.account.values) i: i.mid.toString(),
},
),
);
if (!context.mounted || result == null || result.isEmpty) return;
Future<void> logout() {
_noAccount.value = result.length == Accounts.account.length;
return Accounts.deleteAll(result);
}
showDialog(
context: context,
builder: (context) {
final theme = Theme.of(context);
return AlertDialog(
title: const Text('提示'),
content: Text(
"确认要退出以下账号登录吗\n\n${result.map((i) => i.mid.toString()).join('\n')}",
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'点错了',
style: TextStyle(
color: theme.colorScheme.outline,
),
),
),
TextButton(
onPressed: () {
Get.back();
logout();
},
child: Text(
'仅登出',
style: TextStyle(color: theme.colorScheme.error),
),
),
TextButton(
onPressed: () async {
SmartDialog.showLoading();
final res = await LoginHttp.logout(Accounts.main);
if (res['status']) {
SmartDialog.dismiss();
logout();
Get.back();
} else {
SmartDialog.dismiss();
SmartDialog.showToast(res['msg'].toString());
}
},
child: const Text('确认'),
),
],
);
},
);
}
Widget _buildSearchItem(ThemeData theme) => Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 8,
),
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => Get.toNamed('/settingsSearch'),
borderRadius: const BorderRadius.all(Radius.circular(50)),
child: Ink(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(50)),
color: theme.colorScheme.onInverseSurface,
),
child: const Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 18,
applyTextScaling: true,
Icons.search,
),
Text(
' 搜索',
style: TextStyle(height: 1),
strutStyle: StrutStyle(height: 1, leading: 0),
),
],
),
),
),
),
),
);
}