2023-12-21 09:14:40 +00:00
|
|
|
|
import 'package:auto_size_text/auto_size_text.dart';
|
|
|
|
|
|
import 'package:design/design.dart';
|
|
|
|
|
|
import 'package:guru_app/financial/iap/iap_model.dart';
|
|
|
|
|
|
import 'package:guru_ui/guru_widget.dart';
|
|
|
|
|
|
import 'package:guru_ui/localizations/ui_strings.dart';
|
|
|
|
|
|
import 'package:guru_ui/pages/store/store_design_spec.dart';
|
|
|
|
|
|
import 'package:guru_ui/pages/store/store_page.dart';
|
|
|
|
|
|
import 'package:guru_widgets/common/flexible_container.dart';
|
|
|
|
|
|
import 'package:guru_widgets/common/spacer.dart';
|
|
|
|
|
|
import 'package:guru_widgets/theme/guru_theme.dart';
|
|
|
|
|
|
import 'package:guru_ui/localizations/l10n/generated/app_localizations.dart';
|
|
|
|
|
|
|
|
|
|
|
|
part 'purchase_card.g.dart';
|
|
|
|
|
|
|
|
|
|
|
|
@DesignSpec(width: 210, height: 320, specMode: SpecMode.useSize)
|
|
|
|
|
|
abstract class PurchaseCardDesignSpec implements BasicDesignSpec {
|
|
|
|
|
|
@SpecRadius.circular(SpecHeight(24))
|
|
|
|
|
|
BorderRadius get cardRadius;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecRadius.circular(SpecHeight(12))
|
|
|
|
|
|
BorderRadius get exhibitionRadius;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecRect.fromLTRB(SpecHorizontal(12), SpecVertical(13), SpecHorizontal(12),
|
|
|
|
|
|
SpecVertical(106))
|
|
|
|
|
|
Rect get exhibitionPositionedRect;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecEdgeInsets.only(
|
|
|
|
|
|
start: SpecHorizontal(12),
|
|
|
|
|
|
end: SpecHorizontal(12),
|
|
|
|
|
|
top: SpecVertical(22),
|
|
|
|
|
|
bottom: SpecVertical(2))
|
|
|
|
|
|
EdgeInsets get extraBonusPadding;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecFontSize(18)
|
|
|
|
|
|
double get bonusFontSize;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecAbsoluteFontSize(36)
|
|
|
|
|
|
double get quantityFontSize;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecEdgeInsets.symmetric(horizontal: SpecHorizontal(12))
|
|
|
|
|
|
EdgeInsets get quantityPadding;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecHeight(4)
|
|
|
|
|
|
double get quantityUnitSpacing;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecHeight(34)
|
|
|
|
|
|
double get quantityUnitSize;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecSize(SpecWidth(144), SpecHeight(112))
|
|
|
|
|
|
Size get illustrationSize;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecEdgeInsets.only(top: SpecVertical(18), bottom: SpecVertical(12))
|
|
|
|
|
|
EdgeInsets get illustrationPadding;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecHeight(12)
|
|
|
|
|
|
double get purchaseButtonTopSpacing;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecAspectHeightSize(60, 2.83333333)
|
|
|
|
|
|
Size get purchaseButtonSize;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecHeight(92, consistent: true)
|
|
|
|
|
|
double get labelSize;
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
@SpecHeight(88, consistent: true)
|
2023-12-21 09:14:40 +00:00
|
|
|
|
double get labelTopSpacing;
|
|
|
|
|
|
|
|
|
|
|
|
@SpecWidth(6)
|
|
|
|
|
|
double get labalEndSpacing;
|
|
|
|
|
|
|
|
|
|
|
|
static PurchaseCardDesignSpec from(Size size,
|
|
|
|
|
|
{Offset offset = Offset.zero}) =>
|
|
|
|
|
|
_PurchaseCardDesignSpec.from(size, offset: offset);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PurchaseCardStyle {
|
|
|
|
|
|
final String name;
|
|
|
|
|
|
|
|
|
|
|
|
const PurchaseCardStyle.create(this.name);
|
|
|
|
|
|
|
|
|
|
|
|
// 游戏内货币的资源条
|
|
|
|
|
|
static const PurchaseCardStyle igc = PurchaseCardStyle.create("_igc");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PurchaseCardStyleTheme {
|
|
|
|
|
|
// 额外Bonus的颜色,如果不提供将填充黑色
|
|
|
|
|
|
final Color? extraBonusColor;
|
|
|
|
|
|
|
|
|
|
|
|
// 数量的颜色,如果不提供将填充黑色
|
|
|
|
|
|
final Color? quantityColor;
|
|
|
|
|
|
|
|
|
|
|
|
// 数量前缀的单位icon, 如果不提供将不显示
|
|
|
|
|
|
final QuantityPrefix? quantityPrefix;
|
|
|
|
|
|
|
|
|
|
|
|
// 数量前前缀的单位icon, 如果不提供将不显示
|
|
|
|
|
|
final String? quantityUnit;
|
|
|
|
|
|
|
|
|
|
|
|
// 是否使用千分位分隔符
|
|
|
|
|
|
final bool? useThousandsSeparator;
|
|
|
|
|
|
|
|
|
|
|
|
// 展览区域的渐变背景,如果不提供将不显示
|
|
|
|
|
|
final Gradient? exhibitionGradient;
|
|
|
|
|
|
|
|
|
|
|
|
// 卡片组中各个卡版的默认背景色,如果不提供将会使用GuruTheme中的containerColor替代
|
|
|
|
|
|
final Color? backgroundColor;
|
|
|
|
|
|
|
|
|
|
|
|
// PurchaseButton的样式
|
|
|
|
|
|
final GuruButtonStyle? purchaseButtonStyle;
|
|
|
|
|
|
|
|
|
|
|
|
PurchaseCardStyleTheme({
|
|
|
|
|
|
this.extraBonusColor,
|
|
|
|
|
|
this.quantityColor,
|
|
|
|
|
|
this.quantityPrefix,
|
|
|
|
|
|
this.quantityUnit,
|
|
|
|
|
|
this.exhibitionGradient,
|
|
|
|
|
|
this.backgroundColor,
|
|
|
|
|
|
this.useThousandsSeparator = true,
|
|
|
|
|
|
this.purchaseButtonStyle,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PurchaseCardModel {
|
|
|
|
|
|
final IapProduct? product;
|
|
|
|
|
|
final CardItem cardItem;
|
|
|
|
|
|
final PurchaseCardStyleTheme styleTheme;
|
|
|
|
|
|
final PurchaseCardDesignSpec designSpec;
|
|
|
|
|
|
final VoidCallback onTap;
|
|
|
|
|
|
|
|
|
|
|
|
PurchaseCardModel(
|
|
|
|
|
|
{required this.product,
|
|
|
|
|
|
required this.cardItem,
|
|
|
|
|
|
required this.styleTheme,
|
|
|
|
|
|
required this.designSpec,
|
|
|
|
|
|
required this.onTap});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PurchaseCard extends StatelessWidget {
|
|
|
|
|
|
final PurchaseCardModel model;
|
|
|
|
|
|
|
|
|
|
|
|
IapProduct? get product => model.product;
|
|
|
|
|
|
|
|
|
|
|
|
CardItem get cardItem => model.cardItem;
|
|
|
|
|
|
|
|
|
|
|
|
PurchaseCardStyleTheme get styleTheme => model.styleTheme;
|
|
|
|
|
|
|
|
|
|
|
|
PurchaseCardDesignSpec get designSpec => model.designSpec;
|
|
|
|
|
|
|
|
|
|
|
|
const PurchaseCard({Key? key, required this.model}) : super(key: key);
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
final extraBonus = cardItem.extraBonus ?? 0;
|
|
|
|
|
|
final uiStrings = UIStrings.get();
|
|
|
|
|
|
|
|
|
|
|
|
return FlexibleContainer(
|
|
|
|
|
|
width: designSpec.measuredSize.width,
|
|
|
|
|
|
height: designSpec.measuredSize.height,
|
|
|
|
|
|
radius: designSpec.cardRadius,
|
|
|
|
|
|
color: styleTheme.backgroundColor,
|
|
|
|
|
|
child: Stack(fit: StackFit.expand, children: [
|
|
|
|
|
|
Positioned(
|
|
|
|
|
|
left: designSpec.exhibitionPositionedRect.left,
|
|
|
|
|
|
right: designSpec.exhibitionPositionedRect.right,
|
|
|
|
|
|
top: designSpec.exhibitionPositionedRect.top,
|
|
|
|
|
|
bottom: designSpec.exhibitionPositionedRect.bottom,
|
|
|
|
|
|
child: DecoratedBox(
|
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
|
borderRadius: designSpec.exhibitionRadius,
|
|
|
|
|
|
gradient: styleTheme.exhibitionGradient))),
|
|
|
|
|
|
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: designSpec.extraBonusPadding,
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
extraBonus > 0
|
|
|
|
|
|
? "${(extraBonus * 100).toInt()}% ${uiStrings.bonus}"
|
|
|
|
|
|
: " ",
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
fontSize: designSpec.bonusFontSize,
|
|
|
|
|
|
fontWeight: GuruTheme.fwSemiBold,
|
|
|
|
|
|
color: styleTheme.extraBonusColor ??
|
|
|
|
|
|
const Color(0xFFFF2830)),
|
|
|
|
|
|
)),
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: designSpec.quantityPadding,
|
|
|
|
|
|
child: Row(
|
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
|
children: [
|
|
|
|
|
|
if (styleTheme.quantityUnit != null) ...[
|
|
|
|
|
|
Image.asset(styleTheme.quantityUnit!,
|
|
|
|
|
|
width: designSpec.quantityUnitSize,
|
|
|
|
|
|
height: designSpec.quantityUnitSize),
|
|
|
|
|
|
SizedSpacer(width: designSpec.quantityUnitSpacing)
|
|
|
|
|
|
],
|
|
|
|
|
|
AutoSizeText(
|
|
|
|
|
|
cardItem.quantity.toString(),
|
|
|
|
|
|
minFontSize: 5,
|
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
|
fontSize: designSpec.quantityFontSize,
|
|
|
|
|
|
fontWeight: GuruTheme.fwBold,
|
|
|
|
|
|
color: styleTheme.quantityColor),
|
|
|
|
|
|
)
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
Padding(
|
|
|
|
|
|
padding: designSpec.illustrationPadding,
|
|
|
|
|
|
child: AdaptiveImage(
|
|
|
|
|
|
cardItem.illustration,
|
|
|
|
|
|
width: designSpec.illustrationSize.width,
|
|
|
|
|
|
height: designSpec.illustrationSize.height,
|
|
|
|
|
|
fit: BoxFit.cover
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
GuruButton(
|
|
|
|
|
|
size: designSpec.purchaseButtonSize,
|
|
|
|
|
|
sizeSpec: GuruButtonSizeSpec.s5,
|
|
|
|
|
|
style: GuruButtonStyle.purchase,
|
2024-03-07 03:46:50 +00:00
|
|
|
|
action: product != null ? product!.details.price : '1000000000',
|
2023-12-21 09:14:40 +00:00
|
|
|
|
onPressed: () {
|
|
|
|
|
|
model.onTap.call();
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
]),
|
|
|
|
|
|
if (cardItem.label != null)
|
|
|
|
|
|
Positioned.directional(textDirection: Directionality.of(context),
|
|
|
|
|
|
top: designSpec.labelTopSpacing,
|
|
|
|
|
|
end: designSpec.labalEndSpacing,
|
|
|
|
|
|
child: AdaptiveImage(
|
|
|
|
|
|
cardItem.label!,
|
|
|
|
|
|
width: designSpec.labelSize,
|
|
|
|
|
|
height: designSpec.labelSize,
|
|
|
|
|
|
))
|
|
|
|
|
|
]));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|