简体   繁体   English

Flutter:带有 StreamBuilder 的 FirebaseAuth (v1.2.0) 无法在 Flutter Web 中进行热重载

[英]Flutter: FirebaseAuth (v1.2.0) with StreamBuilder not working on hot reload in Flutter Web

So over the past few weeks I have been testing out FirebaseAuth both for the web and Android and the experience has been mostly bad.所以在过去的几周里,我一直在测试 web 和 Android 的 FirebaseAuth,但体验大多很糟糕。 I have tried to add as much information as I can to give you enough context.我已尝试添加尽可能多的信息,以便为您提供足够的背景信息。

My Goal我的目标

My EndGoal is to make a package to simplify FirebaseAuth in Flutter Basically, the StreamBuilder runs on the authStateChanges stream from FirebaseAuth, It gives a user immediately after signIn or when I reload the whole page (Flutter Web) but doesnt return a user during hot reload eventhough I know the user has been authenticated. My EndGoal is to make a package to simplify FirebaseAuth in Flutter Basically, the StreamBuilder runs on the authStateChanges stream from FirebaseAuth, It gives a user immediately after signIn or when I reload the whole page (Flutter Web) but doesnt return a user during hot reload即使我知道用户已通过身份验证。 It works again when i reload the webpage.当我重新加载网页时它再次起作用。 This does not exist in Android and it works as expected.这在 Android 中不存在,它按预期工作。 Its very frustrating, and i could use some help from anyone!它非常令人沮丧,我可以向任何人寻求帮助!

Flutter Doctor Flutter博士

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.2, on Microsoft Windows [Version 10.0.21296.1010], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[√] Chrome - develop for the web
[!] Visual Studio - develop for Windows (Visual Studio Community 2019 16.5.5)
X Visual Studio is missing necessary components. Please re-run the Visual Studio installer for the
  "Desktop development with C++" workload, and include these components:
    MSVC v142 - VS 2019 C++ x64/x86 build tools
     - If there are multiple build tool versions available, install the latest
    C++ CMake tools for Windows
    Windows 10 SDK
[√] Android Studio (version 4.0)
[√] VS Code (version 1.56.2)
[√] Connected device (3 available)

Dart Versioning Dart 版本控制

Dart VM version: 2.8.4 (stable) (Wed Jun 3 12:26:04 2020 +0200) on "windows_x64"

Steps To Reproduce重现步骤

  • Create Flutter App创建 Flutter 应用程序
  • Create Firebase App创建 Firebase 应用程序
  • Enable Anonymous Authentication in Firebase Console在 Firebase 控制台中启用匿名身份验证
  • Link Flutter to Firebase Android App (Usual Way)将 Flutter 链接到 Firebase Android 应用程序(通常方式)
  • link Flutter to Firebase Web App (Usual Way)将 Flutter 链接到 Firebase Web 应用程序(常规方式)
  • Add Dependencies (Shown Later)添加依赖项(稍后显示)
  • Add main.dart code (Shown Later)添加 main.dart 代码(稍后显示)
  • run using flutter run -d chrome使用flutter run -d chrome 运行

FirebaseSDKVersioning in /web/index.html /web/index.html 中的 FirebaseSDK 版本控制

<script src="https://www.gstatic.com/firebasejs/8.6.2/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.2/firebase-analytics.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.2/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.2/firebase-firestore.js"></script>

(the setup is correct as signIn works)

pubspec.yaml Dependencies pubspec.yaml 依赖项

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  #Firebase Dependencies
  firebase_core: ^1.2.0
  firebase_auth: ^1.2.0

Flutter Code (main.dart) Flutter 代码(main.dart)

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

FirebaseAuth fa = FirebaseAuth.instance;
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (!kIsWeb) {
    await Firebase.initializeApp();
  }
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Auth Demo',
      home: AuthDemo(),
    );
  }
}

class AuthDemo extends StatelessWidget {
  const AuthDemo({Key key}) : super(key: key);

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("AuthDemo"),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () async {
              await fa.signInAnonymously();
            },
            child: Text("Anon"),
          ),
          ElevatedButton(
            onPressed: () async {
              await fa.signOut();
            },
            child: Text("SignOut"),
          ),
          SizedBox(height: 20),
          StreamBuilder(
            stream: fa.authStateChanges(),
            builder: (context, snapshot) {
              return Text(snapshot.data?.uid ?? "[NULL]");
            },
          )
        ],
      ),
    );
  }
}

Basically it returns the UID on page reload or just after signIn but when a hot reload is done, it shows null eventhough the user is loggedIn actually.基本上它会在页面重新加载或登录后返回 UID,但是当热重新加载完成时,即使用户实际上已登录,它也会显示 null。 This is precisely the problem!这正是问题所在!

Please Note请注意

I tried to test it with v1.0.0 of both plugins to verify if my flutter version was incompatible but that didnt work too.我尝试使用两个插件的 v1.0.0 对其进行测试,以验证我的 flutter 版本是否不兼容,但这也不起作用。 This works exactly as I expect (print UID on hot reload) for the dependency versions of:对于以下依赖版本,这完全符合我的预期(在热重载时打印 UID):

firebase_core: "^0.7.0"
firebase_auth: "^0.20.1"

This is very very frustrating, There is absolutely no error, warning or on the console or anywhere.这非常令人沮丧,绝对没有错误,警告或控制台或任何地方。 The SignIn works but the authenticationState does not perist on Hot reload in the Web, (Works perfectly on android) but it works perfectly for the web only on these older versions. SignIn 有效,但 authenticationState 在 Web 中的热重载时不会持续存在(在 android 上完美运行),但它仅适用于这些旧版本的 web。 Is this a bug?这是一个错误吗? If not please help me.如果没有,请帮助我。

Thank you!谢谢!

Manas Hejmadi玛纳斯·海马迪

I just Found a Solution to this problem.我刚刚找到了解决这个问题的方法。 Basically the FireFlutter Team had fixed a production level bug and inturn that exposed a flaw of the Dart SDK, As this was only a Development only bug (bug only during Hot Restart).基本上,FireFlutter 团队已经修复了一个生产级别的错误,并反过来暴露了 Dart SDK 的缺陷,因为这只是一个仅限开发的错误(仅在热重启期间的错误)。 it was not given importance.它没有得到重视。

In my Research I have found that the last version combination that supports StreamBuilder and Hot Restart is在我的研究中,我发现支持 StreamBuilder 和热重启的最后一个版本组合是

firebase_auth: 0.20.1; firebase_auth:0.20.1; firebase_core 0.7.0 firebase_core 0.7.0

firebase_auth: 1.1.0; firebase_auth:1.1.0; firebase_core: 1.0.3 firebase_core:1.0.3

These are the only versions that It works properly on.这些是它可以正常工作的唯一版本。 Every subsequent version has the new upgrade that has exposed the bug.每个后续版本都有暴露该错误的新升级。

The Solution is very Simple.解决方案非常简单。 Works for the latest version (1.2.0) of the firebase_auth and firebase_core plugins too!也适用于 firebase_auth 和 firebase_core 插件的最新版本 (1.2.0)!

Firstly Import Sharedpreferences首先导入 Sharedpreferences

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart' as Foundation;
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class HotRestartBypassMechanism {
  ///The Standard SharedPreference instance
  static final Future<SharedPreferences> prefs =
      SharedPreferences.getInstance();

  ///Saves the Login State (true) for LoggedIn and (false) for LoggedOut
  static saveLoginState(bool isLoggedIn) async {
    //Ignore Operation if Not in DebugMode on Web
    if (!Foundation.kDebugMode && !Foundation.kIsWeb) return;

    SharedPreferences p = await prefs;
    p.setBool('is_logged_in', isLoggedIn);
  }

  ///Gets the login status from SharedPreferences
  static Future<bool> getLoginStatus() async {
    SharedPreferences p = await prefs;
    return p.getBool('is_logged_in') ?? false;
  }
}

class HotRestartByPassBuilder extends StatelessWidget {
  final Widget destinationFragment;
  final Widget loginFragment;
  const HotRestartByPassBuilder({
    Key key,
    this.destinationFragment,
    this.loginFragment,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<bool>(
      future: HotRestartBypassMechanism.getLoginStatus(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.data) {
            return destinationFragment;
          } else {
            return loginFragment;
          }
        } else {
          return loginFragment;
        }
      },
    );
  }
}


//Usage In StreamBuilder
StreamBuilder(
  stream: FirebaseAuth.instance.authStateChanges(),
  builder: (context, snapshot) {
      if (snapshot.hasData) {
        return HomePage();
      } else {
        //Only in Debug Mode & in Web
        if (Foundation.kDebugMode && Foundation.kIsWeb) {
          //---------------------HOT RESTART BYPASS--------------------------
          return HotRestartByPassBuilder(
            destinationFragment: HomePage(),
            loginFragment: LoginPage(),
          );
          //-----------------------------------------------------------------
        } else {
          return LoginPage();
        }
      }
    },
);

//When User Logs in Use
await HotRestartBypassMechanism.saveLoginState(true);

//When User Logs out use
await HotRestartBypassMechanism.saveLoginState(false);

This ensures that it works just as expected during Hot Restart in Flutter Web!这确保它在 Flutter Web 中的热重启期间按预期工作!

Manas' answer works (by caching the login state manually), but this bug is also fixed in the flutter dev channel now. Manas 的回答有效(通过手动缓存登录 state),但这个错误现在也在 flutter 开发通道中修复。 This only affects development as far as I can tell because it only breaks hot reload/restart, but not page refresh.据我所知,这只会影响开发,因为它只会中断热重载/重启,而不是页面刷新。 I found it very inconvenient for web-first development in my local workflow, so I switched to dev locally.我发现在本地工作流程中进行 web-first 开发非常不方便,所以我切换到本地开发。

flutter channel dev
flutter upgrade

As mentioned, this appears to have been a flutterfire fix that exposed a DarkSDK bug.如前所述,这似乎是一个暴露了 DarkSDK 错误的颤振修复。 For the curious, here are the references for this issue/fix:出于好奇,以下是此问题/修复的参考:

FlutterFire issue: https://github.com/FirebaseExtended/flutterfire/issues/6247 FlutterFire 问题: https://github.com/FirebaseExtended/flutterfire/issues/6247

DartSDK issue: https://github.com/dart-lang/sdk/issues/45874 DartSDK 问题: https://github.com/dart-lang/sdk/issues/45874

DartSDK fix: https://github.com/dart-lang/sdk/commit/460887d8149748d3feaad857f1b13721faadeffa DartSDK 修复: https://github.com/dart-lang/sdk/commit/460887d8149748d3feaad857f1b13721faadeffa

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

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