Flutter Firebase手机号OTP登录遇E.164格式错误求助
Flutter IntlPhoneField + Firebase OTP登录:手机号E.164格式错误排查与解决
问题背景
使用Flutter的IntlPhoneField组件结合Firebase实现手机号+OTP登录时,调用verifyPhoneNumber接口收到错误提示:
提供的手机号格式不正确,请输入可解析为E.164格式的手机号。E.164格式手机号需按
[+|国家码][包含区号的用户号码]格式填写。无效格式。
错误原因分析
- 拼接顺序错误:当前代码将用户输入的手机号
phoneNumber和国家码countryCode直接拼接,但顺序颠倒,且国家码缺少必填的+前缀,不符合E.164格式要求。 - 未利用组件自带能力:IntlPhoneField组件本身提供了直接获取标准E.164格式号码的属性,无需手动拼接。
解决方案
方案1:使用IntlPhoneField自带的标准号码(推荐)
IntlPhoneField的回调参数phone为PhoneNumber类型,其completeNumber属性直接返回带+前缀的完整E.164格式号码,无需手动处理拼接逻辑。
修改步骤:
- 调整手机号输入组件的
onChanged回调,直接存储完整标准号码 - 简化Provider调用,仅传递完整号码
- 移除Provider方法中的国家码参数,直接使用完整号码调用Firebase接口
方案2:修复手动拼接逻辑(不推荐)
若坚持手动拼接,需确保:
- 国家码以
+开头 - 拼接顺序为
+国家码 + 纯数字手机号(手机号需去掉前导0)
修改后的代码示例
1. 手机号输入组件(方案1)
IntlPhoneField( controller: phoneController, showCountryFlag: false, decoration: InputDecoration( isDense: true, enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: AppColors.greyColor), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: AppColors.greyColor), ), ), initialCountryCode: 'PK', onChanged: (phone) { // 直接获取符合E.164标准的完整号码 setState(() { fullPhoneNumber = phone.completeNumber; }); }, )
2. 调用Provider代码(方案1)
if (_formKey.currentState!.validate()){ AddNewDialogBox.dialog(context, true); context .read<LoginPhoneProvider>() .phoneSignInn(context, fullPhoneNumber); print(fullPhoneNumber); }
3. Provider中获取OTP代码(方案1)
Future<void> phoneSignInn( BuildContext context, String fullPhoneNumber, ) async { TextEditingController codeController = TextEditingController(); if (kIsWeb) { ConfirmationResult result = await _auth.signInWithPhoneNumber(fullPhoneNumber); showOTPDialog( phoneNumber: fullPhoneNumber, codeController: codeController, context: context, onPressed: () async { PhoneAuthCredential credential = PhoneAuthProvider.credential( verificationId: result.verificationId, smsCode: codeController.text.trim(), ); await _auth.signInWithCredential(credential); Navigator.of(context).pop(); }, ); } else { await _auth.verifyPhoneNumber( phoneNumber: fullPhoneNumber, verificationCompleted: (PhoneAuthCredential credential) async { await _auth.signInWithCredential(credential); }, verificationFailed: (e) { showSnackBar(context, e.message!); }, codeSent: ((String verificationId, int? resendToken) async { showOTPDialog( phoneNumber: fullPhoneNumber, codeController: codeController, context: context, onPressed: () async { PhoneAuthCredential credential = PhoneAuthProvider.credential( verificationId: verificationId, smsCode: codeController.text.trim(), ); await _auth.signInWithCredential(credential); Navigator.of(context).push(MaterialPageRoute( builder: (context) => const DriverSginUpScreen())); }, ); }), codeAutoRetrievalTimeout: (String verificationId) {}, ); } }
4. OTP弹窗代码(无需修改)
void showOTPDialog({ required BuildContext context, required TextEditingController codeController, required VoidCallback onPressed, required String phoneNumber, }) { showDialog( context: context, barrierDismissible: false, builder: (context) => SizedBox( width: double.infinity, child: AlertDialog( title: Text("Enter 6 Digit OTP send to\n$phoneNumber"), content: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ PinCodeTextField( pinTheme: PinTheme( shape: PinCodeFieldShape.box, borderRadius: BorderRadius.circular(5), fieldHeight: 60, fieldWidth: 30, activeFillColor: Colors.white, ), appContext: context, length: 6, controller: codeController, onChanged: (value) { print(value); }, ), ], ), actions: <Widget>[ TextButton( onPressed: onPressed, child: const Text("Done"), ) ], ), ), ); }
关键注意事项
- 确保用户输入的手机号无多余空格、符号,IntlPhoneField会自动处理格式校验
- Firebase手机号验证严格遵循E.164标准,号码必须以
+开头,无任何非数字字符
内容的提问来源于stack exchange,提问作者Rashid Ali
相关产品推荐
相关产品推荐

