member_activity_page.dart 21 KB

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