member_activity_page.dart 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  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/data/bean/goods_bean.dart';
  11. import 'package:location/module/member/activity/member_activity_banner_widget.dart';
  12. import 'package:location/resource/assets.gen.dart';
  13. import 'package:location/resource/colors.gen.dart';
  14. import 'package:location/resource/string.gen.dart';
  15. import 'package:location/utils/common_expand.dart';
  16. import 'package:location/utils/project_expand.dart';
  17. import '../../../resource/fonts.gen.dart';
  18. import '../../../router/app_pages.dart';
  19. import '../../../widget/activity_countdown_txt_view.dart';
  20. import '../../../widget/shimmer_effect.dart';
  21. import 'member_activity_controller.dart';
  22. class MemberActivityPage extends BasePage<MemberActivityController> {
  23. const MemberActivityPage({super.key});
  24. static void start({bool isOffAll = false}) {
  25. if (isOffAll) {
  26. Get.offAllNamed(RoutePath.memberActivity);
  27. } else {
  28. Get.toNamed(RoutePath.memberActivity);
  29. }
  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) {
  44. if (didPop) {
  45. return;
  46. }
  47. controller.onBack();
  48. },
  49. child: Stack(
  50. children: [
  51. buildActivityContent(),
  52. buildToolbar(),
  53. buildMemberBottomView()
  54. ],
  55. ),
  56. );
  57. }
  58. Widget buildToolbar() {
  59. return SafeArea(
  60. child: SizedBox(
  61. width: double.infinity,
  62. height: 56.w,
  63. child: Stack(
  64. children: [
  65. Positioned(
  66. left: 10,
  67. top: 0,
  68. bottom: 0,
  69. child: GestureDetector(
  70. onTap: controller.onBack,
  71. child: Container(
  72. padding: EdgeInsets.only(
  73. left: 10.w, right: 12.w, top: 10.w, bottom: 10.w),
  74. child: Assets.images.iconMemberActivityClose
  75. .image(width: 8.w)),
  76. ),
  77. ),
  78. Center(
  79. child: Text(
  80. StringName.memberActivityTitle,
  81. style: TextStyle(
  82. fontSize: 18.sp,
  83. color: Colors.white,
  84. fontWeight: FontWeight.bold),
  85. ),
  86. ),
  87. Positioned(
  88. right: 10.w,
  89. top: 0,
  90. bottom: 0,
  91. child: Obx(() {
  92. return Visibility(
  93. visible: controller.accountRepository.isLogin.value &&
  94. Platform.isIOS,
  95. child: GestureDetector(
  96. onTap: controller.onRecoverClick,
  97. child: Row(
  98. children: [
  99. Assets.images.iconAppleRecoverSubscribe
  100. .image(width: 14.w, height: 14.w),
  101. SizedBox(width: 1.w),
  102. Text(StringName.appleRecoverSubscribeTxt,
  103. style: TextStyle(
  104. fontSize: 11.sp,
  105. color: Colors.white,
  106. fontWeight: FontWeight.bold))
  107. ],
  108. ),
  109. ),
  110. );
  111. }),
  112. )
  113. ],
  114. ),
  115. ),
  116. );
  117. }
  118. Widget buildActivityContent() {
  119. return SizedBox(
  120. width: double.infinity,
  121. height: double.infinity,
  122. child: SingleChildScrollView(
  123. child: Stack(
  124. children: [
  125. Assets.images.bgMemberActivity
  126. .image(width: double.infinity, fit: BoxFit.cover),
  127. SafeArea(
  128. child: Column(
  129. crossAxisAlignment: CrossAxisAlignment.start,
  130. children: [
  131. SizedBox(height: 83.w),
  132. Container(
  133. margin: EdgeInsets.only(left: 16.w),
  134. child: Assets.images.iconMemberActivityCountdown
  135. .image(height: 22.w)),
  136. SizedBox(height: 10.w),
  137. Container(
  138. margin: EdgeInsets.only(left: 16.w, bottom: 16.w),
  139. child: Assets.images.imgMemberActivityFavourableTxt
  140. .image(height: 20.w),
  141. ),
  142. buildGoodsContainer(),
  143. ],
  144. ),
  145. )
  146. ],
  147. ),
  148. ),
  149. );
  150. }
  151. Widget buildGoodsContainer() {
  152. return Container(
  153. width: double.infinity,
  154. decoration: BoxDecoration(
  155. gradient: LinearGradient(
  156. stops: const [0.0, 0.16],
  157. colors: ['#E4E4FF'.color, Colors.white],
  158. begin: Alignment.topCenter,
  159. end: Alignment.bottomCenter),
  160. borderRadius: BorderRadius.only(
  161. topLeft: Radius.circular(20.w), topRight: Radius.circular(20.w)),
  162. ),
  163. child: Stack(
  164. children: [
  165. Assets.images.bgMemberActivityContainer
  166. .image(width: double.infinity, fit: BoxFit.fill),
  167. Column(
  168. crossAxisAlignment: CrossAxisAlignment.start,
  169. children: [
  170. SizedBox(height: 14.w),
  171. buildFunTitleView(),
  172. buildMemberActivityFunctionIntroduction(),
  173. SizedBox(height: 8.w),
  174. buildGoodsListView(),
  175. buildPrivacyPolicyView(),
  176. SizedBox(height: 150.w),
  177. ],
  178. )
  179. ],
  180. ),
  181. );
  182. }
  183. Widget buildFunTitleView() {
  184. return Container(
  185. margin: EdgeInsets.only(left: 16.w, bottom: 18.w),
  186. child: Stack(
  187. children: [
  188. Container(
  189. width: 117.w,
  190. height: 8.w,
  191. decoration: BoxDecoration(
  192. gradient: LinearGradient(
  193. colors: ['#AA70FE'.color, '#006A2DC4'.color],
  194. begin: Alignment.centerLeft,
  195. end: Alignment.centerRight,
  196. ),
  197. borderRadius: BorderRadius.only(
  198. topLeft: Radius.circular(10.w),
  199. bottomLeft: Radius.circular(10.w))),
  200. margin: EdgeInsets.only(top: 15.5.w),
  201. ),
  202. Container(
  203. margin: EdgeInsets.only(left: 4.w),
  204. child: Text(
  205. StringName.memberActivityFunTitle,
  206. style: TextStyle(
  207. fontSize: 15.sp,
  208. color: '#202020'.color,
  209. fontWeight: FontWeight.bold),
  210. ),
  211. ),
  212. ],
  213. ),
  214. );
  215. }
  216. Widget buildPrivacyPolicyView() {
  217. return Padding(
  218. padding: EdgeInsets.only(left: 15.w),
  219. child: RichText(
  220. text: TextSpan(
  221. style: TextStyle(fontSize: 12.sp, color: ColorName.black40),
  222. children: [
  223. TextSpan(text: '购买前请先阅读'),
  224. TextSpan(
  225. recognizer: TapGestureRecognizer()
  226. ..onTap = () {
  227. controller.onPrivacyPolicyClick();
  228. },
  229. text: '隐私政策',
  230. style: TextStyle(
  231. color: ColorName.black60,
  232. decoration: TextDecoration.underline)),
  233. TextSpan(text: '&'),
  234. TextSpan(
  235. recognizer: TapGestureRecognizer()
  236. ..onTap = () {
  237. controller.onTermOfServiceClick();
  238. },
  239. text: '服务条款',
  240. style: TextStyle(
  241. color: ColorName.black60,
  242. decoration: TextDecoration.underline)),
  243. if (Platform.isIOS) TextSpan(text: '&'),
  244. if (Platform.isIOS)
  245. TextSpan(
  246. recognizer: TapGestureRecognizer()
  247. ..onTap = () {
  248. controller.onRenewalAgreementClick();
  249. },
  250. text: '会员协议',
  251. style: TextStyle(
  252. color: ColorName.black60,
  253. decoration: TextDecoration.underline)),
  254. ])),
  255. );
  256. }
  257. Widget buildCountdownSeparator() {
  258. return Container(
  259. margin: EdgeInsets.symmetric(horizontal: 2.w),
  260. child: IntrinsicHeight(
  261. child: Column(
  262. children: [
  263. Container(
  264. width: 2.w,
  265. height: 2.w,
  266. decoration: BoxDecoration(
  267. color: '#FF5656'.color,
  268. shape: BoxShape.circle,
  269. ),
  270. ),
  271. SizedBox(height: 3.w),
  272. Container(
  273. width: 2.w,
  274. height: 2.w,
  275. decoration: BoxDecoration(
  276. color: '#FF5656'.color,
  277. shape: BoxShape.circle,
  278. ),
  279. )
  280. ],
  281. ),
  282. ),
  283. );
  284. }
  285. Widget buildMemberBottomView() {
  286. return Obx(() {
  287. if (controller.memberStatusInfo != null &&
  288. controller.memberStatusInfo?.permanent == true) {
  289. return SizedBox(); // 不显示
  290. }
  291. return Align(
  292. alignment: Alignment.bottomCenter,
  293. child: Container(
  294. margin: EdgeInsets.only(bottom: 15.w, left: 12.w, right: 12.w),
  295. child: Stack(
  296. children: [
  297. Container(
  298. width: 336.w,
  299. height: 47.w,
  300. decoration: BoxDecoration(
  301. borderRadius: BorderRadius.only(
  302. topLeft: Radius.circular(30.w),
  303. topRight: Radius.circular(30.w),
  304. ),
  305. color: '#FFFED8'.color),
  306. child: Align(
  307. alignment: Alignment(0.0, -0.75),
  308. child: Row(
  309. mainAxisAlignment: MainAxisAlignment.center,
  310. children: [
  311. Text(
  312. StringName.memberActivityCountdown,
  313. style: TextStyle(
  314. fontSize: 11.sp, color: '#FF5656'.color),
  315. ),
  316. SizedBox(width: 4.w),
  317. Obx(() {
  318. return ActivityCountdownTextView(
  319. timeItemHeight: 15.w,
  320. contentPadding:
  321. EdgeInsets.symmetric(horizontal: 2.w),
  322. textStyle: TextStyle(
  323. fontSize: 10.sp, color: Colors.white),
  324. duration: controller.activityDuration ??
  325. Duration(seconds: 0),
  326. separator: buildCountdownSeparator(),
  327. timeBgBoxDecoration: BoxDecoration(
  328. color: '#FF5656'.color,
  329. borderRadius: BorderRadius.circular(3.w),
  330. ));
  331. }),
  332. SizedBox(width: 4.w),
  333. Text(
  334. StringName.memberActivitySpeciallyPreferential,
  335. style: TextStyle(
  336. fontSize: 10.sp, color: '#FF5656'.color),
  337. )
  338. ],
  339. ),
  340. ),
  341. ),
  342. GestureDetector(
  343. onTap: controller.onBuyClick,
  344. child: Container(
  345. margin: EdgeInsets.only(top: 24.w),
  346. height: 50.w,
  347. width: 336.w,
  348. child: ShimmerEffect(
  349. image: Assets.images.imgMemberBtnShadow.provider(),
  350. shimmerWidthFactor: 0.244047619047619,
  351. duration: Duration(milliseconds: 3000),
  352. delay: Duration(milliseconds: 800),
  353. child: Container(
  354. height: 50.w,
  355. width: 336.w,
  356. alignment: Alignment.center,
  357. decoration: BoxDecoration(
  358. image: DecorationImage(
  359. image: Assets.images.iconMemberSettlementBg
  360. .provider(),
  361. fit: BoxFit.fill)),
  362. child: Text(
  363. StringName.memberVipUnlock,
  364. style: TextStyle(
  365. fontSize: 18.sp,
  366. color: '#FFF8EF'.color,
  367. fontWeight: FontWeight.bold),
  368. ),
  369. ),
  370. ),
  371. ),
  372. )
  373. ],
  374. ),
  375. ));
  376. });
  377. }
  378. Widget buildMemberActivityFunctionIntroduction() {
  379. return MemberActivityBannerWidget(
  380. bannerHeight: 145.w,
  381. viewportFraction: 0.88,
  382. itemCount: controller.funImages.length,
  383. itemBuilder: (context, index) {
  384. final image = controller.funImages[index];
  385. return Container(
  386. margin: EdgeInsets.symmetric(horizontal: 5.w),
  387. child: Image(
  388. image: image,
  389. width: double.infinity,
  390. height: double.infinity,
  391. fit: BoxFit.fill),
  392. );
  393. });
  394. }
  395. Widget buildGoodsListView() {
  396. return IntrinsicHeight(
  397. child: Obx(() {
  398. return Column(
  399. children: [
  400. for (int i = 0; i < controller.goodsList.length; i++)
  401. if (i == 0)
  402. buildFavourableGoodsView(controller.goodsList[i])
  403. else
  404. buildNormalGoodsView(controller.goodsList[i])
  405. ],
  406. );
  407. }),
  408. );
  409. }
  410. Widget buildFavourableGoodsView(GoodsBean goodsInfo) {
  411. bool isSelected = controller.selectedGoods?.id == goodsInfo.id;
  412. return GestureDetector(
  413. onTap: () {
  414. controller.onGoodsItemClick(goodsInfo);
  415. },
  416. child: Container(
  417. height: 96.w,
  418. width: double.infinity,
  419. margin: EdgeInsets.only(left: 12.w, right: 16.w, bottom: 12.w),
  420. decoration: BoxDecoration(
  421. image: DecorationImage(
  422. image: isSelected
  423. ? Assets.images.iconMemberSpecialProductsSelect.provider()
  424. : Assets.images.iconMemberSpecialProductsNormal.provider(),
  425. fit: BoxFit.fill,
  426. ),
  427. ),
  428. child: Stack(
  429. children: [
  430. Positioned(
  431. top: 10.w,
  432. bottom: 0,
  433. left: 24.w,
  434. child: Column(
  435. mainAxisAlignment: MainAxisAlignment.center,
  436. children: [
  437. RichText(
  438. text: TextSpan(
  439. style: TextStyle(
  440. color: isSelected
  441. ? '#FF5656'.color
  442. : "#323133".color,
  443. fontWeight: FontWeight.bold),
  444. children: [
  445. TextSpan(
  446. text: '¥',
  447. style: TextStyle(fontSize: 16.sp, height: 1)),
  448. TextSpan(
  449. text: goodsInfo.amount.divideBy100(),
  450. style: TextStyle(
  451. fontSize: 28.sp,
  452. height: 1,
  453. //fontFamily: FontFamily.oppoSans
  454. ))
  455. ])),
  456. Text('¥${goodsInfo.originalAmount.divideBy100()}',
  457. style: TextStyle(
  458. decoration: TextDecoration.lineThrough,
  459. decorationColor: ColorName.black40,
  460. decorationThickness: 1.0,
  461. fontSize: 12.sp,
  462. color: ColorName.black40))
  463. ],
  464. ),
  465. ),
  466. Positioned(
  467. top: 10.w,
  468. bottom: 0,
  469. left: 105.w,
  470. child: Column(
  471. crossAxisAlignment: CrossAxisAlignment.start,
  472. mainAxisAlignment: MainAxisAlignment.center,
  473. children: [
  474. Text(
  475. goodsInfo.name,
  476. style: TextStyle(
  477. fontSize: 17.sp,
  478. color: "#333333".color,
  479. fontWeight: FontWeight.bold),
  480. ),
  481. SizedBox(
  482. height: 3.w,
  483. ),
  484. Text(
  485. goodsInfo.description ?? "",
  486. style: TextStyle(
  487. fontSize: 11.sp,
  488. color: "#9191BA".color,
  489. fontWeight: FontWeight.bold),
  490. ),
  491. ],
  492. ),
  493. ),
  494. ],
  495. ),
  496. ),
  497. );
  498. }
  499. Widget buildNormalGoodsView(GoodsBean goodsInfo) {
  500. bool isSelected = controller.selectedGoods?.id == goodsInfo.id;
  501. return GestureDetector(
  502. onTap: () {
  503. controller.onGoodsItemClick(goodsInfo);
  504. },
  505. child: Container(
  506. width: double.infinity,
  507. height: 72.w,
  508. margin: EdgeInsets.only(left: 16.w, right: 16.w, bottom: 12.w),
  509. child: Stack(
  510. children: [
  511. Container(
  512. width: double.infinity,
  513. height: double.infinity,
  514. decoration: BoxDecoration(
  515. gradient: isSelected
  516. ? LinearGradient(
  517. colors: ['#FFFFFF'.color, '#F3EAFF'.color],
  518. begin: Alignment.topLeft,
  519. end: Alignment.bottomRight,
  520. stops: const [0.4, 1.0])
  521. : null,
  522. borderRadius: BorderRadius.circular(12.w),
  523. border: Border.all(
  524. color: isSelected ? '#7A13C6'.color : '#DDDDDD'.color,
  525. width: isSelected ? 2.5.w : 1.w,
  526. ))),
  527. Stack(
  528. children: [
  529. Positioned(
  530. top: 4.w,
  531. bottom: 0,
  532. left: 20.w,
  533. child: Column(
  534. mainAxisAlignment: MainAxisAlignment.center,
  535. children: [
  536. RichText(
  537. text: TextSpan(
  538. style: TextStyle(
  539. fontFamily: FontFamily.oppoSans,
  540. color: isSelected
  541. ? '#FF5656'.color
  542. : "#323133".color,
  543. fontWeight: FontWeight.bold),
  544. children: [
  545. TextSpan(
  546. text: '¥',
  547. style: TextStyle(fontSize: 16.sp, height: 1)),
  548. TextSpan(
  549. text: goodsInfo.amount.divideBy100(),
  550. style: TextStyle(
  551. fontSize: 28.sp,
  552. height: 1,
  553. //fontFamily: FontFamily.oppoSans
  554. ))
  555. ])),
  556. Text('¥${goodsInfo.originalAmount.divideBy100()}',
  557. style: TextStyle(
  558. decoration: TextDecoration.lineThrough,
  559. decorationColor: ColorName.black40,
  560. decorationThickness: 1.0,
  561. fontSize: 12.sp,
  562. color: ColorName.black40))
  563. ],
  564. ),
  565. ),
  566. Positioned(
  567. top: 0.w,
  568. bottom: 0,
  569. left: 101.w,
  570. child: Column(
  571. crossAxisAlignment: CrossAxisAlignment.start,
  572. mainAxisAlignment: MainAxisAlignment.center,
  573. children: [
  574. Text(
  575. goodsInfo.name,
  576. style: TextStyle(
  577. fontSize: 17.sp,
  578. color: "#333333".color,
  579. fontWeight: FontWeight.bold),
  580. ),
  581. SizedBox(
  582. height: 3.w,
  583. ),
  584. Text(
  585. goodsInfo.description ?? "",
  586. style: TextStyle(
  587. fontSize: 11.sp,
  588. color: "#9191BA".color,
  589. fontWeight: FontWeight.bold),
  590. ),
  591. ],
  592. ),
  593. ),
  594. ],
  595. ),
  596. ],
  597. ),
  598. ),
  599. );
  600. }
  601. }