rewards_detail_page.dart 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. import 'package:cpt_rewards/modules/rewards_confirm/rewards_confirm_page.dart';
  2. import 'package:cs_resources/theme/app_colors_theme.dart';
  3. import 'package:domain/entity/rewards_detail_entity.dart';
  4. import 'package:flutter/cupertino.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:auto_route/auto_route.dart';
  7. import 'package:flutter_hooks/flutter_hooks.dart';
  8. import 'package:hooks_riverpod/hooks_riverpod.dart';
  9. import 'package:router/ext/auto_router_extensions.dart';
  10. import 'package:shared/utils/log_utils.dart';
  11. import 'package:shared/utils/color_utils.dart';
  12. import 'package:widgets/ext/ex_widget.dart';
  13. import 'package:widgets/load_state_layout.dart';
  14. import 'package:widgets/my_appbar.dart';
  15. import 'package:widgets/my_load_image.dart';
  16. import 'package:widgets/my_text_view.dart';
  17. import 'package:widgets/shatter/picker_container.dart';
  18. import 'package:widgets/widget_export.dart';
  19. import 'package:cs_resources/generated/assets.dart';
  20. import '../../../router/page/rewards_page_router.dart';
  21. import './rewards_detail_vm.dart';
  22. @RoutePage()
  23. class RewardsDetailPage extends HookConsumerWidget {
  24. final int? id;
  25. const RewardsDetailPage({Key? key, @PathParam('id') required this.id})
  26. : super(key: key);
  27. //启动当前页面
  28. static void startInstance({
  29. BuildContext? context,
  30. int? id,
  31. }) {
  32. if (context != null) {
  33. context.router.push(RewardsDetailPageRoute(id: id));
  34. } else {
  35. appRouter.push(RewardsDetailPageRoute(id: id));
  36. }
  37. }
  38. // listitem
  39. Widget _buildSaleItem(BuildContext context, WidgetRef ref, _vm, detailInfo) {
  40. String title = detailInfo!.title ?? "";
  41. String createdAt = detailInfo.createdAt ?? "";
  42. List? resources = detailInfo!.resources ?? [];
  43. int point = detailInfo.point ?? 0;
  44. int originalPoint = detailInfo.originalPoint ?? 0;
  45. String description = detailInfo.description ?? "";
  46. bool reservation = detailInfo!.reservation;
  47. String redeemedStart = detailInfo.redeemedStart ?? "";
  48. String redeemedEnd = detailInfo.redeemedEnd ?? "";
  49. String redeemedDate = '$redeemedStart-$redeemedEnd';
  50. return Column(
  51. children: [
  52. Container(
  53. decoration: const BoxDecoration(
  54. color: Colors.white,
  55. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  56. boxShadow: [
  57. BoxShadow(
  58. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  59. ],
  60. ),
  61. width: MediaQuery.of(context).size.width - 30,
  62. // height: 420,
  63. // margin: const EdgeInsets.only(left: 15, right: 15, top: 12.5),
  64. child: Column(
  65. children: [
  66. resources!.length > 0
  67. ? MyLoadImage(
  68. resources[0] ?? '',
  69. width: MediaQuery.of(context).size.width,
  70. height: 150,
  71. )
  72. : Container(),
  73. Column(
  74. crossAxisAlignment: CrossAxisAlignment.start,
  75. mainAxisAlignment: MainAxisAlignment.start,
  76. children: [
  77. Text(
  78. maxLines: 1, // 设置最大行数为2
  79. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  80. title,
  81. style: const TextStyle(
  82. fontSize: 16.0,
  83. color: Colors.black,
  84. fontWeight: FontWeight.w500),
  85. ),
  86. Text(
  87. maxLines: 1, // 设置最大行数为2
  88. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  89. 'Pulished Date: $createdAt',
  90. style: TextStyle(
  91. fontSize: 13.0,
  92. color: ColorUtils.string2Color('#808DAF'),
  93. fontWeight: FontWeight.w400),
  94. ).marginOnly(bottom: 5),
  95. Row(
  96. children: [
  97. Text(
  98. '$point',
  99. style: const TextStyle(
  100. fontSize: 19.0,
  101. color: Colors.black,
  102. fontWeight: FontWeight.w500),
  103. ),
  104. Text(
  105. '$originalPoint',
  106. style: TextStyle(
  107. decoration: TextDecoration.lineThrough,
  108. decorationColor:
  109. ColorUtils.string2Color('#808DAF'),
  110. decorationStyle: TextDecorationStyle.solid,
  111. fontSize: 12.0,
  112. color: ColorUtils.string2Color('#808DAF'),
  113. fontWeight: FontWeight.w400),
  114. ).marginOnly(left: 5, right: 5),
  115. const Text(
  116. 'Points',
  117. style: TextStyle(
  118. fontSize: 13.0,
  119. color: Colors.black,
  120. fontWeight: FontWeight.w400),
  121. ),
  122. ],
  123. ),
  124. Text(
  125. description,
  126. style: TextStyle(
  127. fontSize: 13.0,
  128. color: ColorUtils.string2Color('#808DAF'),
  129. fontWeight: FontWeight.w400),
  130. ).marginOnly(bottom: 10, top: 10),
  131. Container(
  132. height: 28,
  133. decoration: BoxDecoration(
  134. color: ColorUtils.string2Color('#F2F3F6'),
  135. borderRadius:
  136. const BorderRadius.all(Radius.circular(6.0)),
  137. ),
  138. child: Row(
  139. mainAxisSize: MainAxisSize.min,
  140. mainAxisAlignment: MainAxisAlignment.start,
  141. crossAxisAlignment: CrossAxisAlignment.center,
  142. children: [
  143. const MyAssetImage(
  144. Assets.rewardsRewardsDetailDay,
  145. width: 25,
  146. height: 25,
  147. ).marginOnly(right: 5),
  148. Text(
  149. maxLines: 1, // 设置最大行数为2
  150. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  151. redeemedDate,
  152. style: TextStyle(
  153. fontSize: 14.0,
  154. color: ColorUtils.string2Color('#808DAF'),
  155. fontWeight: FontWeight.w400),
  156. )
  157. ],
  158. ).paddingOnly(left: 7, right: 12)),
  159. Container(
  160. height: 28,
  161. decoration: BoxDecoration(
  162. color: ColorUtils.string2Color('#F2F3F6'),
  163. borderRadius:
  164. const BorderRadius.all(Radius.circular(6.0)),
  165. ),
  166. child: Row(
  167. mainAxisSize: MainAxisSize.min,
  168. mainAxisAlignment: MainAxisAlignment.start,
  169. crossAxisAlignment: CrossAxisAlignment.center,
  170. children: [
  171. const MyAssetImage(
  172. Assets.rewardsRewardsDetailRequired,
  173. width: 25,
  174. height: 25,
  175. ).marginOnly(right: 5),
  176. Text(
  177. maxLines: 1, // 设置最大行数为2
  178. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  179. reservation
  180. ? 'Reservation required'
  181. : 'No Reservation Required',
  182. style: TextStyle(
  183. fontSize: 14.0,
  184. color: ColorUtils.string2Color('#808DAF'),
  185. fontWeight: FontWeight.w400),
  186. )
  187. ],
  188. ).paddingOnly(left: 7, right: 12))
  189. .marginOnly(top: 8, bottom: 25)
  190. ],
  191. ).paddingOnly(left: 15, right: 15, top: 10),
  192. ],
  193. )),
  194. ],
  195. ).marginOnly(left: 15, bottom: 15, right: 15);
  196. }
  197. Widget _buildDeal(BuildContext context, WidgetRef ref, _vm) {
  198. return Column(
  199. children: [
  200. Container(
  201. decoration: const BoxDecoration(
  202. color: Colors.white,
  203. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  204. boxShadow: [
  205. BoxShadow(
  206. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  207. ],
  208. ),
  209. width: MediaQuery.of(context).size.width - 30,
  210. child: Column(
  211. crossAxisAlignment: CrossAxisAlignment.start,
  212. children: [
  213. Row(
  214. mainAxisSize: MainAxisSize.min,
  215. mainAxisAlignment: MainAxisAlignment.start,
  216. crossAxisAlignment: CrossAxisAlignment.center,
  217. children: [
  218. const MyAssetImage(
  219. Assets.rewardsRewardsDetailDeal,
  220. width: 25,
  221. height: 25,
  222. ).marginOnly(right: 10),
  223. Text(
  224. 'Redeem Deal At',
  225. style: TextStyle(
  226. fontSize: 15.0,
  227. color: ColorUtils.string2Color('#000000'),
  228. fontWeight: FontWeight.w500),
  229. )
  230. ],
  231. ),
  232. Container(
  233. height: 77,
  234. decoration: BoxDecoration(
  235. color: ColorUtils.string2Color('#F2F3F6'),
  236. borderRadius:
  237. const BorderRadius.all(Radius.circular(6.0)),
  238. ),
  239. child: Row(
  240. mainAxisSize: MainAxisSize.max,
  241. mainAxisAlignment: MainAxisAlignment.start,
  242. crossAxisAlignment: CrossAxisAlignment.center,
  243. children: [
  244. const MyAssetImage(
  245. Assets.rewardsRewardsIndex1,
  246. width: 60,
  247. height: 60,
  248. ).marginOnly(right: 15),
  249. Column(
  250. crossAxisAlignment: CrossAxisAlignment.start,
  251. children: [
  252. Text(
  253. maxLines: 1, // 设置最大行数为2
  254. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  255. 'Delibowl Group',
  256. style: TextStyle(
  257. fontSize: 15.0,
  258. color: ColorUtils.string2Color('#54638C'),
  259. fontWeight: FontWeight.w500),
  260. ).marginOnly(bottom: 8),
  261. Text(
  262. maxLines: 1, // 设置最大行数为2
  263. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  264. '#01-136, SingPost Center, 408600',
  265. style: TextStyle(
  266. fontSize: 14.0,
  267. color: ColorUtils.string2Color('#54638C'),
  268. fontWeight: FontWeight.w400),
  269. )
  270. ],
  271. ).paddingOnly(
  272. top: 5,
  273. )
  274. ],
  275. ).paddingOnly(left: 12, right: 12, bottom: 9, top: 9))
  276. .marginOnly(top: 12, bottom: 12),
  277. Row(
  278. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  279. crossAxisAlignment: CrossAxisAlignment.center,
  280. children: [
  281. Text(
  282. 'Also redeemable in 1 more location',
  283. style: TextStyle(
  284. fontSize: 15.0,
  285. color: ColorUtils.string2Color('#54638C'),
  286. fontWeight: FontWeight.w400),
  287. ),
  288. const MyAssetImage(
  289. Assets.rewardsRewardsRight,
  290. width: 12,
  291. height: 16,
  292. ),
  293. ],
  294. ),
  295. ],
  296. ).paddingOnly(left: 15, right: 15, top: 12, bottom: 20),
  297. ),
  298. ],
  299. ).marginOnly(left: 15, bottom: 15, right: 15);
  300. }
  301. Widget _buildPackage(BuildContext context, WidgetRef ref, _vm, detailInfo) {
  302. String package = detailInfo.package ?? "";
  303. return Column(
  304. children: [
  305. Container(
  306. decoration: const BoxDecoration(
  307. color: Colors.white,
  308. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  309. boxShadow: [
  310. BoxShadow(
  311. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  312. ],
  313. ),
  314. width: MediaQuery.of(context).size.width - 30,
  315. child: Column(
  316. crossAxisAlignment: CrossAxisAlignment.start,
  317. children: [
  318. Row(
  319. mainAxisSize: MainAxisSize.min,
  320. mainAxisAlignment: MainAxisAlignment.start,
  321. crossAxisAlignment: CrossAxisAlignment.center,
  322. children: [
  323. const MyAssetImage(
  324. Assets.rewardsRewardsDetailPackage,
  325. width: 25,
  326. height: 25,
  327. ).marginOnly(right: 10),
  328. Text(
  329. 'What’s In The Package',
  330. style: TextStyle(
  331. fontSize: 15.0,
  332. color: ColorUtils.string2Color('#000000'),
  333. fontWeight: FontWeight.w500),
  334. )
  335. ],
  336. ).marginOnly(bottom: 10),
  337. Column(
  338. crossAxisAlignment: CrossAxisAlignment.start,
  339. children: [
  340. Text(
  341. package,
  342. style: TextStyle(
  343. fontSize: 15.0,
  344. color: ColorUtils.string2Color('#54638C'),
  345. fontWeight: FontWeight.w400),
  346. ),
  347. ],
  348. ),
  349. ],
  350. ).paddingOnly(left: 15, right: 15, top: 12, bottom: 20),
  351. ),
  352. ],
  353. ).marginOnly(left: 15, bottom: 15, right: 15);
  354. }
  355. Widget _buildNotice(BuildContext context, WidgetRef ref, _vm, detailInfo) {
  356. String notice = detailInfo.notice ?? "";
  357. return Column(
  358. children: [
  359. Container(
  360. decoration: const BoxDecoration(
  361. color: Colors.white,
  362. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  363. boxShadow: [
  364. BoxShadow(
  365. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  366. ],
  367. ),
  368. width: MediaQuery.of(context).size.width - 30,
  369. child: Column(
  370. crossAxisAlignment: CrossAxisAlignment.start,
  371. children: [
  372. Row(
  373. mainAxisSize: MainAxisSize.min,
  374. mainAxisAlignment: MainAxisAlignment.start,
  375. crossAxisAlignment: CrossAxisAlignment.center,
  376. children: [
  377. const MyAssetImage(
  378. Assets.rewardsRewardsDetailNotice,
  379. width: 25,
  380. height: 25,
  381. ).marginOnly(right: 10),
  382. Text(
  383. 'Redemption Notice',
  384. style: TextStyle(
  385. fontSize: 15.0,
  386. color: ColorUtils.string2Color('#000000'),
  387. fontWeight: FontWeight.w500),
  388. )
  389. ],
  390. ).marginOnly(bottom: 10),
  391. Column(
  392. crossAxisAlignment: CrossAxisAlignment.start,
  393. children: [
  394. Text(
  395. notice,
  396. style: TextStyle(
  397. fontSize: 15.0,
  398. color: ColorUtils.string2Color('#54638C'),
  399. fontWeight: FontWeight.w400),
  400. ),
  401. ],
  402. ),
  403. ],
  404. ).paddingOnly(left: 15, right: 15, top: 12, bottom: 20),
  405. ),
  406. ],
  407. ).marginOnly(left: 15, bottom: 15, right: 15);
  408. }
  409. Widget _buildInstructions(BuildContext context, WidgetRef ref, _vm) {
  410. return Column(
  411. children: [
  412. Container(
  413. decoration: const BoxDecoration(
  414. color: Colors.white,
  415. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  416. boxShadow: [
  417. BoxShadow(
  418. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  419. ],
  420. ),
  421. width: MediaQuery.of(context).size.width - 30,
  422. child: Column(
  423. crossAxisAlignment: CrossAxisAlignment.start,
  424. children: [
  425. Row(
  426. mainAxisSize: MainAxisSize.min,
  427. mainAxisAlignment: MainAxisAlignment.start,
  428. crossAxisAlignment: CrossAxisAlignment.center,
  429. children: [
  430. const MyAssetImage(
  431. Assets.rewardsRewardsDetailInstructions,
  432. width: 25,
  433. height: 25,
  434. ).marginOnly(right: 10),
  435. Text(
  436. 'Redemption Instructions',
  437. style: TextStyle(
  438. fontSize: 15.0,
  439. color: ColorUtils.string2Color('#000000'),
  440. fontWeight: FontWeight.w500),
  441. )
  442. ],
  443. ).marginOnly(bottom: 10),
  444. Column(
  445. crossAxisAlignment: CrossAxisAlignment.start,
  446. children: [
  447. Text(
  448. 'Reservation not required.Present your YY Circle Voucher (it’s under “Me”tab)At the outlet.',
  449. style: TextStyle(
  450. fontSize: 15.0,
  451. color: ColorUtils.string2Color('#54638C'),
  452. fontWeight: FontWeight.w400),
  453. ),
  454. ],
  455. ),
  456. ],
  457. ).paddingOnly(left: 15, right: 15, top: 12, bottom: 20),
  458. ),
  459. ],
  460. ).marginOnly(left: 15, bottom: 15, right: 15);
  461. }
  462. Widget _buildRedeemable(
  463. BuildContext context, WidgetRef ref, _vm, detailInfo) {
  464. List? redeemable = detailInfo!.redeemable ?? [];
  465. return Column(
  466. children: [
  467. Container(
  468. decoration: const BoxDecoration(
  469. color: Colors.white,
  470. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  471. boxShadow: [
  472. BoxShadow(
  473. color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  474. ],
  475. ),
  476. width: MediaQuery.of(context).size.width - 30,
  477. child: Column(
  478. crossAxisAlignment: CrossAxisAlignment.start,
  479. children: [
  480. Row(
  481. mainAxisSize: MainAxisSize.min,
  482. mainAxisAlignment: MainAxisAlignment.start,
  483. crossAxisAlignment: CrossAxisAlignment.center,
  484. children: [
  485. const MyAssetImage(
  486. Assets.rewardsRewardsDetailNotice,
  487. width: 25,
  488. height: 25,
  489. ).marginOnly(right: 10),
  490. Text(
  491. 'Redeemable On',
  492. style: TextStyle(
  493. fontSize: 15.0,
  494. color: ColorUtils.string2Color('#000000'),
  495. fontWeight: FontWeight.w500),
  496. )
  497. ],
  498. ).marginOnly(bottom: 10),
  499. Column(
  500. crossAxisAlignment: CrossAxisAlignment.start,
  501. children: List.generate(
  502. redeemable!.length,
  503. (index) {
  504. final item = redeemable[index];
  505. return Text(
  506. '${item.day}: ${item.time}',
  507. style: TextStyle(
  508. fontSize: 15.0,
  509. color: ColorUtils.string2Color('#54638C'),
  510. fontWeight: FontWeight.w400),
  511. );
  512. },
  513. ),
  514. ),
  515. ],
  516. ).paddingOnly(left: 15, right: 15, top: 12, bottom: 20),
  517. ),
  518. ],
  519. ).marginOnly(left: 15, bottom: 15, right: 15);
  520. }
  521. @override
  522. Widget build(BuildContext context, WidgetRef ref) {
  523. final _vm = ref.read(rewardsDetailVmProvider.notifier);
  524. final state = ref.watch(rewardsDetailVmProvider);
  525. RewardsDetailEntity? detailInfo = state.detailInfo;
  526. useEffect(() {
  527. // 组件挂载时执行 - 执行接口请求
  528. Future.microtask(() => _vm.initPageData(id: id));
  529. return () {
  530. // 组件卸载时执行
  531. Log.d("property_news_page 组件卸载时执行");
  532. };
  533. }, []);
  534. return Scaffold(
  535. appBar: MyAppBar.appBar(
  536. context,
  537. "Detail",
  538. backgroundColor: context.appColors.whiteBG,
  539. ),
  540. body: LoadStateLayout(
  541. state: state.loadingState,
  542. errorMessage: state.errorMessage,
  543. errorRetry: () {
  544. _vm.retryRequest(id: id);
  545. },
  546. successWidget: Column(
  547. children: [
  548. Expanded(
  549. child: SingleChildScrollView(
  550. scrollDirection: Axis.vertical,
  551. physics: const BouncingScrollPhysics(),
  552. clipBehavior: Clip.none,
  553. child: Column(
  554. children: [
  555. Container(
  556. color: ColorUtils.string2Color('#F2F3F6'),
  557. padding: const EdgeInsets.only(top: 15),
  558. child: Column(
  559. children: [
  560. _buildSaleItem(context, ref, _vm, detailInfo),
  561. // _buildDeal(context, ref, _vm),
  562. _buildPackage(context, ref, _vm, detailInfo),
  563. _buildNotice(context, ref, _vm, detailInfo),
  564. // _buildInstructions(context, ref, _vm),
  565. _buildRedeemable(
  566. context, ref, _vm, detailInfo),
  567. ],
  568. )),
  569. ],
  570. ))),
  571. Container(
  572. height: 50,
  573. color: ColorUtils.string2Color('#4161D0'),
  574. child: Flex(
  575. direction: Axis.horizontal,
  576. children: [
  577. Expanded(
  578. flex: 1,
  579. child: Container(
  580. color: ColorUtils.string2Color('#4161D0'),
  581. height: 100,
  582. child: Row(
  583. mainAxisAlignment: MainAxisAlignment.center,
  584. crossAxisAlignment: CrossAxisAlignment.center,
  585. children: [
  586. MyTextView(
  587. "Redeem",
  588. fontSize: 16,
  589. textColor: Colors.white,
  590. isFontMedium: true,
  591. ),
  592. ],
  593. ),
  594. ).onTap(() {
  595. RewardsConfirmPage.startInstance(id: detailInfo?.id);
  596. }),
  597. ),
  598. ],
  599. ),
  600. )
  601. ],
  602. )),
  603. );
  604. }
  605. }