initializeFcmToken method

Future<void> initializeFcmToken({
  1. required Future<void> onTokenChanged(
    1. String? newToken,
    2. String? oldToken
    ),
})

Initializes FCM token management and syncs with server.

Call this after user is authenticated. The service will:

  1. Get the current FCM token
  2. Sync it to the server via onTokenChanged
  3. Listen for token refreshes and sync automatically

onTokenChanged is called when the token changes. Use this to sync the token to your backend server. The callback receives:

  • newToken: The new FCM token (null if unregistering)
  • oldToken: The previous FCM token (null if first registration)

Example:

await notificationService.initializeFcmToken(
  onTokenChanged: (newToken, oldToken) async {
    await myBackendService.updateFcmToken(newToken, oldToken);
  },
);

Implementation

Future<void> initializeFcmToken({
  required Future<void> Function(String? newToken, String? oldToken) onTokenChanged,
}) async {
  if (_hasFcmTokenInitialized) {
    logd('FCM token already initialized');
    return;
  }

  _onTokenChanged = onTokenChanged;

  // Handle APNS token for iOS/macOS
  if (!kIsWeb && (Platform.isIOS || Platform.isMacOS)) {
    await _waitForApnsToken();
  }

  // Get initial token
  try {
    final token = await FirebaseMessaging.instance.getToken();
    if (token != null) {
      final oldToken = await _getStoredToken();
      // Prefer the persisted token as the source of truth for "old token"
      // (cached in-memory values may be null on cold start).
      if (token != oldToken) {
        await _onTokenChanged!(token, oldToken);
        await _storeToken(token);
      }
      _cachedFcmToken = token;
      logd('FCM token initialized: ${token.substring(0, 20)}...');
    }
  } catch (e) {
    loge(e, 'Failed to get FCM token');
    return; // Non-critical, don't propagate
  }

  // Listen for token refreshes
  _tokenRefreshSubscription = FirebaseMessaging.instance.onTokenRefresh.listen(
    (newToken) async {
      final oldToken = await _getStoredToken();
      try {
        await _onTokenChanged!(newToken, oldToken);
        await _storeToken(newToken);
        _cachedFcmToken = newToken;
        logd('FCM token refreshed: ${newToken.substring(0, 20)}...');
      } catch (e) {
        loge(e, 'Error syncing refreshed token');
      }
    },
  );

  _hasFcmTokenInitialized = true;
  logi('FCM token management initialized');
}