|
|
@@ -1,15 +1,17 @@
|
|
|
import 'dart:async';
|
|
|
-import 'package:intl/intl.dart';
|
|
|
-import 'package:clean/data/bean/store_item.dart';
|
|
|
+
|
|
|
import 'package:clean/base/base_page.dart';
|
|
|
+import 'package:clean/data/bean/store_item.dart';
|
|
|
+import 'package:clean/data/consts/constants.dart';
|
|
|
+import 'package:clean/module/browser/browser_view.dart';
|
|
|
import 'package:clean/module/store/discount/discount_controller.dart';
|
|
|
import 'package:clean/utils/expand.dart';
|
|
|
+import 'package:collection/collection.dart';
|
|
|
import 'package:flutter/Material.dart';
|
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
import 'package:get/get.dart';
|
|
|
-import 'package:clean/module/browser/browser_view.dart';
|
|
|
-import 'package:collection/collection.dart';
|
|
|
-import 'package:clean/data/consts/constants.dart';
|
|
|
+import 'package:intl/intl.dart';
|
|
|
+
|
|
|
import '../../../resource/assets.gen.dart';
|
|
|
import 'count_down_timer.dart';
|
|
|
import 'func_page_view.dart';
|
|
|
@@ -28,10 +30,13 @@ class DiscountPage extends BasePage<DiscountController> {
|
|
|
@override
|
|
|
Widget buildBody(BuildContext context) {
|
|
|
return Obx(() {
|
|
|
- bool isSelectFreeItem = controller.currentSelectedStoreItem.value?.freeTrialName != null;
|
|
|
+ bool isSelectFreeItem =
|
|
|
+ controller.currentSelectedStoreItem.value?.freeTrialName != null;
|
|
|
// 如果有免费试用,则使用免费试用,否则使用第一个
|
|
|
bool canUseFreeTrailPlan = controller.canUseFreeTrailPlan.value;
|
|
|
- StoreItem? freeItem = controller.storeItems.firstWhereOrNull((element) => element.freeTrialName != null) ?? controller.storeItems.firstOrNull;
|
|
|
+ StoreItem? freeItem = controller.storeItems
|
|
|
+ .firstWhereOrNull((element) => element.freeTrialName != null) ??
|
|
|
+ controller.storeItems.firstOrNull;
|
|
|
return Scaffold(
|
|
|
backgroundColor: "#05050D".color,
|
|
|
body: Stack(
|
|
|
@@ -51,7 +56,9 @@ class DiscountPage extends BasePage<DiscountController> {
|
|
|
_DiscountFreeTrialSpecialRow(
|
|
|
item: freeItem,
|
|
|
canUseFreeTrailPlan: canUseFreeTrailPlan,
|
|
|
- isSelected: controller.currentSelectedStoreItem.value?.id == freeItem.id,
|
|
|
+ isSelected:
|
|
|
+ controller.currentSelectedStoreItem.value?.id ==
|
|
|
+ freeItem.id,
|
|
|
onSelect: (title) {
|
|
|
controller.currentSelectedStoreItem.value = freeItem;
|
|
|
},
|
|
|
@@ -61,7 +68,11 @@ class DiscountPage extends BasePage<DiscountController> {
|
|
|
SizedBox(height: 40.h),
|
|
|
_MorePlansSection(),
|
|
|
SizedBox(height: 5.h),
|
|
|
- _OtherPlansSection(items: controller.storeItems.where((element) => element.id != freeItem?.id).toList(), controller: controller),
|
|
|
+ _OtherPlansSection(
|
|
|
+ items: controller.storeItems
|
|
|
+ .where((element) => element.id != freeItem?.id)
|
|
|
+ .toList(),
|
|
|
+ controller: controller),
|
|
|
SizedBox(height: 100.h),
|
|
|
],
|
|
|
),
|
|
|
@@ -97,16 +108,21 @@ class _AppBar extends StatelessWidget {
|
|
|
Widget build(BuildContext context) {
|
|
|
return Row(
|
|
|
children: [
|
|
|
- Container(
|
|
|
- margin: EdgeInsets.only(left: 16.w, top: 14.h),
|
|
|
- child: GestureDetector(
|
|
|
- onTap: () {
|
|
|
- Get.back();
|
|
|
- },
|
|
|
- child: Assets.images.iconStoreClose
|
|
|
- .image(width: 28.w, height: 28.w),
|
|
|
- ),
|
|
|
- ),
|
|
|
+ Obx(() {
|
|
|
+ return Visibility(
|
|
|
+ visible: controller.isShowBackButton.value,
|
|
|
+ child: Container(
|
|
|
+ margin: EdgeInsets.only(left: 16.w, top: 14.h),
|
|
|
+ child: GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ Get.back();
|
|
|
+ },
|
|
|
+ child: Assets.images.iconStoreClose
|
|
|
+ .image(width: 28.w, height: 28.w),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ })
|
|
|
],
|
|
|
);
|
|
|
}
|
|
|
@@ -119,11 +135,9 @@ class _DiscountHeader extends StatelessWidget {
|
|
|
Widget build(BuildContext context) {
|
|
|
return Column(
|
|
|
children: [
|
|
|
- Assets.images.iconDiscountTitle
|
|
|
- .image(width: 259.w, height: 55.h),
|
|
|
+ Assets.images.iconDiscountTitle.image(width: 259.w, height: 55.h),
|
|
|
SizedBox(height: 20.h),
|
|
|
- Assets.images.iconDiscountPercent
|
|
|
- .image(width: 195.w, height: 86.h),
|
|
|
+ Assets.images.iconDiscountPercent.image(width: 195.w, height: 86.h),
|
|
|
SizedBox(height: 13.h),
|
|
|
Container(
|
|
|
width: 197.w,
|
|
|
@@ -220,9 +234,7 @@ class _PurchaseSection extends StatelessWidget {
|
|
|
child: Column(
|
|
|
children: [
|
|
|
Text(
|
|
|
- isSelectFreeItem
|
|
|
- ? ""
|
|
|
- : "Auto-renewalable.Cancel anytime",
|
|
|
+ isSelectFreeItem ? "" : "Auto-renewalable.Cancel anytime",
|
|
|
style: TextStyle(
|
|
|
color: Colors.white.withOpacity(0.8),
|
|
|
fontSize: 12.sp,
|
|
|
@@ -255,39 +267,51 @@ class _PurchaseSection extends StatelessWidget {
|
|
|
),
|
|
|
),
|
|
|
SizedBox(height: 5.h),
|
|
|
- isSelectFreeItem ?
|
|
|
- Text("No payment now!",
|
|
|
- style: TextStyle(
|
|
|
- color: "#57C87A".color,
|
|
|
- fontSize: 12.sp,
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
- ),
|
|
|
- )
|
|
|
- :
|
|
|
- Container(
|
|
|
- alignment: Alignment.center,
|
|
|
- width: double.infinity,
|
|
|
- child: Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.center,
|
|
|
- children: [
|
|
|
- GestureDetector(
|
|
|
- onTap: () {
|
|
|
- BrowserPage.start(Constants.privacyPolicy);
|
|
|
- },
|
|
|
- child: Text("Privacy Policy", style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12.sp, fontWeight: FontWeight.w400)),
|
|
|
- ),
|
|
|
- SizedBox(width: 8.w),
|
|
|
- Text("|", style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12.sp, fontWeight: FontWeight.w400)),
|
|
|
- SizedBox(width: 8.w),
|
|
|
- GestureDetector(
|
|
|
- onTap: () {
|
|
|
- BrowserPage.start(Constants.userAgreement);
|
|
|
- },
|
|
|
- child: Text("Terms of Service", style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12.sp, fontWeight: FontWeight.w400)),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- )
|
|
|
+ isSelectFreeItem
|
|
|
+ ? Text(
|
|
|
+ "No payment now!",
|
|
|
+ style: TextStyle(
|
|
|
+ color: "#57C87A".color,
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ : Container(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ width: double.infinity,
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ BrowserPage.start(Constants.privacyPolicy);
|
|
|
+ },
|
|
|
+ child: Text("Privacy Policy",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white.withOpacity(0.8),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
+ ),
|
|
|
+ SizedBox(width: 8.w),
|
|
|
+ Text("|",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white.withOpacity(0.8),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
+ SizedBox(width: 8.w),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ BrowserPage.start(Constants.userAgreement);
|
|
|
+ },
|
|
|
+ child: Text("Terms of Service",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white.withOpacity(0.8),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ )
|
|
|
],
|
|
|
),
|
|
|
);
|
|
|
@@ -306,7 +330,11 @@ class _MorePlansSection extends StatelessWidget {
|
|
|
children: [
|
|
|
Assets.images.iconStoreMoreArrow.image(width: 8.w, height: 8.w),
|
|
|
SizedBox(width: 5.w),
|
|
|
- Text("More Plans", style: TextStyle(color: 'ffffff'.color.withOpacity(0.7), fontSize: 12.sp, fontWeight: FontWeight.w400)),
|
|
|
+ Text("More Plans",
|
|
|
+ style: TextStyle(
|
|
|
+ color: 'ffffff'.color.withOpacity(0.7),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
SizedBox(width: 5.w),
|
|
|
Assets.images.iconStoreMoreArrow.image(width: 8.w, height: 8.w),
|
|
|
],
|
|
|
@@ -318,7 +346,10 @@ class _MorePlansSection extends StatelessWidget {
|
|
|
class _OtherPlansSection extends StatelessWidget {
|
|
|
final List<StoreItem> items;
|
|
|
final DiscountController controller;
|
|
|
- const _OtherPlansSection({Key? key, required this.items, required this.controller}) : super(key: key);
|
|
|
+
|
|
|
+ const _OtherPlansSection(
|
|
|
+ {Key? key, required this.items, required this.controller})
|
|
|
+ : super(key: key);
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
@@ -326,14 +357,13 @@ class _OtherPlansSection extends StatelessWidget {
|
|
|
return Column(
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
children: [
|
|
|
- ...items.map((item) =>
|
|
|
- Padding(
|
|
|
+ ...items.map((item) => Padding(
|
|
|
padding: EdgeInsets.only(bottom: 12.h),
|
|
|
child: _DiscountFreeTrialSpecialRow(
|
|
|
item: item,
|
|
|
canUseFreeTrailPlan: controller.canUseFreeTrailPlan.value,
|
|
|
- isSelected: controller.currentSelectedStoreItem.value?.id ==
|
|
|
- item.id,
|
|
|
+ isSelected:
|
|
|
+ controller.currentSelectedStoreItem.value?.id == item.id,
|
|
|
onSelect: (item) {
|
|
|
controller.currentSelectedStoreItem.value = item;
|
|
|
},
|
|
|
@@ -355,7 +385,13 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
return item.freeTrialName != null;
|
|
|
}
|
|
|
|
|
|
- const _DiscountFreeTrialSpecialRow({Key? key, required this.item, required this.canUseFreeTrailPlan, required this.isSelected, required this.onSelect}) : super(key: key);
|
|
|
+ const _DiscountFreeTrialSpecialRow(
|
|
|
+ {Key? key,
|
|
|
+ required this.item,
|
|
|
+ required this.canUseFreeTrailPlan,
|
|
|
+ required this.isSelected,
|
|
|
+ required this.onSelect})
|
|
|
+ : super(key: key);
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
@@ -371,17 +407,14 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
|
),
|
|
|
child: Padding(
|
|
|
- padding: EdgeInsets.only(top: 14.h, left: 7.w, right: 9.w, bottom: 2.h),
|
|
|
- child: Text(
|
|
|
- item.freeTrialPriceDesc ?? item.priceDesc ?? "",
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ top: 14.h, left: 7.w, right: 9.w, bottom: 2.h),
|
|
|
+ child: Text(item.freeTrialPriceDesc ?? item.priceDesc ?? "",
|
|
|
style: TextStyle(
|
|
|
color: '#ffffff'.color.withOpacity(0.7),
|
|
|
fontSize: 12,
|
|
|
- fontWeight: FontWeight.w400
|
|
|
- )
|
|
|
- ),
|
|
|
- )
|
|
|
- ),
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
+ )),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
@@ -424,15 +457,26 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
children: [
|
|
|
- Text(item.name, style: TextStyle(color: '#ffffff'.color, fontSize: 16.sp, fontWeight: FontWeight.w700)),
|
|
|
- Text(item.priceDesc ?? "", style: TextStyle(color: '#ffffff'.color.withOpacity(0.6), fontSize: 12.sp, fontWeight: FontWeight.w400)),
|
|
|
+ Text(item.name,
|
|
|
+ style: TextStyle(
|
|
|
+ color: '#ffffff'.color,
|
|
|
+ fontSize: 16.sp,
|
|
|
+ fontWeight: FontWeight.w700)),
|
|
|
+ Text(item.priceDesc ?? "",
|
|
|
+ style: TextStyle(
|
|
|
+ color: '#ffffff'.color.withOpacity(0.6),
|
|
|
+ fontSize: 12.sp,
|
|
|
+ fontWeight: FontWeight.w400)),
|
|
|
],
|
|
|
),
|
|
|
Spacer(),
|
|
|
- Text(amount, style: TextStyle(color: '#ffffff'.color, fontSize: 16.sp, fontWeight: FontWeight.w700)),
|
|
|
+ Text(amount,
|
|
|
+ style: TextStyle(
|
|
|
+ color: '#ffffff'.color,
|
|
|
+ fontSize: 16.sp,
|
|
|
+ fontWeight: FontWeight.w700)),
|
|
|
],
|
|
|
- )
|
|
|
- );
|
|
|
+ ));
|
|
|
}
|
|
|
|
|
|
// 免费标签的容器
|
|
|
@@ -448,8 +492,7 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
'#0279FB'.color,
|
|
|
],
|
|
|
),
|
|
|
- borderRadius: BorderRadius.all(Radius.circular(20.w))
|
|
|
- ),
|
|
|
+ borderRadius: BorderRadius.all(Radius.circular(20.w))),
|
|
|
child: Text(
|
|
|
'No payment now!',
|
|
|
style: TextStyle(
|
|
|
@@ -465,8 +508,7 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
Widget buildBadgeIcon() {
|
|
|
return Positioned(
|
|
|
left: -22,
|
|
|
- child: Assets.images.iconStoreFree.image(width: 30.w, height: 30.w)
|
|
|
- );
|
|
|
+ child: Assets.images.iconStoreFree.image(width: 30.w, height: 30.w));
|
|
|
}
|
|
|
|
|
|
// 免费试用标签
|
|
|
@@ -481,29 +523,29 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
buildBadgeContainer(),
|
|
|
buildBadgeIcon(),
|
|
|
],
|
|
|
- )
|
|
|
- );
|
|
|
+ ));
|
|
|
}
|
|
|
|
|
|
// 主容器
|
|
|
Widget buildMainContainer() {
|
|
|
return Container(
|
|
|
padding: EdgeInsets.all(2),
|
|
|
- decoration: isSelected ? BoxDecoration(
|
|
|
- gradient: LinearGradient(
|
|
|
- begin: Alignment.topLeft,
|
|
|
- end: Alignment.bottomRight,
|
|
|
- colors: [
|
|
|
- '#D2A3FF'.color,
|
|
|
- '#419CFF'.color,
|
|
|
- '#01D0FF'.color,
|
|
|
- ]
|
|
|
- ),
|
|
|
- borderRadius: BorderRadius.circular(20.r),
|
|
|
- ) : BoxDecoration(
|
|
|
- color: '#ffffff'.color.withOpacity(0.2),
|
|
|
- borderRadius: BorderRadius.circular(20.r),
|
|
|
- ),
|
|
|
+ decoration: isSelected
|
|
|
+ ? BoxDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ begin: Alignment.topLeft,
|
|
|
+ end: Alignment.bottomRight,
|
|
|
+ colors: [
|
|
|
+ '#D2A3FF'.color,
|
|
|
+ '#419CFF'.color,
|
|
|
+ '#01D0FF'.color,
|
|
|
+ ]),
|
|
|
+ borderRadius: BorderRadius.circular(20.r),
|
|
|
+ )
|
|
|
+ : BoxDecoration(
|
|
|
+ color: '#ffffff'.color.withOpacity(0.2),
|
|
|
+ borderRadius: BorderRadius.circular(20.r),
|
|
|
+ ),
|
|
|
child: Container(
|
|
|
height: 74.w,
|
|
|
decoration: BoxDecoration(
|
|
|
@@ -513,7 +555,10 @@ class _DiscountFreeTrialSpecialRow extends StatelessWidget {
|
|
|
child: Stack(
|
|
|
clipBehavior: Clip.none,
|
|
|
children: [
|
|
|
- if (canStarFreeTrail) buildTrialNameContent() else buildNormalProductContent(),
|
|
|
+ if (canStarFreeTrail)
|
|
|
+ buildTrialNameContent()
|
|
|
+ else
|
|
|
+ buildNormalProductContent(),
|
|
|
// 右上角FREE标签
|
|
|
if (canStarFreeTrail) buildFreeTrialBadge(),
|
|
|
],
|