Inspector integration tests are resilient to Flutter framework changes (#9321)

diff --git a/flutter-candidate.txt b/flutter-candidate.txt
index c206b15..99dc991 100644
--- a/flutter-candidate.txt
+++ b/flutter-candidate.txt
@@ -1 +1 @@
-2b6b9d12589875842e64f4b78fd0f11337755aaa
+c2ea27002b9c4ab1aff1db6eb1960e4299aca369
diff --git a/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart b/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
index 822cd94..013ec07 100644
--- a/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
+++ b/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
@@ -17,15 +17,13 @@
 const inspectorChangeSettleTime = Duration(seconds: 2);
 
 void main() {
+  const windowSize = Size(2600.0, 1200.0);
   // We need to use real async in this test so we need to use this binding.
   initializeLiveTestWidgetsFlutterBindingWithAssets();
-  const windowSize = Size(2600.0, 1200.0);
 
-  final env = FlutterTestEnvironment(
-    const FlutterRunConfiguration(withDebugger: true),
-  );
+  late FlutterTestEnvironment env;
 
-  env.afterEverySetup = () async {
+  Future<void> resetInspectorSelection() async {
     final service = serviceConnection.inspectorService;
     if (env.reuseTestEnvironment) {
       // Ensure the previous test did not set the selection on the device.
@@ -37,19 +35,26 @@
         isAlive: null,
       );
     }
-  };
-
-  setUp(() async {
-    await env.setupEnvironment();
-    // Ensure the legacy inspector is enabled:
-    preferences.inspector.setLegacyInspectorEnabled(true);
-  });
-
-  tearDownAll(() async {
-    await env.tearDownEnvironment(force: true);
-  });
+  }
 
   group('screenshot tests', () {
+    setUpAll(() {
+      env = FlutterTestEnvironment(
+        const FlutterRunConfiguration(withDebugger: true),
+      );
+      env.afterEverySetup = resetInspectorSelection;
+    });
+
+    setUp(() async {
+      await env.setupEnvironment();
+      // Ensure the legacy inspector is enabled:
+      preferences.inspector.setLegacyInspectorEnabled(true);
+    });
+
+    tearDownAll(() async {
+      await env.tearDownEnvironment(force: true);
+    });
+
     testWidgetsWithWindowSize('navigation', windowSize, (
       WidgetTester tester,
     ) async {
@@ -99,6 +104,9 @@
         matchesDevToolsGolden(
           '../../test_infra/goldens/integration_inspector_select_center_details_tree.png',
         ),
+        // Implementation widgets from Flutter framework are not guaranteed to
+        // be stable.
+        skip: 'https://github.com/flutter/flutter/issues/172037',
       );
 
       // Select the RichText row.
@@ -109,6 +117,9 @@
         matchesDevToolsGolden(
           '../../test_infra/goldens/integration_inspector_richtext_selected.png',
         ),
+        // Implementation widgets from Flutter framework are not guaranteed to
+        // be stable.
+        skip: 'https://github.com/flutter/flutter/issues/172037',
       );
 
       // Test hovering over the icon shown when a property has its default
@@ -131,6 +142,9 @@
         matchesDevToolsGolden(
           '../../test_infra/goldens/integration_inspector_scaffold_selected.png',
         ),
+        // Implementation widgets from Flutter framework are not guaranteed to
+        // be stable.
+        skip: 'https://github.com/flutter/flutter/issues/172037',
       );
 
       // The important thing about this is that the details tree should scroll
@@ -143,6 +157,9 @@
         matchesDevToolsGolden(
           '../../test_infra/goldens/integration_animated_physical_model_selected.png',
         ),
+        // Implementation widgets from Flutter framework are not guaranteed to
+        // be stable.
+        skip: 'https://github.com/flutter/flutter/issues/172037',
       );
 
       await env.tearDownEnvironment();
@@ -401,15 +418,25 @@
   });
 
   group('widget errors', () {
-    testWidgetsWithWindowSize('show navigator and error labels', windowSize, (
-      WidgetTester tester,
-    ) async {
+    setUpAll(() async {
+      env = FlutterTestEnvironment(
+        testAppDirectory: 'test/test_infra/fixtures/inspector_app',
+        const FlutterRunConfiguration(withDebugger: true),
+      );
       await env.setupEnvironment(
         config: const FlutterRunConfiguration(
           withDebugger: true,
           entryScript: 'lib/overflow_errors.dart',
         ),
       );
+      env.afterEverySetup = resetInspectorSelection;
+      // Enable the legacy inspector.
+      preferences.inspector.setLegacyInspectorEnabled(true);
+    });
+
+    testWidgetsWithWindowSize('show navigator and error labels', windowSize, (
+      WidgetTester tester,
+    ) async {
       expect(serviceConnection.serviceManager.service, equals(env.service));
       expect(serviceConnection.serviceManager.isolateManager, isNotNull);
 
diff --git a/packages/devtools_app/test/screens/inspector_v2/inspector_integration_test.dart b/packages/devtools_app/test/screens/inspector_v2/inspector_integration_test.dart
index 5ee0258..6d4e0cb 100644
--- a/packages/devtools_app/test/screens/inspector_v2/inspector_integration_test.dart
+++ b/packages/devtools_app/test/screens/inspector_v2/inspector_integration_test.dart
@@ -15,6 +15,7 @@
 import 'package:devtools_app/src/screens/inspector_shared/inspector_controls.dart';
 import 'package:devtools_app/src/screens/inspector_v2/inspector_screen_body.dart';
 import 'package:devtools_app/src/screens/inspector_v2/inspector_tree_controller.dart';
+import 'package:devtools_app/src/screens/inspector_v2/layout_explorer/ui/utils.dart';
 import 'package:devtools_app/src/screens/inspector_v2/widget_properties/properties_view.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_test/helpers.dart';
@@ -41,17 +42,18 @@
 
   final env = FlutterTestEnvironment(
     const FlutterRunConfiguration(withDebugger: true),
-    useTempDirectory: true,
+    testAppDirectory: 'test/test_infra/fixtures/inspector_app',
   );
 
   env.afterEverySetup = () async {
     final service = serviceConnection.inspectorService;
+    await _resetPubRootDirectories(service as InspectorService);
     if (env.reuseTestEnvironment) {
       // Ensure the previous test did not set the selection on the device.
       // TODO(jacobr): add a proper method to WidgetInspectorService that does
       // this. setSelection currently ignores null selection requests which is
       // a misfeature.
-      await service!.inspectorLibrary.eval(
+      await service.inspectorLibrary.eval(
         'WidgetInspectorService.instance.selection.clear()',
         isAlive: null,
       );
@@ -99,7 +101,7 @@
         await _loadInspectorUI(tester);
 
         // Expect the Center widget to be visible in the widget tree.
-        final centerWidgetFinder = find.richText('Center');
+        final centerWidgetFinder = find.richText('CustomCenter');
         expect(centerWidgetFinder, findsOneWidget);
 
         // Trigger a hot-restart and wait for the first Flutter frame.
@@ -128,8 +130,8 @@
     ) async {
       await _loadInspectorUI(tester);
 
-      // Select the Center widget (row index #16)
-      await tester.tap(find.richText('Center'));
+      // Select the CustomCenter widget (row index #4)
+      await tester.tap(find.richText('CustomCenter'));
       await tester.pumpAndSettle(inspectorChangeSettleTime);
       await expectLater(
         find.byType(InspectorScreenBody),
@@ -160,13 +162,13 @@
         // Toggle implementation widgets on.
         await _toggleImplementationWidgets(tester);
 
-        // Before hidden widgets are expanded, confirm the HeroControllerScope
-        // is hidden:
-        final hideableNodeFinder = findNodeMatching('HeroControllerScope');
+        // Before hidden widgets are expanded, confirm the implementing
+        // Container of CustomContainer is hidden:
+        final hideableNodeFinder = findNodeMatching('Container');
         expect(hideableNodeFinder, findsNothing);
 
-        // Expand the hidden group that contains the HeroControllerScope:
-        final moreWidgetsRow = findChildRowOf('MaterialApp');
+        // Expand the hidden group that contains the Container:
+        final moreWidgetsRow = findChildRowOf('CustomContainer');
         final expandButton = findExpandCollapseButtonForRow(
           rowFinder: moreWidgetsRow,
           isExpand: true,
@@ -178,12 +180,12 @@
           matchesDevToolsGolden(
             '../../test_infra/goldens/integration_inspector_v2_implementation_widgets_expanded.png',
           ),
-          // Re-enable and update goldens once Flutter version in DevTools
-          // includes https://github.com/flutter/flutter/pull/169229.
-          skip: 'https://github.com/flutter/devtools/issues/9206',
+          // Implementation widgets from Flutter framework are not guaranteed to
+          // be stable.
+          skip: 'https://github.com/flutter/flutter/issues/172037',
         );
 
-        // Confirm the HeroControllerScope is visible, and select it:
+        // Confirm the Container is visible, and select it:
         expect(hideableNodeFinder, findsOneWidget);
         await tester.tap(hideableNodeFinder);
         await tester.pumpAndSettle(inspectorChangeSettleTime);
@@ -192,15 +194,15 @@
           matchesDevToolsGolden(
             '../../test_infra/goldens/integration_inspector_v2_hideable_widget_selected.png',
           ),
-          // Re-enable and update goldens once Flutter version in DevTools
-          // includes https://github.com/flutter/flutter/pull/169229.
-          skip: 'https://github.com/flutter/devtools/issues/9206',
+          // Implementation widgets from Flutter framework are not guaranteed to
+          // be stable.
+          skip: 'https://github.com/flutter/flutter/issues/172037',
         );
 
-        // Collapse the hidden group that contains the HeroControllerScope:
-        final scrollConfigurationRow = findChildRowOf('MaterialApp');
+        // Collapse the hidden group that contains the Container:
+        final collapsibleRow = findChildRowOf('CustomContainer');
         final collapseButton = findExpandCollapseButtonForRow(
-          rowFinder: scrollConfigurationRow,
+          rowFinder: collapsibleRow,
           isExpand: false,
         );
         await tester.tap(collapseButton);
@@ -222,32 +224,33 @@
       // Toggle implementation widgets on.
       await _toggleImplementationWidgets(tester);
 
-      // Before searching, confirm the HeroControllerScope is hidden:
-      final hideableNodeFinder = findNodeMatching('HeroControllerScope');
+      // Before searching, confirm the implementing DefaultTextStyle of
+      // CustomApp is hidden:
+      final hideableNodeFinder = findNodeMatching('DefaultTextStyle');
       expect(hideableNodeFinder, findsNothing);
 
-      // Search for the HeroControllerScope:
+      // Search for the DefaultTextStyle:
       final searchButtonFinder = find.ancestor(
         of: find.byIcon(Icons.search),
         matching: find.byType(ToolbarAction),
       );
       await tester.tap(searchButtonFinder);
       await tester.pumpAndSettle(inspectorChangeSettleTime);
-      await tester.enterText(find.byType(TextField), 'HeroControllerScope');
+      await tester.enterText(find.byType(TextField), 'DefaultTextStyle');
       await tester.pumpAndSettle(inspectorChangeSettleTime);
       await tester.tap(find.byIcon(Icons.close));
       await tester.pumpAndSettle(inspectorChangeSettleTime);
 
-      // Confirm the HeroControllerScope is visible and selected:
+      // Confirm the DefaultTextStyle is visible and selected:
       expect(hideableNodeFinder, findsOneWidget);
       await expectLater(
         find.byType(InspectorScreenBody),
         matchesDevToolsGolden(
           '../../test_infra/goldens/integration_inspector_v2_hideable_widget_selected_from_search.png',
         ),
-        // Re-enable and update goldens once Flutter version in DevTools
-        // includes https://github.com/flutter/flutter/pull/169229.
-        skip: 'https://github.com/flutter/devtools/issues/9206',
+        // Implementation widgets from Flutter framework are not guaranteed to
+        // be stable.
+        skip: 'https://github.com/flutter/flutter/issues/172037',
       );
     });
   });
@@ -308,53 +311,58 @@
         tester.state(find.byType(InspectorScreenBody))
             as InspectorScreenBodyState;
 
-    // Find the first Text diagnostic node.
+    // Find the CustomText diagnostic node.
     final diagnostics = state.controller.inspectorTree.rowsInTree.value.map(
       (row) => row!.node.diagnostic,
     );
-    final textDiagnostic = diagnostics.firstWhere(
-      (d) => d?.description == 'Text',
+    final customTextDiagnostic = diagnostics.firstWhere(
+      (d) => d?.description == 'CustomText',
     )!;
-    expect(textDiagnostic.isCreatedByLocalProject, isTrue);
+    expect(customTextDiagnostic.isCreatedByLocalProject, isTrue);
 
     // Toggle implementation widgets off.
     await _toggleImplementationWidgets(tester);
 
-    // Verify the Text diagnostic node is still in the tree.
+    // Verify the CustomText diagnostic node is still in the tree.
     final diagnosticsNow = state.controller.inspectorTree.rowsInTree.value.map(
       (row) => row!.node.diagnostic,
     );
     expect(
-      diagnosticsNow.any((d) => d?.valueRef == textDiagnostic.valueRef),
+      diagnosticsNow.any((d) => d?.valueRef == customTextDiagnostic.valueRef),
       isTrue,
     );
 
-    // Get the RichText child of the Text diagnostic node.
+    // Get the implementing Text child of the CustomText diagnostic node.
     final service = serviceConnection.inspectorService as InspectorService;
     final group = service.createObjectGroup('test-group');
-    final textSubtree = await group.getDetailsSubtree(textDiagnostic);
-    final richTextDiagnostic = (await textSubtree!.children)!.firstWhere(
-      (child) => child.description == 'RichText',
+    final customTextSubtree = await group.getDetailsSubtree(
+      customTextDiagnostic,
+    );
+    final textDiagnostic = (await customTextSubtree!.children)!.firstWhere(
+      (child) => child.description == 'Text',
     );
 
-    // Verify the RichText child is an implementation node that is not in the tree.
-    expect(richTextDiagnostic.isCreatedByLocalProject, isFalse);
+    // Verify the Text child is an implementation node that is not in the tree.
+    expect(textDiagnostic.isCreatedByLocalProject, isFalse);
     expect(
-      diagnosticsNow.any((d) => d?.valueRef == richTextDiagnostic.valueRef),
+      diagnosticsNow.any((d) => d?.valueRef == textDiagnostic.valueRef),
       isFalse,
     );
 
-    // Mimic selecting the RichText diagnostic node with the on-device inspector.
-    await group.setSelectionInspector(richTextDiagnostic.valueRef, false);
+    // Mimic selecting the Text diagnostic node with the on-device inspector.
+    await group.setSelectionInspector(textDiagnostic.valueRef, false);
     await tester.pumpAndSettle(inspectorChangeSettleTime);
 
-    // Verify the Text node is now selected.
+    // Verify the CustomText node is now selected.
     final selectedNode = state.controller.selectedNode.value;
-    expect(selectedNode!.diagnostic!.valueRef, equals(textDiagnostic.valueRef));
+    expect(
+      selectedNode!.diagnostic!.valueRef,
+      equals(customTextDiagnostic.valueRef),
+    );
 
     // Verify the notification about selecting an implementation widget is displayed.
     expect(
-      find.text('Selected an implementation widget of Text: RichText.'),
+      find.text('Selected an implementation widget of CustomText: Text.'),
       findsOneWidget,
     );
   });
@@ -364,8 +372,8 @@
   ) async {
     await _loadInspectorUI(tester);
 
-    // Select the Center widget (row index #16)
-    await tester.tap(find.richText('Center'));
+    // Select the CustomCenter widget (row index #4)
+    await tester.tap(find.richText('CustomCenter'));
     await tester.pumpAndSettle(inspectorChangeSettleTime);
 
     // Disable Inspector V2:
@@ -432,10 +440,6 @@
         'textPreview',
         'children',
         'createdByLocalProject',
-        // TODO(elliette): Once we update to the Flutter version with
-        // https://github.com/flutter/flutter/pull/159701, this should be
-        // deleted.
-        'truncated',
       ];
       const extraneousDetailsForTreeNode = [
         'creationLocation',
@@ -511,58 +515,55 @@
       );
     }
 
-    testWidgetsWithWindowSize('changing parent widget of selected', windowSize, (
-      WidgetTester tester,
-    ) async {
-      await _loadInspectorUI(tester);
+    testWidgetsWithWindowSize(
+      'changing parent widget of selected',
+      windowSize,
+      (WidgetTester tester) async {
+        await _loadInspectorUI(tester);
 
-      // Toggle implementation widgets on.
-      await _toggleImplementationWidgets(tester);
+        // Toggle implementation widgets on.
+        await _toggleImplementationWidgets(tester);
 
-      // Give time for the initial animation to complete.
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
+        // Give time for the initial animation to complete.
+        await tester.pumpAndSettle(inspectorChangeSettleTime);
 
-      // Verify the Text widget is after the Center widget.
-      expect(
-        _treeRowsAreInOrder(
-          treeRowDescriptions: ['Center', 'Text: "Hello, World!"'],
-          startingAtIndex: 15,
-        ),
-        isTrue,
-      );
+        // Verify the CustomButton widget is after the CustomCenter widget.
+        expect(
+          _treeRowsAreInOrder(
+            treeRowDescriptions: ['CustomCenter', 'CustomButton'],
+            startingAtIndex: 7,
+          ),
+          isTrue,
+        );
 
-      // Select the Text widget (row index #16).
-      await tester.tap(_findTreeRowMatching('Text: "Hello, World!"'));
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
+        // Verify the CustomButton widget is not visible in the properties view.
+        expect(_findWidgetLabelMatching('CustomButton'), findsNothing);
 
-      // Verify the Text widget is selected (its properties are displayed):
-      verifyPropertyIsVisible(
-        name: 'data',
-        value: '"Hello, World!"',
-        tester: tester,
-      );
+        // Select the CustomButton widget.
+        await tester.tap(_findTreeRowMatching('CustomButton'));
+        await tester.pumpAndSettle(inspectorChangeSettleTime);
 
-      // Make edit to main.dart to replace Center with an Align.
-      makeEditToFlutterMain(toReplace: 'Center', replaceWith: 'Align');
-      await env.flutter!.hotReload();
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
+        // Verify the CustomButton widget is now visible in the properties view.
+        expect(_findWidgetLabelMatching('CustomButton'), findsOneWidget);
 
-      // Verify the Align is now in the widget tree instead of Center.
-      expect(
-        _treeRowsAreInOrder(
-          treeRowDescriptions: ['Align', 'Text: "Hello, World!"'],
-          startingAtIndex: 15,
-        ),
-        isTrue,
-      );
+        // Make edit to main.dart to replace CustomCenter with an Align.
+        makeEditToFlutterMain(toReplace: 'CustomCenter', replaceWith: 'Align');
+        await env.flutter!.hotReload();
+        await tester.pumpAndSettle(inspectorChangeSettleTime);
 
-      // Verify the Text widget is still selected (its properties are displayed):
-      verifyPropertyIsVisible(
-        name: 'data',
-        value: '"Hello, World!"',
-        tester: tester,
-      );
-    });
+        // Verify the Align is now in the widget tree instead of Center.
+        expect(
+          _treeRowsAreInOrder(
+            treeRowDescriptions: ['Align', 'CustomButton'],
+            startingAtIndex: 7,
+          ),
+          isTrue,
+        );
+
+        // Verify the CustomButton widget is still selected.
+        expect(_findWidgetLabelMatching('CustomButton'), findsOneWidget);
+      },
+    );
   });
 
   group('widget errors', () {
@@ -652,7 +653,7 @@
 }
 
 Finder findNodeMatching(String text) => find.ancestor(
-  of: find.richTextContaining(text),
+  of: find.richText(text),
   matching: find.byType(DescriptionDisplay),
 );
 
@@ -777,5 +778,24 @@
   matching: find.byType(InspectorRowContent),
 );
 
+Finder _findWidgetLabelMatching(String description) => find.ancestor(
+  of: find.richText(description),
+  matching: find.byType(WidgetLabel),
+);
+
 T _getWidgetFromFinder<T>(Finder finder) =>
     finder.first.evaluate().first.widget as T;
+
+Future<void> _resetPubRootDirectories(InspectorService inspectorService) async {
+  final currentPubRootDirectories = await inspectorService
+      .getPubRootDirectories();
+  if (currentPubRootDirectories != null) {
+    await inspectorService.removePubRootDirectories(currentPubRootDirectories);
+  }
+
+  final rootLibrary = await serviceConnection.serviceManager
+      .mainIsolateRootLibraryUriAsString();
+  if (rootLibrary != null) {
+    await inspectorService.addPubRootDirectories([rootLibrary]);
+  }
+}
diff --git a/packages/devtools_app/test/test_infra/fixtures/custom_widgets/README.md b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/README.md
new file mode 100644
index 0000000..6b6c507
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/README.md
@@ -0,0 +1,8 @@
+<!--
+Copyright 2025 The Flutter Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+-->
+# custom_widgets
+
+A basic custom widget library used in test_infra/fixtures/inspector_app.
\ No newline at end of file
diff --git a/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/custom_widgets.dart b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/custom_widgets.dart
new file mode 100644
index 0000000..f08d228
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/custom_widgets.dart
@@ -0,0 +1,5 @@
+// Copyright 2025 The Flutter Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+
+export './src/widgets.dart';
diff --git a/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/src/widgets.dart b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/src/widgets.dart
new file mode 100644
index 0000000..4084be2
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/lib/src/widgets.dart
@@ -0,0 +1,144 @@
+// Copyright 2025 The Flutter Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+class CustomApp extends StatelessWidget {
+  const CustomApp({
+    super.key,
+    required this.home,
+  });
+
+  final Widget home;
+
+  @override
+  Widget build(BuildContext context) {
+    return Directionality(
+      textDirection: TextDirection.ltr,
+      child: DefaultTextStyle(
+        style: const TextStyle(
+          color: Color(0xFF000000),
+          fontSize: 14,
+          fontFamily: 'Roboto',
+        ),
+        child: home,
+      ),
+    );
+  }
+}
+
+class CustomContainer extends StatelessWidget {
+  const CustomContainer({
+    super.key,
+    this.child,
+    this.width,
+    this.height,
+    this.color,
+    this.padding,
+    this.margin,
+    this.decoration,
+  });
+
+  final Widget? child;
+  final double? width;
+  final double? height;
+  final Color? color;
+  final EdgeInsetsGeometry? padding;
+  final EdgeInsetsGeometry? margin;
+  final Decoration? decoration;
+
+  @override
+  Widget build(BuildContext context) {
+    // ignore: avoid-wrapping-in-padding, for testing purposes.
+    return Padding(
+        padding: padding ?? EdgeInsets.zero,
+        child: Container(
+          width: width,
+          height: height,
+          margin: margin,
+          decoration: decoration,
+          color: color,
+          child: child,
+        ));
+  }
+}
+
+class CustomCenter extends Align {
+  const CustomCenter(
+      {super.key, super.widthFactor, super.heightFactor, super.child});
+}
+
+class CustomText extends StatelessWidget {
+  const CustomText(
+    this.data, {
+    super.key,
+    this.style,
+  });
+
+  final String data;
+  final TextStyle? style;
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      data,
+      style: style,
+    );
+  }
+}
+
+class CustomButton extends StatefulWidget {
+  const CustomButton({
+    super.key,
+    required this.onPressed,
+    required this.child,
+  });
+
+  final VoidCallback? onPressed;
+  final Widget child;
+
+  @override
+  State<CustomButton> createState() => _CustomButtonState();
+}
+
+class _CustomButtonState extends State<CustomButton> {
+  bool _isPressed = false;
+
+  void _onTapDown(TapDownDetails details) {
+    setState(() {
+      _isPressed = true;
+    });
+  }
+
+  void _onTapUp(TapUpDetails details) {
+    setState(() {
+      _isPressed = false;
+    });
+    widget.onPressed?.call();
+  }
+
+  void _onTapCancel() {
+    setState(() {
+      _isPressed = false;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTapDown: _onTapDown,
+      onTapUp: _onTapUp,
+      onTapCancel: _onTapCancel,
+      child: CustomContainer(
+        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
+        decoration: BoxDecoration(
+          color: _isPressed ? const Color(0xFF0D47A1) : const Color(0xFF2196F3),
+          borderRadius: BorderRadius.circular(8),
+        ),
+        child: widget.child,
+      ),
+    );
+  }
+}
diff --git a/packages/devtools_app/test/test_infra/fixtures/custom_widgets/pubspec.yaml b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/pubspec.yaml
new file mode 100644
index 0000000..ffe19a4
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/custom_widgets/pubspec.yaml
@@ -0,0 +1,10 @@
+name: custom_widgets
+version: 1.0.0
+
+environment:
+  sdk: ^3.2.0
+  flutter: '>=3.0.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
diff --git a/packages/devtools_app/test/test_infra/fixtures/inspector_app/.gitignore b/packages/devtools_app/test/test_infra/fixtures/inspector_app/.gitignore
new file mode 100644
index 0000000..47e0b4d
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/inspector_app/.gitignore
@@ -0,0 +1,71 @@
+# Miscellaneous
+*.class
+*.lock
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Visual Studio Code related
+.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.packages
+.pub-cache/
+.pub/
+build/
+
+# Android related
+**/android/**/gradle-wrapper.jar
+**/android/.gradle
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/packages/devtools_app/test/test_infra/fixtures/inspector_app/README.md b/packages/devtools_app/test/test_infra/fixtures/inspector_app/README.md
new file mode 100644
index 0000000..33bcb56
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/inspector_app/README.md
@@ -0,0 +1,21 @@
+<!--
+Copyright 2025 The Flutter Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+-->
+
+# inspector_app
+
+App for running DevTools integration tests for the Inspector panel.
+
+## `main.dart`
+
+Contains an app built with widgets from the test_infra/fixtures/custom_widgets
+widget library which is used by the Inspector integration test to verify that
+the implementation are displayed correctly in the widget tree.
+
+## `overflow_errors.dart`
+
+Contains an app which includes overflow errors which is used by the Inspector
+integration test to verify that those errors are displayed correctly in the
+widget tree.
diff --git a/packages/devtools_app/test/test_infra/fixtures/inspector_app/lib/main.dart b/packages/devtools_app/test/test_infra/fixtures/inspector_app/lib/main.dart
new file mode 100644
index 0000000..8387b9d
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/inspector_app/lib/main.dart
@@ -0,0 +1,38 @@
+// Copyright 2025 The Flutter Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+
+import 'package:custom_widgets/custom_widgets.dart';
+import 'package:flutter/material.dart';
+
+void main() {
+  runApp(
+    const CustomApp(
+      home: HomeScreen(),
+    ),
+  );
+}
+
+class HomeScreen extends StatelessWidget {
+  const HomeScreen({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return CustomContainer(
+      color: Colors.cyanAccent,
+      child: CustomCenter(
+        child: CustomButton(
+          onPressed: () {},
+          child: const CustomText(
+            'Click Me!',
+            style: TextStyle(
+              color: Colors.white,
+              fontSize: 18,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/packages/devtools_app/test/test_infra/fixtures/flutter_app/lib/overflow_errors.dart b/packages/devtools_app/test/test_infra/fixtures/inspector_app/lib/overflow_errors.dart
similarity index 100%
rename from packages/devtools_app/test/test_infra/fixtures/flutter_app/lib/overflow_errors.dart
rename to packages/devtools_app/test/test_infra/fixtures/inspector_app/lib/overflow_errors.dart
diff --git a/packages/devtools_app/test/test_infra/fixtures/inspector_app/pubspec.yaml b/packages/devtools_app/test/test_infra/fixtures/inspector_app/pubspec.yaml
new file mode 100644
index 0000000..5c9e2f0
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/fixtures/inspector_app/pubspec.yaml
@@ -0,0 +1,24 @@
+# Copyright 2025 The Flutter Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+name: inspector_app
+description: App for running Inspector integration tests.
+publish_to: 'none'
+version: 1.0.0
+
+environment:
+  sdk: ^3.2.0
+  flutter: '>=3.0.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  custom_widgets:
+    path: ../custom_widgets
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+
+flutter:
+  uses-material-design: true
diff --git a/packages/devtools_app/test/test_infra/goldens/codeview_scrollbars.png b/packages/devtools_app/test/test_infra/goldens/codeview_scrollbars.png
index d32db90..3773f9d 100644
--- a/packages/devtools_app/test/test_infra/goldens/codeview_scrollbars.png
+++ b/packages/devtools_app/test/test_infra/goldens/codeview_scrollbars.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_collapsed.png b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_collapsed.png
index f7c02b1..e148d46 100644
--- a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_collapsed.png
+++ b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_collapsed.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_hidden.png b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_hidden.png
index 5c572b5..f1347bd 100644
--- a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_hidden.png
+++ b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_implementation_widgets_hidden.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_initial_load.png b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_initial_load.png
index a2aa7fa..26d0044 100644
--- a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_initial_load.png
+++ b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_initial_load.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_revert_to_legacy.png b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_revert_to_legacy.png
index 18394b8..60a194d 100644
--- a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_revert_to_legacy.png
+++ b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_revert_to_legacy.png
Binary files differ
diff --git a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_select_center.png b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_select_center.png
index f777daa..d2d00d9 100644
--- a/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_select_center.png
+++ b/packages/devtools_app/test/test_infra/goldens/integration_inspector_v2_select_center.png
Binary files differ