通过电子邮件/密码进行身份验证的颤振问题
Posted
技术标签:
【中文标题】通过电子邮件/密码进行身份验证的颤振问题【英文标题】:Flutter issue with authentication via Email/Password 【发布时间】:2020-11-23 18:39:33 【问题描述】:我是 Flutter 的新手,我正在尝试构建一个具有登录和注册功能的身份验证系统。 我遵循了在线教程,一切正常。但我的身份验证流程略有不同,当我添加额外步骤时,登录或注册屏幕不会重定向到主页登录页面。
这是教程中的代码,运行良好。您会注意到,当应用程序启动时,出现的第一个屏幕是 Wrapper.dart
通过 IF 语句调用的 SignIn。
这里是教程中的文件:tutorial main.dart
void main()
runApp(Main());
class Main extends StatelessWidget
@override
Widget build(BuildContext context)
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
home: Wrapper(),
debugShowCheckedModeBanner: false,
)
);
教程 wrapper.dart
class Wrapper extends StatelessWidget
@override
Widget build(BuildContext context)
final user = Provider.of<User>(context);
// return either Home or Auth widget
if (user == null)
return Authenticate();
else
return Home();
tutorial authenticate.dart
class Authenticate extends StatefulWidget
@override
_AuthenticateState createState() => _AuthenticateState();
class _AuthenticateState extends State<Authenticate>
bool showSignIn = true;
void toggleView()
setState(() => showSignIn = !showSignIn);
@override
Widget build(BuildContext context)
if (showSignIn)
return SignIn(toggleView: toggleView);
else
return Register(toggleView: toggleView);
教程 signIn.dart
class SignIn extends StatefulWidget
final Function toggleView;
SignIn(this.toggleView);
@override
_SignInState createState() => _SignInState();
class _SignInState extends State<SignIn>
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
bool loading = false;
// text Field State
String email = '';
String password = '';
String error = '';
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.brown[400],
elevation: 0.0,
title: Text('Sign in'),
actions: <Widget>[
FlatButton.icon(
icon: Icon(Icons.person),
label: Text('Register'),
onPressed: ()
widget.toggleView();
,
)
]),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration: textInputDecoration.copyWith(hintText: 'Email'),
validator: (val) => val.isEmpty ? 'Enter an email' : null,
onChanged: (val)
setState(() => email = val);
,
),
SizedBox(height: 20.0),
TextFormField(
obscureText: true,
decoration:
textInputDecoration.copyWith(hintText: 'Password'),
validator: (val) => val.length < 6
? 'Password is too short. Min 6 characters long'
: null,
onChanged: (val)
setState(() => password = val);
,
),
SizedBox(height: 20.0),
RaisedButton(
color: Colors.pink[400],
child: Text(
'Sign in',
style: TextStyle(color: Colors.white),
),
onPressed: () async
if (_formKey.currentState.validate())
setState(() => loading = true);
dynamic result = await _auth.signInWithEmailAndPassword(
email, password);
if (result == null)
setState(()
error =
'These credentials are invalid. Please check them and try again!';
loading = false;
);
,
),
]))
));
Registration.dart
文件与SignIn
类似,这里就不展示了。
最后一个需要记录的文件是名为 AuthService.dart
class AuthService
final FirebaseAuth _auth = FirebaseAuth.instance;
// Create user object based on FirebaseUser
User _userFromFirebaseUser(FirebaseUser user)
return user != null ? User(uid: user.uid) : null;
// Auth change user stream
Stream<User> get user
return _auth.onAuthStateChanged.map(
_userFromFirebaseUser); // This is the equivalent of .map((FirebaseUser user) => _userFromFirebaseUser(user));
// SignIn with Email & Password
Future signInWithEmailAndPassword(String email, String password) async
try
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
catch (e)
print(e.toString());
return null;
让我解释一下上述流程的工作原理
当用户启动应用程序时,main.dart
文件调用wrapper.dart
,它检查用户是否为空或存在。
如果User=null
,则表示它没有登录并调用Authenticate.dart
,默认情况下会打开SignIn
屏幕。
如果User != null
,则表示用户已登录并调用Home.dart
屏幕。
****** 我的问题 *****
现在,在我的流程中(见下文),Wrapper
在User=null
时调用StartAuthButtons.dart
。StartAuthButtons
有几个按钮,可以使用不同的方法对用户进行身份验证,例如 Google、Facebook、Apple 和电子邮件/密码。
我的 Wrapper.dart:
class Wrapper extends StatelessWidget
@override
Widget build(BuildContext context)
final user = Provider.of<User>(context);
if (user == null)
return StartAuthButtons();
//return Authenticate(); <-- this was before
else
return Home();
一旦用户点击使用电子邮件和密码登录,我使用 Flutter Navigate
进入登录页面。这是我与教程不同的额外步骤。 (请在下面的代码中检查我的 cmets)
我的 StartAuthButtons.dart
class StartAuthButtons extends StatefulWidget
@override
_StartAuthButtonsState createState() => _StartAuthButtonsState();
class _StartAuthButtonsState extends State<StartAuthButtons>
final AuthService _auth = AuthService();
@override
Widget build(BuildContext context)
return Stack(children: [
AuthLayout(),
Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
title: Center(child: Text('Welcome')),
backgroundColor: Colors.transparent,
),
body: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//Email & Password SignIn
Padding(
padding: EdgeInsets.all(8.0), child: new MaterialButton(
child: SignInEmailPasswordBtn(),
onPressed: ()
****** I THINK THE ISSUE IS HERE *********
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Authenticate()),
);
*** As you can see above, here is where I call Authenticate() ***
****** But by adding this extra step to reach the SignIn page, when I signin it doesn't redirect to the Home once signed in. It stays on the SignIn screen. If I hard refresh the app, the system is logged in **********
)
)
]
)
)
)
))]);
使用Navigate
小部件转到SignIn
页面后,我在成功登录后似乎无法转到Home
屏幕。
身份验证工作正常,因为如果我硬刷新应用程序,它会以登录用户的身份显示Home
屏幕。
似乎onPress
进行身份验证,在Home
和StartAuthButton
之间选择的IF STATEMENT 没有被触发。
非常感谢任何帮助。 抱歉,脚本太长了。 乔
【问题讨论】:
这种行为的原因是您将多个小部件相互堆叠,这将不允许 Provider 到达它。我想说你需要做的就是pop 离开stack。看看这篇文章:medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31 @Frank van Puffelen ,我添加了 Navigator.of(context).maybePop();到登录按钮 onPress。一旦我登录,它确实会显示主页,但是当我点击登录时,屏幕会在转到Home
之前弹回“StartAuthButtons”。我的加载小部件甚至没有被考虑在内。有什么想法可以避免显示 StartAuthButtons 小部件并查看加载吗? (在 SignIn 小部件中的代码下方)
这里代码更新:onPressed: () async if (_formKey.currentState.validate()) setState(() => loading = true); Navigator.of(context).maybePop();等等……
是@Unbreachable 发表了该评论。我只是编辑了你问题的标签。
【参考方案1】:
感谢@Unbreachable 建议查看文章以进行pop & push。
我对两个提交 onPress 按钮上的 SignIn
和 Register
小部件进行了简单的更改。
之前的代码:
onPressed: () async
if (_formKey.currentState.validate())
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
if (result == null)
setState(()
error = 'Email or password are invalid.';
loading = false;
);
,
之后:
onPressed: () async
if (_formKey.currentState.validate())
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
if (result == null)
setState(()
error = 'Email or password are invalid.';
loading = false;
);
else
Navigator.of(context).pop(); <-- here the change.
loading = false; <-- here the change.
,
现在加载工作正常,当消失时我可以看到 home
小部件。
【讨论】:
以上是关于通过电子邮件/密码进行身份验证的颤振问题的主要内容,如果未能解决你的问题,请参考以下文章
使用邮件和密码通过 REST API [Firebase] 进行身份验证
django rest framework jwt 使用电子邮件和密码进行身份验证
用于电子邮件链接身份验证的颤振 ERROR_INVALID_ACTION_CODE 中的 firebase 身份验证
在不使用 Firebase 的情况下使用电子邮件和密码进行 Flutter 身份验证