From f7ff6d7aa80c087f2de7ac4ee5c75281f4a108e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Aug 2024 20:24:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/live.dart | 33 ++++++ lib/pages/live_room/controller.dart | 21 +++- lib/pages/live_room/view.dart | 163 +++++++++++++++------------- 3 files changed, 142 insertions(+), 75 deletions(-) diff --git a/lib/http/live.dart b/lib/http/live.dart index a405fd58..f6fc4ea4 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -84,4 +84,37 @@ class LiveHttp { }; } } + + // 发送弹幕 + static Future sendDanmaku({roomId, msg}) async { + var res = await Request().post(Api.sendLiveMsg, queryParameters: { + 'bubble': 0, + 'msg': msg, + 'color': 16777215, // 颜色 + 'mode': 1, // 模式 + 'room_type': 0, + 'jumpfrom': 71001, // 直播间来源 + 'reply_mid': 0, + 'reply_attr': 0, + 'replay_dmid': '', + 'statistics': {"appId": 100, "platform": 5}, + 'fontsize': 25, // 字体大小 + 'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳 + 'roomid': roomId, + 'csrf': await Request.getCsrf(), + 'csrf_token': await Request.getCsrf(), + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 6a3525e3..e8db54fb 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'dart:ui'; +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; @@ -40,6 +40,8 @@ class LiveRoomController extends GetxController { // 弹幕消息列表 RxList messageList = [].obs; DanmakuController? danmakuController; + // 输入控制器 + TextEditingController inputController = TextEditingController(); @override void onInit() { @@ -222,6 +224,23 @@ class LiveRoomController extends GetxController { plSocket?.sendMessage(joinData); } + // 发送弹幕 + void sendMsg() async { + final msg = inputController.text; + if (msg.isEmpty) { + return; + } + final res = await LiveHttp.sendDanmaku( + roomId: roomId, + msg: msg, + ); + if (res['status']) { + inputController.clear(); + } else { + SmartDialog.showToast(res['msg']); + } + } + @override void onClose() { plSocket?.onClose(); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d1fc6e07..92ea8fd8 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -97,7 +97,7 @@ class _LiveRoomPageState extends State @override void dispose() { - plPlayerController!.dispose(); + plPlayerController.dispose(); if (floating != null) { floating!.dispose(); } @@ -238,10 +238,10 @@ class _LiveRoomPageState extends State ), ), PopScope( - canPop: plPlayerController?.isFullScreen.value != true, + canPop: plPlayerController.isFullScreen.value != true, onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); + if (plPlayerController.isFullScreen.value == true) { + plPlayerController.triggerFullScreen(status: false); } if (MediaQuery.of(context).orientation == Orientation.landscape) { @@ -257,17 +257,53 @@ class _LiveRoomPageState extends State child: videoPlayerPanel, ), ), - const SizedBox(height: 20), // 显示消息的列表 buildMessageListUI( context, _liveRoomController, _scrollController, ), - // 底部安全距离 - SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, - ) + // 弹幕输入框 + Container( + padding: EdgeInsets.only( + left: 14, + right: 14, + top: 4, + bottom: MediaQuery.of(context).padding.bottom + 20), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + border: Border( + top: BorderSide( + color: Colors.white.withOpacity(0.1), + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _liveRoomController.inputController, + style: + const TextStyle(color: Colors.white, fontSize: 13), + decoration: InputDecoration( + hintText: '发送弹幕', + hintStyle: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + border: InputBorder.none, + ), + ), + ), + IconButton( + onPressed: () => _liveRoomController.sendMsg(), + icon: const Icon( + Icons.send, + color: Colors.white, + ), + ), + ], + ), + ), ], ), // 定位 快速滑动到底部 @@ -324,15 +360,15 @@ Widget buildMessageListUI( removeBottom: true, child: ShaderMask( shaderCallback: (Rect bounds) { - return const LinearGradient( + return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, - Colors.black, + Colors.black.withOpacity(0.5), Colors.black, ], - stops: [0.0, 0.1, 1.0], + stops: const [0.01, 0.05, 0.2], ).createShader(bounds); }, blendMode: BlendMode.dstIn, @@ -342,35 +378,46 @@ Widget buildMessageListUI( itemBuilder: (context, index) { final LiveMessageModel liveMsgItem = liveRoomController.messageList[index]; - return Padding( - padding: EdgeInsets.only( - top: index == 0 ? 40.0 : 4.0, - bottom: 4.0, - left: 20.0, - right: 20.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, - ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), ), ), ); @@ -392,23 +439,7 @@ List buildMessageTextSpan( if (liveMsgItem.emots == null) { // 没有表情包的消息 inlineSpanList.add( - TextSpan( - text: liveMsgItem.message ?? '', - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: liveMsgItem.message ?? ''), ); } else { // 有表情包的消息 使用正则匹配 表情包用图片渲染 @@ -435,23 +466,7 @@ List buildMessageTextSpan( }, onNonMatch: (String nonMatch) { inlineSpanList.add( - TextSpan( - text: nonMatch, - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: nonMatch), ); return nonMatch; },