news_child_main_page.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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:flutter_swiper_view/flutter_swiper_view.dart';
  6. import 'package:go_router/go_router.dart';
  7. import 'package:news_app/constant/size_res.dart';
  8. import 'package:news_app/extension/base.dart';
  9. import 'package:news_app/ui/news/special_list_page.dart';
  10. import 'package:news_app/widget/my_txt.dart';
  11. import 'package:shimmer/shimmer.dart';
  12. import '../../constant/color_res.dart';
  13. import '../../gen/assets.gen.dart';
  14. import '../../model/activity_banner_model.dart';
  15. import '../../model/news_data_model.dart';
  16. import '../../provider/news_provider.dart';
  17. import '../../util/time_util.dart';
  18. import '../../widget/list_animation_layout.dart';
  19. import '../../widget/load_image.dart';
  20. /// @author: bo.zeng
  21. /// @email: cnhbwds@gmail.com
  22. /// @date: 2025 2025/4/9 16:00
  23. /// @description:
  24. class NewsChildMainPage extends ConsumerStatefulWidget {
  25. final int tabIndex;
  26. const NewsChildMainPage({super.key, required this.tabIndex});
  27. @override
  28. ConsumerState<NewsChildMainPage> createState() => _NewsChildMainPageState();
  29. }
  30. final newsProvider = NotifierProvider.family<NewsNotifier, NewsState, int>(() {
  31. return NewsNotifier();
  32. });
  33. class _NewsChildMainPageState extends ConsumerState<NewsChildMainPage>
  34. {
  35. final Map<int, String> _iconMap = {
  36. 0: Assets.images.zxzxIcon.path,
  37. 1: Assets.images.zxzxIcon1.path,
  38. 2: Assets.images.zxzxIcon2.path,
  39. 3: Assets.images.zxzxIcon2.path,
  40. };
  41. int pageNum = 1;
  42. int total = 0;
  43. @override
  44. void initState() {
  45. super.initState();
  46. ref.read(newsProvider(widget.tabIndex).notifier).fetchNewsItemList(pageNum);
  47. ref.read(newsProvider(widget.tabIndex).notifier).fetchBannerList();
  48. }
  49. @override
  50. Widget build(BuildContext context) {
  51. // super.build(context);
  52. // consoleLog(widget.tabIndex);
  53. final data = ref.watch(
  54. newsProvider(widget.tabIndex).select((p) => p.newsDataModel),
  55. );
  56. final bannerList = ref.watch(
  57. newsProvider(widget.tabIndex).select((p) => p.bannerList),
  58. );
  59. ref.listen(newsProvider(widget.tabIndex).select((p) => p.newsDataModel), (
  60. p,
  61. next,
  62. ) {
  63. total = next.records?.length ?? 0;
  64. });
  65. return EasyRefresh.builder(
  66. onRefresh: () async {
  67. // if (bannerList.isEmpty) {
  68. await ref
  69. .read(newsProvider(widget.tabIndex).notifier)
  70. .fetchBannerList();
  71. // }
  72. pageNum = 1;
  73. await ref
  74. .read(newsProvider(widget.tabIndex).notifier)
  75. .fetchNewsItemList(pageNum);
  76. return IndicatorResult.success;
  77. },
  78. onLoad: () async {
  79. if (total >= data.total.safeValue) {
  80. return IndicatorResult.noMore;
  81. } else {
  82. pageNum += 1;
  83. await ref
  84. .read(newsProvider(widget.tabIndex).notifier)
  85. .fetchNewsItemList(pageNum);
  86. }
  87. },
  88. childBuilder: (context, physics) {
  89. return Padding(
  90. padding: EdgeInsets.symmetric(
  91. horizontal: horizontalPadding,
  92. ),
  93. child: CustomScrollView(
  94. physics: physics,
  95. slivers: [
  96. //注意独家的部顶没有Banner
  97. if (widget.tabIndex != 5)
  98. SliverToBoxAdapter(
  99. child: Container(
  100. margin: EdgeInsets.only(top: 10.h),
  101. height: 300.h,
  102. child: Builder(
  103. builder: (context) {
  104. //其他都是banner
  105. return buildBanner(
  106. widget.tabIndex,
  107. bannerList,
  108. context,
  109. );
  110. },
  111. ),
  112. ),
  113. ),
  114. SliverToBoxAdapter(child: _buildTitle(widget.tabIndex)),
  115. if (widget.tabIndex <= 3)
  116. SliverToBoxAdapter(
  117. child: Container(
  118. margin: EdgeInsets.only(
  119. left: 10.w,
  120. top: 16.h,
  121. bottom: 10.h,
  122. ),
  123. alignment: Alignment.centerLeft,
  124. child: Image.asset(_iconMap[widget.tabIndex]!, scale: 2.5),
  125. ),
  126. ),
  127. // if (widget.tabIndex == 3)
  128. // SliverToBoxAdapter(child: _buildRowItem()),
  129. //新闻列表
  130. (data.records ?? []).isEmpty ? SliverList.separated(
  131. separatorBuilder: (context, index) {
  132. return Container(height: 10.h);
  133. },
  134. itemBuilder: (context, index) {
  135. return Shimmer.fromColors(
  136. baseColor: Color(0xffe5e5e5),
  137. highlightColor: Color(0xfff5f5f5),
  138. child: AnimationItem(),
  139. );
  140. },
  141. itemCount: 20,
  142. ) : SliverList.separated(
  143. separatorBuilder: (context, index) {
  144. return Container(height: 10.h);
  145. },
  146. itemBuilder: (context, index) {
  147. // if (widget.tabIndex == 4) {
  148. // return _buildLiquorItem(data.records?[index], context);
  149. // } else
  150. if (widget.tabIndex == 5) {
  151. return Container(
  152. child: _buildExclusiveWidget(
  153. data.records?[index],
  154. index,
  155. context,
  156. ),
  157. );
  158. } else {
  159. return Container(
  160. child: buildNewsNewItem(data.records?[index], context),
  161. ); //buildNewsNewItem(data.records?[index], context);
  162. }
  163. },
  164. itemCount: data.records?.length,
  165. )
  166. ],
  167. ),
  168. );
  169. },
  170. );
  171. }
  172. // @override
  173. // bool get wantKeepAlive => true;
  174. }
  175. Widget buildBanner(
  176. int tabIndex,
  177. List<ActivityBannerModel> bannerList,
  178. BuildContext context,
  179. ) {
  180. return Swiper(
  181. autoplay: true,
  182. // 关键配置:关闭viewportFraction和pageSnapping
  183. viewportFraction: 1.0,
  184. onTap: (index) {
  185. final item = bannerList[index];
  186. if ((item.sourceId ?? '').isNotEmpty) {
  187. if (item.type == 'column') {
  188. context.push(SpecialListPage.routeName, extra: item.sourceId);
  189. } else {
  190. context.push("/news/detail", extra: item.sourceId);
  191. }
  192. }
  193. },
  194. itemBuilder: (context, index) {
  195. if (bannerList.isEmpty) {
  196. return Container();
  197. }
  198. // int start = tabIndex * banners.length;
  199. // int end = start + banners.length;
  200. return ClipRRect(
  201. borderRadius: BorderRadius.circular(8),
  202. child: Stack(
  203. children: [
  204. LoadImage(
  205. bannerList[index].image ?? '',
  206. width: screenWidth,
  207. height:300.h,
  208. fit: BoxFit.cover,
  209. holderImg: 'bignone',
  210. ),
  211. if (tabIndex <= 3 || tabIndex == 5)
  212. Positioned(
  213. bottom: 20.h,
  214. left: 15.h,
  215. right: 20.h,
  216. child: Builder(
  217. builder: (context) {
  218. if (tabIndex == 3) {
  219. return Padding(
  220. padding: EdgeInsets.only(bottom: 5.h),
  221. child: myTxt(
  222. text: bannerList[index].title ?? '',
  223. color: Colors.white,
  224. fontSize: 15.sp,
  225. ),
  226. );
  227. } else {
  228. return Column(
  229. spacing: 1.h,
  230. crossAxisAlignment: CrossAxisAlignment.start,
  231. children: [
  232. SizedBox(height: 3.h),
  233. myTxt(
  234. text: bannerList[index].title ?? '',
  235. color: Colors.white,
  236. fontSize: 15.sp,
  237. ),
  238. ],
  239. );
  240. }
  241. },
  242. ),
  243. ),
  244. ],
  245. ),
  246. );
  247. },
  248. itemCount: bannerList.length,
  249. pagination: const SwiperPagination(),
  250. );
  251. }
  252. Widget buildNewsNewItem(NewsRecord? item, BuildContext context) {
  253. if (item == null) {
  254. return const SizedBox.shrink();
  255. }
  256. int imageLength = item.imagesSrc?.length ?? 0;
  257. return GestureDetector(
  258. onTap: () => context.push("/news/detail", extra: item.contentId),
  259. child: Container(
  260. decoration: BoxDecoration(
  261. borderRadius: BorderRadius.circular(8.r),
  262. color: Colors.white,
  263. ),
  264. padding: EdgeInsets.all(10.w),
  265. child: Builder(
  266. builder: (context) {
  267. if (imageLength == 0 || imageLength == 3) {
  268. return _buildNewsVertical(item);
  269. } else {
  270. //别的一律当1张图处理
  271. return Row(
  272. children: [
  273. Expanded(
  274. child: SizedBox(
  275. height: 70.h,
  276. child: Column(
  277. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  278. crossAxisAlignment: CrossAxisAlignment.start,
  279. children: [
  280. myTxt(
  281. text: item.title ?? "",
  282. fontWeight: FontWeight.bold,
  283. color: color333333,
  284. fontSize: 15.sp,
  285. maxLines: 2,
  286. ),
  287. Row(
  288. spacing: 20.w,
  289. children: [
  290. myTxt(
  291. text: item.author ?? "新华日报",
  292. fontSize: 12.sp,
  293. color: colorA7A6A6,
  294. ),
  295. myTxt(
  296. text: item.publishDateStr ?? "4天前",
  297. fontSize: 12.sp,
  298. color: colorA7A6A6,
  299. ),
  300. ],
  301. ),
  302. ],
  303. ),
  304. ),
  305. ),
  306. SizedBox(width: 5.w),
  307. LoadImage(
  308. width: 93.w,
  309. height: 70.h,
  310. item.imagesSrc?[0] ?? "",
  311. fit: BoxFit.cover,
  312. ),
  313. ],
  314. );
  315. }
  316. },
  317. ),
  318. ),
  319. );
  320. }
  321. Widget _buildNewsVertical(NewsRecord item) {
  322. return Column(
  323. spacing: 5.h,
  324. crossAxisAlignment: CrossAxisAlignment.start,
  325. children: [
  326. myTxt(
  327. text: item.title ?? "",
  328. fontWeight: FontWeight.bold,
  329. color: color333333,
  330. fontSize: 15.sp,
  331. maxLines: 1,
  332. ),
  333. myTxt(
  334. text: item.summary ?? "",
  335. color: color666666,
  336. fontSize: 12.sp,
  337. maxLines: 1,
  338. ),
  339. if (item.imagesSrc?.isNotEmpty == true)
  340. Row(
  341. children:
  342. item.imagesSrc
  343. ?.map((e) => Expanded(child: LoadImage(e, fit: BoxFit.cover)))
  344. .toList() ??
  345. [],
  346. ),
  347. Row(
  348. spacing: 20.w,
  349. children: [
  350. myTxt(text: item.author ?? "", fontSize: 12.sp, color: colorA7A6A6),
  351. myTxt(
  352. text: item.publishDateStr ?? "",
  353. fontSize: 12.sp,
  354. color: colorA7A6A6,
  355. ),
  356. ],
  357. ),
  358. ],
  359. );
  360. }
  361. Widget _buildTitle(int index) {
  362. switch (index) {
  363. case 0:
  364. return Container();
  365. case 1:
  366. return Container();
  367. case 2:
  368. return Container();
  369. case 3:
  370. return Container();
  371. case 4:
  372. return Padding(
  373. padding: EdgeInsets.only(
  374. top: 10.h,
  375. bottom: 10.h,
  376. left: horizontalPadding,
  377. ),
  378. child: myTxt(
  379. text: "深一度(行业观察)",
  380. color: Colors.black,
  381. fontWeight: FontWeight.bold,
  382. fontSize: 15.sp,
  383. ),
  384. );
  385. default:
  386. return SizedBox.shrink();
  387. }
  388. }
  389. //独家widget
  390. Widget _buildExclusiveWidget(
  391. NewsRecord? item,
  392. int index,
  393. BuildContext context,
  394. ) {
  395. return GestureDetector(
  396. onTap: () {
  397. context.push("/news/detail", extra: item?.contentId ?? '');
  398. },
  399. child: Container(
  400. margin: EdgeInsets.only(bottom: 15.h),
  401. child: Column(
  402. spacing: 8.h,
  403. children: [
  404. Row(
  405. spacing: 10.w,
  406. children: [
  407. Image.asset(Assets.images.hotDicsussion.path, width: 50.w),
  408. Flexible(
  409. child: myTxt(
  410. text: item?.title ?? "百年后,小西湖终于又有了湖",
  411. color: index == 0 ? Colors.white : color333333,
  412. fontSize: 16.sp,
  413. fontWeight: FontWeight.bold,
  414. maxLines: 1,
  415. ),
  416. ),
  417. ],
  418. ),
  419. ClipRRect(
  420. borderRadius: BorderRadius.circular(8.r),
  421. child: LoadImage(
  422. item?.imagesSrc?.first ?? '',
  423. height: 300.h,
  424. width: screenWidth,
  425. fit: BoxFit.cover,
  426. holderImg: 'bignone',
  427. ),
  428. ),
  429. Row(
  430. spacing: 10.w,
  431. children: [
  432. myTxt(
  433. text: item?.author ?? "新华日报",
  434. color: colorA7A6A6,
  435. fontSize: 12.sp,
  436. fontWeight: FontWeight.bold,
  437. ),
  438. SizedBox(width: 5.w),
  439. myTxt(
  440. text:item?.publishDateStr ?? '',
  441. color: colorA7A6A6,
  442. fontSize: 12.sp,
  443. fontWeight: FontWeight.bold,
  444. ),
  445. ],
  446. ),
  447. ],
  448. ),
  449. ),
  450. );
  451. }