feat: live area

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-05-05 21:53:20 +08:00
parent 2e4c24393d
commit a2f72ee3f3
17 changed files with 262 additions and 408 deletions

View File

@@ -42,10 +42,13 @@ class _SelfSizedHorizontalListState extends State<SelfSizedHorizontalList> {
}
if (widget.itemCount == 0) return const SizedBox.shrink();
if (isInit) {
return Container(
key: infoKey,
padding: widget.padding,
child: widget.childBuilder(0),
return Align(
alignment: Alignment.centerLeft,
child: Padding(
key: infoKey,
padding: widget.padding ?? EdgeInsets.zero,
child: widget.childBuilder(0),
),
);
}

View File

@@ -802,4 +802,7 @@ class Api {
static const String liveFollow =
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
static const String liveSecondList =
'${HttpString.liveBaseUrl}/xlive/app-interface/v2/second/getList';
}

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/models/live/live_room/danmu_info.dart';
import 'package:PiliPlus/models/live/live_room/item.dart';
import 'package:PiliPlus/models/live/live_room/room_info.dart';
import 'package:PiliPlus/models/live/live_room/room_info_h5.dart';
import 'package:PiliPlus/models/live/live_second_list/data.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:PiliPlus/utils/wbi_sign.dart';
@@ -172,6 +173,7 @@ class LiveHttp {
static Future<LoadingState<LiveIndexData>> liveFeedIndex({
required int pn,
required bool isLogin,
bool? moduleSelect,
}) async {
final params = {
if (isLogin) 'access_key': Accounts.main.accessKey,
@@ -185,17 +187,16 @@ class LiveHttp {
'fnval': '912',
'disable_rcmd': '0',
'https_url_req': '1',
'login_event': isLogin ? '1' : '0',
if (moduleSelect == true) 'module_select': '1',
'mobi_app': 'android_hd',
'module_select': '0',
'network': 'wifi',
'page': pn.toString(),
'page': pn,
'platform': 'android',
if (isLogin) 'relation_page': '1',
's_locale': 'zh_CN',
'scale': '2',
'statistics': Constants.statistics,
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
};
Utils.appSign(
params,
@@ -229,4 +230,53 @@ class LiveHttp {
return LoadingState.error(res.data['message']);
}
}
static Future<LoadingState<LiveSecondData>> liveSecondList({
required int pn,
required bool isLogin,
required int? areaId,
required int? parentAreaId,
}) async {
final params = {
if (isLogin) 'access_key': Accounts.main.accessKey,
'appkey': Constants.appKey,
'actionKey': 'appkey',
if (areaId != null) 'area_id': areaId,
if (parentAreaId != null) 'parent_area_id': parentAreaId,
'build': '8350200',
'c_locale': 'zh_CN',
'device': 'pad',
'device_name': 'vivo',
'device_type': '0',
'fnval': '912',
'disable_rcmd': '0',
'https_url_req': '1',
'mobi_app': 'android_hd',
'module_select': '0',
'network': 'wifi',
'page': pn,
'page_size': '20',
'platform': 'android',
'qn': '0',
'tag_version': '1',
's_locale': 'zh_CN',
'scale': '2',
'statistics': Constants.statistics,
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
};
Utils.appSign(
params,
Constants.appKey,
Constants.appSec,
);
var res = await Request().get(
Api.liveSecondList,
queryParameters: params,
);
if (res.data['code'] == 0) {
return LoadingState.success(LiveSecondData.fromJson(res.data['data']));
} else {
return LoadingState.error(res.data['message']);
}
}
}

View File

@@ -1,25 +0,0 @@
class Avatar {
String? cover;
String? event;
String? text;
int? uid;
String? url;
Avatar({this.cover, this.event, this.text, this.uid, this.url});
factory Avatar.fromJson(Map<String, dynamic> json) => Avatar(
cover: json['cover'] as String?,
event: json['event'] as String?,
text: json['text'] as String?,
uid: json['uid'] as int?,
url: json['url'] as String?,
);
Map<String, dynamic> toJson() => {
'cover': cover,
'event': event,
'text': text,
'uid': uid,
'url': url,
};
}

View File

@@ -1,20 +0,0 @@
class CalendarButton {
String? text;
String? link;
CalendarButton({this.text, this.link});
factory CalendarButton.fromJson(Map<String, dynamic> json) {
return CalendarButton(
text: json['text'] as String?,
link: json['link'] as String?,
);
}
Map<String, dynamic> toJson() => {
'text': text,
'link': link,
};
}

View File

@@ -5,11 +5,13 @@ import 'card_data_item.dart';
class CardData {
CardDataItem? bannerV2;
CardDataItem? myIdolV1;
CardDataItem? areaEntranceV3;
CardLiveItem? smallCardV1;
CardData({
this.bannerV2,
this.myIdolV1,
this.areaEntranceV3,
this.smallCardV1,
});
@@ -20,6 +22,10 @@ class CardData {
myIdolV1: json['my_idol_v1'] == null
? null
: CardDataItem.fromJson(json['my_idol_v1'] as Map<String, dynamic>),
areaEntranceV3: json['area_entrance_v3'] == null
? null
: CardDataItem.fromJson(
json['area_entrance_v3'] as Map<String, dynamic>),
smallCardV1: json['small_card_v1'] == null
? null
: CardLiveItem.fromJson(

View File

@@ -1,8 +1,6 @@
import 'quality_description.dart';
import 'watched_show.dart';
class CardLiveItem {
int? id;
int? roomid;
int? uid;
String? uname;
@@ -18,35 +16,14 @@ class CardLiveItem {
int? areaV2ParentId;
String? liveTagName;
int? online;
String? playUrl;
String? playUrlH265;
List? acceptQuality;
int? currentQuality;
int? pkId;
String? link;
int? specialAttention;
int? broadcastType;
String? pendentRu;
String? pendentRuColor;
String? pendentRuPic;
int? officialVerify;
int? currentQn;
List<QualityDescription>? qualityDescription;
String? playUrlCard;
int? flag;
List<dynamic>? pendentList;
int? p2pType;
String? sessionId;
int? groupId;
WatchedShow? watchedShow;
int? isNft;
String? nftDmark;
String? statusText;
String? darkFace;
String? trackid;
int? tagType;
CardLiveItem({
this.id,
this.roomid,
this.uid,
this.uname,
@@ -62,37 +39,16 @@ class CardLiveItem {
this.areaV2ParentId,
this.liveTagName,
this.online,
this.playUrl,
this.playUrlH265,
this.acceptQuality,
this.currentQuality,
this.pkId,
this.link,
this.specialAttention,
this.broadcastType,
this.pendentRu,
this.pendentRuColor,
this.pendentRuPic,
this.officialVerify,
this.currentQn,
this.qualityDescription,
this.playUrlCard,
this.flag,
this.pendentList,
this.p2pType,
this.sessionId,
this.groupId,
this.watchedShow,
this.isNft,
this.nftDmark,
this.statusText,
this.darkFace,
this.trackid,
this.tagType,
});
factory CardLiveItem.fromJson(Map<String, dynamic> json) => CardLiveItem(
id: json['id'] as int?,
roomid: json['roomid'] as int?,
roomid: json['roomid'] ?? json['id'],
uid: json['uid'] as int?,
uname: json['uname'] as String?,
face: json['face'] as String?,
@@ -107,37 +63,15 @@ class CardLiveItem {
areaV2ParentId: json['area_v2_parent_id'] as int?,
liveTagName: json['live_tag_name'] as String?,
online: json['online'] as int?,
playUrl: json['play_url'] as String?,
playUrlH265: json['play_url_h265'] as String?,
acceptQuality: json['accept_quality'],
currentQuality: json['current_quality'] as int?,
pkId: json['pk_id'] as int?,
link: json['link'] as String?,
specialAttention: json['special_attention'] as int?,
broadcastType: json['broadcast_type'] as int?,
pendentRu: json['pendent_ru'] as String?,
pendentRuColor: json['pendent_ru_color'] as String?,
pendentRuPic: json['pendent_ru_pic'] as String?,
officialVerify: json['official_verify'] as int?,
currentQn: json['current_qn'] as int?,
qualityDescription: (json['quality_description'] as List<dynamic>?)
?.map((e) => QualityDescription.fromJson(e as Map<String, dynamic>))
.toList(),
playUrlCard: json['play_url_card'] as String?,
flag: json['flag'] as int?,
pendentList: json['pendent_list'] as List<dynamic>?,
p2pType: json['p2p_type'] as int?,
sessionId: json['session_id'] as String?,
groupId: json['group_id'] as int?,
watchedShow: json['watched_show'] == null
? null
: WatchedShow.fromJson(
json['watched_show'] as Map<String, dynamic>),
isNft: json['is_nft'] as int?,
nftDmark: json['nft_dmark'] as String?,
statusText: json['status_text'] as String?,
darkFace: json['dark_face'] as String?,
trackid: json['trackid'] as String?,
tagType: json['tag_type'],
);
Map<String, dynamic> toJson() => {
@@ -156,32 +90,10 @@ class CardLiveItem {
'area_v2_parent_id': areaV2ParentId,
'live_tag_name': liveTagName,
'online': online,
'play_url': playUrl,
'play_url_h265': playUrlH265,
'accept_quality': acceptQuality,
'current_quality': currentQuality,
'pk_id': pkId,
'link': link,
'special_attention': specialAttention,
'broadcast_type': broadcastType,
'pendent_ru': pendentRu,
'pendent_ru_color': pendentRuColor,
'pendent_ru_pic': pendentRuPic,
'official_verify': officialVerify,
'current_qn': currentQn,
'quality_description':
qualityDescription?.map((e) => e.toJson()).toList(),
'play_url_card': playUrlCard,
'flag': flag,
'pendent_list': pendentList,
'p2p_type': p2pType,
'session_id': sessionId,
'group_id': groupId,
'watched_show': watchedShow?.toJson(),
'is_nft': isNft,
'nft_dmark': nftDmark,
'status_text': statusText,
'dark_face': darkFace,
'trackid': trackid,
};
}

View File

@@ -6,6 +6,8 @@ class LiveIndexData {
int? hasMore;
int? triggerTime;
int? isNeedRefresh;
LiveCardList? followItem;
LiveCardList? areaItem;
LiveIndexData({
this.cardList,
@@ -17,14 +19,22 @@ class LiveIndexData {
LiveIndexData.fromJson(Map<String, dynamic> json) {
if ((json['card_list'] as List<dynamic>?)?.isNotEmpty == true) {
cardList = <LiveCardList>[];
// banner_v2
// my_idol_v1
// area_entrance_v3
// small_card_v1
for (var json in json['card_list']) {
if (const ['my_idol_v1', 'small_card_v1'].contains(json['card_type'])) {
cardList!.add(LiveCardList.fromJson(json));
switch (json['card_type']) {
case 'my_idol_v1':
followItem = LiveCardList.fromJson(json);
break;
case 'area_entrance_v3':
areaItem = LiveCardList.fromJson(json);
break;
case 'small_card_v1':
cardList ??= <LiveCardList>[];
cardList!.add(LiveCardList.fromJson(json));
break;
}
}
}

View File

@@ -1,43 +0,0 @@
class InLive {
int? alphaLight;
int? alphaNight;
String? animationUrl;
String? animationUrlHash;
String? backgroundColorLight;
String? backgroundColorNight;
String? fontColor;
String? text;
InLive({
this.alphaLight,
this.alphaNight,
this.animationUrl,
this.animationUrlHash,
this.backgroundColorLight,
this.backgroundColorNight,
this.fontColor,
this.text,
});
factory InLive.fromJson(Map<String, dynamic> json) => InLive(
alphaLight: json['alpha_light'] as int?,
alphaNight: json['alpha_night'] as int?,
animationUrl: json['animation_url'] as String?,
animationUrlHash: json['animation_url_hash'] as String?,
backgroundColorLight: json['background_color_light'] as String?,
backgroundColorNight: json['background_color_night'] as String?,
fontColor: json['font_color'] as String?,
text: json['text'] as String?,
);
Map<String, dynamic> toJson() => {
'alpha_light': alphaLight,
'alpha_night': alphaNight,
'animation_url': animationUrl,
'animation_url_hash': animationUrlHash,
'background_color_light': backgroundColorLight,
'background_color_night': backgroundColorNight,
'font_color': fontColor,
'text': text,
};
}

View File

@@ -1,76 +0,0 @@
import 'avatar.dart';
import 'calendar_button.dart';
import 'player_args.dart';
import 'right_top_live_badge.dart';
class InlineLive {
Avatar? avatar;
String? cover;
int? inlineStartDelayTime;
int? inlineSustainDuration;
String? link;
PlayerArgs? playerArgs;
dynamic rankListInfo;
RightTopLiveBadge? rightTopLiveBadge;
String? title;
dynamic topViewInfo;
String? upName;
String? inlinePlayUrl;
CalendarButton? calendarButton;
InlineLive({
this.avatar,
this.cover,
this.inlineStartDelayTime,
this.inlineSustainDuration,
this.link,
this.playerArgs,
this.rankListInfo,
this.rightTopLiveBadge,
this.title,
this.topViewInfo,
this.upName,
this.inlinePlayUrl,
this.calendarButton,
});
factory InlineLive.fromJson(Map<String, dynamic> json) => InlineLive(
avatar: json['avatar'] == null
? null
: Avatar.fromJson(json['avatar'] as Map<String, dynamic>),
cover: json['cover'] as String?,
inlineStartDelayTime: json['inline_start_delay_time'] as int?,
inlineSustainDuration: json['inline_sustain_duration'] as int?,
link: json['link'] as String?,
playerArgs: json['player_args'] == null
? null
: PlayerArgs.fromJson(json['player_args'] as Map<String, dynamic>),
rankListInfo: json['rank_list_info'] as dynamic,
rightTopLiveBadge: json['right_top_live_badge'] == null
? null
: RightTopLiveBadge.fromJson(json['right_top_live_badge'] as Map<String, dynamic>),
title: json['title'] as String?,
topViewInfo: json['top_view_info'] as dynamic,
upName: json['up_name'] as String?,
inlinePlayUrl: json['inline_play_url'] as String?,
calendarButton: json['calendar_button'] == null
? null
: CalendarButton.fromJson(json['calendar_button'] as Map<String, dynamic>),
);
Map<String, dynamic> toJson() => {
'avatar': avatar?.toJson(),
'cover': cover,
'inline_start_delay_time': inlineStartDelayTime,
'inline_sustain_duration': inlineSustainDuration,
'link': link,
'player_args': playerArgs?.toJson(),
'rank_list_info': rankListInfo,
'right_top_live_badge': rightTopLiveBadge?.toJson(),
'title': title,
'top_view_info': topViewInfo,
'up_name': upName,
'inline_play_url': inlinePlayUrl,
'calendar_button': calendarButton?.toJson(),
};
}

View File

@@ -1,39 +0,0 @@
class PlayerArgs {
int? canPlay;
bool? hideDanmuSwitch;
int? liveStatus;
int? parentAreaId;
int? areaId;
int? roomId;
int? upId;
PlayerArgs({
this.canPlay,
this.hideDanmuSwitch,
this.liveStatus,
this.parentAreaId,
this.areaId,
this.roomId,
this.upId,
});
factory PlayerArgs.fromJson(Map<String, dynamic> json) => PlayerArgs(
canPlay: json['can_play'] as int?,
hideDanmuSwitch: json['hide_danmu_switch'] as bool?,
liveStatus: json['live_status'] as int?,
parentAreaId: json['parent_area_id'] as int?,
areaId: json['area_id'] as int?,
roomId: json['room_id'] as int?,
upId: json['up_id'] as int?,
);
Map<String, dynamic> toJson() => {
'can_play': canPlay,
'hide_danmu_switch': hideDanmuSwitch,
'live_status': liveStatus,
'parent_area_id': parentAreaId,
'area_id': areaId,
'room_id': roomId,
'up_id': upId,
};
}

View File

@@ -1,20 +0,0 @@
class QualityDescription {
int? qn;
String? desc;
QualityDescription({this.qn, this.desc});
factory QualityDescription.fromJson(Map<String, dynamic> json) {
return QualityDescription(
qn: json['qn'] as int?,
desc: json['desc'] as String?,
);
}
Map<String, dynamic> toJson() => {
'qn': qn,
'desc': desc,
};
}

View File

@@ -1,24 +0,0 @@
import 'in_live.dart';
class RightTopLiveBadge {
InLive? inLive;
int? liveStatus;
RightTopLiveBadge({this.inLive, this.liveStatus});
factory RightTopLiveBadge.fromJson(Map<String, dynamic> json) {
return RightTopLiveBadge(
inLive: json['in_live'] == null
? null
: InLive.fromJson(json['in_live'] as Map<String, dynamic>),
liveStatus: json['live_status'] as int?,
);
}
Map<String, dynamic> toJson() => {
'in_live': inLive?.toJson(),
'live_status': liveStatus,
};
}

View File

@@ -0,0 +1,23 @@
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
class LiveSecondData {
int? count;
List<CardLiveItem>? cardList;
LiveSecondData({
this.count,
this.cardList,
});
factory LiveSecondData.fromJson(Map<String, dynamic> json) => LiveSecondData(
count: json['count'] as int?,
cardList: (json['list'] as List<dynamic>?)
?.map((e) => CardLiveItem.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> toJson() => {
'count': count,
'list': cardList?.map((e) => e.toJson()).toList(),
};
}

View File

@@ -1,26 +1,90 @@
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/http/live.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
import 'package:PiliPlus/models/live/live_feed_index/card_list.dart';
import 'package:PiliPlus/models/live/live_feed_index/data.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
class LiveController extends CommonListController<LiveIndexData, LiveCardList> {
class LiveController extends CommonListController {
@override
void onInit() {
super.onInit();
queryData();
}
int? areaId;
int? parentAreaId;
final RxInt areaIndex = 0.obs;
final Rx<Pair<LiveCardList?, LiveCardList?>> topState =
Pair<LiveCardList?, LiveCardList?>(first: null, second: null).obs;
final RxBool isLogin = Accounts.main.isLogin.obs;
@override
List<LiveCardList>? getDataList(LiveIndexData response) {
List? getDataList(response) {
return response.cardList;
}
@override
Future<LoadingState<LiveIndexData>> customGetData() =>
LiveHttp.liveFeedIndex(pn: currentPage, isLogin: isLogin.value);
bool customHandleResponse(bool isRefresh, Success response) {
if (isRefresh && areaIndex.value == 0) {
topState.value = Pair(
first: response.response.followItem,
second: response.response.areaItem,
);
}
return false;
}
@override
Future<LoadingState> customGetData() {
if (areaIndex.value != 0) {
return LiveHttp.liveSecondList(
pn: currentPage,
isLogin: isLogin.value,
areaId: areaId,
parentAreaId: parentAreaId,
);
}
return LiveHttp.liveFeedIndex(pn: currentPage, isLogin: isLogin.value);
}
@override
Future<void> onRefresh() async {
currentPage = 1;
isEnd = false;
if (areaIndex.value != 0) {
queryTop();
}
await queryData();
}
Future<void> queryTop() async {
final res = await LiveHttp.liveFeedIndex(
pn: currentPage,
isLogin: isLogin.value,
moduleSelect: true,
);
if (res is Success) {
final data = res.data;
topState.value = Pair(
first: data.followItem,
second: data.areaItem,
);
}
}
void onSelectArea(int index, CardLiveItem? cardLiveItem) {
if (index == areaIndex.value) {
return;
}
areaIndex.value = index;
areaId = cardLiveItem?.areaV2Id;
parentAreaId = cardLiveItem?.areaV2ParentId;
currentPage = 1;
isEnd = false;
queryData();
}
}

View File

@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_card_v.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -11,7 +12,7 @@ import 'package:PiliPlus/pages/common/common_page.dart';
import 'package:PiliPlus/pages/live/controller.dart';
import 'package:PiliPlus/pages/live/widgets/live_item_app.dart';
import 'package:PiliPlus/pages/live_follow/view.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -54,7 +55,10 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
top: StyleString.cardSpace,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: Obx(() => _buildBody(controller.loadingState.value)),
sliver: SliverMainAxisGroup(slivers: [
Obx(() => _buildTop(controller.topState.value)),
Obx(() => _buildBody(controller.loadingState.value)),
]),
),
],
),
@@ -62,7 +66,51 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
);
}
Widget _buildBody(LoadingState<List<LiveCardList>?> loadingState) {
Widget _buildTop(Pair<LiveCardList?, LiveCardList?> data) {
return SliverMainAxisGroup(
slivers: [
if (data.first != null)
SliverToBoxAdapter(child: _buildFollowList(data.first!)),
if (data.second?.cardData?.areaEntranceV3?.list?.isNotEmpty == true)
SliverToBoxAdapter(
child: SelfSizedHorizontalList(
gapSize: 12,
padding: const EdgeInsets.only(bottom: 10),
childBuilder: (index) {
late final item =
data.second!.cardData!.areaEntranceV3!.list![index - 1];
return Obx(
() => SearchText(
fontSize: 14,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 3,
),
text: index == 0 ? '推荐' : '${item.title}',
bgColor: index == controller.areaIndex.value
? Theme.of(context).colorScheme.secondaryContainer
: Colors.transparent,
textColor: index == controller.areaIndex.value
? Theme.of(context).colorScheme.onSecondaryContainer
: null,
onTap: (value) {
controller.onSelectArea(
index,
index == 0 ? null : item,
);
},
),
);
},
itemCount:
data.second!.cardData!.areaEntranceV3!.list!.length + 1,
),
),
],
);
}
Widget _buildBody(LoadingState<List?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
@@ -80,47 +128,30 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
),
),
Success() => loadingState.response?.isNotEmpty == true
? Builder(builder: (context) {
List<LiveCardList> list = loadingState.response!;
LiveCardList? followItem;
if (list.first.cardType == 'my_idol_v1') {
followItem = list.first;
list = list.sublist(1);
}
return SliverMainAxisGroup(
slivers: [
if (followItem != null)
SliverPadding(
padding: const EdgeInsets.only(bottom: 12),
sliver: SliverToBoxAdapter(
child: _buildFollowList(followItem),
),
),
if (list.isNotEmpty)
SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: StyleString.cardSpace,
crossAxisSpacing: StyleString.cardSpace,
maxCrossAxisExtent: Grid.smallCardWidth,
childAspectRatio: StyleString.aspectRatio,
mainAxisExtent:
MediaQuery.textScalerOf(context).scale(90),
),
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == list.length - 1) {
controller.onLoadMore();
}
return LiveCardVApp(
item: list[index].cardData!.smallCardV1!,
);
},
childCount: list.length,
),
),
],
);
})
? SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: StyleString.cardSpace,
crossAxisSpacing: StyleString.cardSpace,
maxCrossAxisExtent: Grid.smallCardWidth,
childAspectRatio: StyleString.aspectRatio,
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
),
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == loadingState.response!.length - 1) {
controller.onLoadMore();
}
final item = loadingState.response![index];
if (item is LiveCardList) {
return LiveCardVApp(
item: item.cardData!.smallCardV1!,
);
}
return LiveCardVApp(item: item);
},
childCount: loadingState.response!.length,
),
)
: HttpError(onReload: controller.onReload),
Error() => HttpError(
errMsg: loadingState.errMsg,
@@ -183,22 +214,21 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
),
],
),
_buildFollowBody(theme, item.cardData?.myIdolV1?.list),
if (item.cardData?.myIdolV1?.list?.isNotEmpty == true)
_buildFollowBody(theme, item.cardData!.myIdolV1!.list!),
const SizedBox(height: 10),
],
);
}
Widget _buildFollowBody(ThemeData theme, List<CardLiveItem>? list) {
if (list.isNullOrEmpty) {
return const SizedBox.shrink();
}
Widget _buildFollowBody(ThemeData theme, List<CardLiveItem> followList) {
return MediaQuery.removePadding(
context: context,
removeLeft: context.orientation == Orientation.landscape,
child: SelfSizedHorizontalList(
gapSize: 5,
childBuilder: (index) {
final item = list[index];
final item = followList[index];
return SizedBox(
width: 65,
child: GestureDetector(
@@ -244,7 +274,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
),
);
},
itemCount: list!.length,
itemCount: followList.length,
),
);
}

View File

@@ -17,13 +17,13 @@ class LiveCardVApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(item.id);
String heroTag = Utils.makeHeroTag(item.roomid);
return Card(
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: InkWell(
onTap: () {
Get.toNamed('/liveRoom?roomid=${item.id}');
Get.toNamed('/liveRoom?roomid=${item.roomid}');
},
onLongPress: () => imageSaveDialog(
title: item.title,