简体   繁体   English

用户使用 Firebase 身份验证登录时屏幕不刷新

[英]Screen not refreshing when user logs in with Firebase Authentication

I have a Flutter app in which I am using firebase_auth ^0.18.1+2 to log the user in with an email/password.我有一个 Flutter 应用程序,我在其中使用firebase_auth ^0.18.1+2使用电子邮件/密码登录用户。 There are two screens:有两个屏幕:

  • Home screen: where the user is presented the option to log in主屏幕:向用户提供登录选项的位置
  • Dashboard screen: the first screen to be shown when logged in仪表板屏幕:登录时显示的第一个屏幕

After the user logs in, however, the app does not refresh to show the Dashboard;但是,用户登录后,应用程序不会刷新以显示仪表板; the Home screen continues to appear.主屏幕继续出现。 Can anyone advise?任何人都可以建议吗?

After logging in, only when I perform a Hot Restart does the Dashboard appear.登录后,只有当我执行热重启时,仪表板才会出现。

main.dart : main.dart

MultiProvider(
    providers: [
      // Provider 1
      // ...
      // Provider 2
      // ...
      Provider<AuthService>(
        create: (_) => AuthService(),
      ),
      // Provider 4
      // ...
      // Provider 5
      // ...
    ],
    child: App(),
),

auth_service.dart : auth_service.dart

Stream<User> get authStateChanges => FirebaseAuth.instance.authStateChanges();

app.dart : app.dart

final AuthService authService =
    Provider.of<AuthService>(context, listen: false);

return MaterialApp(
  debugShowCheckedModeBanner: false,
  home: StreamBuilder<User>(
    stream: authService.authStateChanges,
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.active) {

        if (snapshot.hasData) {
        authService.firebaseUser = snapshot.data;

          return Scaffold(
            body: FutureBuilder<DocumentSnapshot>(
              future: authService.fetchExtraUserData(),
              builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.active) {
                      authService.user = snapshot.data;

                      // Dashboard does not appear, however Flutter logging shows that Dashboard
                      // initState() is being called in the background.

                      if (snapshot.hasData) {
                         return Dashboard();
                      } else {
                         return Scaffold(
                            backgroundColor: Colors.white,
                            body: Center(
                               child: CircularProgressIndicator(),
                             ),
                          );
                      }
                  } else {
                      return Scaffold(
                      backgroundColor: Colors.white,
                      body: Center(
                         child: CircularProgressIndicator(),
                        ),
                    );
                  }
              },
            ),
          );
        } else {
          return Home();
        }
      } else {
        return Scaffold(
          backgroundColor: Colors.white,
          body: Center(
            child: CircularProgressIndicator(),
          ),
        );
      }
    },
  ),
);

I have also tried the following with no luck:我也试过以下没有运气:

  • Removing the FutureBuilder which calls authService.fetchExtraUserData()删除调用authService.fetchExtraUserData()FutureBuilder

  • Moving StreamBuilder above MaterialAppStreamBuilder移到MaterialApp之上

    return StreamBuilder<User>( stream: authService.authStateChanges, builder: (context, snapshot) { return MaterialApp( debugShowCheckedModeBanner: false, home: snapshot.hasData? Dashboard(): Home(), ); }, );

It is also interesting to see that logging out works correctly - the user is returned to the Home screen.还有趣的是,注销工作正常 - 用户返回到主屏幕。 This shows that authStateChanges is working correctly in this case.这表明authStateChanges在这种情况下工作正常。

logout(BuildContext context) async => await authService.signOut();

flutter doctor output: flutter doctor output:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, 1.23.0-18.1.pre, on Mac OS X 10.15.7 19H2 x86_64)

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.1)
[✓] Xcode - develop for iOS and macOS (Xcode 12.3)
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.52.0)
[✓] Connected device (3 available)

Well I have a demo which does work perfectly fine, let me post the relevant parts:好吧,我有一个运行良好的演示,让我发布相关部分:

main.dart I used ChangeNotifierProvider.value() main.dart我使用了ChangeNotifierProvider.value()

...
return MultiProvider(
        providers: [
          ChangeNotifierProvider.value(
            value: AuthenticationService.instance(),
          ),
        ],
        child: MaterialApp(
          theme: ThemeData(
            primarySwatch: Colors.red,
          ),
          home: _screenMaker(context),
        ));
...

This is my _screenMaker Widget in the main.dart which is receiving the realtime state since im working with Consumers from the provider Package这是我在main.dart中的Consumers Widget

Widget _screenMaker(BuildContext context) {
  return Consumer<AuthenticationService>(
    builder: (ctx, AuthenticationService auth, _) {
      switch (auth.status) {
        case Status.Uninitialized:
          return Splash();
        case Status.Unauthenticated:
        case Status.Authenticating:
          return LoginPage();
        case Status.NoVerification:
          return Verification();
        case Status.Authenticated:
          return UserInfoPage(
            user: auth.user,
          );
      }
    },
  );
}

Lets look at my AuthentificationService class, Im going to post the complete content but the magic for the state management is happening at the beginning after my getters:让我们看看我的AuthentificationService class,我将发布完整的内容,但 state 管理的魔力发生在我的吸气剂之后:

import 'package:flutter/widgets.dart';
import 'package:firebase_auth/firebase_auth.dart';

enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated , NoVerification}

class AuthenticationService with ChangeNotifier {
  FirebaseAuth _auth = FirebaseAuth.instance;
  User _user;
  Status _status = Status.Uninitialized;

  Status get status => _status;
  User get user => _user;


  // Magic happens here
  AuthenticationService.instance() : _auth = FirebaseAuth.instance {
    _auth.authStateChanges().listen((firebaseUser) {
      print('--- authstate listening');
      if (firebaseUser == null) {
        _status = Status.Unauthenticated;
      } else if(firebaseUser != null && firebaseUser.emailVerified == false) {
        _status = Status.NoVerification;
      } else {
        _user = firebaseUser;
        _status = Status.Authenticated;
      }
      notifyListeners();
    });
  }


  Future<bool> signIn(String email, String password) async {
    try {
      _status = Status.Authenticating;
      notifyListeners();
      await _auth.signInWithEmailAndPassword(email: 'admin@admin.de', password: 'admin123');

      if(_auth.currentUser.emailVerified == null){
        _status = Status.Unauthenticated;
        print('--- user email verified is null');
        notifyListeners();
      }
   
      if(_auth.currentUser.emailVerified != true) {
        _status = Status.Authenticated;
        return true;
      } else {
        print('--- user needs confirmation');
        try {
        _status = Status.NoVerification;
        await user.sendEmailVerification();
        signOut();
        return true;
     } catch (e) {
        print('--- An error occured while trying to send email verification');
        print(e.message);
     }
      }
    } catch (e) {
      print('--- $e');
      signOut();
      notifyListeners();
      return false;
    }
  }



  // Signout function
  Future signOut() async {
    _auth.signOut();
    _status = Status.Unauthenticated;
    notifyListeners();
    return Future.delayed(Duration.zero);
  }

}

Thats everything you need.这就是你需要的一切。 Read more about Consumers, this is a really powerful article for understanding it: https://medium.com/flutter-community/managing-flutter-state-using-provider-e26c78060c26阅读有关消费者的更多信息,这是一篇非常强大的文章,可以帮助您理解它: https://medium.com/flutter-community/managing-flutter-state-using-provider-e26c78060c26

I think the issue is because of the false parameters passed into the provider.of<T>() call.我认为这个问题是因为传入provider.of<T>()调用的false参数。

Try final AuthService authService = Provider.of<AuthService>(context);尝试final AuthService authService = Provider.of<AuthService>(context);

which tried to listen to a value exposed with the provider, from outside of the widget tree.它试图从小部件树的外部侦听提供者公开的值。

final AuthService authService =
Provider.of<AuthService>(context, listen: false);

In this line argument listen: false tells the widget to not rebuild when the state changes(user logs in).在这一行中,参数listen: false告诉小部件在 state 更改(用户登录)时不重建。

Try removing listen: false from the provider.尝试从提供者中删除listen: false

Also instead of creating provider as也不是将提供者创建为

Provider<AuthService>(
    create: (_) => AuthService(),
  )

which only exposes Auth Service values to widget tree and does not rebuild widget tree when state changes.仅在 state 更改时将 Auth Service 值公开给小部件树,并且不会重建小部件树。

Use ChangeNotifierProvider to listen to changes and rebuild your widget tree like this使用ChangeNotifierProvider来监听更改并像这样重建您的小部件树

ChangeNotifierProvider(
    create: (context) => AuthService(),
)

Ok Try this,好的试试这个,

First modify your firebase api class like this首先像这样修改您的 firebase api class

 Stream<User> get user {
    return auth.onAuthStateChanged
        .map((FirebaseUser user) => _userFromFirebase(user));
  }


  User _userFromFirebase(FirebaseUser user) {
    return user != null ? User(id: user.uid) : null;
  } 

after in your main.dart file modify like this在你的 main.dart 文件中修改如下

void main() => runApp(MyApp()); void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      value: AuthService().user,
      child: MaterialApp(
        theme: ThemeData(scaffoldBackgroundColor: Colors.blueAccent),
        debugShowCheckedModeBanner: false,
        home: LoginWrapper(),
        //home: McqLoader(),
      ),
    );
  }
}

make Login wrapper class like this像这样制作登录包装器 class

class LoginWrapper extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    
    final user = Provider.of<User>(context);
    if (user == null) {
      return Login();
    } else {
      return DashboardPage(id: user.id,);
    }
  }
}
class LandingPage extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
   final auth = Provider.of<AuthBase>(context);
   return StreamBuilder<Object>(
     stream: auth.onAuthChange,
     builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
            User user = snapshot.data;
            if (user == null) {
               return SignIn();
            }else {
               return HomePage();
            }
         }
            return CupertinoActivityIndicator();
      },
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM