Riverpod 实现验证码倒计时

74 阅读1分钟

全局验证码状态共享:验证码的倒计时状态需要手动重置

// 验证码倒计时的状态管理
// 验证码状态
enum CaptchaState {
  init, // 初始状态
  counting, // 倒计时中
  restart, // 可重新发送
}

@riverpod
class Captcha extends _$Captcha {
  Timer? _timer;
  @override
  ({CaptchaState state, int countdown}) build() {
    ref.onDispose(() {
      _timer?.cancel();
      _timer = null;
    });
    return (state: CaptchaState.init, countdown: 0);
  }

  // 初始化倒计时
  void initCaptcha() {
    // 取消之前的定时器,防止它继续修改状态
    _timer?.cancel();
    _timer = null;
    // 重置状态
    if (state.state != CaptchaState.init) {
      state = (state: CaptchaState.init, countdown: 0);
    }
  }

  // 启动倒计时
  void startCountdown() {
    if (state.state == CaptchaState.counting) {
      return;
    }
    // 取消之前的定时器
    _timer?.cancel();
    // 初始化状态
    state = (state: CaptchaState.counting, countdown: 60);
    // 创建周期性定时器
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      final newCount = state.countdown - 1;
      if (newCount <= 0) {
        timer.cancel();
        // 更新状态为可重新发送
        state = (state: CaptchaState.restart, countdown: 0);
        return;
      }
      // 更新倒计时状态
      state = (state: CaptchaState.counting, countdown: newCount);
    });
  }
}

调用时:

Widget captCha() {
  return Consumer(
    builder: (context, ref, child) {
      final captchaState = ref.watch(captchaProvider);
      final notifier = ref.read(captchaProvider.notifier);
      return TextButton(
        onPressed: captchaState.state == CaptchaState.counting
            ? null // 倒计时中禁用按钮
            : () {
                ScaffoldMessenger.of(ref.context).showSnackBar(
                  const SnackBar(
                    content: Text('验证码已发送'),
                    duration: Duration(seconds: 1),
                  ),
                );
                notifier.startCountdown();
              },
        style: TextButton.styleFrom(
          padding: const EdgeInsets.symmetric(horizontal: 12),
        ),
        child: Text(
          // 根据状态显示不同的文本
          captchaState.state == CaptchaState.counting
              ? '${captchaState.countdown}s后重发' // 倒计时中
              : captchaState.state == CaptchaState.restart
              ? '重新获取验证码' // 倒计时结束后
              : '获取验证码', // 初始状态
          style: TextStyle(
            color: captchaState.state == CaptchaState.counting
                ? Colors
                      .grey // 倒计时中为灰色
                : Theme.of(ref.context).primaryColor, // 其他状态为主色
            fontWeight: FontWeight.bold,
          ),
        ),
      );
    },
  );
}