import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; import '../../../../app/theme/app_colors.dart'; import '../../../../app/theme/app_typography.dart'; import '../../../../app/theme/app_spacing.dart'; import '../../../../core/services/referral_service.dart'; import '../../../../shared/widgets/genex_button.dart'; import '../../../../app/i18n/app_localizations.dart'; /// A9. 邀请好友 / 推广分享页 /// /// 功能: /// - 专属推荐码二维码(扫码跳转到 App 下载页并自动填入推荐码) /// - 一键复制推荐码 / 复制链接 /// - 系统原生分享(微信、WhatsApp、Telegram、短信、邮件……) /// - 邀请进度(直接推荐人数 + 团队总人数) class SharePage extends StatefulWidget { const SharePage({super.key}); @override State createState() => _SharePageState(); } class _SharePageState extends State { ReferralInfo? _info; bool _loading = true; String? _error; /// App 下载落地页(带推荐码参数) static const String _baseInviteUrl = 'https://app.gogenex.com/download'; String get _inviteLink => _info != null ? '$_baseInviteUrl?ref=${_info!.referralCode}' : _baseInviteUrl; @override void initState() { super.initState(); _loadInfo(); } Future _loadInfo() async { setState(() { _loading = true; _error = null; }); try { final info = await ReferralService.instance.getMyInfo(); if (mounted) setState(() { _info = info; _loading = false; }); } catch (e) { if (mounted) setState(() { _error = e.toString(); _loading = false; }); } } void _showCopied(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.check_circle_rounded, color: Colors.white, size: 18), const SizedBox(width: 8), Text(message), ], ), backgroundColor: AppColors.primary, behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 2), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), margin: const EdgeInsets.fromLTRB(16, 0, 16, 16), ), ); } Future _copyCode() async { if (_info == null) return; await Clipboard.setData(ClipboardData(text: _info!.referralCode)); if (mounted) _showCopied(context.t('share.codeCopied')); } Future _copyLink() async { await Clipboard.setData(ClipboardData(text: _inviteLink)); if (mounted) _showCopied(context.t('share.linkCopied')); } Future _shareNative() async { final code = _info?.referralCode ?? ''; final text = context .t('share.shareText') .replaceAll('{code}', code) .replaceAll('{link}', _inviteLink); await Share.share(text, subject: context.t('share.nativeShareTitle')); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), onPressed: () => Navigator.of(context).pop(), ), title: Text(context.t('share.title')), backgroundColor: Colors.transparent, elevation: 0, surfaceTintColor: Colors.transparent, ), body: _loading ? _buildLoading() : _error != null ? _buildError() : _buildContent(), ); } Widget _buildLoading() { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text( context.t('share.loading'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary), ), ], ), ); } Widget _buildError() { return Center( child: Padding( padding: AppSpacing.pagePadding, child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline_rounded, size: 56, color: AppColors.textTertiary), const SizedBox(height: 16), Text( context.t('share.loadFailed'), style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondary), textAlign: TextAlign.center, ), const SizedBox(height: 20), TextButton.icon( onPressed: _loadInfo, icon: const Icon(Icons.refresh_rounded), label: Text(context.t('share.retry')), style: TextButton.styleFrom(foregroundColor: AppColors.primary), ), ], ), ), ); } Widget _buildContent() { return SingleChildScrollView( padding: AppSpacing.pagePadding, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildHeroCard(), const SizedBox(height: 16), _buildStatsCard(), const SizedBox(height: 16), _buildShareActions(), const SizedBox(height: 24), GenexButton( label: context.t('share.shareToFriend'), onPressed: _shareNative, ), const SizedBox(height: 40), ], ), ); } // ── Hero Card: QR 码 + 推荐码 ───────────────────────────────────────────── Widget _buildHeroCard() { return Container( decoration: BoxDecoration( gradient: AppColors.cardGradient, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: AppColors.primary.withOpacity(0.35), blurRadius: 24, offset: const Offset(0, 8), ), ], ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28), child: Column( children: [ // 顶部标题 Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.auto_awesome_rounded, color: Colors.white60, size: 16), const SizedBox(width: 6), Text( context.t('share.scanToJoin'), style: AppTypography.bodyMedium.copyWith(color: Colors.white70), ), ], ), const SizedBox(height: 6), Text( 'Genex', style: AppTypography.displayMedium.copyWith( color: Colors.white, letterSpacing: 3, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 24), // QR 码卡片 Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.12), blurRadius: 16, offset: const Offset(0, 4), ), ], ), child: QrImageView( data: _inviteLink, version: QrVersions.auto, size: 180, backgroundColor: Colors.white, eyeStyle: const QrEyeStyle( eyeShape: QrEyeShape.square, color: Color(0xFF4834D4), ), dataModuleStyle: const QrDataModuleStyle( dataModuleShape: QrDataModuleShape.square, color: Color(0xFF6C5CE7), ), ), ), const SizedBox(height: 24), // 推荐码 Text( context.t('share.myReferralCode'), style: AppTypography.caption.copyWith(color: Colors.white60), ), const SizedBox(height: 8), GestureDetector( onTap: _copyCode, child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), borderRadius: BorderRadius.circular(40), border: Border.all(color: Colors.white30), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( _info?.referralCode ?? '------', style: AppTypography.h2.copyWith( color: Colors.white, letterSpacing: 4, fontWeight: FontWeight.w700, fontFamily: 'monospace', ), ), const SizedBox(width: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.copy_rounded, size: 12, color: Colors.white), const SizedBox(width: 4), Text( context.t('share.copyCode'), style: AppTypography.caption.copyWith( color: Colors.white, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ), ), ], ), ), ); } // ── 邀请进度 ────────────────────────────────────────────────────────────── Widget _buildStatsCard() { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: AppSpacing.borderRadiusMd, border: Border.all(color: AppColors.borderLight), boxShadow: AppSpacing.shadowSm, ), child: Row( children: [ Expanded( child: _buildStatItem( icon: Icons.people_rounded, value: '${_info?.directReferralCount ?? 0}', label: context.t('share.directReferrals'), ), ), Container(width: 1, height: 44, color: AppColors.borderLight), Expanded( child: _buildStatItem( icon: Icons.groups_rounded, value: '${_info?.totalTeamCount ?? 0}', label: context.t('share.teamSize'), ), ), ], ), ); } Widget _buildStatItem({ required IconData icon, required String value, required String label, }) { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 16, color: AppColors.primary), const SizedBox(width: 4), Text( value, style: AppTypography.h2.copyWith(color: AppColors.primary), ), ], ), const SizedBox(height: 2), Text( label, style: AppTypography.caption.copyWith(color: AppColors.textSecondary), ), ], ); } // ── 分享操作列表 ─────────────────────────────────────────────────────────── Widget _buildShareActions() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 4, bottom: 10), child: Text( context.t('share.quickShare'), style: AppTypography.labelSmall.copyWith(color: AppColors.textTertiary), ), ), Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: AppSpacing.borderRadiusMd, border: Border.all(color: AppColors.borderLight), boxShadow: AppSpacing.shadowSm, ), child: Column( children: [ _buildActionTile( icon: Icons.qr_code_rounded, iconBg: AppColors.primaryContainer, iconColor: AppColors.primary, title: context.t('share.copyCode'), subtitle: _info?.referralCode ?? '', onTap: _copyCode, ), const Divider(indent: 60, height: 1), _buildActionTile( icon: Icons.link_rounded, iconBg: AppColors.infoLight, iconColor: AppColors.info, title: context.t('share.copyLink'), subtitle: _inviteLink, onTap: _copyLink, ), const Divider(indent: 60, height: 1), _buildActionTile( icon: Icons.share_rounded, iconBg: AppColors.successLight, iconColor: AppColors.success, title: context.t('share.shareToFriend'), subtitle: context.t('share.shareSubtitle'), onTap: _shareNative, ), ], ), ), ], ); } Widget _buildActionTile({ required IconData icon, required Color iconBg, required Color iconColor, required String title, required String subtitle, required VoidCallback onTap, }) { return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2), leading: Container( width: 38, height: 38, decoration: BoxDecoration(color: iconBg, borderRadius: BorderRadius.circular(10)), child: Icon(icon, size: 20, color: iconColor), ), title: Text(title, style: AppTypography.bodyMedium), subtitle: Text( subtitle, style: AppTypography.caption.copyWith(color: AppColors.textTertiary), maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: const Icon(Icons.chevron_right_rounded, color: AppColors.textTertiary, size: 20), onTap: onTap, ); } }