diff --git a/lib/http/api.dart b/lib/http/api.dart index 6cb4659a..70488fa3 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -571,4 +571,8 @@ class Api { /// 直播间发送弹幕 static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send'; + + /// 我的关注 - 正在直播 + static const String getFollowingLive = + '${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following'; } diff --git a/lib/http/live.dart b/lib/http/live.dart index f6fc4ea4..f5fd2a43 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -1,3 +1,5 @@ +import 'package:pilipala/models/live/follow.dart'; + import '../models/live/item.dart'; import '../models/live/room_info.dart'; import '../models/live/room_info_h5.dart'; @@ -117,4 +119,27 @@ class LiveHttp { }; } } + + // 我的关注 正在直播 + static Future liveFollowing({int? pn, int? ps}) async { + var res = await Request().get(Api.getFollowingLive, data: { + 'page': pn, + 'page_size': ps, + 'platform': 'web', + 'ignoreRecord': 1, + 'hit_ab': true, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': LiveFollowingModel.fromJson(res.data['data']) + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/live/follow.dart b/lib/models/live/follow.dart new file mode 100644 index 00000000..4a941b8b --- /dev/null +++ b/lib/models/live/follow.dart @@ -0,0 +1,126 @@ +class LiveFollowingModel { + int? count; + List? list; + int? liveCount; + int? neverLivedCount; + List? neverLivedFaces; + int? pageSize; + String? title; + int? totalPage; + + LiveFollowingModel({ + this.count, + this.list, + this.liveCount, + this.neverLivedCount, + this.neverLivedFaces, + this.pageSize, + this.title, + this.totalPage, + }); + + LiveFollowingModel.fromJson(Map json) { + count = json['count']; + if (json['list'] != null) { + list = []; + json['list'].forEach((v) { + list!.add(LiveFollowingItemModel.fromJson(v)); + }); + } + liveCount = json['live_count']; + neverLivedCount = json['never_lived_count']; + if (json['never_lived_faces'] != null) { + neverLivedFaces = []; + json['never_lived_faces'].forEach((v) { + neverLivedFaces!.add(v); + }); + } + pageSize = json['pageSize']; + title = json['title']; + totalPage = json['totalPage']; + } +} + +class LiveFollowingItemModel { + int? roomId; + int? uid; + String? uname; + String? title; + String? face; + int? liveStatus; + int? recordNum; + String? recentRecordId; + int? isAttention; + int? clipNum; + int? fansNum; + String? areaName; + String? areaValue; + String? tags; + String? recentRecordIdV2; + int? recordNumV2; + int? recordLiveTime; + String? areaNameV2; + String? roomNews; + String? watchIcon; + String? textSmall; + String? roomCover; + String? pic; + int? parentAreaId; + int? areaId; + + LiveFollowingItemModel({ + this.roomId, + this.uid, + this.uname, + this.title, + this.face, + this.liveStatus, + this.recordNum, + this.recentRecordId, + this.isAttention, + this.clipNum, + this.fansNum, + this.areaName, + this.areaValue, + this.tags, + this.recentRecordIdV2, + this.recordNumV2, + this.recordLiveTime, + this.areaNameV2, + this.roomNews, + this.watchIcon, + this.textSmall, + this.roomCover, + this.pic, + this.parentAreaId, + this.areaId, + }); + + LiveFollowingItemModel.fromJson(Map json) { + roomId = json['roomid']; + uid = json['uid']; + uname = json['uname']; + title = json['title']; + face = json['face']; + liveStatus = json['live_status']; + recordNum = json['record_num']; + recentRecordId = json['recent_record_id']; + isAttention = json['is_attention']; + clipNum = json['clipnum']; + fansNum = json['fans_num']; + areaName = json['area_name']; + areaValue = json['area_value']; + tags = json['tags']; + recentRecordIdV2 = json['recent_record_id_v2']; + recordNumV2 = json['record_num_v2']; + recordLiveTime = json['record_live_time']; + areaNameV2 = json['area_name_v2']; + roomNews = json['room_news']; + watchIcon = json['watch_icon']; + textSmall = json['text_small']; + roomCover = json['room_cover']; + pic = json['room_cover']; + parentAreaId = json['parent_area_id']; + areaId = json['area_id']; + } +} diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart index 74fb6e9a..e02623a5 100644 --- a/lib/pages/live/controller.dart +++ b/lib/pages/live/controller.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/live.dart'; +import 'package:pilipala/models/live/follow.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/utils/storage.dart'; @@ -11,6 +12,8 @@ class LiveController extends GetxController { int _currentPage = 1; RxInt crossAxisCount = 2.obs; RxList liveList = [].obs; + RxList liveFollowingList = + [].obs; bool flag = false; OverlayEntry? popupDialog; Box setting = GStrorage.setting; @@ -44,6 +47,7 @@ class LiveController extends GetxController { // 下拉刷新 Future onRefresh() async { queryLiveList('init'); + fetchLiveFollowing(); } // 上拉加载 @@ -61,4 +65,17 @@ class LiveController extends GetxController { duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); } } + + // + Future fetchLiveFollowing() async { + var res = await LiveHttp.liveFollowing(pn: 1, ps: 20); + if (res['status']) { + liveFollowingList.value = (res['data'].list + as List) + .where( + (LiveFollowingItemModel item) => item.liveStatus == 1) // 根据条件过滤 + .toList(); + } + return res; + } } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 83605495..a7e4583f 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -6,6 +6,8 @@ import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/live/follow.dart'; import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; @@ -22,6 +24,7 @@ class _LivePageState extends State with AutomaticKeepAliveClientMixin { final LiveController _liveController = Get.put(LiveController()); late Future _futureBuilderFuture; + late Future _futureBuilderFuture2; late ScrollController scrollController; @override @@ -31,6 +34,7 @@ class _LivePageState extends State void initState() { super.initState(); _futureBuilderFuture = _liveController.queryLiveList('init'); + _futureBuilderFuture2 = _liveController.fetchLiveFollowing(); scrollController = _liveController.scrollController; scrollController.addListener( () { @@ -69,6 +73,7 @@ class _LivePageState extends State child: CustomScrollView( controller: _liveController.scrollController, slivers: [ + buildFollowingList(), SliverPadding( // 单列布局 EdgeInsets.zero padding: @@ -147,4 +152,144 @@ class _LivePageState extends State ), ); } + + // 关注的up直播 + Widget buildFollowingList() { + return SliverPadding( + padding: const EdgeInsets.only(top: 16), + sliver: SliverToBoxAdapter( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Obx( + () => Text.rich( + TextSpan( + children: [ + const TextSpan( + text: ' 我的关注 ', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + TextSpan( + text: ' ${_liveController.liveFollowingList.length}', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.primary, + ), + ), + TextSpan( + text: '人正在直播', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), + ), + ], + ), + ), + ), + FutureBuilder( + future: _futureBuilderFuture2, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map? data = snapshot.data; + if (data?['status']) { + RxList list = _liveController.liveFollowingList; + return Obx(() => LiveFollowingListView(list: list.value)); + } else { + return HttpError( + errMsg: data?['msg'] ?? '', + fn: () { + setState(() { + _futureBuilderFuture2 = + _liveController.fetchLiveFollowing(); + }); + }, + ); + } + } else { + return const SizedBox(); + } + }, + ), + ], + ), + ), + ); + } +} + +class LiveFollowingListView extends StatelessWidget { + final List list; + + const LiveFollowingListView({super.key, required this.list}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 100, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + final LiveFollowingItemModel item = list[index]; + return Padding( + padding: const EdgeInsets.fromLTRB(3, 12, 3, 0), + child: Column( + children: [ + InkWell( + onTap: () { + Get.toNamed( + '/liveRoom?roomid=${item.roomId}', + arguments: { + 'liveItem': item, + 'heroTag': item.roomId.toString() + }, + ); + }, + child: Container( + width: 54, + height: 54, + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(27), + border: Border.all( + color: Theme.of(context).colorScheme.primary, + width: 1.5, + ), + ), + child: NetworkImgLayer( + width: 50, + height: 50, + type: 'avatar', + src: list[index].face, + ), + ), + ), + const SizedBox(height: 6), + SizedBox( + width: 62, + child: Text( + list[index].uname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 12, + ), + ), + ), + ], + ), + ); + }, + itemCount: list.length, + ), + ); + } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 5d4e2b67..ab7f30cc 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -58,11 +58,12 @@ class LiveRoomController extends GetxController { if (Get.arguments != null) { liveItem = Get.arguments['liveItem']; heroTag = Get.arguments['heroTag'] ?? ''; - if (liveItem != null && liveItem.pic != null && liveItem.pic != '') { - cover = liveItem.pic; - } - if (liveItem != null && liveItem.cover != null && liveItem.cover != '') { - cover = liveItem.cover; + if (liveItem != null) { + cover = (liveItem.pic != null && liveItem.pic != '') + ? liveItem.pic + : (liveItem.cover != null && liveItem.cover != '') + ? liveItem.cover + : null; } Request.getBuvid().then((value) => buvid = value); }