import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/app_colors.dart'; import '../core/app_design_tokens.dart'; import '../core/navigation_provider.dart'; import '../providers/ai_consent_provider.dart'; import '../providers/auth_provider.dart'; class AiConsentGate extends ConsumerStatefulWidget { final Widget child; const AiConsentGate({super.key, required this.child}); @override ConsumerState createState() => _AiConsentGateState(); } class _AiConsentGateState extends ConsumerState { String? _loadedUserId; @override Widget build(BuildContext context) { final user = ref.watch(authProvider).user; if (user == null || user.id.isEmpty || user.role != 'User') { return widget.child; } if (_loadedUserId != user.id) { _loadedUserId = user.id; Future.microtask( () => ref.read(aiConsentProvider.notifier).load(user.id), ); } final consent = ref.watch(aiConsentProvider); final currentRoute = ref.watch(currentRouteProvider).name; final isConsentInformationRoute = currentRoute == 'aiConsentDetails' || currentRoute == 'staticText'; if (consent.userId != user.id || consent.isLoading) { return const _ConsentLoadingPage(); } if (consent.granted || isConsentInformationRoute) return widget.child; return _ConsentPage(userId: user.id); } } class _ConsentLoadingPage extends StatelessWidget { const _ConsentLoadingPage(); @override Widget build(BuildContext context) { return Scaffold( body: DecoratedBox( decoration: const BoxDecoration(gradient: AppColors.bgGradient), child: const SafeArea( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 30, height: 30, child: CircularProgressIndicator( strokeWidth: 3, color: AppColors.primary, ), ), SizedBox(height: 14), Text( '正在加载授权状态…', style: TextStyle( fontSize: 15, color: AppColors.textSecondary, ), ), ], ), ), ), ), ); } } class _ConsentPage extends ConsumerWidget { final String userId; const _ConsentPage({required this.userId}); Future _grant(BuildContext context, WidgetRef ref) async { final succeeded = await ref.read(aiConsentProvider.notifier).grant(userId); if (!succeeded && context.mounted) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('授权保存失败,请稍后重试'))); } } Future _decline(WidgetRef ref) async { await ref.read(authProvider.notifier).logout(); } @override Widget build(BuildContext context, WidgetRef ref) { final consent = ref.watch(aiConsentProvider); return Scaffold( body: DecoratedBox( decoration: const BoxDecoration(gradient: AppColors.bgGradient), child: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Container( padding: const EdgeInsets.fromLTRB(20, 10, 20, 20), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.96), borderRadius: AppRadius.cardBorder, border: Border.all(color: AppColors.primaryLight, width: 1), boxShadow: AppShadows.panel, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Align( child: Image.asset( 'assets/branding/health_login_character_transparent.png', height: 112, fit: BoxFit.contain, semanticLabel: '小脉健康智能助手', ), ), const SizedBox(height: 2), Align( child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: AppColors.primarySoft, borderRadius: AppRadius.pillBorder, ), child: const Text( '小脉健康 · AI 服务', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.primaryDark, ), ), ), ), const SizedBox(height: 14), const Text( 'AI 健康服务授权', textAlign: TextAlign.center, style: TextStyle( fontSize: 22, height: 1.25, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), const SizedBox(height: 14), const Text( '小脉健康的核心功能使用第三方 AI 服务。经你同意后,你主动输入的健康信息、对话内容、图片和报告可能会发送给相关 AI 服务,用于健康记录、饮食识别、报告整理和 AI 回复。', style: TextStyle( fontSize: 15, height: 1.6, color: AppColors.textSecondary, ), ), const SizedBox(height: 10), const Text( 'AI 生成内容仅供健康管理参考,不能替代医生诊断或治疗。', style: TextStyle( fontSize: 14, height: 1.5, color: AppColors.textSecondary, ), ), if (consent.error != null) ...[ const SizedBox(height: 14), _ConsentErrorBanner( message: consent.error!, onRetry: consent.isSaving ? null : () => ref .read(aiConsentProvider.notifier) .load(userId), ), ], const SizedBox(height: 8), TextButton( onPressed: consent.isSaving ? null : () => pushRoute(ref, 'aiConsentDetails'), child: const Text('查看《AI 数据处理说明》'), ), const SizedBox(height: 8), Row( children: [ Expanded( child: _GradientConsentButton( loading: consent.isSaving, onTap: consent.isSaving ? null : () => _grant(context, ref), ), ), const SizedBox(width: 12), Expanded( child: OutlinedButton( onPressed: consent.isSaving ? null : () => _decline(ref), style: OutlinedButton.styleFrom( minimumSize: const Size.fromHeight(50), foregroundColor: AppColors.textSecondary, side: const BorderSide(color: AppColors.border), shape: RoundedRectangleBorder( borderRadius: AppRadius.lgBorder, ), ), child: const Text('不同意并退出'), ), ), ], ), ], ), ), ), ), ), ), ), ); } } class _ConsentErrorBanner extends StatelessWidget { final String message; final VoidCallback? onRetry; const _ConsentErrorBanner({required this.message, this.onRetry}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(12, 10, 8, 10), decoration: BoxDecoration( color: AppColors.errorLight, borderRadius: AppRadius.mdBorder, ), child: Row( children: [ const Icon( Icons.error_outline_rounded, size: 20, color: AppColors.errorText, ), const SizedBox(width: 8), Expanded( child: Text( message, style: const TextStyle(fontSize: 13, color: AppColors.errorText), ), ), if (onRetry != null) TextButton(onPressed: onRetry, child: const Text('重试')), ], ), ); } } class _GradientConsentButton extends StatelessWidget { final bool loading; final VoidCallback? onTap; const _GradientConsentButton({required this.loading, required this.onTap}); @override Widget build(BuildContext context) { return Semantics( button: true, enabled: onTap != null, label: loading ? '正在保存授权' : '同意并继续', child: IgnorePointer( ignoring: onTap == null, child: AnimatedOpacity( duration: const Duration(milliseconds: 160), opacity: onTap == null && !loading ? 0.55 : 1, child: Container( height: 50, decoration: BoxDecoration( gradient: AppColors.primaryGradient, borderRadius: AppRadius.lgBorder, boxShadow: AppColors.buttonShadow, ), child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: AppRadius.lgBorder, child: Center( child: loading ? const SizedBox( width: 22, height: 22, child: CircularProgressIndicator( strokeWidth: 2.4, color: Colors.white, ), ) : const Text( '同意并继续', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, ), ), ), ), ), ), ), ), ); } }