member_page.dart 23 KB

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