// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../l10n/app_localizations.dart'; const double _vipMaxTextScale = 1.15; const double _vipGradientMiddleStop = 0.48; const double _vipGlowRadius = 0.9; /// 「我的」页高级 VIP 卡:品牌信息、会员徽章、开通按钮与横排权益。 class MineVipBanner extends StatelessWidget { const MineVipBanner({super.key, required this.onOpen}); final VoidCallback onOpen; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Container( key: const Key('mine-vip'), decoration: _decoration(), child: Material( color: AppColors.transparent, borderRadius: BorderRadius.circular(AppDimens.mineVipRadius), clipBehavior: Clip.antiAlias, child: InkWell( onTap: onOpen, child: MediaQuery.withClampedTextScaling( maxScaleFactor: _vipMaxTextScale, child: Stack( children: [ const Positioned.fill(child: _VipAmbientGlow()), Padding( padding: const EdgeInsets.all(AppDimens.mineVipPadding), child: _VipContent(l10n: l10n, onOpen: onOpen), ), ], ), ), ), ), ); } BoxDecoration _decoration() { return BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ AppColors.vipCardStart, AppColors.vipCardMiddle, AppColors.vipCardEnd, ], stops: [0, _vipGradientMiddleStop, 1], ), borderRadius: BorderRadius.circular(AppDimens.mineVipRadius), border: Border.all(color: AppColors.vipCardBorder), boxShadow: const [ BoxShadow( color: AppColors.vipCardRedGlow, blurRadius: AppDimens.spaceXl, offset: Offset(0, AppDimens.spaceSm), ), BoxShadow( color: AppColors.shadowSoft, blurRadius: AppDimens.spaceLg, offset: Offset(0, AppDimens.spaceSm), ), ], ); } } class _VipContent extends StatelessWidget { const _VipContent({required this.l10n, required this.onOpen}); final AppLocalizations l10n; final VoidCallback onOpen; @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded(child: _VipCopy(l10n: l10n)), const SizedBox(width: AppDimens.spaceSm), _VipAction(l10n: l10n, onOpen: onOpen), ], ), const SizedBox(height: AppDimens.spaceSm), const Divider(height: 1, color: AppColors.vipBenefitBorder), const SizedBox(height: AppDimens.spaceSm), _VipBenefits(l10n: l10n), ], ); } } class _VipCopy extends StatelessWidget { const _VipCopy({required this.l10n}); final AppLocalizations l10n; @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ _VipPill(label: l10n.vipMembership), const SizedBox(height: AppDimens.spaceSm), Text( l10n.vipBrandName, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.vipCardText, fontSize: AppDimens.mineVipTitleFont, fontWeight: FontWeight.w700, letterSpacing: -0.4, ), ), const SizedBox(height: AppDimens.spaceXs), Text( l10n.mineVipSubtitle, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.vipCardMuted, fontSize: AppDimens.mineVipSubtitleFont, height: 1.3, ), ), ], ); } } class _VipPill extends StatelessWidget { const _VipPill({required this.label}); final String label; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric( horizontal: AppDimens.spaceSm, vertical: AppDimens.spaceXs, ), decoration: BoxDecoration( color: AppColors.vipBadgeSurface, border: Border.all(color: AppColors.vipBenefitBorder), borderRadius: BorderRadius.circular(AppDimens.pillRadius), ), child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.vipGoldStart, fontSize: AppDimens.chipFont, fontWeight: FontWeight.w700, letterSpacing: 1.1, ), ), ); } } class _VipAction extends StatelessWidget { const _VipAction({required this.l10n, required this.onOpen}); final AppLocalizations l10n; final VoidCallback onOpen; @override Widget build(BuildContext context) { return SizedBox( width: AppDimens.mineVipActionWidth, child: Column( mainAxisSize: MainAxisSize.min, children: [ const _VipEmblem(), const SizedBox(height: AppDimens.spaceXs), _VipOpenButton(label: l10n.vipOpenNow, onOpen: onOpen), ], ), ); } } class _VipEmblem extends StatelessWidget { const _VipEmblem(); @override Widget build(BuildContext context) { return Container( key: const Key('mine-vip-emblem'), width: AppDimens.mineVipEmblem, height: AppDimens.mineVipEmblem, decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.vipGoldStart, AppColors.vipGoldEnd], ), border: Border.all(color: AppColors.vipCardText), boxShadow: const [ BoxShadow(color: AppColors.vipCardGoldGlow, blurRadius: 18), ], ), child: const Icon( Icons.workspace_premium_rounded, color: AppColors.vipCardStart, size: AppDimens.mineVipEmblemIcon, ), ); } } class _VipOpenButton extends StatelessWidget { const _VipOpenButton({required this.label, required this.onOpen}); final String label; final VoidCallback onOpen; @override Widget build(BuildContext context) { return SizedBox( key: const Key('mine-vip-open'), width: AppDimens.mineVipActionWidth, height: AppDimens.mineVipButtonHeight, child: DecoratedBox( decoration: BoxDecoration( gradient: const LinearGradient( colors: [AppColors.vipButtonStart, AppColors.vipButtonEnd], ), borderRadius: BorderRadius.circular(AppDimens.pillRadius), boxShadow: const [ BoxShadow(color: AppColors.vipCardRedGlow, blurRadius: 14), ], ), child: Material( color: AppColors.transparent, child: InkWell( onTap: onOpen, borderRadius: BorderRadius.circular(AppDimens.pillRadius), child: Center( child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.textPrimary, fontSize: AppDimens.body, fontWeight: FontWeight.w700, ), ), ), ), ), ), ); } } class _VipBenefits extends StatelessWidget { const _VipBenefits({required this.l10n}); final AppLocalizations l10n; @override Widget build(BuildContext context) { final items = [ (Icons.all_inclusive_rounded, l10n.vipBenefitFree), (Icons.block_rounded, l10n.vipBenefitNoAds), (Icons.hd_outlined, l10n.vipBenefitHd), (Icons.download_for_offline_outlined, l10n.vipBenefitFastCache), ]; return Row( key: const Key('mine-vip-benefits'), crossAxisAlignment: CrossAxisAlignment.start, children: [ for (var index = 0; index < items.length; index++) Expanded( child: _VipBenefit( itemKey: Key('mine-vip-benefit-$index'), icon: items[index].$1, label: items[index].$2, ), ), ], ); } } class _VipBenefit extends StatelessWidget { const _VipBenefit({ required this.itemKey, required this.icon, required this.label, }); final Key itemKey; final IconData icon; final String label; @override Widget build(BuildContext context) { return Column( key: itemKey, mainAxisAlignment: MainAxisAlignment.start, children: [ Container( width: AppDimens.mineVipBenefitBox, height: AppDimens.mineVipBenefitBox, decoration: BoxDecoration( color: AppColors.vipBenefitSurface, border: Border.all(color: AppColors.vipBenefitBorder), borderRadius: BorderRadius.circular( AppDimens.mineVipBenefitBoxRadius, ), ), child: Icon( icon, color: AppColors.vipGoldStart, size: AppDimens.mineVipBenefitGlyph, ), ), const SizedBox(height: AppDimens.mineVipBenefitRowGap), Text( label, maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: const TextStyle( color: AppColors.vipCardMuted, fontSize: AppDimens.mineVipBenefitFont, fontWeight: FontWeight.w500, height: 1.15, ), ), ], ); } } class _VipAmbientGlow extends StatelessWidget { const _VipAmbientGlow(); @override Widget build(BuildContext context) { return Stack( children: const [ Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( gradient: RadialGradient( center: Alignment(0.85, -0.8), radius: _vipGlowRadius, colors: [AppColors.vipCardGoldGlow, AppColors.transparent], ), ), ), ), Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( gradient: RadialGradient( center: Alignment(-0.9, 1), radius: _vipGlowRadius, colors: [AppColors.vipCardRedGlow, AppColors.transparent], ), ), ), ), ], ); } }