dismissById static method

Future<bool> dismissById(
  1. String id, {
  2. VoidCallback? onDismissed,
})

Dismisses a specific modal by its ID (PRIMARY DISMISSAL METHOD)

This is the recommended way to dismiss modals. It searches through all modals (dialogs, bottom sheets, and queued snackbars) for a matching ID and dismisses only that specific modal, leaving all other modals untouched.

The method uses the appropriate type-specific dismissal logic internally, ensuring proper cleanup and preservation of other active modals.

Parameters:

  • id: The ID of the modal to dismiss (as specified in Modal.show() or Modal.showSnackbar())
  • onDismissed: Optional callback executed after the modal is dismissed

Returns true if a modal with the given ID was found and dismissed, false if no modal with that ID was found.

Example:

// Show a bottom sheet with an ID
Modal.show(
  id: 'settings_sheet',
  type: ModalType.bottomSheet,
  child: SettingsSheet(),
);

// Later, dismiss it by ID
await Modal.dismissById('settings_sheet');

// Works with snackbars too
Modal.showSnackbar(text: 'Message', id: 'my_snack');
await Modal.dismissById('my_snack');

Implementation

static Future<bool> dismissById(String id,
    {VoidCallback? onDismissed}) async {
  if (_showDebugPrints) {
    debugPrint('Modal.dismissById called: id=$id');
  }

  // Check if this ID matches the active dialog
  if (Modal.isDialogActive && _dialogController.state != null) {
    final dialog = _dialogController.state!;
    if (dialog.id == id || dialog.uniqueId == id) {
      if (_showDebugPrints) {
        debugPrint(
            'Modal.dismissById: found dialog with ID=$id. Dismissing...');
      }
      await dismissDialog(onDismissed: onDismissed);
      return true;
    }
  }

  // Check if this ID matches the active sheet
  if (Modal.isSheetActive && _sheetController.state != null) {
    final sheet = _sheetController.state!;
    if (sheet.id == id || sheet.uniqueId == id) {
      if (_showDebugPrints) {
        debugPrint(
            'Modal.dismissById: found bottom sheet with ID=$id. Dismissing...');
      }
      await dismissBottomSheet(onDismissed: onDismissed);
      return true;
    }
  }

  // Check if this ID matches the active side sheet
  if (Modal.isSideSheetActive && _sideSheetController.state != null) {
    final sheet = _sideSheetController.state!;
    if (sheet.id == id || sheet.uniqueId == id) {
      if (_showDebugPrints) {
        debugPrint(
            'Modal.dismissById: found side sheet with ID=$id. Dismissing...');
      }
      await dismissSideSheet(onDismissed: onDismissed);
      return true;
    }
  }

  // Search for and remove from snackbar queue
  final currentQueueMap = _snackbarQueueNotifier.state;
  bool found = false;

  for (final position in currentQueueMap.keys.toList()) {
    if (found) break;

    final queueAtPosition = currentQueueMap[position]!;
    // Search using both id and uniqueId for flexibility
    final matchIndex = queueAtPosition.indexWhere(
      (content) => content.id == id || content.uniqueId == id,
    );

    if (matchIndex >= 0) {
      found = true;
      final snackbar = queueAtPosition[matchIndex];
      if (_showDebugPrints) {
        debugPrint(
            'Modal.dismissById: found snackbar with ID=$id (uniqueId=${snackbar.uniqueId}) at position=$position. Removing...');
      }

      // Check if this is the currently active snackbar
      final isActiveSnackbar =
          _snackbarController.state?.uniqueId == snackbar.uniqueId;

      // If this was the active snackbar, handle transition
      if (isActiveSnackbar) {
        // Try to use the snackbar's internal controller for dismiss animation
        final controller = _getSnackbarController(snackbar.uniqueId);

        if (controller != null && controller.isAttached) {
          // Use imperative dismiss via controller
          if (_showDebugPrints) {
            debugPrint(
                'Modal.dismissById: Using controller to dismiss snackbar ${snackbar.uniqueId}');
          }

          final completer = Completer<void>();
          controller.playDismissAnimation(
            direction: '',
            onComplete: () {
              // Remove from queue after animation completes
              _removeSnackbarAfterDismiss(
                  position, snackbar.uniqueId, onDismissed);
              if (!completer.isCompleted) {
                completer.complete();
              }
            },
          );

          // Wait for animation to complete
          await completer.future;
        } else {
          // Fallback: Use the old reactive approach
          if (_showDebugPrints) {
            debugPrint(
                'Modal.dismissById: Controller not found, using fallback for snackbar ${snackbar.uniqueId}');
          }
          _setSnackbarDismissing(snackbar.uniqueId, true);

          await Future.delayed(0.3.sec, () {
            _removeSnackbarAfterDismiss(
                position, snackbar.uniqueId, onDismissed);
          });
        }
      } else {
        // Not the active snackbar, remove immediately
        final updatedQueue = List<_ModalContent>.from(queueAtPosition);
        updatedQueue.removeAt(matchIndex);

        final updatedQueueMap =
            Map<Alignment, List<_ModalContent>>.from(currentQueueMap);
        if (updatedQueue.isEmpty) {
          updatedQueueMap.remove(position);
        } else {
          updatedQueueMap[position] = updatedQueue;
        }

        _snackbarQueueNotifier.state = updatedQueueMap;

        // Unregister from modal registry
        final updatedRegistry =
            Map<String, ModalType>.from(_modalRegistry.state);
        updatedRegistry.remove(snackbar.uniqueId);
        _modalRegistry.state = updatedRegistry;

        onDismissed?.call();
        snackbar.onDismissed?.call();
      }

      if (_showDebugPrints) {
        debugPrint('Modal.dismissById: successfully dismissed ID=$id');
      }
      return true;
    }
  }

  if (_showDebugPrints) {
    debugPrint(
        'Modal.dismissById: ID=$id not found in any active modal or snackbar queue');
  }
  return false;
}