Flutter-如何检查用户是否登录,如果显示,则显示其他屏幕?

时间:2019-04-01 21:05:52

标签: dart flutter firebase-authentication

我的第一个屏幕是登录屏幕,它需要检查用户是否已登录才能直接打开主屏幕,但是使用此检查我得到了一个错误。

我正在检查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,

                    ),
                  ),
                ],
              ),
            );

          },
      ),


    );
  }




}

6 个答案:

答案 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;
      });
    }
  }