// 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 '../../gallery_localizations.dart'; import 'material_demo_types.dart'; enum SimpleValue { one, two, three } enum CheckedValue { one, two, three, four } class MenuDemo extends StatefulWidget { const MenuDemo({super.key, required this.type}); final MenuDemoType type; @override State createState() => _MenuDemoState(); } class _MenuDemoState extends State { void showInSnackBar(String value) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value))); } @override Widget build(BuildContext context) { Widget demo; switch (widget.type) { case MenuDemoType.contextMenu: demo = _ContextMenuDemo(showInSnackBar: showInSnackBar); case MenuDemoType.sectionedMenu: demo = _SectionedMenuDemo(showInSnackBar: showInSnackBar); case MenuDemoType.simpleMenu: demo = _SimpleMenuDemo(showInSnackBar: showInSnackBar); case MenuDemoType.checklistMenu: demo = _ChecklistMenuDemo(showInSnackBar: showInSnackBar); } return Scaffold( appBar: AppBar( title: Text(GalleryLocalizations.of(context)!.demoMenuTitle), automaticallyImplyLeading: false, ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Center(child: demo), ), ); } } // BEGIN menuDemoContext // Pressing the PopupMenuButton on the right of this item shows // a simple menu with one disabled item. Typically the contents // of this "contextual menu" would reflect the app's state. class _ContextMenuDemo extends StatelessWidget { const _ContextMenuDemo({required this.showInSnackBar}); final void Function(String value) showInSnackBar; @override Widget build(BuildContext context) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return ListTile( title: Text(localizations.demoMenuAnItemWithAContextMenuButton), trailing: PopupMenuButton( padding: EdgeInsets.zero, onSelected: (String value) => showInSnackBar(localizations.demoMenuSelected(value)), itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: localizations.demoMenuContextMenuItemOne, child: Text(localizations.demoMenuContextMenuItemOne), ), PopupMenuItem( enabled: false, child: Text(localizations.demoMenuADisabledMenuItem), ), PopupMenuItem( value: localizations.demoMenuContextMenuItemThree, child: Text(localizations.demoMenuContextMenuItemThree), ), ], ), ); } } // END // BEGIN menuDemoSectioned // Pressing the PopupMenuButton on the right of this item shows // a menu whose items have text labels and icons and a divider // That separates the first three items from the last one. class _SectionedMenuDemo extends StatelessWidget { const _SectionedMenuDemo({required this.showInSnackBar}); final void Function(String value) showInSnackBar; @override Widget build(BuildContext context) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return ListTile( title: Text(localizations.demoMenuAnItemWithASectionedMenu), trailing: PopupMenuButton( padding: EdgeInsets.zero, onSelected: (String value) => showInSnackBar(localizations.demoMenuSelected(value)), itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: localizations.demoMenuPreview, child: ListTile( leading: const Icon(Icons.visibility), title: Text(localizations.demoMenuPreview), ), ), PopupMenuItem( value: localizations.demoMenuShare, child: ListTile( leading: const Icon(Icons.person_add), title: Text(localizations.demoMenuShare), ), ), PopupMenuItem( value: localizations.demoMenuGetLink, child: ListTile( leading: const Icon(Icons.link), title: Text(localizations.demoMenuGetLink), ), ), const PopupMenuDivider(), PopupMenuItem( value: localizations.demoMenuRemove, child: ListTile( leading: const Icon(Icons.delete), title: Text(localizations.demoMenuRemove), ), ), ], ), ); } } // END // BEGIN menuDemoSimple // This entire list item is a PopupMenuButton. Tapping anywhere shows // a menu whose current value is highlighted and aligned over the // list item's center line. class _SimpleMenuDemo extends StatefulWidget { const _SimpleMenuDemo({required this.showInSnackBar}); final void Function(String value) showInSnackBar; @override _SimpleMenuDemoState createState() => _SimpleMenuDemoState(); } class _SimpleMenuDemoState extends State<_SimpleMenuDemo> { late SimpleValue _simpleValue; void showAndSetMenuSelection(BuildContext context, SimpleValue value) { setState(() { _simpleValue = value; }); widget.showInSnackBar( GalleryLocalizations.of(context)!.demoMenuSelected(simpleValueToString(context, value)), ); } String simpleValueToString(BuildContext context, SimpleValue value) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return { SimpleValue.one: localizations.demoMenuItemValueOne, SimpleValue.two: localizations.demoMenuItemValueTwo, SimpleValue.three: localizations.demoMenuItemValueThree, }[value]!; } @override void initState() { super.initState(); _simpleValue = SimpleValue.two; } @override Widget build(BuildContext context) { return PopupMenuButton( padding: EdgeInsets.zero, initialValue: _simpleValue, onSelected: (SimpleValue value) => showAndSetMenuSelection(context, value), itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: SimpleValue.one, child: Text(simpleValueToString(context, SimpleValue.one)), ), PopupMenuItem( value: SimpleValue.two, child: Text(simpleValueToString(context, SimpleValue.two)), ), PopupMenuItem( value: SimpleValue.three, child: Text(simpleValueToString(context, SimpleValue.three)), ), ], child: ListTile( title: Text(GalleryLocalizations.of(context)!.demoMenuAnItemWithASimpleMenu), subtitle: Text(simpleValueToString(context, _simpleValue)), ), ); } } // END // BEGIN menuDemoChecklist // Pressing the PopupMenuButton on the right of this item shows a menu // whose items have checked icons that reflect this app's state. class _ChecklistMenuDemo extends StatefulWidget { const _ChecklistMenuDemo({required this.showInSnackBar}); final void Function(String value) showInSnackBar; @override _ChecklistMenuDemoState createState() => _ChecklistMenuDemoState(); } class _RestorableCheckedValues extends RestorableProperty> { Set _checked = {}; void check(CheckedValue value) { _checked.add(value); notifyListeners(); } void uncheck(CheckedValue value) { _checked.remove(value); notifyListeners(); } bool isChecked(CheckedValue value) => _checked.contains(value); Iterable checkedValuesToString(BuildContext context) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return _checked.map((CheckedValue value) { return { CheckedValue.one: localizations.demoMenuOne, CheckedValue.two: localizations.demoMenuTwo, CheckedValue.three: localizations.demoMenuThree, CheckedValue.four: localizations.demoMenuFour, }[value]!; }); } @override Set createDefaultValue() => _checked; @override Set initWithValue(Set a) { _checked = a; return _checked; } @override Object toPrimitives() => _checked.map((CheckedValue value) => value.index).toList(); @override Set fromPrimitives(Object? data) { final checkedValues = data! as List; return Set.from( checkedValues.map((dynamic id) { return CheckedValue.values[id as int]; }), ); } } class _ChecklistMenuDemoState extends State<_ChecklistMenuDemo> with RestorationMixin { final _RestorableCheckedValues _checkedValues = _RestorableCheckedValues() ..check(CheckedValue.three); @override String get restorationId => 'checklist_menu_demo'; @override void restoreState(RestorationBucket? oldBucket, bool initialRestore) { registerForRestoration(_checkedValues, 'checked_values'); } void showCheckedMenuSelections(BuildContext context, CheckedValue value) { if (_checkedValues.isChecked(value)) { setState(() { _checkedValues.uncheck(value); }); } else { setState(() { _checkedValues.check(value); }); } widget.showInSnackBar( GalleryLocalizations.of( context, )!.demoMenuChecked(_checkedValues.checkedValuesToString(context)), ); } String checkedValueToString(BuildContext context, CheckedValue value) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; return { CheckedValue.one: localizations.demoMenuOne, CheckedValue.two: localizations.demoMenuTwo, CheckedValue.three: localizations.demoMenuThree, CheckedValue.four: localizations.demoMenuFour, }[value]!; } @override void dispose() { _checkedValues.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ListTile( title: Text(GalleryLocalizations.of(context)!.demoMenuAnItemWithAChecklistMenu), trailing: PopupMenuButton( padding: EdgeInsets.zero, onSelected: (CheckedValue value) => showCheckedMenuSelections(context, value), itemBuilder: (BuildContext context) => >[ CheckedPopupMenuItem( value: CheckedValue.one, checked: _checkedValues.isChecked(CheckedValue.one), child: Text(checkedValueToString(context, CheckedValue.one)), ), CheckedPopupMenuItem( value: CheckedValue.two, enabled: false, checked: _checkedValues.isChecked(CheckedValue.two), child: Text(checkedValueToString(context, CheckedValue.two)), ), CheckedPopupMenuItem( value: CheckedValue.three, checked: _checkedValues.isChecked(CheckedValue.three), child: Text(checkedValueToString(context, CheckedValue.three)), ), CheckedPopupMenuItem( value: CheckedValue.four, checked: _checkedValues.isChecked(CheckedValue.four), child: Text(checkedValueToString(context, CheckedValue.four)), ), ], ), ); } } // END