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

Flutter中Firebase手机号认证OTP校验问题求助

Flutter Firebase 手机号认证 OTP 校验问题解决方案

修正认证服务类(FirebaseAuthService)

改用Firebase Auth v2的signInWithPhoneNumber方法(比旧版回调方式更易维护),封装发送验证码与OTP校验逻辑:

import 'package:firebase_auth/firebase_auth.dart';

class FirebaseAuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // 发送手机号验证码
  Future<String?> sendVerificationCode(String phoneNumber) async {
    try {
      // Web端需配置Recaptcha,移动端可省略该参数
      final confirmationResult = await _auth.signInWithPhoneNumber(
        phoneNumber,
        RecaptchaVerifier(
          container: 'recaptcha-container',
          size: RecaptchaVerifierSize.compact,
          theme: RecaptchaVerifierTheme.light,
        ),
      );
      return confirmationResult.verificationId;
    } catch (e) {
      print('发送验证码失败: ${e.toString()}');
      return null;
    }
  }

  // 校验OTP验证码
  Future<UserCredential?> verifyOTP(String verificationId, String smsCode) async {
    try {
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
      return await _auth.signInWithCredential(credential);
    } catch (e) {
      print('OTP校验失败: ${e.toString()}');
      return null;
    }
  }
}

修正OTP页面(OTPView)调用逻辑

确保正确存储verificationId,并规范使用async/await处理异步操作:

import 'package:flutter/material.dart';
import 'firebase_auth_service.dart';

class OTPView extends StatefulWidget {
  final String phoneNumber; // 传入带国际区号的手机号,如+8613xxxxxxxxx
  const OTPView({super.key, required this.phoneNumber});

  @override
  State<OTPView> createState() => _OTPViewState();
}

class _OTPViewState extends State<OTPView> {
  final FirebaseAuthService _authService = FirebaseAuthService();
  final TextEditingController _otpController = TextEditingController();
  String? _verificationId;

  @override
  void initState() {
    super.initState();
    // 页面初始化时自动发送验证码
    _sendVerificationCode();
  }

  Future<void> _sendVerificationCode() async {
    final verificationId = await _authService.sendVerificationCode(widget.phoneNumber);
    if (verificationId != null) {
      setState(() => _verificationId = verificationId);
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('验证码发送失败,请检查手机号格式或网络')),
      );
    }
  }

  Future<void> _onVerifyPressed() async {
    if (_verificationId == null || _otpController.text.length != 6) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('请输入有效的6位验证码')),
      );
      return;
    }

    final credential = await _authService.verifyOTP(
      _verificationId!,
      _otpController.text.trim(), // 去除可能的空格
    );

    if (credential != null) {
      // 校验成功,跳转至主页或业务页面
      Navigator.pushReplacementNamed(context, '/home');
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('验证码无效,请重新输入')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('验证手机号')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: _otpController,
              keyboardType: TextInputType.number,
              maxLength: 6,
              textAlign: TextAlign.center,
              decoration: const InputDecoration(
                labelText: '输入6位验证码',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: _onVerifyPressed,
              child: const Text('验证'),
            ),
          ],
        ),
      ),
    );
  }
}

常见问题排查

  • 手机号格式:必须携带国际区号(如+86),否则Firebase无法识别
  • Firebase配置:
    • Android:在控制台添加SHA-1、SHA-256指纹,确保google-services.json已正确放入项目
    • iOS:配置APNs权限,确保GoogleService-Info.plist已添加到Xcode项目
  • 验证码有效期:Firebase验证码有效期为5分钟,超时需重新发送
  • 错误定位:打印错误日志,根据错误码排查(如invalid-verification-code表示验证码无效,session-expired表示会话过期)

内容的提问来源于stack exchange,提问作者Adam A.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 12:37:02