user_score_page.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. import 'package:easy_refresh/easy_refresh.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_riverpod/flutter_riverpod.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.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/provider/user_score_provider.dart';
  10. import 'package:news_app/widget/load_image.dart';
  11. import 'package:news_app/widget/my_txt.dart';
  12. import '../../constant/color_res.dart';
  13. import '../../constant/config.dart';
  14. import '../../gen/assets.gen.dart';
  15. import '../../main.dart';
  16. import '../../model/user_model.dart';
  17. import '../../model/user_score_model.dart';
  18. import '../../util/theme_util.dart';
  19. import '../../widget/list_animation_layout.dart';
  20. /// @author: bo.zeng
  21. /// @email: cnhbwds@gmail.com
  22. /// @date: 2025 2025/4/9 16:00
  23. /// @description:
  24. class UserScorePage extends ConsumerStatefulWidget {
  25. const UserScorePage({super.key});
  26. @override
  27. ConsumerState<UserScorePage> createState() => _UserScorePageState();
  28. }
  29. final userScoreProvider = NotifierProvider<UserScoreProvider, UserScoreData>(
  30. () {
  31. return UserScoreProvider();
  32. },
  33. );
  34. class _UserScorePageState extends ConsumerState<UserScorePage> {
  35. int pageNum = 0;
  36. int totalNews = 0;
  37. @override
  38. void initState() {
  39. super.initState();
  40. setImmersiveStatusBar();
  41. ref.read(userScoreProvider.notifier).fetchUserScoreList(pageNum);
  42. eventBus.on<String>().listen((event) {
  43. if (event == "mainCall") {
  44. pageNum = 0;
  45. ref.read(userScoreProvider.notifier).fetchUserScoreList(pageNum);
  46. }
  47. });
  48. }
  49. @override
  50. void dispose() {
  51. // TODO: implement dispose
  52. super.dispose();
  53. }
  54. @override
  55. Widget build(BuildContext context) {
  56. final scoreData = ref.watch(userScoreProvider);
  57. ref.listen(userScoreProvider, (pre, next) {
  58. totalNews = next.rows?.length ?? 0;
  59. });
  60. return AnnotatedRegion<SystemUiOverlayStyle>(
  61. value: SystemUiOverlayStyle.light, // or dark based on背景
  62. child: Material(
  63. child: Stack(
  64. children: [
  65. _buildHeader(context),
  66. Positioned(
  67. top: 180.h,
  68. bottom: 0,
  69. left: horizontalPadding,
  70. right: horizontalPadding,
  71. child: Container(
  72. padding: EdgeInsets.all(16.w),
  73. decoration: BoxDecoration(
  74. color: Colors.white,
  75. borderRadius: BorderRadius.circular(10.w),
  76. ),
  77. child: Column(
  78. crossAxisAlignment: CrossAxisAlignment.start,
  79. children: [
  80. Row(
  81. crossAxisAlignment: CrossAxisAlignment.center,
  82. mainAxisAlignment: MainAxisAlignment.start,
  83. children: [
  84. LoadAssetImage("score_shop", width: 40.w, height: 40.h),
  85. SizedBox(width: 8.w),
  86. Expanded(
  87. child: myTxt(
  88. text: "积分商城",
  89. fontWeight: FontWeight.bold,
  90. fontSize: 16.sp,
  91. ),
  92. ),
  93. GestureDetector(
  94. onTap: () {
  95. context.push("/user/shop");
  96. },
  97. child: Container(
  98. width: 90.w,
  99. height: 28.h,
  100. decoration: BoxDecoration(
  101. color: Colors.white,
  102. borderRadius: BorderRadius.circular(14.w),
  103. gradient: LinearGradient(
  104. colors: [color5F59F7, color6592FD],
  105. begin: Alignment.centerLeft,
  106. end: Alignment.centerRight,
  107. ),
  108. ),
  109. alignment: Alignment.center,
  110. child: myTxt(
  111. text: "我要兑换",
  112. fontWeight: FontWeight.normal,
  113. fontSize: 13.sp,
  114. color: Colors.white,
  115. ),
  116. ),
  117. ),
  118. ],
  119. ),
  120. SizedBox(height: 10.h),
  121. Divider(height: 0.1.h, color: Colors.grey[300]),
  122. SizedBox(height: 10.h),
  123. myTxt(
  124. text: "积分明细",
  125. fontWeight: FontWeight.bold,
  126. fontSize: 20.sp,
  127. ),
  128. SizedBox(height: 10.h),
  129. Expanded(
  130. child: SafeArea(
  131. top: false,
  132. child: EasyRefresh.builder(
  133. onRefresh: () async {
  134. pageNum = 0;
  135. await ref
  136. .read(userScoreProvider.notifier)
  137. .fetchUserScoreList(pageNum);
  138. return IndicatorResult.success;
  139. },
  140. onLoad: () async {
  141. if (totalNews >= scoreData.total.safeValue) {
  142. return IndicatorResult.noMore;
  143. } else {
  144. pageNum++;
  145. await ref
  146. .read(userScoreProvider.notifier)
  147. .fetchUserScoreList(pageNum);
  148. return IndicatorResult.success;
  149. }
  150. },
  151. childBuilder: (context, py) {
  152. return (scoreData.rows ?? []).isEmpty
  153. ? ListAnimationLayout()
  154. : ListView.separated(
  155. padding: EdgeInsets.zero,
  156. separatorBuilder:
  157. (context, index) => Divider(
  158. height: 0.1.h,
  159. color: Colors.grey[300],
  160. ),
  161. itemCount: scoreData.rows?.length ?? 0,
  162. itemBuilder: (context, index) {
  163. final item =
  164. scoreData.rows?[index] ??
  165. UserScoreModelRows();
  166. return ListTile(
  167. contentPadding: EdgeInsets.zero,
  168. title: Text(
  169. item.opType ?? '',
  170. style: TextStyle(fontSize: 16),
  171. ),
  172. subtitle: Text(
  173. item.logTime ?? '',
  174. style: TextStyle(color: Colors.grey),
  175. ),
  176. trailing: Text(
  177. "${item.changeExp}积分",
  178. style: TextStyle(
  179. fontSize: 16,
  180. color: Colors.black,
  181. fontWeight: FontWeight.bold,
  182. ),
  183. ),
  184. );
  185. },
  186. );
  187. },
  188. ),
  189. ),
  190. ),
  191. ],
  192. ),
  193. ),
  194. ),
  195. ],
  196. ),
  197. ),
  198. );
  199. }
  200. Widget _buildHeader(BuildContext context) {
  201. UserModel user = ref.watch(globalUserProvider);
  202. return SizedBox(
  203. width: double.infinity,
  204. height: 240.h, // 你自己项目里的适配高度
  205. child: Stack(
  206. fit: StackFit.expand, // 横向完全撑满
  207. children: [
  208. Image.asset(Assets.images.scoreNewBg.path, fit: BoxFit.cover),
  209. Padding(
  210. padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 40.h),
  211. child: Column(
  212. crossAxisAlignment: CrossAxisAlignment.start,
  213. children: [
  214. SizedBox(height: 14.h),
  215. Row(
  216. children: [
  217. GestureDetector(
  218. onTap: () {
  219. eventBus.fire('mainCall');
  220. context.pop();
  221. },
  222. child: Icon(Icons.arrow_back_ios, color: Colors.white),
  223. ),
  224. Expanded(
  225. child: Center(
  226. child: myTxt(
  227. text: '积分详情',
  228. fontWeight: FontWeight.bold,
  229. fontSize: 18.sp,
  230. color: Colors.white,
  231. ),
  232. ),
  233. ),
  234. ],
  235. ),
  236. SizedBox(height: 30.h),
  237. Row(
  238. children: [
  239. Image.asset(Assets.images.starIcon.path, width: 20.w),
  240. SizedBox(width: 10.w),
  241. Text(
  242. '我的积分',
  243. style: TextStyle(fontSize: 16.sp, color: Colors.white70),
  244. ),
  245. ],
  246. ),
  247. SizedBox(height: 5.h),
  248. Text(
  249. user.credit ?? '10345',
  250. style: TextStyle(
  251. fontSize: 30.sp,
  252. fontWeight: FontWeight.bold,
  253. color: Colors.white,
  254. ),
  255. ),
  256. ],
  257. ),
  258. ),
  259. ],
  260. ),
  261. );
  262. }
  263. }
  264. // class PointsDetailPage extends StatelessWidget {
  265. // final List<Map<String, dynamic>> pointsHistory = [
  266. // {
  267. // 'type': '登录赠送',
  268. // 'date': '2025.12.25 12:00',
  269. // 'points': 10,
  270. // 'isPositive': true,
  271. // },
  272. // {
  273. // 'type': '商品兑换',
  274. // 'date': '2025.12.25 12:00',
  275. // 'points': -10,
  276. // 'isPositive': false,
  277. // },
  278. // {
  279. // 'type': '商品兑换',
  280. // 'date': '2025.12.25 12:00',
  281. // 'points': -10,
  282. // 'isPositive': false,
  283. // },
  284. // {
  285. // 'type': '登录赠送',
  286. // 'date': '2025.12.25 12:00',
  287. // 'points': 10,
  288. // 'isPositive': true,
  289. // },
  290. // {
  291. // 'type': '商品兑换',
  292. // 'date': '2025.12.25 12:00',
  293. // 'points': -10,
  294. // 'isPositive': false,
  295. // },
  296. // {
  297. // 'type': '商品兑换',
  298. // 'date': '2025.12.25 12:00',
  299. // 'points': -10,
  300. // 'isPositive': false,
  301. // },
  302. // {
  303. // 'type': '登录赠送',
  304. // 'date': '2025.12.25 12:00',
  305. // 'points': 10,
  306. // 'isPositive': true,
  307. // },
  308. // {
  309. // 'type': '登录赠送',
  310. // 'date': '2025.12.25 12:00',
  311. // 'points': 10,
  312. // 'isPositive': true,
  313. // },
  314. // ];
  315. //
  316. // PointsDetailPage({super.key});
  317. //
  318. // @override
  319. // Widget build(BuildContext context) {
  320. //
  321. // return Material(
  322. // child: Stack(
  323. // children: [
  324. // _buildHeader(context),
  325. // Positioned(
  326. // top: 180.h,
  327. // bottom: 0,
  328. // left: horizontalPadding,
  329. // right: horizontalPadding,
  330. // child: Container(
  331. // padding: EdgeInsets.all(16.w),
  332. // decoration: BoxDecoration(
  333. // color: Colors.white,
  334. // borderRadius: BorderRadius.circular(10.w),
  335. // ),
  336. // child: Column(
  337. // crossAxisAlignment: CrossAxisAlignment.start,
  338. // children: [
  339. // myTxt(text: "积分明细", fontWeight: FontWeight.bold, fontSize: 20.sp),
  340. // SizedBox(height: 10.h),
  341. // Expanded(
  342. // child: EasyRefresh.builder(
  343. // onRefresh: () async {
  344. // pageNum = 0;
  345. // await ref
  346. // .read(userScoreProvider.notifier)
  347. // .fetchUserReadHistory(pageNum);
  348. // return IndicatorResult.success;
  349. // },
  350. // onLoad: () async {
  351. // if (totalNews >= news.total.safeValue) {
  352. // return IndicatorResult.noMore;
  353. // } else {
  354. // pageNum++;
  355. // await ref
  356. // .read(userReadHistoryProvider.notifier)
  357. // .fetchUserReadHistory(pageNum);
  358. // return IndicatorResult.success;
  359. // }
  360. // },
  361. // childBuilder: (context, py) {}
  362. // ),
  363. // ListView.separated(
  364. // padding: EdgeInsets.zero,
  365. // separatorBuilder:
  366. // (context, index) =>
  367. // Divider(height: 0.1.h, color: Colors.grey[300]),
  368. // itemCount: pointsHistory.length,
  369. // itemBuilder: (context, index) {
  370. // final item = pointsHistory[index];
  371. // return ListTile(
  372. // contentPadding: EdgeInsets.zero,
  373. // title: Text(item['type'], style: TextStyle(fontSize: 16)),
  374. // subtitle: Text(
  375. // item['date'],
  376. // style: TextStyle(color: Colors.grey),
  377. // ),
  378. // trailing: Text(
  379. // "${item['isPositive'] ? '+' : ''}${item['points']}积分",
  380. // style: TextStyle(
  381. // fontSize: 16,
  382. // color: item['isPositive'] ? Colors.black : Colors.red,
  383. // fontWeight: FontWeight.bold,
  384. // ),
  385. // ),
  386. // );
  387. // },
  388. // ),
  389. // ),
  390. // ],
  391. // ),
  392. // );,
  393. // ),
  394. // ],
  395. // ),
  396. // );
  397. // }
  398. //
  399. //
  400. // // Widget _buildPointsList() {
  401. // // return
  402. // // }
  403. // }