// 家长同意确认页面 — PIPL 第28条合规 // // 未满 14 岁用户选择"学生"角色后,必须经过家长/监护人确认。 // 页面展示隐私政策要点,要求家长勾选同意并确认。 import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../core/constants/design_tokens.dart'; import '../../../core/theme/app_radius.dart'; import '../bloc/auth_bloc.dart'; /// 家长同意确认页面 class ParentalConsentPage extends StatefulWidget { const ParentalConsentPage({super.key}); @override State createState() => _ParentalConsentPageState(); } class _ParentalConsentPageState extends State { bool _consentGiven = false; bool _privacyPolicyAccepted = false; @override Widget build(BuildContext context) { final theme = Theme.of(context); final canProceed = _consentGiven && _privacyPolicyAccepted; return Scaffold( backgroundColor: theme.colorScheme.surface, appBar: AppBar( title: const Text('家长/监护人确认'), backgroundColor: theme.colorScheme.surface, ), body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(DesignTokens.spacing16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 Icon( Icons.shield_rounded, size: 48, color: theme.colorScheme.primary, ), const SizedBox(height: DesignTokens.spacing12), Text( '儿童个人信息保护', style: theme.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: DesignTokens.spacing8), Text( '根据《中华人民共和国个人信息保护法》第28条,' '未满14周岁未成年人的个人信息处理需要取得父母或监护人的同意。', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: DesignTokens.spacing24), // 信息收集说明卡片 _buildInfoCard( context, icon: Icons.info_outline_rounded, title: '我们会收集哪些信息', items: const [ '昵称和年级(不收集真实姓名和身份证号)', '日记内容和手写笔画', '心情标签和照片', ], ), const SizedBox(height: DesignTokens.spacing12), // 用途说明卡片 _buildInfoCard( context, icon: Icons.security_rounded, title: '信息如何保护', items: const [ '所有数据加密存储和传输', '仅用于日记记录和班级互动', '不会用于商业广告或分享给第三方', '您可以随时查阅、更正或删除孩子数据', ], ), const SizedBox(height: DesignTokens.spacing24), // 同意复选框 _buildCheckbox( value: _privacyPolicyAccepted, onChanged: (v) => setState(() => _privacyPolicyAccepted = v ?? false), text: '我已阅读并同意《暖记隐私政策》和《儿童个人信息保护规则》', ), const SizedBox(height: DesignTokens.spacing4), _buildCheckbox( value: _consentGiven, onChanged: (v) => setState(() => _consentGiven = v ?? false), text: '我是该用户的家长/监护人,同意暖记收集和处理上述信息', ), const SizedBox(height: DesignTokens.spacing32), // 确认按钮 SizedBox( width: double.infinity, child: FilledButton( onPressed: canProceed ? _onConfirm : null, style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric( vertical: DesignTokens.spacing12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.pill), ), ), child: const Text('确认同意,继续'), ), ), const SizedBox(height: DesignTokens.spacing8), // 拒绝按钮 SizedBox( width: double.infinity, child: OutlinedButton( onPressed: () => context.pop(), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric( vertical: DesignTokens.spacing12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.pill), ), ), child: const Text('不同意,返回'), ), ), ], ), ), ), ); } Widget _buildInfoCard( BuildContext context, { required IconData icon, required String title, required List items, }) { final theme = Theme.of(context); return Card( elevation: 0, color: theme.colorScheme.surfaceContainerLow, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Padding( padding: const EdgeInsets.all(DesignTokens.spacing12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, size: 20, color: theme.colorScheme.primary), const SizedBox(width: DesignTokens.spacing8), Text( title, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: DesignTokens.spacing8), ...items.map( (item) => Padding( padding: const EdgeInsets.only( bottom: DesignTokens.spacing4, left: DesignTokens.spacing12, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '• ', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.primary, ), ), Expanded( child: Text( item, style: theme.textTheme.bodySmall, ), ), ], ), ), ), ], ), ), ); } Widget _buildCheckbox({ required bool value, required ValueChanged onChanged, required String text, }) { final theme = Theme.of(context); return InkWell( onTap: () => onChanged(!value), borderRadius: BorderRadius.circular(AppRadius.md), child: Padding( padding: const EdgeInsets.symmetric( vertical: DesignTokens.spacing4, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Checkbox( value: value, onChanged: onChanged, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), const SizedBox(width: DesignTokens.spacing4), Expanded( child: Padding( padding: const EdgeInsets.only(top: 12), child: Text( text, style: theme.textTheme.bodySmall, ), ), ), ], ), ), ); } /// 确认同意 — 发出事件继续注册流程 void _onConfirm() { final consentAt = DateTime.now(); context.read().add(ParentalConsentAccepted(consentAt)); } }