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

Flutter Firebase电话认证报错:invalid-verification-id 求助

问题排查与修复方案

核心问题分析

你遇到的[firebase_auth/invalid-verification-id]错误,核心由以下代码问题导致:

  • 弹窗中的OTP输入框未绑定otpController,导致始终获取不到用户输入的验证码,Firebase因无效验证码返回关联的验证ID错误提示
  • 闭包作用域内的变量同名冲突,codeSent回调的参数verificationId与State成员变量重名,可能导致引用混乱
  • 未处理异步登录异常,且跳转页面时未先关闭弹窗,引发上下文逻辑错误

修复后的完整代码

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:otp_login/OTP_Successful.dart';

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

  @override
  State<SendOtp> createState() => _SendOtpState();
}

class _SendOtpState extends State<SendOtp> {
  final TextEditingController phoneController = TextEditingController();
  final TextEditingController otpController = TextEditingController();
  final FirebaseAuth auth = FirebaseAuth.instance;
  String? verificationId;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Send OTP"),
      ),
      body: Center(
        child: ListView(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          children: [
            TextFormField(
              controller: phoneController,
              decoration: const InputDecoration(labelText: "Enter Phone Number"),
              keyboardType: TextInputType.phone,
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                await FirebaseAuth.instance.verifyPhoneNumber(
                  phoneNumber: phoneController.text,
                  verificationCompleted: (PhoneAuthCredential credential) async {
                    await auth.signInWithCredential(credential);
                    if (mounted) {
                      Navigator.pushReplacement(
                        context,
                        MaterialPageRoute(builder: (context) => const OtpSuccessful()),
                      );
                    }
                  },
                  verificationFailed: (FirebaseAuthException e) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text("验证失败: ${e.message}")),
                    );
                  },
                  codeSent: (String verificationId, int? resendToken) {
                    setState(() {
                      this.verificationId = verificationId;
                    });
                    _showOtpDialog();
                  },
                  codeAutoRetrievalTimeout: (String verificationId) {
                    setState(() {
                      this.verificationId = verificationId;
                    });
                  },
                );
              },
              child: const Text("Send OTP"),
            )
          ],
        ),
      ),
    );
  }

  void _showOtpDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Enter OTP'),
          content: TextFormField(
            controller: otpController,
            decoration: const InputDecoration(labelText: "OTP"),
            keyboardType: TextInputType.number,
            maxLength: 6,
          ),
          actions: [
            ElevatedButton(
              onPressed: () async {
                if (verificationId == null || otpController.text.length != 6) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text("请输入有效的6位OTP")),
                  );
                  return;
                }

                try {
                  PhoneAuthCredential credential = PhoneAuthProvider.credential(
                    verificationId: verificationId!,
                    smsCode: otpController.text,
                  );
                  await auth.signInWithCredential(credential);

                  if (mounted) {
                    Navigator.pop(context);
                    Navigator.pushReplacement(
                      context,
                      MaterialPageRoute(builder: (context) => const OtpSuccessful()),
                    );
                  }
                } on FirebaseAuthException catch (e) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text("登录失败: ${e.message}")),
                  );
                }
              },
              child: const Text("OK"),
            )
          ],
        );
      },
    );
  }
}

关键修复点说明

  • 绑定OTP控制器:为弹窗输入框添加controller: otpController,确保能正确获取用户输入的验证码
  • 分离弹窗逻辑:将弹窗提取为单独方法,避免闭包作用域内的变量同名冲突,确保使用State类的verificationId成员变量
  • 添加输入校验:检查OTP长度是否为6位,提前拦截无效输入
  • 异常捕获与提示:用try-catch包裹登录逻辑,捕获Firebase异常并给出明确提示
  • 优化页面跳转:先关闭弹窗再跳转到成功页面,避免上下文逻辑混乱
  • 完善自动验证:在verificationCompleted回调中处理自动登录场景,并用mounted判断组件状态,避免空上下文错误

内容的提问来源于stack exchange,提问作者VIKAS VERMA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:52:42