Skip to content

Commit 6848110

Browse files
authored
Update menu API docs to help developers migrate to m3 (#128351)
Fixes: flutter/flutter#127215
1 parent 96afa50 commit 6848110

File tree

4 files changed

+210
-2
lines changed

4 files changed

+210
-2
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
/// Flutter code sample for [DropdownMenu].
8+
9+
const List<String> list = <String>['One', 'Two', 'Three', 'Four'];
10+
11+
void main() => runApp(const DropdownMenuApp());
12+
13+
class DropdownMenuApp extends StatelessWidget {
14+
const DropdownMenuApp({super.key});
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return MaterialApp(
19+
theme: ThemeData(useMaterial3:true),
20+
home: Scaffold(
21+
appBar: AppBar(title: const Text('DropdownMenu Sample')),
22+
body: const Center(
23+
child: DropdownMenuExample(),
24+
),
25+
),
26+
);
27+
}
28+
}
29+
30+
class DropdownMenuExample extends StatefulWidget {
31+
const DropdownMenuExample({super.key});
32+
33+
@override
34+
State<DropdownMenuExample> createState() => _DropdownMenuExampleState();
35+
}
36+
37+
class _DropdownMenuExampleState extends State<DropdownMenuExample> {
38+
String dropdownValue = list.first;
39+
40+
@override
41+
Widget build(BuildContext context) {
42+
return DropdownMenu<String>(
43+
initialSelection: list.first,
44+
onSelected: (String? value) {
45+
// This is called when the user selects an item.
46+
setState(() {
47+
dropdownValue = value!;
48+
});
49+
},
50+
dropdownMenuEntries: list.map<DropdownMenuEntry<String>>((String value) {
51+
return DropdownMenuEntry<String>(
52+
value: value,
53+
label: value
54+
);
55+
}).toList(),
56+
);
57+
}
58+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
/// Flutter code sample for [MenuAnchor].
8+
9+
// This is the type used by the menu below.
10+
enum SampleItem { itemOne, itemTwo, itemThree }
11+
12+
void main() => runApp(const MenuAnchorApp());
13+
14+
class MenuAnchorApp extends StatelessWidget {
15+
const MenuAnchorApp({super.key});
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return MaterialApp(
20+
theme: ThemeData(useMaterial3: true),
21+
home: const MenuAnchorExample(),
22+
);
23+
}
24+
}
25+
26+
class MenuAnchorExample extends StatefulWidget {
27+
const MenuAnchorExample({super.key});
28+
29+
@override
30+
State<MenuAnchorExample> createState() => _MenuAnchorExampleState();
31+
}
32+
33+
class _MenuAnchorExampleState extends State<MenuAnchorExample> {
34+
SampleItem? selectedMenu;
35+
36+
@override
37+
Widget build(BuildContext context) {
38+
return Scaffold(
39+
appBar: AppBar(title: const Text('MenuAnchorButton')),
40+
body: Center(
41+
child: MenuAnchor(
42+
builder:
43+
(BuildContext context, MenuController controller, Widget? child) {
44+
return IconButton(
45+
onPressed: () {
46+
if (controller.isOpen) {
47+
controller.close();
48+
} else {
49+
controller.open();
50+
}
51+
},
52+
icon: const Icon(Icons.more_horiz),
53+
tooltip: 'Show menu',
54+
);},
55+
menuChildren: List<MenuItemButton>.generate(
56+
3,
57+
(int index) => MenuItemButton(
58+
onPressed: () => setState(() => selectedMenu = SampleItem.values[index]),
59+
child: Text('Item ${index + 1}'),
60+
),
61+
),
62+
),
63+
),
64+
);
65+
}
66+
}

packages/flutter/lib/src/material/dropdown.dart

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,44 @@ class DropdownButtonHideUnderline extends InheritedWidget {
788788
/// shows the currently selected item as well as an arrow that opens a menu for
789789
/// selecting another item.
790790
///
791+
/// ## Updating to [DropdownMenu]
792+
///
793+
/// There is a Material 3 version of this component,
794+
/// [DropdownMenu] that is preferred for applications that are configured
795+
/// for Material 3 (see [ThemeData.useMaterial3]).
796+
/// The [DropdownMenu] widget's visuals
797+
/// are a little bit different, see the Material 3 spec at
798+
/// <https://m3.material.io/components/menus/guidelines> for
799+
/// more details.
800+
///
801+
/// The [DropdownMenu] widget's API is also slightly different.
802+
/// To update from [DropdownButton] to [DropdownMenu], you will
803+
/// need to make the following changes:
804+
///
805+
/// 1. Instead of using [DropdownButton.items], which
806+
/// takes a list of [DropdownMenuItem]s, use
807+
/// [DropdownMenu.dropdownMenuEntries], which
808+
/// takes a list of [DropdownMenuEntry]'s.
809+
///
810+
/// 2. Instead of using [DropdownButton.onChanged],
811+
/// use [DropdownMenu.onSelected], which is also
812+
/// a callback that is called when the user selects an entry.
813+
///
814+
/// 3. In [DropdownMenu] it is not required to track
815+
/// the current selection in your app's state.
816+
/// So, instead of tracking the current selection in
817+
/// the [DropdownButton.value] property, you can set the
818+
/// [DropdownMenu.initialSelection] property to the
819+
/// item that should be selected before there is any user action.
820+
///
821+
/// 4. You may also need to make changes to the styling of the
822+
/// [DropdownMenu], see the properties in the [DropdownMenu]
823+
/// constructor for more details.
824+
///
825+
/// See the sample below for an example of migrating
826+
/// from [DropdownButton] to [DropdownMenu].
827+
///
828+
/// ## Using [DropdownButton]
791829
/// {@youtube 560 315 https://www.youtube.com/watch?v=ZzQ_PWrFihg}
792830
///
793831
/// One ancestor must be a [Material] widget and typically this is
@@ -802,6 +840,7 @@ class DropdownButtonHideUnderline extends InheritedWidget {
802840
/// dropdown's value. It should also call [State.setState] to rebuild the
803841
/// dropdown with the new value.
804842
///
843+
///
805844
/// {@tool dartpad}
806845
/// This sample shows a [DropdownButton] with a large arrow icon,
807846
/// purple text style, and bold purple underline, whose value is one of "One",
@@ -819,9 +858,13 @@ class DropdownButtonHideUnderline extends InheritedWidget {
819858
/// [disabledHint] is null and [hint] is non-null, the [hint] widget will
820859
/// instead be displayed.
821860
///
822-
/// Requires one of its ancestors to be a [Material] widget.
861+
/// {@tool dartpad}
862+
/// This sample shows how you would rewrite the above [DropdownButton]
863+
/// to use the [DropdownMenu].
864+
///
865+
/// ** See code in examples/api/lib/material/dropdown_menu/dropdown_menu.1.dart **
866+
/// {@end-tool}
823867
///
824-
/// {@youtube 560 315 https://www.youtube.com/watch?v=ZzQ_PWrFihg}
825868
///
826869
/// See also:
827870
///

packages/flutter/lib/src/material/popup_menu.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,41 @@ typedef PopupMenuItemBuilder<T> = List<PopupMenuEntry<T>> Function(BuildContext
10141014
/// If both are null, then a standard overflow icon is created (depending on the
10151015
/// platform).
10161016
///
1017+
/// /// ## Updating to [MenuAnchor]
1018+
///
1019+
/// There is a Material 3 component,
1020+
/// [MenuAnchor] that is preferred for applications that are configured
1021+
/// for Material 3 (see [ThemeData.useMaterial3]).
1022+
/// The [MenuAnchor] widget's visuals
1023+
/// are a little bit different, see the Material 3 spec at
1024+
/// <https://m3.material.io/components/menus/guidelines> for
1025+
/// more details.
1026+
///
1027+
/// The [MenuAnchor] widget's API is also slightly different.
1028+
/// [MenuAnchor]'s were built to be lower level interface for
1029+
/// creating menus that are displayed from an anchor.
1030+
///
1031+
/// There are a few steps you would take to migrate from
1032+
/// [PopupMenuButton] to [MenuAnchor]:
1033+
///
1034+
/// 1. Instead of using the [PopupMenuButton.itemBuilder] to build
1035+
/// a list of [PopupMenuEntry]s, you would use the [MenuAnchor.menuChildren]
1036+
/// which takes a list of [Widget]s. Usually, you would use a list of
1037+
/// [MenuItemButton]s as shown in the example below.
1038+
///
1039+
/// 2. Instead of using the [PopupMenuButton.onSelected] callback, you would
1040+
/// set individual callbacks for each of the [MenuItemButton]s using the
1041+
/// [MenuItemButton.onPressed] property.
1042+
///
1043+
/// 3. To anchor the [MenuAnchor] to a widget, you would use the [MenuAnchor.builder]
1044+
/// to return the widget of choice - usually a [TextButton] or an [IconButton].
1045+
///
1046+
/// 4. You may want to style the [MenuItemButton]s, see the [MenuItemButton]
1047+
/// documentation for details.
1048+
///
1049+
/// Use the sample below for an example of migrating from [PopupMenuButton] to
1050+
/// [MenuAnchor].
1051+
///
10171052
/// {@tool dartpad}
10181053
/// This example shows a menu with three items, selecting between an enum's
10191054
/// values and setting a `selectedMenu` field based on the selection.
@@ -1022,6 +1057,12 @@ typedef PopupMenuItemBuilder<T> = List<PopupMenuEntry<T>> Function(BuildContext
10221057
/// {@end-tool}
10231058
///
10241059
/// {@tool dartpad}
1060+
/// This example shows how to migrate the above to a [MenuAnchor].
1061+
///
1062+
/// ** See code in examples/api/lib/material/menu_anchor/menu_anchor.2.dart **
1063+
/// {@end-tool}
1064+
///
1065+
/// {@tool dartpad}
10251066
/// This sample shows the creation of a popup menu, as described in:
10261067
/// https://m3.material.io/components/menus/overview
10271068
///

0 commit comments

Comments
 (0)