import 'dart:async'; import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_typography.dart'; import '../../app/theme/app_spacing.dart'; /// 短信验证码倒计时按钮 /// /// - 初始: 显示 [label] (如 "获取验证码") /// - 点击后: 显示 "XXs" 倒计时,禁用 /// - 倒计时结束: 恢复可点击 class CountdownButton extends StatefulWidget { final String label; final int seconds; final Future Function() onPressed; final bool enabled; const CountdownButton({ super.key, required this.label, this.seconds = 60, required this.onPressed, this.enabled = true, }); @override State createState() => _CountdownButtonState(); } class _CountdownButtonState extends State { Timer? _timer; int _remaining = 0; bool _loading = false; bool get _isCounting => _remaining > 0; @override void dispose() { _timer?.cancel(); super.dispose(); } Future _handlePress() async { if (_isCounting || _loading || !widget.enabled) return; setState(() => _loading = true); try { await widget.onPressed(); _startCountdown(); } catch (_) { // 发送失败不启动倒计时,让用户重试 } finally { if (mounted) setState(() => _loading = false); } } void _startCountdown() { setState(() => _remaining = widget.seconds); _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (!mounted) { timer.cancel(); return; } setState(() { _remaining--; if (_remaining <= 0) { timer.cancel(); } }); }); } @override Widget build(BuildContext context) { final disabled = _isCounting || _loading || !widget.enabled; final text = _isCounting ? '${_remaining}s' : widget.label; return SizedBox( height: AppSpacing.buttonHeightSm, child: TextButton( onPressed: disabled ? null : _handlePress, style: TextButton.styleFrom( foregroundColor: AppColors.primary, disabledForegroundColor: AppColors.textTertiary, padding: const EdgeInsets.symmetric(horizontal: 16), shape: RoundedRectangleBorder( borderRadius: AppSpacing.borderRadiusSm, ), textStyle: AppTypography.labelMedium, ), child: _loading ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : Text(text), ), ); } }