311 lines
10 KiB
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,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|