rewards_search_page.dart 15 KB

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