Files
PiliPlus/lib/pages/hot/view.dart
2026-04-14 13:46:47 +08:00

154 lines
5.3 KiB
Dart

import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/flutter/scroll_view/scroll_view.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/video_card/video_card_h.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
import 'package:PiliPlus/models/model_hot_video_item.dart';
import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/pages/hot/controller.dart';
import 'package:PiliPlus/pages/rank/view.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class HotPage extends StatefulWidget {
const HotPage({super.key});
@override
State<HotPage> createState() => _HotPageState();
}
class _HotPageState extends State<HotPage>
with AutomaticKeepAliveClientMixin, GridMixin {
final HotController controller = Get.put(HotController());
@override
bool get wantKeepAlive => true;
Widget _buildEntranceItem({
required String iconUrl,
required String title,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Column(
spacing: 4,
mainAxisSize: MainAxisSize.min,
children: [
NetworkImgLayer(
width: 35,
height: 35,
type: .emote,
src: iconUrl,
),
Text(
title,
style: const TextStyle(fontSize: 12),
),
],
),
);
}
@override
Widget build(BuildContext context) {
super.build(context);
return refreshIndicator(
onRefresh: controller.onRefresh,
child: customScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
slivers: [
if (Pref.showHotRcmd)
SliverToBoxAdapter(
child: Padding(
padding: const .only(left: 12, top: 12, right: 12),
child: Row(
mainAxisAlignment: .spaceEvenly,
children: [
_buildEntranceItem(
iconUrl:
'https://i0.hdslb.com/bfs/archive/a3f11218aaf4521b4967db2ae164ecd3052586b9.png',
title: '排行榜',
onTap: () {
try {
final homeController = Get.find<HomeController>();
final index = homeController.tabs.indexOf(
HomeTabType.rank,
);
if (index != -1) {
homeController.tabController.animateTo(index);
} else {
Get.to(
Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('排行榜')),
body: const ViewSafeArea(child: RankPage()),
),
);
}
} catch (_) {}
},
),
_buildEntranceItem(
iconUrl:
'https://i0.hdslb.com/bfs/archive/552ebe8c4794aeef30ebd1568b59ad35f15e21ad.png',
title: '每周必看',
onTap: () => Get.toNamed('/popularSeries'),
),
_buildEntranceItem(
iconUrl:
'https://i0.hdslb.com/bfs/archive/3693ec9335b78ca57353ac0734f36a46f3d179a9.png',
title: '入站必刷',
onTap: () => Get.toNamed('/popularPrecious'),
),
],
),
),
),
SliverPadding(
padding: const EdgeInsets.only(top: 7, bottom: 100),
sliver: Obx(
() => _buildBody(controller.loadingState.value),
),
),
],
),
);
}
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
return switch (loadingState) {
Loading() => gridSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverGrid.builder(
gridDelegate: gridDelegate,
itemBuilder: (context, index) {
if (index == response.length - 1) {
controller.onLoadMore();
}
return VideoCardH(
videoItem: response[index],
onRemove: () => controller.loadingState
..value.data!.removeAt(index)
..refresh(),
);
},
itemCount: response.length,
)
: HttpError(onReload: controller.onReload),
Error(:final errMsg) => HttpError(
errMsg: errMsg,
onReload: controller.onReload,
),
};
}
}