Unverified Commit 0f68b46f authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Revise Action API (#42940)

This updates the Action API in accordance with the design doc for the changes: flutter.dev/go/actions-and-shortcuts-design-revision

Fixes #53276
parent c663cd55
This diff is collapsed.
...@@ -213,7 +213,7 @@ class CupertinoApp extends StatefulWidget { ...@@ -213,7 +213,7 @@ class CupertinoApp extends StatefulWidget {
/// return WidgetsApp( /// return WidgetsApp(
/// shortcuts: <LogicalKeySet, Intent>{ /// shortcuts: <LogicalKeySet, Intent>{
/// ... WidgetsApp.defaultShortcuts, /// ... WidgetsApp.defaultShortcuts,
/// LogicalKeySet(LogicalKeyboardKey.select): const Intent(ActivateAction.key), /// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
/// }, /// },
/// color: const Color(0xFFFF0000), /// color: const Color(0xFFFF0000),
/// builder: (BuildContext context, Widget child) { /// builder: (BuildContext context, Widget child) {
...@@ -239,12 +239,12 @@ class CupertinoApp extends StatefulWidget { ...@@ -239,12 +239,12 @@ class CupertinoApp extends StatefulWidget {
/// ```dart /// ```dart
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return WidgetsApp( /// return WidgetsApp(
/// actions: <LocalKey, ActionFactory>{ /// actions: <Type, Action<Intent>>{
/// ... WidgetsApp.defaultActions, /// ... WidgetsApp.defaultActions,
/// ActivateAction.key: () => CallbackAction( /// ActivateAction: CallbackAction(
/// ActivateAction.key, /// onInvoke: (Intent intent) {
/// onInvoke: (FocusNode focusNode, Intent intent) {
/// // Do something here... /// // Do something here...
/// return null;
/// }, /// },
/// ), /// ),
/// }, /// },
...@@ -257,7 +257,7 @@ class CupertinoApp extends StatefulWidget { ...@@ -257,7 +257,7 @@ class CupertinoApp extends StatefulWidget {
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso} /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final Map<LocalKey, ActionFactory> actions; final Map<Type, Action<Intent>> actions;
@override @override
_CupertinoAppState createState() => _CupertinoAppState(); _CupertinoAppState createState() => _CupertinoAppState();
......
...@@ -183,9 +183,9 @@ class ChangeNotifier implements Listenable { ...@@ -183,9 +183,9 @@ class ChangeNotifier implements Listenable {
/// Call all the registered listeners. /// Call all the registered listeners.
/// ///
/// Call this method whenever the object changes, to notify any clients the /// Call this method whenever the object changes, to notify any clients the
/// object may have. Listeners that are added during this iteration will not /// object may have changed. Listeners that are added during this iteration
/// be visited. Listeners that are removed during this iteration will not be /// will not be visited. Listeners that are removed during this iteration will
/// visited after they are removed. /// not be visited after they are removed.
/// ///
/// Exceptions thrown by listeners will be caught and reported using /// Exceptions thrown by listeners will be caught and reported using
/// [FlutterError.reportError]. /// [FlutterError.reportError].
......
...@@ -476,7 +476,7 @@ class MaterialApp extends StatefulWidget { ...@@ -476,7 +476,7 @@ class MaterialApp extends StatefulWidget {
/// return WidgetsApp( /// return WidgetsApp(
/// shortcuts: <LogicalKeySet, Intent>{ /// shortcuts: <LogicalKeySet, Intent>{
/// ... WidgetsApp.defaultShortcuts, /// ... WidgetsApp.defaultShortcuts,
/// LogicalKeySet(LogicalKeyboardKey.select): const Intent(ActivateAction.key), /// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
/// }, /// },
/// color: const Color(0xFFFF0000), /// color: const Color(0xFFFF0000),
/// builder: (BuildContext context, Widget child) { /// builder: (BuildContext context, Widget child) {
...@@ -502,12 +502,12 @@ class MaterialApp extends StatefulWidget { ...@@ -502,12 +502,12 @@ class MaterialApp extends StatefulWidget {
/// ```dart /// ```dart
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return WidgetsApp( /// return WidgetsApp(
/// actions: <LocalKey, ActionFactory>{ /// actions: <Type, Action<Intent>>{
/// ... WidgetsApp.defaultActions, /// ... WidgetsApp.defaultActions,
/// ActivateAction.key: () => CallbackAction( /// ActivateAction: CallbackAction(
/// ActivateAction.key, /// onInvoke: (Intent intent) {
/// onInvoke: (FocusNode focusNode, Intent intent) {
/// // Do something here... /// // Do something here...
/// return null;
/// }, /// },
/// ), /// ),
/// }, /// },
...@@ -520,7 +520,7 @@ class MaterialApp extends StatefulWidget { ...@@ -520,7 +520,7 @@ class MaterialApp extends StatefulWidget {
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso} /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final Map<LocalKey, ActionFactory> actions; final Map<Type, Action<Intent>> actions;
/// Turns on a [GridPaper] overlay that paints a baseline grid /// Turns on a [GridPaper] overlay that paints a baseline grid
/// Material apps. /// Material apps.
......
...@@ -169,17 +169,17 @@ class Checkbox extends StatefulWidget { ...@@ -169,17 +169,17 @@ class Checkbox extends StatefulWidget {
class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin { class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
bool get enabled => widget.onChanged != null; bool get enabled => widget.onChanged != null;
Map<LocalKey, ActionFactory> _actionMap; Map<Type, Action<Intent>> _actionMap;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_actionMap = <LocalKey, ActionFactory>{ _actionMap = <Type, Action<Intent>>{
ActivateAction.key: _createAction, ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: _actionHandler),
}; };
} }
void _actionHandler(FocusNode node, Intent intent){ void _actionHandler(ActivateIntent intent) {
if (widget.onChanged != null) { if (widget.onChanged != null) {
switch (widget.value) { switch (widget.value) {
case false: case false:
...@@ -193,17 +193,10 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin { ...@@ -193,17 +193,10 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
break; break;
} }
} }
final RenderObject renderObject = node.context.findRenderObject(); final RenderObject renderObject = context.findRenderObject();
renderObject.sendSemanticsEvent(const TapSemanticEvent()); renderObject.sendSemanticsEvent(const TapSemanticEvent());
} }
Action _createAction() {
return CallbackAction(
ActivateAction.key,
onInvoke: _actionHandler,
);
}
bool _focused = false; bool _focused = false;
void _handleFocusHighlightChanged(bool focused) { void _handleFocusHighlightChanged(bool focused) {
if (focused != _focused) { if (focused != _focused) {
......
...@@ -158,7 +158,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>> ...@@ -158,7 +158,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
} }
static final Map<LogicalKeySet, Intent> _webShortcuts =<LogicalKeySet, Intent>{ static final Map<LogicalKeySet, Intent> _webShortcuts =<LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.enter): const ActivateIntent(),
}; };
@override @override
...@@ -1080,7 +1080,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1080,7 +1080,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
FocusNode _internalNode; FocusNode _internalNode;
FocusNode get focusNode => widget.focusNode ?? _internalNode; FocusNode get focusNode => widget.focusNode ?? _internalNode;
bool _hasPrimaryFocus = false; bool _hasPrimaryFocus = false;
Map<LocalKey, ActionFactory> _actionMap; Map<Type, Action<Intent>> _actionMap;
FocusHighlightMode _focusHighlightMode; FocusHighlightMode _focusHighlightMode;
// Only used if needed to create _internalNode. // Only used if needed to create _internalNode.
...@@ -1095,8 +1095,10 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1095,8 +1095,10 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
if (widget.focusNode == null) { if (widget.focusNode == null) {
_internalNode ??= _createFocusNode(); _internalNode ??= _createFocusNode();
} }
_actionMap = <LocalKey, ActionFactory>{ _actionMap = <Type, Action<Intent>>{
ActivateAction.key: _createAction, ActivateIntent: CallbackAction<ActivateIntent>(
onInvoke: (ActivateIntent intent) => _handleTap(),
),
}; };
focusNode.addListener(_handleFocusChanged); focusNode.addListener(_handleFocusChanged);
final FocusManager focusManager = WidgetsBinding.instance.focusManager; final FocusManager focusManager = WidgetsBinding.instance.focusManager;
...@@ -1225,15 +1227,6 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1225,15 +1227,6 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
} }
} }
Action _createAction() {
return CallbackAction(
ActivateAction.key,
onInvoke: (FocusNode node, Intent intent) {
_handleTap();
},
);
}
// When isDense is true, reduce the height of this button from _kMenuItemHeight to // When isDense is true, reduce the height of this button from _kMenuItemHeight to
// _kDenseButtonHeight, but don't make it smaller than the text that it contains. // _kDenseButtonHeight, but don't make it smaller than the text that it contains.
// Similarly, we don't reduce the height of the button so much that its icon // Similarly, we don't reduce the height of the button so much that its icon
......
...@@ -559,27 +559,20 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -559,27 +559,20 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
InteractiveInkFeature _currentSplash; InteractiveInkFeature _currentSplash;
bool _hovering = false; bool _hovering = false;
final Map<_HighlightType, InkHighlight> _highlights = <_HighlightType, InkHighlight>{}; final Map<_HighlightType, InkHighlight> _highlights = <_HighlightType, InkHighlight>{};
Map<LocalKey, ActionFactory> _actionMap; Map<Type, Action<Intent>> _actionMap;
bool get highlightsExist => _highlights.values.where((InkHighlight highlight) => highlight != null).isNotEmpty; bool get highlightsExist => _highlights.values.where((InkHighlight highlight) => highlight != null).isNotEmpty;
void _handleAction(FocusNode node, Intent intent) { void _handleAction(ActivateIntent intent) {
_startSplash(context: node.context); _startSplash(context: context);
_handleTap(node.context); _handleTap(context);
}
Action _createAction() {
return CallbackAction(
ActivateAction.key,
onInvoke: _handleAction,
);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_actionMap = <LocalKey, ActionFactory>{ _actionMap = <Type, Action<Intent>>{
ActivateAction.key: _createAction, ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: _handleAction),
}; };
FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange); FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
} }
......
...@@ -262,31 +262,26 @@ class Radio<T> extends StatefulWidget { ...@@ -262,31 +262,26 @@ class Radio<T> extends StatefulWidget {
class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin { class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
bool get enabled => widget.onChanged != null; bool get enabled => widget.onChanged != null;
Map<LocalKey, ActionFactory> _actionMap; Map<Type, Action<Intent>> _actionMap;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_actionMap = <LocalKey, ActionFactory>{ _actionMap = <Type, Action<Intent>>{
ActivateAction.key: _createAction, ActivateIntent: CallbackAction<ActivateIntent>(
onInvoke: _actionHandler,
),
}; };
} }
void _actionHandler(FocusNode node, Intent intent) { void _actionHandler(ActivateIntent intent) {
if (widget.onChanged != null) { if (widget.onChanged != null) {
widget.onChanged(widget.value); widget.onChanged(widget.value);
} }
final RenderObject renderObject = node.context.findRenderObject(); final RenderObject renderObject = context.findRenderObject();
renderObject.sendSemanticsEvent(const TapSemanticEvent()); renderObject.sendSemanticsEvent(const TapSemanticEvent());
} }
Action _createAction() {
return CallbackAction(
ActivateAction.key,
onInvoke: _actionHandler,
);
}
bool _focused = false; bool _focused = false;
void _handleHighlightChanged(bool focused) { void _handleHighlightChanged(bool focused) {
if (_focused != focused) { if (_focused != focused) {
......
...@@ -230,31 +230,24 @@ class Switch extends StatefulWidget { ...@@ -230,31 +230,24 @@ class Switch extends StatefulWidget {
} }
class _SwitchState extends State<Switch> with TickerProviderStateMixin { class _SwitchState extends State<Switch> with TickerProviderStateMixin {
Map<LocalKey, ActionFactory> _actionMap; Map<Type, Action<Intent>> _actionMap;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_actionMap = <LocalKey, ActionFactory>{ _actionMap = <Type, Action<Intent>>{
ActivateAction.key: _createAction, ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: _actionHandler),
}; };
} }
void _actionHandler(FocusNode node, Intent intent){ void _actionHandler(ActivateIntent intent) {
if (widget.onChanged != null) { if (widget.onChanged != null) {
widget.onChanged(!widget.value); widget.onChanged(!widget.value);
} }
final RenderObject renderObject = node.context.findRenderObject(); final RenderObject renderObject = context.findRenderObject();
renderObject.sendSemanticsEvent(const TapSemanticEvent()); renderObject.sendSemanticsEvent(const TapSemanticEvent());
} }
Action _createAction() {
return CallbackAction(
ActivateAction.key,
onInvoke: _actionHandler,
);
}
bool _focused = false; bool _focused = false;
void _handleFocusHighlightChanged(bool focused) { void _handleFocusHighlightChanged(bool focused) {
if (focused != _focused) { if (focused != _focused) {
......
...@@ -78,7 +78,7 @@ export 'package:flutter/services.dart' show SmartQuotesType, SmartDashesType; ...@@ -78,7 +78,7 @@ export 'package:flutter/services.dart' show SmartQuotesType, SmartDashesType;
/// shortcuts: <LogicalKeySet, Intent>{ /// shortcuts: <LogicalKeySet, Intent>{
/// // Pressing enter on the field will now move to the next field. /// // Pressing enter on the field will now move to the next field.
/// LogicalKeySet(LogicalKeyboardKey.enter): /// LogicalKeySet(LogicalKeyboardKey.enter):
/// Intent(NextFocusAction.key), /// NextFocusIntent(),
/// }, /// },
/// child: FocusTraversalGroup( /// child: FocusTraversalGroup(
/// child: Form( /// child: Form(
......
...@@ -743,7 +743,7 @@ class WidgetsApp extends StatefulWidget { ...@@ -743,7 +743,7 @@ class WidgetsApp extends StatefulWidget {
/// return WidgetsApp( /// return WidgetsApp(
/// shortcuts: <LogicalKeySet, Intent>{ /// shortcuts: <LogicalKeySet, Intent>{
/// ... WidgetsApp.defaultShortcuts, /// ... WidgetsApp.defaultShortcuts,
/// LogicalKeySet(LogicalKeyboardKey.select): const Intent(ActivateAction.key), /// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
/// }, /// },
/// color: const Color(0xFFFF0000), /// color: const Color(0xFFFF0000),
/// builder: (BuildContext context, Widget child) { /// builder: (BuildContext context, Widget child) {
...@@ -790,12 +790,12 @@ class WidgetsApp extends StatefulWidget { ...@@ -790,12 +790,12 @@ class WidgetsApp extends StatefulWidget {
/// ```dart /// ```dart
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return WidgetsApp( /// return WidgetsApp(
/// actions: <LocalKey, ActionFactory>{ /// actions: <Type, Action<Intent>>{
/// ... WidgetsApp.defaultActions, /// ... WidgetsApp.defaultActions,
/// ActivateAction.key: () => CallbackAction( /// ActivateAction: CallbackAction(
/// ActivateAction.key, /// onInvoke: (Intent intent) {
/// onInvoke: (FocusNode focusNode, Intent intent) {
/// // Do something here... /// // Do something here...
/// return null;
/// }, /// },
/// ), /// ),
/// }, /// },
...@@ -818,7 +818,7 @@ class WidgetsApp extends StatefulWidget { ...@@ -818,7 +818,7 @@ class WidgetsApp extends StatefulWidget {
/// * The [Intent] and [Action] classes, which allow definition of new /// * The [Intent] and [Action] classes, which allow definition of new
/// actions. /// actions.
/// {@endtemplate} /// {@endtemplate}
final Map<LocalKey, ActionFactory> actions; final Map<Type, Action<Intent>> actions;
/// If true, forces the performance overlay to be visible in all instances. /// If true, forces the performance overlay to be visible in all instances.
/// ///
...@@ -845,13 +845,13 @@ class WidgetsApp extends StatefulWidget { ...@@ -845,13 +845,13 @@ class WidgetsApp extends StatefulWidget {
static final Map<LogicalKeySet, Intent> _defaultShortcuts = <LogicalKeySet, Intent>{ static final Map<LogicalKeySet, Intent> _defaultShortcuts = <LogicalKeySet, Intent>{
// Activation // Activation
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.enter): const ActivateIntent(),
LogicalKeySet(LogicalKeyboardKey.space): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.space): const ActivateIntent(),
LogicalKeySet(LogicalKeyboardKey.gameButtonA): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.gameButtonA): const ActivateIntent(),
// Keyboard traversal. // Keyboard traversal.
LogicalKeySet(LogicalKeyboardKey.tab): const Intent(NextFocusAction.key), LogicalKeySet(LogicalKeyboardKey.tab): const NextFocusIntent(),
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const Intent(PreviousFocusAction.key), LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const PreviousFocusIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left), LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left),
LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right), LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right),
LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down), LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down),
...@@ -869,11 +869,11 @@ class WidgetsApp extends StatefulWidget { ...@@ -869,11 +869,11 @@ class WidgetsApp extends StatefulWidget {
// Default shortcuts for the web platform. // Default shortcuts for the web platform.
static final Map<LogicalKeySet, Intent> _defaultWebShortcuts = <LogicalKeySet, Intent>{ static final Map<LogicalKeySet, Intent> _defaultWebShortcuts = <LogicalKeySet, Intent>{
// Activation // Activation
LogicalKeySet(LogicalKeyboardKey.space): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.space): const ActivateIntent(),
// Keyboard traversal. // Keyboard traversal.
LogicalKeySet(LogicalKeyboardKey.tab): const Intent(NextFocusAction.key), LogicalKeySet(LogicalKeyboardKey.tab): const NextFocusIntent(),
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const Intent(PreviousFocusAction.key), LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const PreviousFocusIntent(),
// Scrolling // Scrolling
LogicalKeySet(LogicalKeyboardKey.arrowUp): const ScrollIntent(direction: AxisDirection.up), LogicalKeySet(LogicalKeyboardKey.arrowUp): const ScrollIntent(direction: AxisDirection.up),
...@@ -887,12 +887,12 @@ class WidgetsApp extends StatefulWidget { ...@@ -887,12 +887,12 @@ class WidgetsApp extends StatefulWidget {
// Default shortcuts for the macOS platform. // Default shortcuts for the macOS platform.
static final Map<LogicalKeySet, Intent> _defaultMacOsShortcuts = <LogicalKeySet, Intent>{ static final Map<LogicalKeySet, Intent> _defaultMacOsShortcuts = <LogicalKeySet, Intent>{
// Activation // Activation
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.enter): const ActivateIntent(),
LogicalKeySet(LogicalKeyboardKey.space): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.space): const ActivateIntent(),
// Keyboard traversal // Keyboard traversal
LogicalKeySet(LogicalKeyboardKey.tab): const Intent(NextFocusAction.key), LogicalKeySet(LogicalKeyboardKey.tab): const NextFocusIntent(),
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const Intent(PreviousFocusAction.key), LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const PreviousFocusIntent(),
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left), LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left),
LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right), LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right),
LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down), LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down),
...@@ -932,13 +932,13 @@ class WidgetsApp extends StatefulWidget { ...@@ -932,13 +932,13 @@ class WidgetsApp extends StatefulWidget {
} }
/// The default value of [WidgetsApp.actions]. /// The default value of [WidgetsApp.actions].
static final Map<LocalKey, ActionFactory> defaultActions = <LocalKey, ActionFactory>{ static Map<Type, Action<Intent>> defaultActions = <Type, Action<Intent>>{
DoNothingAction.key: () => const DoNothingAction(), DoNothingIntent: DoNothingAction(),
RequestFocusAction.key: () => RequestFocusAction(), RequestFocusIntent: RequestFocusAction(),
NextFocusAction.key: () => NextFocusAction(), NextFocusIntent: NextFocusAction(),
PreviousFocusAction.key: () => PreviousFocusAction(), PreviousFocusIntent: PreviousFocusAction(),
DirectionalFocusAction.key: () => DirectionalFocusAction(), DirectionalFocusIntent: DirectionalFocusAction(),
ScrollAction.key: () => ScrollAction(), ScrollIntent: ScrollAction(),
}; };
@override @override
......
...@@ -92,7 +92,8 @@ enum TraversalDirection { ...@@ -92,7 +92,8 @@ enum TraversalDirection {
/// [FocusTraversalGroup] widget. /// [FocusTraversalGroup] widget.
/// ///
/// The focus traversal policy is what determines which widget is "next", /// The focus traversal policy is what determines which widget is "next",
/// "previous", or in a direction from the currently focused [FocusNode]. /// "previous", or in a direction from the widget associated with the currently
/// focused [FocusNode] (usually a [Focus] widget).
/// ///
/// One of the pre-defined subclasses may be used, or define a custom policy to /// One of the pre-defined subclasses may be used, or define a custom policy to
/// create a unique focus order. /// create a unique focus order.
...@@ -1713,88 +1714,94 @@ class _FocusTraversalGroupMarker extends InheritedWidget { ...@@ -1713,88 +1714,94 @@ class _FocusTraversalGroupMarker extends InheritedWidget {
bool updateShouldNotify(InheritedWidget oldWidget) => false; bool updateShouldNotify(InheritedWidget oldWidget) => false;
} }
// A base class for all of the default actions that request focus for a node. /// An intent for use with the [RequestFocusAction], which supplies the
class _RequestFocusActionBase extends Action { /// [FocusNode] that should be focused.
_RequestFocusActionBase(LocalKey name) : super(name); class RequestFocusIntent extends Intent {
/// A const constructor for a [RequestFocusIntent], so that subclasses may be
/// const.
const RequestFocusIntent(this.focusNode)
: assert(focusNode != null);
FocusNode _previousFocus; /// The [FocusNode] that is to be focused.
final FocusNode focusNode;
@override
void invoke(FocusNode node, Intent intent) {
_previousFocus = primaryFocus;
node.requestFocus();
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<FocusNode>('previous', _previousFocus));
}
} }
/// An [Action] that requests the focus on the node it is invoked on. /// An [Action] that requests the focus on the node it is given in its
/// [RequestFocusIntent].
/// ///
/// This action can be used to request focus for a particular node, by calling /// This action can be used to request focus for a particular node, by calling
/// [Action.invoke] like so: /// [Action.invoke] like so:
/// ///
/// ```dart /// ```dart
/// Actions.invoke(context, const Intent(RequestFocusAction.key), focusNode: _focusNode); /// Actions.invoke(context, const RequestFocusIntent(focusNode));
/// ``` /// ```
/// ///
/// Where the `_focusNode` is the node for which the focus will be requested. /// Where the `focusNode` is the node for which the focus will be requested.
/// ///
/// The difference between requesting focus in this way versus calling /// The difference between requesting focus in this way versus calling
/// [_focusNode.requestFocus] directly is that it will use the [Action] /// [FocusNode.requestFocus] directly is that it will use the [Action]
/// registered in the nearest [Actions] widget associated with [key] to make the /// registered in the nearest [Actions] widget associated with
/// request, rather than just requesting focus directly. This allows the action /// [RequestFocusIntent] to make the request, rather than just requesting focus
/// to have additional side effects, like logging, or undo and redo /// directly. This allows the action to have additional side effects, like
/// functionality. /// logging, or undo and redo functionality.
/// ///
/// However, this [RequestFocusAction] is the default action associated with the /// This [RequestFocusAction] class is the default action associated with the
/// [key] in the [WidgetsApp], and it simply requests focus and has no side /// [RequestFocusIntent] in the [WidgetsApp], and it simply requests focus. You
/// effects. /// can redefine the associated action with your own [Actions] widget.
class RequestFocusAction extends _RequestFocusActionBase { ///
/// Creates a [RequestFocusAction] with a fixed [key]. /// See [FocusTraversalPolicy] for more information about focus traversal.
RequestFocusAction() : super(key); class RequestFocusAction extends Action<RequestFocusIntent> {
/// The [LocalKey] that uniquely identifies this action to an [Intent].
static const LocalKey key = ValueKey<Type>(RequestFocusAction);
@override @override
void invoke(FocusNode node, Intent intent) => _focusAndEnsureVisible(node); void invoke(RequestFocusIntent intent) {
_focusAndEnsureVisible(intent.focusNode);
}
}
/// An [Intent] bound to [NextFocusAction], which moves the focus to the next
/// focusable node in the focus traversal order.
///
/// See [FocusTraversalPolicy] for more information about focus traversal.
class NextFocusIntent extends Intent {
/// Creates a const [NextFocusIntent] so subclasses can be const.
const NextFocusIntent();
} }
/// An [Action] that moves the focus to the next focusable node in the focus /// An [Action] that moves the focus to the next focusable node in the focus
/// order. /// order.
/// ///
/// This action is the default action registered for the [key], and by default /// This action is the default action registered for the [NextFocusIntent], and
/// is bound to the [LogicalKeyboardKey.tab] key in the [WidgetsApp]. /// by default is bound to the [LogicalKeyboardKey.tab] key in the [WidgetsApp].
class NextFocusAction extends _RequestFocusActionBase { ///
/// Creates a [NextFocusAction] with a fixed [key]; /// See [FocusTraversalPolicy] for more information about focus traversal.
NextFocusAction() : super(key); class NextFocusAction extends Action<NextFocusIntent> {
/// The [LocalKey] that uniquely identifies this action to an [Intent].
static const LocalKey key = ValueKey<Type>(NextFocusAction);
@override @override
void invoke(FocusNode node, Intent intent) => node.nextFocus(); void invoke(NextFocusIntent intent) {
primaryFocus.nextFocus();
}
}
/// An [Intent] bound to [PreviousFocusAction], which moves the focus to the
/// previous focusable node in the focus traversal order.
///
/// See [FocusTraversalPolicy] for more information about focus traversal.
class PreviousFocusIntent extends Intent {
/// Creates a const [PreviousFocusIntent] so subclasses can be const.
const PreviousFocusIntent();
} }
/// An [Action] that moves the focus to the previous focusable node in the focus /// An [Action] that moves the focus to the previous focusable node in the focus
/// order. /// order.
/// ///
/// This action is the default action registered for the [key], and by default /// This action is the default action registered for the [PreviousFocusIntent],
/// is bound to a combination of the [LogicalKeyboardKey.tab] key and the /// and by default is bound to a combination of the [LogicalKeyboardKey.tab] key
/// [LogicalKeyboardKey.shift] key in the [WidgetsApp]. /// and the [LogicalKeyboardKey.shift] key in the [WidgetsApp].
class PreviousFocusAction extends _RequestFocusActionBase { ///
/// Creates a [PreviousFocusAction] with a fixed [key]; /// See [FocusTraversalPolicy] for more information about focus traversal.
PreviousFocusAction() : super(key); class PreviousFocusAction extends Action<PreviousFocusIntent> {
/// The [LocalKey] that uniquely identifies this action to an [Intent].
static const LocalKey key = ValueKey<Type>(PreviousFocusAction);
@override @override
void invoke(FocusNode node, Intent intent) => node.previousFocus(); void invoke(PreviousFocusIntent intent) {
primaryFocus.previousFocus();
}
} }
/// An [Intent] that represents moving to the next focusable node in the given /// An [Intent] that represents moving to the next focusable node in the given
...@@ -1804,12 +1811,13 @@ class PreviousFocusAction extends _RequestFocusActionBase { ...@@ -1804,12 +1811,13 @@ class PreviousFocusAction extends _RequestFocusActionBase {
/// [LogicalKeyboardKey.arrowDown], [LogicalKeyboardKey.arrowLeft], and /// [LogicalKeyboardKey.arrowDown], [LogicalKeyboardKey.arrowLeft], and
/// [LogicalKeyboardKey.arrowRight] keys in the [WidgetsApp], with the /// [LogicalKeyboardKey.arrowRight] keys in the [WidgetsApp], with the
/// appropriate associated directions. /// appropriate associated directions.
///
/// See [FocusTraversalPolicy] for more information about focus traversal.
class DirectionalFocusIntent extends Intent { class DirectionalFocusIntent extends Intent {
/// Creates a [DirectionalFocusIntent] with a fixed [key], and the given /// Creates a [DirectionalFocusIntent] intending to move the focus in the
/// [direction]. /// given [direction].
const DirectionalFocusIntent(this.direction, {this.ignoreTextFields = true}) const DirectionalFocusIntent(this.direction, {this.ignoreTextFields = true})
: assert(ignoreTextFields != null), : assert(ignoreTextFields != null);
super(DirectionalFocusAction.key);
/// The direction in which to look for the next focusable node when the /// The direction in which to look for the next focusable node when the
/// associated [DirectionalFocusAction] is invoked. /// associated [DirectionalFocusAction] is invoked.
...@@ -1826,21 +1834,15 @@ class DirectionalFocusIntent extends Intent { ...@@ -1826,21 +1834,15 @@ class DirectionalFocusIntent extends Intent {
/// An [Action] that moves the focus to the focusable node in the direction /// An [Action] that moves the focus to the focusable node in the direction
/// configured by the associated [DirectionalFocusIntent.direction]. /// configured by the associated [DirectionalFocusIntent.direction].
/// ///
/// This is the [Action] associated with the [key] and bound by default to the /// This is the [Action] associated with [DirectionalFocusIntent] and bound by
/// [LogicalKeyboardKey.arrowUp], [LogicalKeyboardKey.arrowDown], /// default to the [LogicalKeyboardKey.arrowUp], [LogicalKeyboardKey.arrowDown],
/// [LogicalKeyboardKey.arrowLeft], and [LogicalKeyboardKey.arrowRight] keys in /// [LogicalKeyboardKey.arrowLeft], and [LogicalKeyboardKey.arrowRight] keys in
/// the [WidgetsApp], with the appropriate associated directions. /// the [WidgetsApp], with the appropriate associated directions.
class DirectionalFocusAction extends _RequestFocusActionBase { class DirectionalFocusAction extends Action<DirectionalFocusIntent> {
/// Creates a [DirectionalFocusAction] with a fixed [key];
DirectionalFocusAction() : super(key);
/// The [LocalKey] that uniquely identifies this action to [DirectionalFocusIntent].
static const LocalKey key = ValueKey<Type>(DirectionalFocusAction);
@override @override
void invoke(FocusNode node, DirectionalFocusIntent intent) { void invoke(DirectionalFocusIntent intent) {
if (!intent.ignoreTextFields || node.context.widget is! EditableText) { if (!intent.ignoreTextFields || primaryFocus.context.widget is! EditableText) {
node.focusInDirection(intent.direction); primaryFocus.focusInDirection(intent.direction);
} }
} }
} }
...@@ -132,7 +132,7 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { ...@@ -132,7 +132,7 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
static final Map<GlobalKey, Element> _registry = <GlobalKey, Element>{}; static final Map<GlobalKey, Element> _registry = <GlobalKey, Element>{};
static final Set<Element> _debugIllFatedElements = HashSet<Element>(); static final Set<Element> _debugIllFatedElements = HashSet<Element>();
// This map keeps track which child reserve the global key with the parent. // This map keeps track which child reserves the global key with the parent.
// Parent, child -> global key. // Parent, child -> global key.
// This provides us a way to remove old reservation while parent rebuilds the // This provides us a way to remove old reservation while parent rebuilds the
// child in the same slot. // child in the same slot.
......
...@@ -889,8 +889,7 @@ class ScrollIntent extends Intent { ...@@ -889,8 +889,7 @@ class ScrollIntent extends Intent {
@required this.direction, @required this.direction,
this.type = ScrollIncrementType.line, this.type = ScrollIncrementType.line,
}) : assert(direction != null), }) : assert(direction != null),
assert(type != null), assert(type != null);
super(ScrollAction.key);
/// The direction in which to scroll the scrollable containing the focused /// The direction in which to scroll the scrollable containing the focused
/// widget. /// widget.
...@@ -898,11 +897,6 @@ class ScrollIntent extends Intent { ...@@ -898,11 +897,6 @@ class ScrollIntent extends Intent {
/// The type of scrolling that is intended. /// The type of scrolling that is intended.
final ScrollIncrementType type; final ScrollIncrementType type;
@override
bool isEnabled(BuildContext context) {
return Scrollable.of(context) != null;
}
} }
/// An [Action] that scrolls the [Scrollable] that encloses the current /// An [Action] that scrolls the [Scrollable] that encloses the current
...@@ -912,13 +906,16 @@ class ScrollIntent extends Intent { ...@@ -912,13 +906,16 @@ class ScrollIntent extends Intent {
/// for a [ScrollIntent.type] set to [ScrollIncrementType.page] is 80% of the /// for a [ScrollIntent.type] set to [ScrollIncrementType.page] is 80% of the
/// size of the scroll window, and for [ScrollIncrementType.line], 50 logical /// size of the scroll window, and for [ScrollIncrementType.line], 50 logical
/// pixels. /// pixels.
class ScrollAction extends Action { class ScrollAction extends Action<ScrollIntent> {
/// Creates a const [ScrollAction].
ScrollAction() : super(key);
/// The [LocalKey] that uniquely connects this action to a [ScrollIntent]. /// The [LocalKey] that uniquely connects this action to a [ScrollIntent].
static const LocalKey key = ValueKey<Type>(ScrollAction); static const LocalKey key = ValueKey<Type>(ScrollAction);
@override
bool get enabled {
final FocusNode focus = primaryFocus;
return focus != null && focus.context != null && Scrollable.of(focus.context) != null;
}
// Returns the scroll increment for a single scroll request, for use when // Returns the scroll increment for a single scroll request, for use when
// scrolling using a hardware keyboard. // scrolling using a hardware keyboard.
// //
...@@ -1013,8 +1010,8 @@ class ScrollAction extends Action { ...@@ -1013,8 +1010,8 @@ class ScrollAction extends Action {
} }
@override @override
void invoke(FocusNode node, ScrollIntent intent) { void invoke(ScrollIntent intent) {
final ScrollableState state = Scrollable.of(node.context); final ScrollableState state = Scrollable.of(primaryFocus.context);
assert(state != null, '$ScrollAction was invoked on a context that has no scrollable parent'); assert(state != null, '$ScrollAction was invoked on a context that has no scrollable parent');
assert(state.position.pixels != null, 'Scrollable must be laid out before it can be scrolled via a ScrollAction'); assert(state.position.pixels != null, 'Scrollable must be laid out before it can be scrolled via a ScrollAction');
assert(state.position.viewportDimension != null); assert(state.position.viewportDimension != null);
......
...@@ -286,6 +286,14 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable { ...@@ -286,6 +286,14 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
/// The optional `keysPressed` argument provides an override to keys that the /// The optional `keysPressed` argument provides an override to keys that the
/// [RawKeyboard] reports. If not specified, uses [RawKeyboard.keysPressed] /// [RawKeyboard] reports. If not specified, uses [RawKeyboard.keysPressed]
/// instead. /// instead.
///
/// If a key mapping is found, then the associated action will be invoked
/// using the [Intent] that the [LogicalKeySet] maps to, and the currently
/// focused widget's context (from [FocusManager.primaryFocus]).
///
/// The object returned is the result of [Action.invoke] being called on the
/// [Action] bound to the [Intent] that the key press maps to, or null, if the
/// key press didn't match any intent.
@protected @protected
bool handleKeypress( bool handleKeypress(
BuildContext context, BuildContext context,
...@@ -316,10 +324,9 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable { ...@@ -316,10 +324,9 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
} }
if (matchedIntent != null) { if (matchedIntent != null) {
final BuildContext primaryContext = primaryFocus?.context; final BuildContext primaryContext = primaryFocus?.context;
if (primaryContext == null) { assert (primaryContext != null);
return false; Actions.invoke(primaryContext, matchedIntent, nullOk: true);
} return true;
return Actions.invoke(primaryContext, matchedIntent, nullOk: true);
} }
return false; return false;
} }
......
...@@ -174,6 +174,7 @@ void main() { ...@@ -174,6 +174,7 @@ void main() {
' Focus\n' ' Focus\n'
' _FocusTraversalGroupMarker\n' ' _FocusTraversalGroupMarker\n'
' FocusTraversalGroup\n' ' FocusTraversalGroup\n'
' _ActionsMarker\n'
' Actions\n' ' Actions\n'
' _ShortcutsMarker\n' ' _ShortcutsMarker\n'
' Semantics\n' ' Semantics\n'
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
...@@ -259,11 +258,11 @@ void main() { ...@@ -259,11 +258,11 @@ void main() {
final BorderRadius borderRadius = BorderRadius.circular(6.0); final BorderRadius borderRadius = BorderRadius.circular(6.0);
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
Future<void> buildTest(LocalKey actionKey) async { Future<void> buildTest(Intent intent) async {
return await tester.pumpWidget( return await tester.pumpWidget(
Shortcuts( Shortcuts(
shortcuts: <LogicalKeySet, Intent>{ shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.space): Intent(actionKey), LogicalKeySet(LogicalKeyboardKey.space): intent,
}, },
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -289,7 +288,7 @@ void main() { ...@@ -289,7 +288,7 @@ void main() {
); );
} }
await buildTest(ActivateAction.key); await buildTest(const ActivateIntent());
focusNode.requestFocus(); focusNode.requestFocus();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -322,7 +321,7 @@ void main() { ...@@ -322,7 +321,7 @@ void main() {
); );
} }
await buildTest(ActivateAction.key); await buildTest(const ActivateIntent());
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.sendKeyEvent(LogicalKeyboardKey.space); await tester.sendKeyEvent(LogicalKeyboardKey.space);
await tester.pump(); await tester.pump();
......
...@@ -323,7 +323,7 @@ void main() { ...@@ -323,7 +323,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets("ink response doesn't focus when disabled", (WidgetTester tester) async { testWidgets("ink response doesn't focus when disabled", (WidgetTester tester) async {
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTouch; FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus'); final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
final GlobalKey childKey = GlobalKey(); final GlobalKey childKey = GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -359,7 +359,7 @@ void main() { ...@@ -359,7 +359,7 @@ void main() {
}); });
testWidgets("ink response doesn't hover when disabled", (WidgetTester tester) async { testWidgets("ink response doesn't hover when disabled", (WidgetTester tester) async {
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTouch; FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus'); final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
final GlobalKey childKey = GlobalKey(); final GlobalKey childKey = GlobalKey();
bool hovering = false; bool hovering = false;
......
...@@ -47,8 +47,8 @@ void main() { ...@@ -47,8 +47,8 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
Shortcuts( Shortcuts(
shortcuts: <LogicalKeySet, Intent>{ shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.enter): const ActivateIntent(),
LogicalKeySet(LogicalKeyboardKey.space): const Intent(ActivateAction.key), LogicalKeySet(LogicalKeyboardKey.space): const ActivateIntent(),
}, },
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
......
...@@ -8,15 +8,19 @@ import 'package:flutter/services.dart'; ...@@ -8,15 +8,19 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
class TestAction extends Action { class TestIntent extends Intent {
TestAction() : super(key); const TestIntent();
}
class TestAction extends Action<Intent> {
TestAction();
static const LocalKey key = ValueKey<Type>(TestAction); static const LocalKey key = ValueKey<Type>(TestAction);
int calls = 0; int calls = 0;
@override @override
void invoke(FocusNode node, Intent intent) { void invoke(Intent intent) {
calls += 1; calls += 1;
} }
} }
...@@ -67,11 +71,11 @@ void main() { ...@@ -67,11 +71,11 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
WidgetsApp( WidgetsApp(
key: key, key: key,
actions: <LocalKey, ActionFactory>{ actions: <Type, Action<Intent>>{
TestAction.key: () => action, TestIntent: action,
}, },
shortcuts: <LogicalKeySet, Intent> { shortcuts: <LogicalKeySet, Intent> {
LogicalKeySet(LogicalKeyboardKey.space): const Intent(TestAction.key), LogicalKeySet(LogicalKeyboardKey.space): const TestIntent(),
}, },
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return Material( return Material(
......
...@@ -9,13 +9,13 @@ import 'package:flutter/src/services/keyboard_key.dart'; ...@@ -9,13 +9,13 @@ import 'package:flutter/src/services/keyboard_key.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
typedef PostInvokeCallback = void Function({Action action, Intent intent, FocusNode focusNode, ActionDispatcher dispatcher}); typedef PostInvokeCallback = void Function({Action<Intent> action, Intent intent, BuildContext context, ActionDispatcher dispatcher});
class TestAction extends CallbackAction { class TestAction extends CallbackAction<TestIntent> {
const TestAction({ TestAction({
@required OnInvokeCallback onInvoke, @required OnInvokeCallback onInvoke,
}) : assert(onInvoke != null), }) : assert(onInvoke != null),
super(key, onInvoke: onInvoke); super(onInvoke: onInvoke);
static const LocalKey key = ValueKey<Type>(TestAction); static const LocalKey key = ValueKey<Type>(TestAction);
} }
...@@ -26,15 +26,15 @@ class TestDispatcher extends ActionDispatcher { ...@@ -26,15 +26,15 @@ class TestDispatcher extends ActionDispatcher {
final PostInvokeCallback postInvoke; final PostInvokeCallback postInvoke;
@override @override
bool invokeAction(Action action, Intent intent, {FocusNode focusNode}) { Object invokeAction(Action<TestIntent> action, Intent intent, [BuildContext context]) {
final bool result = super.invokeAction(action, intent, focusNode: focusNode); final Object result = super.invokeAction(action, intent, context);
postInvoke?.call(action: action, intent: intent, focusNode: focusNode, dispatcher: this); postInvoke?.call(action: action, intent: intent, context: context, dispatcher: this);
return result; return result;
} }
} }
class TestIntent extends Intent { class TestIntent extends Intent {
const TestIntent() : super(TestAction.key); const TestIntent();
} }
class TestShortcutManager extends ShortcutManager { class TestShortcutManager extends ShortcutManager {
...@@ -210,10 +210,11 @@ void main() { ...@@ -210,10 +210,11 @@ void main() {
bool invoked = false; bool invoked = false;
await tester.pumpWidget( await tester.pumpWidget(
Actions( Actions(
actions: <LocalKey, ActionFactory>{ actions: <Type, Action<Intent>>{
TestAction.key: () => TestAction( TestIntent: TestAction(
onInvoke: (FocusNode node, Intent intent) { onInvoke: (Intent intent) {
invoked = true; invoked = true;
return true;
}, },
), ),
}, },
...@@ -247,10 +248,11 @@ void main() { ...@@ -247,10 +248,11 @@ void main() {
LogicalKeySet(LogicalKeyboardKey.shift): const TestIntent(), LogicalKeySet(LogicalKeyboardKey.shift): const TestIntent(),
}, },
child: Actions( child: Actions(
actions: <LocalKey, ActionFactory>{ actions: <Type, Action<Intent>>{
TestAction.key: () => TestAction( TestIntent: TestAction(
onInvoke: (FocusNode node, Intent intent) { onInvoke: (Intent intent) {
invoked = true; invoked = true;
return invoked;
}, },
), ),
}, },
...@@ -285,10 +287,11 @@ void main() { ...@@ -285,10 +287,11 @@ void main() {
LogicalKeySet(LogicalKeyboardKey.shift): const TestIntent(), LogicalKeySet(LogicalKeyboardKey.shift): const TestIntent(),
}, },
child: Actions( child: Actions(
actions: <LocalKey, ActionFactory>{ actions: <Type, Action<Intent>>{
TestAction.key: () => TestAction( TestIntent: TestAction(
onInvoke: (FocusNode node, Intent intent) { onInvoke: (Intent intent) {
invoked = true; invoked = true;
return invoked;
}, },
), ),
}, },
...@@ -317,7 +320,7 @@ void main() { ...@@ -317,7 +320,7 @@ void main() {
Shortcuts(shortcuts: <LogicalKeySet, Intent>{LogicalKeySet( Shortcuts(shortcuts: <LogicalKeySet, Intent>{LogicalKeySet(
LogicalKeyboardKey.shift, LogicalKeyboardKey.shift,
LogicalKeyboardKey.keyA, LogicalKeyboardKey.keyA,
) : const Intent(ActivateAction.key), ) : const ActivateIntent(),
LogicalKeySet( LogicalKeySet(
LogicalKeyboardKey.shift, LogicalKeyboardKey.shift,
LogicalKeyboardKey.arrowRight, LogicalKeyboardKey.arrowRight,
...@@ -334,7 +337,7 @@ void main() { ...@@ -334,7 +337,7 @@ void main() {
expect( expect(
description[0], description[0],
equalsIgnoringHashCodes( equalsIgnoringHashCodes(
'shortcuts: {{Shift + Key A}: Intent#00000(key: [<ActivateAction>]), {Shift + Arrow Right}: DirectionalFocusIntent#00000(key: [<DirectionalFocusAction>])}')); 'shortcuts: {{Shift + Key A}: ActivateIntent#00000, {Shift + Arrow Right}: DirectionalFocusIntent#00000}'));
}); });
test('Shortcuts diagnostics work when debugLabel specified.', () { test('Shortcuts diagnostics work when debugLabel specified.', () {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
...@@ -345,7 +348,7 @@ void main() { ...@@ -345,7 +348,7 @@ void main() {
LogicalKeySet( LogicalKeySet(
LogicalKeyboardKey.keyA, LogicalKeyboardKey.keyA,
LogicalKeyboardKey.keyB, LogicalKeyboardKey.keyB,
): const Intent(ActivateAction.key) ): const ActivateIntent(),
}, },
).debugFillProperties(builder); ).debugFillProperties(builder);
...@@ -368,7 +371,7 @@ void main() { ...@@ -368,7 +371,7 @@ void main() {
LogicalKeySet( LogicalKeySet(
LogicalKeyboardKey.keyA, LogicalKeyboardKey.keyA,
LogicalKeyboardKey.keyB, LogicalKeyboardKey.keyB,
): const Intent(ActivateAction.key) ): const ActivateIntent(),
}, },
).debugFillProperties(builder); ).debugFillProperties(builder);
...@@ -381,7 +384,7 @@ void main() { ...@@ -381,7 +384,7 @@ void main() {
expect(description.length, equals(2)); expect(description.length, equals(2));
expect(description[0], equalsIgnoringHashCodes('manager: ShortcutManager#00000(shortcuts: {})')); expect(description[0], equalsIgnoringHashCodes('manager: ShortcutManager#00000(shortcuts: {})'));
expect(description[1], equalsIgnoringHashCodes('shortcuts: {{Key A + Key B}: Intent#00000(key: [<ActivateAction>])}')); expect(description[1], equalsIgnoringHashCodes('shortcuts: {{Key A + Key B}: ActivateIntent#00000}'));
}); });
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment