opt: 竖屏直播布局
This commit is contained in:
@ -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']);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
Reference in New Issue
Block a user