new_discount_page.dart 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  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/data/bean/goods_info.dart';
  5. import 'package:keyboard/module/store/new_discount/new_discount_controller.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:carousel_slider/carousel_slider.dart';
  8. import 'package:get/get.dart';
  9. import 'package:keyboard/widget/platform_util.dart';
  10. import '../../../data/bean/character_info.dart';
  11. import '../../../data/consts/payment_type.dart';
  12. import '../../../data/consts/web_url.dart';
  13. import '../../../resource/assets.gen.dart';
  14. import '../../../resource/colors.gen.dart';
  15. import '../../../resource/string.gen.dart';
  16. import '../../../router/app_pages.dart';
  17. import '../../../utils/styles.dart';
  18. import '../../../widget/auto_scroll_list_view.dart';
  19. import '../../../widget/click_text_span.dart';
  20. class NewDiscountPage extends BasePage<NewDiscountController> {
  21. const NewDiscountPage({super.key});
  22. /// 跳转
  23. static Future<void> start() async {
  24. await Get.toNamed(RoutePath.newDiscount);
  25. }
  26. @override
  27. bool immersive() {
  28. return true;
  29. }
  30. @override
  31. backgroundColor() => Colors.white;
  32. @override
  33. Widget buildBody(BuildContext context) {
  34. return Stack(
  35. children: [
  36. Positioned(top: 0.w, child: _buildBanner()),
  37. Positioned(
  38. top: 408.w,
  39. child: Container(
  40. decoration: BoxDecoration(
  41. color: ColorName.white,
  42. borderRadius: BorderRadius.only(
  43. topLeft: Radius.circular(24.r),
  44. topRight: Radius.circular(24.r),
  45. ),
  46. ),
  47. width: 360.w,
  48. height: 392.w,
  49. child: SingleChildScrollView(
  50. child: Column(
  51. children: [
  52. SizedBox(height: 16.w),
  53. _buildGoodsCard(),
  54. SizedBox(height: 12.w),
  55. // iOS平台,没有支付方式可以选择
  56. if (!PlatformUtil.isIOS) _buildPayWayCard(),
  57. SizedBox(height: 8.w),
  58. _buildSelectedDesc(),
  59. SizedBox(height: 30.w),
  60. _buildMemberCard(),
  61. SizedBox(height: 200.w),
  62. ],
  63. ),
  64. ),
  65. ),
  66. ),
  67. Positioned(top: 0, left: 0, right: 0, child: _buildTitle()),
  68. Positioned(bottom: 0, left: 0, right: 0, child: _buildBuyButtonCard()),
  69. ],
  70. );
  71. }
  72. _buildTitle() {
  73. return SafeArea(
  74. child: Container(
  75. alignment: Alignment.centerLeft,
  76. padding: EdgeInsets.only(top: 16.h, left: 16.w, right: 16.w),
  77. child: Row(
  78. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  79. children: [
  80. GestureDetector(
  81. onTap: controller.clickBack,
  82. child: Assets.images.iconStoreBack.image(
  83. width: 32.w,
  84. height: 32.w,
  85. ),
  86. ),
  87. // iOS平台,才有恢复订阅按钮
  88. if (PlatformUtil.isIOS)
  89. GestureDetector(
  90. onTap: () {
  91. controller.clickRestore();
  92. },
  93. child: Container(
  94. padding: EdgeInsets.symmetric(
  95. horizontal: 12.w,
  96. vertical: 6.w,
  97. ),
  98. decoration: BoxDecoration(
  99. color: ColorName.black40,
  100. borderRadius: BorderRadius.circular(16.r),
  101. ),
  102. child: Text(
  103. StringName.recoverSubscribe,
  104. style: TextStyle(
  105. color: ColorName.white80,
  106. fontSize: 14.sp,
  107. fontWeight: FontWeight.w500,
  108. ),
  109. ),
  110. ),
  111. ),
  112. ],
  113. ),
  114. ),
  115. );
  116. }
  117. Widget _buildBanner() {
  118. return SizedBox(
  119. width: 360.w,
  120. child: Stack(
  121. children: [
  122. CarouselSlider(
  123. carouselController: controller.carouselSliderController,
  124. options: CarouselOptions(
  125. height: 438.w,
  126. viewportFraction: 1,
  127. initialPage: 0,
  128. enableInfiniteScroll: true,
  129. reverse: false,
  130. autoPlay: true,
  131. autoPlayInterval: const Duration(seconds: 3),
  132. autoPlayAnimationDuration: const Duration(milliseconds: 500),
  133. autoPlayCurve: Curves.fastOutSlowIn,
  134. scrollDirection: Axis.horizontal,
  135. onPageChanged: (index, reason) {
  136. controller.onBannerChanged(index, reason);
  137. },
  138. ),
  139. items:
  140. controller.bannerList.map((item) {
  141. return item.image(width: 360.w, fit: BoxFit.contain);
  142. }).toList(),
  143. ),
  144. Positioned(
  145. bottom: 149.16.w,
  146. left: 31.w,
  147. child: Assets.images.iconNewDiscountCharacterTitle.image(
  148. width: 296.w,
  149. height: 39.w,
  150. ),
  151. ),
  152. Positioned(
  153. bottom: 211.13.w,
  154. left: 42.w,
  155. right: 0,
  156. child: _buildIndicator(),
  157. ),
  158. Positioned(
  159. bottom: 0,
  160. left: 0,
  161. right: 0,
  162. child: _buildAutoCharacterList(),
  163. ),
  164. ],
  165. ),
  166. );
  167. }
  168. /// 指示器
  169. _buildIndicator() {
  170. return Row(
  171. children:
  172. controller.bannerList.asMap().entries.map((entry) {
  173. return Obx(() {
  174. final isSelectedBanner =
  175. controller.currentBannerIndex == entry.key;
  176. return Row(
  177. mainAxisAlignment: MainAxisAlignment.spaceAround,
  178. children: [
  179. GestureDetector(
  180. onTap:
  181. () => controller.carouselSliderController.animateToPage(
  182. entry.key,
  183. ),
  184. child: AnimatedContainer(
  185. duration: const Duration(milliseconds: 300),
  186. margin: EdgeInsets.only(right: 5.w),
  187. height: 5.w,
  188. width: isSelectedBanner ? 10.w : 5.w,
  189. decoration: BoxDecoration(
  190. color:
  191. isSelectedBanner
  192. ? const Color(0xff483459)
  193. : const Color(0xffAFB4BF),
  194. borderRadius: BorderRadius.circular(5.r),
  195. ),
  196. ),
  197. ),
  198. ],
  199. );
  200. });
  201. }).toList(),
  202. );
  203. }
  204. Widget _buildAutoCharacterList() {
  205. return Obx(() {
  206. if (controller.charactersList.isEmpty) {
  207. return Container();
  208. }
  209. return SizedBox(
  210. height: 130.w,
  211. child: AutoScrollListView(
  212. itemCount: (controller.charactersList.length / 2).ceil(),
  213. scrollDirection: Axis.horizontal,
  214. itemBuilder: (context, columnIndex) {
  215. int rowCount = 2;
  216. int startIndex = columnIndex * rowCount;
  217. final List<CharacterInfo> columnItems =
  218. controller.charactersList
  219. .skip(startIndex)
  220. .take(rowCount)
  221. .toList();
  222. return Column(
  223. children:
  224. columnItems.map((item) {
  225. final emoji = item.emoji ?? "";
  226. final name = item.name ?? "";
  227. return Padding(
  228. padding: EdgeInsets.symmetric(
  229. vertical: 4.w,
  230. horizontal: 4.w,
  231. ),
  232. child: Container(
  233. padding: EdgeInsets.symmetric(
  234. horizontal: 12.w,
  235. vertical: 6.w,
  236. ),
  237. decoration: ShapeDecoration(
  238. color: Colors.white,
  239. shape: RoundedRectangleBorder(
  240. side: BorderSide(
  241. width: 1.w,
  242. color: const Color(0xFFF4F1FF),
  243. ),
  244. borderRadius: BorderRadius.circular(31.r),
  245. ),
  246. ),
  247. child: Text(
  248. "$emoji$name",
  249. style: TextStyle(
  250. color: Colors.black.withAlpha(204),
  251. fontSize: 13.sp,
  252. fontWeight: FontWeight.w400,
  253. ),
  254. ),
  255. ),
  256. );
  257. }).toList(),
  258. );
  259. },
  260. ),
  261. );
  262. });
  263. }
  264. Widget _buildBuyButtonCard() {
  265. return Container(
  266. width: 360.w,
  267. padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 11.h),
  268. decoration: ShapeDecoration(
  269. color: Colors.white,
  270. shape: RoundedRectangleBorder(
  271. borderRadius: BorderRadius.only(
  272. topLeft: Radius.circular(24.r),
  273. topRight: Radius.circular(24.r),
  274. ),
  275. ),
  276. shadows: [
  277. BoxShadow(
  278. color: Color(0x99FFE498),
  279. blurRadius: 20,
  280. offset: Offset(0, 0),
  281. spreadRadius: 0,
  282. ),
  283. ],
  284. ),
  285. child: Column(
  286. children: [
  287. GestureDetector(
  288. onTap: controller.clickPayNow,
  289. child: Container(
  290. width: 328.w,
  291. height: 56.w,
  292. decoration: ShapeDecoration(
  293. shape: RoundedRectangleBorder(
  294. side: BorderSide(width: 2.w, color: const Color(0xFFFFF6C9)),
  295. borderRadius: BorderRadius.circular(30.55),
  296. ),
  297. ),
  298. child: Container(
  299. width: 328,
  300. height: 54,
  301. decoration: ShapeDecoration(
  302. gradient: LinearGradient(
  303. begin: Alignment(0.60, -0.39),
  304. end: Alignment(0.60, 0.95),
  305. colors: [
  306. const Color(0xFFF95FAC),
  307. const Color(0xFFFD4D99),
  308. const Color(0xFFFF3E75),
  309. const Color(0xFFFF0F53),
  310. ],
  311. ),
  312. shape: RoundedRectangleBorder(
  313. borderRadius: BorderRadius.circular(30.55),
  314. ),
  315. ),
  316. child: Center(
  317. child: Text(
  318. StringName.newDiscountUnlockNow,
  319. style: Styles.getTextStyleWhiteW500(17.sp),
  320. ),
  321. ),
  322. ),
  323. ),
  324. ),
  325. SizedBox(height: 11.h),
  326. _buildPrivacyTextSpan(),
  327. ],
  328. ),
  329. );
  330. }
  331. Widget _buildGoodsCard() {
  332. return Container(
  333. margin: EdgeInsets.symmetric(horizontal: 16.w),
  334. child: Column(
  335. crossAxisAlignment: CrossAxisAlignment.start,
  336. mainAxisAlignment: MainAxisAlignment.start,
  337. children: [
  338. Obx(() {
  339. final goodsList = controller.filteredGoodsList;
  340. if (goodsList.isEmpty) {
  341. return CircularProgressIndicator();
  342. }
  343. return Column(
  344. children: List.generate(goodsList.length, (index) {
  345. final item = goodsList[index];
  346. final isSelected =
  347. controller.selectedGoodsInfoItem?.id == item.id;
  348. return Column(
  349. children: [
  350. GestureDetector(
  351. onTap: () => controller.onGoodsItemClick(item),
  352. child: _buildGoodsItem(item, isSelected),
  353. ),
  354. if (index != goodsList.length - 1) SizedBox(height: 10.w),
  355. ],
  356. );
  357. }),
  358. );
  359. }),
  360. ],
  361. ),
  362. );
  363. }
  364. Widget _buildPayWayCard() {
  365. return GestureDetector(
  366. onTap: () => controller.clickPayWaySwitch(),
  367. child: Container(
  368. margin: EdgeInsets.symmetric(horizontal: 16.w),
  369. height: 36.h,
  370. padding: EdgeInsets.symmetric(horizontal: 10.w),
  371. decoration: ShapeDecoration(
  372. shape: RoundedRectangleBorder(
  373. side: BorderSide(width: 1, color: const Color(0xFFECEBE0)),
  374. borderRadius: BorderRadius.circular(10.r),
  375. ),
  376. ),
  377. child: Row(
  378. children: [
  379. Text(
  380. StringName.storePayWay,
  381. style: Styles.getTextStyleBlack204W400(14.sp),
  382. ),
  383. const Spacer(),
  384. Obx(() {
  385. if (controller.selectedPayWay == null) {
  386. return SizedBox.shrink();
  387. }
  388. return Row(
  389. children: [
  390. Image.asset(
  391. getPaymentIconPath(
  392. payMethod: controller.selectedPayWay!.payMethod,
  393. payPlatform: controller.selectedPayWay!.payPlatform,
  394. ),
  395. width: 20.w,
  396. height: 20.w,
  397. ),
  398. SizedBox(width: 4.w),
  399. Text(
  400. controller.selectedPayWay?.title ?? '',
  401. style: Styles.getTextStyleBlack204W400(14.sp),
  402. ),
  403. SizedBox(width: 6.w),
  404. Assets.images.iconStoreSwitchPay.image(
  405. width: 20.w,
  406. height: 20.w,
  407. fit: BoxFit.fill,
  408. ),
  409. ],
  410. );
  411. }),
  412. ],
  413. ),
  414. ),
  415. );
  416. }
  417. Widget _buildGoodsItem(GoodsInfo item, bool isSelected) {
  418. return Container(
  419. height: 70.w,
  420. width: 328.w,
  421. padding: EdgeInsets.symmetric(horizontal: 16.w),
  422. decoration:
  423. isSelected
  424. ? BoxDecoration(
  425. borderRadius: BorderRadius.circular(16.w),
  426. image: DecorationImage(
  427. image: Assets.images.bgNewDiscountItemSelect.provider(),
  428. fit: BoxFit.fill,
  429. ),
  430. )
  431. : BoxDecoration(
  432. borderRadius: BorderRadius.circular(16.w),
  433. image: DecorationImage(
  434. image: Assets.images.bgNewDiscountItemUnselect.provider(),
  435. fit: BoxFit.fill,
  436. ),
  437. ),
  438. child: Row(
  439. children: [
  440. Text(
  441. "¥",
  442. style: TextStyle(
  443. color:
  444. isSelected
  445. ? const Color(0xFFFF684E)
  446. : Colors.black.withAlpha(204),
  447. fontSize: 16.sp,
  448. fontWeight: FontWeight.w700,
  449. ),
  450. ),
  451. Text(
  452. (item.code == "vip_permanent")
  453. ? '${item.priceDescNumber}'
  454. : '${item.amountText}',
  455. textAlign: TextAlign.center,
  456. style: TextStyle(
  457. color:
  458. isSelected
  459. ? const Color(0xFFFF684E)
  460. : Colors.black.withAlpha(204),
  461. fontSize: 29.sp,
  462. fontWeight: FontWeight.w700,
  463. ),
  464. ),
  465. if (item.code == "vip_permanent")
  466. Text(
  467. item.priceDescUnit,
  468. style: TextStyle(
  469. color: Colors.black.withAlpha(153),
  470. fontSize: 12.sp,
  471. fontWeight: FontWeight.w400,
  472. ),
  473. ),
  474. SizedBox(width: 9.w),
  475. Column(
  476. crossAxisAlignment: CrossAxisAlignment.start,
  477. mainAxisAlignment: MainAxisAlignment.center,
  478. children: [
  479. Row(
  480. children: [
  481. Text(
  482. item.name,
  483. style: TextStyle(
  484. color: Colors.black.withAlpha(204),
  485. fontSize: 14.sp,
  486. fontWeight: FontWeight.w500,
  487. ),
  488. ),
  489. SizedBox(width: 4.5.w),
  490. if (item.timeLimitDesc?.isNotEmpty == true)
  491. Container(
  492. padding: EdgeInsets.symmetric(
  493. horizontal: 4.w,
  494. vertical: 2.w,
  495. ),
  496. margin: EdgeInsets.only(bottom: 4.w),
  497. decoration: ShapeDecoration(
  498. gradient: LinearGradient(
  499. begin: Alignment(0.00, 0.50),
  500. end: Alignment(1.00, 0.50),
  501. colors: [
  502. const Color(0xFFFF684E),
  503. const Color(0xFFFF4F9A),
  504. ],
  505. ),
  506. shape: RoundedRectangleBorder(
  507. borderRadius: BorderRadius.only(
  508. topLeft: Radius.circular(10.50),
  509. topRight: Radius.circular(10.50),
  510. bottomRight: Radius.circular(10.50),
  511. ),
  512. ),
  513. ),
  514. child: Text(
  515. item.timeLimitDesc!,
  516. style: TextStyle(
  517. color: Colors.white,
  518. fontSize: 12.sp,
  519. fontWeight: FontWeight.w500,
  520. ),
  521. ),
  522. ),
  523. ],
  524. ),
  525. Text(
  526. (item.code == "vip_permanent")
  527. ? '${item.description}'
  528. : '${item.priceDesc}',
  529. style: TextStyle(
  530. color: Colors.black.withAlpha(153),
  531. fontSize: 12.sp,
  532. fontWeight: FontWeight.w400,
  533. ),
  534. ),
  535. ],
  536. ),
  537. // Spacer(),
  538. // isSelected
  539. // ? Assets.images.iconCustomCharacterAddDialogSelect.image(
  540. // width: 20.w,
  541. // height: 20.w,
  542. // fit: BoxFit.contain,
  543. // )
  544. // : Container(
  545. // width: 20.w,
  546. // height: 20.w,
  547. // decoration: ShapeDecoration(
  548. // color: Colors.white,
  549. // shape: OvalBorder(
  550. // side: BorderSide(
  551. // width: 2.w,
  552. // color: const Color(0xFFE6E6E6),
  553. // ),
  554. // ),
  555. // ),
  556. // ),
  557. ],
  558. ),
  559. );
  560. }
  561. _buildSelectedDesc() {
  562. return Obx(() {
  563. return Container(
  564. margin: EdgeInsets.symmetric(horizontal: 16.w),
  565. child: Row(
  566. children: [
  567. Text(
  568. controller.selectedGoodsInfoItem?.selectDesc ?? "",
  569. style: TextStyle(
  570. color: Colors.black.withAlpha(102),
  571. fontSize: 12.sp,
  572. fontWeight: FontWeight.w400,
  573. ),
  574. ),
  575. ],
  576. ),
  577. );
  578. });
  579. }
  580. Widget _buildMemberCard() {
  581. return Container(
  582. margin: EdgeInsets.symmetric(horizontal: 16.w),
  583. child: Column(
  584. mainAxisAlignment: MainAxisAlignment.start,
  585. crossAxisAlignment: CrossAxisAlignment.start,
  586. children: [
  587. Assets.images.iconNewDiscountMembershipCardTitle.image(
  588. width: 79.w,
  589. height: 24.w,
  590. ),
  591. SizedBox(height: 6.w),
  592. Text(
  593. StringName.newDiscountMemberCardDesc,
  594. style: TextStyle(
  595. color: Colors.black.withValues(alpha: 87),
  596. fontSize: 11.sp,
  597. fontWeight: FontWeight.w400,
  598. ),
  599. ),
  600. SizedBox(height: 8.w),
  601. _buildMemberCardItem(),
  602. SizedBox(height: 28.w,),
  603. _buildUserNotice(),
  604. ],
  605. ),
  606. );
  607. }
  608. // 卡片权益
  609. _buildMemberCardItem() {
  610. return SizedBox(
  611. height: 80.w,
  612. child: AutoScrollListView(
  613. scrollDirection: Axis.horizontal,
  614. itemBuilder: (context, index) {
  615. final item = controller.memberCardList[index];
  616. return Container(
  617. margin: EdgeInsets.only(right: 8.w),
  618. width: 140.w,
  619. height: 65.w,
  620. decoration: ShapeDecoration(
  621. gradient: item.gradient,
  622. shape: RoundedRectangleBorder(
  623. side: BorderSide(width: 1.w, color: Colors.black.withAlpha(8)),
  624. borderRadius: BorderRadius.circular(20.r),
  625. ),
  626. ),
  627. child: Row(
  628. mainAxisAlignment: MainAxisAlignment.center,
  629. crossAxisAlignment: CrossAxisAlignment.center,
  630. children: [
  631. item.imageUrl.image(width: 28.w, height: 27.62.w),
  632. SizedBox(width: 8.w),
  633. Column(
  634. crossAxisAlignment: CrossAxisAlignment.start,
  635. mainAxisAlignment: MainAxisAlignment.center,
  636. children: [
  637. Text(
  638. item.title,
  639. style: TextStyle(
  640. color: Colors.black.withAlpha(204),
  641. fontSize: 12.sp,
  642. fontWeight: FontWeight.w700,
  643. ),
  644. ),
  645. SizedBox(height: 6.w),
  646. Text(
  647. item.desc,
  648. style: TextStyle(
  649. color: Colors.black.withAlpha(128),
  650. fontSize: 10.sp,
  651. fontWeight: FontWeight.w400,
  652. ),
  653. ),
  654. ],
  655. ),
  656. ],
  657. ),
  658. );
  659. },
  660. itemCount: controller.memberCardList.length,
  661. ),
  662. );
  663. }
  664. Widget _buildPrivacyTextSpan() {
  665. return Row(
  666. mainAxisAlignment: MainAxisAlignment.center,
  667. children: [
  668. Obx(() {
  669. return GestureDetector(
  670. behavior: HitTestBehavior.opaque,
  671. onTap: () {
  672. controller.isAgree.value = !controller.isAgree.value;
  673. },
  674. child:
  675. controller.isAgree.value
  676. ? Assets.images.iconStoreAgreePrivacy.image(
  677. width: 16.w,
  678. height: 16.w,
  679. )
  680. : SizedBox(
  681. child: Container(
  682. padding: EdgeInsets.all(1.w),
  683. width: 16.w,
  684. height: 16.w,
  685. child: Container(
  686. decoration: BoxDecoration(
  687. shape: BoxShape.circle,
  688. border: Border.all(
  689. color: Colors.black.withAlpha(153),
  690. width: 1.w,
  691. ),
  692. ),
  693. ),
  694. ),
  695. ),
  696. );
  697. }),
  698. Text.rich(
  699. PlatformUtil.isIOS
  700. ? TextSpan(
  701. children: [
  702. TextSpan(
  703. text: StringName.textSpanIHaveReadAndAgree,
  704. style: TextStyle(
  705. color: Colors.black.withAlpha(153),
  706. fontSize: 10.sp,
  707. fontWeight: FontWeight.w400,
  708. ),
  709. ),
  710. ClickTextSpan(
  711. text: StringName.textSpanPrivacyPolicy,
  712. url: WebUrl.privacyPolicy,
  713. ),
  714. ClickTextSpan(
  715. text: StringName.textSpanServiceTerms,
  716. url: WebUrl.serviceAgreement,
  717. ),
  718. TextSpan(
  719. text: StringName.textSpanAnd,
  720. style: TextStyle(
  721. color: Colors.black.withAlpha(153),
  722. fontSize: 10.sp,
  723. fontWeight: FontWeight.w400,
  724. ),
  725. ),
  726. ClickTextSpan(
  727. text: StringName.textSpanMemberAgreement,
  728. url: WebUrl.subscribeAgreement,
  729. ),
  730. ],
  731. )
  732. : TextSpan(
  733. children: [
  734. TextSpan(
  735. text: StringName.textSpanIHaveReadAndAgree,
  736. style: TextStyle(
  737. color: Colors.black.withAlpha(153),
  738. fontSize: 10.sp,
  739. fontWeight: FontWeight.w400,
  740. ),
  741. ),
  742. ClickTextSpan(
  743. text: StringName.textSpanPrivacyPolicy,
  744. url: WebUrl.privacyPolicy,
  745. ),
  746. ClickTextSpan(
  747. text: StringName.textSpanServiceTerms,
  748. url: WebUrl.serviceAgreement,
  749. ),
  750. TextSpan(
  751. text: StringName.textSpanAnd,
  752. style: TextStyle(
  753. color: Colors.black.withAlpha(153),
  754. fontSize: 10.sp,
  755. fontWeight: FontWeight.w400,
  756. ),
  757. ),
  758. ClickTextSpan(text: StringName.textSpanMembershipAgreement, url: WebUrl.memberServiceAgreement)
  759. ],
  760. ),
  761. ),
  762. ],
  763. );
  764. }
  765. // 用户须知
  766. Widget _buildUserNotice() {
  767. return Container(
  768. decoration: BoxDecoration(borderRadius: BorderRadius.circular(16.r)),
  769. child: Column(
  770. crossAxisAlignment: CrossAxisAlignment.start,
  771. children: [
  772. Text('购买须知', style: Styles.getTextStyleBlack204W400(12.sp)),
  773. SizedBox(height: 8.h),
  774. Text(
  775. "1.会员权益将在购买成功后自动生效,如遇延迟,请耐心等待5-10分钟。若长时间未生效,请联系客服处理;\n"
  776. "2.本服务为虚拟商品,一经购买成功即视为使用,恕不支持退款或转让,感谢您的理解;\n"
  777. "3.您的支持是我们持续优化与服务的动力,我们将竭诚为您提供更好的体验;\n"
  778. "4.相关细则以《会员服务协议》为准,在法律允许范围内,我们保留最终解释权。开通即视为同意协议条款,请谨慎购买。\n"
  779. "如有疑问,欢迎随时咨询在线客服!",
  780. style: Styles.getTextStyleBlack153W400(10.sp),
  781. ),
  782. ],
  783. ),
  784. );
  785. }
  786. }