@@ -28,6 +28,7 @@ void main() {
2828 expect (invoked, isTrue);
2929 });
3030 });
31+
3132 group (Actions , () {
3233 Intent ? invokedIntent;
3334 Action <Intent >? invokedAction;
@@ -99,6 +100,7 @@ void main() {
99100 expect (result, isTrue);
100101 expect (invoked, isTrue);
101102 });
103+
102104 testWidgets ('maybeInvoke returns null when no action is found' , (WidgetTester tester) async {
103105 final GlobalKey containerKey = GlobalKey ();
104106 bool invoked = false ;
@@ -125,6 +127,7 @@ void main() {
125127 expect (result, isNull);
126128 expect (invoked, isFalse);
127129 });
130+
128131 testWidgets ('invoke throws when no action is found' , (WidgetTester tester) async {
129132 final GlobalKey containerKey = GlobalKey ();
130133 bool invoked = false ;
@@ -151,6 +154,7 @@ void main() {
151154 expect (result, isNull);
152155 expect (invoked, isFalse);
153156 });
157+
154158 testWidgets ('Actions widget can invoke actions with custom dispatcher' , (WidgetTester tester) async {
155159 final GlobalKey containerKey = GlobalKey ();
156160 bool invoked = false ;
@@ -181,6 +185,7 @@ void main() {
181185 expect (invoked, isTrue);
182186 expect (invokedIntent, equals (intent));
183187 });
188+
184189 testWidgets ('Actions can invoke actions in ancestor dispatcher' , (WidgetTester tester) async {
185190 final GlobalKey containerKey = GlobalKey ();
186191 bool invoked = false ;
@@ -217,6 +222,7 @@ void main() {
217222 expect (invokedAction, equals (testAction));
218223 expect (invokedDispatcher.runtimeType, equals (TestDispatcher1 ));
219224 });
225+
220226 testWidgets ("Actions can invoke actions in ancestor dispatcher if a lower one isn't specified" , (WidgetTester tester) async {
221227 final GlobalKey containerKey = GlobalKey ();
222228 bool invoked = false ;
@@ -252,6 +258,7 @@ void main() {
252258 expect (invokedAction, equals (testAction));
253259 expect (invokedDispatcher.runtimeType, equals (TestDispatcher1 ));
254260 });
261+
255262 testWidgets ('Actions widget can be found with of' , (WidgetTester tester) async {
256263 final GlobalKey containerKey = GlobalKey ();
257264 final ActionDispatcher testDispatcher = TestDispatcher1 (postInvoke: collect);
@@ -268,6 +275,7 @@ void main() {
268275 final ActionDispatcher dispatcher = Actions .of (containerKey.currentContext! );
269276 expect (dispatcher, equals (testDispatcher));
270277 });
278+
271279 testWidgets ('Action can be found with find' , (WidgetTester tester) async {
272280 final GlobalKey containerKey = GlobalKey ();
273281 final ActionDispatcher testDispatcher = TestDispatcher1 (postInvoke: collect);
@@ -314,6 +322,7 @@ void main() {
314322 expect (() => Actions .find <DoNothingIntent >(containerKey.currentContext! ), throwsAssertionError);
315323 expect (Actions .maybeFind <DoNothingIntent >(containerKey.currentContext! ), isNull);
316324 });
325+
317326 testWidgets ('FocusableActionDetector keeps track of focus and hover even when disabled.' , (WidgetTester tester) async {
318327 FocusManager .instance.highlightStrategy = FocusHighlightStrategy .alwaysTraditional;
319328 final GlobalKey containerKey = GlobalKey ();
@@ -383,6 +392,7 @@ void main() {
383392 expect (hovering, isFalse);
384393 expect (focusing, isFalse);
385394 });
395+
386396 testWidgets ('FocusableActionDetector changes mouse cursor when hovered' , (WidgetTester tester) async {
387397 await tester.pumpWidget (
388398 MouseRegion (
@@ -415,6 +425,7 @@ void main() {
415425
416426 expect (RendererBinding .instance.mouseTracker.debugDeviceActiveCursor (1 ), SystemMouseCursors .forbidden);
417427 });
428+
418429 testWidgets ('Actions.invoke returns the value of Action.invoke' , (WidgetTester tester) async {
419430 final GlobalKey containerKey = GlobalKey ();
420431 final Object sentinel = Object ();
@@ -445,6 +456,7 @@ void main() {
445456 expect (identical (result, sentinel), isTrue);
446457 expect (invoked, isTrue);
447458 });
459+
448460 testWidgets ('ContextAction can return null' , (WidgetTester tester) async {
449461 final GlobalKey containerKey = GlobalKey ();
450462 const TestIntent intent = TestIntent ();
@@ -471,6 +483,7 @@ void main() {
471483 expect (invokedDispatcher.runtimeType, equals (TestDispatcher1 ));
472484 expect (testAction.capturedContexts.single, containerKey.currentContext);
473485 });
486+
474487 testWidgets ('Disabled actions stop propagation to an ancestor' , (WidgetTester tester) async {
475488 final GlobalKey containerKey = GlobalKey ();
476489 bool invoked = false ;
@@ -775,6 +788,7 @@ void main() {
775788 expect (hovering, isFalse);
776789 expect (focusing, isFalse);
777790 });
791+
778792 testWidgets ('FocusableActionDetector shows focus highlight appropriately when focused and disabled' , (WidgetTester tester) async {
779793 FocusManager .instance.highlightStrategy = FocusHighlightStrategy .alwaysTraditional;
780794 final GlobalKey containerKey = GlobalKey ();
@@ -805,6 +819,7 @@ void main() {
805819 await tester.pump ();
806820 expect (focusing, isTrue);
807821 });
822+
808823 testWidgets ('FocusableActionDetector can be used without callbacks' , (WidgetTester tester) async {
809824 FocusManager .instance.highlightStrategy = FocusHighlightStrategy .alwaysTraditional;
810825 final GlobalKey containerKey = GlobalKey ();
@@ -951,6 +966,110 @@ void main() {
951966 expect (buttonNode2.hasFocus, isFalse);
952967 },
953968 );
969+
970+ testWidgets ('FocusableActionDetector can exclude Focus semantics' , (WidgetTester tester) async {
971+ await tester.pumpWidget (
972+ MaterialApp (
973+ home: FocusableActionDetector (
974+ child: Column (
975+ children: < Widget > [
976+ TextButton (
977+ onPressed: () {},
978+ child: const Text ('Button 1' ),
979+ ),
980+ TextButton (
981+ onPressed: () {},
982+ child: const Text ('Button 2' ),
983+ ),
984+ ],
985+ ),
986+ ),
987+ ),
988+ );
989+
990+ expect (
991+ tester.getSemantics (find.byType (FocusableActionDetector )),
992+ matchesSemantics (
993+ scopesRoute: true ,
994+ children: < Matcher > [
995+ // This semantic is from `Focus` widget under `FocusableActionDetector`.
996+ matchesSemantics (
997+ isFocusable: true ,
998+ children: < Matcher > [
999+ matchesSemantics (
1000+ hasTapAction: true ,
1001+ isButton: true ,
1002+ hasEnabledState: true ,
1003+ isEnabled: true ,
1004+ isFocusable: true ,
1005+ label: 'Button 1' ,
1006+ textDirection: TextDirection .ltr,
1007+ ),
1008+ matchesSemantics (
1009+ hasTapAction: true ,
1010+ isButton: true ,
1011+ hasEnabledState: true ,
1012+ isEnabled: true ,
1013+ isFocusable: true ,
1014+ label: 'Button 2' ,
1015+ textDirection: TextDirection .ltr,
1016+ ),
1017+ ],
1018+ ),
1019+ ],
1020+ ),
1021+ );
1022+
1023+ // Set `includeFocusSemantics` to false to exclude semantics
1024+ // from `Focus` widget under `FocusableActionDetector`.
1025+ await tester.pumpWidget (
1026+ MaterialApp (
1027+ home: FocusableActionDetector (
1028+ includeFocusSemantics: false ,
1029+ child: Column (
1030+ children: < Widget > [
1031+ TextButton (
1032+ onPressed: () {},
1033+ child: const Text ('Button 1' ),
1034+ ),
1035+ TextButton (
1036+ onPressed: () {},
1037+ child: const Text ('Button 2' ),
1038+ ),
1039+ ],
1040+ ),
1041+ ),
1042+ ),
1043+ );
1044+
1045+ // Semantics from the `Focus` widget will be removed.
1046+ expect (
1047+ tester.getSemantics (find.byType (FocusableActionDetector )),
1048+ matchesSemantics (
1049+ scopesRoute: true ,
1050+ children: < Matcher > [
1051+ matchesSemantics (
1052+ hasTapAction: true ,
1053+ isButton: true ,
1054+ hasEnabledState: true ,
1055+ isEnabled: true ,
1056+ isFocusable: true ,
1057+ label: 'Button 1' ,
1058+ textDirection: TextDirection .ltr,
1059+ ),
1060+ matchesSemantics (
1061+ hasTapAction: true ,
1062+ isButton: true ,
1063+ hasEnabledState: true ,
1064+ isEnabled: true ,
1065+ isFocusable: true ,
1066+ label: 'Button 2' ,
1067+ textDirection: TextDirection .ltr,
1068+ ),
1069+ ],
1070+ ),
1071+ );
1072+ });
9541073 });
9551074
9561075 group ('Action subclasses' , () {
0 commit comments