NakedMenubar
Horizontal menu navigation and nested actions
Use NakedMenubar for an application menu row. Supply menu anchors as children; the bar coordinates keyboard movement and opening between menus. Use NakedMenubarScope only when custom descendants need the shared menu context.
Build actions with NakedMenuItem, checkable actions with NakedMenuCheckboxItem, mutually exclusive actions with NakedMenuRadioGroup, and nested actions with NakedMenuSubmenu. Give each trigger an accessible label and retain Escape and focus restoration behavior.
Basic implementation
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';
import '../src/example_app.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const ExampleApp(child: Center(child: MenubarExample()));
}
}
class MenubarExample extends StatefulWidget {
const MenubarExample({super.key});
@override
State<MenubarExample> createState() => _MenubarExampleState();
}
class _MenubarExampleState extends State<MenubarExample> {
final _file = MenuController();
final _edit = MenuController();
final _view = MenuController();
String _status = 'Nothing selected';
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
NakedMenubar(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_menu(_file, 'File', const ['New', 'Open']),
_menu(_edit, 'Edit', const ['Copy', 'Paste']),
_menu(_view, 'View', const ['Zoom in', 'Zoom out']),
],
),
),
const SizedBox(height: 24),
Text(_status),
],
);
}
Widget _menu(MenuController controller, String label, List<String> items) {
return NakedMenu<String>(
controller: controller,
onSelected: (value) => setState(() => _status = value),
builder: (context, state, _) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: state.isOpen ? Colors.blue.shade50 : Colors.white,
child: Text(label),
);
},
overlayBuilder: (context, info) {
return Material(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (final item in items)
NakedMenu.Item(
value: item,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Text(item),
),
),
],
),
);
},
);
}
}Source and accessibility
See the package implementation for the complete API. Keep visible labels, focus indication and sufficient contrast in your presentation layer.