guru_sdk/guru_ui/lib/pages/store/purchase_banner.dart

311 lines
10 KiB
Dart

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,
),
),
),
);
}
}