showNotification method

Future<int> showNotification(
  1. NotificationPayload payload
)

Displays a local notification.

Supports rich notifications including:

  • Images (downloaded and cached automatically)
  • Action buttons (up to 3 per notification)
  • Custom sounds
  • Badge updates

Parameters:

  • payload: The notification content and configuration

Returns the notification ID that was used.

Implementation

Future<int> showNotification(NotificationPayload payload) async {
  if (!_initialized) {
    throw StateError('NotificationService not initialized. Call initialize() first.');
  }

  try {
    final id = payload.id ?? DateTime.now().millisecondsSinceEpoch.remainder(100000);

    // Download image if provided
    String? imagePath;
    if (payload.imageUrl != null && payload.imageUrl!.isNotEmpty) {
      imagePath = await NotificationImageLoader.downloadImage(payload.imageUrl!);
      if (imagePath != null) {
        logi('Notification image downloaded: $imagePath');
      } else {
        logi('Failed to download notification image, displaying without image');
      }
    }

    // Build action buttons for Android
    List<AndroidNotificationAction>? androidActions;
    if (payload.actions.isNotEmpty && !kIsWeb && Platform.isAndroid) {
      androidActions = payload.actions.map((action) {
        return AndroidNotificationAction(
          action.id,
          action.label,
          icon: action.icon != null ? DrawableResourceAndroidBitmap(action.icon!) : null,
          showsUserInterface: action.launchesApp,
          contextual: false,
        );
      }).toList();
    }

    // Android-specific settings
    // Use channel from payload, or default to standard channel
    final channelId = payload.channelId ?? NotificationChannelManager.channelDefault;

    final androidDetails = AndroidNotificationDetails(
      channelId,
      _getChannelName(channelId),
      channelDescription: _getChannelDescription(channelId),
      importance: Importance.high,
      playSound: true,
      enableVibration: true,
      enableLights: true,
      styleInformation: imagePath != null
          ? BigPictureStyleInformation(
              FilePathAndroidBitmap(imagePath),
              contentTitle: payload.title,
              summaryText: payload.body,
              hideExpandedLargeIcon: false,
            )
          : null,
      actions: androidActions,
      category: payload.category != null
          ? AndroidNotificationCategory.values.firstWhere(
              (c) => c.toString().split('.').last == payload.category,
              orElse: () => AndroidNotificationCategory.message,
            )
          : null,
    );

    // Build iOS/macOS attachment for image
    List<DarwinNotificationAttachment>? iosAttachments;
    if (imagePath != null && !kIsWeb && (Platform.isIOS || Platform.isMacOS)) {
      iosAttachments = [
        DarwinNotificationAttachment(imagePath),
      ];
    }

    // iOS-specific settings
    final iosDetails = DarwinNotificationDetails(
      presentAlert: true,
      presentBadge: true,
      presentSound: true,
      badgeNumber: payload.badge,
      categoryIdentifier: payload.category,
      attachments: iosAttachments,
    );

    final notificationDetails = NotificationDetails(
      android: androidDetails,
      iOS: iosDetails,
      macOS: iosDetails,
    );

    // Serialize payload data for tap handling
    final payloadJson = _serializePayload(payload);

    await _localNotifications!.show(
      id,
      payload.title ?? 'Notification',
      payload.body,
      notificationDetails,
      payload: payloadJson,
    );

    logi('Displayed notification: $id with ${payload.actions.length} actions');
    return id;
  } catch (e, stackTrace) {
    loge(e, 'Error showing notification', stackTrace);
    _onError?.call('Error showing notification: $e', stackTrace);
    rethrow;
  }
}