user_favorite_page.dart 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import 'package:easy_refresh/easy_refresh.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_riverpod/flutter_riverpod.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:go_router/go_router.dart';
  6. import 'package:news_app/constant/size_res.dart';
  7. import 'package:news_app/extension/base.dart';
  8. import 'package:news_app/ui/activity/favorite_video_item_widget.dart';
  9. import 'package:news_app/widget/my_txt.dart';
  10. import '../../constant/color_res.dart';
  11. import '../../model/activity_model.dart';
  12. import '../../provider/user_favorite_provider.dart';
  13. import '../../widget/empty_1_widget.dart';
  14. import '../activity/activity_card_widget.dart';
  15. import '../news/news_child_main_page.dart';
  16. /// @author: bo.zeng
  17. /// @email: cnhbwds@gmail.com
  18. /// @date: 2025 2025/4/9 16:00
  19. /// @description:
  20. class UserFavoritePage extends ConsumerWidget {
  21. const UserFavoritePage({super.key});
  22. @override
  23. Widget build(BuildContext context, WidgetRef ref) {
  24. return DefaultTabController(
  25. length: 3,
  26. child: Scaffold(
  27. appBar: AppBar(
  28. // 移除阴影
  29. scrolledUnderElevation: 0,
  30. // 禁用滚动时的阴影变化
  31. backgroundColor: Colors.white,
  32. title: myTxt(
  33. text: "我的收藏",
  34. fontSize: 18.sp,
  35. fontWeight: FontWeight.bold,
  36. ),
  37. centerTitle: true,
  38. bottom: TabBar(
  39. tabAlignment: TabAlignment.center,
  40. isScrollable: false,
  41. indicatorColor: color2877FF,
  42. dividerHeight: 0,
  43. labelColor: color2877FF,
  44. // 选中标签颜色
  45. unselectedLabelColor: color7788A0,
  46. labelStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
  47. unselectedLabelStyle: TextStyle(
  48. fontSize: 16.sp,
  49. fontWeight: FontWeight.bold,
  50. ),
  51. tabs: [Tab(text: "文章"), Tab(text: "活动"), Tab(text: "视频")],
  52. ),
  53. ),
  54. body: TabBarView(
  55. children: [
  56. _UserNewsListPage(),
  57. _UserActivityListPage(),
  58. _UserVideoListPage(),
  59. ],
  60. ),
  61. ),
  62. );
  63. }
  64. }
  65. final favoriteNewsProvider = NotifierProvider<FavoriteNewsProvider, UserNews>(
  66. () {
  67. return FavoriteNewsProvider();
  68. },
  69. );
  70. class _UserNewsListPage extends ConsumerStatefulWidget {
  71. const _UserNewsListPage();
  72. @override
  73. ConsumerState<_UserNewsListPage> createState() => _UserNewsListPageState();
  74. }
  75. class _UserNewsListPageState extends ConsumerState<_UserNewsListPage>
  76. with AutomaticKeepAliveClientMixin {
  77. int pageNum = 0;
  78. int totalNews = 0;
  79. @override
  80. void initState() {
  81. super.initState();
  82. ref.read(favoriteNewsProvider.notifier).fetchUserFavorite(pageNum: pageNum);
  83. }
  84. @override
  85. Widget build(BuildContext context) {
  86. super.build(context);
  87. final news = ref.watch(favoriteNewsProvider);
  88. ref.listen(favoriteNewsProvider, (pre, next) {
  89. totalNews = next.rows?.length ?? 0;
  90. });
  91. return news.rows?.isEmpty == true
  92. ? Empty1widget()
  93. : EasyRefresh.builder(
  94. onRefresh: () async {
  95. pageNum = 0;
  96. await ref
  97. .read(favoriteNewsProvider.notifier)
  98. .fetchUserFavorite(pageNum: pageNum);
  99. return IndicatorResult.success;
  100. },
  101. onLoad: () async {
  102. if (totalNews >= news.total.safeValue) {
  103. return IndicatorResult.noMore;
  104. } else {
  105. pageNum++;
  106. await ref
  107. .read(favoriteNewsProvider.notifier)
  108. .fetchUserFavorite(pageNum: pageNum);
  109. return IndicatorResult.success;
  110. }
  111. },
  112. childBuilder: (context, py) {
  113. return ListView.separated(
  114. physics: py,
  115. padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
  116. itemBuilder: (BuildContext context, int index) {
  117. return buildNewsNewItem(news.rows![index]!, context);
  118. },
  119. separatorBuilder: (BuildContext context, int index) {
  120. return SizedBox.shrink();
  121. },
  122. itemCount: news.rows?.length ?? 0,
  123. );
  124. },
  125. );
  126. }
  127. @override
  128. bool get wantKeepAlive => true;
  129. }
  130. final favoriteActivityProvider =
  131. NotifierProvider<FavoriteActivityProvider, UserActivity>(() {
  132. return FavoriteActivityProvider();
  133. });
  134. class _UserActivityListPage extends ConsumerStatefulWidget {
  135. @override
  136. ConsumerState<ConsumerStatefulWidget> createState() {
  137. return _UserActivityListPageState();
  138. }
  139. }
  140. class _UserActivityListPageState extends ConsumerState<_UserActivityListPage>
  141. with AutomaticKeepAliveClientMixin {
  142. int pageNum = 0;
  143. int totalActivity = 0;
  144. @override
  145. void initState() {
  146. super.initState();
  147. ref
  148. .read(favoriteActivityProvider.notifier)
  149. .fetchUserActivity(pageNum: pageNum);
  150. }
  151. @override
  152. Widget build(BuildContext context) {
  153. super.build(context);
  154. final activities = ref.watch(favoriteActivityProvider);
  155. ref.listen(favoriteActivityProvider, (pre, next) {
  156. totalActivity = next.rows?.length ?? 0;
  157. });
  158. return activities.rows?.isEmpty == true
  159. ? Empty1widget()
  160. : EasyRefresh.builder(
  161. onRefresh: () async {
  162. pageNum = 0;
  163. await ref
  164. .read(favoriteActivityProvider.notifier)
  165. .fetchUserActivity(pageNum: pageNum);
  166. return IndicatorResult.success;
  167. },
  168. onLoad: () async {
  169. if (totalActivity >= activities.total.safeValue) {
  170. return IndicatorResult.noMore;
  171. } else {
  172. pageNum++;
  173. await ref
  174. .read(favoriteActivityProvider.notifier)
  175. .fetchUserActivity(pageNum: pageNum);
  176. return IndicatorResult.success;
  177. }
  178. },
  179. childBuilder: (context, py) {
  180. return ListView.separated(
  181. physics: py,
  182. separatorBuilder: (context, index) {
  183. return SizedBox(height: 10.h);
  184. },
  185. padding: EdgeInsets.symmetric(
  186. horizontal: horizontalPadding,
  187. vertical: horizontalPadding,
  188. ),
  189. itemCount: activities.rows?.length ?? 0,
  190. itemBuilder: (context, index) {
  191. return GestureDetector(
  192. onTap: () {
  193. context.push(
  194. "/activity/detail",
  195. extra: activities.rows?[index]?.contentId ?? "",
  196. );
  197. },
  198. child: ActivityCardWidget(
  199. cellData: activities.rows?[index] ?? ActivityModelRecord(),
  200. ),
  201. );
  202. },
  203. );
  204. },
  205. );
  206. }
  207. @override
  208. bool get wantKeepAlive => true;
  209. }
  210. final favoriteVideoProvider =
  211. NotifierProvider<FavoriteVideoProvider, UserVideo>(() {
  212. return FavoriteVideoProvider();
  213. });
  214. class _UserVideoListPage extends ConsumerStatefulWidget {
  215. const _UserVideoListPage();
  216. @override
  217. ConsumerState<ConsumerStatefulWidget> createState() {
  218. return _UserVideoListPageState();
  219. }
  220. }
  221. class _UserVideoListPageState extends ConsumerState<_UserVideoListPage>
  222. with AutomaticKeepAliveClientMixin {
  223. int pageNum = 0;
  224. int totalVideo = 0;
  225. @override
  226. void initState() {
  227. super.initState();
  228. ref.read(favoriteVideoProvider.notifier).fetchUserVideo(pageNum: pageNum);
  229. }
  230. @override
  231. Widget build(BuildContext context) {
  232. super.build(context);
  233. final videos = ref.watch(favoriteVideoProvider);
  234. ref.listen(favoriteVideoProvider, (pre, next) {
  235. totalVideo = next.rows?.length ?? 0;
  236. });
  237. return videos.rows?.isEmpty == true
  238. ? Empty1widget()
  239. : EasyRefresh.builder(
  240. onRefresh: () async {
  241. pageNum = 0;
  242. await ref
  243. .read(favoriteVideoProvider.notifier)
  244. .fetchUserVideo(pageNum: pageNum);
  245. return IndicatorResult.success;
  246. },
  247. onLoad: () async {
  248. if (totalVideo >= videos.total.safeValue) {
  249. return IndicatorResult.noMore;
  250. } else {
  251. pageNum++;
  252. await ref
  253. .read(favoriteVideoProvider.notifier)
  254. .fetchUserVideo(pageNum: pageNum);
  255. return IndicatorResult.success;
  256. }
  257. },
  258. childBuilder: (context, py) {
  259. return ListView.separated(
  260. physics: py,
  261. padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
  262. itemBuilder: (context, index) {
  263. return FavoriteVideoItemWidget(data: videos.rows![index]);
  264. },
  265. separatorBuilder: (context, index) {
  266. return SizedBox(height: 10.h);
  267. },
  268. itemCount: videos.rows?.length ?? 0,
  269. );
  270. },
  271. );
  272. }
  273. @override
  274. bool get wantKeepAlive => true;
  275. }