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

Flutter中Firebase Authentication邮箱验证始终返回‘邮箱未注册’问题

问题:Firebase Authentication无法识别已注册邮箱

问题描述

我正在开发一款Flutter应用,采用Firebase Authentication实现邮箱密码登录流程:用户先在登录页面输入邮箱,点击“Continue”后,应用检查该邮箱是否已在Firebase注册——未注册则提示用户创建账号,已注册则跳转至密码输入页面。但目前遇到异常:即使输入Firebase控制台显示已注册的邮箱,应用仍返回“Email is not registered”提示。

核心验证代码

Future<void> checkEmail() async {
  String email = emailController.text.trim();

  setState(() {
    isLoading = true;
  });

  try {
    // Attempt to fetch sign-in methods for the email
    List<String> signInMethods =
        await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);

    if (signInMethods.isEmpty) {
      // Email is not registered
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Email is not registered. Please create an account.'),
        ),
      );
    } else {
      // Email is registered, proceed to password screen
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => SigninScreenPassword(
            email: email,
          ),
        ),
      );
    }
  } catch (e) {
    // Handle errors
    print('Error fetching sign-in methods: $e');
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('An error occurred: $e'),
      ),
    );
  } finally {
    setState(() {
      isLoading = false;
    });
  }
}

完整页面代码

// ignore_for_file: deprecated_member_use

import 'package:cloture/screens/authentication/create_account_screen.dart';
import 'package:cloture/screens/authentication/sign_in_screen_password.dart';
import 'package:cloture/utilities/buttons.dart';
import 'package:cloture/utilities/colors.dart';
import 'package:cloture/utilities/text.dart';
import 'package:cloture/utilities/textfield.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:cloture/gen/assets.gen.dart';

class SigninScreenEmail extends StatefulWidget {
  const SigninScreenEmail({super.key});

  @override
  State<SigninScreenEmail> createState() => _SigninScreenEmailState();
}

class _SigninScreenEmailState extends State<SigninScreenEmail> {
  TextEditingController emailController = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool isLoading = false;

  Future<void> checkEmail() async {
    String email = emailController.text.trim();

    setState(() {
      isLoading = true;
    });

    try {
      // Fetch sign-in methods for the email
      List<String> signInMethods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);

      if (signInMethods.isEmpty) {
        // Email is not registered
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Email is not registered. Please create an account.'),
          ),
        );
      } else {
        // Debugging: Print the sign-in methods
        print("Sign-in methods for email: $signInMethods");

        // Email is registered, proceed to password screen
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => SigninScreenPassword(
              email: email,
            ),
          ),
        );
      }
    } catch (e) {
      // Check for Firebase-specific errors
      print('Error fetching sign-in methods: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('An error occurred: $e'),
        ),
      );
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: white100,
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 27, vertical: 123),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              reusableText(
                'Sign in',
                32.sp,
                FontWeight.bold,
                black100,
                -0.41,
                TextAlign.left,
              ),
              SizedBox(height: 32.h),
              Form(
                key: _formKey,
                child: authTextField(
                    hintText: 'Email Address',
                    controller: emailController,
                    keyboardType: TextInputType.emailAddress,
                    textInputAction: TextInputAction.done,
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Please enter your email';
                      } else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+')
                          .hasMatch(value)) {
                        return 'Please enter a valid email address';
                      }
                      return null;
                    }),
              ),
              SizedBox(
                height: 16.h,
              ),
              appButton(
                'Continue',
                primary200,
                white100,
                47.h,
                344.w,
                100.r,
                16.sp,
                FontWeight.w500,
                Colors.transparent,
                -0.5,
                () {
                  if (_formKey.currentState!.validate()) {
                    checkEmail();
                  }
                },
              ),
              SizedBox(
                height: 16.h,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  reusableText(
                    'Don\'t have an account?',
                    12.sp,
                    FontWeight.w500,
                    black100,
                    -0.5,
                    TextAlign.left,
                  ),
                  SizedBox(
                    width: 8.w,
                  ),
                  GestureDetector(
                    onTap: () {
                      Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) => const CreateAccountScreen(),
                        ),
                      );
                    },
                    child: reusableText(
                      'Create One',
                      12.sp,
                      FontWeight.bold,
                      primary200,
                      -0.5,
                      TextAlign.left,
                    ),
                  ),
                ],
              ),
              SizedBox(
                height: 71.h,
              ),
              authButton(
                'Continue with Apple',
                Assets.images.appleSvg.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 12.h,
              ),
              authButton(
                'Continue with Google',
                Assets.images.googlePng0.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 12.h,
              ),
              authButton(
                'Continue with Facebook',
                Assets.images.facebookPng0.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 71.h,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

已尝试操作

  • 确认Firebase项目配置正确,控制台可查看该注册邮箱
  • 输入邮箱与控制台记录完全一致
  • 调用fetchSignInMethodsForEmail前已对邮箱做trim()处理

预期与实际行为

  • 预期:已注册邮箱跳转至密码输入页,未注册邮箱提示创建账号
  • 实际:输入已注册邮箱仍提示“Email is not registered”

可能原因及解决方案

1. Firebase邮箱枚举保护功能开启

Firebase Auth默认开启邮箱枚举保护,该功能会隐藏已注册邮箱的信息,防止恶意攻击者通过接口枚举邮箱地址。此时调用fetchSignInMethodsForEmail会返回空列表,即使邮箱已注册。

解决方案:

  • 调试阶段:临时关闭该功能(不建议生产环境使用)
    进入Firebase控制台 → 认证 → 设置 → 高级 → 邮箱枚举保护 → 关闭“启用邮箱枚举保护”
  • 生产环境:改用登录尝试判断邮箱状态
    直接调用signInWithEmailAndPassword,通过错误码区分邮箱是否注册:
    Future<void> checkEmail() async {
      String email = emailController.text.trim().toLowerCase();
      setState(() => isLoading = true);
    
      try {
        // 用空密码触发登录错误
        await FirebaseAuth.instance.signInWithEmailAndPassword(
          email: email,
          password: '',
        );
      } on FirebaseAuthException catch (e) {
        if (e.code == 'user-not-found') {
          // 邮箱未注册
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Email is not registered. Please create an account.')),
          );
        } else if (e.code == 'wrong-password') {
          // 邮箱已注册,跳转至密码输入页
          Navigator.of(context).push(
            MaterialPageRoute(builder: (context) => SigninScreenPassword(email: email)),
          );
        } else {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('An error occurred: ${e.message}')),
          );
        }
      } catch (e) {
        print('Error: $e');
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('An error occurred: $e')),
        );
      } finally {
        setState(() => isLoading = false);
      }
    }
    

2. 邮箱地址大小写未标准化

虽然Firebase Auth对邮箱大小写不敏感,但fetchSignInMethodsForEmail可能会严格匹配输入的大小写格式。如果注册时使用了大写字母(如Test@Example.com),而输入时用全小写(test@example.com),可能导致返回空列表。

解决方案:

在trim()后将邮箱转为全小写:

String email = emailController.text.trim().toLowerCase();

3. 账号未启用邮箱/密码登录方式

如果该邮箱是通过第三方登录(如Google/Facebook)注册的,且未启用邮箱/密码登录方式,fetchSignInMethodsForEmail会返回第三方登录方法列表,但如果后续需要支持密码登录,需补充设置。

解决方案:

  • 检查Firebase控制台中该账号的登录方式,若需支持邮箱密码登录,可引导用户补充设置密码
  • 调整逻辑,根据signInMethods的内容提示用户对应的登录方式

4. Firebase项目配置异常

  • 应用的google-services.json(Android)或GoogleService-Info.plist(iOS)不是当前项目的最新配置
  • 项目未启用“邮箱/密码”登录提供商

解决方案:

  • 重新下载最新的配置文件,替换项目中的旧文件
  • 进入Firebase控制台 → 认证 → 登录方法 → 确保“邮箱/密码”已启用

5. 账号状态异常

检查Firebase控制台中该账号的状态,是否被标记为禁用或已被删除。

解决方案:

进入Firebase控制台 → 认证 → 用户,找到该账号,确认状态为“启用”


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:27:03