NakedDialog

Headless dialog component with focus management and accessibility

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

When to use this

  • Confirmations: "Are you sure?" dialogs for destructive actions
  • 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

Basic implementation

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')),
            ],
          ),
        ],
      ),
    ),
  ),
);

API

showNakedDialog

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.

NakedDialog

class NakedDialog extends StatelessWidget {
  const NakedDialog({
    Key? key,
    required this.child,
    this.modal = true,
    this.semanticLabel,
  });
}
  • child: Your dialog content.
  • modal: When true, blocks background semantics and interaction.
  • semanticLabel: Optional screen reader label.

Notes

  • NakedDialog only sets semantics; visuals are entirely up to you.
  • For non‑modal popovers, set modal: false and manage dismissal yourself.