flutter 发送验证码

博客围绕发送验证码需求展开,包含限制文本框输入长度和仅允许输入数字。作者针对部分朋友不知如何使用代码及报错问题进行解释,因文章较早且当时无空安全检测,作者重新编写了Flutter的demo代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个发送验证码的需求:包括限制文本框输入长度和只允许输入数字

请使用===分割线===以下部分代码运行

按惯例 先上图: 

    

class MyBody extends StatefulWidget {
  @override
  _MyBodyState createState() => _MyBodyState();
}

class _MyBodyState extends State<MyBody> {
  bool  isButtonEnable=true;      //按钮状态  是否可点击
  String buttonText='发送验证码';   //初始文本
  int count=60;                     //初始倒计时时间
  Timer timer;                       //倒计时的计时器
  TextEditingController mController=TextEditingController();

  void _buttonClickListen(){
    setState(() {
      if(isButtonEnable){         //当按钮可点击时
        isButtonEnable=false;   //按钮状态标记
        _initTimer();

        return null;            //返回null按钮禁止点击
      }else{                    //当按钮不可点击时
//        debugPrint('false');
        return null;             //返回null按钮禁止点击
      }
    });
  }


 void _initTimer(){
    timer = new Timer.periodic(Duration(seconds: 1), (Timer timer) {
      count--;
      setState(() {
        if(count==0){
          timer.cancel();             //倒计时结束取消定时器
          isButtonEnable=true;        //按钮可点击
          count=60;                   //重置时间
          buttonText='发送验证码';     //重置按钮文本
        }else{
          buttonText='重新发送($count)';  //更新文本内容
        }
      });
    });
  }


  @override
  void dispose() {
    timer?.cancel();      //销毁计时器
    timer=null;
    mController?.dispose();
    super.dispose();
  }


  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
//        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
              color: Colors.white,
              padding: EdgeInsets.only(left: 10,right: 10),
              child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
//                  crossAxisAlignment: CrossAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.baseline,
                  textBaseline: TextBaseline.ideographic,
                  children: <Widget>[
                    Text('验证码',style: TextStyle(fontSize: 13,color: Color(0xff333333)),),
                    Expanded(
                      child: Padding(padding: EdgeInsets.only(left: 15,right: 15,top: 15),
                      child: TextFormField(
                        maxLines: 1,
                        onSaved: (value) { },
                        controller: mController,
                        textAlign: TextAlign.left,
                        inputFormatters: [WhitelistingTextInputFormatter.digitsOnly,LengthLimitingTextInputFormatter(6)],
                        decoration: InputDecoration(
                          hintText: ('填写验证码'),
                          contentPadding: EdgeInsets.only(top: -5,bottom: 0),
                          hintStyle: TextStyle(
                            color: Color(0xff999999),
                            fontSize: 13,
                          ),
                          alignLabelWithHint: true,
                          border: OutlineInputBorder(borderSide: BorderSide.none),
                        ),
                      ),),
                    ),
                    Container(
                      width: 120,
                      child: FlatButton(
                        disabledColor: Colors.grey.withOpacity(0.1),     //按钮禁用时的颜色
                        disabledTextColor: Colors.white,                   //按钮禁用时的文本颜色
                        textColor:isButtonEnable?Colors.white:Colors.black.withOpacity(0.2),                           //文本颜色
                        color: isButtonEnable?Color(0xff44c5fe):Colors.grey.withOpacity(0.1),                          //按钮的颜色
                        splashColor: isButtonEnable?Colors.white.withOpacity(0.1):Colors.transparent,
                        shape: StadiumBorder(side: BorderSide.none),
                        onPressed: (){ setState(() {
                          _buttonClickListen();
                        });},
//                        child: Text('重新发送 (${secondSy})'),
                        child: Text('$buttonText',style: TextStyle(fontSize: 13,),),
                      ),
                    ),
                  ],
              ),
          ),
          Container(
            width: double.infinity,
            height: 45,
            margin: EdgeInsets.only(top: 50,left: 10,right: 10),
            child: RaisedButton(
              onPressed: () {
                debugPrint('${mController.text}');
              },
              shape: StadiumBorder(side: BorderSide.none),
              color: Color(0xff44c5fe),
              child: Text(
                '下一步',
                style: TextStyle(color: Colors.white,fontSize: 15),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

=============================分割线===============================

鉴于部分朋友评论不知道怎么用 其实就当成一个小组件就可以了  至于代码有报错  我这里解释一下,这篇文章是比较早的,当时还没有空安全的检测  而且 我很久没写flutter了  但是为了解决各位的问题  我重新写了一下这个demo,下面把代码贴出来

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class msgcode extends StatefulWidget {
  const msgcode({Key? key}) : super(key: key);

  @override
  State<msgcode> createState() => _MsgCodeState();
}

class _MsgCodeState extends State<msgcode> {
  @override
  Widget build(BuildContext context) {
    return  Scaffold(
        appBar:AppBar(
          automaticallyImplyLeading: true,
          title:const Text('验证码倒计时小组件'),
        ),
        body: const MyBody(),
    );
  }
}

class MyBody extends StatefulWidget {
  const MyBody({Key? key}) : super(key: key);

  @override
  _MyBodyState createState() => _MyBodyState();
}

class _MyBodyState extends State<MyBody> {
  bool isButtonEnable=true;      //按钮初始状态  是否可点击
  String buttonText='发送验证码';   //初始文本
  int count=60;                     //初始倒计时时间
  Timer? timer;                       //倒计时的计时器
  TextEditingController? mController=TextEditingController();


  
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
//        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            alignment: Alignment.center,
            padding: const EdgeInsets.only(left: 10,right: 10,top: 10),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.center,
              textBaseline: TextBaseline.ideographic,
              children: <Widget>[
                const Text('验证码',style: TextStyle(fontSize: 13,color: Color(0xff333333))),
                Expanded(
                  child: Padding(padding: const EdgeInsets.only(left: 15,right: 15),
                    child: TextFormField(
                      maxLines: 1,
                      onSaved: (value) { },
                      controller: mController,
                      textAlign: TextAlign.start,
                       inputFormatters: [
                        FilteringTextInputFormatter.allow(RegExp("[0-9]")),    //只允许输入0-9的数字
                        LengthLimitingTextInputFormatter(6)                     //最大输入长度为6
                      ],
                      decoration: const InputDecoration(
                        hintText: ('填写验证码'),
                        // fillColor: Colors.red,  //设置TextFormField背景颜色
                        // filled: true,
                        contentPadding: EdgeInsets.only(top: 25,bottom: 0),
                        hintStyle: TextStyle(
                          color: Color(0xff999999),
                          fontSize: 13,
                        ),
                        alignLabelWithHint: true,

                        border: OutlineInputBorder(borderSide: BorderSide.none),
                      ),
                    ),),
                ),
                 SizedBox(
                  width: 120,
                  child: FlatButton(
                    disabledColor: Colors.grey.withOpacity(0.1),     //按钮禁用时的颜色
                    disabledTextColor: Colors.white,                   //按钮禁用时的文本颜色
                    textColor:isButtonEnable?Colors.white:Colors.black.withOpacity(0.2),                           //文本颜色
                    color: isButtonEnable?const Color(0xff44c5fe):Colors.grey.withOpacity(0.1),                          //按钮的颜色
                    splashColor: isButtonEnable?Colors.white.withOpacity(0.1):Colors.transparent,
                    shape:const StadiumBorder(side: BorderSide.none),
                    onPressed: (){
                      if(isButtonEnable){
                        debugPrint('$isButtonEnable');
                        setState(() {
                          _buttonClickListen();
                        });
                      }},
                    child:Text(buttonText,style:const TextStyle(fontSize: 13,),),
                  ),
                ),
              ],
            ),
          ),
          Container(
            width: double.infinity,
            height: 45,
            margin: const EdgeInsets.only(top: 50,left: 10,right: 10),
            child: ElevatedButton(
              onPressed: () {
                debugPrint('${mController?.text}');
              },
              style: ButtonStyle(
                backgroundColor:MaterialStateProperty.all(const Color(0xff44c5fe)),
                shape: MaterialStateProperty.all(const StadiumBorder(side: BorderSide.none)),//圆角弧度
              ),

              child:const Text('下一步', style: TextStyle(color: Colors.white,fontSize: 15),
              ),
            ),
          ),
        ],
      ),
    );
  }


  void _buttonClickListen(){
    setState(() {
      if(isButtonEnable){         //当按钮可点击时
        isButtonEnable=false;   //按钮状态标记
        _initTimer();
      }
    });
  }

  void _initTimer(){
    timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
      count--;
      setState(() {
        if(count==0){
          timer.cancel();             //倒计时结束取消定时器
          isButtonEnable=true;        //按钮可点击
          count=60;                   //重置时间
          buttonText='发送验证码';     //重置按钮文本
        }else{
          buttonText='重新发送($count)';  //更新文本内容
        }
      });
    });
  }

  @override
  void dispose() {
    timer?.cancel();      //销毁计时器
    timer=null;
    mController?.dispose();
    super.dispose();
  }

}

### 如何在 Flutter 中实现手机验证码登录和邮箱密码登录 #### 手机验证码登录功能的实现 为了实现在 Flutter 应用中的手机验证码登录,可以采用如下方式: 创建一个专门用于处理手机号码输入以及发送验证码逻辑的方法。此方法会先验证电话号码的有效性再尝试获取验证码。 ```dart Future<void> _sendSmsVerification() async { final phone = _phoneController.text; // 正则表达式检查手机号合法性 if (!RegExp(r'^1\d{10}$').hasMatch(phone)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('请输入有效的手机号')), ); return; } try { await FirebaseAuth.instance.verifyPhoneNumber( phoneNumber: '+86$phone', verificationCompleted: (PhoneAuthCredential credential) {}, codeSent: (String verificationId, int? resendToken) { setState(() => _verificationId = verificationId); }, codeAutoRetrievalTimeout: (String verificationId) {}, verificationFailed: (FirebaseAuthException e) { throw Exception(e.message ?? '未知错误'); }, ); // 显示倒计时按钮防止频繁点击请求验证码 setState(() => _isCountingDown = true); const oneSec = Duration(seconds: 1); Timer.periodic(oneSec, (Timer timer) { if (_countdownTime == 0) { timer.cancel(); setState(() { _isCountingDown = false; _countdownTime = 60; // reset countdown time after finish }); } else { setState(() => _countdownTime--); } }); } catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$e'))); } } ``` 当用户成功接收并填写短信验证码之后,则可以通过 `signInWithCredential` 方法完成最终的身份验证过程[^1]。 对于验证码倒计时期间不可重复触发的行为控制,在上述代码片段中通过 `_isCountingDown` 变量来进行管理,并利用定时器减少剩余时间直至结束为止。 #### 邮箱密码登录功能的实现 针对邮箱密码形式的登录机制而言,主要依赖于 Firebase Authentication 提供的相关 API 接口来达成目的。具体来说就是收集用户的电子邮件地址及其对应的密码信息后提交给服务器端进行校验操作。 ```dart void signInWithEmailAndPassword(String email, String password) async { try { UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword( email: email.trim(), password: password.trim()); Navigator.pushReplacementNamed(context, '/home'); // 登录成功跳转到主页 } on FirebaseAuthException catch (error) { switch (error.code) { case "invalid-email": ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text("电子信箱格式不正确"))); break; case "wrong-password": ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text("密码错误"))); break; default: ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(error.toString()))); } } } ``` 这里展示了如何捕获可能出现的各种异常情况并向用户提供相应的提示消息;同时也包含了简单的导航指令以便顺利进入应用程序内部界面[^4]。 另外值得注意的是,在实际开发过程中还需要考虑诸如安全性措施(比如加密传输)、用户体验优化等方面的问题以确保整个系统的稳定性和可靠性。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值