diff --git a/examples/api/lib/material/dropdown_menu/dropdown_menu.1.dart b/examples/api/lib/material/dropdown_menu/dropdown_menu.1.dart new file mode 100644 index 00000000000..dbb22177f3d --- /dev/null +++ b/examples/api/lib/material/dropdown_menu/dropdown_menu.1.dart @@ -0,0 +1,58 @@ +// 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'; + +/// Flutter code sample for [DropdownMenu]. + +const List list = ['One', 'Two', 'Three', 'Four']; + +void main() => runApp(const DropdownMenuApp()); + +class DropdownMenuApp extends StatelessWidget { + const DropdownMenuApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData(useMaterial3:true), + home: Scaffold( + appBar: AppBar(title: const Text('DropdownMenu Sample')), + body: const Center( + child: DropdownMenuExample(), + ), + ), + ); + } +} + +class DropdownMenuExample extends StatefulWidget { + const DropdownMenuExample({super.key}); + + @override + State createState() => _DropdownMenuExampleState(); +} + +class _DropdownMenuExampleState extends State { + String dropdownValue = list.first; + + @override + Widget build(BuildContext context) { + return DropdownMenu( + initialSelection: list.first, + onSelected: (String? value) { + // This is called when the user selects an item. + setState(() { + dropdownValue = value!; + }); + }, + dropdownMenuEntries: list.map>((String value) { + return DropdownMenuEntry( + value: value, + label: value + ); + }).toList(), + ); + } +} diff --git a/examples/api/lib/material/menu_anchor/menu_anchor.2.dart b/examples/api/lib/material/menu_anchor/menu_anchor.2.dart new file mode 100644 index 00000000000..0a411a13a08 --- /dev/null +++ b/examples/api/lib/material/menu_anchor/menu_anchor.2.dart @@ -0,0 +1,66 @@ +// 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'; + +/// Flutter code sample for [MenuAnchor]. + +// This is the type used by the menu below. +enum SampleItem { itemOne, itemTwo, itemThree } + +void main() => runApp(const MenuAnchorApp()); + +class MenuAnchorApp extends StatelessWidget { + const MenuAnchorApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData(useMaterial3: true), + home: const MenuAnchorExample(), + ); + } +} + +class MenuAnchorExample extends StatefulWidget { + const MenuAnchorExample({super.key}); + + @override + State createState() => _MenuAnchorExampleState(); +} + +class _MenuAnchorExampleState extends State { + SampleItem? selectedMenu; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('MenuAnchorButton')), + body: Center( + child: MenuAnchor( + builder: + (BuildContext context, MenuController controller, Widget? child) { + return IconButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + icon: const Icon(Icons.more_horiz), + tooltip: 'Show menu', + );}, + menuChildren: List.generate( + 3, + (int index) => MenuItemButton( + onPressed: () => setState(() => selectedMenu = SampleItem.values[index]), + child: Text('Item ${index + 1}'), + ), + ), + ), + ), + ); + } +} diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 3b74bc8110e..e948a87a027 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -788,6 +788,44 @@ class DropdownButtonHideUnderline extends InheritedWidget { /// shows the currently selected item as well as an arrow that opens a menu for /// selecting another item. /// +/// ## Updating to [DropdownMenu] +/// +/// There is a Material 3 version of this component, +/// [DropdownMenu] that is preferred for applications that are configured +/// for Material 3 (see [ThemeData.useMaterial3]). +/// The [DropdownMenu] widget's visuals +/// are a little bit different, see the Material 3 spec at +/// for +/// more details. +/// +/// The [DropdownMenu] widget's API is also slightly different. +/// To update from [DropdownButton] to [DropdownMenu], you will +/// need to make the following changes: +/// +/// 1. Instead of using [DropdownButton.items], which +/// takes a list of [DropdownMenuItem]s, use +/// [DropdownMenu.dropdownMenuEntries], which +/// takes a list of [DropdownMenuEntry]'s. +/// +/// 2. Instead of using [DropdownButton.onChanged], +/// use [DropdownMenu.onSelected], which is also +/// a callback that is called when the user selects an entry. +/// +/// 3. In [DropdownMenu] it is not required to track +/// the current selection in your app's state. +/// So, instead of tracking the current selection in +/// the [DropdownButton.value] property, you can set the +/// [DropdownMenu.initialSelection] property to the +/// item that should be selected before there is any user action. +/// +/// 4. You may also need to make changes to the styling of the +/// [DropdownMenu], see the properties in the [DropdownMenu] +/// constructor for more details. +/// +/// See the sample below for an example of migrating +/// from [DropdownButton] to [DropdownMenu]. +/// +/// ## Using [DropdownButton] /// {@youtube 560 315 https://www.youtube.com/watch?v=ZzQ_PWrFihg} /// /// One ancestor must be a [Material] widget and typically this is @@ -802,6 +840,7 @@ class DropdownButtonHideUnderline extends InheritedWidget { /// dropdown's value. It should also call [State.setState] to rebuild the /// dropdown with the new value. /// +/// /// {@tool dartpad} /// This sample shows a [DropdownButton] with a large arrow icon, /// purple text style, and bold purple underline, whose value is one of "One", @@ -819,9 +858,13 @@ class DropdownButtonHideUnderline extends InheritedWidget { /// [disabledHint] is null and [hint] is non-null, the [hint] widget will /// instead be displayed. /// -/// Requires one of its ancestors to be a [Material] widget. +/// {@tool dartpad} +/// This sample shows how you would rewrite the above [DropdownButton] +/// to use the [DropdownMenu]. +/// +/// ** See code in examples/api/lib/material/dropdown_menu/dropdown_menu.1.dart ** +/// {@end-tool} /// -/// {@youtube 560 315 https://www.youtube.com/watch?v=ZzQ_PWrFihg} /// /// See also: /// diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index c830a828755..a5db27efa40 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -1014,6 +1014,41 @@ typedef PopupMenuItemBuilder = List> Function(BuildContext /// If both are null, then a standard overflow icon is created (depending on the /// platform). /// +/// /// ## Updating to [MenuAnchor] +/// +/// There is a Material 3 component, +/// [MenuAnchor] that is preferred for applications that are configured +/// for Material 3 (see [ThemeData.useMaterial3]). +/// The [MenuAnchor] widget's visuals +/// are a little bit different, see the Material 3 spec at +/// for +/// more details. +/// +/// The [MenuAnchor] widget's API is also slightly different. +/// [MenuAnchor]'s were built to be lower level interface for +/// creating menus that are displayed from an anchor. +/// +/// There are a few steps you would take to migrate from +/// [PopupMenuButton] to [MenuAnchor]: +/// +/// 1. Instead of using the [PopupMenuButton.itemBuilder] to build +/// a list of [PopupMenuEntry]s, you would use the [MenuAnchor.menuChildren] +/// which takes a list of [Widget]s. Usually, you would use a list of +/// [MenuItemButton]s as shown in the example below. +/// +/// 2. Instead of using the [PopupMenuButton.onSelected] callback, you would +/// set individual callbacks for each of the [MenuItemButton]s using the +/// [MenuItemButton.onPressed] property. +/// +/// 3. To anchor the [MenuAnchor] to a widget, you would use the [MenuAnchor.builder] +/// to return the widget of choice - usually a [TextButton] or an [IconButton]. +/// +/// 4. You may want to style the [MenuItemButton]s, see the [MenuItemButton] +/// documentation for details. +/// +/// Use the sample below for an example of migrating from [PopupMenuButton] to +/// [MenuAnchor]. +/// /// {@tool dartpad} /// This example shows a menu with three items, selecting between an enum's /// values and setting a `selectedMenu` field based on the selection. @@ -1022,6 +1057,12 @@ typedef PopupMenuItemBuilder = List> Function(BuildContext /// {@end-tool} /// /// {@tool dartpad} +/// This example shows how to migrate the above to a [MenuAnchor]. +/// +/// ** See code in examples/api/lib/material/menu_anchor/menu_anchor.2.dart ** +/// {@end-tool} +/// +/// {@tool dartpad} /// This sample shows the creation of a popup menu, as described in: /// https://m3.material.io/components/menus/overview ///