import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_v.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.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'; 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/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_area/view.dart'; import 'package:PiliPlus/pages/live_follow/view.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'; class LivePage extends CommonPage { const LivePage({super.key}); @override State createState() => _LivePageState(); } class _LivePageState extends CommonPageState with AutomaticKeepAliveClientMixin { @override LiveController controller = Get.put(LiveController()); @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); final ThemeData theme = Theme.of(context); return Container( clipBehavior: Clip.hardEdge, margin: const EdgeInsets.only( left: StyleString.safeSpace, right: StyleString.safeSpace), decoration: const BoxDecoration( borderRadius: StyleString.mdRadius, ), child: refreshIndicator( onRefresh: controller.onRefresh, child: CustomScrollView( controller: controller.scrollController, physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( padding: EdgeInsets.only( top: StyleString.cardSpace, bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: SliverMainAxisGroup(slivers: [ Obx(() => _buildTop(theme, controller.topState.value)), Obx(() => _buildBody(theme, controller.loadingState.value)), ]), ), ], ), ), ); } Widget _buildTop(ThemeData theme, Pair data) { return SliverMainAxisGroup( slivers: [ if (data.first != null) SliverToBoxAdapter(child: _buildFollowList(theme, data.first!)), if (data.second?.cardData?.areaEntranceV3?.list?.isNotEmpty == true) SliverToBoxAdapter( child: Row( children: [ Expanded( child: SelfSizedHorizontalList( gapSize: 12, padding: const EdgeInsets.only(top: 10, bottom: 10, right: 12), 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.colorScheme.secondaryContainer : Colors.transparent, textColor: index == controller.areaIndex.value ? theme.colorScheme.onSecondaryContainer : null, onTap: (value) { controller.onSelectArea( index, index == 0 ? null : item, ); }, ), ); }, itemCount: data.second!.cardData!.areaEntranceV3!.list!.length + 1, ), ), iconButton( size: 26, iconSize: 16, context: context, tooltip: '全部标签', icon: Icons.widgets, onPressed: () { Get.to(const LiveAreaPage()); }, ), ], ), ) else const SliverToBoxAdapter(child: SizedBox(height: 10)), ], ); } Widget _buildBody(ThemeData theme, LoadingState loadingState) { return switch (loadingState) { Loading() => 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) { return const VideoCardVSkeleton(); }, childCount: 10, ), ), Success() => SliverMainAxisGroup( slivers: [ if (controller.newTags?.isNotEmpty == true) SliverToBoxAdapter( child: SelfSizedHorizontalList( gapSize: 12, padding: const EdgeInsets.only(bottom: 8), childBuilder: (index) { late final item = controller.newTags![index]; return Obx( () => SearchText( fontSize: 13, padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), text: '${item.name}', bgColor: index == controller.tagIndex.value ? theme.colorScheme.secondaryContainer : Colors.transparent, textColor: index == controller.tagIndex.value ? theme.colorScheme.onSecondaryContainer : null, onTap: (value) { controller.onSelectTag( index, item.sortType, ); }, ), ); }, itemCount: controller.newTags!.length, ), ), loadingState.response?.isNotEmpty == true ? 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, onReload: controller.onReload, ), }; } Widget _buildFollowList(ThemeData theme, LiveCardList item) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text.rich( TextSpan( children: [ const TextSpan(text: '我的关注 '), TextSpan( text: '${item.cardData?.myIdolV1?.extraInfo?.totalCount ?? 0}', style: TextStyle( fontSize: 13, color: theme.colorScheme.primary, ), ), TextSpan( text: '人正在直播', style: TextStyle( fontSize: 13, color: theme.colorScheme.outline, ), ), ], ), ), const Spacer(), GestureDetector( onTap: () { Get.to(const LiveFollowPage()); }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '查看更多', style: TextStyle( color: theme.colorScheme.outline, ), ), Icon( size: 20, Icons.keyboard_arrow_right_outlined, color: theme.colorScheme.outline, ), ], ), ), ], ), if (item.cardData?.myIdolV1?.list?.isNotEmpty == true) _buildFollowBody(theme, item.cardData!.myIdolV1!.list!), ], ); } Widget _buildFollowBody(ThemeData theme, List followList) { return MediaQuery.removePadding( context: context, removeLeft: context.orientation == Orientation.landscape, child: SelfSizedHorizontalList( gapSize: 5, childBuilder: (index) { final item = followList[index]; return SizedBox( width: 65, child: GestureDetector( onTap: () { Get.toNamed('/liveRoom?roomid=${item.roomid}'); }, onLongPress: () { Feedback.forLongPress(context); Get.toNamed('/member?mid=${item.uid}'); }, child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 8), Container( margin: const EdgeInsets.all(2), padding: const EdgeInsets.all(2), decoration: BoxDecoration( border: Border.all( width: 1.5, color: theme.colorScheme.primary, strokeAlign: BorderSide.strokeAlignOutside, ), shape: BoxShape.circle, ), child: NetworkImgLayer( type: 'avatar', width: 45, height: 45, src: item.face, ), ), const SizedBox(height: 4), Text( item.uname!, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12), textAlign: TextAlign.center, ), ], ), ), ); }, itemCount: followList.length, ), ); } }