- Notifications
You must be signed in to change notification settings - Fork 4.1k
Labels
blocked: flutterplatform: webIssues / PRs which are specifically for web.Issues / PRs which are specifically for web.plugin: authplugin: cloud_firestoreplugin: storageresolution: fixedA fix has been merged or is pending merge from a PR.A fix has been merged or is pending merge from a PR.type: bugSomething isn't workingSomething isn't working
Description
Bug report
Describe the bug
Listeners to AuthStateChanges are not released on web hot restart, resulting in multiple listeners. These listeners cannot be cleared by the developer.
Steps to reproduce
- Run the following code in Flutter Web (replacing the test username and password)
import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Firebase Auth Test', home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late FirebaseAuth firebaseAuth; late Stream<String?> firebaseAuthUserStream; late StreamSubscription firebaseAuthListener; @override void initState() { super.initState(); firebaseAuth = FirebaseAuth.instance; firebaseAuthUserStream = FirebaseAuth.instance .authStateChanges() .map((event) => event?.uid) .asBroadcastStream(); firebaseAuthListener = firebaseAuthUserStream.listen((uid) { print('AuthStateChanges: $uid'); }); } @override void dispose() { firebaseAuthListener.cancel(); super.dispose(); } void initFirebase() async { firebaseAuth.signInWithEmailAndPassword( email: 'test@testuser.com', password: '12345678'); } @override Widget build(BuildContext parentContext) { print('Build Home'); return Scaffold( body: SafeArea( child: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ StreamBuilder<String?>( initialData: null, stream: firebaseAuthUserStream, builder: (context, snapshot) { print('Build Stream Builder'); if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } if (snapshot.hasData) { print(snapshot.data); print('Rebuild Stream'); return Text('User: ${snapshot.data!}'); } return Text('No data'); }), TextButton( onPressed: initFirebase, child: Text('Login'), ), ], ), ), ), ); } } - Press hot restart.
- Press hot restart a few more times.
- Press the Login button.
- Observe that the listener is duplicated the number of times that you hot restarted.
Expected behavior
Listeners are released and not duplicated. Web behavior should be the same as on mobile and desktop.
Sample project
This issue is likely specific to FlutterFire as replacing the auth listener with a regular listener does not result in duplicated listeners.
** Regular Listener Example **
import 'dart:async'; import 'package:flutter/material.dart'; void main() async { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Listener Web Reload Test', home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { StreamController<String?> streamController = StreamController.broadcast(); late StreamSubscription streamSubscription; @override void initState() { super.initState(); streamSubscription = streamController.stream.listen((value) { print('Update Change: $value'); }); } @override void dispose() { streamSubscription.cancel(); super.dispose(); } void updateValue() async { streamController.add(UniqueKey().toString()); } @override Widget build(BuildContext parentContext) { print('Build Home'); return Scaffold( body: SafeArea( child: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ StreamBuilder<String?>( initialData: null, stream: streamController.stream, builder: (context, snapshot) { print('Build Stream Builder'); if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } if (snapshot.hasData) { print(snapshot.data); print('Rebuild Stream'); return Text('Value: ${snapshot.data!}'); } return Text('No data'); }), TextButton( onPressed: updateValue, child: Text('Update Value'), ), ], ), ), ), ); } } Additional context
Add any other context about the problem here.
Flutter doctor
Run flutter doctor and paste the output below:
Click To Expand
[√] Flutter (Channel stable, 2.5.1, on Microsoft Windows [Version 6.1.7601], locale en-US) [!] Android toolchain - develop for Android devices (Android SDK version 30.0.2) X cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. X Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/windows#android-setup for more details. [√] Chrome - develop for the web [√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.9.0) [√] Android Studio (version 2020.3) [√] VS Code, 64-bit edition (version 1.60.1) [√] Connected device (3 available) Flutter dependencies
Run flutter pub deps -- --style=compact and paste the output below:
Click To Expand
firebase_core: ^1.6.0 firebase_auth: ^3.1.1 google_sign_in: ^5.1.0 cloud_firestore: ^2.5.3 firebase: ^9.0.1 firebase_storage: ^10.0.3 cloud_functions: ^3.0.3 CodingSoot, jpangburn, JankoLancer, tamas-p, matt-hall-zory and 3 more
Metadata
Metadata
Assignees
Labels
blocked: flutterplatform: webIssues / PRs which are specifically for web.Issues / PRs which are specifically for web.plugin: authplugin: cloud_firestoreplugin: storageresolution: fixedA fix has been merged or is pending merge from a PR.A fix has been merged or is pending merge from a PR.type: bugSomething isn't workingSomething isn't working