member_page.dart 22 KB

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