new_discount_page.dart 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. import 'package:flutter/src/widgets/framework.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:keyboard/base/base_page.dart';
  4. import 'package:keyboard/module/store/new_discount/new_discount_controller.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:carousel_slider/carousel_slider.dart';
  7. import 'package:get/get.dart';
  8. import '../../../data/bean/character_info.dart';
  9. import '../../../data/consts/payment_type.dart';
  10. import '../../../data/consts/web_url.dart';
  11. import '../../../resource/assets.gen.dart';
  12. import '../../../resource/colors.gen.dart';
  13. import '../../../resource/string.gen.dart';
  14. import '../../../router/app_pages.dart';
  15. import '../../../utils/styles.dart';
  16. import '../../../widget/auto_scroll_list_view.dart';
  17. import '../../../widget/click_text_span.dart';
  18. class NewDiscountPage extends BasePage<NewDiscountController> {
  19. const NewDiscountPage({super.key});
  20. /// 跳转
  21. static void start() {
  22. Get.toNamed(RoutePath.newDiscount);
  23. }
  24. @override
  25. bool immersive() {
  26. return true;
  27. }
  28. @override
  29. backgroundColor() => Colors.white;
  30. @override
  31. Widget buildBody(BuildContext context) {
  32. return Stack(
  33. children: [
  34. Positioned(top: 0.w, child: _buildBanner()),
  35. Positioned(
  36. top: 408.w,
  37. child: Container(
  38. decoration: BoxDecoration(
  39. color: ColorName.white,
  40. borderRadius: BorderRadius.only(
  41. topLeft: Radius.circular(24.r),
  42. topRight: Radius.circular(24.r),
  43. ),
  44. ),
  45. width: 360.w,
  46. height: 392.w,
  47. child: SingleChildScrollView(
  48. child: Expanded(
  49. child: Column(
  50. children: [
  51. _buildGoodsCard(),
  52. SizedBox(height: 200.w),
  53. ],
  54. ),
  55. ),
  56. ),
  57. ),
  58. ),
  59. Positioned(top: 0, left: 0, right: 0, child: _buildTitle()),
  60. Positioned(bottom: 0, left: 0, right: 0, child: _buildBuyButtonCard()),
  61. ],
  62. );
  63. }
  64. _buildTitle() {
  65. return SafeArea(
  66. child: Container(
  67. alignment: Alignment.centerLeft,
  68. padding: EdgeInsets.only(top: 16.h, left: 16.w, right: 16.w),
  69. child: Row(
  70. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  71. children: [
  72. GestureDetector(
  73. onTap: controller.clickBack,
  74. child: Assets.images.iconStoreBack.image(
  75. width: 32.w,
  76. height: 32.w,
  77. ),
  78. ),
  79. ],
  80. ),
  81. ),
  82. );
  83. }
  84. Widget _buildBanner() {
  85. return SizedBox(
  86. width: 360.w,
  87. child: Stack(
  88. children: [
  89. CarouselSlider(
  90. carouselController: controller.carouselSliderController,
  91. options: CarouselOptions(
  92. height: 438.w,
  93. viewportFraction: 1,
  94. initialPage: 0,
  95. enableInfiniteScroll: true,
  96. reverse: false,
  97. autoPlay: true,
  98. autoPlayInterval: const Duration(seconds: 3),
  99. autoPlayAnimationDuration: const Duration(milliseconds: 500),
  100. autoPlayCurve: Curves.fastOutSlowIn,
  101. scrollDirection: Axis.horizontal,
  102. onPageChanged: (index, reason) {
  103. controller.onBannerChanged(index, reason);
  104. },
  105. ),
  106. items:
  107. controller.bannerList.map((item) {
  108. return item.image(width: 360.w, fit: BoxFit.contain);
  109. }).toList(),
  110. ),
  111. Positioned(
  112. bottom: 149.16.w,
  113. left: 31.w,
  114. child: Assets.images.iconNewDiscountCharacterTitle.image(
  115. width: 296.w,
  116. height: 39.w,
  117. ),
  118. ),
  119. Positioned(
  120. bottom: 211.13.w,
  121. left: 42.w,
  122. right: 0,
  123. child: _buildIndicator(),
  124. ),
  125. Positioned(
  126. bottom: 0,
  127. left: 0,
  128. right: 0,
  129. child: _buildAutoCharacterList(),
  130. ),
  131. ],
  132. ),
  133. );
  134. }
  135. /// 指示器
  136. _buildIndicator() {
  137. return Row(
  138. children:
  139. controller.bannerList.asMap().entries.map((entry) {
  140. return Obx(() {
  141. final isSelectedBanner =
  142. controller.currentBannerIndex == entry.key;
  143. return Row(
  144. mainAxisAlignment: MainAxisAlignment.spaceAround,
  145. children: [
  146. GestureDetector(
  147. onTap:
  148. () => controller.carouselSliderController.animateToPage(
  149. entry.key,
  150. ),
  151. child: AnimatedContainer(
  152. duration: const Duration(milliseconds: 300),
  153. margin: EdgeInsets.only(right: 5.w),
  154. height: 5.w,
  155. width: isSelectedBanner ? 10.w : 5.w,
  156. decoration: BoxDecoration(
  157. color:
  158. isSelectedBanner
  159. ? const Color(0xff483459)
  160. : const Color(0xffAFB4BF),
  161. borderRadius: BorderRadius.circular(5.r),
  162. ),
  163. ),
  164. ),
  165. ],
  166. );
  167. });
  168. }).toList(),
  169. );
  170. }
  171. Widget _buildAutoCharacterList() {
  172. return Obx(() {
  173. if (controller.charactersList.isEmpty) {
  174. return Container();
  175. }
  176. return SizedBox(
  177. height: 130.w,
  178. child: AutoScrollListView(
  179. itemCount: (controller.charactersList.length / 2).ceil(),
  180. scrollDirection: Axis.horizontal,
  181. itemBuilder: (context, columnIndex) {
  182. int rowCount = 2;
  183. int startIndex = columnIndex * rowCount;
  184. final List<CharacterInfo> columnItems =
  185. controller.charactersList
  186. .skip(startIndex)
  187. .take(rowCount)
  188. .toList();
  189. return Column(
  190. children:
  191. columnItems.map((item) {
  192. final emoji = item.emoji ?? "";
  193. final name = item.name ?? "";
  194. return Padding(
  195. padding: EdgeInsets.symmetric(
  196. vertical: 4.w,
  197. horizontal: 4.w,
  198. ),
  199. child: Container(
  200. padding: EdgeInsets.symmetric(
  201. horizontal: 12.w,
  202. vertical: 6.w,
  203. ),
  204. decoration: ShapeDecoration(
  205. color: Colors.white,
  206. shape: RoundedRectangleBorder(
  207. side: BorderSide(
  208. width: 1.w,
  209. color: const Color(0xFFF4F1FF),
  210. ),
  211. borderRadius: BorderRadius.circular(31.r),
  212. ),
  213. ),
  214. child: Text(
  215. "$emoji$name",
  216. style: TextStyle(
  217. color: Colors.black.withAlpha(204),
  218. fontSize: 13.sp,
  219. fontWeight: FontWeight.w400,
  220. ),
  221. ),
  222. ),
  223. );
  224. }).toList(),
  225. );
  226. },
  227. ),
  228. );
  229. });
  230. }
  231. Widget _buildBuyButtonCard() {
  232. return Container(
  233. width: 360.w,
  234. padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 11.h),
  235. decoration: ShapeDecoration(
  236. color: Colors.white,
  237. shape: RoundedRectangleBorder(
  238. borderRadius: BorderRadius.only(
  239. topLeft: Radius.circular(24.r),
  240. topRight: Radius.circular(24.r),
  241. ),
  242. ),
  243. shadows: [
  244. BoxShadow(
  245. color: Color(0x99FFE498),
  246. blurRadius: 20,
  247. offset: Offset(0, 0),
  248. spreadRadius: 0,
  249. ),
  250. ],
  251. ),
  252. child: Column(
  253. children: [
  254. GestureDetector(
  255. onTap: controller.clickPayNow,
  256. child: Container(
  257. width: 328.w,
  258. height: 54.h,
  259. decoration: ShapeDecoration(
  260. gradient: LinearGradient(
  261. begin: Alignment(0.60, -0.39),
  262. end: Alignment(0.60, 0.95),
  263. colors: [
  264. const Color(0xFFF95FAC),
  265. const Color(0xFFFD5E4D),
  266. const Color(0xFFFD5E4D),
  267. const Color(0xFFFB8A3C),
  268. ],
  269. ),
  270. shape: RoundedRectangleBorder(
  271. borderRadius: BorderRadius.circular(30.55.r),
  272. ),
  273. ),
  274. child: Center(
  275. child: Text(
  276. StringName.storePayNow,
  277. style: Styles.getTextStyleWhiteW500(17.sp),
  278. ),
  279. ),
  280. ),
  281. ),
  282. SizedBox(height: 11.h),
  283. Row(
  284. mainAxisAlignment: MainAxisAlignment.center,
  285. children: [
  286. Obx(() {
  287. return GestureDetector(
  288. behavior: HitTestBehavior.opaque,
  289. onTap: () {
  290. controller.isAgree.value = !controller.isAgree.value;
  291. },
  292. child:
  293. controller.isAgree.value
  294. ? Assets.images.iconStoreAgreePrivacy.image(
  295. width: 16.w,
  296. height: 16.w,
  297. )
  298. : SizedBox(
  299. child: Container(
  300. padding: EdgeInsets.all(1.w),
  301. width: 16.w,
  302. height: 16.w,
  303. child: Container(
  304. decoration: BoxDecoration(
  305. shape: BoxShape.circle,
  306. border: Border.all(
  307. color: Colors.black.withAlpha(153),
  308. width: 1.w,
  309. ),
  310. ),
  311. ),
  312. ),
  313. ),
  314. );
  315. }),
  316. Text.rich(
  317. TextSpan(
  318. children: [
  319. TextSpan(
  320. text: StringName.textSpanIHaveReadAndAgree,
  321. style: TextStyle(
  322. color: Colors.black.withAlpha(153),
  323. fontSize: 10.sp,
  324. fontWeight: FontWeight.w400,
  325. ),
  326. ),
  327. ClickTextSpan(
  328. text: StringName.textSpanPrivacyPolicy,
  329. url: WebUrl.privacyPolicy,
  330. ),
  331. TextSpan(
  332. text: StringName.textSpanAnd,
  333. style: TextStyle(
  334. color: Colors.black.withAlpha(153),
  335. fontSize: 10.sp,
  336. fontWeight: FontWeight.w400,
  337. ),
  338. ),
  339. ClickTextSpan(
  340. text: StringName.textSpanServiceTerms,
  341. url: WebUrl.serviceAgreement,
  342. ),
  343. ],
  344. ),
  345. ),
  346. ],
  347. ),
  348. ],
  349. ),
  350. );
  351. }
  352. Widget _buildGoodsCard() {
  353. return Container(
  354. margin: EdgeInsets.symmetric(horizontal: 16.w),
  355. padding: EdgeInsets.only(
  356. top: 16.w,
  357. left: 16.w,
  358. right: 16.w,
  359. bottom: 24.w,
  360. ),
  361. decoration: BoxDecoration(
  362. color: Colors.white,
  363. borderRadius: BorderRadius.only(
  364. topLeft: Radius.circular(16.r),
  365. topRight: Radius.circular(16.r),
  366. ),
  367. ),
  368. child: Column(
  369. crossAxisAlignment: CrossAxisAlignment.start,
  370. mainAxisAlignment: MainAxisAlignment.start,
  371. children: [
  372. SizedBox(height: 16.h),
  373. Obx(() {
  374. return Column(
  375. children:
  376. controller.filteredGoodsList.map((item) {
  377. return Obx(() {
  378. return GestureDetector(
  379. onTap: () => controller.onGoodsItemClick(item),
  380. child: _buildGoodsItem(
  381. item,
  382. controller.selectedGoodsInfoItem?.id == item.id,
  383. ),
  384. );
  385. });
  386. }).toList(),
  387. );
  388. }),
  389. _buildPayWayCard(),
  390. ],
  391. ),
  392. );
  393. }
  394. Widget _buildPayWayCard() {
  395. return GestureDetector(
  396. onTap: () => controller.clickPayWaySwitch(),
  397. child: Container(
  398. height: 36.h,
  399. padding: EdgeInsets.symmetric(horizontal: 10.w),
  400. decoration: ShapeDecoration(
  401. shape: RoundedRectangleBorder(
  402. side: BorderSide(width: 1, color: const Color(0xFFECEBE0)),
  403. borderRadius: BorderRadius.circular(10.r),
  404. ),
  405. ),
  406. child: Row(
  407. children: [
  408. Text(
  409. StringName.storePayWay,
  410. style: Styles.getTextStyleBlack204W400(14.sp),
  411. ),
  412. const Spacer(),
  413. Obx(() {
  414. if (controller.selectedPayWay == null) {
  415. return SizedBox.shrink();
  416. }
  417. return Row(
  418. children: [
  419. Image.asset(
  420. getPaymentIconPath(
  421. payMethod: controller.selectedPayWay!.payMethod,
  422. payPlatform: controller.selectedPayWay!.payPlatform,
  423. ),
  424. width: 20.w,
  425. height: 20.w,
  426. ),
  427. SizedBox(width: 4.w),
  428. Text(
  429. controller.selectedPayWay?.title ?? '',
  430. style: Styles.getTextStyleBlack204W400(14.sp),
  431. ),
  432. SizedBox(width: 6.w),
  433. Assets.images.iconStoreSwitchPay.image(
  434. width: 20.w,
  435. height: 20.w,
  436. fit: BoxFit.fill,
  437. ),
  438. ],
  439. );
  440. }),
  441. ],
  442. ),
  443. ),
  444. );
  445. }
  446. Widget _buildGoodsItem(item, bool isSelected) {
  447. return Container(
  448. margin: EdgeInsets.only(bottom: 8.h),
  449. decoration: ShapeDecoration(
  450. color: Colors.white,
  451. shape: RoundedRectangleBorder(
  452. side: BorderSide(
  453. width: 2.w,
  454. color: Colors.black.withAlpha( 26),
  455. ),
  456. borderRadius: BorderRadius.circular(16),
  457. ),
  458. ),
  459. child: Row(
  460. children: [
  461. Text(
  462. '¥${item.amountText}',
  463. textAlign: TextAlign.center,
  464. style:
  465. isSelected
  466. ? Styles.getTextStyleFF7F14W500(12.sp)
  467. : Styles.getTextStyleWhiteW500(12.sp),
  468. )
  469. // Container(
  470. // width: 212.w,
  471. // height: 70.h,
  472. // decoration: ShapeDecoration(
  473. // gradient:
  474. // isSelected
  475. // ? LinearGradient(
  476. // begin: Alignment(-0.06, 0.50),
  477. // end: Alignment(1.14, 0.50),
  478. // colors: [
  479. // const Color(0xFFFFF895),
  480. // const Color(0xFFFFE941),
  481. // ],
  482. // )
  483. // : null,
  484. // color: isSelected ? null : const Color(0xFFFFFDEE),
  485. // shape: RoundedRectangleBorder(
  486. // side: BorderSide(width: 1, color: const Color(0xFFFEE86B)),
  487. // borderRadius: BorderRadius.circular(isSelected ? 8 : 10.r),
  488. // ),
  489. // ),
  490. // child: Stack(
  491. // children: [
  492. // if (isSelected)
  493. // IgnorePointer(
  494. // child: Assets.images.bgStoreSelectedItem.image(
  495. // width: 212.w,
  496. // height: 70.h,
  497. // fit: BoxFit.fill,
  498. // ),
  499. // ),
  500. // Padding(
  501. // padding: EdgeInsets.only(left: 16.w),
  502. // child: Column(
  503. // crossAxisAlignment: CrossAxisAlignment.start,
  504. // mainAxisAlignment: MainAxisAlignment.center,
  505. // children: [
  506. // Row(
  507. // children: [
  508. // RichText(
  509. // text: TextSpan(
  510. // children: [
  511. // TextSpan(
  512. // text: '¥',
  513. // style: Styles.getTextStyleFF663300W700(14.sp),
  514. // ),
  515. // TextSpan(
  516. // text: item.priceDescNumber,
  517. // style: Styles.getTextStyleFF663300W700(18.sp),
  518. // ),
  519. // TextSpan(
  520. // text: item.priceDescUnit,
  521. // style: Styles.getTextStyleFF663300W400(13.sp),
  522. // ),
  523. // ],
  524. // ),
  525. // ),
  526. // if (item.mostDesc?.isNotEmpty == true)
  527. // Container(
  528. // padding: EdgeInsets.only(
  529. // left: 16.w,
  530. // top: 2.h,
  531. // bottom: 2.h,
  532. // ),
  533. // decoration: BoxDecoration(
  534. // image: DecorationImage(
  535. // image: Assets.images.iconStoreMost.provider(),
  536. // fit: BoxFit.cover,
  537. // alignment: Alignment.bottomLeft,
  538. // ),
  539. // ),
  540. // child: Text(
  541. // item.mostDesc!,
  542. // style: TextStyle(
  543. // color: Colors.white,
  544. // fontSize: 12.sp,
  545. // fontWeight: FontWeight.w500,
  546. // letterSpacing: -0.60,
  547. // ),
  548. // ),
  549. // ),
  550. // ],
  551. // ),
  552. // Text(
  553. // item.description!,
  554. // style: Styles.getTextStyle99673300W400(12.sp),
  555. // ),
  556. // ],
  557. // ),
  558. // ),
  559. // ],
  560. // ),
  561. // ),
  562. // // 右侧
  563. // Expanded(
  564. // child: Column(
  565. // mainAxisAlignment: MainAxisAlignment.center,
  566. // crossAxisAlignment: CrossAxisAlignment.center,
  567. // children: [
  568. // Text(
  569. // item.name,
  570. // style:
  571. // isSelected
  572. // ? Styles.getTextStyleFFECBBW500(15.sp)
  573. // : Styles.getTextStyleFF663300W500(15.sp),
  574. // ),
  575. // Container(
  576. // padding: EdgeInsets.symmetric(horizontal: 8.w),
  577. // decoration: ShapeDecoration(
  578. // color: isSelected ? const Color(0xFFFFECBB) : null,
  579. // gradient:
  580. // isSelected
  581. // ? null
  582. // : LinearGradient(
  583. // begin: Alignment(0.77, -0.00),
  584. // end: Alignment(0.77, 1.00),
  585. // colors: [
  586. // const Color(0xFFFF9416),
  587. // const Color(0xFFFF7813),
  588. // ],
  589. // ),
  590. // shape: RoundedRectangleBorder(
  591. // borderRadius: BorderRadius.circular(
  592. // isSelected ? 17.r : 10.r,
  593. // ),
  594. // ),
  595. // ),
  596. // child: Text(
  597. // '¥${item.amountText}',
  598. // textAlign: TextAlign.center,
  599. // style:
  600. // isSelected
  601. // ? Styles.getTextStyleFF7F14W500(12.sp)
  602. // : Styles.getTextStyleWhiteW500(12.sp),
  603. // ),
  604. // ),
  605. // ],
  606. // ),
  607. // ),
  608. ],
  609. ),
  610. );
  611. }
  612. }