flutter实现一个发短信倒计时60秒且关闭倒计时页面计时仍生效刷新的功能

11 篇文章 0 订阅
6 篇文章 0 订阅

前言:

最近在做一个弹窗中点击发送短信的功能,要求是60秒倒计时,关闭弹窗或者退出当前页面后计时仍在进行。

准备阶段 - 思路:

1.先实现弹窗功能

2.绘制内容填充并完成ui展示

3.点击发送按钮触发倒计时功能,倒计时60秒,计时完成后可再触发点击

4.处理关闭弹窗后的计时生效问题,计时仍然有效(比如计时至56秒时关闭弹窗,等10秒再打开弹窗,此时倒计时时间更新为46秒)

UI效果展示:

编码阶段 - 实现过程:

新建class PersonQRCodeDialog

定义变量:
  static const int sixtySeconds = 60; 倒计时60秒

  static String _sendQRCode = S.current.pm_send_qrcode; 发送按钮文字
  static Color _sendQRCodeColor = const Color(0xff3e71dd); 发送按钮文字颜色
  static Timer? _timer; 计时器
  static int _timeCount = sixtySeconds; 剩余倒计时时间
  static int showDialogTime = 0; 弹窗被打开时的时间戳
  static bool isTimerDialogClose = false; 判断弹窗是否被关闭
  static Map<int?, int?> cacheTimeCountBindPerson = <int, int>{}; 根据不同用户id保存与之对应的倒计时时间
  static Map<int?, int?> cacheMillisecondsTimeBindPerson = <int, int>{}; 根据不同用户id保存与之对应的关闭弹窗的时间(此map需要触发了倒计时且计时进行中且关闭了弹窗才会有作用)

定义入口函数:
showPersonQRCodeDialog(...) {

    弹窗启动时间赋值
    showDialogTime = (DateTime.now().millisecondsSinceEpoch ~/ 1000);

    判断当前用户id在两个map中都包含有
    if (cacheTimeCountBindPerson.containsKey(personId) &&
        cacheMillisecondsTimeBindPerson.containsKey(personId)) {

      获取到弹窗打开与关闭的时间间隔
      var intervalTime =
          showDialogTime - cacheMillisecondsTimeBindPerson[personId]!;
      BILog.i(tag, "intervalTime: $intervalTime");

      判断时间间隔是否大于等于当前用户id所保存的倒计时时间
      if (intervalTime >= cacheTimeCountBindPerson[personId]!) {
        BILog.i(tag, "countdown has expired. now you can send qrcode.");

        大于说明倒计时时间已读完60秒,无需再处理计时,直接释放掉当前用户保存的时间
        cacheTimeCountBindPerson.remove(personId);
      } else {
        BILog.i(tag, "still in range. you need wait for countdown.");

        否则通过intervalTime更新掉cacheTimeCountBindPerson map中当前用户的倒计时时间,拿到map中存放的时间减去弹窗开关的时间间隔
        cacheTimeCountBindPerson[personId] =
            cacheTimeCountBindPerson[personId]! - intervalTime;
      }
    }

bool hasMobile = (qrcodeVM.mobile.isNotEmpty);
String bottomLeftText =
    hasMobile ? S.current.pm_btn_cancel : S.current.pm_i_know;
Color bottomLeftTextColor =
    hasMobile ? const Color(0xff000000) : const Color(0xff3e71dd);
String sendQrcodeTip = hasMobile
    ? S.current.pm_send_qrcode_to_visitor_tip
    : S.current.pm_send_qrcode_need_complete_phone_info_tip;
Color sendQrcodeTipColor =
    hasMobile ? const Color(0xff60a35c) : const Color(0xffddb83e);
  var showModalBottomSheetFuture = showDialog(
      context: context,
      useSafeArea: false,
      builder: (context) {
        return StatefulBuilder(builder: (context, setStateBottomSheet) {
          _initTimer(setStateBottomSheet, personId);
          return SimpleDialog(
              contentPadding: EdgeInsets.all(0.rpx),
              children: [
                SizedBox(
                  width: double.maxFinite,
                  child: Column(mainAxisSize: MainAxisSize.min, children: [
                    // 查看二维码标题
                    Container(
                      margin: EdgeInsets.only(top: 16.rpx),
                      alignment: Alignment.center,
                      child: Text(
                        S.current.pm_look_qrcode,
                        textAlign: TextAlign.center,
                        style: TextStyle(
                            fontSize: 16.rpx,
                            color: const Color(0xff000000),
                            fontWeight: FontWeight.w600),
                      ),
                    ),
                    SizedBox(height: 10.rpx),
                    // 提示词
                    Container(
                      alignment: Alignment.center,
                      child: Text(
                        sendQrcodeTip,
                        textAlign: TextAlign.center,
                        style: TextStyle(
                            fontSize: 12.rpx,
                            color: sendQrcodeTipColor,
                            fontWeight: FontWeight.w600),
                      ),
                    ),
                    SizedBox(height: 16.rpx),
                    // 二维码展示
                    Center(
                        child: QrImage(
                      data: qrinfo ?? "",
                      version: QrVersions.auto,
                      size: 135.rpx,
                      foregroundColor: Colors.black,
                    )),
                    SizedBox(height: 16.rpx),
                    Divider(height: 1.rpx, color: Colors.black12),
                    Container(
                      height: 40.rpx,
                      alignment: Alignment.center,
                      child: Row(children: [
                        // 关闭
                        Flexible(
                          flex: 1,
                          fit: FlexFit.loose,
                          child: InkWell(
                            onTap: () {
                              Navigator.pop(context);
                            },
                            child: Container(
                              alignment: Alignment.center,
                              child: Text(
                                bottomLeftText,
                                textAlign: TextAlign.center,
                                style: TextStyle(
                                    fontSize: 14.rpx,
                                    color: bottomLeftTextColor,
                                    fontWeight: FontWeight.w600),
                              ),
                            ),
                          ),
                        ),
                        Offstage(
                          offstage: hasMobile ? false : true,
                          child: VerticalDivider(
                              color: Colors.black12, width: 1.rpx),
                        ),
                        // 发送二维码
                        Flexible(
                          flex: hasMobile ? 1 : 0,
                          fit: FlexFit.loose,
                          child: Offstage(
                            offstage: hasMobile ? false : true,
                            child: InkWell(
                              onTap: () async {
                                if (_timer != null &&
                                    (_timer?.isActive ?? false)) {
                                  return;
                                }
                                var code = await qrcodeVM.sendQRCodeToSms();
                                if (code == 200) {
                                  _startTimer(setStateBottomSheet, personId);
                                  showCustomToast(
                                      S.current.pm_send_qrcode_success_tip,
                                      _toastContent());
                                }
                                BIEvent.onCountEvent(
                                    CustomAnalysisEvent.eventClickSendQRCode);
                              },
                              child: Container(
                                alignment: Alignment.center,
                                child: Text(
                                  _sendQRCode,
                                  style: TextStyle(
                                      fontSize: 14.rpx,
                                      color: _sendQRCodeColor,
                                      fontWeight: FontWeight.w600),
                                ),
                              ),
                            ),
                          ),
                        ),
                      ]),
                    ),
                  ]),
                ),
              ]);
        });
      });
  showModalBottomSheetFuture.then((value) {
    BILog.i(tag, "showModalBottomSheet dismiss");
    // 弹窗消失时关闭计时器,更新personId对应的计时时间
    if (_timer != null) {
      _timer?.cancel();
      _timer = null;
      cacheTimeCountBindPerson[personId] = _timeCount;
      cacheMillisecondsTimeBindPerson[personId] =
          DateTime.now().millisecondsSinceEpoch ~/ 1000;
      isTimerDialogClose = true;
    }
  });
}

static void _initTimer(StateSetter setStateBottomSheet, int? personId) {
  if (_timer != null && (_timer?.isActive ?? false)) {
    _startTimer(setStateBottomSheet, personId);
  } else {
    if (cacheTimeCountBindPerson.containsKey(personId)) {
      var timeCount = cacheTimeCountBindPerson[personId];
      _timeCount = timeCount ?? 0;
      if (_timeCount > 0) {
        // 当前person之前触发了倒计时,应继续执行倒计时操作
        _startTimer(setStateBottomSheet, personId);
        // 立即刷新发送按钮的倒计时时间
        setStateBottomSheet(() {
          _sendQRCodeColor = C.font99;
          _sendQRCode = "${S.current.pm_send_qrcode}(${_timeCount}s)";
        });
        return;
      }
    }
    _sendQRCode = S.current.pm_send_qrcode;
    _sendQRCodeColor = const Color(0xff3e71dd);
    _timer = null;
    _timeCount = sixtySeconds;
  }
}



static void _startTimer(StateSetter state, int? personId) {
  _timer ??= Timer.periodic(const Duration(seconds: 1), (timer) {
    state(() {
      if (_timeCount <= 0) {
        _timer?.cancel();
        _timer = null;
        _timeCount = sixtySeconds;
        if (cacheTimeCountBindPerson.containsKey(personId)) {
          cacheTimeCountBindPerson.remove(personId);
        }
        if (cacheMillisecondsTimeBindPerson.containsKey(personId)) {
          cacheMillisecondsTimeBindPerson.remove(personId);
        }
        _sendQRCode = S.current.pm_send_qrcode;
        _sendQRCodeColor = const Color(0xff3e71dd);
      } else {
        _timeCount = (!isTimerDialogClose && Platform.isIOS)
            ? (sixtySeconds - timer.tick)
            : (_timeCount - 1);
        _sendQRCode = "${S.current.pm_send_qrcode}(${_timeCount}s)";
        _sendQRCodeColor = C.font99;
      }
    });
  });
}

}

触发弹窗直接调用:

PersonQRCodeDialog.showPersonQRCodeDialog(...);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值