Flutter:带有 StreamBuilder 的 FirebaseAuth (v1.2.0) 无法在 Flutter Web 中进行热重载
Flutter: FirebaseAuth (v1.2.0) with StreamBuilder not working on hot reload in Flutter Web
所以在过去的几周里,我一直在为网络和 Android 测试 FirebaseAuth
而且经验大多很糟糕。
我已尝试添加尽可能多的信息来为您提供足够的上下文。
我的目标
我的最终目标是做一个包来简化 Flutter 中的 FirebaseAuth
基本上,来自 FirebaseAuth 的 authStateChanges 流上的 StreamBuilder 运行s,它在登录后或当我重新加载整个页面(Flutter Web)时立即为用户提供,但在热重新加载期间没有 return 用户,尽管我知道用户已通过身份验证。当我重新加载网页时它再次工作。
这在 Android 中不存在,并且按预期工作。
非常令人沮丧,我需要任何人的帮助!
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 版本控制
Dart VM version: 2.8.4 (stable) (Wed Jun 3 12:26:04 2020 +0200) on "windows_x64"
重现步骤
- 创建 Flutter 应用程序
- 创建 Firebase 应用程序
- 在 Firebase 控制台中启用匿名身份验证
- Link Flutter 到 Firebase Android 应用程序(通常方式)
- link Flutter 到 Firebase Web 应用程序(通常方式)
- 添加依赖项(稍后显示)
- 添加 main.dart 代码(稍后显示)
- 运行 使用 flutter 运行 -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]");
},
)
],
),
);
}
}
基本上它 return 是页面重新加载时或刚登录后的 UID,但是当热重新加载完成时,它显示 null,即使用户实际上已登录。这正是问题所在!
请注意
我尝试使用两个插件的 v1.0.0 对其进行测试,以验证我的 flutter 版本是否不兼容,但这也不起作用。
对于以下依赖版本,这完全符合我的预期(在热重载时打印 UID):
firebase_core: "^0.7.0"
firebase_auth: "^0.20.1"
这非常令人沮丧,绝对没有错误、警告或控制台或任何地方。
SignIn 有效,但 authenticationState 不会持续存在于 Web 中的热重载,(在 android 上完美运行)
但它仅适用于这些旧版本的网络。
这是一个错误吗?
如果不是请帮助我。
谢谢!
玛纳斯海马迪
我刚刚找到了这个问题的解决方案!
基本上,FireFlutter 团队修复了一个生产级错误,进而暴露了 Dart SDK 的一个缺陷。
由于这只是一个仅用于开发的错误(仅在热重启期间出现错误),因此没有给予重视。
在我的研究中我发现最后一个支持 StreamBuilder 和 Hot Restart 的版本组合是
firebase_auth: 0.20.1;
firebase_core 0.7.0
firebase_auth: 1.1.0;
firebase_core: 1.0.3
这些是唯一可以正常运行的版本。每个后续版本都有新的升级暴露了这个bug。
解决方法很简单!也适用于 firebase_auth 和 firebase_core 插件的最新版本 (1.2.0)!
首先导入共享首选项
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 热重启期间按预期工作!
Manas 的回答有效(通过手动缓存登录状态),但这个错误现在也在 flutter dev 频道中修复了。据我所知,这只会影响开发,因为它只会中断热 reload/restart,但不会刷新页面。我发现在我的本地工作流程中进行 web-first 开发非常不方便,所以我切换到本地开发。
flutter channel dev
flutter upgrade
如前所述,这似乎是一个暴露了 DarkSDK 错误的 flutterfire 修复程序。出于好奇,这里是 issue/fix:
的参考资料
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
所以在过去的几周里,我一直在为网络和 Android 测试 FirebaseAuth 而且经验大多很糟糕。 我已尝试添加尽可能多的信息来为您提供足够的上下文。
我的目标
我的最终目标是做一个包来简化 Flutter 中的 FirebaseAuth 基本上,来自 FirebaseAuth 的 authStateChanges 流上的 StreamBuilder 运行s,它在登录后或当我重新加载整个页面(Flutter Web)时立即为用户提供,但在热重新加载期间没有 return 用户,尽管我知道用户已通过身份验证。当我重新加载网页时它再次工作。 这在 Android 中不存在,并且按预期工作。 非常令人沮丧,我需要任何人的帮助!
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 版本控制
Dart VM version: 2.8.4 (stable) (Wed Jun 3 12:26:04 2020 +0200) on "windows_x64"
重现步骤
- 创建 Flutter 应用程序
- 创建 Firebase 应用程序
- 在 Firebase 控制台中启用匿名身份验证
- Link Flutter 到 Firebase Android 应用程序(通常方式)
- link Flutter 到 Firebase Web 应用程序(通常方式)
- 添加依赖项(稍后显示)
- 添加 main.dart 代码(稍后显示)
- 运行 使用 flutter 运行 -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]");
},
)
],
),
);
}
}
基本上它 return 是页面重新加载时或刚登录后的 UID,但是当热重新加载完成时,它显示 null,即使用户实际上已登录。这正是问题所在!
请注意
我尝试使用两个插件的 v1.0.0 对其进行测试,以验证我的 flutter 版本是否不兼容,但这也不起作用。 对于以下依赖版本,这完全符合我的预期(在热重载时打印 UID):
firebase_core: "^0.7.0"
firebase_auth: "^0.20.1"
这非常令人沮丧,绝对没有错误、警告或控制台或任何地方。 SignIn 有效,但 authenticationState 不会持续存在于 Web 中的热重载,(在 android 上完美运行) 但它仅适用于这些旧版本的网络。 这是一个错误吗? 如果不是请帮助我。
谢谢!
玛纳斯海马迪
我刚刚找到了这个问题的解决方案! 基本上,FireFlutter 团队修复了一个生产级错误,进而暴露了 Dart SDK 的一个缺陷。 由于这只是一个仅用于开发的错误(仅在热重启期间出现错误),因此没有给予重视。
在我的研究中我发现最后一个支持 StreamBuilder 和 Hot Restart 的版本组合是
firebase_auth: 0.20.1; firebase_core 0.7.0
firebase_auth: 1.1.0; firebase_core: 1.0.3
这些是唯一可以正常运行的版本。每个后续版本都有新的升级暴露了这个bug。
解决方法很简单!也适用于 firebase_auth 和 firebase_core 插件的最新版本 (1.2.0)!
首先导入共享首选项
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 热重启期间按预期工作!
Manas 的回答有效(通过手动缓存登录状态),但这个错误现在也在 flutter dev 频道中修复了。据我所知,这只会影响开发,因为它只会中断热 reload/restart,但不会刷新页面。我发现在我的本地工作流程中进行 web-first 开发非常不方便,所以我切换到本地开发。
flutter channel dev
flutter upgrade
如前所述,这似乎是一个暴露了 DarkSDK 错误的 flutterfire 修复程序。出于好奇,这里是 issue/fix:
的参考资料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