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

在Flutter的adsearch.dart中实现Dialog式Firebase OTP认证

实现Dialog形式的Firebase OTP认证流程(adsearch.dart)

以下是完整的实现方案,直接在adsearch.dart文件中完成Dialog式OTP登录,并在userAuthentication函数中调用:

1. 前置配置

确保项目已集成Firebase Auth,并在Firebase控制台启用Phone认证,同时在pubspec.yaml中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  firebase_auth: ^4.17.0

2. 实现Dialog组件

在adsearch.dart中添加以下代码,包含手机号输入页、OTP验证页的切换逻辑:

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

// 显示OTP认证Dialog
void _showPhoneAuthDialog(BuildContext context) {
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _otpController = TextEditingController();
  String? _verificationId;
  int _currentPage = 0; // 0: 手机号输入页, 1: OTP验证页
  bool _isLoading = false;

  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (context) => StatefulBuilder(
      builder: (context, setState) => AlertDialog(
        title: Text(_currentPage == 0 ? '登录账号' : '输入验证码'),
        content: _isLoading
            ? const Center(child: CircularProgressIndicator())
            : _currentPage == 0
                ? TextField(
                    controller: _phoneController,
                    keyboardType: TextInputType.phone,
                    decoration: const InputDecoration(
                      hintText: '请输入手机号(如+86138xxxxxxx)',
                      prefixIcon: Icon(Icons.phone),
                    ),
                  )
                : Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      TextField(
                        controller: _otpController,
                        keyboardType: TextInputType.number,
                        maxLength: 6,
                        decoration: const InputDecoration(
                          hintText: '请输入6位验证码',
                          prefixIcon: Icon(Icons.sms),
                        ),
                      ),
                      const SizedBox(height: 10),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          TextButton(
                            onPressed: () => setState(() => _currentPage = 0),
                            child: const Text('修改手机号'),
                          ),
                          TextButton(
                            onPressed: _verificationId == null
                                ? null
                                : () => _resendOTP(setState, context),
                            child: const Text('重新发送'),
                          ),
                        ],
                      ),
                    ],
                  ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          ElevatedButton(
            onPressed: _isLoading ? null : () => _handleAction(setState, context),
            child: Text(_currentPage == 0 ? '发送验证码' : '验证登录'),
          ),
        ],
      ),
    ),
  );

  // 处理页面按钮点击逻辑
  void _handleAction(StateSetter setState, BuildContext context) async {
    setState(() => _isLoading = true);
    try {
      if (_currentPage == 0) {
        // 发送OTP
        await FirebaseAuth.instance.verifyPhoneNumber(
          phoneNumber: _phoneController.text.trim(),
          verificationCompleted: (PhoneAuthCredential credential) async {
            // 自动验证(部分设备支持)
            await FirebaseAuth.instance.signInWithCredential(credential);
            Navigator.pop(context);
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('登录成功')),
            );
          },
          verificationFailed: (FirebaseAuthException e) {
            setState(() => _isLoading = false);
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('发送失败:${e.message}')),
            );
          },
          codeSent: (String verificationId, int? resendToken) {
            setState(() {
              _isLoading = false;
              _verificationId = verificationId;
              _currentPage = 1;
            });
          },
          codeAutoRetrievalTimeout: (String verificationId) {
            _verificationId = verificationId;
          },
        );
      } else {
        // 验证OTP
        final credential = PhoneAuthProvider.credential(
          verificationId: _verificationId!,
          smsCode: _otpController.text.trim(),
        );
        await FirebaseAuth.instance.signInWithCredential(credential);
        Navigator.pop(context);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('登录成功')),
        );
      }
    } catch (e) {
      setState(() => _isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('操作失败:${e.toString()}')),
      );
    }
  }

  // 重新发送OTP
  void _resendOTP(StateSetter setState, BuildContext context) async {
    setState(() => _isLoading = true);
    try {
      await FirebaseAuth.instance.verifyPhoneNumber(
        phoneNumber: _phoneController.text.trim(),
        verificationCompleted: (PhoneAuthCredential credential) async {
          await FirebaseAuth.instance.signInWithCredential(credential);
          Navigator.pop(context);
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('登录成功')),
          );
        },
        verificationFailed: (FirebaseAuthException e) {
          setState(() => _isLoading = false);
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('重新发送失败:${e.message}')),
          );
        },
        codeSent: (String verificationId, int? resendToken) {
          setState(() {
            _isLoading = false;
            _verificationId = verificationId;
          });
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('验证码已重新发送')),
          );
        },
        codeAutoRetrievalTimeout: (String verificationId) {
          _verificationId = verificationId;
        },
      );
    } catch (e) {
      setState(() => _isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('重新发送失败:${e.toString()}')),
      );
    }
  }
}

3. 在userAuthentication函数中调用Dialog

在adsearch.dart的userAuthentication函数中直接调用上述Dialog:

void userAuthentication(BuildContext context) {
  // 触发OTP认证Dialog
  _showPhoneAuthDialog(context);
}

关键功能说明

  • 页面切换:通过_currentPage变量控制显示手机号输入页或OTP验证页
  • 加载状态:操作过程中显示CircularProgressIndicator,防止重复点击
  • 错误处理:发送/验证失败时通过SnackBar提示具体错误信息
  • 额外选项:OTP页提供「修改手机号」返回上一页,「重新发送」再次获取验证码
  • 自动验证:支持部分设备的自动验证码填充与登录

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:22:52