【问题标题】:Flutter: FirebaseAuth (v1.2.0) with StreamBuilder not working on hot reload in Flutter WebFlutter:带有 StreamBuilder 的 FirebaseAuth (v1.2.0) 无法在 Flutter Web 中进行热重载
【发布时间】:2021-08-14 02:59:54
【问题描述】:

所以在过去的几周里,我一直在为 Web 和 Android 测试 FirebaseAuth 而且经验大多很糟糕。 我已尝试添加尽可能多的信息,以便为您提供足够的背景信息。

我的目标

我的最终目标是制作一个包来简化 Flutter 中的 FirebaseAuth 基本上,StreamBuilder 在来自 FirebaseAuth 的 authStateChanges 流上运行,它在登录后或当我重新加载整个页面(Flutter Web)时立即为用户提供,但在热重新加载期间不返回用户,即使我知道用户已通过身份验证。当我重新加载网页时它再次起作用。 这在 Android 中不存在,它按预期工作。 这非常令人沮丧,我可以寻求任何人的帮助!

颤振医生

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 版本控制

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

复制步骤

  • 创建 Flutter 应用
  • 创建 Firebase 应用
  • 在 Firebase 控制台中启用匿名身份验证
  • 将 Flutter 链接到 Firebase Android 应用(常规方式)
  • 将 Flutter 链接到 Firebase Web App(常规方式)
  • 添加依赖项(稍后显示)
  • 添加 main.dart 代码(稍后显示)
  • 使用 flutter run -d chrome 运行

/web/index.html 中的 FirebaseSDKVersioning

<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 依赖项

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 代码 (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]");
            },
          )
        ],
      ),
    );
  }
}

基本上它在页面重新加载或登录后返回 UID,但是当热重新加载完成时,即使用户实际上已登录,它也会显示 null。这正是问题所在!

请注意

我尝试使用两个插件的 v1.0.0 对其进行测试,以验证我的颤振版本是否不兼容,但这也不起作用。 这完全符合我对以下依赖版本的预期(在热重载时打印 UID):

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

这非常非常令人沮丧,绝对没有错误,警告或在控制台或任何地方。 SignIn 有效,但 authenticationState 不会在 Web 中的热重载上持续存在,(在 android 上完美运行) 但它仅适用于这些旧版本的网络。 这是一个错误吗? 如果没有,请帮助我。

谢谢!

玛纳斯·海马迪

【问题讨论】:

  • 找到任何解决方案了吗?陷入同样的​​问题。
  • 嘿! @KamranBashir 是的,我找到了解决方案!把我的答案贴在下面!

标签: android firebase flutter firebase-authentication flutter-web


【解决方案1】:

我刚刚找到了解决这个问题的方法! 基本上,FireFlutter 团队修复了一个生产级错误,并反过来暴露了 Dart SDK 的一个缺陷。 由于这只是一个仅限开发的错误(仅在热重启期间的错误),因此没有得到重视。

在我的研究中,我发现支持 StreamBuilder 和热重启的最后一个版本组合是

firebase_auth:0.20.1; firebase_core 0.7.0

firebase_auth:1.1.0; firebase_core:1.0.3

这些是它可以正常工作的唯一版本。每个后续版本都有暴露该错误的新升级。

解决方案非常简单!也适用于最新版本 (1.2.0) 的 firebase_auth 和 firebase_core 插件!

首先导入 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);

这确保它在 Flutter Web 中的热重启期间按预期工作!

【讨论】:

  • 完美运行,谢谢!
  • 您能否分享指向您引用的原始问题的链接,在 Dart 中暴露了一个错误?这将有助于跟踪何时可以删除此修复。
  • @droptic 看了你的回答后,我想你已经发现了,可以吗?但如果您仍然需要它,这里是我创建的问题的链接:FLutterFire #6247
【解决方案2】:

Manas 的回答有效(通过手动缓存登录状态),但这个错误现在也在颤振开发频道中得到修复。据我所知,这只会影响开发,因为它只会破坏热重载/重启,但不会破坏页面刷新。我发现在本地工作流中进行 web-first 开发非常不方便,所以我切换到本地开发。

flutter channel dev
flutter upgrade

如前所述,这似乎是一个暴露了 DarkSDK 错误的 Flutterfire 修复程序。对于好奇,以下是此问题/修复的参考:

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

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

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

【讨论】:

  • 谢谢你!我相信这会帮助很多人!这个错误真是令人沮丧!
猜你喜欢
  • 2020-02-23
  • 2021-04-27
  • 2021-06-17
  • 1970-01-01
  • 2020-08-10
  • 1970-01-01
  • 2020-11-17
  • 2022-08-19
  • 2020-02-08
相关资源
最近更新 更多