Flutter StreamBuilder 无法识别 Firebase Auth 登录

问题描述 投票:0回答:1

我正在实施电子邮件优先流程,以使用 Firebase Auth 登录我的 Flutter 应用程序。对于用户流程,我使用的是 StreamBuilder,它应该识别用户是否登录。根据是否登录,用户应该被适当地重定向。问题是 StreamBuilder 在用户登录后不会导航到

HomeView()

这是我的 StreamBuilder 的实现:如果用户已登录,应用程序应该转到

HomeView()
,否则转到
InsertEmailView()
。未登录时,根据是否使用插入的电子邮件,导航至
LoginView()
RegisterView()

这就是想要的导航:
InsertEmailView -> LoginView / RegisterView -> HomeView

但是,如果用户从
LoginView()
开始(不使用
InsertEmailView()
),则重定向到
HomeView()
有效,否则无效。

class _WidgetTreeState extends State<WidgetTree>{
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: AuthService().authStateChanges, 
      builder: (context, snapshot){
        if (snapshot.connectionState == ConnectionState.waiting){
          return const CircularProgressIndicator();
        } else if (snapshot.hasData){
          return HomeView();
        } else {
          //return const LoginView(email: "[email protected]");
          return InsertEmailView(onEmailSubmitted: (email) {
            AuthService().isEmailInUse(email).then((isEmailInUse) {
              print(isEmailInUse);
              if (isEmailInUse){
                Navigator.push(
                  context, 
                  MaterialPageRoute(
                    builder: (context) => LoginView(email: email)
                  )
                );
              } else {
                Navigator.push(
                  context, 
                  MaterialPageRoute(
                    builder: (context) => RegisterView(email: email)
                  )
                );
              }
            },);
          });
        }
      }
    );
  }
}

如您所见,我实现了一个

AuthService()
,它实现了我需要的必要 Firebase Auth 方法,甚至检查给定的电子邮件是否已使用(效果很好)。
我实现了
authStateChanges
如下:

class AuthService with ChangeNotifier {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  User? get currentUser => _auth.currentUser;

  Stream<User?> get authStateChanges => _auth.authStateChanges();

  Future<void> signInWithEmailAndPassword({
    required String email, 
    required String password
  }) async {
    await _auth.signInWithEmailAndPassword(
      email: email, 
      password: password
    );
  }
  
  Future<void> createUserWithEmailAndPassword({
    required String email, 
    required String password
  }) async {
    await _auth.createUserWithEmailAndPassword(
      email: email, 
      password: password
    );
  }

  Future<void> signOut() async {
    await _auth.signOut();
  }

  Future<bool> isEmailInUse(String email) async {
    try {
      List<String> signInMethods = await _auth.fetchSignInMethodsForEmail(email);
      return signInMethods.contains("password"); // usual auth method is "password"
    } catch (error) {
      print('Error checking email: $error');
      return false;
    }
  }
}

这里还有

LoginView()
的实现:

class LoginView extends StatefulWidget {
  final String email;

  const LoginView({super.key, required this.email});

  @override
  State<LoginView> createState() => _LoginViewState(email: email);
}

class _LoginViewState extends State<LoginView> {
  String? errorMessage = '';
  String email;

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  _LoginViewState({required this.email}){
    _emailController.text = email;
  }

  Future<void> signInWithEmailAndPassword() async {
    try {
      await AuthService().signInWithEmailAndPassword(
        email: _emailController.text, 
        password: _passwordController.text
      );
    } on FirebaseAuthException catch (e) {
      setState(() {
        errorMessage = e.message;
      });
    }
  }

  Widget _title(){
    return const Padding(
      padding: EdgeInsets.all(30.0),
      child: Text(
        "Logge dich mit\nE-Mail und Passwort ein!",
        textAlign: TextAlign.center,
        style: TextStyle(
          fontSize: 20,
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  } 

  Widget _entryField(
    String title,
    TextEditingController controller,
    bool obscureText
  ){
    return SizedBox(
      width: 200,
      child: TextField(
        controller: controller,
        obscureText: obscureText,
        decoration: InputDecoration(
          labelText: title,
          border: const OutlineInputBorder(),
        ),
      ),
    );
  }

  Widget _errorMessage(){
    return Text(
      errorMessage == '' ?  '' : 'Humm ? $errorMessage',
      style: const TextStyle(
        color: Colors.red
      ),
    );
  }

  Widget _signInButton(){
    return ElevatedButton(
      onPressed: () {
        signInWithEmailAndPassword();//.then((value) => {
          //Navigator.push(context, MaterialPageRoute(builder: (context) => HomeView()))
        //});
      },
      child: const Text("Anmelden"),
    );
  }

  Widget _switchToRegister(){
    return ElevatedButton(
      onPressed: () {
        Navigator.push(context, MaterialPageRoute(builder: (context) => RegisterView(email: email),));
      },
      child: const Text("Doch registrieren?")
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            _title(),
            _entryField('E-Mail', _emailController, false),
            const SizedBox(height: 16),
            _entryField('Passwort', _passwordController, true),
            const SizedBox(height: 16),
            _errorMessage(),
            const SizedBox(height: 16),
            _signInButton(),
            const SizedBox(height: 16),
            _switchToRegister()
          ],
        ),
      ),
    );
  }
}

我可以想象

Navigator
破坏了快照实现。我还尝试将
AuthService
作为变量实现,以便在
AuthServices
中不存在多个
StreamBuilder
,但这也不起作用。

有人知道如何解决我的问题吗?

flutter firebase firebase-authentication stream-builder flutter-streambuilder
1个回答
0
投票

我不完全确定这是否有效,但我相信主要问题是你被推到了一条新路线,所以你不再在你的

StreamBuilder
中,所以我认为如果你简单地弹回到它可能会被修复登录后即可。也就是说。改变

  Widget _signInButton(){
    return ElevatedButton(
      onPressed: () {
        signInWithEmailAndPassword();//.then((value) => {
          //Navigator.push(context, MaterialPageRoute(builder: (context) => HomeView()))
        //});
      },
      child: const Text("Anmelden"),
    );
  }

  Widget _signInButton(){
    return ElevatedButton(
      onPressed: () {
        signInWithEmailAndPassword().then((value) => {
          Navigator.pop(context);
        });
      },
      child: const Text("Anmelden"),
    );
  }

还有一个问题是你一直调用

AuthService()
,每次都会创建一个新的实例。它需要是一个单例。最简单的方法是将这些行添加到类中

  static final AuthService _singleton = AuthService._internal();
  
  factory AuthService() {
    return _singleton;
  }
  
  AuthService._internal();
© www.soinside.com 2019 - 2024. All rights reserved.