member_page.dart 19 KB

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