Add labeled tappable target guideline and gallery tests (#22824)

diff --git a/examples/flutter_gallery/test/accessibility_test.dart b/examples/flutter_gallery/test/accessibility_test.dart
index bd651a7..27a3c51 100644
--- a/examples/flutter_gallery/test/accessibility_test.dart
+++ b/examples/flutter_gallery/test/accessibility_test.dart
@@ -8,231 +8,464 @@
     testWidgets('backdrop_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: BackdropDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('bottom_app_bar_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: BottomAppBarDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('bottom_navigation_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: BottomNavigationDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('buttons_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ButtonsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('cards_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: CardsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('chip_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ChipDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('data_table_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: DataTableDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('date_and_time_picker_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: DateAndTimePickerDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('dialog_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: DialogDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('drawer_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: DrawerDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('elevation_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ElevationDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('expansion_panels_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ExpansionPanelsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('grid_list_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: GridListDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('icons_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: IconsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('leave_behind_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: LeaveBehindDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('list_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: ListDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('menu_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: MenuDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('modal_bottom_sheet_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ModalBottomSheetDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('overscroll_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: OverscrollDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('page_selector_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: PageSelectorDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('persistent_bottom_sheet_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: PersistentBottomSheetDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('progress_indicator_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ProgressIndicatorDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('reorderable_list_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: ReorderableListDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('scrollable_tabs_demo', (WidgetTester tester) async {
      final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: ScrollableTabsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('search_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: SearchDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('selection_controls_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: SelectionControlsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('slider_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: SliderDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('snack_bar_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: SnackBarDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('tabs_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: TabsDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('tabs_fab_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: TabsFabDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('text_form_field_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(const MaterialApp(home: TextFormFieldDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('tooltip_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: TooltipDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
       handle.dispose();
     });
 
     testWidgets('two_level_list_demo', (WidgetTester tester) async {
       final SemanticsHandle handle = tester.ensureSemantics();
       await tester.pumpWidget(MaterialApp(home: TwoLevelListDemo()));
-      expect(tester, meetsGuideline(androidTapTargetGuideline));
+      await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
+      handle.dispose();
+    });
+  });
+
+  group('All material demos have labeled tap targets', () {
+    testWidgets('backdrop_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: BackdropDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('bottom_app_bar_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: BottomAppBarDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('bottom_navigation_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: BottomNavigationDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('buttons_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ButtonsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('cards_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: CardsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('chip_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ChipDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('data_table_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: DataTableDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('date_and_time_picker_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: DateAndTimePickerDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('dialog_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: DialogDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('drawer_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: DrawerDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('elevation_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ElevationDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('expansion_panels_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ExpansionPanelsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('grid_list_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: GridListDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('icons_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: IconsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('leave_behind_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: LeaveBehindDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('list_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: ListDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('menu_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: MenuDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('modal_bottom_sheet_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ModalBottomSheetDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('overscroll_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: OverscrollDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('page_selector_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: PageSelectorDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('persistent_bottom_sheet_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: PersistentBottomSheetDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('progress_indicator_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ProgressIndicatorDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('reorderable_list_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: ReorderableListDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('scrollable_tabs_demo', (WidgetTester tester) async {
+     final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: ScrollableTabsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('search_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: SearchDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('selection_controls_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: SelectionControlsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('slider_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: SliderDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('snack_bar_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: SnackBarDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('tabs_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: TabsDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('tabs_fab_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: TabsFabDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('text_form_field_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(const MaterialApp(home: TextFormFieldDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    }, skip: true);
+
+    testWidgets('tooltip_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: TooltipDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
+      handle.dispose();
+    });
+
+    testWidgets('two_level_list_demo', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(MaterialApp(home: TwoLevelListDemo()));
+      await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
       handle.dispose();
     });
   });
diff --git a/packages/flutter_test/lib/src/accessibility.dart b/packages/flutter_test/lib/src/accessibility.dart
index ade0de6..3433ae0 100644
--- a/packages/flutter_test/lib/src/accessibility.dart
+++ b/packages/flutter_test/lib/src/accessibility.dart
@@ -124,6 +124,41 @@
   String get description => 'Tappable objects should be at least $size';
 }
 
+/// A guideline which enforces that all nodes with a tap or long press action
+/// also have a label.
+@visibleForTesting
+class LabeledTapTargetGuideline extends AccessibilityGuideline {
+  const LabeledTapTargetGuideline._();
+
+  @override
+  String get description => 'Tappable widgets should have a semantic label';
+
+  @override
+  FutureOr<Evaluation> evaluate(WidgetTester tester) {
+   final SemanticsNode root = tester.binding.pipelineOwner.semanticsOwner.rootSemanticsNode;
+    Evaluation traverse(SemanticsNode node) {
+      Evaluation result = const Evaluation.pass();
+      node.visitChildren((SemanticsNode child) {
+        result += traverse(child);
+        return true;
+      });
+      if (node.isMergedIntoParent)
+        return result;
+      final SemanticsData data = node.getSemanticsData();
+      // Skip node if it has no actions, or is marked as hidden.
+      if (!data.hasAction(ui.SemanticsAction.longPress) && !data.hasAction(ui.SemanticsAction.tap))
+        return result;
+      if (data.label == null || data.label.isEmpty) {
+        result += Evaluation.fail(
+          '$node: expected tappable node to have semantic label, but none was found\n',
+        );
+      }
+      return result;
+    }
+    return traverse(root);
+  }
+}
+
 /// A guideline which verifies that all nodes that contribute semantics via text
 /// meet minimum contrast levels.
 ///
@@ -403,3 +438,7 @@
 /// foreground and background colors. The contrast ratio is calculated from
 /// these colors according to the [WCAG](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef)
 const AccessibilityGuideline textContrastGuideline = MinimumTextContrastGuideline._();
+
+/// A guideline which enforces that all nodes with a tap or long press action
+/// also have a label.
+const AccessibilityGuideline labeledTapTargetGuideline = LabeledTapTargetGuideline._();
diff --git a/packages/flutter_test/test/accessibility_test.dart b/packages/flutter_test/test/accessibility_test.dart
index 7c32847..6fad639 100644
--- a/packages/flutter_test/test/accessibility_test.dart
+++ b/packages/flutter_test/test/accessibility_test.dart
@@ -361,6 +361,63 @@
       handle.dispose();
     });
   });
+
+   group('Labeled tappable node guideline', () {
+    testWidgets('Passes when node is labeled', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(_boilerplate(Semantics(
+        container: true,
+        child: const SizedBox(width: 10.0, height: 10.0),
+        onTap: () {},
+        label: 'test',
+      )));
+      final Evaluation result = await labeledTapTargetGuideline.evaluate(tester);
+      expect(result.passed, true);
+      handle.dispose();
+    });
+    testWidgets('Fails if long-press has no label', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(_boilerplate(Semantics(
+        container: true,
+        child: const SizedBox(width: 10.0, height: 10.0),
+        onLongPress: () {},
+        label: '',
+      )));
+      final Evaluation result = await labeledTapTargetGuideline.evaluate(tester);
+      expect(result.passed, false);
+      handle.dispose();
+    });
+
+    testWidgets('Fails if tap has no label', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(_boilerplate(Semantics(
+        container: true,
+        child: const SizedBox(width: 10.0, height: 10.0),
+        onTap: () {},
+        label: '',
+      )));
+      final Evaluation result = await labeledTapTargetGuideline.evaluate(tester);
+      expect(result.passed, false);
+      handle.dispose();
+    });
+
+    testWidgets('Passes if tap is merged into labeled node', (WidgetTester tester) async {
+      final SemanticsHandle handle = tester.ensureSemantics();
+      await tester.pumpWidget(_boilerplate(Semantics(
+        container: true,
+        onLongPress: () {},
+        label: '',
+        child: Semantics(
+          label: 'test',
+          child: const SizedBox(width: 10.0, height: 10.0),
+        ),
+      )));
+      final Evaluation result = await labeledTapTargetGuideline.evaluate(tester);
+      expect(result.passed, true);
+      handle.dispose();
+    });
+  });
+
 }
 
 Widget _boilerplate(Widget child) {