opt: 竖屏直播布局

This commit is contained in:
guozhigq
2024-09-22 23:16:19 +08:00
parent 031358e72e
commit 249600ec27
4 changed files with 150 additions and 136 deletions

View File

@ -1,11 +1,13 @@
class RoomInfoModel { class RoomInfoModel {
RoomInfoModel({ RoomInfoModel({
this.roomId, this.roomId,
this.isPortrait,
this.liveStatus, this.liveStatus,
this.liveTime, this.liveTime,
this.playurlInfo, this.playurlInfo,
}); });
int? roomId; int? roomId;
bool? isPortrait;
int? liveStatus; int? liveStatus;
int? liveTime; int? liveTime;
PlayurlInfo? playurlInfo; PlayurlInfo? playurlInfo;
@ -13,6 +15,7 @@ class RoomInfoModel {
RoomInfoModel.fromJson(Map<String, dynamic> json) { RoomInfoModel.fromJson(Map<String, dynamic> json) {
roomId = json['room_id']; roomId = json['room_id'];
liveStatus = json['live_status']; liveStatus = json['live_status'];
isPortrait = json['is_portrait'];
liveTime = json['live_time']; liveTime = json['live_time'];
playurlInfo = PlayurlInfo.fromJson(json['playurl_info']); playurlInfo = PlayurlInfo.fromJson(json['playurl_info']);
} }

View File

@ -48,6 +48,7 @@ class LiveRoomController extends GetxController {
// 直播间弹幕开关 默认打开 // 直播间弹幕开关 默认打开
RxBool danmakuSwitch = true.obs; RxBool danmakuSwitch = true.obs;
late String buvid; late String buvid;
RxBool isPortrait = false.obs;
@override @override
void onInit() { void onInit() {
@ -101,6 +102,7 @@ class LiveRoomController extends GetxController {
Future queryLiveInfo() async { Future queryLiveInfo() async {
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn);
if (res['status']) { if (res['status']) {
isPortrait.value = res['data'].isPortrait;
List<CodecItem> codec = List<CodecItem> codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec; res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first; CodecItem item = codec.first;

View File

@ -115,6 +115,9 @@ class _LiveRoomPageState extends State<LiveRoomPage>
plPlayerController = _liveRoomController.plPlayerController; plPlayerController = _liveRoomController.plPlayerController;
return PLVideoPlayer( return PLVideoPlayer(
controller: plPlayerController, controller: plPlayerController,
alignment: _liveRoomController.isPortrait.value
? Alignment.topCenter
: Alignment.center,
bottomControl: BottomControl( bottomControl: BottomControl(
controller: plPlayerController, controller: plPlayerController,
liveRoomCtr: _liveRoomController, liveRoomCtr: _liveRoomController,
@ -178,64 +181,18 @@ class _LiveRoomPageState extends State<LiveRoomPage>
), ),
), ),
), ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AppBar( Obx(
centerTitle: false, () => SizedBox(
titleSpacing: 0, height: MediaQuery.of(context).padding.top +
backgroundColor: Colors.transparent, (_liveRoomController.isPortrait.value ||
foregroundColor: Colors.white, MediaQuery.of(context).orientation ==
toolbarHeight: Orientation.landscape
MediaQuery.of(context).orientation == Orientation.portrait ? 0
? 56 : kToolbarHeight),
: 0,
title: FutureBuilder(
future: _futureBuilder,
builder: (context, snapshot) {
if (snapshot.data == null) {
return const SizedBox();
}
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => Row(
children: [
NetworkImgLayer(
width: 34,
height: 34,
type: 'avatar',
src: _liveRoomController
.roomInfoH5.value.anchorInfo!.baseInfo!.face,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_liveRoomController.roomInfoH5.value
.anchorInfo!.baseInfo!.uname!,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 1),
if (_liveRoomController
.roomInfoH5.value.watchedShow !=
null)
Text(
_liveRoomController.roomInfoH5.value
.watchedShow!['text_large'] ??
'',
style: const TextStyle(fontSize: 12),
),
],
),
],
),
);
} else {
return const SizedBox();
}
},
), ),
), ),
PopScope( PopScope(
@ -249,66 +206,141 @@ class _LiveRoomPageState extends State<LiveRoomPage>
verticalScreen(); verticalScreen();
} }
}, },
child: SizedBox( child: Obx(
width: Get.size.width, () => Container(
height: MediaQuery.of(context).orientation == width: Get.size.width,
Orientation.landscape height: MediaQuery.of(context).orientation ==
? Get.size.height Orientation.landscape
: Get.size.width * 9 / 16, ? Get.size.height
child: videoPlayerPanel, : !_liveRoomController.isPortrait.value
? Get.size.width * 9 / 16
: Get.size.height -
MediaQuery.of(context).padding.top,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(6)),
),
child: videoPlayerPanel,
),
), ),
), ),
// 显示消息的列表 ],
buildMessageListUI( ),
// 定位 快速滑动到底部
Positioned(
right: 20,
bottom: MediaQuery.of(context).padding.bottom + 80,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 4),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: ElevatedButton.icon(
onPressed: () {
_scrollToBottom();
},
icon: const Icon(Icons.keyboard_arrow_down), // 图标
label: const Text('新消息'), // 文字
style: ElevatedButton.styleFrom(
// primary: Colors.blue, // 按钮背景颜色
// onPrimary: Colors.white, // 按钮文字颜色
padding: const EdgeInsets.fromLTRB(14, 12, 20, 12), // 按钮内边距
),
),
),
),
// 顶栏
Positioned(
top: 0,
left: 0,
right: 0,
child: AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
toolbarHeight:
MediaQuery.of(context).orientation == Orientation.portrait
? 56
: 0,
title: FutureBuilder(
future: _futureBuilder,
builder: (context, snapshot) {
if (snapshot.data == null) {
return const SizedBox();
}
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => Row(
children: [
NetworkImgLayer(
width: 34,
height: 34,
type: 'avatar',
src: _liveRoomController
.roomInfoH5.value.anchorInfo!.baseInfo!.face,
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_liveRoomController.roomInfoH5.value.anchorInfo!
.baseInfo!.uname!,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 1),
if (_liveRoomController
.roomInfoH5.value.watchedShow !=
null)
Text(
_liveRoomController.roomInfoH5.value
.watchedShow!['text_large'] ??
'',
style: const TextStyle(fontSize: 12),
),
],
),
],
),
);
} else {
return const SizedBox();
}
},
),
),
),
// 消息列表
Obx(
() => Positioned(
top: MediaQuery.of(context).padding.top +
kToolbarHeight +
(_liveRoomController.isPortrait.value
? Get.size.width
: Get.size.width * 9 / 16),
bottom: 110,
left: 0,
right: 0,
child: buildMessageListUI(
context, context,
_liveRoomController, _liveRoomController,
_scrollController, _scrollController,
), ),
// Container( ),
// padding: const EdgeInsets.only( ),
// left: 14, right: 14, top: 4, bottom: 4), // 消息输入框
// margin: const EdgeInsets.only( Visibility(
// bottom: 6, visible: MediaQuery.of(context).orientation == Orientation.portrait,
// left: 14, child: Positioned(
// ), bottom: 0,
// decoration: BoxDecoration( left: 0,
// color: Colors.grey.withOpacity(0.1), right: 0,
// borderRadius: const BorderRadius.all(Radius.circular(20)), child: Container(
// ),
// child: Obx(
// () => AnimatedSwitcher(
// duration: const Duration(milliseconds: 300),
// transitionBuilder:
// (Widget child, Animation<double> animation) {
// return FadeTransition(opacity: animation, child: child);
// },
// child: Text.rich(
// key:
// ValueKey(_liveRoomController.joinRoomTip['userName']),
// TextSpan(
// style: const TextStyle(color: Colors.white),
// children: [
// TextSpan(
// text:
// '${_liveRoomController.joinRoomTip['userName']} ',
// style: TextStyle(
// color: Colors.white.withOpacity(0.6),
// ),
// ),
// TextSpan(
// text:
// '${_liveRoomController.joinRoomTip['message']}',
// style: const TextStyle(color: Colors.white),
// ),
// ],
// ),
// ),
// ),
// ),
// ),
const SizedBox(height: 10),
// 弹幕输入框
Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 14, left: 14,
right: 14, right: 14,
@ -384,32 +416,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
], ],
), ),
), ),
],
),
// 定位 快速滑动到底部
Positioned(
right: 20,
bottom: MediaQuery.of(context).padding.bottom + 80,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 4),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: ElevatedButton.icon(
onPressed: () {
_scrollToBottom();
},
icon: const Icon(Icons.keyboard_arrow_down), // 图标
label: const Text('新消息'), // 文字
style: ElevatedButton.styleFrom(
// primary: Colors.blue, // 按钮背景颜色
// onPrimary: Colors.white, // 按钮文字颜色
padding: const EdgeInsets.fromLTRB(14, 12, 20, 12), // 按钮内边距
),
),
), ),
), ),
], ],
@ -467,7 +473,7 @@ Widget buildMessageListUI(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1), color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20)), borderRadius: const BorderRadius.all(Radius.circular(20)),
), ),
margin: EdgeInsets.only( margin: EdgeInsets.only(

View File

@ -41,6 +41,7 @@ class PLVideoPlayer extends StatefulWidget {
this.customWidgets, this.customWidgets,
this.showEposideCb, this.showEposideCb,
this.fullScreenCb, this.fullScreenCb,
this.alignment = Alignment.center,
super.key, super.key,
}); });
@ -55,6 +56,7 @@ class PLVideoPlayer extends StatefulWidget {
final List<Widget>? customWidgets; final List<Widget>? customWidgets;
final Function? showEposideCb; final Function? showEposideCb;
final Function? fullScreenCb; final Function? fullScreenCb;
final Alignment? alignment;
@override @override
State<PLVideoPlayer> createState() => _PLVideoPlayerState(); State<PLVideoPlayer> createState() => _PLVideoPlayerState();
@ -393,6 +395,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
key: ValueKey(_.videoFit.value), key: ValueKey(_.videoFit.value),
controller: videoController, controller: videoController,
controls: NoVideoControls, controls: NoVideoControls,
alignment: widget.alignment!,
pauseUponEnteringBackgroundMode: !enableBackgroundPlay, pauseUponEnteringBackgroundMode: !enableBackgroundPlay,
resumeUponEnteringForegroundMode: true, resumeUponEnteringForegroundMode: true,
subtitleViewConfiguration: const SubtitleViewConfiguration( subtitleViewConfiguration: const SubtitleViewConfiguration(