我正在实施电子邮件优先流程,以使用 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
,但这也不起作用。
有人知道如何解决我的问题吗?
我不完全确定这是否有效,但我相信主要问题是你被推到了一条新路线,所以你不再在你的
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();