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/module/store/discount/discount_controller.dart'; import 'package:clean/utils/expand.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 '../../../resource/assets.gen.dart'; import 'count_down_timer.dart'; import 'func_page_view.dart'; class DiscountPage extends BasePage { const DiscountPage({super.key}); @override bool immersive() { return true; } @override bool statusBarDarkFont() => false; @override Widget buildBody(BuildContext context) { return Obx(() { bool isSelectFreeItem = controller.currentSelectedStoreItem.value?.freeTrialName != null; // 如果有免费试用,则使用免费试用,否则使用第一个 StoreItem? freeItem = controller.storeItems.firstWhereOrNull((element) => element.freeTrialName != null) ?? controller.storeItems.firstOrNull; return Scaffold( backgroundColor: "#05050D".color, body: Stack( children: [ SafeArea( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ _AppBar(), _DiscountHeader(), SizedBox(height: 26.h), // 创建一个1分钟的倒计时 CountdownTimer(duration: const Duration(minutes: 1)), SizedBox(height: 40.h), if (freeItem != null) _DiscountFreeTrialSpecialRow( item: freeItem, isSelected: controller.currentSelectedStoreItem.value?.id == freeItem.id, onSelect: (title) { controller.currentSelectedStoreItem.value = freeItem; }, ), SizedBox(height: 40.h), _FeaturesPreview(), SizedBox(height: 40.h), _MorePlansSection(), SizedBox(height: 5.h), _OtherPlansSection(items: controller.storeItems.where((element) => element.id != freeItem?.id).toList(), controller: controller), SizedBox(height: 100.h), ], ), ), ), SafeArea( top: false, child: Column( mainAxisSize: MainAxisSize.max, children: [ Spacer(), SizedBox( width: double.infinity, child: _PurchaseSection( isSelectFreeItem: isSelectFreeItem, controller: controller, ), ) ], ), ), ], ), ); }); } } class _AppBar extends StatelessWidget { const _AppBar({Key? key}) : super(key: key); @override 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), ), ), ], ); } } class _DiscountHeader extends StatelessWidget { const _DiscountHeader({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Assets.images.iconDiscountTitle .image(width: 259.w, height: 55.h), SizedBox(height: 20.h), Assets.images.iconDiscountPercent .image(width: 195.w, height: 86.h), SizedBox(height: 13.h), Container( width: 197.w, height: 32.h, padding: EdgeInsets.all(1.w), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ '#CF9EFD'.color, '#4DCFFF'.color.withOpacity(0.5), ], ), borderRadius: BorderRadius.all(Radius.circular(18.r)), ), child: Container( decoration: BoxDecoration( color: "#05050D".color, borderRadius: BorderRadius.all(Radius.circular(18.r)), ), child: Center( child: Text( "Get CleanPro Premium", style: TextStyle( color: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.w700, ), ), ), ), ), ], ); } } class _FeaturesPreview extends StatelessWidget { const _FeaturesPreview({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return InfinitePreviewPageView( height: 98.h, autoPlay: true, autoPlayDuration: const Duration(seconds: 3), items: [ PreviewItem( title: 'One-click Remove Similar Photos', icon: Assets.images.iconStoreSimilar.image(), ), PreviewItem( title: 'Detect Blurry and Junk Photos', icon: Assets.images.iconStoreAi.image(), ), PreviewItem( title: 'Merge Duplicate Contacts', icon: Assets.images.iconStoreContacts.image(), ), PreviewItem( title: 'Premium Unlimited', icon: Assets.images.iconStorePremium.image(), ), ], showIndicator: true, ); } } class _PurchaseSection extends StatelessWidget { final bool isSelectFreeItem; final DiscountController controller; const _PurchaseSection({ Key? key, required this.isSelectFreeItem, required this.controller, }) : super(key: key); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ "000000".color.withOpacity(0), "000000".color, ], ), ), child: Column( children: [ Text( isSelectFreeItem ? "" : "Auto-renewalable.Cancel anytime", style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 12.sp, ), ), SizedBox(height: 1.h), GestureDetector( onTap: () { controller.onBuyClick(); }, child: Container( width: 312.w, height: 48.h, decoration: BoxDecoration( color: "#0279FB".color, borderRadius: BorderRadius.all( Radius.circular(24.r), ), ), child: Center( child: Text( isSelectFreeItem ? "START FREE TRIAL" : "Continue", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w700, fontSize: 16.sp, ), ), ), ), ), 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)), ), ], ), ) ], ), ); } } class _MorePlansSection extends StatelessWidget { const _MorePlansSection({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( width: double.infinity, child: Row( mainAxisAlignment: MainAxisAlignment.center, 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)), SizedBox(width: 5.w), Assets.images.iconStoreMoreArrow.image(width: 8.w, height: 8.w), ], ), ); } } class _OtherPlansSection extends StatelessWidget { final List items; final DiscountController controller; const _OtherPlansSection({Key? key, required this.items, required this.controller}) : super(key: key); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ...items.map((item) => Padding( padding: EdgeInsets.only(bottom: 12.h), child: _DiscountFreeTrialSpecialRow( item: item, isSelected: controller.currentSelectedStoreItem.value?.id == item.id, onSelect: (item) { controller.currentSelectedStoreItem.value = item; }, ), )), ], ); } } class _DiscountFreeTrialSpecialRow extends StatelessWidget { final StoreItem item; final bool isSelected; final Function(StoreItem) onSelect; bool get canStarFreeTrail { return item.freeTrialName != null; } const _DiscountFreeTrialSpecialRow({Key? key, required this.item, required this.isSelected, required this.onSelect}) : super(key: key); @override Widget build(BuildContext context) { // 价格信息容器 Widget buildPriceInfoContainer() { return Positioned( bottom: -18.h, child: Container( alignment: Alignment.bottomCenter, height: 43.h, decoration: BoxDecoration( color: '#1B2231'.color, 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 ?? "", style: TextStyle( color: '#ffffff'.color.withOpacity(0.7), fontSize: 12, fontWeight: FontWeight.w400 ) ), ) ), ); } // 试用名称内容 Widget buildTrialNameContent() { return Padding( padding: EdgeInsets.symmetric(vertical: 7.h, horizontal: 12.w), child: SizedBox( width: double.infinity, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.freeTrialName ?? item.name, style: TextStyle( color: isSelected ? '#FFe168'.color : '#ffffff'.color, fontSize: 22.sp, fontWeight: FontWeight.w900, ), ), ], ), ), ); } Widget buildNormalProductContent() { final formatter = NumberFormat.currency( symbol: '\$', decimalDigits: 2, ); var amount = formatter.format(item.amount / 100); return Padding( padding: EdgeInsets.symmetric(vertical: 7.h, horizontal: 12.w), child: Row( children: [ Column( 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)), ], ), Spacer(), Text(amount, style: TextStyle(color: '#ffffff'.color, fontSize: 16.sp, fontWeight: FontWeight.w700)), ], ) ); } // 免费标签的容器 Widget buildBadgeContainer() { return Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ '#53CDFE'.color, '#0279FB'.color, ], ), borderRadius: BorderRadius.all(Radius.circular(20.w)) ), child: Text( 'No payment now!', style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w700, ), ), ); } // 免费标签的图标 Widget buildBadgeIcon() { return Positioned( left: -22, child: Assets.images.iconStoreFree.image(width: 30.w, height: 30.w) ); } // 免费试用标签 Widget buildFreeTrialBadge() { return Positioned( top: -14, right: 10, child: Stack( clipBehavior: Clip.none, alignment: Alignment.centerLeft, children: [ 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), ), child: Container( height: 74.w, decoration: BoxDecoration( color: isSelected ? '#111f4b'.color : '000000'.color, borderRadius: BorderRadius.circular(18.r), ), child: Stack( clipBehavior: Clip.none, children: [ if (canStarFreeTrail) buildTrialNameContent() else buildNormalProductContent(), // 右上角FREE标签 if (canStarFreeTrail) buildFreeTrialBadge(), ], ), ), ); } return Container( margin: EdgeInsets.symmetric(horizontal: 15.w), child: GestureDetector( onTap: () { onSelect(item); }, child: Stack( alignment: Alignment.bottomRight, clipBehavior: Clip.none, children: [ if (canStarFreeTrail) buildPriceInfoContainer(), buildMainContainer(), ], ), ), ); } }