@@ -636,13 +636,15 @@ class _ActivatorIntentPair with Diagnosticable {
636
636
}
637
637
}
638
638
639
- /// A manager of keyboard shortcut bindings.
640
- ///
641
- /// A `ShortcutManager` is obtained by calling [Shortcuts.of] on the context of
642
- /// the widget that you want to find a manager for.
639
+ /// A manager of keyboard shortcut bindings used by [Shortcuts] to handle key
640
+ /// events.
643
641
///
644
642
/// The manager may be listened to (with [addListener] /[removeListener] ) for
645
643
/// change notifications when the shortcuts change.
644
+ ///
645
+ /// Typically, a [Shortcuts] widget supplies its own manager, but in uncommon
646
+ /// cases where overriding the usual shortcut manager behavior is desired, a
647
+ /// subclassed [ShortcutManager] may be supplied.
646
648
class ShortcutManager with Diagnosticable , ChangeNotifier {
647
649
/// Constructs a [ShortcutManager] .
648
650
ShortcutManager ({
@@ -773,6 +775,10 @@ class ShortcutManager with Diagnosticable, ChangeNotifier {
773
775
/// when invoking an [Action] via a keyboard key combination that maps to an
774
776
/// [Intent] .
775
777
///
778
+ /// See the article on [Using Actions and
779
+ /// Shortcuts](https://docs.flutter.dev/development/ui/advanced/actions_and_shortcuts)
780
+ /// for a detailed explanation.
781
+ ///
776
782
/// {@tool dartpad}
777
783
/// Here, we will use the [Shortcuts] and [Actions] widgets to add and subtract
778
784
/// from a counter. When the child widget has keyboard focus, and a user presses
@@ -810,35 +816,61 @@ class ShortcutManager with Diagnosticable, ChangeNotifier {
810
816
/// * [Action] , a class for defining an invocation of a user action.
811
817
/// * [CallbackAction] , a class for creating an action from a callback.
812
818
class Shortcuts extends StatefulWidget {
813
- /// Creates a const [Shortcuts] widget.
819
+ /// Creates a const [Shortcuts] widget that owns the map of shortcuts and
820
+ /// creates its own manager.
821
+ ///
822
+ /// When using this constructor, [manager] will return null.
814
823
///
815
824
/// The [child] and [shortcuts] arguments are required.
825
+ ///
826
+ /// See also:
827
+ ///
828
+ /// * [Shortcuts.manager] , a constructor that uses a [ShortcutManager] to
829
+ /// manage the shortcuts list instead.
816
830
const Shortcuts ({
817
831
super .key,
818
- this .manager,
819
- required this .shortcuts,
832
+ required Map <ShortcutActivator , Intent > shortcuts,
833
+ required this .child,
834
+ this .debugLabel,
835
+ }) : _shortcuts = shortcuts,
836
+ manager = null ,
837
+ assert (shortcuts != null ),
838
+ assert (child != null );
839
+
840
+ /// Creates a const [Shortcuts] widget that uses the [manager] to
841
+ /// manage the map of shortcuts.
842
+ ///
843
+ /// If this constructor is used, [shortcuts] will return the contents of
844
+ /// [ShortcutManager.shortcuts] .
845
+ ///
846
+ /// The [child] and [manager] arguments are required.
847
+ const Shortcuts .manager ({
848
+ super .key,
849
+ required ShortcutManager this .manager,
820
850
required this .child,
821
851
this .debugLabel,
822
- }) : assert (shortcuts != null ),
852
+ }) : _shortcuts = const < ShortcutActivator , Intent > {},
853
+ assert (manager != null ),
823
854
assert (child != null );
824
855
825
856
/// The [ShortcutManager] that will manage the mapping between key
826
857
/// combinations and [Action] s.
827
858
///
828
- /// If not specified, uses a default-constructed [ShortcutManager] .
829
- ///
830
- /// This manager will be given new [shortcuts] to manage whenever the
831
- /// [shortcuts] change materially .
859
+ /// If this widget was created with [Shortcuts.manager] , then
860
+ /// [ShortcutManager.shortcuts] will be used as the source for shortcuts. If
861
+ /// the unnamed constructor is used, this manager will be null, and a
862
+ /// default-constructed `ShortcutsManager` will be used .
832
863
final ShortcutManager ? manager;
833
864
834
865
/// {@template flutter.widgets.shortcuts.shortcuts}
835
- /// The map of shortcuts that the [ShortcutManager] will be given to manage.
836
- ///
837
- /// For performance reasons, it is recommended that a pre-built map is passed
838
- /// in here (e.g. a final variable from your widget class) instead of defining
839
- /// it inline in the build function.
866
+ /// The map of shortcuts that describes the mapping between a key sequence
867
+ /// defined by a [ShortcutActivator] and the [Intent] that will be emitted
868
+ /// when that key sequence is pressed.
840
869
/// {@endtemplate}
841
- final Map <ShortcutActivator , Intent > shortcuts;
870
+ Map <ShortcutActivator , Intent > get shortcuts {
871
+ return manager == null ? _shortcuts : manager! .shortcuts;
872
+ }
873
+ final Map <ShortcutActivator , Intent > _shortcuts;
842
874
843
875
/// The child widget for this [Shortcuts] widget.
844
876
///
@@ -854,52 +886,6 @@ class Shortcuts extends StatefulWidget {
854
886
/// unnecessarily with large default shortcut maps.
855
887
final String ? debugLabel;
856
888
857
- /// Returns the [ShortcutManager] that most tightly encloses the given
858
- /// [BuildContext] .
859
- ///
860
- /// If no [Shortcuts] widget encloses the context given, will assert in debug
861
- /// mode and throw an exception in release mode.
862
- ///
863
- /// See also:
864
- ///
865
- /// * [maybeOf] , which is similar to this function, but will return null if
866
- /// it doesn't find a [Shortcuts] ancestor.
867
- static ShortcutManager of (BuildContext context) {
868
- assert (context != null );
869
- final _ShortcutsMarker ? inherited = context.dependOnInheritedWidgetOfExactType <_ShortcutsMarker >();
870
- assert (() {
871
- if (inherited == null ) {
872
- throw FlutterError (
873
- 'Unable to find a $Shortcuts widget in the context.\n '
874
- '$Shortcuts .of() was called with a context that does not contain a '
875
- '$Shortcuts widget.\n '
876
- 'No $Shortcuts ancestor could be found starting from the context that was '
877
- 'passed to $Shortcuts .of().\n '
878
- 'The context used was:\n '
879
- ' $context ' ,
880
- );
881
- }
882
- return true ;
883
- }());
884
- return inherited! .manager;
885
- }
886
-
887
- /// Returns the [ShortcutManager] that most tightly encloses the given
888
- /// [BuildContext] .
889
- ///
890
- /// If no [Shortcuts] widget encloses the context given, will return null.
891
- ///
892
- /// See also:
893
- ///
894
- /// * [of] , which is similar to this function, but returns a non-nullable
895
- /// result, and will throw an exception if it doesn't find a [Shortcuts]
896
- /// ancestor.
897
- static ShortcutManager ? maybeOf (BuildContext context) {
898
- assert (context != null );
899
- final _ShortcutsMarker ? inherited = context.dependOnInheritedWidgetOfExactType <_ShortcutsMarker >();
900
- return inherited? .manager;
901
- }
902
-
903
889
@override
904
890
State <Shortcuts > createState () => _ShortcutsState ();
905
891
@@ -926,8 +912,8 @@ class _ShortcutsState extends State<Shortcuts> {
926
912
super .initState ();
927
913
if (widget.manager == null ) {
928
914
_internalManager = ShortcutManager ();
915
+ _internalManager! .shortcuts = widget.shortcuts;
929
916
}
930
- manager.shortcuts = widget.shortcuts;
931
917
}
932
918
933
919
@override
@@ -941,7 +927,7 @@ class _ShortcutsState extends State<Shortcuts> {
941
927
_internalManager ?? = ShortcutManager ();
942
928
}
943
929
}
944
- manager .shortcuts = widget.shortcuts;
930
+ _internalManager ? .shortcuts = widget.shortcuts;
945
931
}
946
932
947
933
KeyEventResult _handleOnKey (FocusNode node, RawKeyEvent event) {
@@ -1331,8 +1317,7 @@ class _ShortcutRegistrarState extends State<ShortcutRegistrar> {
1331
1317
1332
1318
@override
1333
1319
Widget build (BuildContext context) {
1334
- return Shortcuts (
1335
- shortcuts: registry.shortcuts,
1320
+ return Shortcuts .manager (
1336
1321
manager: manager,
1337
1322
child: _ShortcutRegistrarMarker (
1338
1323
registry: registry,
0 commit comments