Template
Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库 7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系 5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter 通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
61 lines
1.4 KiB
Dart
61 lines
1.4 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// 带倒计时的发送按钮(常用于短信验证码)。
|
|
///
|
|
/// 倒计时结束后自动恢复可点击状态。
|
|
class CountDownButton extends StatefulWidget {
|
|
const CountDownButton({
|
|
super.key,
|
|
required this.onSend,
|
|
this.label = '发送验证码',
|
|
this.countingLabel = '重新发送',
|
|
this.seconds = 60,
|
|
});
|
|
|
|
final VoidCallback onSend;
|
|
final String label;
|
|
final String countingLabel;
|
|
final int seconds;
|
|
|
|
@override
|
|
State<CountDownButton> createState() => _CountDownButtonState();
|
|
}
|
|
|
|
class _CountDownButtonState extends State<CountDownButton> {
|
|
int _remaining = 0;
|
|
Timer? _timer;
|
|
|
|
bool get _isCounting => _remaining > 0;
|
|
|
|
void _start() {
|
|
widget.onSend();
|
|
setState(() => _remaining = widget.seconds);
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (t) {
|
|
if (_remaining <= 1) {
|
|
t.cancel();
|
|
if (mounted) setState(() => _remaining = 0);
|
|
} else {
|
|
if (mounted) setState(() => _remaining--);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return TextButton(
|
|
onPressed: _isCounting ? null : _start,
|
|
child: Text(
|
|
_isCounting ? '${widget.countingLabel}(${_remaining}s)' : widget.label,
|
|
),
|
|
);
|
|
}
|
|
}
|