diff --git a/README.md b/README.md index 6f456ff05..f2283dcf1 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ ## feat +- [x] 直播弹幕 - [x] 修改头像/用户名/签名/性别/生日 - [x] 创建/编辑/删除收藏夹 - [x] 评论楼中楼查看对话 diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 2f0f39d69..1ae6ac80a 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,9 +1,18 @@ +import 'dart:convert'; + +import 'package:PiliPalaX/models/live/danmu_info.dart'; +import 'package:PiliPalaX/tcp/live.dart'; +import 'package:PiliPalaX/utils/danmaku.dart'; +import 'package:PiliPalaX/utils/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/http/constants.dart'; import 'package:PiliPalaX/http/live.dart'; import 'package:PiliPalaX/models/live/room_info.dart'; import 'package:PiliPalaX/plugin/pl_player/index.dart'; import 'package:ns_danmaku/danmaku_controller.dart'; +import 'package:ns_danmaku/models/danmaku_item.dart'; import '../../models/live/room_info_h5.dart'; import '../../utils/video_utils.dart'; @@ -94,4 +103,65 @@ class LiveRoomController extends GetxController { } return res; } + + LiveMessageStream? msgStream; + final ScrollController scrollController = ScrollController(); + + void scrollToBottom() { + if (disableAutoScroll.value) return; + if (scrollController.hasClients) { + scrollController.animateTo( + scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.linearToEaseOut, + ); + } + } + + void liveMsg() { + LiveHttp.liveRoomGetDanmakuToken(roomId: roomId).then((v) { + if (v['status']) { + LiveDanmakuInfo info = v['data']; + // logger.d("info => $info"); + msgStream = LiveMessageStream( + streamToken: info.data.token, + roomId: roomId, + uid: GStorage.userInfo.get('userInfoCache')?.mid ?? 0, + host: info.data.hostList[0].host, + port: info.data.hostList[0].port, + ); + msgStream?.addEventListener((obj) { + if (obj['cmd'] == 'DANMU_MSG') { + // logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}'); + messages.add(obj); + Map json = jsonDecode(obj['info'][0][15]['extra']); + controller?.addItems([ + DanmakuItem( + json['content'], + color: DmUtils.decimalToColor(json['color']), + // time: e.progress, + type: DmUtils.getPosition(json['mode']), + ) + ]); + WidgetsBinding.instance.addPostFrameCallback( + (_) => scrollToBottom(), + ); + } + }); + msgStream?.init(); + scrollController.addListener(() { + if (scrollController.position.userScrollDirection == + ScrollDirection.forward) { + disableAutoScroll.value = true; + } else if (scrollController.position.userScrollDirection == + ScrollDirection.reverse) { + final pos = scrollController.position; + if (pos.maxScrollExtent - pos.pixels <= 100) { + disableAutoScroll.value = false; + } + } + }); + } + }); + } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index c2bbacd98..c49a06009 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -65,6 +65,7 @@ class _LiveRoomPageState extends State { videoSourceInit(); _futureBuilderFuture = _liveRoomController.queryLiveInfo(); plPlayerController.autoEnterFullscreen(); + _liveRoomController.liveMsg(); } Future videoSourceInit() async { @@ -87,6 +88,9 @@ class _LiveRoomPageState extends State { _node.dispose(); plPlayerController.dispose(); _ctr.dispose(); + _liveRoomController.msgStream?.close(); + _liveRoomController.scrollController.removeListener(() {}); + _liveRoomController.scrollController.dispose(); super.dispose(); } diff --git a/lib/pages/live_room/widgets/chat.dart b/lib/pages/live_room/widgets/chat.dart index 4124c64b7..5cdda13d0 100644 --- a/lib/pages/live_room/widgets/chat.dart +++ b/lib/pages/live_room/widgets/chat.dart @@ -1,20 +1,12 @@ import 'dart:convert'; import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; -import 'package:PiliPalaX/http/live.dart'; -import 'package:PiliPalaX/models/live/danmu_info.dart'; import 'package:PiliPalaX/pages/live_room/controller.dart'; import 'package:PiliPalaX/services/loggeer.dart'; -import 'package:PiliPalaX/tcp/live.dart'; -import 'package:PiliPalaX/utils/danmaku.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; -import 'package:ns_danmaku/models/danmaku_item.dart'; - -import '../../../utils/storage.dart'; class LiveRoomChat extends StatefulWidget { final int roomId; @@ -29,31 +21,16 @@ class LiveRoomChat extends StatefulWidget { } class _LiveRoomChatState extends State { - LiveMessageStream? msgStream; - - final ScrollController _scrollController = ScrollController(); - bool get disableAutoScroll => widget.liveRoomController.disableAutoScroll.value; - void _scrollToBottom() { - if (disableAutoScroll) return; - if (_scrollController.hasClients) { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 200), - curve: Curves.linearToEaseOut, - ); - } - } - @override Widget build(BuildContext context) { return Stack( children: [ Obx( () => ListView.separated( - controller: _scrollController, + controller: widget.liveRoomController.scrollController, separatorBuilder: (_, index) => const SizedBox(height: 6), itemCount: widget.liveRoomController.messages.length, itemBuilder: (context, index) { @@ -114,7 +91,7 @@ class _LiveRoomChatState extends State { label: const Text('回到底部'), onPressed: () { widget.liveRoomController.disableAutoScroll.value = false; - _scrollToBottom(); + widget.liveRoomController.scrollToBottom(); }, ), ) @@ -124,63 +101,6 @@ class _LiveRoomChatState extends State { ); } - @override - void initState() { - super.initState(); - LiveHttp.liveRoomGetDanmakuToken(roomId: widget.roomId).then((v) { - if (v['status']) { - LiveDanmakuInfo info = v['data']; - // logger.d("info => $info"); - msgStream = LiveMessageStream( - streamToken: info.data.token, - roomId: widget.roomId, - uid: GStorage.userInfo.get('userInfoCache')?.mid ?? 0, - host: info.data.hostList[0].host, - port: info.data.hostList[0].port, - ); - msgStream?.addEventListener((obj) { - if (obj['cmd'] == 'DANMU_MSG') { - // logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}'); - widget.liveRoomController.messages.add(obj); - Map json = jsonDecode(obj['info'][0][15]['extra']); - widget.liveRoomController.controller?.addItems([ - DanmakuItem( - json['content'], - color: DmUtils.decimalToColor(json['color']), - // time: e.progress, - type: DmUtils.getPosition(json['mode']), - ) - ]); - WidgetsBinding.instance.addPostFrameCallback( - (_) => _scrollToBottom(), - ); - } - }); - msgStream?.init(); - _scrollController.addListener(() { - if (_scrollController.position.userScrollDirection == - ScrollDirection.forward) { - widget.liveRoomController.disableAutoScroll.value = true; - } else if (_scrollController.position.userScrollDirection == - ScrollDirection.reverse) { - final pos = _scrollController.position; - if (pos.maxScrollExtent - pos.pixels <= 100) { - widget.liveRoomController.disableAutoScroll.value = false; - } - } - }); - } - }); - } - - @override - void dispose() { - msgStream?.close(); - _scrollController.removeListener(() {}); - _scrollController.dispose(); - super.dispose(); - } - TextSpan _buildMsg(obj) { dynamic emots = jsonDecode(obj['info'][0][15]['extra'])['emots']; dynamic uemote = obj['info'][0][13];