rewards_search_page.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. import 'package:cpt_rewards/modules/rewards_detail/rewards_detail_page.dart';
  2. import 'package:cs_resources/theme/app_colors_theme.dart';
  3. import 'package:cs_resources/theme/theme_config.dart';
  4. import 'package:domain/entity/rewards_search_entity.dart';
  5. import 'package:flutter/cupertino.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:auto_route/auto_route.dart';
  8. import 'package:flutter_hooks/flutter_hooks.dart';
  9. import 'package:hooks_riverpod/hooks_riverpod.dart';
  10. import 'package:router/ext/auto_router_extensions.dart';
  11. import 'package:shared/utils/log_utils.dart';
  12. import 'package:shared/utils/color_utils.dart';
  13. import 'package:widgets/ext/ex_widget.dart';
  14. import 'package:widgets/load_state_layout.dart';
  15. import 'package:widgets/my_appbar.dart';
  16. import 'package:widgets/my_load_image.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_search_vm.dart';
  22. @RoutePage()
  23. class RewardsSearchPage extends HookConsumerWidget {
  24. const RewardsSearchPage({Key? key}) : super(key: key);
  25. //启动当前页面
  26. static void startInstance({BuildContext? context}) {
  27. if (context != null) {
  28. context.router.push(const RewardsSearchPageRoute());
  29. } else {
  30. appRouter.push(const RewardsSearchPageRoute());
  31. }
  32. }
  33. Widget _buildItemLeftSection(BuildContext context, WidgetRef ref, item, _vm) {
  34. int point = item!['point'] ?? 0;
  35. int originalPoint = item!['original_point'] ?? 0;
  36. int id = item!['id'];
  37. return Column(
  38. children: [
  39. Column(
  40. crossAxisAlignment: CrossAxisAlignment.start,
  41. children: [
  42. MyLoadImage(
  43. item?['resources']?[0] ?? '',
  44. width: MediaQuery.of(context).size.width,
  45. height: 150,
  46. ),
  47. Text(
  48. maxLines: 1, // 设置最大行数为2
  49. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  50. item?['name'] ?? '',
  51. style: const TextStyle(
  52. fontSize: 16.0,
  53. color: Colors.black,
  54. fontWeight: FontWeight.w500),
  55. ).marginOnly(bottom: 5, top: 10),
  56. Row(
  57. children: [
  58. Text(
  59. '$point',
  60. style: const TextStyle(
  61. fontSize: 19.0,
  62. color: Colors.black,
  63. fontWeight: FontWeight.w500),
  64. ),
  65. Text(
  66. '$originalPoint',
  67. style: TextStyle(
  68. decoration: TextDecoration.lineThrough,
  69. decorationColor: ColorUtils.string2Color('#000000'),
  70. decorationStyle: TextDecorationStyle.solid,
  71. fontSize: 12.0,
  72. color: ColorUtils.string2Color('#000000'),
  73. fontWeight: FontWeight.w400),
  74. ).marginOnly(left: 5, right: 5),
  75. const Text(
  76. 'Points',
  77. style: TextStyle(
  78. fontSize: 13.0,
  79. color: Colors.black,
  80. fontWeight: FontWeight.w400),
  81. ),
  82. ],
  83. ),
  84. ],
  85. ).onTap(() {
  86. RewardsDetailPage.startInstance(id: id);
  87. }),
  88. ],
  89. ).paddingOnly(top: 0);
  90. }
  91. // listitem
  92. Widget _buildSaleItem(BuildContext context, WidgetRef ref, _vm, item) {
  93. return Container(
  94. decoration: const BoxDecoration(
  95. color: Colors.white,
  96. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  97. boxShadow: [
  98. BoxShadow(color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  99. ],
  100. ),
  101. child: Column(
  102. children: [
  103. SizedBox(
  104. width: MediaQuery.of(context).size.width - 30,
  105. height: 235,
  106. // margin: const EdgeInsets.only(left: 15, right: 15, top: 12.5),
  107. child: Column(
  108. crossAxisAlignment: CrossAxisAlignment.start,
  109. mainAxisAlignment: MainAxisAlignment.start,
  110. children: [
  111. _buildItemLeftSection(context, ref, item, _vm)
  112. .marginOnly(bottom: 5),
  113. ],
  114. ).paddingOnly(left: 20, right: 20),
  115. ).constrained(
  116. minHeight: 235,
  117. ),
  118. ],
  119. ),
  120. ).marginOnly(left: 15, bottom: 15, right: 15);
  121. }
  122. // SearchOf
  123. Widget _buildSearchOf(BuildContext context, WidgetRef ref, _vm, detailInfo) {
  124. List recent = detailInfo.recent;
  125. List trending = detailInfo.trending;
  126. List<RewardsSearchRewards> rewards = detailInfo.rewards;
  127. return Column(
  128. crossAxisAlignment: CrossAxisAlignment.start,
  129. mainAxisAlignment: MainAxisAlignment.start,
  130. children: [
  131. recent.length > 0
  132. ? _buildSearchRecent(context, ref, _vm, recent)
  133. : Container(),
  134. trending.length > 0
  135. ? _buildSearchTrending(context, ref, _vm, trending)
  136. : Container(),
  137. rewards.length > 0
  138. ? _buildSearchRecently(context, ref, _vm, rewards)
  139. : Container(),
  140. ],
  141. );
  142. }
  143. // Recent
  144. Widget _buildSearchRecent(BuildContext context, WidgetRef ref, _vm, recent) {
  145. // List itemsList = _vm.state.list.toList();
  146. return Column(
  147. crossAxisAlignment: CrossAxisAlignment.start,
  148. mainAxisAlignment: MainAxisAlignment.start,
  149. children: [
  150. Text(
  151. 'Recent Searches',
  152. style: TextStyle(
  153. fontSize: 16.0,
  154. color: ColorUtils.string2Color('#000000'),
  155. fontWeight: FontWeight.w500),
  156. ).marginOnly(bottom: 20),
  157. Wrap(
  158. direction: Axis.horizontal, // 水平方向排列
  159. spacing: 10.0, // 子组件之间的间距
  160. runSpacing: 15.0, // 子组件行与行之间的间距
  161. children: List.generate(recent.length, (index) {
  162. return _buildSearchRecentItem(context, ref, _vm, recent[index]);
  163. }))
  164. ],
  165. ).marginOnly(bottom: 20);
  166. }
  167. Widget _buildSearchRecentItem(
  168. BuildContext context, WidgetRef ref, _vm, recent) {
  169. return Container(
  170. decoration: const BoxDecoration(
  171. color: Colors.white,
  172. borderRadius: BorderRadius.all(Radius.circular(3.0)),
  173. ),
  174. padding: const EdgeInsets.all(10),
  175. child: Text(
  176. recent,
  177. style: TextStyle(
  178. fontSize: 14.0,
  179. color: ColorUtils.string2Color('#969696'),
  180. fontWeight: FontWeight.w400),
  181. ),
  182. ).onTap(() {
  183. _vm.searchIn(recent);
  184. });
  185. }
  186. // Trending
  187. Widget _buildSearchTrending(
  188. BuildContext context, WidgetRef ref, _vm, trending) {
  189. // List itemsList = _vm.state.list.toList();
  190. return Column(
  191. crossAxisAlignment: CrossAxisAlignment.start,
  192. mainAxisAlignment: MainAxisAlignment.start,
  193. children: [
  194. Text(
  195. 'Trending Searches',
  196. style: TextStyle(
  197. fontSize: 16.0,
  198. color: ColorUtils.string2Color('#000000'),
  199. fontWeight: FontWeight.w500),
  200. ).marginOnly(bottom: 20),
  201. Wrap(
  202. direction: Axis.horizontal, // 水平方向排列
  203. spacing: 10.0, // 子组件之间的间距
  204. runSpacing: 15.0, // 子组件行与行之间的间距
  205. children: List.generate(trending.length, (index) {
  206. return _buildSearchTrendingItem(
  207. context, ref, _vm, trending[index]);
  208. }))
  209. ],
  210. ).marginOnly(bottom: 20);
  211. }
  212. Widget _buildSearchTrendingItem(
  213. BuildContext context, WidgetRef ref, _vm, trending) {
  214. return Container(
  215. decoration: const BoxDecoration(
  216. color: Colors.white,
  217. borderRadius: BorderRadius.all(Radius.circular(3.0)),
  218. ),
  219. padding: const EdgeInsets.all(10),
  220. child: Row(
  221. mainAxisSize: MainAxisSize.min,
  222. children: [
  223. const MyAssetImage(
  224. Assets.rewardsRewardsHost,
  225. width: 12,
  226. height: 14,
  227. ).marginOnly(right: 5),
  228. Text(
  229. trending,
  230. style: TextStyle(
  231. fontSize: 14.0,
  232. color: ColorUtils.string2Color('#969696'),
  233. fontWeight: FontWeight.w400),
  234. ),
  235. ],
  236. ),
  237. ).onTap(() {
  238. _vm.searchIn(trending);
  239. });
  240. }
  241. // Recently
  242. Widget _buildSearchRecently(
  243. BuildContext context, WidgetRef ref, _vm, rewards) {
  244. // List itemsList = _vm.state.list.toList();
  245. return Column(
  246. crossAxisAlignment: CrossAxisAlignment.start,
  247. mainAxisAlignment: MainAxisAlignment.start,
  248. children: [
  249. Text(
  250. 'Recently viewed',
  251. style: TextStyle(
  252. fontSize: 16.0,
  253. color: ColorUtils.string2Color('#000000'),
  254. fontWeight: FontWeight.w500),
  255. ).marginOnly(bottom: 18),
  256. Column(
  257. crossAxisAlignment: CrossAxisAlignment.center,
  258. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  259. children: List.generate(rewards.length, (index) {
  260. return _buildSearchItem(context, ref, _vm, rewards[index]);
  261. }))
  262. ],
  263. );
  264. }
  265. Widget _buildSearchItem(BuildContext context, WidgetRef ref, _vm, item) {
  266. RewardsSearchRewards itm = item;
  267. int point = itm.point ?? 0;
  268. int originalPoint = itm.originalPoint ?? 0;
  269. int id = itm.id;
  270. return Container(
  271. // width: MediaQuery.of(context).size.width / 2 - 25,
  272. // height: 240,
  273. padding: const EdgeInsets.only(bottom: 20),
  274. decoration: const BoxDecoration(
  275. color: Colors.white,
  276. borderRadius: BorderRadius.all(Radius.circular(6.0)),
  277. boxShadow: [
  278. BoxShadow(color: Color.fromRGBO(184, 191, 217, 0.3), blurRadius: 6)
  279. ],
  280. ),
  281. child: Column(
  282. children: [
  283. MyLoadImage(
  284. item.resources?[0] ?? '',
  285. width: MediaQuery.of(context).size.width,
  286. height: 150,
  287. ),
  288. Column(
  289. crossAxisAlignment: CrossAxisAlignment.start,
  290. children: [
  291. Text(
  292. maxLines: 1, // 设置最大行数为2
  293. overflow: TextOverflow.ellipsis, // 超出部分用省略号表示
  294. item.name ?? '',
  295. style: const TextStyle(
  296. fontSize: 15.0,
  297. color: Colors.black,
  298. fontWeight: FontWeight.w500),
  299. ).marginOnly(bottom: 5, top: 10),
  300. Row(
  301. children: [
  302. Text(
  303. '$point',
  304. style: const TextStyle(
  305. fontSize: 18.0,
  306. color: Colors.black,
  307. fontWeight: FontWeight.w500),
  308. ),
  309. Text(
  310. '$originalPoint',
  311. style: TextStyle(
  312. decoration: TextDecoration.lineThrough,
  313. decorationColor: ColorUtils.string2Color('#808DAF'),
  314. decorationStyle: TextDecorationStyle.solid,
  315. fontSize: 13.0,
  316. color: ColorUtils.string2Color('#808DAF'),
  317. fontWeight: FontWeight.w400),
  318. ).marginOnly(left: 5, right: 5),
  319. const Text(
  320. 'Points',
  321. style: TextStyle(
  322. fontSize: 13.0,
  323. color: Colors.black,
  324. fontWeight: FontWeight.w400),
  325. ),
  326. ],
  327. )
  328. ],
  329. ).paddingOnly(top: 12, left: 12, right: 12)
  330. ],
  331. )).marginOnly(bottom: 13).onTap(() {
  332. RewardsDetailPage.startInstance(id: id);
  333. });
  334. }
  335. // list
  336. Widget _buildSaleList(BuildContext context, WidgetRef ref, _vm) {
  337. final state = ref.watch(rewardsSearchVmProvider);
  338. return SliverList(
  339. delegate: SliverChildBuilderDelegate((context, index) {
  340. return _buildSaleItem(context, ref, _vm, state.list![index]);
  341. }, childCount: state.list!.length));
  342. }
  343. @override
  344. Widget build(BuildContext context, WidgetRef ref) {
  345. final _vm = ref.read(rewardsSearchVmProvider.notifier);
  346. final state = ref.watch(rewardsSearchVmProvider);
  347. bool searchIs = state.searchIs ?? true;
  348. RewardsSearchEntity? detailInfo = state.detailInfo;
  349. useEffect(() {
  350. // 组件挂载时执行 - 执行接口请求
  351. Future.microtask(() => _vm.initPageData());
  352. return () {
  353. // 组件卸载时执行
  354. Log.d("property_news_page 组件卸载时执行");
  355. };
  356. }, []);
  357. return Scaffold(
  358. appBar: MyAppBar.searchAppBar(context, onSearch: (value) {
  359. _vm.searchIn(value);
  360. },
  361. value: state.keyword,
  362. backgroundColor: Colors.white,
  363. systemUiOverlayStyle:
  364. ThemeConfig.systemUiOverlayStyleLightThemeBlack),
  365. body: searchIs
  366. ? LoadStateLayout(
  367. state: state.loadingState,
  368. errorMessage: state.errorMessage,
  369. errorRetry: () {
  370. _vm.retryRequest();
  371. },
  372. successWidget: SingleChildScrollView(
  373. scrollDirection: Axis.vertical,
  374. physics: const BouncingScrollPhysics(),
  375. clipBehavior: Clip.none,
  376. child: Container(
  377. width: MediaQuery.of(context).size.width,
  378. color: ColorUtils.string2Color('#F2F3F6'),
  379. padding:
  380. const EdgeInsets.only(top: 20, left: 15, right: 15),
  381. child: _buildSearchOf(context, ref, _vm, detailInfo)),
  382. ))
  383. : Container(
  384. child: Column(
  385. children: [
  386. Expanded(
  387. child: EasyRefresh(
  388. controller: _vm.refreshController,
  389. // 上拉加载
  390. onLoad: () async {
  391. Log.d("----onLoad");
  392. _vm.loadMore();
  393. },
  394. // 下拉刷新
  395. onRefresh: () async {
  396. Log.d("----onRefresh");
  397. _vm.onRefresh();
  398. },
  399. child: Container(
  400. color: ColorUtils.string2Color('#F2F3F6'),
  401. padding: const EdgeInsets.only(top: 15),
  402. child: LoadStateLayout(
  403. state: state.loadingState,
  404. errorMessage: state.errorMessage,
  405. errorRetry: () {
  406. _vm.retryRequest();
  407. },
  408. successSliverWidget: [
  409. _buildSaleList(context, ref, _vm)
  410. ])),
  411. ),
  412. )
  413. ],
  414. ),
  415. ),
  416. );
  417. }
  418. }