import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:design/design.dart'; import 'package:design_spec/design_spec.dart'; import 'package:guru_app/financial/iap/iap_model.dart'; import 'package:guru_ui/guru_widget.dart'; import 'package:guru_ui/pages/store/store_page.dart'; import 'package:guru_widgets/button/purchase_button.dart'; import 'package:guru_widgets/button/single_tap_widget.dart'; import 'package:guru_widgets/common/flexible_container.dart'; import 'package:guru_widgets/common/spacer.dart'; import 'package:guru_widgets/localizations/widgets_strings.dart'; import 'package:guru_widgets/theme/guru_theme.dart'; import 'package:guru_popup/guru_popup.dart'; import 'package:guru_utils/widget/widget_utils.dart'; import 'package:guru_utils/feedback/feedback_manager.dart'; /// Created by Haoyi on 2023/6/6 /// part 'purchase_banner.g.dart'; @DesignSpec(width: 750, height: 200, specMode: SpecMode.nested) abstract class PurchaseBannerDesignSpec implements BasicDesignSpec { @SpecHorizontal(40) double get bannerHorizontalSpcing; @SpecHeight(200) double get bannerHeight; @SpecOrigin(12) double get bannerRadius; @SpecHorizontal(354) double get removeAdsBackgroungWidth; @SpecHeight(144) double get removeAdsImageWidth; @SpecVertical(80) double get removeAdsImageEndSpacing; @SpecVertical(28) double get removeAdsImageVerticalSpacing; @SpecWidth(390) double get productDetailsWidth; @SpecAbsoluteFontSize(26, consistent: true) double get productDetailsFontSize; @SpecFontSize(26) double get productPriceFontSize; @SpecOffset(SpecOrigin(0), SpecVertical(1)) Offset get productPriceTextShadowOffset; @SpecVertical(32) double get productDetailsTopSpacing; @SpecHorizontal(40) double get productDetailsStartSpacing; @SpecHorizontal(300) double get productDetailsEndSpacing; @SpecVertical(20) double get purchaseButtonTopSpacing; @SpecVertical(56) double get purchaseButtonHeight; @SpecWidth(168) double get purchaseButtonWidth; @SpecVertical(16) double get tipsSpacing; @SpecHeight(40) double get tipsSize; @SpecHeight(136) double get tipsMinHeight; @SpecWidth(486) double get tipsWidth; @SpecFontSize(26) double get tipsFontSize; @SpecVertical(12) double get tipsGap; @SpecHeight(24) double get tipsRadius; @SpecEdgeInsets.only( top: SpecHeight(22), bottom: SpecHeight(26), start: SpecWidth(32), end: SpecWidth(32)) EdgeInsets get tipsContentPadding; @SpecHeight(70, consistent: true) double get summaryHeight; static PurchaseBannerDesignSpec create(Size size, {Offset offset = Offset.zero}) => _PurchaseBannerDesignSpec.from(size, offset: offset); } class PurchaseBannerStyle { final String name; const PurchaseBannerStyle.create(this.name); // 游戏内货币的资源条 static const PurchaseBannerStyle remove_ad = PurchaseBannerStyle.create("remove_ad"); } class PurchaseBannerStyleTheme { final Color? backgroundColor; final Gradient? backgroundGradient; final String? mainImage; final String? mainBackGround; final String? summary; final String? tipsIcon; final String? tips; final PurchaseButtonStyle? buttonStyle; const PurchaseBannerStyleTheme({ this.backgroundColor, this.backgroundGradient, this.mainImage, this.mainBackGround, this.summary, this.tipsIcon, this.tips, this.buttonStyle }); static const defaultTheme = PurchaseBannerStyleTheme(); } class PurchaseBannerModel { final IapProduct? product; final BannerItem bannerItem; final PurchaseBannerStyleTheme styleTheme; final PurchaseBannerDesignSpec designSpec; final VoidCallback onTap; PurchaseBannerModel( {required this.product, required this.bannerItem, required this.styleTheme, required this.designSpec, required this.onTap}); } class PurchaseBanner extends StatelessWidget { final PurchaseBannerModel model; final GlobalKey tipsKey; IapProduct? get product => model.product; BannerItem get bannerItem => model.bannerItem; PurchaseBannerStyleTheme get styleTheme => model.styleTheme; PurchaseBannerDesignSpec get designSpec => model.designSpec; const PurchaseBanner({Key? key, required this.model, required this.tipsKey}) : super(key: key); @override Widget build(BuildContext context) { final textDirection = Directionality.of(Get.context!); final guruTheme = GuruTheme.of(context); final iconScheme = guruTheme.iconScheme; final color = styleTheme.backgroundColor; final gradient = styleTheme.backgroundGradient ?? const LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [Color(0xFFFFB343), Color(0xFFFFDE7A)]); final useTipsIcon = styleTheme.tipsIcon ?? iconScheme.tipsIcon; return FlexibleContainer( width: double.infinity, child: TapWidget( onTap: () { // model.onTap(); if (guruTheme.feedbackCapabilities.canPerform()) { FeedbackManager.instance.perform(FeedbackOccasion.clickItem); } }, child: FlexibleContainer( width: double.infinity, height: designSpec.bannerHeight, radius: BorderRadius.all(Radius.circular(designSpec.bannerRadius)), color: color, gradient: gradient, child: Stack( fit: StackFit.expand, children: [ Positioned.directional( width: designSpec.removeAdsBackgroungWidth, textDirection: textDirection, end: 0, top: 0, bottom: 0, child: styleTheme.mainBackGround != null ? Transform.flip( flipX: textDirection == TextDirection.ltr, child: Image.asset(styleTheme.mainBackGround!, fit: BoxFit.fitHeight, height: designSpec.bannerHeight), ) : Container()), Positioned.directional( width: designSpec.removeAdsBackgroungWidth, textDirection: textDirection, end: 0, top: 0, bottom: 0, child: styleTheme.mainBackGround != null ? Image.asset(styleTheme.mainImage!, fit: BoxFit.fitHeight, height: designSpec.bannerHeight) : Container()), Positioned.directional( textDirection: textDirection, width: designSpec.measuredSize.width - designSpec.bannerHorizontalSpcing * 2 - designSpec.productDetailsEndSpacing, start: designSpec.productDetailsStartSpacing, top: 16, bottom: 0, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( alignment: AlignmentDirectional.centerStart, height: designSpec.summaryHeight, child: AutoSizeText( bannerItem.summary ?? '', maxLines: 2, overflow: TextOverflow.visible, style: TextStyle( height: 1.4, fontSize: designSpec.productDetailsFontSize, fontWeight: GuruTheme.fwBold, color: const Color(0xFF7E1E00)), )), SizedSpacer(height: designSpec.purchaseButtonTopSpacing), GuruButton( size: Size(designSpec.purchaseButtonWidth, designSpec.purchaseButtonHeight), sizeSpec: GuruButtonSizeSpec.s5, action: product != null ? product!.details.price : 'BUY', onPressed: () { model.onTap(); }) ], )), if (bannerItem.tips != null && bannerItem.tips!.isNotEmpty) Positioned.directional( textDirection: textDirection, end: designSpec.tipsSpacing, top: designSpec.tipsSpacing, child: GestureDetector( onTap: () { GuruPopup.instance.showTipsOverlay( WidgetUtils.getWidgetBoundary(tipsKey, offset: Offset(0.0, Get.statusBarHeight / Get.pixelRatio)), width: designSpec.tipsWidth, height: designSpec.tipsMinHeight, radius: Radius.circular(designSpec.tipsRadius), backgroundColor: Colors.black.withOpacity(0.9), child: buildTipContent(designSpec, bannerItem.tips)); }, child: useTipsIcon != null ? Image.asset(useTipsIcon, key: tipsKey, width: designSpec.tipsSize, height: designSpec.tipsSize) : Icon(Icons.help_outline, key: tipsKey, size: designSpec.tipsSize, color: Colors.blue)), ), ], ), ), ), ); } Widget buildTipContent(PurchaseBannerDesignSpec designSpec, String? tips) { return Material( child: Padding( padding: designSpec.tipsContentPadding, child: Text( tips ?? '', textAlign: TextAlign.center, style: TextStyle( fontSize: designSpec.tipsFontSize, fontWeight: GuruTheme.fwSemiBold, color: Colors.white, // design : line 40 font size 26 height: 1.6, ), ), ), ); } }