|
|
@@ -0,0 +1,860 @@
|
|
|
+import 'package:carousel_slider/carousel_slider.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
+import 'package:get/get.dart';
|
|
|
+import 'package:keyboard/base/base_page.dart';
|
|
|
+import 'package:keyboard/data/consts/payment_type.dart';
|
|
|
+import 'package:keyboard/data/consts/web_url.dart';
|
|
|
+import 'package:keyboard/module/store/store_controller.dart';
|
|
|
+import 'package:keyboard/module/store/store_user_reviews_bean.dart';
|
|
|
+import 'package:keyboard/resource/string.gen.dart';
|
|
|
+
|
|
|
+import '../../resource/assets.gen.dart';
|
|
|
+import '../../router/app_pages.dart';
|
|
|
+import '../../utils/date_util.dart';
|
|
|
+import '../../utils/horizontal_dashed_line.dart';
|
|
|
+import '../../utils/styles.dart';
|
|
|
+import '../../widget/click_text_span.dart';
|
|
|
+
|
|
|
+class StorePage extends BasePage<StoreController> {
|
|
|
+ const StorePage({super.key});
|
|
|
+
|
|
|
+ static start() {
|
|
|
+ Get.toNamed(RoutePath.store);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ backgroundColor() => const Color(0xFFFFF8D4);
|
|
|
+
|
|
|
+ @override
|
|
|
+ bool immersive() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget buildBody(BuildContext context) {
|
|
|
+ return Stack(
|
|
|
+ children: [
|
|
|
+ SingleChildScrollView(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ _buildBanner(context),
|
|
|
+ SizedBox(height: 12.h),
|
|
|
+ _buildGoodsCard(),
|
|
|
+ _buildVipDesc(),
|
|
|
+ SizedBox(height: 32.h),
|
|
|
+ _buildUserReviews(),
|
|
|
+ SizedBox(height: 20.h),
|
|
|
+ _buildUserNotice(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Positioned(top: 0, left: 0, right: 0, child: _buildTitle()),
|
|
|
+ Positioned(bottom: 0, left: 0, right: 0, child: _buildBuyButtonCard()),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ _buildTitle() {
|
|
|
+ return SafeArea(
|
|
|
+ child: Container(
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ padding: EdgeInsets.only(top: 16.h, left: 16.w, right: 16.w),
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap: controller.clickBack,
|
|
|
+ child: Assets.images.iconStoreBack.image(
|
|
|
+ width: 32.w,
|
|
|
+ height: 32.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Obx(() {
|
|
|
+ return SizedBox(
|
|
|
+ height: 32.h,
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ // 会员状态信息
|
|
|
+ _buildMemberInfo(),
|
|
|
+ SizedBox(width: 8.w),
|
|
|
+ controller.isLogin
|
|
|
+ ? Assets.images.iconMineUserLogged.image(
|
|
|
+ width: 28.w,
|
|
|
+ height: 28.w,
|
|
|
+ )
|
|
|
+ : Assets.images.iconMineUserNoLogin.image(
|
|
|
+ width: 28.w,
|
|
|
+ height: 28.w,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 会员信息
|
|
|
+ Widget _buildMemberInfo() {
|
|
|
+ return Container(
|
|
|
+ height: 32.h,
|
|
|
+ alignment: Alignment.center,
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 12.w),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.black.withAlpha(77),
|
|
|
+ borderRadius: BorderRadius.circular(21.r),
|
|
|
+ ),
|
|
|
+ child: Text.rich(
|
|
|
+ TextSpan(children: _getMemberStatusText()),
|
|
|
+ textAlign: TextAlign.right,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 会员状态文字逻辑提取
|
|
|
+ List<TextSpan> _getMemberStatusText() {
|
|
|
+ if (!controller.isLogin) {
|
|
|
+ return [
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.memberNoLogged,
|
|
|
+ style: _vipTextStyle(isHighlight: true),
|
|
|
+ ),
|
|
|
+ TextSpan(text: StringName.memberCardNoVipDesc, style: _vipTextStyle()),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ final isMember = controller.memberStatusInfo?.isMember ?? false;
|
|
|
+ final isPermanent = controller.memberStatusInfo?.permanent ?? false;
|
|
|
+ final username = controller.userInfo?.name ?? '';
|
|
|
+
|
|
|
+ if (isMember) {
|
|
|
+ if (isPermanent) {
|
|
|
+ return [
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.memberCardPermanentVipDesc1,
|
|
|
+ style: _vipTextStyle(),
|
|
|
+ ),
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.memberCardPermanentVipDesc2,
|
|
|
+ style: _vipTextStyle(isHighlight: true),
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+ } else {
|
|
|
+ return [
|
|
|
+ TextSpan(text: StringName.memberVipDesc, style: _vipTextStyle()),
|
|
|
+ TextSpan(
|
|
|
+ text: DateUtil.fromMillisecondsSinceEpoch(
|
|
|
+ 'yyyy.MM.dd',
|
|
|
+ controller.memberStatusInfo?.endTimestamp ?? 0,
|
|
|
+ ),
|
|
|
+ style: _vipTextStyle(isHighlight: true),
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 登录但不是会员
|
|
|
+ return [
|
|
|
+ TextSpan(text: username, style: _vipTextStyle(isHighlight: true)),
|
|
|
+ TextSpan(text: StringName.memberCardNoVipDesc, style: _vipTextStyle()),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 统一的会员状态文本样式
|
|
|
+ TextStyle _vipTextStyle({bool isHighlight = false}) {
|
|
|
+ return TextStyle(
|
|
|
+ color: isHighlight ? const Color(0xFFFFD273) : Colors.white,
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildGoodsCard() {
|
|
|
+
|
|
|
+ return Container(
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ top: 16.h,
|
|
|
+ left: 16.w,
|
|
|
+ right: 16.w,
|
|
|
+ bottom: 24.h,
|
|
|
+ ),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.white,
|
|
|
+ borderRadius: BorderRadius.only(
|
|
|
+ topLeft: Radius.circular(16.r),
|
|
|
+ topRight: Radius.circular(16.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Assets.images.iconGoodsInfoTitle.image(
|
|
|
+ width: 115.w,
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ ),
|
|
|
+ SizedBox(height: 16.h),
|
|
|
+ Obx(() {
|
|
|
+ return Column(
|
|
|
+ children:
|
|
|
+ controller.goodsInfoList.map((item) {
|
|
|
+ return Obx(() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () => controller.onGoodsItemClick(item),
|
|
|
+ child: _buildGoodsItem(
|
|
|
+ item,
|
|
|
+ controller.selectedGoodsInfoItem?.id == item.id,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }).toList(),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ _buildPayWayCard(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPayWayCard() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () => controller.clickPayWaySwitch(),
|
|
|
+ child: Container(
|
|
|
+ height: 36.h,
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ side: BorderSide(width: 1, color: const Color(0xFFECEBE0)),
|
|
|
+ borderRadius: BorderRadius.circular(10.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ StringName.storePayWay,
|
|
|
+ style: Styles.getTextStyleBlack204W400(14.sp),
|
|
|
+ ),
|
|
|
+ const Spacer(),
|
|
|
+ Obx(() {
|
|
|
+ if (controller.selectedPayWay == null) {
|
|
|
+ return SizedBox.shrink();
|
|
|
+ }
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ Image.asset(
|
|
|
+ getPaymentIconPath(
|
|
|
+ payMethod: controller.selectedPayWay!.payMethod,
|
|
|
+ payPlatform: controller.selectedPayWay!.payPlatform,
|
|
|
+ ),
|
|
|
+ width: 20.w,
|
|
|
+ height: 20.w,
|
|
|
+ ),
|
|
|
+ SizedBox(width: 4.w),
|
|
|
+ Text(
|
|
|
+ controller.selectedPayWay?.title ?? '',
|
|
|
+ style: Styles.getTextStyleBlack204W400(14.sp),
|
|
|
+ ),
|
|
|
+ SizedBox(width: 6.w),
|
|
|
+ Assets.images.iconStoreSwitchPay.image(
|
|
|
+ width: 20.w,
|
|
|
+ height: 20.w,
|
|
|
+ fit: BoxFit.fill,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildGoodsItem(item, bool isSelected) {
|
|
|
+ return Container(
|
|
|
+ margin: EdgeInsets.only(bottom: 8.h),
|
|
|
+ width: 296.w,
|
|
|
+ height: 70.h,
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ begin: Alignment(0.77, -0.00),
|
|
|
+ end: Alignment(0.77, 1.00),
|
|
|
+ colors:
|
|
|
+ isSelected
|
|
|
+ ? [const Color(0xFFFF9416), const Color(0xFFFF7813)]
|
|
|
+ : [const Color(0xFFFEE057), const Color(0xFFFFC400)],
|
|
|
+ ),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(10.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ width: 212.w,
|
|
|
+ height: 70.h,
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ gradient:
|
|
|
+ isSelected
|
|
|
+ ? LinearGradient(
|
|
|
+ begin: Alignment(-0.06, 0.50),
|
|
|
+ end: Alignment(1.14, 0.50),
|
|
|
+ colors: [
|
|
|
+ const Color(0xFFFFF895),
|
|
|
+ const Color(0xFFFFE941),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ : null,
|
|
|
+ color: isSelected ? null : const Color(0xFFFFFDEE),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ side: BorderSide(width: 1, color: const Color(0xFFFEE86B)),
|
|
|
+ borderRadius: BorderRadius.circular(isSelected ? 8 : 10.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Stack(
|
|
|
+ children: [
|
|
|
+ if (isSelected)
|
|
|
+ IgnorePointer(
|
|
|
+ child: Assets.images.bgStoreSelectedItem.image(
|
|
|
+ width: 212.w,
|
|
|
+ height: 70.h,
|
|
|
+ fit: BoxFit.fill,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Padding(
|
|
|
+ padding: EdgeInsets.only(left: 16.w),
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ RichText(
|
|
|
+ text: TextSpan(
|
|
|
+ children: [
|
|
|
+ TextSpan(
|
|
|
+ text: '¥',
|
|
|
+ style: Styles.getTextStyleFF663300W700(14.sp),
|
|
|
+ ),
|
|
|
+ TextSpan(
|
|
|
+ text: item.priceDescNumber,
|
|
|
+ style: Styles.getTextStyleFF663300W700(18.sp),
|
|
|
+ ),
|
|
|
+ TextSpan(
|
|
|
+ text: item.priceDescUnit,
|
|
|
+ style: Styles.getTextStyleFF663300W400(13.sp),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ if (item.mostDesc?.isNotEmpty == true)
|
|
|
+ Container(
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ left: 16.w,
|
|
|
+ top: 2.h,
|
|
|
+ bottom: 2.h,
|
|
|
+ ),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ image: DecorationImage(
|
|
|
+ image: Assets.images.iconStoreMost.provider(),
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ alignment: Alignment.bottomLeft,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Text(
|
|
|
+ item.mostDesc!,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ letterSpacing: -0.60,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ Text(
|
|
|
+ item.description!,
|
|
|
+ style: Styles.getTextStyle99673300W400(12.sp),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ // 右侧
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ item.name,
|
|
|
+ style:
|
|
|
+ isSelected
|
|
|
+ ? Styles.getTextStyleFFECBBW500(15.sp)
|
|
|
+ : Styles.getTextStyleFF663300W500(15.sp),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 8.w),
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ color: isSelected ? const Color(0xFFFFECBB) : null,
|
|
|
+ gradient:
|
|
|
+ isSelected
|
|
|
+ ? null
|
|
|
+ : LinearGradient(
|
|
|
+ begin: Alignment(0.77, -0.00),
|
|
|
+ end: Alignment(0.77, 1.00),
|
|
|
+ colors: [
|
|
|
+ const Color(0xFFFF9416),
|
|
|
+ const Color(0xFFFF7813),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(
|
|
|
+ isSelected ? 17.r : 10.r,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Text(
|
|
|
+ '¥${item.amountText}',
|
|
|
+ textAlign: TextAlign.center,
|
|
|
+ style:
|
|
|
+ isSelected
|
|
|
+ ? Styles.getTextStyleFF7F14W500(12.sp)
|
|
|
+ : Styles.getTextStyleWhiteW500(12.sp),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildVipDesc() {
|
|
|
+ return Container(
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ width: double.infinity,
|
|
|
+ height: 36.h,
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ begin: Alignment(0.00, 0.50),
|
|
|
+ end: Alignment(1.00, 0.50),
|
|
|
+ colors: [const Color(0x7FFFF3A3), const Color(0x21FFF3A3)],
|
|
|
+ ),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.only(
|
|
|
+ bottomLeft: Radius.circular(20.r),
|
|
|
+ bottomRight: Radius.circular(20.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ shadows: [
|
|
|
+ BoxShadow(
|
|
|
+ color: Colors.black.withAlpha(10),
|
|
|
+ blurRadius: 10.r,
|
|
|
+ spreadRadius: 0.r,
|
|
|
+ offset: Offset(0, 8.r),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ child: Text("*选中商品时展示文案", style: Styles.getTextStyle99673300W400(12.sp)),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 轮播图
|
|
|
+ Widget _buildBanner(BuildContext context) {
|
|
|
+ return SizedBox(
|
|
|
+ width: double.infinity,
|
|
|
+ child: Stack(
|
|
|
+ children: [
|
|
|
+ CarouselSlider(
|
|
|
+ carouselController: controller.carouselSliderController,
|
|
|
+ options: CarouselOptions(
|
|
|
+ height: 280.h,
|
|
|
+ viewportFraction: 1,
|
|
|
+ initialPage: 0,
|
|
|
+ enableInfiniteScroll: true,
|
|
|
+ reverse: false,
|
|
|
+ autoPlay: true,
|
|
|
+ autoPlayInterval: const Duration(seconds: 3),
|
|
|
+ autoPlayAnimationDuration: const Duration(milliseconds: 800),
|
|
|
+ autoPlayCurve: Curves.fastOutSlowIn,
|
|
|
+ scrollDirection: Axis.horizontal,
|
|
|
+ onPageChanged: (index, reason) {
|
|
|
+ controller.onBannerChanged(index, reason);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+
|
|
|
+ items:
|
|
|
+ controller.bannerList.map((item) {
|
|
|
+ return item.banner.image(
|
|
|
+ width: double.infinity,
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ );
|
|
|
+ }).toList(),
|
|
|
+ ),
|
|
|
+ Positioned(bottom: 0, left: 0, right: 0, child: _buildIndicator()),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 指示器
|
|
|
+ _buildIndicator() {
|
|
|
+ return Container(
|
|
|
+ height: 36.h,
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ color: const Color(0xE5121212),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(21.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
|
+ children:
|
|
|
+ controller.bannerList.asMap().entries.map((entry) {
|
|
|
+ return Obx(() {
|
|
|
+ final isSelectedBanner =
|
|
|
+ controller.currentBannerIndex == entry.key;
|
|
|
+ return Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap:
|
|
|
+ () => controller.carouselSliderController
|
|
|
+ .animateToPage(entry.key),
|
|
|
+ child: SizedBox(
|
|
|
+ width: 100.w,
|
|
|
+ child: Stack(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ clipBehavior: Clip.none,
|
|
|
+ children: [
|
|
|
+ if (isSelectedBanner)
|
|
|
+ Positioned(
|
|
|
+ top: -8.h,
|
|
|
+ child: controller
|
|
|
+ .bannerList[entry.key]
|
|
|
+ .indicatorImg
|
|
|
+ .image(
|
|
|
+ width: 100.w,
|
|
|
+ height: 40.h,
|
|
|
+ fit: BoxFit.fill,
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ else
|
|
|
+ Text(
|
|
|
+ controller.bannerList[entry.key].unSelectedDesc,
|
|
|
+ style: Styles.getTextStyleWhiteW400(14.sp),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ if (entry.key != controller.bannerList.length - 1)
|
|
|
+ Padding(
|
|
|
+ padding: EdgeInsets.only(left: 4.w),
|
|
|
+ child: Assets.images.iconStoreDivider.image(
|
|
|
+ width: 2.w,
|
|
|
+ height: 17.h,
|
|
|
+ fit: BoxFit.fill,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }).toList(),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildUserReviews() {
|
|
|
+ return Container(
|
|
|
+ width: double.infinity,
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Assets.images.iconStoreUserReviewsTitle.image(
|
|
|
+ width: 240.w,
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ ),
|
|
|
+ SizedBox(height: 16.h),
|
|
|
+ Container(
|
|
|
+ width: double.infinity,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ borderRadius: BorderRadius.circular(19.r),
|
|
|
+ gradient: LinearGradient(
|
|
|
+ begin: Alignment.topCenter,
|
|
|
+ end: Alignment.bottomCenter,
|
|
|
+ colors: [Colors.white, Color(0xfffff8d4)],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ SizedBox(height: 20.h),
|
|
|
+ Container(
|
|
|
+ width: 326.w,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ borderRadius: BorderRadius.circular(19.r),
|
|
|
+ image: DecorationImage(
|
|
|
+ alignment: Alignment.topCenter,
|
|
|
+ image: Assets.images.bgStoreUserReviews.provider(),
|
|
|
+ fit: BoxFit.contain,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ padding: EdgeInsets.only(left: 16.w),
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ height: 39.h,
|
|
|
+ child: Assets.images.iconStoreUserReviewsLogo.image(
|
|
|
+ width: 92.w,
|
|
|
+ height: 24.h,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ width: 326.w,
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ color: Colors.white,
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(16.r),
|
|
|
+ ),
|
|
|
+ shadows: [
|
|
|
+ BoxShadow(
|
|
|
+ color: Colors.black.withAlpha(10),
|
|
|
+ blurRadius: 10.r,
|
|
|
+ spreadRadius: 0.r,
|
|
|
+ offset: Offset(0, 5.r),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children:
|
|
|
+ controller.userReviewsList.map((item) {
|
|
|
+ return _buildReviewsItem(item);
|
|
|
+ }).toList(),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildReviewsItem(StoreUserReviewsBean item) {
|
|
|
+ return Container(
|
|
|
+ padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
|
|
|
+
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ item.avatar.image(width: 28.w, height: 28.h, fit: BoxFit.cover),
|
|
|
+ SizedBox(width: 10.w),
|
|
|
+ Text(
|
|
|
+ item.userName,
|
|
|
+ style: Styles.getTextStyleBlack204W500(14.sp),
|
|
|
+ ),
|
|
|
+ SizedBox(width: 6.w),
|
|
|
+ Assets.images.iconStorePermanentMember.image(
|
|
|
+ width: 70.w,
|
|
|
+ height: 20.h,
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ SizedBox(height: 4.h),
|
|
|
+ Padding(
|
|
|
+ padding: EdgeInsets.only(left: 38.w),
|
|
|
+ child: Text(
|
|
|
+ item.userReviews,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withAlpha(153),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(height: 12.h),
|
|
|
+
|
|
|
+ if (controller.userReviewsList.indexOf(item) !=
|
|
|
+ controller.userReviewsList.length - 1)
|
|
|
+ HorizontalDashedLine(
|
|
|
+ width: 304.w,
|
|
|
+ color: const Color(0XFFEDEBE1),
|
|
|
+ strokeWidth: 2.h,
|
|
|
+ dashLength: 4.w,
|
|
|
+ dashSpace: 4.w,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户须知
|
|
|
+ Widget _buildUserNotice() {
|
|
|
+ return Container(
|
|
|
+ margin: EdgeInsets.symmetric(horizontal: 16.w),
|
|
|
+
|
|
|
+ decoration: BoxDecoration(borderRadius: BorderRadius.circular(16.r)),
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Text('购买须知', style: Styles.getTextStyleFF663300W400(12.sp)),
|
|
|
+ SizedBox(height: 8.h),
|
|
|
+ Text(
|
|
|
+ "1. 本产品为互联网技术服务虚拟产品,会员购买成功立即生效,一经开通不支持退款。\n"
|
|
|
+ "2. 本产品为付费会员制产品,会员费用是指开通平台所有功能使用权限的基础费用。开通权限后,部分功能可能会免费(具体以实际展示情况为准)使用,部分功能需消耗一定的虚拟币(金币)方可使用。\n"
|
|
|
+ "3. 各种功能的金币消耗可参考虚拟币规则(金币中心)。\n"
|
|
|
+ "4. 开通会员后,若赠送虚拟币(金币)消耗完毕,可单独购买金币。\n"
|
|
|
+ "5. 根据业务情况,我方有权调整不同功能的虚拟币消耗价格,价格会在最新的金币中心展示。\n"
|
|
|
+ "6. 请在购买前仔细阅读《会员服务协议》和购买须知,确定您同意所有条款后继续操作。",
|
|
|
+ style: Styles.getTextStyle99673300W400(10.sp),
|
|
|
+ ),
|
|
|
+ SizedBox(height: 120.h),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildBuyButtonCard() {
|
|
|
+ return Container(
|
|
|
+ width: 360.w,
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 11.h),
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ color: Colors.white,
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.only(
|
|
|
+ topLeft: Radius.circular(24.r),
|
|
|
+ topRight: Radius.circular(24.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ shadows: [
|
|
|
+ BoxShadow(
|
|
|
+ color: Color(0x99FFE498),
|
|
|
+ blurRadius: 20,
|
|
|
+ offset: Offset(0, 0),
|
|
|
+ spreadRadius: 0,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap: controller.clickPayNow,
|
|
|
+ child: Container(
|
|
|
+ width: 328.w,
|
|
|
+ height: 54.h,
|
|
|
+ decoration: ShapeDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ begin: Alignment(0.60, -0.39),
|
|
|
+ end: Alignment(0.60, 0.95),
|
|
|
+ colors: [
|
|
|
+ const Color(0xFFF95FAC),
|
|
|
+ const Color(0xFFFD5E4D),
|
|
|
+ const Color(0xFFFD5E4D),
|
|
|
+ const Color(0xFFFB8A3C),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(30.55.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ StringName.storePayNow,
|
|
|
+ style: Styles.getTextStyleWhiteW500(17.sp),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),),
|
|
|
+ SizedBox(height: 11.h),
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Obx(() {
|
|
|
+ return GestureDetector(
|
|
|
+ behavior: HitTestBehavior.opaque,
|
|
|
+ onTap: () {
|
|
|
+ controller.isAgree.value = !controller.isAgree.value;
|
|
|
+ },
|
|
|
+ child:
|
|
|
+ controller.isAgree.value
|
|
|
+ ? Assets.images.iconStoreAgreePrivacy.image(
|
|
|
+ width: 16.w,
|
|
|
+ height: 16.w,
|
|
|
+ )
|
|
|
+ : SizedBox(
|
|
|
+ child: Container(
|
|
|
+ padding: EdgeInsets.all(1.w),
|
|
|
+ width: 16.w,
|
|
|
+ height: 16.w,
|
|
|
+ child: Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ shape: BoxShape.circle,
|
|
|
+ border: Border.all(
|
|
|
+ color: Colors.black.withAlpha(153),
|
|
|
+ width: 1.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ Text.rich(
|
|
|
+ TextSpan(
|
|
|
+ children: [
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.textSpanIHaveReadAndAgree,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withAlpha(153),
|
|
|
+ fontSize: 10.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ClickTextSpan(
|
|
|
+ text: StringName.textSpanPrivacyPolicy,
|
|
|
+ url: WebUrl.privacyPolicy,
|
|
|
+ ),
|
|
|
+
|
|
|
+ TextSpan(
|
|
|
+ text: StringName.textSpanAnd,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.black.withAlpha(153),
|
|
|
+ fontSize: 10.sp,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+
|
|
|
+ ClickTextSpan(
|
|
|
+ text: StringName.textSpanServiceTerms,
|
|
|
+ url: WebUrl.serviceAgreement,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|