member_activity_page.dart 21 KB

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