| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/services.dart'; |
| |
| /// Flutter code sample for [MenuAnchor]. |
| |
| void main() => runApp(const MenuApp()); |
| |
| /// An enhanced enum to define the available menus and their shortcuts. |
| /// |
| /// Using an enum for menu definition is not required, but this illustrates how |
| /// they could be used for simple menu systems. |
| enum MenuEntry { |
| about('About'), |
| showMessage('Show Message', SingleActivator(LogicalKeyboardKey.keyS, control: true)), |
| hideMessage('Hide Message', SingleActivator(LogicalKeyboardKey.keyS, control: true)), |
| colorMenu('Color Menu'), |
| colorRed('Red Background', SingleActivator(LogicalKeyboardKey.keyR, control: true)), |
| colorGreen('Green Background', SingleActivator(LogicalKeyboardKey.keyG, control: true)), |
| colorBlue('Blue Background', SingleActivator(LogicalKeyboardKey.keyB, control: true)); |
| |
| const MenuEntry(this.label, [this.shortcut]); |
| final String label; |
| final MenuSerializableShortcut? shortcut; |
| } |
| |
| class MyCascadingMenu extends StatefulWidget { |
| const MyCascadingMenu({super.key, required this.message}); |
| |
| final String message; |
| |
| @override |
| State<MyCascadingMenu> createState() => _MyCascadingMenuState(); |
| } |
| |
| class _MyCascadingMenuState extends State<MyCascadingMenu> { |
| MenuEntry? _lastSelection; |
| final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button'); |
| ShortcutRegistryEntry? _shortcutsEntry; |
| |
| Color get backgroundColor => _backgroundColor; |
| Color _backgroundColor = Colors.red; |
| set backgroundColor(Color value) { |
| if (_backgroundColor != value) { |
| setState(() { |
| _backgroundColor = value; |
| }); |
| } |
| } |
| |
| bool get showingMessage => _showingMessage; |
| bool _showingMessage = false; |
| set showingMessage(bool value) { |
| if (_showingMessage != value) { |
| setState(() { |
| _showingMessage = value; |
| }); |
| } |
| } |
| |
| @override |
| void didChangeDependencies() { |
| super.didChangeDependencies(); |
| // Dispose of any previously registered shortcuts, since they are about to |
| // be replaced. |
| _shortcutsEntry?.dispose(); |
| // Collect the shortcuts from the different menu selections so that they can |
| // be registered to apply to the entire app. Menus don't register their |
| // shortcuts, they only display the shortcut hint text. |
| final Map<ShortcutActivator, Intent> shortcuts = <ShortcutActivator, Intent>{ |
| for (final MenuEntry item in MenuEntry.values) |
| if (item.shortcut != null) item.shortcut!: VoidCallbackIntent(() => _activate(item)), |
| }; |
| // Register the shortcuts with the ShortcutRegistry so that they are |
| // available to the entire application. |
| _shortcutsEntry = ShortcutRegistry.of(context).addAll(shortcuts); |
| } |
| |
| @override |
| void dispose() { |
| _shortcutsEntry?.dispose(); |
| _buttonFocusNode.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Column( |
| crossAxisAlignment: CrossAxisAlignment.start, |
| children: <Widget>[ |
| MenuAnchor( |
| childFocusNode: _buttonFocusNode, |
| menuChildren: <Widget>[ |
| MenuItemButton( |
| child: Text(MenuEntry.about.label), |
| onPressed: () => _activate(MenuEntry.about), |
| ), |
| if (_showingMessage) |
| MenuItemButton( |
| onPressed: () => _activate(MenuEntry.hideMessage), |
| shortcut: MenuEntry.hideMessage.shortcut, |
| child: Text(MenuEntry.hideMessage.label), |
| ), |
| if (!_showingMessage) |
| MenuItemButton( |
| onPressed: () => _activate(MenuEntry.showMessage), |
| shortcut: MenuEntry.showMessage.shortcut, |
| child: Text(MenuEntry.showMessage.label), |
| ), |
| SubmenuButton( |
| menuChildren: <Widget>[ |
| MenuItemButton( |
| onPressed: () => _activate(MenuEntry.colorRed), |
| shortcut: MenuEntry.colorRed.shortcut, |
| child: Text(MenuEntry.colorRed.label), |
| ), |
| MenuItemButton( |
| onPressed: () => _activate(MenuEntry.colorGreen), |
| shortcut: MenuEntry.colorGreen.shortcut, |
| child: Text(MenuEntry.colorGreen.label), |
| ), |
| MenuItemButton( |
| onPressed: () => _activate(MenuEntry.colorBlue), |
| shortcut: MenuEntry.colorBlue.shortcut, |
| child: Text(MenuEntry.colorBlue.label), |
| ), |
| ], |
| child: const Text('Background Color'), |
| ), |
| ], |
| builder: (BuildContext context, MenuController controller, Widget? child) { |
| return TextButton( |
| focusNode: _buttonFocusNode, |
| onPressed: () { |
| if (controller.isOpen) { |
| controller.close(); |
| } else { |
| controller.open(); |
| } |
| }, |
| child: const Text('OPEN MENU'), |
| ); |
| }, |
| ), |
| Expanded( |
| child: Container( |
| alignment: Alignment.center, |
| color: backgroundColor, |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| Padding( |
| padding: const EdgeInsets.all(12.0), |
| child: Text( |
| showingMessage ? widget.message : '', |
| style: Theme.of(context).textTheme.headlineSmall, |
| ), |
| ), |
| Text(_lastSelection != null ? 'Last Selected: ${_lastSelection!.label}' : ''), |
| ], |
| ), |
| ), |
| ), |
| ], |
| ); |
| } |
| |
| void _activate(MenuEntry selection) { |
| setState(() { |
| _lastSelection = selection; |
| }); |
| |
| switch (selection) { |
| case MenuEntry.about: |
| showAboutDialog( |
| context: context, |
| applicationName: 'MenuBar Sample', |
| applicationVersion: '1.0.0', |
| ); |
| case MenuEntry.hideMessage: |
| case MenuEntry.showMessage: |
| showingMessage = !showingMessage; |
| case MenuEntry.colorMenu: |
| break; |
| case MenuEntry.colorRed: |
| backgroundColor = Colors.red; |
| case MenuEntry.colorGreen: |
| backgroundColor = Colors.green; |
| case MenuEntry.colorBlue: |
| backgroundColor = Colors.blue; |
| } |
| } |
| } |
| |
| class MenuApp extends StatelessWidget { |
| const MenuApp({super.key}); |
| |
| static const String kMessage = '"Talk less. Smile more." - A. Burr'; |
| |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| theme: ThemeData(useMaterial3: true), |
| home: const Scaffold(body: SafeArea(child: MyCascadingMenu(message: kMessage))), |
| ); |
| } |
| } |