8
8
9
9
import 'dart:async' ;
10
10
import 'dart:js_interop' ;
11
-
11
+ import 'dart:js_interop_unsafe' ;
12
+ import 'package:flutter/foundation.dart' ;
13
+ import 'package:web/web.dart' as web;
12
14
import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart' ;
13
15
import 'package:firebase_core_web/firebase_core_web_interop.dart' ;
14
16
import 'package:http_parser/http_parser.dart' ;
@@ -382,8 +384,7 @@ class Auth extends JsObjectWrapper<auth_interop.AuthJsImpl> {
382
384
jsObject.onAuthStateChanged (nextWrapper.toJS, errorWrapper.toJS);
383
385
384
386
await completer.future;
385
-
386
- await (unsubscribe.callAsFunction () as JSPromise <JSAny ?>? )? .toDart;
387
+ unsubscribe.callAsFunction ();
387
388
}
388
389
389
390
JSFunction ? _onAuthUnsubscribe;
@@ -394,13 +395,42 @@ class Auth extends JsObjectWrapper<auth_interop.AuthJsImpl> {
394
395
// ignore: close_sinks
395
396
StreamController <User ?>? _changeController;
396
397
398
+ String get authStateWindowsKey => 'flutterfire-${app .name }_authStateChanges' ;
399
+ String get idTokenStateWindowsKey => 'flutterfire-${app .name }_idTokenChanges' ;
400
+
401
+ // No way to unsubscribe from event listeners on hot reload so we set on the windows object
402
+ // and clean up on hot restart if it exists.
403
+ // See: https://github.com/firebase/flutterfire/issues/7064
404
+ void _unsubscribeWindowsListener (String key) {
405
+ if (kDebugMode) {
406
+ final unsubscribe = web.window.getProperty (key.toJS);
407
+ if (unsubscribe != null ) {
408
+ (unsubscribe as JSFunction ).callAsFunction ();
409
+ }
410
+ }
411
+ }
412
+
413
+ void _setWindowsListener (String key, JSFunction unsubscribe) {
414
+ if (kDebugMode) {
415
+ web.window.setProperty (key.toJS, unsubscribe);
416
+ }
417
+ }
418
+
419
+ void _removeWindowsListener (String key) {
420
+ if (kDebugMode) {
421
+ web.window.delete (key.toJS);
422
+ }
423
+ }
424
+
397
425
/// Sends events when the users sign-in state changes.
398
426
///
399
427
/// After 4.0.0, this is only triggered on sign-in or sign-out.
400
428
/// To keep the old behavior, see [onIdTokenChanged] .
401
429
///
402
430
/// If the value is `null` , there is no signed-in user.
403
431
Stream <User ?> get onAuthStateChanged {
432
+ _unsubscribeWindowsListener (authStateWindowsKey);
433
+
404
434
if (_changeController == null ) {
405
435
final nextWrapper = (auth_interop.UserJsImpl ? user) {
406
436
_changeController! .add (User .getInstance (user));
@@ -410,15 +440,17 @@ class Auth extends JsObjectWrapper<auth_interop.AuthJsImpl> {
410
440
411
441
void startListen () {
412
442
assert (_onAuthUnsubscribe == null );
413
- _onAuthUnsubscribe =
443
+ final unsubscribe =
414
444
jsObject.onAuthStateChanged (nextWrapper.toJS, errorWrapper.toJS);
445
+ _onAuthUnsubscribe = unsubscribe;
446
+ _setWindowsListener (authStateWindowsKey, unsubscribe);
415
447
}
416
448
417
- Future <void > stopListen () async {
418
- await (_onAuthUnsubscribe! .callAsFunction () as JSPromise <JSAny ?>? )
419
- ? .toDart;
449
+ void stopListen () {
450
+ _onAuthUnsubscribe! .callAsFunction ();
420
451
_onAuthUnsubscribe = null ;
421
452
_changeController = null ;
453
+ _removeWindowsListener (authStateWindowsKey);
422
454
}
423
455
424
456
_changeController = StreamController <User ?>.broadcast (
@@ -444,6 +476,7 @@ class Auth extends JsObjectWrapper<auth_interop.AuthJsImpl> {
444
476
///
445
477
/// If the value is `null` , there is no signed-in user.
446
478
Stream <User ?> get onIdTokenChanged {
479
+ _unsubscribeWindowsListener (idTokenStateWindowsKey);
447
480
if (_idTokenChangedController == null ) {
448
481
final nextWrapper = (auth_interop.UserJsImpl ? user) {
449
482
_idTokenChangedController! .add (User .getInstance (user));
@@ -453,16 +486,17 @@ class Auth extends JsObjectWrapper<auth_interop.AuthJsImpl> {
453
486
454
487
void startListen () {
455
488
assert (_onIdTokenChangedUnsubscribe == null );
456
- _onIdTokenChangedUnsubscribe =
489
+ final unsubscribe =
457
490
jsObject.onIdTokenChanged (nextWrapper.toJS, errorWrapper.toJS);
491
+ _onIdTokenChangedUnsubscribe = unsubscribe;
492
+ _setWindowsListener (idTokenStateWindowsKey, unsubscribe);
458
493
}
459
494
460
- Future <void > stopListen () async {
461
- await (_onIdTokenChangedUnsubscribe! .callAsFunction ()
462
- as JSPromise <JSAny ?>? )
463
- ? .toDart;
495
+ void stopListen () {
496
+ _onIdTokenChangedUnsubscribe! .callAsFunction ();
464
497
_onIdTokenChangedUnsubscribe = null ;
465
498
_idTokenChangedController = null ;
499
+ _removeWindowsListener (idTokenStateWindowsKey);
466
500
}
467
501
468
502
_idTokenChangedController = StreamController <User ?>.broadcast (
0 commit comments