member_page.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/gestures.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/src/widgets/framework.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.dart';
  6. import 'package:get/get.dart';
  7. import 'package:get/get_core/src/get_main.dart';
  8. import 'package:location/base/base_page.dart';
  9. import 'package:location/module/member/member_controller.dart';
  10. import 'package:location/resource/assets.gen.dart';
  11. import 'package:location/resource/colors.gen.dart';
  12. import 'package:location/utils/common_expand.dart';
  13. import 'package:location/utils/project_expand.dart';
  14. import 'package:location/widget/auto_scroll_list_view.dart';
  15. import '../../data/bean/member_status_info.dart';
  16. import '../../data/bean/pay_item_bean.dart';
  17. import '../../data/consts/payment_type.dart';
  18. import '../../resource/fonts.gen.dart';
  19. import '../../resource/string.gen.dart';
  20. import '../../router/app_pages.dart';
  21. import '../../utils/common_util.dart';
  22. import '../../utils/date_util.dart';
  23. import '../../widget/animated_switcher_widget.dart';
  24. import 'member_evaluate_bean.dart';
  25. class MemberPage extends BasePage<MemberController> {
  26. const MemberPage({super.key});
  27. static void start() {
  28. Get.toNamed(RoutePath.member);
  29. }
  30. @override
  31. bool immersive() {
  32. return true;
  33. }
  34. @override
  35. bool statusBarDarkFont() {
  36. return false;
  37. }
  38. @override
  39. Widget buildBody(BuildContext context) {
  40. return PopScope(
  41. canPop: false,
  42. onPopInvokedWithResult: (bool didPop, dynamic result) async {
  43. controller.onPopBack();
  44. },
  45. child: Stack(
  46. children: [
  47. SingleChildScrollView(
  48. controller: controller.scrollController,
  49. child: Stack(
  50. children: [
  51. Assets.images.bgMemberHeader.image(width: double.infinity),
  52. SafeArea(
  53. child: Column(
  54. children: [
  55. SizedBox(height: 62.w),
  56. buildUserInfoView(),
  57. SizedBox(height: 26.w),
  58. Container(
  59. width: double.infinity,
  60. decoration: BoxDecoration(
  61. borderRadius: BorderRadius.only(
  62. topLeft: Radius.circular(14.w),
  63. topRight: Radius.circular(14.w)),
  64. gradient: LinearGradient(
  65. begin: Alignment.topCenter,
  66. end: Alignment.bottomCenter,
  67. stops: [0.0, 0.1],
  68. colors: ['#EFE9FF'.color, Colors.white])),
  69. child: Column(
  70. crossAxisAlignment: CrossAxisAlignment.start,
  71. children: [
  72. SizedBox(height: 23.w),
  73. buildGoodsList(),
  74. SizedBox(height: 12.w),
  75. buildPrivacyPolicyView(),
  76. buildPayWayView(),
  77. SizedBox(height: 30.w),
  78. Padding(
  79. padding: EdgeInsets.only(left: 12.w),
  80. child: Text(StringName.memberEquityIntroduction,
  81. style: TextStyle(
  82. fontSize: 16.sp,
  83. color: ColorName.black90,
  84. fontWeight: FontWeight.bold)),
  85. ),
  86. SizedBox(height: 19.w),
  87. buildFunctionList(),
  88. SizedBox(height: 40.w),
  89. Padding(
  90. padding: EdgeInsets.only(left: 12.w),
  91. child: Text(StringName.memberUserEvaluate,
  92. style: TextStyle(
  93. fontSize: 16.sp,
  94. color: ColorName.black90,
  95. fontWeight: FontWeight.bold)),
  96. ),
  97. SizedBox(height: 8.w),
  98. buildUserEvaluateList(),
  99. SizedBox(height: 20.w),
  100. Container(
  101. padding: EdgeInsets.all(12.w),
  102. decoration: BoxDecoration(
  103. color: '#F7F7F7'.color,
  104. borderRadius: BorderRadius.circular(6.w)),
  105. margin: EdgeInsets.symmetric(horizontal: 12.w),
  106. child: Text(StringName.memberTips,
  107. style: TextStyle(
  108. fontSize: 12.sp, color: '#A7A7A7'.color)),
  109. ),
  110. SizedBox(height: 100.w)
  111. ],
  112. ),
  113. )
  114. ],
  115. ),
  116. )
  117. ],
  118. ),
  119. ),
  120. buildHeadBar(),
  121. buildMemberBottomView()
  122. ],
  123. ),
  124. );
  125. }
  126. Widget buildGoodsList() {
  127. return Obx(() {
  128. return SizedBox(
  129. height: 123.w,
  130. child: ListView.builder(
  131. padding: EdgeInsets.only(left: 12.w),
  132. physics: const BouncingScrollPhysics(
  133. parent: AlwaysScrollableScrollPhysics()),
  134. scrollDirection: Axis.horizontal,
  135. itemBuilder: (BuildContext ctx, int index) {
  136. return Obx(() {
  137. final item = controller.goodsList[index];
  138. bool isSelected = controller.selectedGoods?.id == item.id;
  139. return GestureDetector(
  140. behavior: HitTestBehavior.translucent,
  141. onTap: () => controller.onGoodsItemClick(item),
  142. child: Container(
  143. margin: EdgeInsets.only(right: 10.w),
  144. width: 138.w,
  145. height: 123.w,
  146. child: Stack(
  147. children: [
  148. Container(
  149. width: double.infinity,
  150. height: double.infinity,
  151. decoration: BoxDecoration(
  152. color: isSelected
  153. ? '#6BC4BAFF'.color
  154. : ColorName.white20,
  155. borderRadius: BorderRadius.circular(18.w),
  156. border: Border.all(
  157. color: isSelected
  158. ? '#C4BAFF'.color
  159. : '#E8E8E8'.color,
  160. width: 3.w)),
  161. padding: EdgeInsets.only(left: 10.w),
  162. child: Column(
  163. crossAxisAlignment: CrossAxisAlignment.start,
  164. children: [
  165. Spacer(flex: 2),
  166. Text(
  167. item.name,
  168. style: TextStyle(
  169. fontSize: 14.sp,
  170. color: ColorName.black90,
  171. fontWeight: FontWeight.bold),
  172. ),
  173. SizedBox(height: 3.w),
  174. Text('¥${item.originalAmount.divideBy100()}',
  175. style: TextStyle(
  176. decoration: TextDecoration.lineThrough,
  177. fontSize: 10.sp,
  178. color: ColorName.black60)),
  179. Spacer(flex: 1),
  180. RichText(
  181. text: TextSpan(
  182. style: TextStyle(
  183. color: isSelected
  184. ? '#EA1231'.color
  185. : ColorName.black80,
  186. fontWeight: FontWeight.bold),
  187. children: [
  188. TextSpan(
  189. text: '¥',
  190. style: TextStyle(
  191. fontSize: 20.sp, height: 1)),
  192. TextSpan(
  193. text: item.amount.divideBy100(),
  194. style: TextStyle(
  195. fontSize: 34.sp,
  196. height: 1,
  197. fontFamily: FontFamily.oppoSans))
  198. ])),
  199. Padding(
  200. padding: EdgeInsets.only(left: 7.w),
  201. child: Text(item.description ?? '',
  202. style: TextStyle(
  203. fontSize: 12.sp,
  204. color: ColorName.black40)),
  205. ),
  206. Spacer(
  207. flex: 1,
  208. )
  209. ],
  210. ),
  211. ),
  212. Visibility(
  213. visible: item.tag?.isNotEmpty == true,
  214. child: Positioned(
  215. top: 0,
  216. right: 0,
  217. child: Container(
  218. decoration: BoxDecoration(
  219. gradient: LinearGradient(colors: [
  220. '#A26CFF'.color,
  221. '#FF7CD8'.color,
  222. '#898BFF'.color
  223. ]),
  224. borderRadius: BorderRadius.only(
  225. topRight: Radius.circular(11.w),
  226. bottomLeft: Radius.circular(11.w))),
  227. padding: EdgeInsets.symmetric(
  228. horizontal: 10.w, vertical: 4.w),
  229. child: Text(item.tag ?? '',
  230. style: TextStyle(
  231. fontSize: 12.sp,
  232. color: Colors.white)),
  233. ),
  234. ))
  235. ],
  236. )),
  237. );
  238. });
  239. },
  240. itemCount: controller.goodsList.length),
  241. );
  242. });
  243. }
  244. Widget buildMemberBottomView() {
  245. return Obx(() {
  246. return Visibility(
  247. visible: controller.memberStatusInfo == null ||
  248. controller.memberStatusInfo?.permanent == false,
  249. child: Align(
  250. alignment: Alignment.bottomCenter,
  251. child: Container(
  252. padding: EdgeInsets.only(
  253. left: 12.w, right: 12.w, top: 13.w, bottom: 20.w),
  254. decoration: BoxDecoration(
  255. color: Colors.white,
  256. boxShadow: [
  257. BoxShadow(
  258. color: Colors.black.withOpacity(0.05),
  259. offset: Offset(0, -2),
  260. blurRadius: 4)
  261. ],
  262. borderRadius: BorderRadius.only(
  263. topLeft: Radius.circular(16.w),
  264. topRight: Radius.circular(16.w)),
  265. ),
  266. child: Obx(() {
  267. return Row(
  268. children: [
  269. Text('¥',
  270. style: TextStyle(
  271. fontSize: 14.sp,
  272. color: '#EA1231'.color,
  273. fontWeight: FontWeight.bold)),
  274. Text(
  275. controller.selectedGoods?.amount.divideBy100() ?? '--',
  276. style: TextStyle(
  277. fontSize: 24.sp,
  278. color: '#EA1231'.color,
  279. fontWeight: FontWeight.bold),
  280. ),
  281. Text(
  282. ' / ${controller.selectedGoods?.name ?? '--'}',
  283. style: TextStyle(fontSize: 12.sp, color: '#000000'.color),
  284. ),
  285. Spacer(),
  286. GestureDetector(
  287. onTap: controller.onBuyClick,
  288. child: Container(
  289. decoration: BoxDecoration(
  290. color: ColorName.colorPrimary,
  291. borderRadius: BorderRadius.circular(100.w)),
  292. width: 164.w,
  293. height: 44.w,
  294. child: Center(
  295. child: Text(
  296. controller.memberStatusInfo?.expired == false
  297. ? StringName.memberVipRenew
  298. : StringName.memberVipUnlock,
  299. style: TextStyle(
  300. fontSize: 15.sp,
  301. color: Colors.white,
  302. fontWeight: FontWeight.bold),
  303. ),
  304. ),
  305. ),
  306. )
  307. ],
  308. );
  309. }),
  310. ),
  311. ),
  312. );
  313. });
  314. }
  315. Widget buildUserInfoView() {
  316. return Row(
  317. children: [
  318. SizedBox(width: 20.w),
  319. Assets.images.iconMemberAvatar.image(width: 40.w, height: 40.w),
  320. SizedBox(width: 10.w),
  321. Column(
  322. crossAxisAlignment: CrossAxisAlignment.start,
  323. children: [
  324. Obx(() {
  325. return GestureDetector(
  326. onTap: controller.onLoginClick,
  327. child: Text(
  328. controller.phone?.isNotEmpty == true
  329. ? controller.getUserName(controller.phone!)
  330. : StringName.mineAccountGoLogin,
  331. style: TextStyle(
  332. fontSize: 16.sp,
  333. color: Colors.white,
  334. fontWeight: FontWeight.bold)),
  335. );
  336. }),
  337. buildMemberCardVipDesc()
  338. ],
  339. ),
  340. Spacer(),
  341. Container(
  342. decoration: BoxDecoration(
  343. color: '#272F51'.color,
  344. border: Border.all(color: '#99CAB0F0'.color, width: 1.w),
  345. borderRadius: BorderRadius.circular(100.w),
  346. ),
  347. padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 6.w),
  348. child: Obx(() {
  349. return Text(
  350. MemberStatusInfo.getLevelDesc(controller.memberStatusInfo),
  351. style: TextStyle(fontSize: 12.sp, color: '#D2CCFF'.color));
  352. })),
  353. SizedBox(width: 18.w)
  354. ],
  355. );
  356. }
  357. Widget buildMemberCardVipDesc() {
  358. return Obx(() {
  359. String desc = '';
  360. if (!controller.isLogin) {
  361. desc = StringName.memberCardNoLoginDesc;
  362. } else if (controller.memberStatusInfo == null ||
  363. controller.memberStatusInfo?.expired == true) {
  364. desc = StringName.memberCardNoVipDesc;
  365. } else if (controller.memberStatusInfo?.expired == false &&
  366. controller.memberStatusInfo?.permanent == true) {
  367. desc = StringName.memberCardPermanentVipDesc;
  368. } else {
  369. desc =
  370. '${DateUtil.fromMillisecondsSinceEpoch('yyyy.MM.dd', controller.memberStatusInfo?.endTimestamp ?? 0)} ${StringName.memberCardExpirationDesc}';
  371. }
  372. return Text(desc,
  373. style: TextStyle(fontSize: 12.sp, color: Colors.white60));
  374. });
  375. }
  376. Widget buildHeadBar() {
  377. return Obx(() {
  378. return Container(
  379. color: ColorName.colorPrimary.withOpacity(controller.toolBarOpacity),
  380. child: SafeArea(
  381. child: SizedBox(
  382. width: double.infinity,
  383. height: 56.w,
  384. child: Stack(alignment: Alignment.center, children: [
  385. Positioned(
  386. left: 12.w,
  387. child: GestureDetector(
  388. onTap: () => controller.onPopBack(),
  389. child: Assets.images.iconWhiteBack
  390. .image(width: 24.w, height: 24.w),
  391. )),
  392. Container(child: buildVerticalSlideshowWidget())
  393. ]),
  394. ),
  395. ),
  396. );
  397. });
  398. }
  399. Widget buildVerticalSlideshowWidget() {
  400. return Container(
  401. width: 220.w,
  402. height: 24.w,
  403. decoration: BoxDecoration(
  404. color: '#1F000000'.color,
  405. borderRadius: BorderRadius.circular(100.w),
  406. ),
  407. child: Center(
  408. child: AnimatedSwitcherWidget(
  409. controller: controller.switcherController)),
  410. );
  411. }
  412. Widget buildPrivacyPolicyView() {
  413. return Padding(
  414. padding: EdgeInsets.only(left: 12.w),
  415. child: RichText(
  416. text: TextSpan(
  417. style: TextStyle(fontSize: 12.sp, color: ColorName.black40),
  418. children: [
  419. TextSpan(text: '购买前请先阅读'),
  420. TextSpan(
  421. recognizer: TapGestureRecognizer()
  422. ..onTap = () {
  423. controller.onPrivacyPolicyClick();
  424. },
  425. text: '隐私政策',
  426. style: TextStyle(
  427. color: ColorName.black60,
  428. decoration: TextDecoration.underline)),
  429. TextSpan(text: '&'),
  430. TextSpan(
  431. recognizer: TapGestureRecognizer()
  432. ..onTap = () {
  433. controller.onTermOfServiceClick();
  434. },
  435. text: '服务条款',
  436. style: TextStyle(
  437. color: ColorName.black60,
  438. decoration: TextDecoration.underline)),
  439. ])),
  440. );
  441. }
  442. Widget buildFunctionList() {
  443. return SizedBox(
  444. height: 80.w,
  445. child: AutoScrollListView(
  446. padding: EdgeInsets.only(left: 12.w),
  447. itemBuilder: (ctx, index) {
  448. final item = controller.funList[index];
  449. return Padding(
  450. padding: EdgeInsets.only(right: 20.w),
  451. child: Column(
  452. children: [
  453. Image.asset(item.iconPath, width: 36.w, height: 36.w),
  454. Spacer(flex: 3),
  455. Text(item.funName,
  456. style: TextStyle(
  457. fontSize: 12.8.sp,
  458. color: ColorName.black90,
  459. fontWeight: FontWeight.bold)),
  460. Spacer(flex: 2),
  461. Text(item.funDesc,
  462. style: TextStyle(
  463. fontSize: 10.6.sp,
  464. color: ColorName.black50,
  465. )),
  466. ],
  467. ),
  468. );
  469. },
  470. itemCount: controller.funList.length));
  471. }
  472. Widget buildUserEvaluateList() {
  473. return Column(
  474. children: [
  475. for (int index = 0; index < controller.evaluateList.length; index++)
  476. buildUserEvaluateItem(controller.evaluateList[index],
  477. index == controller.evaluateList.length - 1)
  478. ],
  479. );
  480. }
  481. Widget buildUserEvaluateItem(MemberEvaluateBean item, bool isLast) {
  482. return Column(
  483. crossAxisAlignment: CrossAxisAlignment.start,
  484. children: [
  485. SizedBox(height: 20.w),
  486. Row(
  487. children: [
  488. SizedBox(width: 12.w),
  489. Image.asset(item.avatarPath, width: 24.w, height: 24.w),
  490. SizedBox(width: 8.w),
  491. Text(
  492. item.userName,
  493. style: TextStyle(fontSize: 14.sp, color: Colors.black),
  494. )
  495. ],
  496. ),
  497. SizedBox(height: 1.w),
  498. Padding(
  499. padding: EdgeInsets.only(left: 44.w, right: 12.w),
  500. child: Text(
  501. item.userEvaluate,
  502. style: TextStyle(fontSize: 14.sp, color: '#BF000000'.color),
  503. )),
  504. SizedBox(height: 20.w),
  505. Visibility(
  506. child: Container(
  507. margin: EdgeInsets.only(left: 26.w),
  508. width: 288.w,
  509. color: '#21000000'.color,
  510. height: 1.w),
  511. )
  512. ],
  513. );
  514. }
  515. Widget buildPayWayView() {
  516. return Obx(() {
  517. return Visibility(
  518. visible: controller.payItemList.isNotEmpty,
  519. child: Container(
  520. margin: EdgeInsets.only(top: 7.w),
  521. child: Column(
  522. children: [
  523. for (PayItemBean item in controller.payItemList)
  524. buildPayWayItem(item)
  525. ],
  526. ),
  527. ),
  528. );
  529. });
  530. }
  531. Widget buildPayWayItem(PayItemBean item) {
  532. return GestureDetector(
  533. behavior: HitTestBehavior.translucent,
  534. onTap: () => controller.onPayWayItemClick(item),
  535. child: Obx(() {
  536. bool isSelected = controller.selectedPayWay?.id == item.id;
  537. return Container(
  538. padding: EdgeInsets.symmetric(vertical: 10.w),
  539. child: Row(
  540. children: [
  541. SizedBox(width: 12.w),
  542. Image.asset(
  543. getPaymentIconPath(
  544. payMethod: item.payMethod, payPlatform: item.payPlatform),
  545. width: 24.w,
  546. height: 24.w),
  547. SizedBox(width: 6.w),
  548. Text(item.title,
  549. style: TextStyle(fontSize: 14.sp, color: ColorName.black90)),
  550. Spacer(),
  551. Image.asset(
  552. isSelected
  553. ? Assets.images.iconCbSelected.path
  554. : Assets.images.iconCbUnSelect.path,
  555. width: 20.w,
  556. height: 20.w),
  557. SizedBox(width: 20.w),
  558. ],
  559. ),
  560. );
  561. }),
  562. );
  563. }
  564. }