initialize method
Future<void>
initialize({
- NotificationActionCallback? onNotificationTapped,
- NotificationButtonActionCallback? onNotificationAction,
- ForegroundMessageCallback? onForegroundMessage,
- NotificationErrorCallback? onError,
- bool showNotificationsInForeground = true,
- int reminderIntervalDays = 30,
- Future<
void> onTokenChanged()?, - AuthServiceInt? authService,
- @Deprecated('Use authService parameter instead for race-free initialization') bool autoConnectAuth = true,
Initializes the notification service.
This method sets up all notification handling including:
- Local notification plugin initialization
- FCM message listeners (foreground, background, terminated)
- Notification action handlers
- Platform-specific configuration
- Auto-wiring to auth service (if registered in GetIt)
This is the only method consuming apps need to call to set up notifications.
Parameters:
onNotificationTapped: Callback for when user taps a notificationonNotificationAction: Callback for when user taps an action buttononForegroundMessage: Callback for when notification arrives in foregroundonError: Callback for handling errorsshowNotificationsInForeground: Whether to display notifications in foreground (default: true)reminderIntervalDays: Days between permission reminders (default: 30)onTokenChanged: Callback for FCM token changes. If not provided and auth service is available, uses the default Firebase callable function implementation.authService: Pass auth service directly for race-free initialization. When provided, sets up auth connection immediately without using GetIt. This is the recommended approach for new code - see plan.auth-race.md.autoConnectAuth: Whether to automatically connect to auth service if available in GetIt (default: true). Set to false if you want to call connectToAuthService manually with custom configuration. Deprecated: UseauthServiceparameter instead.
Race-Free Initialization (Recommended)
For race-free initialization, pass authService directly and set
autoConnectAuth: false:
await notificationService.initialize(
authService: auth,
autoConnectAuth: false,
onNotificationTapped: (route, data) async { ... },
);
Legacy Initialization
await NotificationService().initialize(
onNotificationTapped: (route, data) async {
if (route != null) {
Navigator.of(context).pushNamed(route, arguments: data);
}
},
showNotificationsInForeground: true,
);
Implementation
Future<void> initialize({
NotificationActionCallback? onNotificationTapped,
NotificationButtonActionCallback? onNotificationAction,
ForegroundMessageCallback? onForegroundMessage,
NotificationErrorCallback? onError,
bool showNotificationsInForeground = true,
int reminderIntervalDays = 30,
Future<void> Function(String? newToken, String? oldToken)? onTokenChanged,
// NEW: Pass auth service directly for race-free initialization
AuthServiceInt? authService,
// DEPRECATED: Use authService parameter instead
@Deprecated('Use authService parameter instead for race-free initialization')
bool autoConnectAuth = true,
}) async {
// CRITICAL: Set auth reference FIRST, before any await statements.
//
// The auth callback (handleAuthenticated) may fire immediately after
// AuthService construction via a microtask. If we await anything before
// setting _authService, the callback could execute while _authService
// is still null.
//
// See: "Critical implementation constraints" section in plan.auth-race.md
if (authService != null) {
_authService = authService;
_isConnectedToAuthService = true;
logd('NotificationService: Initialized with auth service (race-free path)');
}
if (_initialized) {
logi('NotificationService already initialized');
return;
}
try {
_onNotificationTapped = onNotificationTapped;
_onNotificationAction = onNotificationAction;
_onForegroundMessage = onForegroundMessage;
_onError = onError;
_showNotificationsInForeground = showNotificationsInForeground;
_reminderIntervalDays = reminderIntervalDays;
// Initialize local notifications plugin
await _initializeLocalNotifications();
// Set up FCM message handlers
await _setupFCMHandlers();
// Check for initial message (app opened from terminated state via notification)
await _checkInitialMessage();
_initialized = true;
logi('NotificationService initialized successfully');
// Store token callback if provided (used by both paths)
if (onTokenChanged != null) {
_onTokenChanged = onTokenChanged;
}
// Skip auto-connect if authService was provided directly (race-free path)
if (authService != null) {
logd('NotificationService: Auth service provided directly, skipping auto-connect');
// Token callback already stored above, auth service already set at top
} else if (autoConnectAuth) {
// Legacy path: Auto-wire to auth service if available in GetIt
// Check if auth service is available before attempting to connect
bool authAvailable = false;
try {
authAvailable = GetIt.I.isRegistered<AuthServiceInt>();
} catch (e) {
// GetIt not initialized or other error
logd('GetIt check failed: $e');
}
if (authAvailable) {
await connectToAuthService(onTokenChanged: onTokenChanged);
} else {
// This is a configuration error - report it but don't crash
const errorMsg = 'autoConnectAuth is enabled but AuthServiceInt is not '
'registered in GetIt. FCM token management will not work automatically. '
'Either register AuthServiceInt before initializing NotificationService, '
'or set autoConnectAuth: false and call connectToAuthService() manually later.';
loge(errorMsg);
_onError?.call(errorMsg, null);
}
}
// Note: If neither authService nor autoConnectAuth, token callback is still
// stored above for manual connection later via connectToAuthService()
} catch (e, stackTrace) {
loge(e, 'Failed to initialize NotificationService', stackTrace);
_onError?.call('Failed to initialize NotificationService: $e', stackTrace);
rethrow;
}
}