Trigger semantic actions in widget tests — Flutter
The Semantics widget provides a number of callbacks for user actions, like onDidGainAccessibilityFocus & onDidLoseAccessibilityFocus, onTap, onLongPress and more.
To be able to test the side-effects, like showing a SnackBar when onTap is called, we need some way to trigger these actions in widget tests.
Sample App
The app is quite simple, a text which is wrapped with a Semantics widget that overrides three callbacks: onDidGainAccessibilityFocus, onDidLoseAccessibilityFocus & onTap. When any of the callback is triggered, a SnackBar is shown. Run the following code on an iOS/Android device with Voiceover/Talkback turned on.
Demo
Terminology
Flutter, maintains a tree called the semantics tree. Each node in this tree, called SemanticsNode, corresponds to a widget or a group of widgets. These nodes contain information about the widgets, like it’s label, hint, etc. It’s used by the screen readers to make announcements.
Semantics Owner — The class responsible for managing a SemanticsNode. It is responsible for relaying messages from the semantics node to the OS and vice-versa.
Semantics Action — The possible actions that can be conveyed from the operating system accessibility APIs to a SemanticsNode. Example actions are: tap, long press, scroll, accessibility focus gained, accessibility focus lost, etc.
Trigger SemanticsAction from a Test
Widget tests can access the SemanticsOwner using the tester object in the following way:
final semanticsOwner = tester.binding.pipelineOwner.semanticsOwner
Semantics owner has a performAction method which triggers a SemanticsAction as if it came from the OS. It requires a semantics node ID and the action to be performed.
We can get the semantics node ID using the following code snippet:
tester.getSemantics(find.text("Hello World!")).id;
We can trigger the accessibility focus action using:
semanticsOwner.performAction(
semantics.id,
SemanticsAction.didGainAccessibilityFocus,
);