Merge branch 'main' into design
This commit is contained in:
@ -45,25 +45,37 @@ class EpisodeBottomSheet {
|
|||||||
title = '第${episode.title}话 ${episode.longTitle!}';
|
title = '第${episode.title}话 ${episode.longTitle!}';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return InkWell(
|
return isFullScreen || episode?.cover == null || episode?.cover == ''
|
||||||
|
? ListTile(
|
||||||
|
onTap: () {
|
||||||
|
SmartDialog.showToast('切换至「$title」');
|
||||||
|
changeFucCall.call(episode, index);
|
||||||
|
},
|
||||||
|
dense: false,
|
||||||
|
leading: isCurrentIndex
|
||||||
|
? Image.asset(
|
||||||
|
'assets/images/live.gif',
|
||||||
|
color: primary,
|
||||||
|
height: 12,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
title: Text(title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isCurrentIndex ? primary : onSurface,
|
||||||
|
)))
|
||||||
|
: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SmartDialog.showToast('切换至「$title」');
|
SmartDialog.showToast('切换至「$title」');
|
||||||
changeFucCall.call(episode, index);
|
changeFucCall.call(episode, index);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8),
|
padding:
|
||||||
child: isFullScreen
|
const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8),
|
||||||
? Text(
|
child: Row(
|
||||||
title,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: isCurrentIndex ? primary : onSurface,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
children: [
|
children: [
|
||||||
NetworkImgLayer(width: 130, height: 75, src: episode.cover),
|
NetworkImgLayer(
|
||||||
|
width: 130, height: 75, src: episode?.cover ?? ''),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@ -65,6 +65,20 @@ void main() async {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 小白条、导航栏沉浸
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
List<String> versionParts = Platform.version.split('.');
|
||||||
|
int androidVersion = int.parse(versionParts[0]);
|
||||||
|
if (androidVersion >= 29) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
|
}
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||||
|
systemNavigationBarColor: Colors.transparent,
|
||||||
|
systemNavigationBarDividerColor: Colors.transparent,
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Data.init();
|
Data.init();
|
||||||
GlobalData();
|
GlobalData();
|
||||||
PiliSchame.init();
|
PiliSchame.init();
|
||||||
@ -130,26 +144,33 @@ class MyApp extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeData themeData = ThemeData(
|
// ThemeData themeData = ThemeData(
|
||||||
colorScheme: currentThemeValue == ThemeType.dark
|
// colorScheme: currentThemeValue == ThemeType.dark
|
||||||
? darkColorScheme
|
// ? darkColorScheme
|
||||||
: lightColorScheme,
|
// : lightColorScheme,
|
||||||
);
|
// );
|
||||||
|
|
||||||
// 小白条、导航栏沉浸
|
// // 小白条、导航栏沉浸
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
// if (Platform.isAndroid) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
// List<String> versionParts = Platform.version.split('.');
|
||||||
systemNavigationBarColor: GlobalData().enableMYBar
|
// int androidVersion = int.parse(versionParts[0]);
|
||||||
? const Color(0x00010000)
|
// if (androidVersion >= 29) {
|
||||||
: themeData.canvasColor,
|
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
systemNavigationBarDividerColor: GlobalData().enableMYBar
|
// }
|
||||||
? const Color(0x00010000)
|
// SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
: themeData.canvasColor,
|
// systemNavigationBarColor: GlobalData().enableMYBar
|
||||||
systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark
|
// ? const Color(0x00010000)
|
||||||
? Brightness.light
|
// : themeData.canvasColor,
|
||||||
: Brightness.dark,
|
// systemNavigationBarDividerColor: GlobalData().enableMYBar
|
||||||
statusBarColor: Colors.transparent,
|
// ? const Color(0x00010000)
|
||||||
));
|
// : themeData.canvasColor,
|
||||||
|
// systemNavigationBarIconBrightness:
|
||||||
|
// currentThemeValue == ThemeType.dark
|
||||||
|
// ? Brightness.light
|
||||||
|
// : Brightness.dark,
|
||||||
|
// statusBarColor: Colors.transparent,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
// 图片缓存
|
// 图片缓存
|
||||||
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
|
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
|
||||||
|
|||||||
@ -412,8 +412,8 @@ class Part {
|
|||||||
dimension = json["dimension"] == null
|
dimension = json["dimension"] == null
|
||||||
? null
|
? null
|
||||||
: Dimension.fromJson(json["dimension"]);
|
: Dimension.fromJson(json["dimension"]);
|
||||||
firstFrame = json["first_frame"];
|
firstFrame = json["first_frame"] ?? '';
|
||||||
cover = json["first_frame"];
|
cover = json["first_frame"] ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|||||||
@ -115,20 +115,15 @@ class VideoDetailController extends GetxController
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
final Map argMap = Get.arguments;
|
final Map argMap = Get.arguments;
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
var keys = argMap.keys.toList();
|
if (argMap.containsKey('videoItem')) {
|
||||||
if (keys.isNotEmpty) {
|
|
||||||
if (keys.contains('videoItem')) {
|
|
||||||
var args = argMap['videoItem'];
|
var args = argMap['videoItem'];
|
||||||
if (args.pic != null && args.pic != '') {
|
updateCover(args.pic);
|
||||||
videoItem['pic'] = args.pic;
|
|
||||||
cover.value = args.pic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keys.contains('pic')) {
|
|
||||||
videoItem['pic'] = argMap['pic'];
|
|
||||||
cover.value = argMap['pic'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argMap.containsKey('pic')) {
|
||||||
|
updateCover(argMap['pic']);
|
||||||
}
|
}
|
||||||
|
|
||||||
tabCtr = TabController(length: 2, vsync: this);
|
tabCtr = TabController(length: 2, vsync: this);
|
||||||
autoPlay.value =
|
autoPlay.value =
|
||||||
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
|
||||||
@ -550,4 +545,10 @@ class VideoDetailController extends GetxController
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateCover(String? pic) {
|
||||||
|
if (pic != null && pic != '') {
|
||||||
|
cover.value = videoItem['pic'] = pic;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,6 +90,7 @@ class VideoIntroController extends GetxController {
|
|||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}'];
|
videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}'];
|
||||||
|
videoDetailCtr.cover.value = result['data'].pic ?? '';
|
||||||
// 获取到粉丝数再返回
|
// 获取到粉丝数再返回
|
||||||
await queryUserStat();
|
await queryUserStat();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,7 +135,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
late final dynamic owner;
|
late final dynamic owner;
|
||||||
late final dynamic follower;
|
late final dynamic follower;
|
||||||
late final dynamic followStatus;
|
|
||||||
late int mid;
|
late int mid;
|
||||||
late String memberHeroTag;
|
late String memberHeroTag;
|
||||||
late bool enableAi;
|
late bool enableAi;
|
||||||
@ -164,7 +163,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
owner = widget.videoDetail!.owner;
|
owner = widget.videoDetail!.owner;
|
||||||
follower = Utils.numFormat(videoIntroController.userStat['follower']);
|
follower = Utils.numFormat(videoIntroController.userStat['follower']);
|
||||||
followStatus = videoIntroController.followStatus;
|
|
||||||
enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true);
|
enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true);
|
||||||
_expandableCtr = ExpandableController(initialExpanded: false);
|
_expandableCtr = ExpandableController(initialExpanded: false);
|
||||||
}
|
}
|
||||||
@ -441,48 +439,39 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Obx(
|
Obx(
|
||||||
() => AnimatedOpacity(
|
() {
|
||||||
opacity:
|
final bool isFollowed =
|
||||||
videoIntroController.followStatus.isEmpty ? 0 : 1,
|
videoIntroController.followStatus['attribute'] != 0;
|
||||||
duration: const Duration(milliseconds: 50),
|
return videoIntroController.followStatus.isEmpty
|
||||||
child: SizedBox(
|
? const SizedBox()
|
||||||
|
: SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
child: Obx(
|
child: TextButton(
|
||||||
() => videoIntroController.followStatus.isNotEmpty
|
|
||||||
? TextButton(
|
|
||||||
onPressed:
|
onPressed:
|
||||||
videoIntroController.actionRelationMod,
|
videoIntroController.actionRelationMod,
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 8, right: 8),
|
left: 8,
|
||||||
foregroundColor:
|
right: 8,
|
||||||
followStatus['attribute'] != 0
|
),
|
||||||
|
foregroundColor: isFollowed
|
||||||
? outline
|
? outline
|
||||||
: t.colorScheme.onPrimary,
|
: t.colorScheme.onPrimary,
|
||||||
backgroundColor:
|
backgroundColor: isFollowed
|
||||||
followStatus['attribute'] != 0
|
|
||||||
? t.colorScheme.onInverseSurface
|
? t.colorScheme.onInverseSurface
|
||||||
: t.colorScheme
|
: t.colorScheme.primary, // 设置按钮背景色
|
||||||
.primary, // 设置按钮背景色
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
followStatus['attribute'] != 0
|
isFollowed ? '已关注' : '关注',
|
||||||
? '已关注'
|
|
||||||
: '关注',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: t
|
fontSize:
|
||||||
.textTheme.labelMedium!.fontSize),
|
t.textTheme.labelMedium!.fontSize,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: ElevatedButton(
|
|
||||||
onPressed:
|
|
||||||
videoIntroController.actionRelationMod,
|
|
||||||
child: const Text('关注'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -427,7 +427,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
if (extraRow == 1)
|
if (extraRow == 1)
|
||||||
InkWell(
|
InkWell(
|
||||||
// 一楼点击【共xx条回复】展开评论详情
|
// 一楼点击【共xx条回复】展开评论详情
|
||||||
onTap: () => replyReply!(replyItem, null),
|
onTap: () => replyReply!(replyItem),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.fromLTRB(8, 5, 8, 8),
|
padding: const EdgeInsets.fromLTRB(8, 5, 8, 8),
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import 'package:pilipala/plugin/pl_player/index.dart';
|
|||||||
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/services/service_locator.dart';
|
import 'package:pilipala/services/service_locator.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:status_bar_control/status_bar_control.dart';
|
||||||
|
|
||||||
import '../../../plugin/pl_player/models/bottom_control_type.dart';
|
import '../../../plugin/pl_player/models/bottom_control_type.dart';
|
||||||
import '../../../services/shutdown_timer_service.dart';
|
import '../../../services/shutdown_timer_service.dart';
|
||||||
@ -38,7 +39,7 @@ class VideoDetailPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _VideoDetailPageState extends State<VideoDetailPage>
|
class _VideoDetailPageState extends State<VideoDetailPage>
|
||||||
with TickerProviderStateMixin, RouteAware {
|
with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver {
|
||||||
late VideoDetailController vdCtr;
|
late VideoDetailController vdCtr;
|
||||||
PlPlayerController? plPlayerController;
|
PlPlayerController? plPlayerController;
|
||||||
final ScrollController _extendNestCtr = ScrollController();
|
final ScrollController _extendNestCtr = ScrollController();
|
||||||
@ -61,10 +62,14 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
late bool autoPiP;
|
late bool autoPiP;
|
||||||
late Floating floating;
|
late Floating floating;
|
||||||
bool isShowing = true;
|
bool isShowing = true;
|
||||||
|
// 生命周期监听
|
||||||
|
late final AppLifecycleListener _lifecycleListener;
|
||||||
|
late double statusHeight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
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 = localCache.get('sheetHeight');
|
||||||
@ -96,6 +101,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
floating = vdCtr.floating!;
|
floating = vdCtr.floating!;
|
||||||
autoEnterPip();
|
autoEnterPip();
|
||||||
}
|
}
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
lifecycleListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取视频资源,初始化播放器
|
// 获取视频资源,初始化播放器
|
||||||
@ -203,6 +210,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStatusHeight() async {
|
||||||
|
statusHeight = await StatusBarControl.getHeight;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
shutdownTimerService.handleWaitingFinished();
|
shutdownTimerService.handleWaitingFinished();
|
||||||
@ -219,6 +230,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
floating.dispose();
|
floating.dispose();
|
||||||
}
|
}
|
||||||
appbarStream.close();
|
appbarStream.close();
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
_lifecycleListener.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +294,166 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生命周期监听
|
||||||
|
void lifecycleListener() {
|
||||||
|
_lifecycleListener = AppLifecycleListener(
|
||||||
|
// onResume: () => _handleTransition('resume'),
|
||||||
|
// 后台
|
||||||
|
// onInactive: () => _handleTransition('inactive'),
|
||||||
|
// 在Android和iOS端不生效
|
||||||
|
// onHide: () => _handleTransition('hide'),
|
||||||
|
onShow: () => _handleTransition('show'),
|
||||||
|
onPause: () => _handleTransition('pause'),
|
||||||
|
onRestart: () => _handleTransition('restart'),
|
||||||
|
onDetach: () => _handleTransition('detach'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTransition(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case 'show' || 'restart':
|
||||||
|
plPlayerController?.danmakuController?.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 手动播放
|
||||||
|
Widget handlePlayPanel() {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: handlePlay,
|
||||||
|
child: Image.network(
|
||||||
|
vdCtr.videoItem['pic'],
|
||||||
|
width: Get.width,
|
||||||
|
height: videoHeight,
|
||||||
|
fit: BoxFit.cover, // 适应方式根据需要调整
|
||||||
|
),
|
||||||
|
),
|
||||||
|
buildCustomAppBar(),
|
||||||
|
Positioned(
|
||||||
|
right: 12,
|
||||||
|
bottom: 10,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: handlePlay,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/play.png',
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// tabbar
|
||||||
|
Widget tabbarBuild() {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 45,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
width: 1,
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Material(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Obx(
|
||||||
|
() => TabBar(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
controller: vdCtr.tabCtr,
|
||||||
|
labelStyle: const TextStyle(fontSize: 13),
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
tabs:
|
||||||
|
vdCtr.tabs.map((String name) => Tab(text: name)).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
flex: 1,
|
||||||
|
child: Center(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Obx(() => AnimatedOpacity(
|
||||||
|
opacity: playerStatus.value != PlayerStatus.playing
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.drag_handle_rounded,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 32,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () => vdCtr.showShootDanmakuSheet(),
|
||||||
|
child:
|
||||||
|
const Text('发弹幕', style: TextStyle(fontSize: 12)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
child: Obx(
|
||||||
|
() => !vdCtr.isShowCover.value
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
plPlayerController?.isOpenDanmu.value =
|
||||||
|
!(plPlayerController?.isOpenDanmu.value ??
|
||||||
|
false);
|
||||||
|
},
|
||||||
|
icon: !(plPlayerController?.isOpenDanmu.value ??
|
||||||
|
false)
|
||||||
|
? SvgPicture.asset(
|
||||||
|
'assets/images/video/danmu_close.svg',
|
||||||
|
// ignore: deprecated_member_use
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline,
|
||||||
|
)
|
||||||
|
: SvgPicture.asset(
|
||||||
|
'assets/images/video/danmu_open.svg',
|
||||||
|
// ignore: deprecated_member_use
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
'assets/images/video/danmu_close.svg',
|
||||||
|
// ignore: deprecated_member_use
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 18),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sizeContext = MediaQuery.sizeOf(context);
|
final sizeContext = MediaQuery.sizeOf(context);
|
||||||
@ -338,168 +511,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/// tabbar
|
|
||||||
Widget tabbarBuild = Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 45,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
width: 1,
|
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Material(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
flex: 1,
|
|
||||||
child: Obx(
|
|
||||||
() => TabBar(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
controller: vdCtr.tabCtr,
|
|
||||||
labelStyle: const TextStyle(fontSize: 13),
|
|
||||||
labelPadding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
tabs: vdCtr.tabs
|
|
||||||
.map(
|
|
||||||
(String name) => Tab(text: name),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
flex: 1,
|
|
||||||
child: Center(
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Obx(() => AnimatedOpacity(
|
|
||||||
opacity: playerStatus.value != PlayerStatus.playing
|
|
||||||
? 1
|
|
||||||
: 0,
|
|
||||||
duration: const Duration(milliseconds: 100),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.drag_handle_rounded,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
SizedBox(
|
|
||||||
height: 32,
|
|
||||||
child: TextButton(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () => vdCtr.showShootDanmakuSheet(),
|
|
||||||
child:
|
|
||||||
const Text('发弹幕', style: TextStyle(fontSize: 12)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 38,
|
|
||||||
height: 38,
|
|
||||||
child: Obx(
|
|
||||||
() => !vdCtr.isShowCover.value
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
plPlayerController?.isOpenDanmu.value =
|
|
||||||
!(plPlayerController
|
|
||||||
?.isOpenDanmu.value ??
|
|
||||||
false);
|
|
||||||
},
|
|
||||||
icon:
|
|
||||||
!(plPlayerController?.isOpenDanmu.value ??
|
|
||||||
false)
|
|
||||||
? SvgPicture.asset(
|
|
||||||
'assets/images/video/danmu_close.svg',
|
|
||||||
// ignore: deprecated_member_use
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
)
|
|
||||||
: SvgPicture.asset(
|
|
||||||
'assets/images/video/danmu_open.svg',
|
|
||||||
// ignore: deprecated_member_use
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: IconButton(
|
|
||||||
icon: SvgPicture.asset(
|
|
||||||
'assets/images/video/danmu_close.svg',
|
|
||||||
// ignore: deprecated_member_use
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 18),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 手动播放
|
|
||||||
Widget handlePlayPanel() {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
handlePlay();
|
|
||||||
},
|
|
||||||
child: Obx(
|
|
||||||
() => AnimatedOpacity(
|
|
||||||
duration: const Duration(milliseconds: 100), // 渐变动画的持续时间
|
|
||||||
opacity: 1, // 设置不透明度
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
type: 'emote',
|
|
||||||
src: vdCtr.cover.value,
|
|
||||||
width: Get.width,
|
|
||||||
height: videoHeight.value,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: buildCustomAppBar(),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 12,
|
|
||||||
bottom: 10,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '播放',
|
|
||||||
onPressed: () => handlePlay(),
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/play.png',
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget childWhenDisabled = SafeArea(
|
Widget childWhenDisabled = SafeArea(
|
||||||
top: MediaQuery.of(context).orientation == Orientation.portrait &&
|
top: MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||||
plPlayerController?.isFullScreen.value == true,
|
plPlayerController?.isFullScreen.value == true,
|
||||||
bottom: MediaQuery.of(context).orientation == Orientation.portrait &&
|
bottom: MediaQuery.of(context).orientation == Orientation.portrait &&
|
||||||
plPlayerController?.isFullScreen.value == true,
|
plPlayerController?.isFullScreen.value == true,
|
||||||
left: false, //plPlayerController?.isFullScreen.value != true,
|
left: false,
|
||||||
right: false, //plPlayerController?.isFullScreen.value != true,
|
right: false,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@ -517,12 +535,22 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
controller: _extendNestCtr,
|
controller: _extendNestCtr,
|
||||||
headerSliverBuilder:
|
headerSliverBuilder:
|
||||||
(BuildContext context2, bool innerBoxIsScrolled) {
|
(BuildContext context2, bool innerBoxIsScrolled) {
|
||||||
|
final Orientation orientation =
|
||||||
|
MediaQuery.of(context).orientation;
|
||||||
|
final bool isFullScreen =
|
||||||
|
plPlayerController?.isFullScreen.value == true;
|
||||||
|
final double expandedHeight =
|
||||||
|
orientation == Orientation.landscape || isFullScreen
|
||||||
|
? (MediaQuery.sizeOf(context).height -
|
||||||
|
(orientation == Orientation.landscape
|
||||||
|
? 0
|
||||||
|
: MediaQuery.of(context).padding.top))
|
||||||
|
: videoHeight.value;
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
if (MediaQuery.of(context).orientation ==
|
if (orientation == Orientation.landscape ||
|
||||||
Orientation.landscape ||
|
isFullScreen) {
|
||||||
plPlayerController?.isFullScreen.value == true) {
|
|
||||||
enterFullScreen();
|
enterFullScreen();
|
||||||
} else {
|
} else {
|
||||||
exitFullScreen();
|
exitFullScreen();
|
||||||
@ -534,15 +562,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
forceElevated: innerBoxIsScrolled,
|
forceElevated: innerBoxIsScrolled,
|
||||||
expandedHeight: MediaQuery.of(context).orientation ==
|
expandedHeight: expandedHeight,
|
||||||
Orientation.landscape ||
|
|
||||||
plPlayerController?.isFullScreen.value == true
|
|
||||||
? (MediaQuery.sizeOf(context).height -
|
|
||||||
(MediaQuery.of(context).orientation ==
|
|
||||||
Orientation.landscape
|
|
||||||
? 0
|
|
||||||
: MediaQuery.of(context).padding.top))
|
|
||||||
: videoHeight.value,
|
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: PopScope(
|
background: PopScope(
|
||||||
@ -562,13 +582,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (BuildContext context,
|
builder: (BuildContext context,
|
||||||
BoxConstraints boxConstraints) {
|
BoxConstraints boxConstraints) {
|
||||||
// final double maxWidth =
|
|
||||||
// boxConstraints.maxWidth;
|
|
||||||
// final double maxHeight =
|
|
||||||
// boxConstraints.maxHeight;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (isShowing) videoPlayerPanel,
|
if (isShowing)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 0),
|
||||||
|
child: videoPlayerPanel,
|
||||||
|
),
|
||||||
|
|
||||||
/// 关闭自动播放时 手动播放
|
/// 关闭自动播放时 手动播放
|
||||||
Obx(
|
Obx(
|
||||||
@ -610,7 +630,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
tabbarBuild,
|
tabbarBuild(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: vdCtr.tabCtr,
|
controller: vdCtr.tabCtr,
|
||||||
|
|||||||
@ -117,7 +117,7 @@ class PiliSchame {
|
|||||||
// ignore: always_specify_types
|
// ignore: always_specify_types
|
||||||
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
||||||
arguments: <String, String?>{
|
arguments: <String, String?>{
|
||||||
'pic': null,
|
'pic': '',
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user