---
title: NakedDialog
description: Headless dialog component with focus management and accessibility
keywords: [flutter, dialog, modal, headless, overlay]
---

Headless dialog component. Handles focus management, backdrop interaction, and accessibility. Use the builder pattern for custom styling.

## When to use this

- **Ordinary confirmations**: Reversible or low-risk choices
- **Alert confirmations**: Urgent or destructive choices that require immediate attention
- **Forms**: Modal forms for user input (login, contact, etc.)
- **Content display**: Show detailed information or images
- **Custom modals**: When standard dialog styles don't match your design

<Info>
  See the complete example in [`packages/example/lib/api/naked_dialog.0.dart`](https://github.com/btwld/naked_ui/blob/main/packages/example/lib/api/naked_dialog.0.dart).
</Info>

## Basic implementation

```dart
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';

final result = await showNakedDialog<String>(
  context: context,
  barrierColor: Colors.black54,
  builder: (context) => NakedDialog(
    modal: true,
    semanticLabel: 'Confirm Action',
    child: Container(
      margin: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Padding(
            padding: EdgeInsets.all(16),
            child: const Text('Are you sure?'),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              TextButton(onPressed: () => Navigator.pop(context, 'cancel'), child: const Text('Cancel')),
              const SizedBox(width: 8),
              FilledButton(onPressed: () => Navigator.pop(context, 'ok'), child: const Text('OK')),
            ],
          ),
        ],
      ),
    ),
  ),
);
```

For an urgent or destructive confirmation, use the alert helper. Its builder
returns visual content only; the helper supplies the single alert-dialog
semantics wrapper and always moves focus into the route.

```dart
final result = await showNakedAlertDialog<bool>(
  context: context,
  barrierColor: Colors.black54,
  semanticLabel: localizedDeleteProjectTitle,
  initialFocusNode: cancelFocusNode,
  builder: (context) => YourStyledAlertContents(
    cancelFocusNode: cancelFocusNode,
    onCancel: () => Navigator.pop(context, false),
    onConfirm: () => Navigator.pop(context, true),
  ),
);
```

## API

### `showNakedDialog`

```dart
Future<T?> showNakedDialog<T>({
  required BuildContext context,
  required WidgetBuilder builder,
  required Color barrierColor,
  bool barrierDismissible = true,
  String? barrierLabel,
  bool useRootNavigator = true,
  RouteSettings? routeSettings,
  Offset? anchorPoint,
  Duration transitionDuration = const Duration(milliseconds: 400),
  RouteTransitionsBuilder? transitionBuilder,
  bool requestFocus = true,
  TraversalEdgeBehavior? traversalEdgeBehavior,
})
```

- barrierColor: Required background overlay color.
- barrierLabel: Announced by screen readers when the barrier appears; pass `MaterialLocalizations.of(context).modalBarrierDismissLabel` for localization.
- barrierDismissible: Tap outside to dismiss (default true).
- transitionDuration / transitionBuilder: Customize entry/exit animations.
- anchorPoint: Supply a pointer offset to anchor desktop-style dialogs near the invocation point.
- requestFocus: Whether the dialog route should grab focus when it enters.
- traversalEdgeBehavior: Defaults to `TraversalEdgeBehavior.closedLoop` to keep focus inside the dialog; override for custom traversal.
- useRootNavigator: Present on the root navigator instead of the nested one.

### `showNakedAlertDialog`

```dart
Future<T?> showNakedAlertDialog<T>({
  required BuildContext context,
  required WidgetBuilder builder,
  required Color barrierColor,
  required String semanticLabel,
  String? barrierLabel,
  bool barrierDismissible = false,
  bool useRootNavigator = true,
  RouteSettings? routeSettings,
  Offset? anchorPoint,
  Duration transitionDuration = const Duration(milliseconds: 400),
  RouteTransitionsBuilder? transitionBuilder,
  FocusNode? initialFocusNode,
})
```

- semanticLabel: Required, non-empty, caller-localized alert-dialog name.
- initialFocusNode: Optional caller-owned safe target. When it is absent or unusable, descendant autofocus is honored before falling back to the first traversable descendant.
- barrierDismissible: Outside taps are inert by default. When enabled, `barrierLabel` must be non-empty and localized.
- Escape and platform Back: Always cancel safely with a null result.
- Focus always enters the alert, loops inside, and returns to a surviving invoker when the route closes.

### `NakedDialog`

```dart
class NakedDialog extends StatelessWidget {
  const NakedDialog({
    Key? key,
    required this.child,
    this.modal = true,
    this.semanticLabel,
    this.excludeSemantics = false,
    this.semanticsRole = SemanticsRole.dialog,
  });
}
```

- child: Your dialog content.
- modal: When true, blocks background semantics and interaction.
- semanticLabel: Optional screen reader label.
- excludeSemantics: Hides the dialog subtree from accessibility services.
- semanticsRole: Constrained to `SemanticsRole.dialog` or `SemanticsRole.alertDialog`.

## Notes

- `NakedDialog` only sets semantics; visuals are entirely up to you.
- Do not wrap `showNakedAlertDialog` content in another `NakedDialog`.
- Keep caller-owned focus nodes in `State` and dispose them there.
- For non‑modal popovers, set `modal: false` and manage dismissal yourself.
