我的第一个屏幕是登录屏幕,它需要检查用户是否已登录才能直接打开主屏幕,但是使用此检查我得到了一个错误。
我正在检查initState,条件返回的是true,所以看起来问题出在导航器上。
如果用户已登录,跳过第一屏的正确方法是什么?
错误:
I/flutter (20803): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (20803): The following assertion was thrown building Navigator-[GlobalObjectKey<NavigatorState>
I/flutter (20803): _WidgetsAppState#8ce27](dirty, state: NavigatorState#db484(tickers: tracking 2 tickers)):
I/flutter (20803): 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 2106 pos 12: '!_debugLocked':
I/flutter (20803): is not true.
I/flutter (20803): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (20803): more information in this error message to help you determine and fix the underlying cause.
代码:
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passController = TextEditingController();
final _formKey = GlobalKey<FormState>();
final _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
if(FirebaseAuth.instance.currentUser() != null){
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => HomeScreen()
));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: ScopedModelDescendant<UserModel>(
builder: (context, child, model){
if(model.isLoading)
return Center(
child: CircularProgressIndicator(),
);
return Form(
key: _formKey,
child: ListView(
padding: EdgeInsets.all(16),
children: <Widget>[
SizedBox(height: 67),
Icon(Icons.chrome_reader_mode, size: 150, color: Colors.blue,),
SizedBox(height: 16,),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
hintText: "Digite seu e-mail",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
fillColor: Colors.blueAccent
),
keyboardType: TextInputType.emailAddress,
validator: (text){
if(text.isEmpty || !text.contains("@"))
return "E-mail inválido!";
},
),
SizedBox(height: 16,),
TextFormField(
controller: _passController,
decoration: InputDecoration(
hintText: "Digite sua senha",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
fillColor: Colors.blueAccent
),
obscureText: true,
validator: (text){
if(text.isEmpty || text.length < 6)
return "Digite a senha!";
},
),
SizedBox(height: 16,),
FlatButton(
padding: EdgeInsets.all(13),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
color: Colors.blue,
child: Text("Entrar",
style: TextStyle(
color: Colors.white,
fontSize: 20
),
),
onPressed: (){
if(_formKey.currentState.validate()){
model.signIn(
email: _emailController.text,
pass: _passController.text,
onSuccess: _onSuccess,
onFail: _onFail,
);
}
},
),
SizedBox(height: 10,),
InkWell(
onTap: (){
if(_emailController.text.isEmpty || !_emailController.text.contains("@")){
_scaffoldKey.currentState.showSnackBar(
SnackBar(content: Text("Insira um e-mail válido para recuperação",
style: TextStyle(fontSize: 14),
),
backgroundColor: Colors.redAccent,
duration: Duration(seconds: 3),
)
);
} else {
model.recoverPass(_emailController.text);
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text("O e-mail de recuperação foi enviado!",
style: TextStyle(fontSize: 14),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 3),
)
);
}
},
child: Text("Esqueci minha senha",
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w400
),
textAlign: TextAlign.center,
),
),
SizedBox(height: 30,),
InkWell(
onTap: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (context)=> SignUpScreen())
);
},
child: Text("Não tem conta? Cadastre-se!",
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600
),
textAlign: TextAlign.center,
),
),
],
),
);
},
),
);
}
}
答案 0 :(得分:3)
那么您可以使用另一种方法来解决此类问题。而是检查您的loginScreen类中是否有用户登录,您可以在此之前执行此步骤,然后决定是否有用户登录时是否显示loginScreen或显示另一个屏幕,我想是MainScreen,如果该用户已登录。
我将简要介绍如何完成此操作。希望对您有所帮助。但是在我向您解释源代码中出什么问题之前。
if(FirebaseAuth.instance.currentUser() != null){
// wrong call in wrong place!
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => HomeScreen()
));
}
您的代码已损坏,因为currentUser()
是一个异步函数,并且在您进行调用时,此函数将返回一个不完整的Future对象,该对象是一个非null的对象。因此,导航器pushReplacement
总是被调用,并且由于小部件的状态尚未就绪而崩溃。
作为解决方案,您可以使用FutureBuilder并确定将打开哪个屏幕。
int main(){
runApp( YourApp() )
}
class YourApp extends StatelessWidget{
@override
Widget build(){
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot){
if (snapshot.hasData){
FirebaseUser user = snapshot.data; // this is your user instance
/// is because there is user already logged
return MainScreen();
}
/// other way there is no user logged.
return LoginScreen();
}
);
}
}
使用这种方法可以避免使用LoginScreen类来验证是否有用户登录!
请注意,您可以将snapshot.connectionState
属性与switch case
结合使用,以实现更精细的控件。
答案 1 :(得分:3)
我使用 main.dart 文件中的共享首选项做到了。 对我来说效果很好。
Widget build(BuildContext context) {
return FutureBuilder(
future: SharedPreferences.getInstance(),
builder:
(BuildContext context, AsyncSnapshot<SharedPreferences> prefs) {
var x = prefs.data;
if (prefs.hasData) {
if (x.getBool('isloggedin')) {
if (x.getString('type') == 'doctor') {
return MaterialApp(home: DrHome());
} else
return MaterialApp(home: PtHome());
}
}
return MaterialApp(home: SignIn());
});
}
答案 2 :(得分:2)
无需使用Firebase
就可以使用SharedPreferences
下面是一个简单的代码:Main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final SharedPreferences prefs = await SharedPreferences.getInstance();
var isLoggedIn = (prefs.getBool('isLoggedIn') == null) ? false : prefs.getBool('isLoggedIn');
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: isLoggedIn ? anotherPage() : loginPage(),
));
}
使用flutter软件包:shared_preferences
答案 3 :(得分:1)
这就是我的方法。首先获取当前用户。如果未登录,则该值为null;否则,该用户已登录。
// Get the firebase user
User firebaseUser = FirebaseAuth.instance.currentUser;
Widget firstWidget;
// Assign widget based on availability of currentUser
if (firebaseUser != null) {
firstWidget = Home();
} else {
firstWidget = LoginScreen();
}
// Run the app with appropriate screen
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'UniClass',
theme: ThemeData(
primaryColor: kPrimaryColor,
scaffoldBackgroundColor: Colors.white,
),
home: firstWidget,
);
答案 4 :(得分:1)
我不知道这对其他人是否有帮助,但我的方法是使用 Provider 并创建另一个名为 wrapper 的类,这个类将负责无压力地切换 btw 屏幕......我不不知道您是如何进行身份验证的,但是我在另一个类中进行了所有身份验证,该类是我创建的,并在 AuthService 中命名为 AuthService,所有身份验证都已完成
class Wrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user = Provider.of<UserModels>(context);
if (user == null) {
return LoginScreen();
} else {
return HomeScreen();
}
}
}
现在进行身份验证
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
//create user object based on firebase user
UserModels _userFromFirebaseUser(User user) {
return user != null ? UserModels(uid: user.uid) : null;
}
//auth change user stream
Stream<UserModels> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in with email and password
Future signInUser(String email, String pwd, {context}) async {
try {
await _auth
.signInWithEmailAndPassword(email: email, password: pwd)
.then((result) {
User user = result.user;
return _userFromFirebaseUser(user);
}).catchError((err) {
if (err.code == 'user-not-found') {
Flushbar(
message: "No user found for that email.",
duration: Duration(seconds: 7),
)..show(context);
} else if (err.code == 'wrong-password') {
Flushbar(
message: "Wrong password provided for that user.",
duration: Duration(seconds: 7),
)..show(context);
} else {
Flushbar(
message: "Internal Error: Something went wrong.",
duration: Duration(seconds: 7),
)..show(context);
}
});
} catch (e) {
print(e.toString());
return null;
}
}
}
现在点击您的登录按钮
onPressed: () async {
var form = formKey.currentState;
if (form.validate()) {
setState(() {
_isChecking = true;
});
String email = _email.text;
String pwd = _pwd.text;
await _authService
.signInUser(email, pwd, context: context)
.then((result) {
if (result != null) {
setState(() {
_isChecking = false;
});
} else {
setState(() {
_isChecking = false;
});
}
}).catchError((error) {
setState(() {
_isChecking = false;
});
});
}
},
这应该可以为您完成所有工作,而您不必考虑太多
答案 5 :(得分:0)
使用这个,我有同样的错误(导航器错误调用 null),我修复了它。
在您的代码中在 firebase Auth 之前添加 await
只需检查此解决方案:
@override
void initState() {
super.initState();
detectUser();
}
void detectUser() async {
setState(() {
loading = true;
});
FirebaseAuth _auth1 = await FirebaseAuth.instance;
if (_auth1.currentUser != null) {
print('email: ' + _auth1.currentUser.email);
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => Main(),
),
);
setState(() {
loading = false;
});
} else {
//print(user1);
setState(() {
loading = false;
});
}
}