import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import '../../design/pencil_theme.dart'; /// 设计:`desgin/funymee_home.pen`「注销账户 · 步骤1」「注销账户 · 步骤2 二次验证」。 /// 返回 `true` 表示用户在第二步确认注销(可继续调接口)。 Future showDeleteAccountConfirmationFlow(BuildContext context) async { while (true) { final step1Ok = await showDialog( context: context, barrierDismissible: false, barrierColor: _kScrim, builder: (ctx) => const _DeleteAccountStep1Dialog(), ); if (!context.mounted) return false; if (step1Ok != true) return false; final step2 = await showDialog<_DeleteStep2Result>( context: context, barrierDismissible: false, barrierColor: _kScrim, builder: (ctx) => const _DeleteAccountStep2Dialog(), ); if (!context.mounted) return false; if (step2 == _DeleteStep2Result.confirmed) return true; if (step2 == _DeleteStep2Result.backToStep1) continue; return false; } } enum _DeleteStep2Result { backToStep1, confirmed } // --- 设计 token(与 .pen 一致)--- const _kModalWidth = 350.0; const _kScrim = Color(0xB31C1917); const _kModalBorder = Color(0xFFFECACA); const _kDanger = Color(0xFFDC2626); const _kDangerIconBg = Color(0xFFFEE2E2); const _kTitle = PencilTheme.stone900; const _kBody = Color(0xFF57534E); const _kChkLabel = PencilTheme.stone700; const _kCancelFill = Color(0xFFF5F5F4); const _kCancelStroke = Color(0xFFE7E5E4); const _kPillBg = Color(0xFFFEF3C7); const _kPillInk = Color(0xFFB45309); const _kShieldBg = Color(0xFFFFEDD5); const _kShieldIcon = Color(0xFFC2410C); const _kInputFill = Color(0xFFFAFAF9); const _kInputStroke = Color(0xFFD6D3D1); const _kHintInfo = Color(0xFF78716C); class _DeleteAccountStep1Dialog extends StatefulWidget { const _DeleteAccountStep1Dialog(); @override State<_DeleteAccountStep1Dialog> createState() => _DeleteAccountStep1DialogState(); } class _DeleteAccountStep1DialogState extends State<_DeleteAccountStep1Dialog> { bool _ack = false; @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: _kModalWidth), child: Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: _kModalBorder), boxShadow: const [ BoxShadow( color: Color(0x18000000), blurRadius: 28, offset: Offset(0, 12), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Center( child: Container( width: 52, height: 52, decoration: BoxDecoration( color: _kDangerIconBg, borderRadius: BorderRadius.circular(999), ), child: const Icon( Icons.warning_amber_rounded, size: 28, color: _kDanger, ), ), ), const SizedBox(height: 16), Text( 'Delete account', textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 20, fontWeight: FontWeight.w700, color: _kTitle, ), ), const SizedBox(height: 16), Text( 'This cannot be undone. Your account, creations, credits, and all related data will be permanently removed.', textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.w500, height: 1.45, color: _kBody, ), ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 22, height: 22, child: Checkbox( value: _ack, onChanged: (v) => setState(() => _ack = v ?? false), activeColor: _kDanger, side: const BorderSide(color: _kDanger, width: 2), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), const SizedBox(width: 10), Expanded( child: Text( 'I understand the consequences and accept the risk of losing my data.', style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w600, height: 1.4, color: _kChkLabel, ), ), ), ], ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: _DialogSecondaryButton( label: 'Cancel', onTap: () => Navigator.of(context).pop(false), ), ), const SizedBox(width: 12), Expanded( child: _DialogPrimaryButton( label: 'Continue', enabled: _ack, onTap: _ack ? () => Navigator.of(context).pop(true) : null, ), ), ], ), ], ), ), ), ); } } class _DeleteAccountStep2Dialog extends StatefulWidget { const _DeleteAccountStep2Dialog(); @override State<_DeleteAccountStep2Dialog> createState() => _DeleteAccountStep2DialogState(); } class _DeleteAccountStep2DialogState extends State<_DeleteAccountStep2Dialog> { static const _phrase = 'PERMANENTLY DELETE'; final _controller = TextEditingController(); @override void initState() { super.initState(); _controller.addListener(() => setState(() {})); } @override void dispose() { _controller.dispose(); super.dispose(); } bool get _canConfirm => _controller.text == _phrase; @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: _kModalWidth), child: Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: _kModalBorder), boxShadow: const [ BoxShadow( color: Color(0x18000000), blurRadius: 28, offset: Offset(0, 12), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _kPillBg, borderRadius: BorderRadius.circular(999), ), child: Text( 'Verification', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w700, color: _kPillInk, ), ), ), ), const SizedBox(height: 14), Center( child: Container( width: 48, height: 48, decoration: BoxDecoration( color: _kShieldBg, borderRadius: BorderRadius.circular(14), ), child: const Icon( Icons.gpp_maybe_rounded, size: 26, color: _kShieldIcon, ), ), ), const SizedBox(height: 14), Text( 'Final confirmation', textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 19, fontWeight: FontWeight.w700, color: _kTitle, ), ), const SizedBox(height: 14), Text( 'Type PERMANENTLY DELETE below exactly as shown. Wrong text cannot be submitted.', textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w500, height: 1.45, color: _kBody, ), ), const SizedBox(height: 14), SizedBox( height: 48, child: TextField( controller: _controller, textAlignVertical: TextAlignVertical.center, style: GoogleFonts.inter( fontSize: 15, fontWeight: FontWeight.w600, color: _kTitle, ), decoration: InputDecoration( isDense: true, contentPadding: const EdgeInsets.symmetric( horizontal: 14, vertical: 14, ), filled: true, fillColor: _kInputFill, hintText: _phrase, hintStyle: GoogleFonts.inter( fontSize: 15, fontWeight: FontWeight.w600, color: _kBody.withValues(alpha: 0.45), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _kInputStroke), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _kInputStroke), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _kDanger, width: 1.5), ), ), ), ), const SizedBox(height: 14), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(top: 2), child: Icon( Icons.info_outline_rounded, size: 16, color: PencilTheme.underlineGold, ), ), const SizedBox(width: 8), Expanded( child: Text( 'The button stays disabled until the phrase matches exactly.', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, height: 1.35, color: _kHintInfo, ), ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: _DialogSecondaryButton( label: 'Back', onTap: () => Navigator.of(context) .pop(_DeleteStep2Result.backToStep1), ), ), const SizedBox(width: 12), Expanded( child: _DialogPrimaryButton( label: 'Delete account', enabled: _canConfirm, onTap: _canConfirm ? () => Navigator.of(context) .pop(_DeleteStep2Result.confirmed) : null, ), ), ], ), ], ), ), ), ); } } class _DialogSecondaryButton extends StatelessWidget { const _DialogSecondaryButton({ required this.label, required this.onTap, }); final String label; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: _kCancelFill, borderRadius: BorderRadius.circular(14), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(14), child: Container( width: double.infinity, height: 48, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all(color: _kCancelStroke), ), child: Text( label, style: GoogleFonts.inter( fontSize: 15, fontWeight: FontWeight.w600, color: _kChkLabel, ), ), ), ), ); } } class _DialogPrimaryButton extends StatelessWidget { const _DialogPrimaryButton({ required this.label, required this.enabled, required this.onTap, }); final String label; final bool enabled; final VoidCallback? onTap; @override Widget build(BuildContext context) { return Material( color: enabled ? _kDanger : _kDanger.withValues(alpha: 0.38), borderRadius: BorderRadius.circular(14), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(14), child: SizedBox( width: double.infinity, height: 48, child: Center( child: Text( label, style: GoogleFonts.inter( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ), ); } }