You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter印度手机号验证失效问题排查与修复求助

印度手机号验证界面校验问题修复

问题描述

开发印度手机号验证界面时,当前代码无法完成有效校验,需求为:验证通过则调用API,未通过则高亮TextFormField并显示错误提示。

原代码

Form(
        key: _formKeymobile,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              S.of(context).verify,
              style: TextStyle(
                  fontSize: 30.0.sp,
                  fontFamily: 'DancingScript',
                  color: Colors.purple),
            ),
            SizedBox(
              height: 20,
            ),
            TextFormField(
              style: TextStyle(color: Colors.blue, fontSize: 20.0.sp),
              controller: _mobilenumber,
              validator: (value) {
                const pattern =
                    r'(^(?:(?:\+|0{0,2})91(\s*[\ -]\s*)?|[0]?)?[6789]\d{9}|(\d[ -]?){10}\d$)';
                final regExp = RegExp(pattern);
                if (value!.isEmpty) {
                  return S.of(context).EnterSomeText;
                } else if (value.length != 10) {
                } else if (!regExp.hasMatch(value)) {
                  return S.of(context).Mobilenumbermust10digits;
                  return S.of(context).MobileNumbermustbedigits;
                }
              },
              maxLength: 11,
              keyboardType: TextInputType.phone,
              decoration: InputDecoration(
                  hintText: S.of(context).mobile,
                  border: OutlineInputBorder()),
            ),
            SizedBox(
              height: 8.0,
            ),
            Row(
              children: [
                Checkbox(
                  checkColor: Colors.white,
                  value: isChecked,
                  onChanged: (bool? value) {
                    setState(() {
                      isChecked = value!;
                    });
                    // print(isChecked);
                  },
                ),
                SizedBox(
                  width: MediaQuery.of(context).size.width * 0.60,
                  child: Column(
                    children: [
                      Text(S.of(context).tcone),
                      InkWell(
                          onTap: () {
                            openWebsite(
                                
                                context);
                          },
                          child: Text(
                            S.of(context).tctwo,
                            style: TextStyle(color: Colors.blue),
                          )),
                      Text("and"),
                      InkWell(
                          onTap: () {
                            openWebsite(
                                
                                context);
                          },
                          child: Text(
                            S.of(context).tcthree,
                            style: TextStyle(color: Colors.blue),
                          )),
                    ],
                  ),
                )
              ],
            ),
            ElevatedButton(
              onPressed: () async {
                if (isChecked == true) {
                  if (_formKeymobile.currentState!.validate() &&
                      isChecked == true) {
                    String? deviceId = await _getId();
                    if (deviceId != null) {
                      print("api called");
                    } else {
                      EasyLoading.showError("Unable to get device id");
                    }
                  }
                } else {
                  EasyLoading.showError("Please click the box");
                }
              },
              style: ButtonStyle(
                  backgroundColor:
                      MaterialStateProperty.all(Colors.purple)),
              child: Text(S.of(context).otp),
            ),
            SizedBox(
              height: 8.0,
            ),
            ElevatedButton(
              onPressed: () async {
                if (_formKeymobile.currentState!.validate() &&
                    isChecked == true) {
                  print("form validated");
                }
              },
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.green)),
              child: Text("Already have OTP"),
            ),
          ],
        )

问题分析与修复方案

1. 校验逻辑漏洞

  • 原validator中,当手机号长度不符合要求时,无错误返回,导致该校验步骤完全失效;
  • 正则不匹配分支存在两个连续return语句,第二个永远无法执行,属于明显逻辑错误。

2. 正则表达式优化

原正则过于冗余,印度手机号标准格式包含两种情况:

  • 纯10位数字,以6/7/8/9开头;
  • 带+91前缀或0前缀的10位数字。
    简化后的正则更精准易维护:r'^(?:\+91|0)?[6789]\d{9}$'

3. 输入长度调整

若允许用户输入带+91前缀的手机号,maxLength应设为13(+91+10位数字);若仅允许纯10位手机号,设为10即可。

修复后的核心代码

TextFormField(
  style: TextStyle(color: Colors.blue, fontSize: 20.0.sp),
  controller: _mobilenumber,
  validator: (value) {
    // 简化后的印度手机号校验正则
    const pattern = r'^(?:\+91|0)?[6789]\d{9}$';
    final regExp = RegExp(pattern);
    
    if (value!.isEmpty) {
      return S.of(context).EnterSomeText;
    } 
    // 统一校验格式,不符合则返回错误提示
    else if (!regExp.hasMatch(value)) {
      return S.of(context).Mobilenumbermust10digits;
    }
    // 校验通过必须返回null
    return null;
  },
  // 允许带+91前缀,设置最大长度为13
  maxLength: 13,
  keyboardType: TextInputType.phone,
  decoration: InputDecoration(
      hintText: S.of(context).mobile,
      border: OutlineInputBorder()),
)

额外优化点

  • 原按钮逻辑中重复判断isChecked == true,可简化为一次判断;
  • 修复后,校验不通过时Form会自动高亮TextFormField并显示错误提示,无需额外手动处理。

内容的提问来源于stack exchange,提问作者Navjot Aulakh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:21:58