@@ -7,7 +7,10 @@ import 'dart:async';
7
7
import 'package:flutter/foundation.dart' ;
8
8
import 'package:flutter/services.dart' ;
9
9
10
+ import 'actions.dart' ;
11
+ import 'basic.dart' ;
10
12
import 'binding.dart' ;
13
+ import 'focus_manager.dart' ;
11
14
import 'framework.dart' ;
12
15
import 'shortcuts.dart' ;
13
16
@@ -44,6 +47,7 @@ class ShortcutSerialization {
44
47
/// This is used by a [CharacterActivator] to serialize itself.
45
48
ShortcutSerialization .character (String character)
46
49
: _internal = < String , Object ? > {_kShortcutCharacter: character},
50
+ _character = character,
47
51
assert (character.length == 1 );
48
52
49
53
/// Creates a [ShortcutSerialization] representing a specific
@@ -70,6 +74,11 @@ class ShortcutSerialization {
70
74
trigger != LogicalKeyboardKey .metaRight,
71
75
'Specifying a modifier key as a trigger is not allowed. '
72
76
'Use provided boolean parameters instead.' ),
77
+ _trigger = trigger,
78
+ _control = control,
79
+ _shift = shift,
80
+ _alt = alt,
81
+ _meta = meta,
73
82
_internal = < String , Object ? > {
74
83
_kShortcutTrigger: trigger.keyId,
75
84
_kShortcutModifiers: (control ? _shortcutModifierControl : 0 ) |
@@ -80,6 +89,35 @@ class ShortcutSerialization {
80
89
81
90
final Map <String , Object ?> _internal;
82
91
92
+ /// The keyboard key that triggers this shortcut, if any.
93
+ LogicalKeyboardKey ? get trigger => _trigger;
94
+ LogicalKeyboardKey ? _trigger;
95
+
96
+ /// The character that triggers this shortcut, if any.
97
+ String ? get character => _character;
98
+ String ? _character;
99
+
100
+ /// If this shortcut has a [trigger] , this indicates whether or not the
101
+ /// control modifier needs to be down or not.
102
+ bool ? get control => _control;
103
+ bool ? _control;
104
+
105
+ /// If this shortcut has a [trigger] , this indicates whether or not the
106
+ /// shift modifier needs to be down or not.
107
+ bool ? get shift => _shift;
108
+ bool ? _shift;
109
+
110
+ /// If this shortcut has a [trigger] , this indicates whether or not the
111
+ /// alt modifier needs to be down or not.
112
+ bool ? get alt => _alt;
113
+ bool ? _alt;
114
+
115
+ /// If this shortcut has a [trigger] , this indicates whether or not the meta
116
+ /// (also known as the Windows or Command key) modifier needs to be down or
117
+ /// not.
118
+ bool ? get meta => _meta;
119
+ bool ? _meta;
120
+
83
121
/// The bit mask for the [LogicalKeyboardKey.meta] key (or it's left/right
84
122
/// equivalents) being down.
85
123
static const int _shortcutModifierMeta = 1 << 0 ;
@@ -124,10 +162,13 @@ mixin MenuSerializableShortcut {
124
162
/// An abstract class for describing cascading menu hierarchies that are part of
125
163
/// a [PlatformMenuBar] .
126
164
///
127
- /// This type is used by [PlatformMenuDelegate.setMenus] to accept the menu
165
+ /// This type is also used by [PlatformMenuDelegate.setMenus] to accept the menu
128
166
/// hierarchy to be sent to the platform, and by [PlatformMenuBar] to define the
129
167
/// menu hierarchy.
130
168
///
169
+ /// This class is abstract, and so can't be used directly. Typically subclasses
170
+ /// like [PlatformMenuItem] are used.
171
+ ///
131
172
/// See also:
132
173
///
133
174
/// * [PlatformMenuBar] , a widget that renders menu items using platform APIs
@@ -141,8 +182,8 @@ abstract class MenuItem with Diagnosticable {
141
182
///
142
183
/// The `delegate` is the [PlatformMenuDelegate] that is requesting the
143
184
/// serialization. The `index` is the position of this menu item in the list
144
- /// of children of the [PlatformMenu] it belongs to, and `count` is the number
145
- /// of children in the [PlatformMenu] it belongs to.
185
+ /// of [menus] of the [PlatformMenu] it belongs to, and `count` is the number
186
+ /// of [menus] in the [PlatformMenu] it belongs to.
146
187
///
147
188
/// The `getId` parameter is a [MenuItemSerializableIdGenerator] function that
148
189
/// generates a unique ID for each menu item, which is to be returned in the
@@ -152,6 +193,17 @@ abstract class MenuItem with Diagnosticable {
152
193
required MenuItemSerializableIdGenerator getId,
153
194
});
154
195
196
+ /// The optional shortcut that selects this [MenuItem] .
197
+ ///
198
+ /// This shortcut is only enabled when [onSelected] is set.
199
+ MenuSerializableShortcut ? get shortcut => null ;
200
+
201
+ /// Returns any child [MenuItem] s of this item.
202
+ ///
203
+ /// Returns an empty list if this type of menu item doesn't have
204
+ /// children.
205
+ List <MenuItem > get menus => const < MenuItem > [];
206
+
155
207
/// Returns all descendant [MenuItem] s of this item.
156
208
///
157
209
/// Returns an empty list if this type of menu item doesn't have
@@ -163,9 +215,27 @@ abstract class MenuItem with Diagnosticable {
163
215
///
164
216
/// Only items that do not have submenus will have this callback invoked.
165
217
///
218
+ /// Only one of [onSelected] or [onSelectedIntent] may be specified.
219
+ ///
220
+ /// If neither [onSelected] nor [onSelectedIntent] are specified, then this
221
+ /// menu item is considered to be disabled.
222
+ ///
166
223
/// The default implementation returns null.
167
224
VoidCallback ? get onSelected => null ;
168
225
226
+ /// Returns an intent, if any, to be invoked if the platform receives a
227
+ /// "Menu.selectedCallback" method call from the platform for this item.
228
+ ///
229
+ /// Only items that do not have submenus will have this intent invoked.
230
+ ///
231
+ /// Only one of [onSelected] or [onSelectedIntent] may be specified.
232
+ ///
233
+ /// If neither [onSelected] nor [onSelectedIntent] are specified, then this
234
+ /// menu item is considered to be disabled.
235
+ ///
236
+ /// The default implementation returns null.
237
+ Intent ? get onSelectedIntent => null ;
238
+
169
239
/// Returns a callback, if any, to be invoked if the platform menu receives a
170
240
/// "Menu.opened" method call from the platform for this item.
171
241
///
@@ -181,6 +251,12 @@ abstract class MenuItem with Diagnosticable {
181
251
///
182
252
/// The default implementation returns null.
183
253
VoidCallback ? get onClose => null ;
254
+
255
+ /// Returns the list of group members if this menu item is a "grouping" menu
256
+ /// item, such as [PlatformMenuItemGroup] .
257
+ ///
258
+ /// Defaults to an empty list.
259
+ List <MenuItem > get members => const < MenuItem > [];
184
260
}
185
261
186
262
/// An abstract delegate class that can be used to set
@@ -383,7 +459,12 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
383
459
}
384
460
final MenuItem item = _idMap[id]! ;
385
461
if (call.method == _kMenuSelectedCallbackMethod) {
462
+ assert (item.onSelected == null || item.onSelectedIntent == null ,
463
+ 'Only one of MenuItem.onSelected or MenuItem.onSelectedIntent may be specified' );
386
464
item.onSelected? .call ();
465
+ if (item.onSelectedIntent != null ) {
466
+ Actions .maybeInvoke (FocusManager .instance.primaryFocus! .context! , item.onSelectedIntent! );
467
+ }
387
468
} else if (call.method == _kMenuItemOpenedMethod) {
388
469
item.onOpen? .call ();
389
470
} else if (call.method == _kMenuItemClosedMethod) {
@@ -407,7 +488,7 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
407
488
/// the platform menu bar.
408
489
///
409
490
/// As far as Flutter is concerned, this widget has no visual representation,
410
- /// and intercepts no events: it just returns the [body ] from its build
491
+ /// and intercepts no events: it just returns the [child ] from its build
411
492
/// function. This is because all of the rendering, shortcuts, and event
412
493
/// handling for the menu is handled by the plugin on the host platform.
413
494
///
@@ -429,18 +510,32 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
429
510
class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin {
430
511
/// Creates a const [PlatformMenuBar] .
431
512
///
432
- /// The [body ] and [menus] attributes are required.
513
+ /// The [child ] and [menus] attributes are required.
433
514
const PlatformMenuBar ({
434
515
super .key,
435
- required this .body,
436
516
required this .menus,
437
- });
517
+ this .child,
518
+ @Deprecated (
519
+ 'Use the child attribute instead. '
520
+ 'This feature was deprecated after v3.1.0-0.0.pre.'
521
+ )
522
+ this .body,
523
+ }) : assert (body == null || child == null ,
524
+ 'The body argument is deprecated, and only one of body or child may be used.' );
525
+
526
+ /// The widget below this widget in the tree.
527
+ ///
528
+ /// {@macro flutter.widgets.ProxyWidget.child}
529
+ final Widget ? child;
438
530
439
- /// The widget to be rendered in the Flutter window that these platform menus
440
- /// are associated with.
531
+ /// The widget below this widget in the tree.
441
532
///
442
- /// This is typically the body of the application's UI.
443
- final Widget body;
533
+ /// This attribute is deprecated, use [child] instead.
534
+ @Deprecated (
535
+ 'Use the child attribute instead. '
536
+ 'This feature was deprecated after v3.1.0-0.0.pre.'
537
+ )
538
+ final Widget ? body;
444
539
445
540
/// The list of menu items that are the top level children of the
446
541
/// [PlatformMenuBar] .
@@ -512,7 +607,7 @@ class _PlatformMenuBarState extends State<PlatformMenuBar> {
512
607
Widget build (BuildContext context) {
513
608
// PlatformMenuBar is really about managing the platform menu bar, and
514
609
// doesn't do any rendering or event handling in Flutter.
515
- return widget.body;
610
+ return widget.child ?? widget. body ?? const SizedBox () ;
516
611
}
517
612
}
518
613
@@ -547,6 +642,7 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin {
547
642
/// The menu items in the submenu opened by this menu item.
548
643
///
549
644
/// If this is an empty list, this [PlatformMenu] will be disabled.
645
+ @override
550
646
final List <MenuItem > menus;
551
647
552
648
/// Returns all descendant [MenuItem] s of this item.
@@ -646,6 +742,7 @@ class PlatformMenuItemGroup extends MenuItem {
646
742
/// The [MenuItem] s that are members of this menu item group.
647
743
///
648
744
/// An assertion will be thrown if there isn't at least one member of the group.
745
+ @override
649
746
final List <MenuItem > members;
650
747
651
748
@override
@@ -654,19 +751,32 @@ class PlatformMenuItemGroup extends MenuItem {
654
751
required MenuItemSerializableIdGenerator getId,
655
752
}) {
656
753
assert (members.isNotEmpty, 'There must be at least one member in a PlatformMenuItemGroup' );
754
+ return serialize (this , delegate, getId: getId);
755
+ }
756
+
757
+ /// Converts the supplied object to the correct channel representation for the
758
+ /// 'flutter/menu' channel.
759
+ ///
760
+ /// This API is supplied so that implementers of [PlatformMenuItemGroup] can share
761
+ /// this implementation.
762
+ static Iterable <Map <String , Object ?>> serialize (
763
+ MenuItem group,
764
+ PlatformMenuDelegate delegate, {
765
+ required MenuItemSerializableIdGenerator getId,
766
+ }) {
657
767
final List <Map <String , Object ?>> result = < Map <String , Object ?>> [];
658
768
result.add (< String , Object ? > {
659
- _kIdKey: getId (this ),
769
+ _kIdKey: getId (group ),
660
770
_kIsDividerKey: true ,
661
771
});
662
- for (final MenuItem item in members) {
772
+ for (final MenuItem item in group. members) {
663
773
result.addAll (item.toChannelRepresentation (
664
774
delegate,
665
775
getId: getId,
666
776
));
667
777
}
668
778
result.add (< String , Object ? > {
669
- _kIdKey: getId (this ),
779
+ _kIdKey: getId (group ),
670
780
_kIsDividerKey: true ,
671
781
});
672
782
return result;
@@ -697,14 +807,16 @@ class PlatformMenuItem extends MenuItem {
697
807
required this .label,
698
808
this .shortcut,
699
809
this .onSelected,
700
- });
810
+ this .onSelectedIntent,
811
+ }) : assert (onSelected == null || onSelectedIntent == null , 'Only one of onSelected or onSelectedIntent may be specified' );
701
812
702
813
/// The required label used for rendering the menu item.
703
814
final String label;
704
815
705
816
/// The optional shortcut that selects this [PlatformMenuItem] .
706
817
///
707
818
/// This shortcut is only enabled when [onSelected] is set.
819
+ @override
708
820
final MenuSerializableShortcut ? shortcut;
709
821
710
822
/// An optional callback that is called when this [PlatformMenuItem] is
@@ -714,6 +826,13 @@ class PlatformMenuItem extends MenuItem {
714
826
@override
715
827
final VoidCallback ? onSelected;
716
828
829
+ /// An optional intent that is invoked when this [PlatformMenuItem] is
830
+ /// selected.
831
+ ///
832
+ /// If unset, this menu item will be disabled.
833
+ @override
834
+ final Intent ? onSelectedIntent;
835
+
717
836
@override
718
837
Iterable <Map <String , Object ?>> toChannelRepresentation (
719
838
PlatformMenuDelegate delegate, {
@@ -741,6 +860,9 @@ class PlatformMenuItem extends MenuItem {
741
860
};
742
861
}
743
862
863
+ @override
864
+ String toStringShort () => '${describeIdentity (this )}($label )' ;
865
+
744
866
@override
745
867
void debugFillProperties (DiagnosticPropertiesBuilder properties) {
746
868
super .debugFillProperties (properties);
@@ -779,9 +901,7 @@ class PlatformProvidedMenuItem extends PlatformMenuItem {
779
901
const PlatformProvidedMenuItem ({
780
902
required this .type,
781
903
this .enabled = true ,
782
- }) : super (
783
- label: '' , // The label is ignored for standard menus.
784
- );
904
+ }) : super (label: '' ); // The label is ignored for platform provided menus.
785
905
786
906
/// The type of default menu this is.
787
907
///
@@ -832,7 +952,7 @@ class PlatformProvidedMenuItem extends PlatformMenuItem {
832
952
assert (() {
833
953
if (! hasMenu (type)) {
834
954
throw ArgumentError (
835
- 'Platform ${defaultTargetPlatform .name } has no standard menu for '
955
+ 'Platform ${defaultTargetPlatform .name } has no platform provided menu for '
836
956
'$type . Call PlatformProvidedMenuItem.hasMenu to determine this before '
837
957
'instantiating one.' ,
838
958
);
@@ -856,7 +976,8 @@ class PlatformProvidedMenuItem extends PlatformMenuItem {
856
976
}
857
977
}
858
978
859
- /// The list of possible standard, prebuilt menus for use in a [PlatformMenuBar] .
979
+ /// The list of possible platform provided, prebuilt menus for use in a
980
+ /// [PlatformMenuBar] .
860
981
///
861
982
/// These are menus that the platform typically provides that cannot be
862
983
/// reproduced in Flutter without calling platform functions, but are standard
@@ -870,7 +991,7 @@ class PlatformProvidedMenuItem extends PlatformMenuItem {
870
991
/// Add these to your [PlatformMenuBar] using the [PlatformProvidedMenuItem]
871
992
/// class.
872
993
///
873
- /// You can tell if the platform supports the given standard menu using the
994
+ /// You can tell if the platform provides the given menu using the
874
995
/// [PlatformProvidedMenuItem.hasMenu] method.
875
996
// Must be kept in sync with the plugin code's enum of the same name.
876
997
enum PlatformProvidedMenuItemType {
0 commit comments