feat: videoDetail menu edit
This commit is contained in:
BIN
assets/images/coin.png
Normal file
BIN
assets/images/coin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
94
lib/models/common/action_type.dart
Normal file
94
lib/models/common/action_type.dart
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// 操作类型的枚举值:点赞 不喜欢 收藏 投币 稍后再看 下载封面 后台播放 听视频 分享 下载视频
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
enum ActionType {
|
||||||
|
like,
|
||||||
|
coin,
|
||||||
|
collect,
|
||||||
|
watchLater,
|
||||||
|
share,
|
||||||
|
dislike,
|
||||||
|
downloadCover,
|
||||||
|
copyLink,
|
||||||
|
threeAction,
|
||||||
|
// backgroundPlay,
|
||||||
|
// listenVideo,
|
||||||
|
// downloadVideo,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionTypeExtension on ActionType {
|
||||||
|
String get value => [
|
||||||
|
'like',
|
||||||
|
'coin',
|
||||||
|
'collect',
|
||||||
|
'watchLater',
|
||||||
|
'share',
|
||||||
|
'dislike',
|
||||||
|
'downloadCover',
|
||||||
|
'copyLink',
|
||||||
|
// 'backgroundPlay',
|
||||||
|
// 'listenVideo',
|
||||||
|
// 'downloadVideo',
|
||||||
|
][index];
|
||||||
|
String get label => [
|
||||||
|
'点赞视频',
|
||||||
|
'投币',
|
||||||
|
'收藏视频',
|
||||||
|
'稍后再看',
|
||||||
|
'视频分享',
|
||||||
|
'不喜欢',
|
||||||
|
'下载封面',
|
||||||
|
'复制链接',
|
||||||
|
// '后台播放',
|
||||||
|
// '听视频',
|
||||||
|
// '下载视频',
|
||||||
|
][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map> actionMenuConfig = [
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.thumb_up_alt_outlined),
|
||||||
|
'label': '点赞视频',
|
||||||
|
'value': ActionType.like,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': Image.asset(
|
||||||
|
'assets/images/coin.png',
|
||||||
|
width: 26,
|
||||||
|
color: IconTheme.of(Get.context!).color!.withOpacity(0.65),
|
||||||
|
),
|
||||||
|
'label': '投币',
|
||||||
|
'value': ActionType.coin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.star_border),
|
||||||
|
'label': '收藏视频',
|
||||||
|
'value': ActionType.collect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.watch_later_outlined),
|
||||||
|
'label': '稍后再看',
|
||||||
|
'value': ActionType.watchLater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.share),
|
||||||
|
'label': '视频分享',
|
||||||
|
'value': ActionType.share,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.thumb_down_alt_outlined),
|
||||||
|
'label': '不喜欢',
|
||||||
|
'value': ActionType.dislike,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.image_outlined),
|
||||||
|
'label': '下载封面',
|
||||||
|
'value': ActionType.downloadCover,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(Icons.link_outlined),
|
||||||
|
'label': '复制链接',
|
||||||
|
'value': ActionType.copyLink,
|
||||||
|
},
|
||||||
|
];
|
||||||
100
lib/pages/setting/pages/action_menu_set.dart
Normal file
100
lib/pages/setting/pages/action_menu_set.dart
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/models/common/action_type.dart';
|
||||||
|
import '../../../utils/storage.dart';
|
||||||
|
|
||||||
|
class ActionMenuSetPage extends StatefulWidget {
|
||||||
|
const ActionMenuSetPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ActionMenuSetPage> createState() => _ActionMenuSetPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ActionMenuSetPageState extends State<ActionMenuSetPage> {
|
||||||
|
Box settingStorage = GStrorage.setting;
|
||||||
|
late List<String> actionTypeSort;
|
||||||
|
late List<Map> allLabels;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort,
|
||||||
|
defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']);
|
||||||
|
allLabels = actionMenuConfig;
|
||||||
|
allLabels.sort((a, b) {
|
||||||
|
int indexA = actionTypeSort.indexOf((a['value'] as ActionType).value);
|
||||||
|
int indexB = actionTypeSort.indexOf((b['value'] as ActionType).value);
|
||||||
|
if (indexA == -1) indexA = actionTypeSort.length;
|
||||||
|
if (indexB == -1) indexB = actionTypeSort.length;
|
||||||
|
return indexA.compareTo(indexB);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveEdit() {
|
||||||
|
List<String> sortedTabbar = allLabels
|
||||||
|
.where((i) => actionTypeSort.contains((i['value'] as ActionType).value))
|
||||||
|
.map<String>((i) => (i['value'] as ActionType).value)
|
||||||
|
.toList();
|
||||||
|
settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar);
|
||||||
|
SmartDialog.showToast('保存成功,下次启动时生效');
|
||||||
|
}
|
||||||
|
|
||||||
|
void onReorder(int oldIndex, int newIndex) {
|
||||||
|
setState(() {
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final tabsItem = allLabels.removeAt(oldIndex);
|
||||||
|
allLabels.insert(newIndex, tabsItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final listTiles = [
|
||||||
|
for (int i = 0; i < allLabels.length; i++) ...[
|
||||||
|
CheckboxListTile(
|
||||||
|
key: Key((allLabels[i]['value'] as ActionType).value),
|
||||||
|
value: actionTypeSort
|
||||||
|
.contains((allLabels[i]['value'] as ActionType).value),
|
||||||
|
onChanged: (bool? newValue) {
|
||||||
|
String actionTypeId = (allLabels[i]['value'] as ActionType).value;
|
||||||
|
if (!newValue!) {
|
||||||
|
actionTypeSort.remove(actionTypeId);
|
||||||
|
} else {
|
||||||
|
actionTypeSort.add(actionTypeId);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
allLabels[i]['icon'],
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(allLabels[i]['label']),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
secondary: const Icon(Icons.drag_indicator_rounded),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('视频操作菜单'),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => saveEdit(), child: const Text('保存')),
|
||||||
|
const SizedBox(width: 12)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: ReorderableListView(
|
||||||
|
onReorder: onReorder,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
footer: SizedBox(
|
||||||
|
height: MediaQuery.of(context).padding.bottom + 30,
|
||||||
|
),
|
||||||
|
children: listTiles,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -289,6 +289,11 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
onTap: () => Get.toNamed('/navbarSetting'),
|
onTap: () => Get.toNamed('/navbarSetting'),
|
||||||
title: Text('底部导航栏设置', style: titleStyle),
|
title: Text('底部导航栏设置', style: titleStyle),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
onTap: () => Get.toNamed('/actionMenuSet'),
|
||||||
|
title: Text('操作菜单设置', style: titleStyle),
|
||||||
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -529,26 +528,21 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(top: 6, bottom: 4),
|
margin: const EdgeInsets.only(top: 6, bottom: 4),
|
||||||
height: constraints.maxWidth / 5 * 0.8,
|
height: constraints.maxWidth / 5,
|
||||||
child: GridView.count(
|
child: ListView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
scrollDirection: Axis.horizontal,
|
||||||
primary: false,
|
children: [
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
crossAxisCount: 5,
|
|
||||||
childAspectRatio: 1.25,
|
|
||||||
children: <Widget>[
|
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
icon: const Icon(Icons.thumb_up_alt_outlined),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
selectIcon: const Icon(Icons.thumb_up),
|
||||||
onTap: handleState(videoIntroController.actionLikeVideo),
|
onTap: handleState(videoIntroController.actionLikeVideo),
|
||||||
selectStatus: videoIntroController.hasLike.value,
|
selectStatus: videoIntroController.hasLike.value,
|
||||||
text: widget.videoDetail!.stat!.like!.toString()),
|
text: widget.videoDetail!.stat!.like!.toString()),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
icon: Image.asset('assets/images/coin.png', width: 30),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
|
||||||
onTap: handleState(videoIntroController.actionCoinVideo),
|
onTap: handleState(videoIntroController.actionCoinVideo),
|
||||||
selectStatus: videoIntroController.hasCoin.value,
|
selectStatus: videoIntroController.hasCoin.value,
|
||||||
text: widget.videoDetail!.stat!.coin!.toString(),
|
text: widget.videoDetail!.stat!.coin!.toString(),
|
||||||
@ -556,8 +550,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.star),
|
icon: const Icon(Icons.star_border),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
selectIcon: const Icon(Icons.star),
|
||||||
onTap: () => showFavBottomSheet(),
|
onTap: () => showFavBottomSheet(),
|
||||||
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
||||||
selectStatus: videoIntroController.hasFav.value,
|
selectStatus: videoIntroController.hasFav.value,
|
||||||
@ -565,7 +559,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.clock),
|
icon: const Icon(Icons.watch_later_outlined),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final res =
|
final res =
|
||||||
await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid);
|
await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid);
|
||||||
@ -575,7 +569,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
text: '稍后看',
|
text: '稍后看',
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
icon: const Icon(Icons.share),
|
||||||
onTap: () => videoIntroController.actionShareVideo(),
|
onTap: () => videoIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
text: '分享',
|
text: '分享',
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
class ActionItem extends StatelessWidget {
|
||||||
final Icon? icon;
|
final dynamic icon;
|
||||||
final Icon? selectIcon;
|
final Icon? selectIcon;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
final Function? onLongPress;
|
final Function? onLongPress;
|
||||||
@ -31,26 +32,37 @@ class ActionItem extends StatelessWidget {
|
|||||||
if (onLongPress != null) {onLongPress!()}
|
if (onLongPress != null) {onLongPress!()}
|
||||||
},
|
},
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
child: Column(
|
child: SizedBox(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
width: (Get.size.width - 24) / 5,
|
||||||
children: [
|
child: Column(
|
||||||
const SizedBox(height: 4),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
selectStatus
|
children: [
|
||||||
? Icon(selectIcon!.icon!,
|
const SizedBox(height: 4),
|
||||||
size: 18, color: Theme.of(context).colorScheme.primary)
|
icon is Icon
|
||||||
: Icon(icon!.icon!,
|
? Icon(
|
||||||
size: 18, color: Theme.of(context).colorScheme.outline),
|
selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon,
|
||||||
const SizedBox(height: 6),
|
color: selectStatus
|
||||||
Text(
|
? Theme.of(context).colorScheme.primary
|
||||||
text ?? '',
|
: Theme.of(context).colorScheme.outline,
|
||||||
style: TextStyle(
|
)
|
||||||
color: selectStatus
|
: Image.asset(
|
||||||
? Theme.of(context).colorScheme.primary
|
'assets/images/coin.png',
|
||||||
: Theme.of(context).colorScheme.outline,
|
width: 25,
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
color: selectStatus
|
||||||
),
|
? Theme.of(context).colorScheme.primary
|
||||||
)
|
: Theme.of(context).colorScheme.outline,
|
||||||
],
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
text ?? '',
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
selectStatus ? Theme.of(context).colorScheme.primary : null,
|
||||||
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import '../pages/search/index.dart';
|
|||||||
import '../pages/search_result/index.dart';
|
import '../pages/search_result/index.dart';
|
||||||
import '../pages/setting/extra_setting.dart';
|
import '../pages/setting/extra_setting.dart';
|
||||||
import '../pages/setting/index.dart';
|
import '../pages/setting/index.dart';
|
||||||
|
import '../pages/setting/pages/action_menu_set.dart';
|
||||||
import '../pages/setting/pages/color_select.dart';
|
import '../pages/setting/pages/color_select.dart';
|
||||||
import '../pages/setting/pages/display_mode.dart';
|
import '../pages/setting/pages/display_mode.dart';
|
||||||
import '../pages/setting/pages/font_size_select.dart';
|
import '../pages/setting/pages/font_size_select.dart';
|
||||||
@ -174,6 +175,9 @@ class Routes {
|
|||||||
// navigation bar
|
// navigation bar
|
||||||
CustomGetPage(
|
CustomGetPage(
|
||||||
name: '/navbarSetting', page: () => const NavigationBarSetPage()),
|
name: '/navbarSetting', page: () => const NavigationBarSetPage()),
|
||||||
|
// 操作菜单
|
||||||
|
CustomGetPage(
|
||||||
|
name: '/actionMenuSet', page: () => const ActionMenuSetPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,8 @@ class SettingBoxKey {
|
|||||||
tabbarSort = 'tabbarSort', // 首页tabbar
|
tabbarSort = 'tabbarSort', // 首页tabbar
|
||||||
dynamicBadgeMode = 'dynamicBadgeMode',
|
dynamicBadgeMode = 'dynamicBadgeMode',
|
||||||
enableGradientBg = 'enableGradientBg',
|
enableGradientBg = 'enableGradientBg',
|
||||||
navBarSort = 'navBarSort';
|
navBarSort = 'navBarSort',
|
||||||
|
actionTypeSort = 'actionTypeSort';
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
|||||||
Reference in New Issue
Block a user