Merge branch 'main' into opt-followUp

This commit is contained in:
guozhigq
2024-12-10 22:23:45 +08:00
20 changed files with 648 additions and 559 deletions

View File

@ -626,4 +626,7 @@ class Api {
/// 修复标题和海报 /// 修复标题和海报
// /api/view?id=${aid} /all/video/av${aid} /video/av${aid}/ // /api/view?id=${aid} /all/video/av${aid} /video/av${aid}/
static const String fixTitleAndPic = '${HttpString.biliplusBaseUrl}/api/view'; static const String fixTitleAndPic = '${HttpString.biliplusBaseUrl}/api/view';
/// 专栏详情
static const String opusDetail = '/x/polymer/web-dynamic/v1/opus/detail';
} }

View File

@ -215,4 +215,25 @@ class DynamicsHttp {
}; };
} }
} }
static Future opusDetail({
required int opusId,
}) async {
var res = await Request().get(
Api.opusDetail,
data: {'id': opusId},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
} }

View File

@ -216,6 +216,21 @@ class BuildMainApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Box setting = GStorage.setting;
/// 纯黑模式主题配置
ColorScheme? pureDarkColorScheme;
final bool enablePureBlack =
setting.get(SettingBoxKey.enablePureBlack, defaultValue: false);
if (enablePureBlack) {
pureDarkColorScheme = darkColorScheme.copyWith(
background: Colors.black,
surface: Colors.black,
onPrimary: Colors.black,
onSecondary: Colors.black,
);
}
final SnackBarThemeData snackBarTheme = SnackBarThemeData( final SnackBarThemeData snackBarTheme = SnackBarThemeData(
actionTextColor: lightColorScheme.primary, actionTextColor: lightColorScheme.primary,
backgroundColor: lightColorScheme.secondaryContainer, backgroundColor: lightColorScheme.secondaryContainer,
@ -255,13 +270,13 @@ class BuildMainApp extends StatelessWidget {
title: 'PiliPala', title: 'PiliPala',
theme: buildThemeData( theme: buildThemeData(
currentThemeValue == ThemeType.dark currentThemeValue == ThemeType.dark
? darkColorScheme ? pureDarkColorScheme ?? darkColorScheme
: lightColorScheme, : lightColorScheme,
), ),
darkTheme: buildThemeData( darkTheme: buildThemeData(
currentThemeValue == ThemeType.light currentThemeValue == ThemeType.light
? lightColorScheme ? lightColorScheme
: darkColorScheme, : pureDarkColorScheme ?? darkColorScheme,
), ),
localizationsDelegates: const [ localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/http/html.dart'; import 'package:pilipala/http/dynamics.dart';
import 'package:pilipala/http/reply.dart'; import 'package:pilipala/http/reply.dart';
import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart';
import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/models/video/reply/item.dart';
@ -12,6 +12,7 @@ class DynamicDetailController extends GetxController {
DynamicDetailController(this.oid, this.type); DynamicDetailController(this.oid, this.type);
int? oid; int? oid;
int? type; int? type;
int? opusId;
dynamic item; dynamic item;
int? floor; int? floor;
String nextOffset = ""; String nextOffset = "";
@ -56,6 +57,12 @@ class DynamicDetailController extends GetxController {
if (reqType == 'init') { if (reqType == 'init') {
nextOffset = ''; nextOffset = '';
noMore.value = ''; noMore.value = '';
if (opusId != null && oid == 0) {
var res = await DynamicsHttp.opusDetail(opusId: opusId!);
if (res['status']) {
oid = int.parse(res['data']['item']['basic']['comment_id_str']);
}
}
} }
var res = await ReplyHttp.replyList( var res = await ReplyHttp.replyList(
oid: oid!, oid: oid!,
@ -110,15 +117,12 @@ class DynamicDetailController extends GetxController {
sortTypeTitle.value = _sortType.titles; sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels; sortTypeLabel.value = _sortType.labels;
replyList.clear(); replyList.clear();
noMore.value = '';
isLoadingMore = false;
isEnd = false;
queryReplyList(reqType: 'init'); queryReplyList(reqType: 'init');
} }
// 根据jumpUrl获取动态html
reqHtmlByOpusId(int id) async {
var res = await HtmlHttp.reqHtml(id, 'opus');
oid = res['commentId'];
}
// 上拉加载 // 上拉加载
Future onLoad() async { Future onLoad() async {
queryReplyList(reqType: 'onLoad'); queryReplyList(reqType: 'onLoad');

View File

@ -89,9 +89,8 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
_dynamicDetailController = Get.put( _dynamicDetailController = Get.put(
DynamicDetailController(oid, replyType), DynamicDetailController(oid, replyType),
tag: opusId.toString()); tag: opusId.toString());
_dynamicDetailController.opusId = opusId;
_futureBuilderFuture = _dynamicDetailController.queryReplyList(); _futureBuilderFuture = _dynamicDetailController.queryReplyList();
await _dynamicDetailController.reqHtmlByOpusId(opusId!);
setState(() {});
} }
} else { } else {
oid = moduleDynamic.major!.draw!.id!; oid = moduleDynamic.major!.draw!.id!;

View File

@ -194,26 +194,31 @@ class _LivePageState extends State<LivePage>
), ),
), ),
), ),
InkWell( Obx(
onTap: () { () => Visibility(
Get.toNamed('/liveFollowing'); visible: _liveController.liveFollowingCount.value > 0,
}, child: InkWell(
highlightColor: Colors.transparent, onTap: () {
splashColor: Colors.transparent, Get.toNamed('/liveFollowing');
child: Row( },
children: [ highlightColor: Colors.transparent,
Text( splashColor: Colors.transparent,
'查看更多', child: Row(
style: TextStyle( children: [
fontSize: 14, Text(
color: Theme.of(context).colorScheme.outline, '查看更多',
), style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.outline,
),
),
Icon(
Icons.chevron_right,
color: Theme.of(context).colorScheme.outline,
),
],
), ),
Icon( ),
Icons.chevron_right,
color: Theme.of(context).colorScheme.outline,
),
],
), ),
), ),
], ],
@ -222,19 +227,18 @@ class _LivePageState extends State<LivePage>
future: _futureBuilderFuture2, future: _futureBuilderFuture2,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
Map? data = snapshot.data; Map? data = snapshot.data;
if (data?['status']) { if (data != null && data['status']) {
RxList list = _liveController.liveFollowingList; RxList list = _liveController.liveFollowingList;
return LiveFollowingListView(list: list); return list.isNotEmpty
? LiveFollowingListView(list: list)
: const Center(child: Text('没有人在直播'));
} else { } else {
return SizedBox( return SizedBox(
height: 80, height: 80,
child: Center( child: Center(
child: Text( child: Text(
data?['msg'] ?? '', data?['msg'] ?? '请求异常',
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.outline, color: Theme.of(context).colorScheme.outline,
fontSize: 12, fontSize: 12,
@ -283,6 +287,15 @@ class LiveFollowingListView extends StatelessWidget {
}, },
); );
}, },
onLongPress: () {
Get.toNamed(
'/member?mid=${list[index].uid}',
arguments: {
'face': list[index].face,
'heroTag': list[index].uid.toString(),
},
);
},
child: Container( child: Container(
width: 54, width: 54,
height: 54, height: 54,

View File

@ -9,7 +9,7 @@ class LiveFollowController extends GetxController {
RxInt crossAxisCount = 2.obs; RxInt crossAxisCount = 2.obs;
Box setting = GStorage.setting; Box setting = GStorage.setting;
int _currentPage = 1; int _currentPage = 1;
RxInt liveFollowingCount = 0.obs; RxString liveFollowingCount = '- '.obs;
RxList<LiveFollowingItemModel> liveFollowingList = RxList<LiveFollowingItemModel> liveFollowingList =
<LiveFollowingItemModel>[].obs; <LiveFollowingItemModel>[].obs;
@ -28,10 +28,11 @@ class LiveFollowController extends GetxController {
if (res['status']) { if (res['status']) {
if (type == 'init') { if (type == 'init') {
liveFollowingList.value = res['data'].list; liveFollowingList.value = res['data'].list;
liveFollowingCount.value = res['data'].liveCount; liveFollowingCount.value = res['data'].liveCount.toString();
} else if (type == 'onLoad') { } else if (type == 'onLoad') {
liveFollowingList.addAll(res['data'].list); liveFollowingList.addAll(res['data'].list);
} }
liveFollowingList.removeWhere((e) => e.liveStatus != 1);
_currentPage += 1; _currentPage += 1;
} else { } else {
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);

View File

@ -110,7 +110,8 @@ class _LiveRoomPageState extends State<LiveRoomPage>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final isPortrait = mediaQuery.orientation == Orientation.portrait; final isPortrait = mediaQuery.orientation == Orientation.portrait;
final isLandscape = mediaQuery.orientation == Orientation.landscape; final RxBool isLandscape =
(mediaQuery.orientation == Orientation.landscape).obs;
final padding = mediaQuery.padding; final padding = mediaQuery.padding;
@ -194,7 +195,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
Obx( Obx(
() => SizedBox( () => SizedBox(
height: padding.top + height: padding.top +
(_liveRoomController.isPortrait.value || isLandscape (_liveRoomController.isPortrait.value || isLandscape.value
? 0 ? 0
: kToolbarHeight), : kToolbarHeight),
), ),
@ -205,14 +206,14 @@ class _LiveRoomPageState extends State<LiveRoomPage>
if (plPlayerController.isFullScreen.value == true) { if (plPlayerController.isFullScreen.value == true) {
plPlayerController.triggerFullScreen(status: false); plPlayerController.triggerFullScreen(status: false);
} }
if (isLandscape) { if (isLandscape.value) {
verticalScreen(); verticalScreen();
} }
}, },
child: Obx( child: Obx(
() => Container( () => Container(
width: Get.size.width, width: Get.size.width,
height: isLandscape height: isLandscape.value
? Get.size.height ? Get.size.height
: !_liveRoomController.isPortrait.value : !_liveRoomController.isPortrait.value
? Get.size.width * 9 / 16 ? Get.size.width * 9 / 16
@ -313,7 +314,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
), ),
// 消息列表 // 消息列表
Visibility( Visibility(
visible: !isLandscape, visible: !isLandscape.value,
child: Obx( child: Obx(
() => Align( () => Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,

View File

@ -249,6 +249,15 @@ class _StyleSettingState extends State<StyleSetting> {
'当前模式:${settingController.themeType.value.description}', '当前模式:${settingController.themeType.value.description}',
style: subTitleStyle)), style: subTitleStyle)),
), ),
SetSwitchItem(
title: '纯黑模式',
subTitle: '深色模式时使用纯黑色背景适用于OLED屏幕',
setKey: SettingBoxKey.enablePureBlack,
defaultVal: false,
callFn: (bool val) => {
if (val && Get.isDarkMode) {Get.appUpdate()}
},
),
ListTile( ListTile(
dense: false, dense: false,
onTap: () => settingController.setDynamicBadgeMode(context), onTap: () => settingController.setDynamicBadgeMode(context),

View File

@ -276,7 +276,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
WidgetSpan( WidgetSpan(
child: Visibility( child: Visibility(
visible: widget.videoDetail!.copyright == 2, visible: widget.videoDetail!.copyright == 2,
child: const PBadge(text: '转载', type: 'color'), child: const PBadge(text: '转载', type: 'color', stack: 'relative'),
), ),
), ),
const TextSpan(text: ' '), const TextSpan(text: ' '),

View File

@ -73,7 +73,7 @@ class VideoReplyController extends GetxController {
/// 临时修复 /// 临时修复
final bool flag = replyList final bool flag = replyList
.any((ReplyItemModel reply) => reply.rpid == replies.first.rpid); .any((ReplyItemModel reply) => reply.rpid == replies.first.rpid);
if (replies.length == 1 && flag) { if (replies.length == 1 && flag && type == 'onLoad') {
replies.clear(); replies.clear();
isEnd = true; isEnd = true;
} }

View File

@ -196,7 +196,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
var data = snapshot.data; Map? data = snapshot.data;
if (_videoReplyController.replyList.isNotEmpty || if (_videoReplyController.replyList.isNotEmpty ||
(data != null && data['status'])) { (data != null && data['status'])) {
// 请求成功 // 请求成功
@ -258,7 +258,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
} else { } else {
// 请求错误 // 请求错误
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data?['msg'] ?? '请求异常',
fn: () { fn: () {
setState(() { setState(() {
_futureBuilderFuture = _futureBuilderFuture =

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:floating/floating.dart'; import 'package:floating/floating.dart';
@ -76,7 +77,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
getStatusHeight(); getStatusHeight();
heroTag = Get.arguments['heroTag']; heroTag = Get.arguments['heroTag'];
vdCtr = Get.put(VideoDetailController(), tag: heroTag); vdCtr = Get.put(VideoDetailController(), tag: heroTag);
vdCtr.sheetHeight.value = localCache.get('sheetHeight'); vdCtr.sheetHeight.value = GlobalDataCache.sheetHeight;
videoIntroController = Get.put( videoIntroController = Get.put(
VideoIntroController(bvid: Get.parameters['bvid']!), VideoIntroController(bvid: Get.parameters['bvid']!),
tag: heroTag); tag: heroTag);
@ -223,8 +224,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
void _extendNestCtrListener() { void _extendNestCtrListener() {
final double offset = _extendNestCtr.position.pixels; final double offset = _extendNestCtr.position.pixels;
if (vdCtr.videoDirection.value == 'horizontal') { if (vdCtr.videoDirection.value == 'horizontal') {
vdCtr.sheetHeight.value = vdCtr.sheetHeight.value = max(GlobalDataCache.sheetHeight,
Get.size.height - videoHeight - statusBarHeight + offset; Get.size.height - videoHeight - statusBarHeight + offset);
appbarStream.add(offset); appbarStream.add(offset);
} else { } else {
if (offset > (Get.size.width * 22 / 16 - videoHeight)) { if (offset > (Get.size.width * 22 / 16 - videoHeight)) {

View File

@ -1068,13 +1068,10 @@ class _HeaderControlState extends State<HeaderControl> {
); );
final bool isLandscape = final bool isLandscape =
MediaQuery.of(context).orientation == Orientation.landscape; MediaQuery.of(context).orientation == Orientation.landscape;
return AppBar( return Padding(
backgroundColor: Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 14),
foregroundColor: Colors.white, child: Column(
primary: false, mainAxisSize: MainAxisSize.min,
automaticallyImplyLeading: false,
titleSpacing: 14,
title: Column(
children: [ children: [
if (isFullScreen.value && isLandscape) ...[ if (isFullScreen.value && isLandscape) ...[
Row( Row(
@ -1083,10 +1080,7 @@ class _HeaderControlState extends State<HeaderControl> {
if (videoIntroController.isShowOnlineTotal) if (videoIntroController.isShowOnlineTotal)
Text( Text(
'${videoIntroController.total.value}人正在看', '${videoIntroController.total.value}人正在看',
style: const TextStyle( style: textStyle,
color: Colors.white,
fontSize: 12,
),
), ),
const Spacer(), const Spacer(),
Expanded( Expanded(
@ -1097,18 +1091,12 @@ class _HeaderControlState extends State<HeaderControl> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
String currentTime = _formatTime(snapshot.data!); String currentTime = _formatTime(snapshot.data!);
return Text( return Text(currentTime, style: textStyle);
currentTime,
style: const TextStyle(fontSize: 12),
);
} else if (snapshot.connectionState == } else if (snapshot.connectionState ==
ConnectionState.waiting) { ConnectionState.waiting) {
// 如果Stream还未发出数据先显示初始获取的时间 // 如果Stream还未发出数据先显示初始获取的时间
String currentTime = _formatTime(initialTime); String currentTime = _formatTime(initialTime);
return Text( return Text(currentTime, style: textStyle);
currentTime,
style: const TextStyle(fontSize: 12),
);
} else { } else {
return const SizedBox(); return const SizedBox();
} }
@ -1159,7 +1147,8 @@ class _HeaderControlState extends State<HeaderControl> {
() => Marquee( () => Marquee(
text: videoIntroController.videoDetail.value.title ?? text: videoIntroController.videoDetail.value.title ??
'', '',
style: const TextStyle(fontSize: 16), style: const TextStyle(
fontSize: 16, color: Colors.white),
scrollAxis: Axis.horizontal, scrollAxis: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
blankSpace: constraints.maxWidth, blankSpace: constraints.maxWidth,

View File

@ -45,36 +45,44 @@ class WhisperController extends GetxController {
if (isLoading) return; if (isLoading) return;
var res = await MsgHttp.sessionList( var res = await MsgHttp.sessionList(
endTs: type == 'onLoad' ? sessionList.last.sessionTs : null); endTs: type == 'onLoad' ? sessionList.last.sessionTs : null);
if (res['status'] && try {
res['data'].sessionList != null && if (res['status'] &&
res['data'].sessionList.isNotEmpty) { res['data'].sessionList != null &&
await queryAccountList(res['data'].sessionList); res['data'].sessionList.isNotEmpty) {
// 将 accountList 转换为 Map 结构 await queryAccountList(res['data'].sessionList);
Map<int, dynamic> accountMap = {}; // 将 accountList 转换为 Map 结构
for (var j in accountList) { Map<int, dynamic> accountMap = {};
accountMap[j.mid!] = j; for (var j in accountList) {
} accountMap[j.mid!] = j;
}
// 遍历 sessionList通过 mid 查找并赋值 accountInfo // 遍历 sessionList通过 mid 查找并赋值 accountInfo
for (var i in res['data'].sessionList) { for (var i in res['data'].sessionList) {
var accountInfo = accountMap[i.talkerId]; var accountInfo = accountMap[i.talkerId];
if (accountInfo != null) { if (accountInfo != null) {
i.accountInfo = accountInfo; i.accountInfo = accountInfo;
}
if (i.talkerId == 844424930131966) {
i.accountInfo = AccountListModel(
name: 'UP主小助手',
face:
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
);
}
} }
if (i.talkerId == 844424930131966) { if (type == 'onLoad') {
i.accountInfo = AccountListModel( sessionList.addAll(res['data'].sessionList);
name: 'UP主小助手', } else {
face: sessionList.value = res['data'].sessionList;
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
);
} }
} }
if (type == 'onLoad') { } catch (err) {
sessionList.addAll(res['data'].sessionList); res = {
} else { 'status': false,
sessionList.value = res['data'].sessionList; 'message': err.toString(),
} };
} }
isLoading = false; isLoading = false;
return res; return res;
} }

View File

@ -44,7 +44,24 @@ class _WhisperPageState extends State<WhisperPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('消息')), appBar: AppBar(
title: const Text('消息'),
actions: [
IconButton(
icon: Icon(Icons.open_in_browser_rounded,
color: Theme.of(context).colorScheme.primary),
tooltip: '用浏览器打开',
onPressed: () {
Get.toNamed('/webview', parameters: {
'url': 'https://message.bilibili.com',
'type': 'whisper',
'pageTitle': '消息中心',
});
},
),
const SizedBox(width: 12)
],
),
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
_whisperController.unread(); _whisperController.unread();

View File

@ -521,6 +521,7 @@ class SystemNotice extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map content = item.content ?? ''; Map content = item.content ?? '';
Color primary = Theme.of(context).colorScheme.primary;
return Row( return Row(
children: [ children: [
const SizedBox(width: 12), const SizedBox(width: 12),
@ -557,12 +558,35 @@ class SystemNotice extends StatelessWidget {
.labelSmall! .labelSmall!
.copyWith(color: Theme.of(context).colorScheme.outline), .copyWith(color: Theme.of(context).colorScheme.outline),
), ),
Divider( Divider(color: primary.withOpacity(0.05)),
color: Theme.of(context).colorScheme.primary.withOpacity(0.05), SelectableText(content['text']),
), if (content['jump_text'] != null &&
SelectableText( content['jump_uri'] != null) ...[
content['text'], Divider(color: primary.withOpacity(0.05)),
) Align(
alignment: Alignment.center,
child: TextButton(
onPressed: () {
Get.toNamed('/webview', parameters: {
'url': content['jump_uri'],
'type': 'url',
'pageTitle': content['jump_text'] == ''
? '查看详情'
: content['jump_text'],
});
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith((states) {
return primary.withAlpha(20);
}),
),
child: Text(content['jump_text'] == ''
? '查看详情'
: content['jump_text']),
),
),
]
], ],
), ),
), ),

View File

@ -292,10 +292,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
style: ButtonStyle( style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero), padding: MaterialStateProperty.all(EdgeInsets.zero),
), ),
child: const Text( child: const Text('选集', style: textStyle),
'选集',
style: TextStyle(color: Colors.white, fontSize: 13),
),
), ),
), ),
@ -310,14 +307,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
padding: MaterialStateProperty.all(EdgeInsets.zero), padding: MaterialStateProperty.all(EdgeInsets.zero),
), ),
child: Obx( child: Obx(
() => Text( () => Text(_.videoFitDEsc.value, style: textStyle),
_.videoFitDEsc.value,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
), ),
), ),
), ),
@ -339,10 +329,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
height: 40, height: 40,
padding: const EdgeInsets.only(left: 20), padding: const EdgeInsets.only(left: 20),
value: speed, value: speed,
child: Text( child: Text('${speed}x', style: textStyle),
'${speed}x',
style: textStyle.copyWith(fontWeight: FontWeight.bold),
),
); );
}).toList(); }).toList();
}, },
@ -352,10 +339,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
alignment: Alignment.center, alignment: Alignment.center,
margin: const EdgeInsets.only(right: 4), margin: const EdgeInsets.only(right: 4),
child: Obx( child: Obx(
() => Text( () => Text('${_.playbackSpeed.toString()}x', style: textStyle),
'${_.playbackSpeed.toString()}x',
style: textStyle.copyWith(fontWeight: FontWeight.bold),
),
), ),
), ),
), ),
@ -443,336 +427,338 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: 12,
); );
return Stack( return ClipRect(
fit: StackFit.passthrough, child: Stack(
children: <Widget>[ fit: StackFit.passthrough,
Obx( children: <Widget>[
() => Video( Obx(
key: ValueKey(_.videoFit.value), () => Video(
controller: videoController, key: ValueKey(_.videoFit.value),
controls: NoVideoControls, controller: videoController,
alignment: widget.alignment!, controls: NoVideoControls,
pauseUponEnteringBackgroundMode: !enableBackgroundPlay, alignment: widget.alignment!,
resumeUponEnteringForegroundMode: true, pauseUponEnteringBackgroundMode: !enableBackgroundPlay,
subtitleViewConfiguration: const SubtitleViewConfiguration( resumeUponEnteringForegroundMode: true,
style: subTitleStyle, subtitleViewConfiguration: const SubtitleViewConfiguration(
padding: EdgeInsets.all(24.0), style: subTitleStyle,
padding: EdgeInsets.all(24.0),
),
fit: _.videoFit.value,
), ),
fit: _.videoFit.value,
), ),
),
/// 长按倍速 toast /// 长按倍速 toast
Obx( Obx(
() => Align( () => Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: FractionalTranslation( child: FractionalTranslation(
translation: const Offset(0.0, 0.3), // 上下偏移量(负数向上偏移) translation: const Offset(0.0, 0.3), // 上下偏移量(负数向上偏移)
child: AnimatedOpacity( child: AnimatedOpacity(
curve: Curves.easeInOut, curve: Curves.easeInOut,
opacity: _.doubleSpeedStatus.value ? 1.0 : 0.0, opacity: _.doubleSpeedStatus.value ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
child: Container( child: Container(
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0x88000000), color: const Color(0x88000000),
borderRadius: BorderRadius.circular(16.0), borderRadius: BorderRadius.circular(16.0),
),
height: 32.0,
width: 70.0,
child: const Center(
child: Text(
'倍速中',
style: TextStyle(color: Colors.white, fontSize: 13),
), ),
)), height: 32.0,
width: 70.0,
child: const Center(
child: Text(
'倍速中',
style: TextStyle(color: Colors.white, fontSize: 13),
),
)),
),
), ),
), ),
), ),
),
/// 时间进度 toast /// 时间进度 toast
Obx( Obx(
() => Align( () => Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: FractionalTranslation( child: FractionalTranslation(
translation: const Offset(0.0, 1.0), // 上下偏移量(负数向上偏移) translation: const Offset(0.0, 1.0), // 上下偏移量(负数向上偏移)
child: AnimatedOpacity( child: AnimatedOpacity(
curve: Curves.easeInOut, curve: Curves.easeInOut,
opacity: _.isSliderMoving.value ? 1.0 : 0.0, opacity: _.isSliderMoving.value ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
child: IntrinsicWidth( child: IntrinsicWidth(
child: Container( child: Container(
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0x88000000), color: const Color(0x88000000),
borderRadius: BorderRadius.circular(64.0), borderRadius: BorderRadius.circular(64.0),
), ),
height: 34.0, height: 34.0,
padding: const EdgeInsets.only(left: 10, right: 10), padding: const EdgeInsets.only(left: 10, right: 10),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Obx(() { Obx(() {
return Text( return Text(
_.sliderTempPosition.value.inMinutes >= 60 _.sliderTempPosition.value.inMinutes >= 60
? printDurationWithHours( ? printDurationWithHours(
_.sliderTempPosition.value) _.sliderTempPosition.value)
: printDuration(_.sliderTempPosition.value), : printDuration(_.sliderTempPosition.value),
style: textStyle, style: textStyle,
); );
}), }),
const SizedBox(width: 2), const SizedBox(width: 2),
const Text('/', style: textStyle), const Text('/', style: textStyle),
const SizedBox(width: 2), const SizedBox(width: 2),
Obx( Obx(
() => Text( () => Text(
_.duration.value.inMinutes >= 60 _.duration.value.inMinutes >= 60
? printDurationWithHours(_.duration.value) ? printDurationWithHours(_.duration.value)
: printDuration(_.duration.value), : printDuration(_.duration.value),
style: textStyle, style: textStyle,
),
), ),
), ],
], ),
), ),
), ),
), ),
), ),
), ),
), ),
),
/// 音量🔊 控制条展示 /// 音量🔊 控制条展示
Obx( Obx(
() => ControlBar( () => ControlBar(
visible: _volumeIndicator.value, visible: _volumeIndicator.value,
icon: _volumeValue.value < 1.0 / 3.0 icon: _volumeValue.value < 1.0 / 3.0
? Icons.volume_mute ? Icons.volume_mute
: _volumeValue.value < 2.0 / 3.0 : _volumeValue.value < 2.0 / 3.0
? Icons.volume_down ? Icons.volume_down
: Icons.volume_up, : Icons.volume_up,
value: _volumeValue.value, value: _volumeValue.value,
),
), ),
),
/// 亮度🌞 控制条展示 /// 亮度🌞 控制条展示
Obx( Obx(
() => ControlBar( () => ControlBar(
visible: _brightnessIndicator.value, visible: _brightnessIndicator.value,
icon: _brightnessValue.value < 1.0 / 3.0 icon: _brightnessValue.value < 1.0 / 3.0
? Icons.brightness_low ? Icons.brightness_low
: _brightnessValue.value < 2.0 / 3.0 : _brightnessValue.value < 2.0 / 3.0
? Icons.brightness_medium ? Icons.brightness_medium
: Icons.brightness_high, : Icons.brightness_high,
value: _brightnessValue.value, value: _brightnessValue.value,
),
), ),
),
// Obx(() { // Obx(() {
// if (_.buffered.value == Duration.zero) { // if (_.buffered.value == Duration.zero) {
// return Positioned.fill( // return Positioned.fill(
// child: Container( // child: Container(
// color: Colors.black, // color: Colors.black,
// child: Center( // child: Center(
// child: Image.asset( // child: Image.asset(
// 'assets/images/loading.gif', // 'assets/images/loading.gif',
// height: 25, // height: 25,
// ), // ),
// ), // ),
// ), // ),
// ); // );
// } else { // } else {
// return Container(); // return Container();
// } // }
// }), // }),
/// 弹幕面板 /// 弹幕面板
if (widget.danmuWidget != null) if (widget.danmuWidget != null)
Positioned.fill(top: 4, child: widget.danmuWidget!), Positioned.fill(top: 4, child: widget.danmuWidget!),
/// 开启且有字幕时展示 /// 开启且有字幕时展示
Stack( Stack(
children: [ children: [
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
bottom: 30, bottom: 30,
child: Align( child: Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Obx( child: Obx(
() => Visibility( () => Visibility(
visible: widget.controller.subTitleCode.value != -1, visible: widget.controller.subTitleCode.value != -1,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
color: widget.controller.subtitleContent.value != '' color: widget.controller.subtitleContent.value != ''
? Colors.black.withOpacity(0.6) ? Colors.black.withOpacity(0.6)
: Colors.transparent, : Colors.transparent,
),
padding: widget.controller.subTitleCode.value != -1
? const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
)
: EdgeInsets.zero,
child: Text(
widget.controller.subtitleContent.value,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
), ),
), padding: widget.controller.subTitleCode.value != -1
)), ? const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
)
: EdgeInsets.zero,
child: Text(
widget.controller.subtitleContent.value,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
)),
),
), ),
), ),
), ],
],
),
/// 手势
Positioned.fill(
left: 16,
top: 25,
right: 15,
bottom: 15,
child: GestureDetector(
onTap: () {
_.controls = !_.showControls.value;
},
onDoubleTapDown: (TapDownDetails details) {
// live模式下禁用 锁定时🔒禁用
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
final double totalWidth = MediaQuery.sizeOf(context).width;
final double tapPosition = details.localPosition.dx;
final double sectionWidth = totalWidth / 3;
String type = 'left';
if (tapPosition < sectionWidth) {
type = 'left';
} else if (tapPosition < sectionWidth * 2) {
type = 'center';
} else {
type = 'right';
}
doubleTapFuc(type);
},
onLongPressStart: (LongPressStartDetails detail) {
feedBack();
_.setDoubleSpeedStatus(true);
},
onLongPressEnd: (LongPressEndDetails details) {
_.setDoubleSpeedStatus(false);
},
/// 水平位置 快进 live模式下禁用
onHorizontalDragUpdate: (DragUpdateDetails details) {
// live模式下禁用 锁定时🔒禁用
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
// final double tapPosition = details.localPosition.dx;
final int curSliderPosition =
_.sliderPosition.value.inMilliseconds;
final double scale = 90000 / MediaQuery.sizeOf(context).width;
final Duration pos = Duration(
milliseconds:
curSliderPosition + (details.delta.dx * scale).round());
final Duration result =
pos.clamp(Duration.zero, _.duration.value);
_.onUpdatedSliderProgress(result);
_.onChangedSliderStart();
},
onHorizontalDragEnd: (DragEndDetails details) {
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
_.onChangedSliderEnd();
_.seekTo(_.sliderPosition.value, type: 'slider');
},
// 垂直方向 音量/亮度调节
onVerticalDragUpdate: (DragUpdateDetails details) async {
final double totalWidth = MediaQuery.sizeOf(context).width;
final double tapPosition = details.localPosition.dx;
final double sectionWidth =
fullScreenGestureMode == FullScreenGestureMode.none
? totalWidth / 2
: totalWidth / 3;
final double delta = details.delta.dy;
/// 锁定时禁用
if (_.controlsLock.value) {
return;
}
if (lastFullScreenToggleTime != null &&
DateTime.now().difference(lastFullScreenToggleTime!) <
const Duration(milliseconds: 500)) {
return;
}
if (tapPosition < sectionWidth) {
// 左边区域 👈
final double level = (_.isFullScreen.value
? Get.size.height
: screenWidth * 9 / 16) *
3;
final double brightness =
_brightnessValue.value - delta / level;
final double result = brightness.clamp(0.0, 1.0);
setBrightness(result);
} else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) {
// 全屏
final double dy = details.delta.dy;
const double threshold = 7.0; // 滑动阈值
final bool flag = fullScreenGestureMode !=
FullScreenGestureMode.fromBottomtoTop;
if (dy > _distance.value &&
dy > threshold &&
!_.controlsLock.value) {
if (_.isFullScreen.value ^ flag) {
lastFullScreenToggleTime = DateTime.now();
// 下滑退出全屏
await widget.controller.triggerFullScreen(status: flag);
widget.fullScreenCb?.call(flag);
}
_distance.value = 0.0;
} else if (dy < _distance.value &&
dy < -threshold &&
!_.controlsLock.value) {
if (!_.isFullScreen.value ^ flag) {
lastFullScreenToggleTime = DateTime.now();
// 上滑进入全屏
await widget.controller.triggerFullScreen(status: !flag);
widget.fullScreenCb?.call(!flag);
}
_distance.value = 0.0;
}
_distance.value = dy;
} else {
// 右边区域 👈
EasyThrottle.throttle(
'setVolume', const Duration(milliseconds: 20), () {
final double level = (_.isFullScreen.value
? Get.size.height
: screenWidth * 9 / 16);
final double volume = _volumeValue.value -
double.parse(delta.toStringAsFixed(1)) / level;
final double result = volume.clamp(0.0, 1.0);
setVolume(result);
});
}
},
onVerticalDragEnd: (DragEndDetails details) {},
), ),
),
// 头部、底部控制条 /// 手势
Obx( Positioned.fill(
() => Column( left: 16,
mainAxisAlignment: MainAxisAlignment.spaceBetween, top: 25,
children: [ right: 15,
if (widget.headerControl != null || _.headerControl != null) ...[ bottom: 15,
Flexible( child: GestureDetector(
child: ClipRect( onTap: () {
_.controls = !_.showControls.value;
},
onDoubleTapDown: (TapDownDetails details) {
// live模式下禁用 锁定时🔒禁用
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
final double totalWidth = MediaQuery.sizeOf(context).width;
final double tapPosition = details.localPosition.dx;
final double sectionWidth = totalWidth / 3;
String type = 'left';
if (tapPosition < sectionWidth) {
type = 'left';
} else if (tapPosition < sectionWidth * 2) {
type = 'center';
} else {
type = 'right';
}
doubleTapFuc(type);
},
onLongPressStart: (LongPressStartDetails detail) {
feedBack();
_.setDoubleSpeedStatus(true);
},
onLongPressEnd: (LongPressEndDetails details) {
_.setDoubleSpeedStatus(false);
},
/// 水平位置 快进 live模式下禁用
onHorizontalDragUpdate: (DragUpdateDetails details) {
// live模式下禁用 锁定时🔒禁用
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
// final double tapPosition = details.localPosition.dx;
final int curSliderPosition =
_.sliderPosition.value.inMilliseconds;
final double scale = 90000 / MediaQuery.sizeOf(context).width;
final Duration pos = Duration(
milliseconds:
curSliderPosition + (details.delta.dx * scale).round());
final Duration result =
pos.clamp(Duration.zero, _.duration.value);
_.onUpdatedSliderProgress(result);
_.onChangedSliderStart();
},
onHorizontalDragEnd: (DragEndDetails details) {
if (_.videoType == 'live' || _.controlsLock.value) {
return;
}
_.onChangedSliderEnd();
_.seekTo(_.sliderPosition.value, type: 'slider');
},
// 垂直方向 音量/亮度调节
onVerticalDragUpdate: (DragUpdateDetails details) async {
final double totalWidth = MediaQuery.sizeOf(context).width;
final double tapPosition = details.localPosition.dx;
final double sectionWidth =
fullScreenGestureMode == FullScreenGestureMode.none
? totalWidth / 2
: totalWidth / 3;
final double delta = details.delta.dy;
/// 锁定时禁用
if (_.controlsLock.value) {
return;
}
if (lastFullScreenToggleTime != null &&
DateTime.now().difference(lastFullScreenToggleTime!) <
const Duration(milliseconds: 500)) {
return;
}
if (tapPosition < sectionWidth) {
// 左边区域 👈
final double level = (_.isFullScreen.value
? Get.size.height
: screenWidth * 9 / 16) *
3;
final double brightness =
_brightnessValue.value - delta / level;
final double result = brightness.clamp(0.0, 1.0);
setBrightness(result);
} else if (isUsingFullScreenGestures(
tapPosition, sectionWidth)) {
// 全屏
final double dy = details.delta.dy;
const double threshold = 7.0; // 滑动阈值
final bool flag = fullScreenGestureMode !=
FullScreenGestureMode.fromBottomtoTop;
if (dy > _distance.value &&
dy > threshold &&
!_.controlsLock.value) {
if (_.isFullScreen.value ^ flag) {
lastFullScreenToggleTime = DateTime.now();
// 下滑退出全屏
await widget.controller.triggerFullScreen(status: flag);
widget.fullScreenCb?.call(flag);
}
_distance.value = 0.0;
} else if (dy < _distance.value &&
dy < -threshold &&
!_.controlsLock.value) {
if (!_.isFullScreen.value ^ flag) {
lastFullScreenToggleTime = DateTime.now();
// 上滑进入全屏
await widget.controller.triggerFullScreen(status: !flag);
widget.fullScreenCb?.call(!flag);
}
_distance.value = 0.0;
}
_distance.value = dy;
} else {
// 右边区域 👈
EasyThrottle.throttle(
'setVolume', const Duration(milliseconds: 20), () {
final double level = (_.isFullScreen.value
? Get.size.height
: screenWidth * 9 / 16);
final double volume = _volumeValue.value -
double.parse(delta.toStringAsFixed(1)) / level;
final double result = volume.clamp(0.0, 1.0);
setVolume(result);
});
}
},
onVerticalDragEnd: (DragEndDetails details) {},
),
),
// 头部、底部控制条
Obx(
() => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (widget.headerControl != null ||
_.headerControl != null) ...[
Flexible(
child: AppBarAni( child: AppBarAni(
controller: animationController, controller: animationController,
visible: !_.controlsLock.value && _.showControls.value, visible: !_.controlsLock.value && _.showControls.value,
@ -780,13 +766,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
child: widget.headerControl ?? _.headerControl!, child: widget.headerControl ?? _.headerControl!,
), ),
), ),
), ] else ...[
] else ...[ const SizedBox.shrink()
const SizedBox.shrink() ],
], Flexible(
Flexible( flex: _.videoType == 'live' ? 0 : 1,
flex: _.videoType == 'live' ? 0 : 1,
child: ClipRect(
child: AppBarAni( child: AppBarAni(
controller: animationController, controller: animationController,
visible: !_.controlsLock.value && _.showControls.value, visible: !_.controlsLock.value && _.showControls.value,
@ -799,141 +783,141 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
), ),
), ),
), ),
), ],
], ),
), ),
),
/// 进度条 live模式下禁用 /// 进度条 live模式下禁用
Obx( Obx(
() { () {
final int value = _.sliderPositionSeconds.value; final int value = _.sliderPositionSeconds.value;
final int max = _.durationSeconds.value; final int max = _.durationSeconds.value;
final int buffer = _.bufferedSeconds.value; final int buffer = _.bufferedSeconds.value;
if (_.showControls.value) { if (_.showControls.value) {
return Container(); return Container();
} }
if (defaultBtmProgressBehavior == if (defaultBtmProgressBehavior ==
BtmProgresBehavior.alwaysHide.code) { BtmProgresBehavior.alwaysHide.code) {
return const SizedBox(); return const SizedBox();
} }
if (defaultBtmProgressBehavior == if (defaultBtmProgressBehavior ==
BtmProgresBehavior.onlyShowFullScreen.code && BtmProgresBehavior.onlyShowFullScreen.code &&
!_.isFullScreen.value) { !_.isFullScreen.value) {
return const SizedBox(); return const SizedBox();
} else if (defaultBtmProgressBehavior == } else if (defaultBtmProgressBehavior ==
BtmProgresBehavior.onlyHideFullScreen.code && BtmProgresBehavior.onlyHideFullScreen.code &&
_.isFullScreen.value) { _.isFullScreen.value) {
return const SizedBox(); return const SizedBox();
} }
if (_.videoType == 'live') { if (_.videoType == 'live') {
return const SizedBox(); return const SizedBox();
} }
if (value > max || max <= 0) { if (value > max || max <= 0) {
return const SizedBox(); return const SizedBox();
} }
return Positioned( return Positioned(
bottom: -1.5, bottom: -1.5,
left: 0, left: 0,
right: 0, right: 0,
child: ProgressBar( child: ProgressBar(
progress: Duration(seconds: value), progress: Duration(seconds: value),
buffered: Duration(seconds: buffer), buffered: Duration(seconds: buffer),
total: Duration(seconds: max), total: Duration(seconds: max),
progressBarColor: colorTheme, progressBarColor: colorTheme,
baseBarColor: Colors.white.withOpacity(0.2), baseBarColor: Colors.white.withOpacity(0.2),
bufferedBarColor: Colors.white.withOpacity(0.6), bufferedBarColor: Colors.white.withOpacity(0.6),
timeLabelLocation: TimeLabelLocation.none, timeLabelLocation: TimeLabelLocation.none,
thumbColor: colorTheme, thumbColor: colorTheme,
barHeight: 3, barHeight: 3,
thumbRadius: 0.0, thumbRadius: 0.0,
// onDragStart: (duration) { // onDragStart: (duration) {
// _.onChangedSliderStart(); // _.onChangedSliderStart();
// }, // },
// onDragEnd: () { // onDragEnd: () {
// _.onChangedSliderEnd(); // _.onChangedSliderEnd();
// }, // },
// onDragUpdate: (details) { // onDragUpdate: (details) {
// print(details); // print(details);
// }, // },
// onSeek: (duration) { // onSeek: (duration) {
// feedBack(); // feedBack();
// _.onChangedSlider(duration.inSeconds.toDouble()); // _.onChangedSlider(duration.inSeconds.toDouble());
// _.seekTo(duration); // _.seekTo(duration);
// }, // },
), ),
// SlideTransition( // SlideTransition(
// position: Tween<Offset>( // position: Tween<Offset>(
// begin: Offset.zero, // begin: Offset.zero,
// end: const Offset(0, -1), // end: const Offset(0, -1),
// ).animate(CurvedAnimation( // ).animate(CurvedAnimation(
// parent: animationController, // parent: animationController,
// curve: Curves.easeInOut, // curve: Curves.easeInOut,
// )), // )),
// child: ), // child: ),
); );
}, },
), ),
// 锁 // 锁
Obx( Obx(
() => Visibility( () => Visibility(
visible: _.videoType != 'live' && _.isFullScreen.value, visible: _.videoType != 'live' && _.isFullScreen.value,
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FractionalTranslation( child: FractionalTranslation(
translation: const Offset(1, 0.0), translation: const Offset(1, 0.0),
child: Visibility( child: Visibility(
visible: _.showControls.value, visible: _.showControls.value,
child: ComBtn( child: ComBtn(
icon: Icon( icon: Icon(
_.controlsLock.value _.controlsLock.value
? FontAwesomeIcons.lock ? FontAwesomeIcons.lock
: FontAwesomeIcons.lockOpen, : FontAwesomeIcons.lockOpen,
size: 15, size: 15,
color: Colors.white, color: Colors.white,
),
fuc: () => _.onLockControl(!_.controlsLock.value),
), ),
fuc: () => _.onLockControl(!_.controlsLock.value),
), ),
), ),
), ),
), ),
), ),
), //
// Obx(() {
Obx(() { if (_.dataStatus.loading || _.isBuffering.value) {
if (_.dataStatus.loading || _.isBuffering.value) { return Center(
return Center( child: Container(
child: Container( padding: const EdgeInsets.all(30),
padding: const EdgeInsets.all(30), decoration: const BoxDecoration(
decoration: const BoxDecoration( shape: BoxShape.circle,
shape: BoxShape.circle, gradient: RadialGradient(
gradient: RadialGradient( colors: [Colors.black26, Colors.transparent],
colors: [Colors.black26, Colors.transparent], ),
),
child: Lottie.asset(
'assets/loading.json',
width: 200,
), ),
), ),
child: Lottie.asset( );
'assets/loading.json', } else {
width: 200, return const SizedBox();
), }
), }),
);
} else {
return const SizedBox();
}
}),
/// 快进/快退面板 /// 快进/快退面板
SeekPanel( SeekPanel(
mountSeekBackwardButton: _mountSeekBackwardButton, mountSeekBackwardButton: _mountSeekBackwardButton,
mountSeekForwardButton: _mountSeekForwardButton, mountSeekForwardButton: _mountSeekForwardButton,
hideSeekBackwardButton: _hideSeekBackwardButton, hideSeekBackwardButton: _hideSeekBackwardButton,
hideSeekForwardButton: _hideSeekForwardButton, hideSeekForwardButton: _hideSeekForwardButton,
onSubmittedcb: _handleSubmittedCallback, onSubmittedcb: _handleSubmittedCallback,
), ),
], ],
),
); );
} }
} }

View File

@ -18,19 +18,18 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Padding(
color: Colors.transparent,
height: 90,
padding: const EdgeInsets.symmetric(horizontal: 18), padding: const EdgeInsets.symmetric(horizontal: 18),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB(7, 0, 7, 6), padding: const EdgeInsets.fromLTRB(7, 0, 7, 4),
child: ProgressBarWidget(controller: controller!), child: ProgressBarWidget(controller: controller!),
), ),
Row(children: buildBottomControl!), Row(children: buildBottomControl!),
const SizedBox(height: 10), const SizedBox(height: 6),
], ],
), ),
); );

View File

@ -137,7 +137,8 @@ class SettingBoxKey {
enableGradientBg = 'enableGradientBg', enableGradientBg = 'enableGradientBg',
enableDynamicSwitch = 'enableDynamicSwitch', enableDynamicSwitch = 'enableDynamicSwitch',
navBarSort = 'navBarSort', navBarSort = 'navBarSort',
actionTypeSort = 'actionTypeSort'; actionTypeSort = 'actionTypeSort',
enablePureBlack = 'enablePureBlack';
} }
class LocalCacheKey { class LocalCacheKey {