From 2ff1d021710c964cb72d91172f725c239aee2e25 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 10 Aug 2023 16:23:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=94=BB=E8=B4=A8=E3=80=81=E9=9F=B3=E8=B4=A8=E3=80=81=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/quality.dart | 8 ++ lib/pages/setting/play_setting.dart | 134 +++++++++++++++++++++ lib/pages/setting/view.dart | 15 +++ lib/pages/setting/widgets/select_item.dart | 120 ++++++++++++++++++ lib/pages/setting/widgets/switch_item.dart | 66 ++++++++++ lib/router/app_pages.dart | 4 + lib/utils/storage.dart | 10 ++ 7 files changed, 357 insertions(+) create mode 100644 lib/pages/setting/play_setting.dart create mode 100644 lib/pages/setting/widgets/select_item.dart create mode 100644 lib/pages/setting/widgets/switch_item.dart diff --git a/lib/models/video/play/quality.dart b/lib/models/video/play/quality.dart index 80313b6d..6b1daa89 100644 --- a/lib/models/video/play/quality.dart +++ b/lib/models/video/play/quality.dart @@ -113,6 +113,14 @@ extension VideoDecodeFormatsCode on VideoDecodeFormats { ]; get code => _codeList[index]; + static VideoDecodeFormats? fromCode(String code) { + final index = _codeList.indexOf(code); + if (index != -1) { + return VideoDecodeFormats.values[index]; + } + return null; + } + static VideoDecodeFormats? fromString(String val) { var result = VideoDecodeFormats.values.first; for (var i in _codeList) { diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart new file mode 100644 index 00000000..9a79d39b --- /dev/null +++ b/lib/pages/setting/play_setting.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/video/play/quality.dart'; +import 'package:pilipala/utils/storage.dart'; + +import 'widgets/switch_item.dart'; + +class PlaySetting extends StatefulWidget { + const PlaySetting({super.key}); + + @override + State createState() => _PlaySettingState(); +} + +class _PlaySettingState extends State { + Box setting = GStrorage.setting; + late dynamic defaultVideoQa; + late dynamic defaultAudioQa; + late dynamic defaultDecode; + + @override + void initState() { + super.initState(); + defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa, + defaultValue: VideoQuality.values.last.code); + defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, + defaultValue: AudioQuality.values.last.code); + defaultDecode = setting.get(SettingBoxKey.defaultDecode, + defaultValue: VideoDecodeFormats.values.last.code); + } + + @override + Widget build(BuildContext context) { + TextStyle subTitleStyle = Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline); + return Scaffold( + appBar: AppBar( + title: const Text('播放设置'), + ), + body: ListView( + children: [ + const SetSwitchItem( + title: '自动播放', + subTitle: '进入详情页自动播放', + setKey: SettingBoxKey.autoPlayEnable, + ), + const SetSwitchItem( + title: '开启硬解', + subTitle: '以较低功耗播放视频', + setKey: SettingBoxKey.enableHA, + ), + ListTile( + dense: false, + title: const Text('默认画质'), + subtitle: Text( + '当前画质' + VideoQualityCode.fromCode(defaultVideoQa)!.description!, + style: subTitleStyle, + ), + trailing: PopupMenuButton( + initialValue: defaultVideoQa, + icon: const Icon(Icons.arrow_forward_rounded, size: 22), + onSelected: (item) { + defaultVideoQa = item; + setting.put(SettingBoxKey.defaultVideoQa, item); + setState(() {}); + }, + itemBuilder: (BuildContext context) => [ + for (var i in VideoQuality.values.reversed) ...[ + PopupMenuItem( + value: i.code, + child: Text(i.description), + ), + ] + ], + ), + ), + ListTile( + dense: false, + title: const Text('默认音质'), + subtitle: Text( + '当前音质' + AudioQualityCode.fromCode(defaultAudioQa)!.description!, + style: subTitleStyle, + ), + trailing: PopupMenuButton( + initialValue: defaultAudioQa, + icon: const Icon(Icons.arrow_forward_rounded, size: 22), + onSelected: (item) { + defaultAudioQa = item; + setting.put(SettingBoxKey.defaultAudioQa, item); + setState(() {}); + }, + itemBuilder: (BuildContext context) => [ + for (var i in AudioQuality.values.reversed) ...[ + PopupMenuItem( + value: i.code, + child: Text(i.description), + ), + ] + ], + ), + ), + ListTile( + dense: false, + title: const Text('默认解码格式'), + subtitle: Text( + '当前解码格式' + + VideoDecodeFormatsCode.fromCode(defaultDecode)!.description!, + style: subTitleStyle, + ), + trailing: PopupMenuButton( + initialValue: defaultDecode, + icon: const Icon(Icons.arrow_forward_rounded, size: 22), + onSelected: (item) { + defaultDecode = item; + setting.put(SettingBoxKey.defaultDecode, item); + setState(() {}); + }, + itemBuilder: (BuildContext context) => [ + for (var i in VideoDecodeFormats.values) ...[ + PopupMenuItem( + value: i.code, + child: Text(i.description), + ), + ] + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 155eec2a..3883397d 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -40,6 +40,21 @@ class SettingPage extends StatelessWidget { ), ), ), + ListTile( + onTap: () => Get.toNamed('/playSetting'), + dense: false, + title: const Text('播放设置'), + ), + // ListTile( + // onTap: () {}, + // dense: false, + // title: const Text('外观设置'), + // ), + // ListTile( + // onTap: () {}, + // dense: false, + // title: const Text('其他设置'), + // ), Obx( () => Visibility( visible: settingController.userLogin.value, diff --git a/lib/pages/setting/widgets/select_item.dart b/lib/pages/setting/widgets/select_item.dart new file mode 100644 index 00000000..297a5ecc --- /dev/null +++ b/lib/pages/setting/widgets/select_item.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/video/play/quality.dart'; +import 'package:pilipala/utils/storage.dart'; + +class SetSelectItem extends StatefulWidget { + final String? title; + final String? subTitle; + final String? setKey; + const SetSelectItem({ + this.title, + this.subTitle, + this.setKey, + Key? key, + }) : super(key: key); + + @override + State createState() => _SetSelectItemState(); +} + +class _SetSelectItemState extends State { + Box Setting = GStrorage.setting; + late var currentVal; + late int currentIndex; + late List menus; + late List popMenuItems; + + @override + void initState() { + super.initState(); + late String defaultVal; + switch (widget.setKey) { + case 'defaultVideoQa': + defaultVal = VideoQuality.values.last.description; + List list = menus = VideoQuality.values.reversed.toList(); + currentVal = Setting.get(widget.setKey, defaultValue: defaultVal); + currentIndex = + list.firstWhere((i) => i.description == currentVal).index; + + popMenuItems = [ + for (var i in list) ...[ + PopupMenuItem( + value: i.code, + child: Text(i.description), + ) + ] + ]; + + break; + case 'defaultAudioQa': + defaultVal = AudioQuality.values.last.description; + List list = menus = AudioQuality.values.reversed.toList(); + currentVal = Setting.get(widget.setKey, defaultValue: defaultVal); + currentIndex = + list.firstWhere((i) => i.description == currentVal).index; + + popMenuItems = [ + for (var i in list) ...[ + PopupMenuItem( + value: i.index, + child: Text(i.description), + ), + ] + ]; + break; + case 'defaultDecode': + defaultVal = VideoDecodeFormats.values[0].description; + currentVal = Setting.get(widget.setKey, defaultValue: defaultVal); + List list = menus = VideoDecodeFormats.values; + + currentIndex = + list.firstWhere((i) => i.description == currentVal).index; + + popMenuItems = [ + for (var i in list) ...[ + PopupMenuItem( + value: i.index, + child: Text(i.description), + ), + ] + ]; + break; + case 'defaultVideoSpeed': + defaultVal = '1.0'; + currentVal = Setting.get(widget.setKey, defaultValue: defaultVal); + + break; + } + } + + @override + Widget build(BuildContext context) { + TextStyle subTitleStyle = Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline); + return ListTile( + onTap: () {}, + dense: false, + title: Text(widget.title!), + subtitle: Text( + '当前${widget.title!} $currentVal', + style: subTitleStyle, + ), + trailing: PopupMenuButton( + initialValue: currentIndex, + icon: const Icon( + Icons.arrow_forward_rounded, + size: 22, + ), + onSelected: (item) { + currentVal = menus.firstWhere((e) => e.code == item).first; + setState(() {}); + }, + itemBuilder: (BuildContext context) => + [...popMenuItems], + ), + ); + } +} diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart new file mode 100644 index 00000000..258c7ef7 --- /dev/null +++ b/lib/pages/setting/widgets/switch_item.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/utils/storage.dart'; + +class SetSwitchItem extends StatefulWidget { + final String? title; + final String? subTitle; + final String? setKey; + + const SetSwitchItem({ + this.title, + this.subTitle, + this.setKey, + Key? key, + }) : super(key: key); + + @override + State createState() => _SetSwitchItemState(); +} + +class _SetSwitchItemState extends State { + // ignore: non_constant_identifier_names + Box Setting = GStrorage.setting; + late bool val; + + @override + void initState() { + super.initState(); + val = Setting.get(widget.setKey, defaultValue: false); + } + + @override + Widget build(BuildContext context) { + TextStyle subTitleStyle = Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline); + return ListTile( + enableFeedback: true, + onTap: () { + Setting.put(widget.setKey, !val); + }, + title: Text(widget.title!), + subtitle: widget.subTitle != null + ? Text(widget.subTitle!, style: subTitleStyle) + : null, + trailing: Transform.scale( + scale: 0.8, + child: Switch( + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && states.first == MaterialState.selected) { + return const Icon(Icons.done); + } + return null; // All other states will use the default thumbIcon. + }), + value: val, + onChanged: (value) { + val = value; + Setting.put(widget.setKey, value); + setState(() {}); + }), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index d6de8ae4..652bc25a 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -14,6 +14,7 @@ import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/searchResult/index.dart'; +import 'package:pilipala/pages/setting/play_setting.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/replyReply/index.dart'; import 'package:pilipala/pages/webview/index.dart'; @@ -68,5 +69,8 @@ class Routes { GetPage(name: '/member', page: () => const MemberPage()), // 二级回复 GetPage(name: '/replyReply', page: () => const VideoReplyReplyPanel()), + + // 播放设置 + GetPage(name: '/playSetting', page: () => const PlaySetting()) ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index b39dcbfc..63d17f83 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -68,6 +68,16 @@ class UserBoxKey { class SettingBoxKey { static const String themeMode = 'themeMode'; static const String feedBackEnable = 'feedBackEnable'; + static const String defaultFontSize = 'fontSize'; + static const String defaultVideoQa = 'defaultVideoQa'; + static const String defaultAudioQa = 'defaultAudioQa'; + static const String defaultDecode = 'defaultDecode'; + static const String defaultVideoSpeed = 'defaultVideoSpeed'; + static const String autoUpgradeEnable = 'autoUpgradeEnable'; + static const String autoPlayEnable = 'autoPlayEnable'; + static const String enableHA = 'enableHA'; + + static const String danmakuEnable = 'danmakuEnable'; } class LocalCacheKey {