member_activity_page.dart 22 KB

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