dispose method
Disposes of the service and cleans up resources.
Implementation
Future<void> dispose() async {
await _foregroundSubscription?.cancel();
await _openedAppSubscription?.cancel();
await _tokenRefreshSubscription?.cancel();
await _authSubscription?.cancel();
await _lifecycleSubscription?.cancel();
await _badgeCountController.close();
_foregroundSubscription = null;
_openedAppSubscription = null;
_tokenRefreshSubscription = null;
_authSubscription = null;
_lifecycleSubscription = null;
_waitingForSettingsReturn = false;
_settingsChannel?.setMethodCallHandler(null);
_settingsChannel = null;
_onSystemNotificationSettingsOpened = null;
_handlingDeepLink = false;
_lifecycleResumeHandlerActive = false;
_initialized = false;
_hasFcmTokenInitialized = false;
// FCM-038/FCM-082/D1/RC-05: complete any still-in-flight single-flight
// primitive with a sentinel BEFORE replacing it, so a coalesced awaiter
// unblocks rather than hangs; then clear it plus the FCM-020 stash + the
// in-flight flag so a stale generation cannot regress a fresh capture on
// the dispose→re-initialize singleton-reuse cycle.
final inFlightInit = _fcmInitInFlight;
if (inFlightInit != null && !inFlightInit.isCompleted) {
inFlightInit.complete(FcmTokenInitResult.captureFailed);
}
_fcmInitInFlight = null;
_isCapturingInitialToken = false;
_stashedRefreshToken = null;
// B.3: clear the deferred-replay flag so a flag left true by a prior
// initialize() that threw before its replay block cannot trigger a phantom
// _handleLogin() on the next dispose→re-initialize singleton-reuse cycle.
_deferredLoginCapture = false;
// A.6/FCM-039: remove the persisted capture-backoff timestamp on dispose so a
// stale value from a prior generation cannot throttle the reinit's deferred
// login capture on the dispose→re-initialize singleton-reuse cycle. Only the
// backoff timestamp is cleared — the install-scoped one-time backfill keys
// (marker / attempt count / first-attempt timestamp) are deliberately NOT
// reset here; they must survive dispose. Best-effort: dispose is teardown and
// must not throw if the prefs platform channel is unavailable.
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_lastFcmCaptureAttemptAtKey);
} catch (e) {
logd('dispose: could not clear FCM capture-backoff timestamp: $e');
}
logi('NotificationService disposed');
}