Skip to content

Commit da24f10

Browse files
authored
Removing Shorcuts.of and Shortctus.maybeOf (#104215)
This removes Shorcuts.of and Shortctus.maybeOf because they're not especially useful, since the only thing you can really set on a ShortcutManager is the shortcuts, and the Shortcuts widget that you give it to manages those, so if it rebuilds, it overwrites what you set. Also, adds a Shortcuts.manager constructor and removes the manager argument to the Shortcuts widget. Removing these will also eliminate an InheritedWidget for each Shortcuts widget, improving memory usage.
1 parent d5fbc37 commit da24f10

File tree

3 files changed

+195
-166
lines changed

3 files changed

+195
-166
lines changed

packages/flutter/lib/src/widgets/actions.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ typedef ActionListenerCallback = void Function(Action<Intent> action);
9797
/// developers to change that if they add an ancestor [Actions] widget that maps
9898
/// [SelectAllTextIntent] to a different [Action].
9999
///
100+
/// See the article on [Using Actions and
101+
/// Shortcuts](https://docs.flutter.dev/development/ui/advanced/actions_and_shortcuts)
102+
/// for a detailed explanation.
103+
///
100104
/// See also:
101105
///
102106
/// * [Shortcuts], which is a widget that contains a key map, in which it looks

packages/flutter/lib/src/widgets/shortcuts.dart

Lines changed: 53 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -636,13 +636,15 @@ class _ActivatorIntentPair with Diagnosticable {
636636
}
637637
}
638638

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.
643641
///
644642
/// The manager may be listened to (with [addListener]/[removeListener]) for
645643
/// 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.
646648
class ShortcutManager with Diagnosticable, ChangeNotifier {
647649
/// Constructs a [ShortcutManager].
648650
ShortcutManager({
@@ -773,6 +775,10 @@ class ShortcutManager with Diagnosticable, ChangeNotifier {
773775
/// when invoking an [Action] via a keyboard key combination that maps to an
774776
/// [Intent].
775777
///
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+
///
776782
/// {@tool dartpad}
777783
/// Here, we will use the [Shortcuts] and [Actions] widgets to add and subtract
778784
/// from a counter. When the child widget has keyboard focus, and a user presses
@@ -810,35 +816,61 @@ class ShortcutManager with Diagnosticable, ChangeNotifier {
810816
/// * [Action], a class for defining an invocation of a user action.
811817
/// * [CallbackAction], a class for creating an action from a callback.
812818
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.
814823
///
815824
/// 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.
816830
const Shortcuts({
817831
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,
820850
required this.child,
821851
this.debugLabel,
822-
}) : assert(shortcuts != null),
852+
}) : _shortcuts = const <ShortcutActivator, Intent>{},
853+
assert(manager != null),
823854
assert(child != null);
824855

825856
/// The [ShortcutManager] that will manage the mapping between key
826857
/// combinations and [Action]s.
827858
///
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.
832863
final ShortcutManager? manager;
833864

834865
/// {@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.
840869
/// {@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;
842874

843875
/// The child widget for this [Shortcuts] widget.
844876
///
@@ -854,52 +886,6 @@ class Shortcuts extends StatefulWidget {
854886
/// unnecessarily with large default shortcut maps.
855887
final String? debugLabel;
856888

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-
903889
@override
904890
State<Shortcuts> createState() => _ShortcutsState();
905891

@@ -926,8 +912,8 @@ class _ShortcutsState extends State<Shortcuts> {
926912
super.initState();
927913
if (widget.manager == null) {
928914
_internalManager = ShortcutManager();
915+
_internalManager!.shortcuts = widget.shortcuts;
929916
}
930-
manager.shortcuts = widget.shortcuts;
931917
}
932918

933919
@override
@@ -941,7 +927,7 @@ class _ShortcutsState extends State<Shortcuts> {
941927
_internalManager ??= ShortcutManager();
942928
}
943929
}
944-
manager.shortcuts = widget.shortcuts;
930+
_internalManager?.shortcuts = widget.shortcuts;
945931
}
946932

947933
KeyEventResult _handleOnKey(FocusNode node, RawKeyEvent event) {
@@ -1331,8 +1317,7 @@ class _ShortcutRegistrarState extends State<ShortcutRegistrar> {
13311317

13321318
@override
13331319
Widget build(BuildContext context) {
1334-
return Shortcuts(
1335-
shortcuts: registry.shortcuts,
1320+
return Shortcuts.manager(
13361321
manager: manager,
13371322
child: _ShortcutRegistrarMarker(
13381323
registry: registry,

0 commit comments

Comments
 (0)