member_activity_page.dart 22 KB

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