"Fossies" - the Fresh Open Source Software Archive

Member "flutter-3.7.0/packages/flutter/lib/src/widgets/actions.dart" (24 Jan 2023, 67850 Bytes) of package /linux/misc/flutter-3.7.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "actions.dart": 3.3.10_vs_3.7.0.

    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/foundation.dart';
    6 import 'package:flutter/gestures.dart';
    7 import 'package:flutter/rendering.dart';
    8 import 'package:flutter/scheduler.dart';
    9 import 'package:flutter/services.dart';
   10 
   11 import 'basic.dart';
   12 import 'focus_manager.dart';
   13 import 'focus_scope.dart';
   14 import 'framework.dart';
   15 import 'media_query.dart';
   16 import 'shortcuts.dart';
   17 
   18 // BuildContext/Element doesn't have a parent accessor, but it can be
   19 // simulated with visitAncestorElements. _getParent is needed because
   20 // context.getElementForInheritedWidgetOfExactType will return itself if it
   21 // happens to be of the correct type. getParent should be O(1), since we
   22 // always return false at the first ancestor.
   23 BuildContext _getParent(BuildContext context) {
   24   late final BuildContext parent;
   25   context.visitAncestorElements((Element ancestor) {
   26     parent = ancestor;
   27     return false;
   28   });
   29   return parent;
   30 }
   31 
   32 /// An abstract class representing a particular configuration of an [Action].
   33 ///
   34 /// This class is what the [Shortcuts.shortcuts] map has as values, and is used
   35 /// by an [ActionDispatcher] to look up an action and invoke it, giving it this
   36 /// object to extract configuration information from.
   37 ///
   38 /// See also:
   39 ///
   40 ///  * [Actions.invoke], which invokes the action associated with a specified
   41 ///    [Intent] using the [Actions] widget that most tightly encloses the given
   42 ///    [BuildContext].
   43 @immutable
   44 abstract class Intent with Diagnosticable {
   45   /// Abstract const constructor. This constructor enables subclasses to provide
   46   /// const constructors so that they can be used in const expressions.
   47   const Intent();
   48 
   49   /// An intent that is mapped to a [DoNothingAction], which, as the name
   50   /// implies, does nothing.
   51   ///
   52   /// This Intent is mapped to an action in the [WidgetsApp] that does nothing,
   53   /// so that it can be bound to a key in a [Shortcuts] widget in order to
   54   /// disable a key binding made above it in the hierarchy.
   55   static const DoNothingIntent doNothing = DoNothingIntent._();
   56 }
   57 
   58 /// The kind of callback that an [Action] uses to notify of changes to the
   59 /// action's state.
   60 ///
   61 /// To register an action listener, call [Action.addActionListener].
   62 typedef ActionListenerCallback = void Function(Action<Intent> action);
   63 
   64 /// Base class for actions.
   65 ///
   66 /// As the name implies, an [Action] is an action or command to be performed.
   67 /// They are typically invoked as a result of a user action, such as a keyboard
   68 /// shortcut in a [Shortcuts] widget, which is used to look up an [Intent],
   69 /// which is given to an [ActionDispatcher] to map the [Intent] to an [Action]
   70 /// and invoke it.
   71 ///
   72 /// The [ActionDispatcher] can invoke an [Action] on the primary focus, or
   73 /// without regard for focus.
   74 ///
   75 /// ### Action Overriding
   76 ///
   77 /// When using a leaf widget to build a more specialized widget, it's sometimes
   78 /// desirable to change the default handling of an [Intent] defined in the leaf
   79 /// widget. For instance, [TextField]'s [SelectAllTextIntent] by default selects
   80 /// the text it currently contains, but in a US phone number widget that
   81 /// consists of 3 different [TextField]s (area code, prefix and line number),
   82 /// [SelectAllTextIntent] should instead select the text within all 3
   83 /// [TextField]s.
   84 ///
   85 /// An overridable [Action] is a special kind of [Action] created using the
   86 /// [Action.overridable] constructor. It has access to a default [Action], and a
   87 /// nullable override [Action]. It has the same behavior as its override if that
   88 /// exists, and mirrors the behavior of its `defaultAction` otherwise.
   89 ///
   90 /// The [Action.overridable] constructor creates overridable [Action]s that use
   91 /// a [BuildContext] to find a suitable override in its ancestor [Actions]
   92 /// widget. This can be used to provide a default implementation when creating a
   93 /// general purpose leaf widget, and later override it when building a more
   94 /// specialized widget using that leaf widget. Using the [TextField] example
   95 /// above, the [TextField] widget uses an overridable [Action] to provide a
   96 /// sensible default for [SelectAllTextIntent], while still allowing app
   97 /// developers to change that if they add an ancestor [Actions] widget that maps
   98 /// [SelectAllTextIntent] to a different [Action].
   99 ///
  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 ///
  104 /// See also:
  105 ///
  106 ///  * [Shortcuts], which is a widget that contains a key map, in which it looks
  107 ///    up key combinations in order to invoke actions.
  108 ///  * [Actions], which is a widget that defines a map of [Intent] to [Action]
  109 ///    and allows redefining of actions for its descendants.
  110 ///  * [ActionDispatcher], a class that takes an [Action] and invokes it, passing
  111 ///    a given [Intent].
  112 ///  * [Action.overridable] for an example on how to make an [Action]
  113 ///    overridable.
  114 abstract class Action<T extends Intent> with Diagnosticable {
  115   /// Creates an [Action].
  116   Action();
  117 
  118   /// Creates an [Action] that allows itself to be overridden by the closest
  119   /// ancestor [Action] in the given [context] that handles the same [Intent],
  120   /// if one exists.
  121   ///
  122   /// When invoked, the resulting [Action] tries to find the closest [Action] in
  123   /// the given `context` that handles the same type of [Intent] as the
  124   /// `defaultAction`, then calls its [Action.invoke] method. When no override
  125   /// [Action]s can be found, it invokes the `defaultAction`.
  126   ///
  127   /// An overridable action delegates everything to its override if one exists,
  128   /// and has the same behavior as its `defaultAction` otherwise. For this
  129   /// reason, the override has full control over whether and how an [Intent]
  130   /// should be handled, or a key event should be consumed. An override
  131   /// [Action]'s [callingAction] property will be set to the [Action] it
  132   /// currently overrides, giving it access to the default behavior. See the
  133   /// [callingAction] property for an example.
  134   ///
  135   /// The `context` argument is the [BuildContext] to find the override with. It
  136   /// is typically a [BuildContext] above the [Actions] widget that contains
  137   /// this overridable [Action].
  138   ///
  139   /// The `defaultAction` argument is the [Action] to be invoked where there's
  140   /// no ancestor [Action]s can't be found in `context` that handle the same
  141   /// type of [Intent].
  142   ///
  143   /// This is useful for providing a set of default [Action]s in a leaf widget
  144   /// to allow further overriding, or to allow the [Intent] to propagate to
  145   /// parent widgets that also support this [Intent].
  146   ///
  147   /// {@tool dartpad}
  148   /// This sample shows how to implement a rudimentary `CopyableText` widget
  149   /// that responds to Ctrl-C by copying its own content to the clipboard.
  150   ///
  151   /// if `CopyableText` is to be provided in a package, developers using the
  152   /// widget may want to change how copying is handled. As the author of the
  153   /// package, you can enable that by making the corresponding [Action]
  154   /// overridable. In the second part of the code sample, three `CopyableText`
  155   /// widgets are used to build a verification code widget which overrides the
  156   /// "copy" action by copying the combined numbers from all three `CopyableText`
  157   /// widgets.
  158   ///
  159   /// ** See code in examples/api/lib/widgets/actions/action.action_overridable.0.dart **
  160   /// {@end-tool}
  161   factory Action.overridable({
  162     required Action<T> defaultAction,
  163     required BuildContext context,
  164   }) {
  165     return defaultAction._makeOverridableAction(context);
  166   }
  167 
  168   final ObserverList<ActionListenerCallback> _listeners = ObserverList<ActionListenerCallback>();
  169 
  170   Action<T>? _currentCallingAction;
  171   // ignore: use_setters_to_change_properties, (code predates enabling of this lint)
  172   void _updateCallingAction(Action<T>? value) {
  173     _currentCallingAction = value;
  174   }
  175 
  176   /// The [Action] overridden by this [Action].
  177   ///
  178   /// The [Action.overridable] constructor creates an overridable [Action] that
  179   /// allows itself to be overridden by the closest ancestor [Action], and falls
  180   /// back to its own `defaultAction` when no overrides can be found. When an
  181   /// override is present, an overridable [Action] forwards all incoming
  182   /// method calls to the override, and allows the override to access the
  183   /// `defaultAction` via its [callingAction] property.
  184   ///
  185   /// Before forwarding the call to the override, the overridable [Action] is
  186   /// responsible for setting [callingAction] to its `defaultAction`, which is
  187   /// already taken care of by the overridable [Action] created using
  188   /// [Action.overridable].
  189   ///
  190   /// This property is only non-null when this [Action] is an override of the
  191   /// [callingAction], and is currently being invoked from [callingAction].
  192   ///
  193   /// Invoking [callingAction]'s methods, or accessing its properties, is
  194   /// allowed and does not introduce infinite loops or infinite recursions.
  195   ///
  196   /// {@tool snippet}
  197   /// An example `Action` that handles [PasteTextIntent] but has mostly the same
  198   /// behavior as the overridable action. It's OK to call
  199   /// `callingAction?.isActionEnabled` in the implementation of this `Action`.
  200   ///
  201   /// ```dart
  202   /// class MyPasteAction extends Action<PasteTextIntent> {
  203   ///   @override
  204   ///   Object? invoke(PasteTextIntent intent) {
  205   ///     print(intent);
  206   ///     return callingAction?.invoke(intent);
  207   ///   }
  208   ///
  209   ///   @override
  210   ///   bool get isActionEnabled => callingAction?.isActionEnabled ?? false;
  211   ///
  212   ///   @override
  213   ///   bool consumesKey(PasteTextIntent intent) => callingAction?.consumesKey(intent) ?? false;
  214   /// }
  215   /// ```
  216   /// {@end-tool}
  217   @protected
  218   Action<T>? get callingAction => _currentCallingAction;
  219 
  220   /// Gets the type of intent this action responds to.
  221   Type get intentType => T;
  222 
  223   /// Returns true if the action is enabled and is ready to be invoked.
  224   ///
  225   /// This will be called by the [ActionDispatcher] before attempting to invoke
  226   /// the action.
  227   bool isEnabled(T intent) => isActionEnabled;
  228 
  229   /// Whether this [Action] is inherently enabled.
  230   ///
  231   /// If [isActionEnabled] is false, then this [Action] is disabled for any
  232   /// given [Intent].
  233   //
  234   /// If the enabled state changes, overriding subclasses must call
  235   /// [notifyActionListeners] to notify any listeners of the change.
  236   ///
  237   /// In the case of an overridable `Action`, accessing this property creates
  238   /// an dependency on the overridable `Action`s `lookupContext`.
  239   bool get isActionEnabled => true;
  240 
  241   /// Indicates whether this action should treat key events mapped to this
  242   /// action as being "handled" when it is invoked via the key event.
  243   ///
  244   /// If the key is handled, then no other key event handlers in the focus chain
  245   /// will receive the event.
  246   ///
  247   /// If the key event is not handled, it will be passed back to the engine, and
  248   /// continue to be processed there, allowing text fields and non-Flutter
  249   /// widgets to receive the key event.
  250   ///
  251   /// The default implementation returns true.
  252   bool consumesKey(T intent) => true;
  253 
  254   /// Called when the action is to be performed.
  255   ///
  256   /// This is called by the [ActionDispatcher] when an action is invoked via
  257   /// [Actions.invoke], or when an action is invoked using
  258   /// [ActionDispatcher.invokeAction] directly.
  259   ///
  260   /// This method is only meant to be invoked by an [ActionDispatcher], or by
  261   /// its subclasses, and only when [isEnabled] is true.
  262   ///
  263   /// When overriding this method, the returned value can be any Object, but
  264   /// changing the return type of the override to match the type of the returned
  265   /// value provides more type safety.
  266   ///
  267   /// For instance, if an override of [invoke] returned an `int`, then it might
  268   /// be defined like so:
  269   ///
  270   /// ```dart
  271   /// class IncrementIntent extends Intent {
  272   ///   const IncrementIntent({required this.index});
  273   ///
  274   ///   final int index;
  275   /// }
  276   ///
  277   /// class MyIncrementAction extends Action<IncrementIntent> {
  278   ///   @override
  279   ///   int invoke(IncrementIntent intent) {
  280   ///     return intent.index + 1;
  281   ///   }
  282   /// }
  283   /// ```
  284   ///
  285   /// To receive the result of invoking an action, it must be invoked using
  286   /// [Actions.invoke], or by invoking it using an [ActionDispatcher]. An action
  287   /// invoked via a [Shortcuts] widget will have its return value ignored.
  288   @protected
  289   Object? invoke(T intent);
  290 
  291   /// Register a callback to listen for changes to the state of this action.
  292   ///
  293   /// If you call this, you must call [removeActionListener] a matching number
  294   /// of times, or memory leaks will occur. To help manage this and avoid memory
  295   /// leaks, use of the [ActionListener] widget to register and unregister your
  296   /// listener appropriately is highly recommended.
  297   ///
  298   /// {@template flutter.widgets.Action.addActionListener}
  299   /// If a listener had been added twice, and is removed once during an
  300   /// iteration (i.e. in response to a notification), it will still be called
  301   /// again. If, on the other hand, it is removed as many times as it was
  302   /// registered, then it will no longer be called. This odd behavior is the
  303   /// result of the [Action] not being able to determine which listener
  304   /// is being removed, since they are identical, and therefore conservatively
  305   /// still calling all the listeners when it knows that any are still
  306   /// registered.
  307   ///
  308   /// This surprising behavior can be unexpectedly observed when registering a
  309   /// listener on two separate objects which are both forwarding all
  310   /// registrations to a common upstream object.
  311   /// {@endtemplate}
  312   @mustCallSuper
  313   void addActionListener(ActionListenerCallback listener) => _listeners.add(listener);
  314 
  315   /// Remove a previously registered closure from the list of closures that are
  316   /// notified when the object changes.
  317   ///
  318   /// If the given listener is not registered, the call is ignored.
  319   ///
  320   /// If you call [addActionListener], you must call this method a matching
  321   /// number of times, or memory leaks will occur. To help manage this and avoid
  322   /// memory leaks, use of the [ActionListener] widget to register and
  323   /// unregister your listener appropriately is highly recommended.
  324   ///
  325   /// {@macro flutter.widgets.Action.addActionListener}
  326   @mustCallSuper
  327   void removeActionListener(ActionListenerCallback listener) => _listeners.remove(listener);
  328 
  329   /// Call all the registered listeners.
  330   ///
  331   /// Subclasses should call this method whenever the object changes, to notify
  332   /// any clients the object may have changed. Listeners that are added during this
  333   /// iteration will not be visited. Listeners that are removed during this
  334   /// iteration will not be visited after they are removed.
  335   ///
  336   /// Exceptions thrown by listeners will be caught and reported using
  337   /// [FlutterError.reportError].
  338   ///
  339   /// Surprising behavior can result when reentrantly removing a listener (i.e.
  340   /// in response to a notification) that has been registered multiple times.
  341   /// See the discussion at [removeActionListener].
  342   @protected
  343   @visibleForTesting
  344   @pragma('vm:notify-debugger-on-exception')
  345   void notifyActionListeners() {
  346     if (_listeners.isEmpty) {
  347       return;
  348     }
  349 
  350     // Make a local copy so that a listener can unregister while the list is
  351     // being iterated over.
  352     final List<ActionListenerCallback> localListeners = List<ActionListenerCallback>.of(_listeners);
  353     for (final ActionListenerCallback listener in localListeners) {
  354       InformationCollector? collector;
  355       assert(() {
  356         collector = () => <DiagnosticsNode>[
  357           DiagnosticsProperty<Action<T>>(
  358             'The $runtimeType sending notification was',
  359             this,
  360             style: DiagnosticsTreeStyle.errorProperty,
  361           ),
  362         ];
  363         return true;
  364       }());
  365       try {
  366         if (_listeners.contains(listener)) {
  367           listener(this);
  368         }
  369       } catch (exception, stack) {
  370         FlutterError.reportError(FlutterErrorDetails(
  371           exception: exception,
  372           stack: stack,
  373           library: 'widgets library',
  374           context: ErrorDescription('while dispatching notifications for $runtimeType'),
  375           informationCollector: collector,
  376         ));
  377       }
  378     }
  379   }
  380 
  381   Action<T> _makeOverridableAction(BuildContext context) {
  382     return _OverridableAction<T>(defaultAction: this, lookupContext: context);
  383   }
  384 }
  385 
  386 /// A helper widget for making sure that listeners on an action are removed properly.
  387 ///
  388 /// Listeners on the [Action] class must have their listener callbacks removed
  389 /// with [Action.removeActionListener] when the listener is disposed of. This widget
  390 /// helps with that, by providing a lifetime for the connection between the
  391 /// [listener] and the [Action], and by handling the adding and removing of
  392 /// the [listener] at the right points in the widget lifecycle.
  393 ///
  394 /// If you listen to an [Action] widget in a widget hierarchy, you should use
  395 /// this widget. If you are using an [Action] outside of a widget context, then
  396 /// you must call removeListener yourself.
  397 ///
  398 /// {@tool dartpad}
  399 /// This example shows how ActionListener handles adding and removing of
  400 /// the [listener] in the widget lifecycle.
  401 ///
  402 /// ** See code in examples/api/lib/widgets/actions/action_listener.0.dart **
  403 /// {@end-tool}
  404 ///
  405 @immutable
  406 class ActionListener extends StatefulWidget {
  407   /// Create a const [ActionListener].
  408   ///
  409   /// The [listener], [action], and [child] arguments must not be null.
  410   const ActionListener({
  411     super.key,
  412     required this.listener,
  413     required this.action,
  414     required this.child,
  415   })  : assert(listener != null),
  416         assert(action != null),
  417         assert(child != null);
  418 
  419   /// The [ActionListenerCallback] callback to register with the [action].
  420   ///
  421   /// Must not be null.
  422   final ActionListenerCallback listener;
  423 
  424   /// The [Action] that the callback will be registered with.
  425   ///
  426   /// Must not be null.
  427   final Action<Intent> action;
  428 
  429   /// {@macro flutter.widgets.ProxyWidget.child}
  430   final Widget child;
  431 
  432   @override
  433   State<ActionListener> createState() => _ActionListenerState();
  434 }
  435 
  436 class _ActionListenerState extends State<ActionListener> {
  437   @override
  438   void initState() {
  439     super.initState();
  440     widget.action.addActionListener(widget.listener);
  441   }
  442 
  443   @override
  444   void didUpdateWidget(ActionListener oldWidget) {
  445     super.didUpdateWidget(oldWidget);
  446     if (oldWidget.action == widget.action && oldWidget.listener == widget.listener) {
  447       return;
  448     }
  449     oldWidget.action.removeActionListener(oldWidget.listener);
  450     widget.action.addActionListener(widget.listener);
  451   }
  452 
  453   @override
  454   void dispose() {
  455     widget.action.removeActionListener(widget.listener);
  456     super.dispose();
  457   }
  458 
  459   @override
  460   Widget build(BuildContext context) => widget.child;
  461 }
  462 
  463 /// An abstract [Action] subclass that adds an optional [BuildContext] to the
  464 /// [invoke] method to be able to provide context to actions.
  465 ///
  466 /// [ActionDispatcher.invokeAction] checks to see if the action it is invoking
  467 /// is a [ContextAction], and if it is, supplies it with a context.
  468 abstract class ContextAction<T extends Intent> extends Action<T> {
  469   /// Called when the action is to be performed.
  470   ///
  471   /// This is called by the [ActionDispatcher] when an action is invoked via
  472   /// [Actions.invoke], or when an action is invoked using
  473   /// [ActionDispatcher.invokeAction] directly.
  474   ///
  475   /// This method is only meant to be invoked by an [ActionDispatcher], or by
  476   /// its subclasses, and only when [isEnabled] is true.
  477   ///
  478   /// The optional `context` parameter is the context of the invocation of the
  479   /// action, and in the case of an action invoked by a [ShortcutManager], via
  480   /// a [Shortcuts] widget, will be the context of the [Shortcuts] widget.
  481   ///
  482   /// When overriding this method, the returned value can be any Object, but
  483   /// changing the return type of the override to match the type of the returned
  484   /// value provides more type safety.
  485   ///
  486   /// For instance, if an override of [invoke] returned an `int`, then it might
  487   /// be defined like so:
  488   ///
  489   /// ```dart
  490   /// class IncrementIntent extends Intent {
  491   ///   const IncrementIntent({required this.index});
  492   ///
  493   ///   final int index;
  494   /// }
  495   ///
  496   /// class MyIncrementAction extends ContextAction<IncrementIntent> {
  497   ///   @override
  498   ///   int invoke(IncrementIntent intent, [BuildContext? context]) {
  499   ///     return intent.index + 1;
  500   ///   }
  501   /// }
  502   /// ```
  503   @protected
  504   @override
  505   Object? invoke(T intent, [BuildContext? context]);
  506 
  507   @override
  508   ContextAction<T> _makeOverridableAction(BuildContext context) {
  509     return _OverridableContextAction<T>(defaultAction: this, lookupContext: context);
  510   }
  511 }
  512 
  513 /// The signature of a callback accepted by [CallbackAction].
  514 typedef OnInvokeCallback<T extends Intent> = Object? Function(T intent);
  515 
  516 /// An [Action] that takes a callback in order to configure it without having to
  517 /// create an explicit [Action] subclass just to call a callback.
  518 ///
  519 /// See also:
  520 ///
  521 ///  * [Shortcuts], which is a widget that contains a key map, in which it looks
  522 ///    up key combinations in order to invoke actions.
  523 ///  * [Actions], which is a widget that defines a map of [Intent] to [Action]
  524 ///    and allows redefining of actions for its descendants.
  525 ///  * [ActionDispatcher], a class that takes an [Action] and invokes it using a
  526 ///    [FocusNode] for context.
  527 class CallbackAction<T extends Intent> extends Action<T> {
  528   /// A constructor for a [CallbackAction].
  529   ///
  530   /// The `intentKey` and [onInvoke] parameters must not be null.
  531   /// The [onInvoke] parameter is required.
  532   CallbackAction({required this.onInvoke}) : assert(onInvoke != null);
  533 
  534   /// The callback to be called when invoked.
  535   ///
  536   /// Must not be null.
  537   @protected
  538   final OnInvokeCallback<T> onInvoke;
  539 
  540   @override
  541   Object? invoke(T intent) => onInvoke(intent);
  542 }
  543 
  544 /// An action dispatcher that simply invokes the actions given to it.
  545 ///
  546 /// See also:
  547 ///
  548 ///  - [ShortcutManager], that uses this class to invoke actions.
  549 ///  - [Shortcuts] widget, which defines key mappings to [Intent]s.
  550 ///  - [Actions] widget, which defines a mapping between a in [Intent] type and
  551 ///    an [Action].
  552 class ActionDispatcher with Diagnosticable {
  553   /// Creates an action dispatcher that invokes actions directly.
  554   const ActionDispatcher();
  555 
  556   /// Invokes the given `action`, passing it the given `intent`.
  557   ///
  558   /// The action will be invoked with the given `context`, if given, but only if
  559   /// the action is a [ContextAction] subclass. If no `context` is given, and
  560   /// the action is a [ContextAction], then the context from the [primaryFocus]
  561   /// is used.
  562   ///
  563   /// Returns the object returned from [Action.invoke].
  564   ///
  565   /// The caller must receive a `true` result from [Action.isEnabled] before
  566   /// calling this function. This function will assert if the action is not
  567   /// enabled when called.
  568   Object? invokeAction(
  569     covariant Action<Intent> action,
  570     covariant Intent intent, [
  571     BuildContext? context,
  572   ]) {
  573     assert(action != null);
  574     assert(intent != null);
  575     assert(action.isEnabled(intent), 'Action must be enabled when calling invokeAction');
  576     if (action is ContextAction) {
  577       context ??= primaryFocus?.context;
  578       return action.invoke(intent, context);
  579     } else {
  580       return action.invoke(intent);
  581     }
  582   }
  583 }
  584 
  585 /// A widget that establishes an [ActionDispatcher] and a map of [Intent] to
  586 /// [Action] to be used by its descendants when invoking an [Action].
  587 ///
  588 /// Actions are typically invoked using [Actions.invoke] with the context
  589 /// containing the ambient [Actions] widget.
  590 ///
  591 /// {@tool dartpad}
  592 /// This example creates a custom [Action] subclass `ModifyAction` for modifying
  593 /// a model, and another, `SaveAction` for saving it.
  594 ///
  595 /// This example demonstrates passing arguments to the [Intent] to be carried to
  596 /// the [Action]. Actions can get data either from their own construction (like
  597 /// the `model` in this example), or from the intent passed to them when invoked
  598 /// (like the increment `amount` in this example).
  599 ///
  600 /// This example also demonstrates how to use Intents to limit a widget's
  601 /// dependencies on its surroundings. The `SaveButton` widget defined in this
  602 /// example can invoke actions defined in its ancestor widgets, which can be
  603 /// customized to match the part of the widget tree that it is in. It doesn't
  604 /// need to know about the `SaveAction` class, only the `SaveIntent`, and it
  605 /// only needs to know about a value notifier, not the entire model.
  606 ///
  607 /// ** See code in examples/api/lib/widgets/actions/actions.0.dart **
  608 /// {@end-tool}
  609 ///
  610 /// See also:
  611 ///
  612 ///  * [ActionDispatcher], the object that this widget uses to manage actions.
  613 ///  * [Action], a class for containing and defining an invocation of a user
  614 ///    action.
  615 ///  * [Intent], a class that holds a unique [LocalKey] identifying an action,
  616 ///    as well as configuration information for running the [Action].
  617 ///  * [Shortcuts], a widget used to bind key combinations to [Intent]s.
  618 class Actions extends StatefulWidget {
  619   /// Creates an [Actions] widget.
  620   ///
  621   /// The [child], [actions], and [dispatcher] arguments must not be null.
  622   const Actions({
  623     super.key,
  624     this.dispatcher,
  625     required this.actions,
  626     required this.child,
  627   })  : assert(actions != null),
  628         assert(child != null);
  629 
  630   /// The [ActionDispatcher] object that invokes actions.
  631   ///
  632   /// This is what is returned from [Actions.of], and used by [Actions.invoke].
  633   ///
  634   /// If this [dispatcher] is null, then [Actions.of] and [Actions.invoke] will
  635   /// look up the tree until they find an Actions widget that has a dispatcher
  636   /// set. If not such widget is found, then they will return/use a
  637   /// default-constructed [ActionDispatcher].
  638   final ActionDispatcher? dispatcher;
  639 
  640   /// {@template flutter.widgets.actions.actions}
  641   /// A map of [Intent] keys to [Action<Intent>] objects that defines which
  642   /// actions this widget knows about.
  643   ///
  644   /// For performance reasons, it is recommended that a pre-built map is
  645   /// passed in here (e.g. a final variable from your widget class) instead of
  646   /// defining it inline in the build function.
  647   /// {@endtemplate}
  648   final Map<Type, Action<Intent>> actions;
  649 
  650   /// {@macro flutter.widgets.ProxyWidget.child}
  651   final Widget child;
  652 
  653   // Visits the Actions widget ancestors of the given element using
  654   // getElementForInheritedWidgetOfExactType. Returns true if the visitor found
  655   // what it was looking for.
  656   static bool _visitActionsAncestors(BuildContext context, bool Function(InheritedElement element) visitor) {
  657     InheritedElement? actionsElement = context.getElementForInheritedWidgetOfExactType<_ActionsMarker>();
  658     while (actionsElement != null) {
  659       if (visitor(actionsElement) == true) {
  660         break;
  661       }
  662       // _getParent is needed here because
  663       // context.getElementForInheritedWidgetOfExactType will return itself if it
  664       // happens to be of the correct type.
  665       final BuildContext parent = _getParent(actionsElement);
  666       actionsElement = parent.getElementForInheritedWidgetOfExactType<_ActionsMarker>();
  667     }
  668     return actionsElement != null;
  669   }
  670 
  671   // Finds the nearest valid ActionDispatcher, or creates a new one if it
  672   // doesn't find one.
  673   static ActionDispatcher _findDispatcher(BuildContext context) {
  674     ActionDispatcher? dispatcher;
  675     _visitActionsAncestors(context, (InheritedElement element) {
  676       final ActionDispatcher? found = (element.widget as _ActionsMarker).dispatcher;
  677       if (found != null) {
  678         dispatcher = found;
  679         return true;
  680       }
  681       return false;
  682     });
  683     return dispatcher ?? const ActionDispatcher();
  684   }
  685 
  686   /// Returns a [VoidCallback] handler that invokes the bound action for the
  687   /// given `intent` if the action is enabled, and returns null if the action is
  688   /// not enabled, or no matching action is found.
  689   ///
  690   /// This is intended to be used in widgets which have something similar to an
  691   /// `onTap` handler, which takes a `VoidCallback`, and can be set to the
  692   /// result of calling this function.
  693   ///
  694   /// Creates a dependency on the [Actions] widget that maps the bound action so
  695   /// that if the actions change, the context will be rebuilt and find the
  696   /// updated action.
  697   static VoidCallback? handler<T extends Intent>(BuildContext context, T intent) {
  698     final Action<T>? action = Actions.maybeFind<T>(context);
  699     if (action != null && action.isEnabled(intent)) {
  700       return () {
  701         // Could be that the action was enabled when the closure was created,
  702         // but is now no longer enabled, so check again.
  703         if (action.isEnabled(intent)) {
  704           Actions.of(context).invokeAction(action, intent, context);
  705         }
  706       };
  707     }
  708     return null;
  709   }
  710 
  711   /// Finds the [Action] bound to the given intent type `T` in the given `context`.
  712   ///
  713   /// Creates a dependency on the [Actions] widget that maps the bound action so
  714   /// that if the actions change, the context will be rebuilt and find the
  715   /// updated action.
  716   ///
  717   /// The optional `intent` argument supplies the type of the intent to look for
  718   /// if the concrete type of the intent sought isn't available. If not
  719   /// supplied, then `T` is used.
  720   ///
  721   /// If no [Actions] widget surrounds the given context, this function will
  722   /// assert in debug mode, and throw an exception in release mode.
  723   ///
  724   /// See also:
  725   ///
  726   ///  * [maybeFind], which is similar to this function, but will return null if
  727   ///    no [Actions] ancestor is found.
  728   static Action<T> find<T extends Intent>(BuildContext context, { T? intent }) {
  729     final Action<T>? action = maybeFind(context, intent: intent);
  730 
  731     assert(() {
  732       if (action == null) {
  733         final Type type = intent?.runtimeType ?? T;
  734         throw FlutterError(
  735           'Unable to find an action for a $type in an $Actions widget '
  736           'in the given context.\n'
  737           "$Actions.find() was called on a context that doesn't contain an "
  738           '$Actions widget with a mapping for the given intent type.\n'
  739           'The context used was:\n'
  740           '  $context\n'
  741           'The intent type requested was:\n'
  742           '  $type',
  743         );
  744       }
  745       return true;
  746     }());
  747     return action!;
  748   }
  749 
  750   /// Finds the [Action] bound to the given intent type `T` in the given `context`.
  751   ///
  752   /// Creates a dependency on the [Actions] widget that maps the bound action so
  753   /// that if the actions change, the context will be rebuilt and find the
  754   /// updated action.
  755   ///
  756   /// The optional `intent` argument supplies the type of the intent to look for
  757   /// if the concrete type of the intent sought isn't available. If not
  758   /// supplied, then `T` is used.
  759   ///
  760   /// If no [Actions] widget surrounds the given context, this function will
  761   /// return null.
  762   ///
  763   /// See also:
  764   ///
  765   ///  * [find], which is similar to this function, but will throw if
  766   ///    no [Actions] ancestor is found.
  767   static Action<T>? maybeFind<T extends Intent>(BuildContext context, { T? intent }) {
  768     Action<T>? action;
  769 
  770     // Specialize the type if a runtime example instance of the intent is given.
  771     // This allows this function to be called by code that doesn't know the
  772     // concrete type of the intent at compile time.
  773     final Type type = intent?.runtimeType ?? T;
  774     assert(
  775       type != Intent,
  776       'The type passed to "find" resolved to "Intent": either a non-Intent '
  777       'generic type argument or an example intent derived from Intent must be '
  778       'specified. Intent may be used as the generic type as long as the optional '
  779       '"intent" argument is passed.',
  780     );
  781 
  782     _visitActionsAncestors(context, (InheritedElement element) {
  783       final _ActionsMarker actions = element.widget as _ActionsMarker;
  784       final Action<T>? result = _castAction(actions, intent: intent);
  785       if (result != null) {
  786         context.dependOnInheritedElement(element);
  787         action = result;
  788         return true;
  789       }
  790       return false;
  791     });
  792 
  793     return action;
  794   }
  795 
  796   static Action<T>? _maybeFindWithoutDependingOn<T extends Intent>(BuildContext context, { T? intent }) {
  797     Action<T>? action;
  798 
  799     // Specialize the type if a runtime example instance of the intent is given.
  800     // This allows this function to be called by code that doesn't know the
  801     // concrete type of the intent at compile time.
  802     final Type type = intent?.runtimeType ?? T;
  803     assert(
  804       type != Intent,
  805       'The type passed to "find" resolved to "Intent": either a non-Intent '
  806       'generic type argument or an example intent derived from Intent must be '
  807       'specified. Intent may be used as the generic type as long as the optional '
  808       '"intent" argument is passed.',
  809     );
  810 
  811     _visitActionsAncestors(context, (InheritedElement element) {
  812       final _ActionsMarker actions = element.widget as _ActionsMarker;
  813       final Action<T>? result = _castAction(actions, intent: intent);
  814       if (result != null) {
  815         action = result;
  816         return true;
  817       }
  818       return false;
  819     });
  820 
  821     return action;
  822   }
  823 
  824   // Find the [Action] that handles the given `intent` in the given
  825   // `_ActionsMarker`, and verify it has the right type parameter.
  826   static Action<T>? _castAction<T extends Intent>(_ActionsMarker actionsMarker, { T? intent }) {
  827     final Action<Intent>? mappedAction = actionsMarker.actions[intent?.runtimeType ?? T];
  828     if (mappedAction is Action<T>?) {
  829       return mappedAction;
  830     } else {
  831       assert(
  832         false,
  833         '$T cannot be handled by an Action of runtime type ${mappedAction.runtimeType}.'
  834       );
  835       return null;
  836     }
  837   }
  838 
  839   /// Returns the [ActionDispatcher] associated with the [Actions] widget that
  840   /// most tightly encloses the given [BuildContext].
  841   ///
  842   /// Will return a newly created [ActionDispatcher] if no ambient [Actions]
  843   /// widget is found.
  844   static ActionDispatcher of(BuildContext context) {
  845     assert(context != null);
  846     final _ActionsMarker? marker = context.dependOnInheritedWidgetOfExactType<_ActionsMarker>();
  847     return marker?.dispatcher ?? _findDispatcher(context);
  848   }
  849 
  850   /// Invokes the action associated with the given [Intent] using the
  851   /// [Actions] widget that most tightly encloses the given [BuildContext].
  852   ///
  853   /// This method returns the result of invoking the action's [Action.invoke]
  854   /// method.
  855   ///
  856   /// The `context` and `intent` arguments must not be null.
  857   ///
  858   /// If the given `intent` doesn't map to an action, then it will look to the
  859   /// next ancestor [Actions] widget in the hierarchy until it reaches the root.
  860   ///
  861   /// This method will throw an exception if no ambient [Actions] widget is
  862   /// found, or when a suitable [Action] is found but it returns false for
  863   /// [Action.isEnabled].
  864   static Object? invoke<T extends Intent>(
  865     BuildContext context,
  866     T intent,
  867   ) {
  868     assert(intent != null);
  869     assert(context != null);
  870     Object? returnValue;
  871 
  872     final bool actionFound = _visitActionsAncestors(context, (InheritedElement element) {
  873       final _ActionsMarker actions = element.widget as _ActionsMarker;
  874       final Action<T>? result = _castAction(actions, intent: intent);
  875       if (result != null && result.isEnabled(intent)) {
  876         // Invoke the action we found using the relevant dispatcher from the Actions
  877         // Element we found.
  878         returnValue = _findDispatcher(element).invokeAction(result, intent, context);
  879       }
  880       return result != null;
  881     });
  882 
  883     assert(() {
  884       if (!actionFound) {
  885         throw FlutterError(
  886           'Unable to find an action for an Intent with type '
  887           '${intent.runtimeType} in an $Actions widget in the given context.\n'
  888           '$Actions.invoke() was unable to find an $Actions widget that '
  889           "contained a mapping for the given intent, or the intent type isn't the "
  890           'same as the type argument to invoke (which is $T - try supplying a '
  891           'type argument to invoke if one was not given)\n'
  892           'The context used was:\n'
  893           '  $context\n'
  894           'The intent type requested was:\n'
  895           '  ${intent.runtimeType}',
  896         );
  897       }
  898       return true;
  899     }());
  900     return returnValue;
  901   }
  902 
  903   /// Invokes the action associated with the given [Intent] using the
  904   /// [Actions] widget that most tightly encloses the given [BuildContext].
  905   ///
  906   /// This method returns the result of invoking the action's [Action.invoke]
  907   /// method. If no action mapping was found for the specified intent, or if the
  908   /// first action found was disabled, or the action itself returns null
  909   /// from [Action.invoke], then this method returns null.
  910   ///
  911   /// The `context` and `intent` arguments must not be null.
  912   ///
  913   /// If the given `intent` doesn't map to an action, then it will look to the
  914   /// next ancestor [Actions] widget in the hierarchy until it reaches the root.
  915   /// If a suitable [Action] is found but its [Action.isEnabled] returns false,
  916   /// the search will stop and this method will return null.
  917   static Object? maybeInvoke<T extends Intent>(
  918     BuildContext context,
  919     T intent,
  920   ) {
  921     assert(intent != null);
  922     assert(context != null);
  923     Object? returnValue;
  924 
  925     _visitActionsAncestors(context, (InheritedElement element) {
  926       final _ActionsMarker actions = element.widget as _ActionsMarker;
  927       final Action<T>? result = _castAction(actions, intent: intent);
  928       if (result != null && result.isEnabled(intent)) {
  929         // Invoke the action we found using the relevant dispatcher from the Actions
  930         // Element we found.
  931         returnValue = _findDispatcher(element).invokeAction(result, intent, context);
  932       }
  933       return result != null;
  934     });
  935     return returnValue;
  936   }
  937 
  938   @override
  939   State<Actions> createState() => _ActionsState();
  940 
  941   @override
  942   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  943     super.debugFillProperties(properties);
  944     properties.add(DiagnosticsProperty<ActionDispatcher>('dispatcher', dispatcher));
  945     properties.add(DiagnosticsProperty<Map<Type, Action<Intent>>>('actions', actions));
  946   }
  947 }
  948 
  949 class _ActionsState extends State<Actions> {
  950   // The set of actions that this Actions widget is current listening to.
  951   Set<Action<Intent>>? listenedActions = <Action<Intent>>{};
  952   // Used to tell the marker to rebuild its dependencies when the state of an
  953   // action in the map changes.
  954   Object rebuildKey = Object();
  955 
  956   @override
  957   void initState() {
  958     super.initState();
  959     _updateActionListeners();
  960   }
  961 
  962   void _handleActionChanged(Action<Intent> action) {
  963     // Generate a new key so that the marker notifies dependents.
  964     setState(() {
  965       rebuildKey = Object();
  966     });
  967   }
  968 
  969   void _updateActionListeners() {
  970     final Set<Action<Intent>> widgetActions = widget.actions.values.toSet();
  971     final Set<Action<Intent>> removedActions = listenedActions!.difference(widgetActions);
  972     final Set<Action<Intent>> addedActions = widgetActions.difference(listenedActions!);
  973 
  974     for (final Action<Intent> action in removedActions) {
  975       action.removeActionListener(_handleActionChanged);
  976     }
  977     for (final Action<Intent> action in addedActions) {
  978       action.addActionListener(_handleActionChanged);
  979     }
  980     listenedActions = widgetActions;
  981   }
  982 
  983   @override
  984   void didUpdateWidget(Actions oldWidget) {
  985     super.didUpdateWidget(oldWidget);
  986     _updateActionListeners();
  987   }
  988 
  989   @override
  990   void dispose() {
  991     super.dispose();
  992     for (final Action<Intent> action in listenedActions!) {
  993       action.removeActionListener(_handleActionChanged);
  994     }
  995     listenedActions = null;
  996   }
  997 
  998   @override
  999   Widget build(BuildContext context) {
 1000     return _ActionsMarker(
 1001       actions: widget.actions,
 1002       dispatcher: widget.dispatcher,
 1003       rebuildKey: rebuildKey,
 1004       child: widget.child,
 1005     );
 1006   }
 1007 }
 1008 
 1009 // An inherited widget used by Actions widget for fast lookup of the Actions
 1010 // widget information.
 1011 class _ActionsMarker extends InheritedWidget {
 1012   const _ActionsMarker({
 1013     required this.dispatcher,
 1014     required this.actions,
 1015     required this.rebuildKey,
 1016     required super.child,
 1017   })  : assert(child != null),
 1018         assert(actions != null);
 1019 
 1020   final ActionDispatcher? dispatcher;
 1021   final Map<Type, Action<Intent>> actions;
 1022   final Object rebuildKey;
 1023 
 1024   @override
 1025   bool updateShouldNotify(_ActionsMarker oldWidget) {
 1026     return rebuildKey != oldWidget.rebuildKey
 1027         || oldWidget.dispatcher != dispatcher
 1028         || !mapEquals<Type, Action<Intent>>(oldWidget.actions, actions);
 1029   }
 1030 }
 1031 
 1032 /// A widget that combines the functionality of [Actions], [Shortcuts],
 1033 /// [MouseRegion] and a [Focus] widget to create a detector that defines actions
 1034 /// and key bindings, and provides callbacks for handling focus and hover
 1035 /// highlights.
 1036 ///
 1037 /// {@youtube 560 315 https://www.youtube.com/watch?v=R84AGg0lKs8}
 1038 ///
 1039 /// This widget can be used to give a control the required detection modes for
 1040 /// focus and hover handling. It is most often used when authoring a new control
 1041 /// widget, and the new control should be enabled for keyboard traversal and
 1042 /// activation.
 1043 ///
 1044 /// {@tool dartpad}
 1045 /// This example shows how keyboard interaction can be added to a custom control
 1046 /// that changes color when hovered and focused, and can toggle a light when
 1047 /// activated, either by touch or by hitting the `X` key on the keyboard when
 1048 /// the "And Me" button has the keyboard focus (be sure to use TAB to move the
 1049 /// focus to the "And Me" button before trying it out).
 1050 ///
 1051 /// This example defines its own key binding for the `X` key, but in this case,
 1052 /// there is also a default key binding for [ActivateAction] in the default key
 1053 /// bindings created by [WidgetsApp] (the parent for [MaterialApp], and
 1054 /// [CupertinoApp]), so the `ENTER` key will also activate the buttons.
 1055 ///
 1056 /// ** See code in examples/api/lib/widgets/actions/focusable_action_detector.0.dart **
 1057 /// {@end-tool}
 1058 ///
 1059 /// This widget doesn't have any visual representation, it is just a detector that
 1060 /// provides focus and hover capabilities.
 1061 ///
 1062 /// It hosts its own [FocusNode] or uses [focusNode], if given.
 1063 class FocusableActionDetector extends StatefulWidget {
 1064   /// Create a const [FocusableActionDetector].
 1065   ///
 1066   /// The [enabled], [autofocus], [mouseCursor], and [child] arguments must not be null.
 1067   const FocusableActionDetector({
 1068     super.key,
 1069     this.enabled = true,
 1070     this.focusNode,
 1071     this.autofocus = false,
 1072     this.descendantsAreFocusable = true,
 1073     this.descendantsAreTraversable = true,
 1074     this.shortcuts,
 1075     this.actions,
 1076     this.onShowFocusHighlight,
 1077     this.onShowHoverHighlight,
 1078     this.onFocusChange,
 1079     this.mouseCursor = MouseCursor.defer,
 1080     this.includeFocusSemantics = true,
 1081     required this.child,
 1082   })  : assert(enabled != null),
 1083         assert(autofocus != null),
 1084         assert(mouseCursor != null),
 1085         assert(child != null);
 1086 
 1087   /// Is this widget enabled or not.
 1088   ///
 1089   /// If disabled, will not send any notifications needed to update highlight or
 1090   /// focus state, and will not define or respond to any actions or shortcuts.
 1091   ///
 1092   /// When disabled, adds [Focus] to the widget tree, but sets
 1093   /// [Focus.canRequestFocus] to false.
 1094   final bool enabled;
 1095 
 1096   /// {@macro flutter.widgets.Focus.focusNode}
 1097   final FocusNode? focusNode;
 1098 
 1099   /// {@macro flutter.widgets.Focus.autofocus}
 1100   final bool autofocus;
 1101 
 1102   /// {@macro flutter.widgets.Focus.descendantsAreFocusable}
 1103   final bool descendantsAreFocusable;
 1104 
 1105   /// {@macro flutter.widgets.Focus.descendantsAreTraversable}
 1106   final bool descendantsAreTraversable;
 1107 
 1108   /// {@macro flutter.widgets.actions.actions}
 1109   final Map<Type, Action<Intent>>? actions;
 1110 
 1111   /// {@macro flutter.widgets.shortcuts.shortcuts}
 1112   final Map<ShortcutActivator, Intent>? shortcuts;
 1113 
 1114   /// A function that will be called when the focus highlight should be shown or
 1115   /// hidden.
 1116   ///
 1117   /// This method is not triggered at the unmount of the widget.
 1118   final ValueChanged<bool>? onShowFocusHighlight;
 1119 
 1120   /// A function that will be called when the hover highlight should be shown or hidden.
 1121   ///
 1122   /// This method is not triggered at the unmount of the widget.
 1123   final ValueChanged<bool>? onShowHoverHighlight;
 1124 
 1125   /// A function that will be called when the focus changes.
 1126   ///
 1127   /// Called with true if the [focusNode] has primary focus.
 1128   final ValueChanged<bool>? onFocusChange;
 1129 
 1130   /// The cursor for a mouse pointer when it enters or is hovering over the
 1131   /// widget.
 1132   ///
 1133   /// The [mouseCursor] defaults to [MouseCursor.defer], deferring the choice of
 1134   /// cursor to the next region behind it in hit-test order.
 1135   final MouseCursor mouseCursor;
 1136 
 1137   /// Whether to include semantics from [Focus].
 1138   ///
 1139   /// Defaults to true.
 1140   final bool includeFocusSemantics;
 1141 
 1142   /// The child widget for this [FocusableActionDetector] widget.
 1143   ///
 1144   /// {@macro flutter.widgets.ProxyWidget.child}
 1145   final Widget child;
 1146 
 1147   @override
 1148   State<FocusableActionDetector> createState() => _FocusableActionDetectorState();
 1149 }
 1150 
 1151 class _FocusableActionDetectorState extends State<FocusableActionDetector> {
 1152   @override
 1153   void initState() {
 1154     super.initState();
 1155     SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
 1156       _updateHighlightMode(FocusManager.instance.highlightMode);
 1157     });
 1158     FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
 1159   }
 1160 
 1161   @override
 1162   void dispose() {
 1163     FocusManager.instance.removeHighlightModeListener(_handleFocusHighlightModeChange);
 1164     super.dispose();
 1165   }
 1166 
 1167   bool _canShowHighlight = false;
 1168   void _updateHighlightMode(FocusHighlightMode mode) {
 1169     _mayTriggerCallback(task: () {
 1170       switch (FocusManager.instance.highlightMode) {
 1171         case FocusHighlightMode.touch:
 1172           _canShowHighlight = false;
 1173           break;
 1174         case FocusHighlightMode.traditional:
 1175           _canShowHighlight = true;
 1176           break;
 1177       }
 1178     });
 1179   }
 1180 
 1181   // Have to have this separate from the _updateHighlightMode because it gets
 1182   // called in initState, where things aren't mounted yet.
 1183   // Since this method is a highlight mode listener, it is only called
 1184   // immediately following pointer events.
 1185   void _handleFocusHighlightModeChange(FocusHighlightMode mode) {
 1186     if (!mounted) {
 1187       return;
 1188     }
 1189     _updateHighlightMode(mode);
 1190   }
 1191 
 1192   bool _hovering = false;
 1193   void _handleMouseEnter(PointerEnterEvent event) {
 1194     if (!_hovering) {
 1195       _mayTriggerCallback(task: () {
 1196         _hovering = true;
 1197       });
 1198     }
 1199   }
 1200 
 1201   void _handleMouseExit(PointerExitEvent event) {
 1202     if (_hovering) {
 1203       _mayTriggerCallback(task: () {
 1204         _hovering = false;
 1205       });
 1206     }
 1207   }
 1208 
 1209   bool _focused = false;
 1210   void _handleFocusChange(bool focused) {
 1211     if (_focused != focused) {
 1212       _mayTriggerCallback(task: () {
 1213         _focused = focused;
 1214       });
 1215       widget.onFocusChange?.call(_focused);
 1216     }
 1217   }
 1218 
 1219   // Record old states, do `task` if not null, then compare old states with the
 1220   // new states, and trigger callbacks if necessary.
 1221   //
 1222   // The old states are collected from `oldWidget` if it is provided, or the
 1223   // current widget (before doing `task`) otherwise. The new states are always
 1224   // collected from the current widget.
 1225   void _mayTriggerCallback({VoidCallback? task, FocusableActionDetector? oldWidget}) {
 1226     bool shouldShowHoverHighlight(FocusableActionDetector target) {
 1227       return _hovering && target.enabled && _canShowHighlight;
 1228     }
 1229 
 1230     bool canRequestFocus(FocusableActionDetector target) {
 1231       final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional;
 1232       switch (mode) {
 1233         case NavigationMode.traditional:
 1234           return target.enabled;
 1235         case NavigationMode.directional:
 1236           return true;
 1237       }
 1238     }
 1239 
 1240     bool shouldShowFocusHighlight(FocusableActionDetector target) {
 1241       return _focused && _canShowHighlight && canRequestFocus(target);
 1242     }
 1243 
 1244     assert(SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks);
 1245     final FocusableActionDetector oldTarget = oldWidget ?? widget;
 1246     final bool didShowHoverHighlight = shouldShowHoverHighlight(oldTarget);
 1247     final bool didShowFocusHighlight = shouldShowFocusHighlight(oldTarget);
 1248     if (task != null) {
 1249       task();
 1250     }
 1251     final bool doShowHoverHighlight = shouldShowHoverHighlight(widget);
 1252     final bool doShowFocusHighlight = shouldShowFocusHighlight(widget);
 1253     if (didShowFocusHighlight != doShowFocusHighlight) {
 1254       widget.onShowFocusHighlight?.call(doShowFocusHighlight);
 1255     }
 1256     if (didShowHoverHighlight != doShowHoverHighlight) {
 1257       widget.onShowHoverHighlight?.call(doShowHoverHighlight);
 1258     }
 1259   }
 1260 
 1261   @override
 1262   void didUpdateWidget(FocusableActionDetector oldWidget) {
 1263     super.didUpdateWidget(oldWidget);
 1264     if (widget.enabled != oldWidget.enabled) {
 1265       SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
 1266         _mayTriggerCallback(oldWidget: oldWidget);
 1267       });
 1268     }
 1269   }
 1270 
 1271   bool get _canRequestFocus {
 1272     final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional;
 1273     switch (mode) {
 1274       case NavigationMode.traditional:
 1275         return widget.enabled;
 1276       case NavigationMode.directional:
 1277         return true;
 1278     }
 1279   }
 1280 
 1281   // This global key is needed to keep only the necessary widgets in the tree
 1282   // while maintaining the subtree's state.
 1283   //
 1284   // See https://github.com/flutter/flutter/issues/64058 for an explanation of
 1285   // why using a global key over keeping the shape of the tree.
 1286   final GlobalKey _mouseRegionKey = GlobalKey();
 1287 
 1288   @override
 1289   Widget build(BuildContext context) {
 1290     Widget child = MouseRegion(
 1291       key: _mouseRegionKey,
 1292       onEnter: _handleMouseEnter,
 1293       onExit: _handleMouseExit,
 1294       cursor: widget.mouseCursor,
 1295       child: Focus(
 1296         focusNode: widget.focusNode,
 1297         autofocus: widget.autofocus,
 1298         descendantsAreFocusable: widget.descendantsAreFocusable,
 1299         descendantsAreTraversable: widget.descendantsAreTraversable,
 1300         canRequestFocus: _canRequestFocus,
 1301         onFocusChange: _handleFocusChange,
 1302         includeSemantics: widget.includeFocusSemantics,
 1303         child: widget.child,
 1304       ),
 1305     );
 1306     if (widget.enabled && widget.actions != null && widget.actions!.isNotEmpty) {
 1307       child = Actions(actions: widget.actions!, child: child);
 1308     }
 1309     if (widget.enabled && widget.shortcuts != null && widget.shortcuts!.isNotEmpty) {
 1310       child = Shortcuts(shortcuts: widget.shortcuts!, child: child);
 1311     }
 1312     return child;
 1313   }
 1314 }
 1315 
 1316 /// An [Intent] that keeps a [VoidCallback] to be invoked by a
 1317 /// [VoidCallbackAction] when it receives this intent.
 1318 class VoidCallbackIntent extends Intent {
 1319   /// Creates a [VoidCallbackIntent].
 1320   const VoidCallbackIntent(this.callback);
 1321 
 1322   /// The callback that is to be called by the [VoidCallbackAction] that
 1323   /// receives this intent.
 1324   final VoidCallback callback;
 1325 }
 1326 
 1327 /// An [Action] that invokes the [VoidCallback] given to it in the
 1328 /// [VoidCallbackIntent] passed to it when invoked.
 1329 ///
 1330 /// See also:
 1331 ///
 1332 ///  * [CallbackAction], which is an action that will invoke a callback with the
 1333 ///    intent passed to the action's invoke method. The callback is configured
 1334 ///    on the action, not the intent, like this class.
 1335 class VoidCallbackAction extends Action<VoidCallbackIntent> {
 1336   @override
 1337   Object? invoke(VoidCallbackIntent intent) {
 1338     intent.callback();
 1339     return null;
 1340   }
 1341 }
 1342 
 1343 /// An [Intent] that is bound to a [DoNothingAction].
 1344 ///
 1345 /// Attaching a [DoNothingIntent] to a [Shortcuts] mapping is one way to disable
 1346 /// a keyboard shortcut defined by a widget higher in the widget hierarchy and
 1347 /// consume any key event that triggers it via a shortcut.
 1348 ///
 1349 /// This intent cannot be subclassed.
 1350 ///
 1351 /// See also:
 1352 ///
 1353 ///  * [DoNothingAndStopPropagationIntent], a similar intent that will not
 1354 ///    handle the key event, but will still keep it from being passed to other key
 1355 ///    handlers in the focus chain.
 1356 class DoNothingIntent extends Intent {
 1357   /// Creates a const [DoNothingIntent].
 1358   const factory DoNothingIntent() = DoNothingIntent._;
 1359 
 1360   // Make DoNothingIntent constructor private so it can't be subclassed.
 1361   const DoNothingIntent._();
 1362 }
 1363 
 1364 /// An [Intent] that is bound to a [DoNothingAction], but, in addition to not
 1365 /// performing an action, also stops the propagation of the key event bound to
 1366 /// this intent to other key event handlers in the focus chain.
 1367 ///
 1368 /// Attaching a [DoNothingAndStopPropagationIntent] to a [Shortcuts.shortcuts]
 1369 /// mapping is one way to disable a keyboard shortcut defined by a widget higher
 1370 /// in the widget hierarchy. In addition, the bound [DoNothingAction] will
 1371 /// return false from [DoNothingAction.consumesKey], causing the key bound to
 1372 /// this intent to be passed on to the platform embedding as "not handled" with
 1373 /// out passing it to other key handlers in the focus chain (e.g. parent
 1374 /// `Shortcuts` widgets higher up in the chain).
 1375 ///
 1376 /// This intent cannot be subclassed.
 1377 ///
 1378 /// See also:
 1379 ///
 1380 ///  * [DoNothingIntent], a similar intent that will handle the key event.
 1381 class DoNothingAndStopPropagationIntent extends Intent {
 1382   /// Creates a const [DoNothingAndStopPropagationIntent].
 1383   const factory DoNothingAndStopPropagationIntent() = DoNothingAndStopPropagationIntent._;
 1384 
 1385   // Make DoNothingAndStopPropagationIntent constructor private so it can't be subclassed.
 1386   const DoNothingAndStopPropagationIntent._();
 1387 }
 1388 
 1389 /// An [Action] that doesn't perform any action when invoked.
 1390 ///
 1391 /// Attaching a [DoNothingAction] to an [Actions.actions] mapping is a way to
 1392 /// disable an action defined by a widget higher in the widget hierarchy.
 1393 ///
 1394 /// If [consumesKey] returns false, then not only will this action do nothing,
 1395 /// but it will stop the propagation of the key event used to trigger it to
 1396 /// other widgets in the focus chain and tell the embedding that the key wasn't
 1397 /// handled, allowing text input fields or other non-Flutter elements to receive
 1398 /// that key event. The return value of [consumesKey] can be set via the
 1399 /// `consumesKey` argument to the constructor.
 1400 ///
 1401 /// This action can be bound to any [Intent].
 1402 ///
 1403 /// See also:
 1404 ///  - [DoNothingIntent], which is an intent that can be bound to a [KeySet] in
 1405 ///    a [Shortcuts] widget to do nothing.
 1406 ///  - [DoNothingAndStopPropagationIntent], which is an intent that can be bound
 1407 ///    to a [KeySet] in a [Shortcuts] widget to do nothing and also stop key event
 1408 ///    propagation to other key handlers in the focus chain.
 1409 class DoNothingAction extends Action<Intent> {
 1410   /// Creates a [DoNothingAction].
 1411   ///
 1412   /// The optional [consumesKey] argument defaults to true.
 1413   DoNothingAction({bool consumesKey = true}) : _consumesKey = consumesKey;
 1414 
 1415   @override
 1416   bool consumesKey(Intent intent) => _consumesKey;
 1417   final bool _consumesKey;
 1418 
 1419   @override
 1420   void invoke(Intent intent) {}
 1421 }
 1422 
 1423 /// An [Intent] that activates the currently focused control.
 1424 ///
 1425 /// This intent is bound by default to the [LogicalKeyboardKey.space] key on all
 1426 /// platforms, and also to the [LogicalKeyboardKey.enter] key on all platforms
 1427 /// except the web, where ENTER doesn't toggle selection. On the web, ENTER is
 1428 /// bound to [ButtonActivateIntent] instead.
 1429 ///
 1430 /// See also:
 1431 ///
 1432 ///  * [WidgetsApp.defaultShortcuts], which contains the default shortcuts used
 1433 ///    in apps.
 1434 ///  * [WidgetsApp.shortcuts], which defines the shortcuts to use in an
 1435 ///    application (and defaults to [WidgetsApp.defaultShortcuts]).
 1436 class ActivateIntent extends Intent {
 1437   /// Creates an intent that activates the currently focused control.
 1438   const ActivateIntent();
 1439 }
 1440 
 1441 /// An [Intent] that activates the currently focused button.
 1442 ///
 1443 /// This intent is bound by default to the [LogicalKeyboardKey.enter] key on the
 1444 /// web, where ENTER can be used to activate buttons, but not toggle selection.
 1445 /// All other platforms bind [LogicalKeyboardKey.enter] to [ActivateIntent].
 1446 ///
 1447 /// See also:
 1448 ///
 1449 ///  * [WidgetsApp.defaultShortcuts], which contains the default shortcuts used
 1450 ///    in apps.
 1451 ///  * [WidgetsApp.shortcuts], which defines the shortcuts to use in an
 1452 ///    application (and defaults to [WidgetsApp.defaultShortcuts]).
 1453 class ButtonActivateIntent extends Intent {
 1454   /// Creates an intent that the currently focused control, if it's a button.
 1455   const ButtonActivateIntent();
 1456 }
 1457 
 1458 /// An [Action] that activates the currently focused control.
 1459 ///
 1460 /// This is an abstract class that serves as a base class for actions that
 1461 /// activate a control. By default, is bound to [LogicalKeyboardKey.enter],
 1462 /// [LogicalKeyboardKey.gameButtonA], and [LogicalKeyboardKey.space] in the
 1463 /// default keyboard map in [WidgetsApp].
 1464 abstract class ActivateAction extends Action<ActivateIntent> { }
 1465 
 1466 /// An [Intent] that selects the currently focused control.
 1467 class SelectIntent extends Intent {
 1468   /// Creates an intent that selects the currently focused control.
 1469   const SelectIntent();
 1470 }
 1471 
 1472 /// An action that selects the currently focused control.
 1473 ///
 1474 /// This is an abstract class that serves as a base class for actions that
 1475 /// select something. It is not bound to any key by default.
 1476 abstract class SelectAction extends Action<SelectIntent> { }
 1477 
 1478 /// An [Intent] that dismisses the currently focused widget.
 1479 ///
 1480 /// The [WidgetsApp.defaultShortcuts] binds this intent to the
 1481 /// [LogicalKeyboardKey.escape] and [LogicalKeyboardKey.gameButtonB] keys.
 1482 ///
 1483 /// See also:
 1484 ///  - [ModalRoute] which listens for this intent to dismiss modal routes
 1485 ///    (dialogs, pop-up menus, drawers, etc).
 1486 class DismissIntent extends Intent {
 1487   /// Creates an intent that dismisses the currently focused widget.
 1488   const DismissIntent();
 1489 }
 1490 
 1491 /// An [Action] that dismisses the focused widget.
 1492 ///
 1493 /// This is an abstract class that serves as a base class for dismiss actions.
 1494 abstract class DismissAction extends Action<DismissIntent> { }
 1495 
 1496 /// An [Intent] that evaluates a series of specified [orderedIntents] for
 1497 /// execution.
 1498 ///
 1499 /// The first intent that matches an enabled action is used.
 1500 class PrioritizedIntents extends Intent {
 1501   /// Creates an intent that is used with [PrioritizedAction] to specify a list
 1502   /// of intents, the first available of which will be used.
 1503   const PrioritizedIntents({
 1504     required this.orderedIntents,
 1505   })  : assert(orderedIntents != null);
 1506 
 1507   /// List of intents to be evaluated in order for execution. When an
 1508   /// [Action.isEnabled] returns true, that action will be invoked and
 1509   /// progression through the ordered intents stops.
 1510   final List<Intent> orderedIntents;
 1511 }
 1512 
 1513 /// An [Action] that iterates through a list of [Intent]s, invoking the first
 1514 /// that is enabled.
 1515 class PrioritizedAction extends Action<PrioritizedIntents> {
 1516   late Action<dynamic> _selectedAction;
 1517   late Intent _selectedIntent;
 1518 
 1519   @override
 1520   bool isEnabled(PrioritizedIntents intent) {
 1521     final FocusNode? focus = primaryFocus;
 1522     if  (focus == null || focus.context == null) {
 1523       return false;
 1524     }
 1525     for (final Intent candidateIntent in intent.orderedIntents) {
 1526       final Action<Intent>? candidateAction = Actions.maybeFind<Intent>(
 1527         focus.context!,
 1528         intent: candidateIntent,
 1529       );
 1530       if (candidateAction != null && candidateAction.isEnabled(candidateIntent)) {
 1531         _selectedAction = candidateAction;
 1532         _selectedIntent = candidateIntent;
 1533         return true;
 1534       }
 1535     }
 1536     return false;
 1537   }
 1538 
 1539   @override
 1540   void invoke(PrioritizedIntents intent) {
 1541     assert(_selectedAction != null);
 1542     assert(_selectedIntent != null);
 1543     _selectedAction.invoke(_selectedIntent);
 1544   }
 1545 }
 1546 
 1547 mixin _OverridableActionMixin<T extends Intent> on Action<T> {
 1548   // When debugAssertMutuallyRecursive is true, this action will throw an
 1549   // assertion error when the override calls this action's "invoke" method and
 1550   // the override is already being invoked from within the "invoke" method.
 1551   bool debugAssertMutuallyRecursive = false;
 1552   bool debugAssertIsActionEnabledMutuallyRecursive = false;
 1553   bool debugAssertIsEnabledMutuallyRecursive = false;
 1554   bool debugAssertConsumeKeyMutuallyRecursive = false;
 1555 
 1556   // The default action to invoke if an enabled override Action can't be found
 1557   // using [lookupContext];
 1558   Action<T> get defaultAction;
 1559 
 1560   // The [BuildContext] used to find the override of this [Action].
 1561   BuildContext get lookupContext;
 1562 
 1563   // How to invoke [defaultAction], given the caller [fromAction].
 1564   Object? invokeDefaultAction(T intent, Action<T>? fromAction, BuildContext? context);
 1565 
 1566   Action<T>? getOverrideAction({ bool declareDependency = false }) {
 1567     final Action<T>? override = declareDependency
 1568      ? Actions.maybeFind(lookupContext)
 1569      : Actions._maybeFindWithoutDependingOn(lookupContext);
 1570     assert(!identical(override, this));
 1571     return override;
 1572   }
 1573 
 1574   @override
 1575   void _updateCallingAction(Action<T>? value) {
 1576     super._updateCallingAction(value);
 1577     defaultAction._updateCallingAction(value);
 1578   }
 1579 
 1580   Object? _invokeOverride(Action<T> overrideAction, T intent, BuildContext? context) {
 1581     assert(!debugAssertMutuallyRecursive);
 1582     assert(() {
 1583       debugAssertMutuallyRecursive = true;
 1584       return true;
 1585     }());
 1586     overrideAction._updateCallingAction(defaultAction);
 1587     final Object? returnValue = overrideAction is ContextAction<T>
 1588       ? overrideAction.invoke(intent, context)
 1589       : overrideAction.invoke(intent);
 1590     overrideAction._updateCallingAction(null);
 1591     assert(() {
 1592       debugAssertMutuallyRecursive = false;
 1593       return true;
 1594     }());
 1595     return returnValue;
 1596   }
 1597 
 1598   @override
 1599   Object? invoke(T intent, [BuildContext? context]) {
 1600     final Action<T>? overrideAction = getOverrideAction();
 1601     final Object? returnValue = overrideAction == null
 1602       ? invokeDefaultAction(intent, callingAction, context)
 1603       : _invokeOverride(overrideAction, intent, context);
 1604     return returnValue;
 1605   }
 1606 
 1607   bool isOverrideActionEnabled(Action<T> overrideAction) {
 1608     assert(!debugAssertIsActionEnabledMutuallyRecursive);
 1609     assert(() {
 1610       debugAssertIsActionEnabledMutuallyRecursive = true;
 1611       return true;
 1612     }());
 1613     overrideAction._updateCallingAction(defaultAction);
 1614     final bool isOverrideEnabled = overrideAction.isActionEnabled;
 1615     overrideAction._updateCallingAction(null);
 1616     assert(() {
 1617       debugAssertIsActionEnabledMutuallyRecursive = false;
 1618       return true;
 1619     }());
 1620     return isOverrideEnabled;
 1621   }
 1622 
 1623   @override
 1624   bool get isActionEnabled {
 1625     final Action<T>? overrideAction = getOverrideAction(declareDependency: true);
 1626     final bool returnValue = overrideAction != null
 1627       ? isOverrideActionEnabled(overrideAction)
 1628       : defaultAction.isActionEnabled;
 1629     return returnValue;
 1630   }
 1631 
 1632   @override
 1633   bool isEnabled(T intent) {
 1634     assert(!debugAssertIsEnabledMutuallyRecursive);
 1635     assert(() {
 1636       debugAssertIsEnabledMutuallyRecursive = true;
 1637       return true;
 1638     }());
 1639 
 1640     final Action<T>? overrideAction = getOverrideAction();
 1641     overrideAction?._updateCallingAction(defaultAction);
 1642     final bool returnValue = (overrideAction ?? defaultAction).isEnabled(intent);
 1643     overrideAction?._updateCallingAction(null);
 1644     assert(() {
 1645       debugAssertIsEnabledMutuallyRecursive = false;
 1646       return true;
 1647     }());
 1648     return returnValue;
 1649   }
 1650 
 1651   @override
 1652   bool consumesKey(T intent) {
 1653     assert(!debugAssertConsumeKeyMutuallyRecursive);
 1654     assert(() {
 1655       debugAssertConsumeKeyMutuallyRecursive = true;
 1656       return true;
 1657     }());
 1658     final Action<T>? overrideAction = getOverrideAction();
 1659     overrideAction?._updateCallingAction(defaultAction);
 1660     final bool isEnabled = (overrideAction ?? defaultAction).consumesKey(intent);
 1661     overrideAction?._updateCallingAction(null);
 1662     assert(() {
 1663       debugAssertConsumeKeyMutuallyRecursive = false;
 1664       return true;
 1665     }());
 1666     return isEnabled;
 1667   }
 1668 
 1669   @override
 1670   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
 1671     super.debugFillProperties(properties);
 1672     properties.add(DiagnosticsProperty<Action<T>>('defaultAction', defaultAction));
 1673   }
 1674 }
 1675 
 1676 class _OverridableAction<T extends Intent> extends ContextAction<T> with _OverridableActionMixin<T> {
 1677   _OverridableAction({ required this.defaultAction, required this.lookupContext }) ;
 1678 
 1679   @override
 1680   final Action<T> defaultAction;
 1681 
 1682   @override
 1683   final BuildContext lookupContext;
 1684 
 1685   @override
 1686   Object? invokeDefaultAction(T intent, Action<T>? fromAction, BuildContext? context) {
 1687     if (fromAction == null) {
 1688       return defaultAction.invoke(intent);
 1689     } else {
 1690       final Object? returnValue = defaultAction.invoke(intent);
 1691       return returnValue;
 1692     }
 1693   }
 1694 
 1695   @override
 1696   ContextAction<T> _makeOverridableAction(BuildContext context) {
 1697     return _OverridableAction<T>(defaultAction: defaultAction, lookupContext: context);
 1698   }
 1699 }
 1700 
 1701 class _OverridableContextAction<T extends Intent> extends ContextAction<T> with _OverridableActionMixin<T> {
 1702   _OverridableContextAction({ required this.defaultAction, required this.lookupContext });
 1703 
 1704   @override
 1705   final ContextAction<T> defaultAction;
 1706 
 1707   @override
 1708   final BuildContext lookupContext;
 1709 
 1710   @override
 1711   Object? _invokeOverride(Action<T> overrideAction, T intent, BuildContext? context) {
 1712     assert(context != null);
 1713     assert(!debugAssertMutuallyRecursive);
 1714     assert(() {
 1715       debugAssertMutuallyRecursive = true;
 1716       return true;
 1717     }());
 1718 
 1719     // Wrap the default Action together with the calling context in case
 1720     // overrideAction is not a ContextAction and thus have no access to the
 1721     // calling BuildContext.
 1722     final Action<T> wrappedDefault = _ContextActionToActionAdapter<T>(invokeContext: context!, action: defaultAction);
 1723     overrideAction._updateCallingAction(wrappedDefault);
 1724     final Object? returnValue = overrideAction is ContextAction<T>
 1725       ? overrideAction.invoke(intent, context)
 1726       : overrideAction.invoke(intent);
 1727     overrideAction._updateCallingAction(null);
 1728 
 1729     assert(() {
 1730       debugAssertMutuallyRecursive = false;
 1731       return true;
 1732     }());
 1733     return returnValue;
 1734   }
 1735 
 1736   @override
 1737   Object? invokeDefaultAction(T intent, Action<T>? fromAction, BuildContext? context) {
 1738     if (fromAction == null) {
 1739       return defaultAction.invoke(intent, context);
 1740     } else {
 1741       final Object? returnValue = defaultAction.invoke(intent, context);
 1742       return returnValue;
 1743     }
 1744   }
 1745 
 1746   @override
 1747   ContextAction<T> _makeOverridableAction(BuildContext context) {
 1748     return _OverridableContextAction<T>(defaultAction: defaultAction, lookupContext: context);
 1749   }
 1750 }
 1751 
 1752 class _ContextActionToActionAdapter<T extends Intent> extends Action<T> {
 1753   _ContextActionToActionAdapter({required this.invokeContext, required this.action});
 1754 
 1755   final BuildContext invokeContext;
 1756   final ContextAction<T> action;
 1757 
 1758   @override
 1759   void _updateCallingAction(Action<T>? value) {
 1760     action._updateCallingAction(value);
 1761   }
 1762 
 1763   @override
 1764   Action<T>? get callingAction => action.callingAction;
 1765 
 1766   @override
 1767   bool isEnabled(T intent) => action.isEnabled(intent);
 1768 
 1769   @override
 1770   bool get isActionEnabled => action.isActionEnabled;
 1771 
 1772   @override
 1773   bool consumesKey(T intent) => action.consumesKey(intent);
 1774 
 1775   @override
 1776   void addActionListener(ActionListenerCallback listener) {
 1777     super.addActionListener(listener);
 1778     action.addActionListener(listener);
 1779   }
 1780 
 1781   @override
 1782   void removeActionListener(ActionListenerCallback listener) {
 1783     super.removeActionListener(listener);
 1784     action.removeActionListener(listener);
 1785   }
 1786 
 1787   @override
 1788   @protected
 1789   void notifyActionListeners() => action.notifyActionListeners();
 1790 
 1791   @override
 1792   Object? invoke(T intent) => action.invoke(intent, invokeContext);
 1793 }