member_page.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  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 Stack(
  379. children: [
  380. IgnorePointer(
  381. child: Container(
  382. color: ColorName.colorPrimary.withOpacity(controller.toolBarOpacity),
  383. child: SafeArea(
  384. child: SizedBox(
  385. width: double.infinity,
  386. height: 56.w,
  387. ),
  388. ),
  389. ),
  390. ),
  391. SafeArea(
  392. child: SizedBox(
  393. width: double.infinity,
  394. height: 56.w,
  395. child: Stack(alignment: Alignment.center, children: [
  396. Positioned(
  397. left: 12.w,
  398. child: GestureDetector(
  399. onTap: () => controller.onPopBack(),
  400. child: Assets.images.iconWhiteBack
  401. .image(width: 24.w, height: 24.w),
  402. )),
  403. Container(child: buildVerticalSlideshowWidget())
  404. ]),
  405. ),
  406. ),
  407. ],
  408. );
  409. });
  410. }
  411. Widget buildVerticalSlideshowWidget() {
  412. return Container(
  413. width: 220.w,
  414. height: 24.w,
  415. decoration: BoxDecoration(
  416. color: '#1F000000'.color,
  417. borderRadius: BorderRadius.circular(100.w),
  418. ),
  419. child: Center(
  420. child: AnimatedSwitcherWidget(
  421. controller: controller.switcherController)),
  422. );
  423. }
  424. Widget buildPrivacyPolicyView() {
  425. return Padding(
  426. padding: EdgeInsets.only(left: 12.w),
  427. child: RichText(
  428. text: TextSpan(
  429. style: TextStyle(fontSize: 12.sp, color: ColorName.black40),
  430. children: [
  431. TextSpan(text: '购买前请先阅读'),
  432. TextSpan(
  433. recognizer: TapGestureRecognizer()
  434. ..onTap = () {
  435. controller.onPrivacyPolicyClick();
  436. },
  437. text: '隐私政策',
  438. style: TextStyle(
  439. color: ColorName.black60,
  440. decoration: TextDecoration.underline)),
  441. TextSpan(text: '&'),
  442. TextSpan(
  443. recognizer: TapGestureRecognizer()
  444. ..onTap = () {
  445. controller.onTermOfServiceClick();
  446. },
  447. text: '服务条款',
  448. style: TextStyle(
  449. color: ColorName.black60,
  450. decoration: TextDecoration.underline)),
  451. ])),
  452. );
  453. }
  454. Widget buildFunctionList() {
  455. return SizedBox(
  456. height: 80.w,
  457. child: AutoScrollListView(
  458. padding: EdgeInsets.only(left: 12.w),
  459. itemBuilder: (ctx, index) {
  460. final item = controller.funList[index];
  461. return Padding(
  462. padding: EdgeInsets.only(right: 20.w),
  463. child: Column(
  464. children: [
  465. Image.asset(item.iconPath, width: 36.w, height: 36.w),
  466. Spacer(flex: 3),
  467. Text(item.funName,
  468. style: TextStyle(
  469. fontSize: 12.8.sp,
  470. color: ColorName.black90,
  471. fontWeight: FontWeight.bold)),
  472. Spacer(flex: 2),
  473. Text(item.funDesc,
  474. style: TextStyle(
  475. fontSize: 10.6.sp,
  476. color: ColorName.black50,
  477. )),
  478. ],
  479. ),
  480. );
  481. },
  482. itemCount: controller.funList.length));
  483. }
  484. Widget buildUserEvaluateList() {
  485. return Column(
  486. children: [
  487. for (int index = 0; index < controller.evaluateList.length; index++)
  488. buildUserEvaluateItem(controller.evaluateList[index],
  489. index == controller.evaluateList.length - 1)
  490. ],
  491. );
  492. }
  493. Widget buildUserEvaluateItem(MemberEvaluateBean item, bool isLast) {
  494. return Column(
  495. crossAxisAlignment: CrossAxisAlignment.start,
  496. children: [
  497. SizedBox(height: 20.w),
  498. Row(
  499. children: [
  500. SizedBox(width: 12.w),
  501. Image.asset(item.avatarPath, width: 24.w, height: 24.w),
  502. SizedBox(width: 8.w),
  503. Text(
  504. item.userName,
  505. style: TextStyle(fontSize: 14.sp, color: Colors.black),
  506. )
  507. ],
  508. ),
  509. SizedBox(height: 1.w),
  510. Padding(
  511. padding: EdgeInsets.only(left: 44.w, right: 12.w),
  512. child: Text(
  513. item.userEvaluate,
  514. style: TextStyle(fontSize: 14.sp, color: '#BF000000'.color),
  515. )),
  516. SizedBox(height: 20.w),
  517. Visibility(
  518. child: Container(
  519. margin: EdgeInsets.only(left: 26.w),
  520. width: 288.w,
  521. color: '#21000000'.color,
  522. height: 1.w),
  523. )
  524. ],
  525. );
  526. }
  527. Widget buildPayWayView() {
  528. return Obx(() {
  529. return Visibility(
  530. visible: controller.payItemList.isNotEmpty,
  531. child: Container(
  532. margin: EdgeInsets.only(top: 7.w),
  533. child: Column(
  534. children: [
  535. for (PayItemBean item in controller.payItemList)
  536. buildPayWayItem(item)
  537. ],
  538. ),
  539. ),
  540. );
  541. });
  542. }
  543. Widget buildPayWayItem(PayItemBean item) {
  544. return GestureDetector(
  545. behavior: HitTestBehavior.translucent,
  546. onTap: () => controller.onPayWayItemClick(item),
  547. child: Obx(() {
  548. bool isSelected = controller.selectedPayWay?.id == item.id;
  549. return Container(
  550. padding: EdgeInsets.symmetric(vertical: 10.w),
  551. child: Row(
  552. children: [
  553. SizedBox(width: 12.w),
  554. Image.asset(
  555. getPaymentIconPath(
  556. payMethod: item.payMethod, payPlatform: item.payPlatform),
  557. width: 24.w,
  558. height: 24.w),
  559. SizedBox(width: 6.w),
  560. Text(item.title,
  561. style: TextStyle(fontSize: 14.sp, color: ColorName.black90)),
  562. Spacer(),
  563. Image.asset(
  564. isSelected
  565. ? Assets.images.iconCbSelected.path
  566. : Assets.images.iconCbUnSelect.path,
  567. width: 20.w,
  568. height: 20.w),
  569. SizedBox(width: 20.w),
  570. ],
  571. ),
  572. );
  573. }),
  574. );
  575. }
  576. }