Merge branch 'master' into 2.40.2
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index f3b0e91..fb76f88 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -65,7 +65,7 @@
           wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
           echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
           sudo apt-get update
-          sudo apt-get install dcm=1.22.0-1 # To avoid errors add `-1` (build number) to the version
+          sudo apt-get install dcm=1.24.2-1 # To avoid errors add `-1` (build number) to the version
           sudo chmod +x /usr/bin/dcm
           echo "$(dcm --version)"
       - name: Setup Dart SDK
@@ -99,6 +99,24 @@
           PACKAGE: ${{ matrix.package }}
         run: ./tool/ci/package_tests.sh
 
+  test-tool:
+    name: Tool test
+    needs: flutter-prep
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+    steps:
+      - name: git clone
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
+      - name: Load Cached Flutter SDK
+        uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
+        with:
+          path: |
+            ./tool/flutter-sdk
+          key: flutter-sdk-${{ runner.os }}-${{ needs.flutter-prep.outputs.latest_flutter_candidate }}
+      - name: tool/ci/tool_tests.sh
+        run: ./tool/ci/tool_tests.sh
+
   test:
     name: ${{ matrix.bot }}
     needs: flutter-prep
@@ -155,7 +173,7 @@
         run: ./tool/ci/bots.sh
 
       - name: Upload Golden Failure Artifacts
-        uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: golden_image_failures.${{ matrix.bot }}
@@ -165,7 +183,7 @@
         env:
           WORKFLOW_ID: ${{ github.run_id }}
         run: |
-          echo "::notice title=To Quickly Fix Goldens:: Run \`devtools_tool fix-goldens --run-id=$WORKFLOW_ID\` on your local branch."
+          echo "::notice title=To Quickly Fix Goldens:: Run \`dt fix-goldens --run-id=$WORKFLOW_ID\` on your local branch."
 
   devtools-app-integration-test:
     name: devtools_app integration-test ${{ matrix.bot }} - ${{ matrix.device }} - shard ${{ matrix.shard }}
@@ -226,7 +244,7 @@
         run: ./tool/ci/bots.sh
 
       - name: Upload Golden Failure Artifacts
-        uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029
+        uses: actions/upload-artifact@v4
         if: failure()
         with:
           name: golden_image_failures.${{ matrix.bot }}
diff --git a/.github/workflows/daily-dev-bump.yaml b/.github/workflows/daily-dev-bump.yaml
index 6dfa383..15b6d44 100644
--- a/.github/workflows/daily-dev-bump.yaml
+++ b/.github/workflows/daily-dev-bump.yaml
@@ -41,7 +41,6 @@
         with:
           ref: master
 
-      - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
       - name: Load Cached Flutter SDK
         uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
         with:
@@ -59,14 +58,18 @@
         id: version-bump
         run: |
           set -ex
+
+          # Ensure we are using the Dart executable from the Flutter SDK
+          export PATH=`pwd`/tool/flutter-sdk/bin/cache/dart-sdk/bin:`pwd`/tool/flutter-sdk/bin:`pwd`/bin:$PATH
+
           pushd tool/
           dart pub get
           popd
 
-          # Ensure the devtools_tool command is available
+          # Ensure the dt command is available
           export PATH="$PATH":`pwd`/tool/bin
 
-          ORIGINAL_VERSION=$(devtools_tool update-version current-version | tail -1)
+          ORIGINAL_VERSION=$(dt update-version current-version | tail -1)
 
           if [ -z "$UPDATE_TYPE" ]; then
             # If $UPDATE_TYPE is not set, then assume it is dev
@@ -76,14 +79,14 @@
           # If there is a major, minor, or patch bump, do it.
 
           if [ "$UPDATE_TYPE" == "patch+dev" ]; then
-            devtools_tool update-version auto --type patch
-            devtools_tool update-version auto --type dev
+            dt update-version auto --type patch
+            dt update-version auto --type dev
           elif [ "$UPDATE_TYPE" == "minor+dev" ]; then
-            devtools_tool update-version auto --type minor
-            devtools_tool update-version auto --type dev
+            dt update-version auto --type minor
+            dt update-version auto --type dev
           elif [ "$UPDATE_TYPE" == "major+dev" ]; then
-            devtools_tool update-version auto --type major
-            devtools_tool update-version auto --type dev
+            dt update-version auto --type major
+            dt update-version auto --type dev
           elif [ "$UPDATE_TYPE" == "dev" ]; then
             if ! echo "$ORIGINAL_VERSION" | grep -Eq "\-dev\.[0-9]+" ; then
               ERROR_DESCRIPTION="Doing \
@@ -94,13 +97,13 @@
               echo "::error ,title=Cannot do a dev bump on a Release Version ($ORIGINAL_VERSION)::$ERROR_DESCRIPTION" 
               exit 1;
             fi
-            devtools_tool update-version auto --type dev
+            dt update-version auto --type dev
           else
             echo "ERROR: UNEXPECTED UPDATE TYPE: $UPDATE_TYPE"
             exit 1
           fi
 
-          NEW_VERSION=$(devtools_tool update-version current-version | tail -1)
+          NEW_VERSION=$(dt update-version current-version | tail -1)
 
           echo "COMMIT_MESSAGE=Updating from $ORIGINAL_VERSION to $NEW_VERSION" >> $GITHUB_OUTPUT
         env:
diff --git a/packages/.vscode/extensions.json b/.vscode/extensions.json
similarity index 100%
rename from packages/.vscode/extensions.json
rename to .vscode/extensions.json
diff --git a/packages/.vscode/launch.json b/.vscode/launch.json
similarity index 60%
rename from packages/.vscode/launch.json
rename to .vscode/launch.json
index 0e9958b..2f91630 100644
--- a/packages/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -6,7 +6,7 @@
     "configurations": [
         {
             "name": "opened test or devtools",
-            "cwd": "devtools_app",
+            "cwd": "packages/devtools_app",
             "request": "launch",
             "type": "dart",
         },
@@ -14,13 +14,13 @@
             "name": "devtools",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
         },
         {
             "name": "devtools + experiments",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
             "args": [
                 "--dart-define",
                 "memory_disconnect_experience=true",
@@ -32,21 +32,21 @@
             "name": "devtools + profile",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
             "flutterMode": "profile",
         },
         {
             "name": "devtools + release",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
             "flutterMode": "release",
         },
         {
             "name": "devtools + profile + experiments",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
             "flutterMode": "profile",
             "args": [
                 "--dart-define=enable_experiments=true"
@@ -56,63 +56,74 @@
             "name": "devtools + release",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/lib/main.dart",
+            "program": "packages/devtools_app/lib/main.dart",
             "flutterMode": "release",
         },
         {
             "name": "memory/default",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/memory/default.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/memory/default.stager_app.g.dart",
         },
         {
             "name": "memory/diff_snapshot",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/memory/diff_snapshot.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/memory/diff_snapshot.stager_app.g.dart",
         },
         {
             "name": "performance/default",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/performance/default.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/performance/default.stager_app.g.dart",
         },
         {
             "name": "profiler/default",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/cpu_profiler/default.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/cpu_profiler/default.stager_app.g.dart",
         },
         {
             "name": "fixtures/flutter_app",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/fixtures/flutter_app/lib/main.dart",
+            "program": "packages/devtools_app/test/test_infra/fixtures/flutter_app/lib/main.dart",
         },
         {
             "name": "fixtures/memory_app",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart",
-        },
-        {
-            "name": "standalone_ui/vs_code",
-            "request": "launch",
-            "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/standalone_ui/vs_code.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/fixtures/memory_app/lib/main.dart",
         },
         {
             "name": "standalone_ui/editor_sidebar",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_app/test/test_infra/scenes/standalone_ui/editor_sidebar.stager_app.g.dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_sidebar.stager_app.g.dart",
+            "preLaunchTask": "Start DTD on Port 8500",
+        },
+        {
+            "name": "standalone_ui/editor_sidebar + experiments",
+            "request": "launch",
+            "type": "dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_sidebar.stager_app.g.dart",
+            "preLaunchTask": "Start DTD on Port 8500",
+            "args": [
+                "--dart-define=enable_experiments=true"
+            ],
+        },
+        {
+            "name": "standalone_ui/property_editor_sidebar",
+            "request": "launch",
+            "type": "dart",
+            "program": "packages/devtools_app/test/test_infra/scenes/standalone_ui/property_editor_sidebar.stager_app.g.dart",
             "preLaunchTask": "Start DTD on Port 8500",
         },
         {
             "name": "devtools_extensions: foo + sim",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/main.dart",
+            "program": "packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/main.dart",
             "args": [
                 "--dart-define=use_simulated_environment=true"
             ],
@@ -121,7 +132,7 @@
             "name": "devtools_extensions: dart_foo + sim",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_extensions/example/packages_with_extensions/dart_foo/packages/dart_foo_devtools_extension/lib/main.dart",
+            "program": "packages/devtools_extensions/example/packages_with_extensions/dart_foo/packages/dart_foo_devtools_extension/lib/main.dart",
             "args": [
                 "--dart-define=use_simulated_environment=true"
             ],
@@ -130,13 +141,13 @@
             "name": "devtools_extensions: app_that_uses_foo",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_extensions/example/app_that_uses_foo/lib/main.dart",
+            "program": "packages/devtools_extensions/example/app_that_uses_foo/lib/main.dart",
         },
         {
             "name": "devtools_extensions: app_that_uses_foo - bin",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_extensions/example/app_that_uses_foo/bin/script.dart",
+            "program": "packages/devtools_extensions/example/app_that_uses_foo/bin/script.dart",
         },
         {
             "name": "attach",
@@ -147,7 +158,7 @@
             "name": "foo_devtools_extension example + simulated environment",
             "request": "launch",
             "type": "dart",
-            "program": "devtools_extensions/example/foo/packages/foo_devtools_extension/lib/main.dart",
+            "program": "packages/devtools_extensions/example/foo/packages/foo_devtools_extension/lib/main.dart",
             "args": [
                 "--dart-define=use_simulated_environment=true"
             ],
diff --git a/packages/.vscode/settings.json b/.vscode/settings.json
similarity index 60%
rename from packages/.vscode/settings.json
rename to .vscode/settings.json
index 9d209c6..9e614ef 100644
--- a/packages/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,5 +1,5 @@
 {
     // Set current working directory to devtools_app.
-    "terminal.integrated.cwd": "devtools_app",
+    "terminal.integrated.cwd": "packages/devtools_app",
     "dart.showTodos": false,
 }
diff --git a/packages/.vscode/tasks.json b/.vscode/tasks.json
similarity index 100%
rename from packages/.vscode/tasks.json
rename to .vscode/tasks.json
diff --git a/BETA_TESTING.md b/BETA_TESTING.md
index 57df2d1..a60344c 100644
--- a/BETA_TESTING.md
+++ b/BETA_TESTING.md
@@ -47,7 +47,7 @@
     - Make sure to [configure Git to keep your fork in sync](https://docs.github.com/en/get-started/quickstart/fork-a-repo#configuring-git-to-sync-your-fork-with-the-upstream-repository)
     with the upstream DevTools repo.
 
-2. Ensure that you have access to the `devtools_tool` executable by:
+2. Ensure that you have access to the `dt` executable by:
 	- Running `flutter pub get` on the `devtools/tool` directory
 	- Adding the `devtools/tool/bin` folder to your `PATH` environment variable:
 	  - **MacOS Users**
@@ -63,7 +63,7 @@
 		- Click the **New** button and paste in `<DEVTOOLS_DIR>/tool/bin`, replacing `<DEVTOOLS_DIR>`
 		with the local path to your DevTools repo.
 	
-	Explore the commands and helpers that the `devtools_tool` provides by running `devtools_tool -h`. 
+	Explore the commands and helpers that the `dt` provides by running `dt -h`. 
 
 ## Prepare to build DevTools
 
@@ -73,8 +73,8 @@
 git checkout master
 git reset --hard origin/master
 
-devtools_tool update-flutter-sdk
-devtools_tool pub-get --only-main --upgrade
+dt update-flutter-sdk
+dt pub-get --only-main --upgrade
 ```
 
 ## Start DevTools and connect to an app
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5dff668..af2673f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -58,7 +58,7 @@
 to connect to Github with SSH.
 2. Make sure to [configure Git to keep your fork in sync](https://docs.github.com/en/get-started/quickstart/fork-a-repo#configuring-git-to-sync-your-fork-with-the-upstream-repository)
 with the upstream DevTools repo.
-3. Ensure that you have access to the `devtools_tool` executable by:
+3. Ensure that you have access to the DevTools repo management tool exectuable, `dt`:
 	- Running `flutter pub get` on the `devtools/tool` directory
 	- Adding the `devtools/tool/bin` folder to your `PATH` environment variable:
 	  - **MacOS Users**
@@ -74,7 +74,7 @@
 		- Click the **New** button and paste in `<DEVTOOLS_DIR>/tool/bin`, replacing `<DEVTOOLS_DIR>`
 		with the local path to your DevTools repo.
 
-	Explore the commands and helpers that `devtools_tool` provides by running `devtools_tool -h`.
+	Explore the commands and helpers that `dt` provides by running `dt -h`.
 4. **Optional:** enable and activate DCM (Dart Code Metrics) - see the [DCM section below](#enable-and-activate-dcm-dart-code-metrics)
 
 #### Set up your IDE
@@ -88,13 +88,13 @@
 
 ### Workflow for making changes
 
-1. Change your local Flutter SDK to the latest flutter candidate branch: `devtools_tool update-flutter-sdk --from-path`
+1. Change your local Flutter SDK to the latest flutter candidate branch: `dt update-flutter-sdk --from-path`
 
 > Note: Until  https://github.com/flutter/devtools/issues/7939 is fixed, run
-`devtools_tool update-flutter-sdk --use-cache` instead.
+`dt update-flutter-sdk --use-cache` instead.
 
 2. Create a branch from your cloned DevTools repo: `git checkout -b myBranch`
-3. Ensure your branch, dependencies, and generated code are up-to-date: `devtools_tool sync`
+3. Ensure your branch, dependencies, and generated code are up-to-date: `dt sync`
 4. Implement your changes, and commit to your branch: `git commit -m “description”`
 	- If your improvement is user-facing, [document it](packages/devtools_app/release_notes/README.md) in the same PR.
 5. Push to your branch to GitHub: `git push origin myBranch`
@@ -112,13 +112,13 @@
 
 - If at any time you need to re-sync your branch, run:
 	```
-	devtools_tool sync
+	dt sync
 	```
 	This will pull the latest code from the upstream DevTools, upgrade dependencies, and perform code generation.
 
 - If you want to upgrade dependencies and re-generate code (like mocks), but do not want to merge `upstream/master`, instead run
 	```
-	devtools_tool generate-code --upgrade
+	dt generate-code --upgrade
 	```
 
  - To update DCM to the same version as on GitHub bots with apt-get or brew:
@@ -171,7 +171,7 @@
 
 Then you can run DevTools with the server by running the following from the top-level `devtools` directory:
 ```
-devtools_tool serve
+dt serve
 ```
 
 ### DevTools + VS Code integration (IDE-embedded DevTools experience)
@@ -195,7 +195,7 @@
 },
 ```
 
-This instructs VS Code to run the `devtools_tool serve` command instead of running `dart devtools`.
+This instructs VS Code to run the `dt serve` command instead of running `dart devtools`.
 You must set the `LOCAL_DART_SDK` and `FLUTTER_ROOT` env variables correctly for the script to work.
 
 Next, restart VS Code (or run the **Developer: Reload Window** command from the command palette (`F1`))
diff --git a/TESTING.md b/TESTING.md
index 2a2a0f7..1d61b98 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -24,15 +24,15 @@
 the CI. To update your local flutter version, run:
 
 ```shell
-devtools_tool update-flutter-sdk --update-on-path
+dt update-flutter-sdk --update-on-path
 ```
 
-> Note: Running this command requires that you have followed the [set up instructions](CONTRIBUTING.md#set-up-your-devtools-environment) in the DevTools contributing guide regarding cloning the Flutter SDK from GitHub, adding the `devtools_tool` executable to your PATH, and running `dart pub get` in the `tool` directory.
+> Note: Running this command requires that you have followed the [set up instructions](CONTRIBUTING.md#set-up-your-devtools-environment) in the DevTools contributing guide regarding cloning the Flutter SDK from GitHub, adding the `dt` executable to your PATH, and running `dart pub get` in the `tool` directory.
 
 You may need to re-generate the testing mocks before running the tests:
 
 ```shell
-devtools_tool generate-code --upgrade
+dt generate-code --upgrade
 ```
 
 Now you can proceed with running DevTools tests:
@@ -71,7 +71,7 @@
     on the CI. To update your local flutter version, run:
 
         ```shell
-        devtools_tool update-flutter-sdk --update-on-path
+        dt update-flutter-sdk --update-on-path
         ```
 
     - Then proceed with updating the goldens:
diff --git a/TRIAGE.md b/TRIAGE.md
index fe4d06f..910e65a 100644
--- a/TRIAGE.md
+++ b/TRIAGE.md
@@ -2,29 +2,41 @@
 
 ## The Process
 
-DevTools issues are triaged as time allows throughout the week, and during a dedicated triage meeting that occurs weekly.
-During the triage meeting, new and existing issues are categorized, prioritized, and / or commented on to provide updates
-or gather clarification.
+DevTools issues are triaged weekly as time allows or at a dedicated time set aside by the triager.
+The triager is assigned by an automatic rotation of DevTools team members. 
 
-The DevTools discord [channel](https://discord.com/channels/608014603317936148/958862085297672282) is also scrubbed for
-any recent user questions / concerns that require a response.
+## Quick links
+- [Untriaged issues](https://github.com/flutter/devtools/issues?q=is%3Aopen+is%3Aissue+-label%3AP0%2CP1%2CP2%2CP3)
+- [Reproduce to verify issues](https://github.com/flutter/devtools/labels/reproduce%20to%20verify)
+(issues that need to be manually reproduced in order to verify validity)
+- [flutter/flutter issues related to DevTools](https://github.com/flutter/flutter/labels/d%3A%20devtools)
 
-## DevTools Issues
+## Triager responsibilities
 
-Queue: https://github.com/flutter/devtools/issues?q=is%3Aopen+is%3Aissue+-label%3AP0%2CP1%2CP2%2CP3
+The triager should spend about ~1 hour per week on maintaining the health of the DevTools repository.
 
-For each issue, perform all of these tasks that apply:
+1. Triage any [new flutter/devtools issues](https://github.com/flutter/devtools/issues?q=is%3Aopen+is%3Aissue+-label%3AP0%2CP1%2CP2%2CP3)
+by applying [proper labels](#label-the-issue) and [assigning priority](#prioritize-the-issue).
+2. Triage any new [flutter/flutter issues related to DevTools](https://github.com/flutter/flutter/labels/d%3A%20devtools).
+Transfer any issues to the `flutter/devtools` repo that should be tracked on our own issue tracker, and close issues you
+find that are obsolete.
+3. Try to reproduce any issues with the [reproduce to verify](https://github.com/flutter/devtools/labels/reproduce%20to%20verify) label.
+4. Spend at least 20 minutes [cleaning up the issue backlog](#clean-up-the-issue-backlog).
+5. Look through the DevTools discord [channel](https://discord.com/channels/608014603317936148/958862085297672282) for any recent user
+questions or concerns that require a response.
 
-### Label/project the issue:
-* Add labels for its proper category or categories ( “Inspector page”, “debugger page”, “bug”, etc.)
+### Label the issue
+
+* Add labels for its proper category or categories ( “screen: inspector", “screen: network", “bug”, etc.)
+* Add cost labels ("cost: low", "cost: medium", etc.) if you have a good idea of how much work it will
+take to resolve this issue. Leave the cost label off if you do not know.
 * Add label “waiting for customer response” if you requested more details from reporter
 * Add label “fix it friday” if the issue should be fixed and looks easy to fix
 * Add label "good first issue" if the issue looks like an easy starter bug for a new contributor
 
-### Prioritize the issue. 
+### Prioritize the issue
 
 Follow the prioritization rubric [here](https://github.com/flutter/flutter/blob/master/docs/contributing/issue_hygiene/README.md#priorities).
-
 If the issue requires specific expertise, tag a product area owner (see below) in a comment and ask them to take a look. 
 If the issue is actively being worked on or if it needs immediate / almost-immediate attention (P0, P1), assign the issue
 to a product area owner.
@@ -45,9 +57,14 @@
 For anything else that requires immediate attention but does not fit into one of
 the above areas, please tag @kenzieschmoll or @elliette.
 
-## Related Flutter Issues
+**Ping the [hackers-devtools](https://discord.com/channels/608014603317936148/1106667330093723668) discord channel
+about issues marked “severe: …” or “P0”.**
 
-flutter/flutter issues relating to DevTools:
-https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3A%22d%3A+devtools%22+++no%3Amilestone+ 
+### Clean up the issue backlog
 
-Ping the [hackers-devtools](https://discord.com/channels/608014603317936148/1106667330093723668) discord channel about issues marked “severe: …” or “P0”. 
+This step is to ensure the health of the [DevTools issue backlog](https://github.com/flutter/devtools/issues) over time.
+There are a couple of things to do as part of the backlog clean up work:
+- Close any obsolete issues. Recommendation: start with the oldest issues first since these are the most likely to be stale.
+- Add good candidates for product excellence / quality work to the 
+[DevTools Product Excellence project](https://github.com/orgs/flutter/projects/157). This project feeds monthly milestone
+planning for ongoing P.E. work.
diff --git a/packages/analysis_options.yaml b/analysis_options.yaml
similarity index 97%
rename from packages/analysis_options.yaml
rename to analysis_options.yaml
index 37e5dbe..40a61d2 100644
--- a/packages/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -12,9 +12,9 @@
     # treat missing required parameters as a warning (not a hint)
     missing_required_param: warning
   exclude:
-    - build/**
+    - '**/build/**'
     - '**.freezed.dart'
-    - flutter-sdk/
+    - tool/flutter-sdk/
 
 linter:
   rules:
@@ -43,7 +43,6 @@
     - avoid_function_literals_in_foreach_calls
     - avoid_init_to_null
     # - avoid_js_rounded_ints # only useful when targeting JS runtime
-    - avoid_null_checks_in_equality_operators
     # - avoid_positional_boolean_parameters # not yet tested
     - avoid_print
     # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
@@ -86,7 +85,6 @@
     # - one_member_abstracts # too many false positives
     # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
     - overridden_fields
-    - package_api_docs
     - package_names
     - package_prefixed_library_names
     # - parameter_assignments # we do this commonly
@@ -114,7 +112,8 @@
     - prefer_relative_imports
     - prefer_single_quotes
     - prefer_typing_uninitialized_variables
-    - require_trailing_commas
+    # Obsolete with the new Dart formatter.
+    # - require_trailing_commas
     - recursive_getters
     - slash_for_doc_comments
     - sort_child_properties_last
@@ -138,7 +137,6 @@
     - unnecessary_statements
     - unnecessary_this
     - unrelated_type_equality_checks
-    - unsafe_html
     - use_rethrow_when_possible
     # - use_setters_to_change_properties # not yet tested
     # - use_string_buffers # https://github.com/dart-lang/linter/pull/664
@@ -226,7 +224,8 @@
     # - prefer-moving-to-variable:
     #    allowed-duplicated-chains: 2
     # - prefer-static-class
-    - prefer-trailing-comma
+    # Obsolete with the new Dart formatter.
+    # - prefer-trailing-comma
     - always-remove-listener
     # - avoid-border-all Micro-optimization to avoid a const constructor.
     # - avoid-returning-widgets  This one is nice but has a lot of false positives.
diff --git a/case_study/code_size/optimized/code_size_images/lib/animals.dart b/case_study/code_size/optimized/code_size_images/lib/animals.dart
index e58ddde..ff0a498 100644
--- a/case_study/code_size/optimized/code_size_images/lib/animals.dart
+++ b/case_study/code_size/optimized/code_size_images/lib/animals.dart
@@ -1,6 +1,8 @@
 import 'package:flutter/material.dart';
 
 class Animals extends StatelessWidget {
+  const Animals({super.key});
+
   @override
   Widget build(BuildContext context) {
     return ListView(
diff --git a/case_study/code_size/optimized/code_size_images/lib/main.dart b/case_study/code_size/optimized/code_size_images/lib/main.dart
index 77a7e66..21a8369 100644
--- a/case_study/code_size/optimized/code_size_images/lib/main.dart
+++ b/case_study/code_size/optimized/code_size_images/lib/main.dart
@@ -3,10 +3,12 @@
 import 'animals.dart';
 
 void main() {
-  runApp(MyApp());
+  runApp(const MyApp());
 }
 
 class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
   // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
@@ -22,7 +24,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  const MyHomePage({Key? key}) : super(key: key);
+  const MyHomePage({super.key});
 
   @override
   State<MyHomePage> createState() => _MyHomePageState();
@@ -35,7 +37,7 @@
       appBar: AppBar(
         title: const Text('Code Size Demo'),
       ),
-      body: Center(
+      body: const Center(
         child: Animals(),
       ),
     );
diff --git a/case_study/code_size/optimized/code_size_images/test/widget_test.dart b/case_study/code_size/optimized/code_size_images/test/widget_test.dart
index ba11e18..bb00e04 100644
--- a/case_study/code_size/optimized/code_size_images/test/widget_test.dart
+++ b/case_study/code_size/optimized/code_size_images/test/widget_test.dart
@@ -12,7 +12,7 @@
 void main() {
   testWidgets('Counter increments smoke test', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    await tester.pumpWidget(const MyApp());
 
     // Verify that our counter starts at 0.
     expect(find.text('0'), findsOneWidget);
diff --git a/case_study/code_size/optimized/code_size_package/lib/main.dart b/case_study/code_size/optimized/code_size_package/lib/main.dart
index 0be9c94..d676f92 100644
--- a/case_study/code_size/optimized/code_size_package/lib/main.dart
+++ b/case_study/code_size/optimized/code_size_package/lib/main.dart
@@ -5,10 +5,12 @@
 import 'package:steel_crypt/steel_crypt.dart' as encrypt;
 
 void main() {
-  runApp(MyApp());
+  runApp(const MyApp());
 }
 
 class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
@@ -22,7 +24,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  const MyHomePage({Key? key, required this.title}) : super(key: key);
+  const MyHomePage({super.key, required this.title});
 
   final String title;
 
diff --git a/case_study/code_size/optimized/code_size_package/test/widget_test.dart b/case_study/code_size/optimized/code_size_package/test/widget_test.dart
index 5ddbd62..36cba9a 100644
--- a/case_study/code_size/optimized/code_size_package/test/widget_test.dart
+++ b/case_study/code_size/optimized/code_size_package/test/widget_test.dart
@@ -12,7 +12,7 @@
 void main() {
   testWidgets('Counter increments smoke test', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    await tester.pumpWidget(const MyApp());
 
     // Verify that our counter starts at 0.
     expect(find.text('0'), findsOneWidget);
diff --git a/case_study/code_size/unoptimized/code_size_images/lib/animals.dart b/case_study/code_size/unoptimized/code_size_images/lib/animals.dart
index e58ddde..ff0a498 100644
--- a/case_study/code_size/unoptimized/code_size_images/lib/animals.dart
+++ b/case_study/code_size/unoptimized/code_size_images/lib/animals.dart
@@ -1,6 +1,8 @@
 import 'package:flutter/material.dart';
 
 class Animals extends StatelessWidget {
+  const Animals({super.key});
+
   @override
   Widget build(BuildContext context) {
     return ListView(
diff --git a/case_study/code_size/unoptimized/code_size_images/lib/main.dart b/case_study/code_size/unoptimized/code_size_images/lib/main.dart
index 77a7e66..21a8369 100644
--- a/case_study/code_size/unoptimized/code_size_images/lib/main.dart
+++ b/case_study/code_size/unoptimized/code_size_images/lib/main.dart
@@ -3,10 +3,12 @@
 import 'animals.dart';
 
 void main() {
-  runApp(MyApp());
+  runApp(const MyApp());
 }
 
 class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
   // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
@@ -22,7 +24,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  const MyHomePage({Key? key}) : super(key: key);
+  const MyHomePage({super.key});
 
   @override
   State<MyHomePage> createState() => _MyHomePageState();
@@ -35,7 +37,7 @@
       appBar: AppBar(
         title: const Text('Code Size Demo'),
       ),
-      body: Center(
+      body: const Center(
         child: Animals(),
       ),
     );
diff --git a/case_study/code_size/unoptimized/code_size_images/test/widget_test.dart b/case_study/code_size/unoptimized/code_size_images/test/widget_test.dart
index ba11e18..bb00e04 100644
--- a/case_study/code_size/unoptimized/code_size_images/test/widget_test.dart
+++ b/case_study/code_size/unoptimized/code_size_images/test/widget_test.dart
@@ -12,7 +12,7 @@
 void main() {
   testWidgets('Counter increments smoke test', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    await tester.pumpWidget(const MyApp());
 
     // Verify that our counter starts at 0.
     expect(find.text('0'), findsOneWidget);
diff --git a/case_study/code_size/unoptimized/code_size_package/lib/main.dart b/case_study/code_size/unoptimized/code_size_package/lib/main.dart
index b2bda32..3f9e149 100644
--- a/case_study/code_size/unoptimized/code_size_package/lib/main.dart
+++ b/case_study/code_size/unoptimized/code_size_package/lib/main.dart
@@ -2,10 +2,12 @@
 import 'package:flutter/material.dart';
 
 void main() {
-  runApp(MyApp());
+  runApp(const MyApp());
 }
 
 class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
@@ -19,7 +21,7 @@
 }
 
 class MyHomePage extends StatefulWidget {
-  const MyHomePage({Key? key, required this.title}) : super(key: key);
+  const MyHomePage({super.key, required this.title});
 
   final String title;
 
diff --git a/case_study/code_size/unoptimized/code_size_package/test/widget_test.dart b/case_study/code_size/unoptimized/code_size_package/test/widget_test.dart
index 5ddbd62..36cba9a 100644
--- a/case_study/code_size/unoptimized/code_size_package/test/widget_test.dart
+++ b/case_study/code_size/unoptimized/code_size_package/test/widget_test.dart
@@ -12,7 +12,7 @@
 void main() {
   testWidgets('Counter increments smoke test', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    await tester.pumpWidget(const MyApp());
 
     // Verify that our counter starts at 0.
     expect(find.text('0'), findsOneWidget);
diff --git a/case_study/memory_leaks/memory_leak_app/lib/about.dart b/case_study/memory_leaks/memory_leak_app/lib/about.dart
index 113ab2b..6189871 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/about.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/about.dart
@@ -7,13 +7,16 @@
 import 'common.dart';
 
 class About extends StatefulWidget {
+  // ignore: prefer_const_constructors_in_immutables, intentional example code.
+  About({super.key});
+
   @override
   State<About> createState() => AboutState();
 }
 
 class AboutState extends State<About> {
-  static const String heading = '$aboutMenu\n\n';
-  static const String helpText = '''
+  static const heading = '$aboutMenu\n\n';
+  static const helpText = '''
 This application makes Restful HTTP GET
 requests to three different Restful servers.
 Selecting a request e.g., Weather will
@@ -23,13 +26,13 @@
 
 The menu, on the main page, has options:
 ''';
-  static const String logOption = '\n    $logMenu';
-  static const String aboutOption = '\n    $aboutMenu';
+  static const logOption = '\n    $logMenu';
+  static const aboutOption = '\n    $aboutMenu';
 
-  static const String logDescr = ' display all messages.';
-  static const String aboutDescr = ' display this page.';
+  static const logDescr = ' display all messages.';
+  static const aboutDescr = ' display this page.';
 
-  final TextStyle defaultStyle = const TextStyle(
+  final defaultStyle = const TextStyle(
     fontSize: 20,
     color: Colors.blueGrey,
   );
diff --git a/case_study/memory_leaks/memory_leak_app/lib/common.dart b/case_study/memory_leaks/memory_leak_app/lib/common.dart
index c63b179..749806a 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/common.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/common.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const String appName = 'Memory Leak';
+const appName = 'Memory Leak';
 
-const String logMenu = 'Log';
-const String aboutMenu = 'About';
+const logMenu = 'Log';
+const aboutMenu = 'About';
diff --git a/case_study/memory_leaks/memory_leak_app/lib/logging.dart b/case_study/memory_leaks/memory_leak_app/lib/logging.dart
index 7444fd6..f767a16 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/logging.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/logging.dart
@@ -19,10 +19,10 @@
     return _theLogging!;
   }
 
-  final List<String> _logs = [];
+  final _logs = <String>[];
 
   void add(String entry) {
-    final TimeStamp newTimeStamp = TimeStamp.record(DateTime.now());
+    final newTimeStamp = TimeStamp.record(DateTime.now());
 
     _logs.add('[${model.log.length}] : ${newTimeStamp.time}] $entry');
   }
@@ -61,9 +61,9 @@
 
   List<TimeStamp> log = <TimeStamp>[];
 
-  final String _time = '';
-  final String _date = '';
-  final String _meridiem = '';
+  final _time = '';
+  final _date = '';
+  final _meridiem = '';
   Timer? _clockUpdateTimer;
   DateTime now = DateTime.now();
 
@@ -118,7 +118,7 @@
       return;
     }
 
-    final TimeStamp newTimeStamp = TimeStamp.record(now);
+    final newTimeStamp = TimeStamp.record(now);
 
     log.add(newTimeStamp);
 
diff --git a/case_study/memory_leaks/memory_leak_app/lib/main.dart b/case_study/memory_leaks/memory_leak_app/lib/main.dart
index 84d58a2..fa7d41b 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/main.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/main.dart
@@ -16,7 +16,7 @@
   Logging.logging.add('Starting...');
 
   runApp(
-    MaterialApp(
+    const MaterialApp(
       // Title
       title: appName,
       // Home
@@ -26,6 +26,8 @@
 }
 
 class MyHome extends StatefulWidget {
+  const MyHome({super.key});
+
   @override
   State<MyHome> createState() => MyHomeState();
 }
@@ -101,6 +103,7 @@
         );
         break;
       default:
+        // ignore: avoid_print, fine for example app.
         print('ERROR: Unhandled Menu.');
     }
   }
diff --git a/case_study/memory_leaks/memory_leak_app/lib/restful_servers.dart b/case_study/memory_leaks/memory_leak_app/lib/restful_servers.dart
index c4bcce7..9c764d5 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/restful_servers.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/restful_servers.dart
@@ -13,9 +13,9 @@
 
   String get activeFriendlyName;
 
-  dynamic findData(dynamic data);
+  Object? findData(Object? data);
 
-  String display(dynamic data, int index);
+  String display(Object? data, int index);
 }
 
 /// StarWars information server.
@@ -25,20 +25,20 @@
     _activeFriendlyName = name;
   }
 
-  static const String starWarsFilms = 'StarWars Films';
-  static const String starWarsPeople = 'StarWars People';
-  static const String starWarsPlanets = 'StarWars Planets';
-  static const String starWarsSpecies = 'StarWars Species';
-  static const String starWarsStarships = 'StarWars Starships';
-  static const String starWarsVehicles = 'StarWars Vehicles';
+  static const starWarsFilms = 'StarWars Films';
+  static const starWarsPeople = 'StarWars People';
+  static const starWarsPlanets = 'StarWars Planets';
+  static const starWarsSpecies = 'StarWars Species';
+  static const starWarsStarships = 'StarWars Starships';
+  static const starWarsVehicles = 'StarWars Vehicles';
 
-  static const Map<String, String> _friendlyNames = {
-    '$starWarsFilms': 'https://swapi.co/api/films',
-    '$starWarsPeople': 'https://swapi.co/api/people',
-    '$starWarsPlanets': 'https://swapi.co/api/planets',
-    '$starWarsSpecies': 'https://swapi.co/api/species',
-    '$starWarsStarships': 'https://swapi.co/api/starships',
-    '$starWarsVehicles': 'https://swapi.co/api/vehicles',
+  static const _friendlyNames = {
+    starWarsFilms: 'https://swapi.co/api/films',
+    starWarsPeople: 'https://swapi.co/api/people',
+    starWarsPlanets: 'https://swapi.co/api/planets',
+    starWarsSpecies: 'https://swapi.co/api/species',
+    starWarsStarships: 'https://swapi.co/api/starships',
+    starWarsVehicles: 'https://swapi.co/api/vehicles',
   };
 
   late final String _activeFriendlyName;
@@ -53,14 +53,16 @@
   String uri() => _defaultUri;
 
   @override
-  dynamic findData(dynamic data) => data['results'];
+  Object? findData(Object? data) => (data as Map)['results'];
 
   @override
-  String display(dynamic data, int index) {
+  String display(Object? data, int index) {
     // data from film Restful URI has slightly different format
     // title instead of name.
     final isFilm = _defaultUri == _friendlyNames[starWarsFilms];
-    return data == null ? '' : data[index][isFilm ? 'title' : 'name'];
+    return data == null
+        ? ''
+        : ((data as Map)[index] as Map)[isFilm ? 'title' : 'name'];
   }
 }
 
@@ -70,7 +72,7 @@
   static const citiBikesUri =
       'https://feeds.citibikenyc.com/stations/stations.json';
 
-  static const String friendlyName = 'NYC Bike Sharing';
+  static const friendlyName = 'NYC Bike Sharing';
 
   @override
   String uri() => citiBikesUri;
@@ -79,11 +81,11 @@
   String get activeFriendlyName => friendlyName;
 
   @override
-  dynamic findData(dynamic data) => data['stationBeanList'];
+  Object? findData(Object? data) => (data as Map)['stationBeanList'];
 
   @override
-  String display(dynamic data, int index) =>
-      data == null ? '' : data[index]['stationName'];
+  String display(Object? data, int index) =>
+      data == null ? '' : ((data as List)[index] as Map)['stationName'];
 }
 
 class CityInformation {
@@ -108,7 +110,7 @@
 ///   Subscribe for free OpenWeatherMap then create an appid using:
 ///       https://home.openweathermap.org/api_keys
 class OpenWeatherMapAPI extends RestfulAPI {
-  static const String friendlyName = 'Weather';
+  static const friendlyName = 'Weather';
 
   static const _baseUrl = 'http://api.openweathermap.org/data/2.5/group?id=';
   static const _unitsOption = '&units=imperial';
@@ -145,9 +147,9 @@
   CityInformation? secondCity;
 
   String _cityIdsList({int initialStart = -1, int count = 20}) {
-    final StringBuffer buff = StringBuffer();
+    final buff = StringBuffer();
 
-    final int start = initialStart == -1 ? randomSeed() : initialStart;
+    final start = initialStart == -1 ? randomSeed() : initialStart;
     firstCity = CityInformation(_cityIds.keys.toList()[start]);
 
     secondCity = firstCity;
@@ -174,18 +176,19 @@
   String get activeFriendlyName => friendlyName;
 
   @override
-  dynamic findData(dynamic data) => data['list']; // weather group
+  Object? findData(Object? data) => (data as Map)['list']; // weather group
 
-  static String cityName(dynamic data, int index) => '${data[index]['name']}';
+  static String cityName(Object? data, int index) =>
+      '${((data as List)[index] as Map)['name']}';
 
-  static String temperature(dynamic data, int index) =>
-      '${data[index]['main']['temp']}';
+  static String temperature(Object? data, int index) =>
+      '${(((data as List)[index] as Map)['main'] as Map)['temp']}';
 
-  static String weather(dynamic data, int index) =>
-      '${data[index]['weather'][0]['main']}';
+  static String weather(Object? data, int index) =>
+      '${((((data as Map)[index] as Map)['weather'] as List)[0] as Map)['main']}';
 
   @override
-  String display(dynamic data, int index) =>
+  String display(Object? data, int index) =>
       '${OpenWeatherMapAPI.cityName(data, index)} '
       '${OpenWeatherMapAPI.temperature(data, index)} '
       '${OpenWeatherMapAPI.weather(data, index)}';
diff --git a/case_study/memory_leaks/memory_leak_app/lib/tabs/http_data.dart b/case_study/memory_leaks/memory_leak_app/lib/tabs/http_data.dart
index fbecb2a..ed14506 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/tabs/http_data.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/tabs/http_data.dart
@@ -15,6 +15,8 @@
 
 /// Create a stateful widget
 class MyGetHttpData extends StatefulWidget {
+  const MyGetHttpData({super.key});
+
   @override
   State<MyGetHttpData> createState() => MyGetHttpDataState();
 }
@@ -25,23 +27,23 @@
     api = currentRestfulAPI = computeUri();
   }
 
-  final Logging logs = Logging.logging;
+  final logs = Logging.logging;
 
   late RestfulAPI api;
-  List? data;
+  List<Object?>? data;
 
   RestfulAPI computeUri() {
     switch (restfulApi) {
-      case '${OpenWeatherMapAPI.friendlyName}':
+      case OpenWeatherMapAPI.friendlyName:
         return OpenWeatherMapAPI();
-      case '${CitiBikesNYC.friendlyName}':
+      case CitiBikesNYC.friendlyName:
         return CitiBikesNYC();
-      case '${StarWars.starWarsFilms}':
-      case '${StarWars.starWarsPeople}':
-      case '${StarWars.starWarsPlanets}':
-      case '${StarWars.starWarsSpecies}':
-      case '${StarWars.starWarsStarships}':
-      case '${StarWars.starWarsVehicles}':
+      case StarWars.starWarsFilms:
+      case StarWars.starWarsPeople:
+      case StarWars.starWarsPlanets:
+      case StarWars.starWarsSpecies:
+      case StarWars.starWarsStarships:
+      case StarWars.starWarsVehicles:
         return StarWars(restfulApi);
       default:
         return StarWars();
@@ -73,7 +75,7 @@
       // Get the JSON data
       final dataConvertedToJSON = json.decode(response.body);
       // Extract the required part and assign it to the global variable named data
-      data = api.findData(dataConvertedToJSON);
+      data = api.findData(dataConvertedToJSON) as List;
     });
 
     return 'Successful';
@@ -91,7 +93,6 @@
         bottom: PreferredSize(
           preferredSize: const Size.fromHeight(48.0),
           child: Theme(
-            // ignore: deprecated_member_use
             data: Theme.of(context),
             child: Container(
               height: 48.0,
diff --git a/case_study/memory_leaks/memory_leak_app/lib/tabs/logger.dart b/case_study/memory_leaks/memory_leak_app/lib/tabs/logger.dart
index 1c13d5e..f563b0e 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/tabs/logger.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/tabs/logger.dart
@@ -9,11 +9,13 @@
 import '../common.dart';
 import '../logging.dart';
 
-//  class Logger extends StatelessWidget {
 class Logger extends StatefulWidget {
-  final Logging _logging = Logging.logging;
+  Logger({super.key});
+
+  final _logging = Logging.logging;
 
   @override
+  // ignore: no_logic_in_create_state, intentional leaking example.
   State<Logger> createState() => LoggerState(_logging);
 }
 
@@ -21,7 +23,7 @@
   LoggerState(this._logging);
 
   final Logging _logging;
-  final List<String> _saved = [];
+  final _saved = <String>[];
   final _biggerFont = const TextStyle(fontSize: 18.0);
 
   @override
@@ -34,7 +36,7 @@
     );
   }
 
-  // ignore: unused_element
+  // ignore: unused_element, intentional example code.
   void _pushSaved() {
     unawaited(
       Navigator.of(context).push(
diff --git a/case_study/memory_leaks/memory_leak_app/lib/tabs/settings.dart b/case_study/memory_leaks/memory_leak_app/lib/tabs/settings.dart
index 3b27adb..1814805 100644
--- a/case_study/memory_leaks/memory_leak_app/lib/tabs/settings.dart
+++ b/case_study/memory_leaks/memory_leak_app/lib/tabs/settings.dart
@@ -11,15 +11,16 @@
 import 'http_data.dart';
 
 class Settings extends StatefulWidget {
-  Settings() : restfulRoot = currentRestfulAPI;
+  Settings({super.key}) : restfulRoot = currentRestfulAPI;
 
-  final Logging logs = Logging.logging;
+  final logs = Logging.logging;
 
   static late final SettingsState state;
 
   final RestfulAPI restfulRoot;
 
   @override
+  // ignore: no_logic_in_create_state, intentional leaking example.
   State<Settings> createState() {
     state = SettingsState();
     return state;
@@ -35,17 +36,17 @@
 
 class SettingsState extends State<Settings> {
   Map<String, IconData> values = {
-    '${CitiBikesNYC.friendlyName}': Icons.directions_bike,
-    '${StarWars.starWarsFilms}': Icons.videocam,
-    '${StarWars.starWarsPeople}': Icons.people_outline,
-    '${StarWars.starWarsPlanets}': Icons.bubble_chart,
-    '${StarWars.starWarsSpecies}': Icons.android,
-    '${StarWars.starWarsStarships}': Icons.tram,
-    '${StarWars.starWarsVehicles}': Icons.time_to_leave,
-    '${OpenWeatherMapAPI.friendlyName}': Icons.cloud,
+    CitiBikesNYC.friendlyName: Icons.directions_bike,
+    StarWars.starWarsFilms: Icons.videocam,
+    StarWars.starWarsPeople: Icons.people_outline,
+    StarWars.starWarsPlanets: Icons.bubble_chart,
+    StarWars.starWarsSpecies: Icons.android,
+    StarWars.starWarsStarships: Icons.tram,
+    StarWars.starWarsVehicles: Icons.time_to_leave,
+    OpenWeatherMapAPI.friendlyName: Icons.cloud,
   };
 
-  final Logging logs = Logging.logging;
+  final logs = Logging.logging;
 
   @override
   Widget build(BuildContext context) {
@@ -66,7 +67,8 @@
               unawaited(
                 Navigator.push(
                   context,
-                  MaterialPageRoute(builder: (context) => MyGetHttpData()),
+                  MaterialPageRoute(
+                      builder: (context) => const MyGetHttpData()),
                 ),
               );
             },
diff --git a/case_study/platform_channel/lib/channel_demo.dart b/case_study/platform_channel/lib/channel_demo.dart
index 2312175..4ddbcd4 100644
--- a/case_study/platform_channel/lib/channel_demo.dart
+++ b/case_study/platform_channel/lib/channel_demo.dart
@@ -4,6 +4,8 @@
 import 'package:flutter/services.dart';
 
 class ChannelDemo extends StatefulWidget {
+  const ChannelDemo({super.key});
+
   @override
   State<ChannelDemo> createState() => _ChannelDemoState();
 }
diff --git a/case_study/platform_channel/lib/main.dart b/case_study/platform_channel/lib/main.dart
index 34ddf95..632d6ce 100644
--- a/case_study/platform_channel/lib/main.dart
+++ b/case_study/platform_channel/lib/main.dart
@@ -4,11 +4,13 @@
 
 import 'channel_demo.dart';
 
-void main() => runApp(MyApp());
+void main() => runApp(const MyApp());
 
 const platformChannelTitle = 'Platform Channel Demo';
 
 class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
@@ -35,7 +37,7 @@
             unawaited(
               Navigator.push(
                 context,
-                MaterialPageRoute(builder: (context) => ChannelDemo()),
+                MaterialPageRoute(builder: (context) => const ChannelDemo()),
               ),
             );
           },
diff --git a/case_study/platform_channel/test/widget_test.dart b/case_study/platform_channel/test/widget_test.dart
index b5c8be0..0fd3564 100644
--- a/case_study/platform_channel/test/widget_test.dart
+++ b/case_study/platform_channel/test/widget_test.dart
@@ -13,7 +13,7 @@
 void main() {
   testWidgets('Counter increments smoke test', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    await tester.pumpWidget(const MyApp());
 
     // Verify that our counter starts at 0.
     expect(find.text('0'), findsOneWidget);
diff --git a/flutter-candidate.txt b/flutter-candidate.txt
index 6a435c0..5556d16 100644
--- a/flutter-candidate.txt
+++ b/flutter-candidate.txt
@@ -1 +1 @@
-e5cbed969cb2916bdfd1bb501e5533bef9feef64
+a0ba2decab156c88708c4261d40660ab8f60da5f
diff --git a/packages/devtools_app/analysis_options.yaml b/packages/devtools_app/analysis_options.yaml
index 245c0ff..b90b481 100644
--- a/packages/devtools_app/analysis_options.yaml
+++ b/packages/devtools_app/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: ../analysis_options.yaml
+include: ../../analysis_options.yaml
 
 analyzer:
   exclude:
diff --git a/packages/devtools_app/benchmark/devtools_benchmarks_test.dart b/packages/devtools_app/benchmark/devtools_benchmarks_test.dart
index a6c6422..1d5111f 100644
--- a/packages/devtools_app/benchmark/devtools_benchmarks_test.dart
+++ b/packages/devtools_app/benchmark/devtools_benchmarks_test.dart
@@ -8,28 +8,17 @@
 import 'dart:convert' show JsonEncoder;
 import 'dart:io';
 
+import 'package:collection/collection.dart';
 import 'package:test/test.dart';
+import 'package:web_benchmarks/metrics.dart';
 import 'package:web_benchmarks/server.dart';
 
 import 'test_infra/common.dart';
 import 'test_infra/project_root_directory.dart';
 
-List<String> metricList({required bool useWasm}) => [
-      // The skwasm renderer doesn't have preroll or apply frame steps in its
-      // rendering.
-      if (!useWasm) ...[
-        'preroll_frame',
-        'apply_frame',
-      ],
-      'drawFrameDuration',
-    ];
+const _isWasmScore = 'isWasm';
 
-final valueList = <String>[
-  'average',
-  'outlierAverage',
-  'outlierRatio',
-  'noise',
-];
+const _extraScores = [totalUiFrameAverage, _isWasmScore];
 
 /// Tests that the DevTools web benchmarks are run and reported correctly.
 void main() {
@@ -40,10 +29,6 @@
         await _runBenchmarks(useWasm: useWasm);
       },
       timeout: const Timeout(Duration(minutes: 10)),
-      // TODO(https://github.com/dart-lang/sdk/issues/56664): unskip the wasm
-      // benchmarks once this issue is resolved and we can bump DevTools to a
-      // version of Flutter that includes the fix.
-      skip: useWasm,
     );
   }
 
@@ -54,32 +39,38 @@
   stdout.writeln('Starting web benchmark tests ...');
   final taskResult = await serveWebBenchmark(
     benchmarkAppDirectory: projectRootDirectory(),
-    entryPoint: 'benchmark/test_infra/client.dart',
-    compilationOptions: useWasm
-        ? const CompilationOptions.wasm()
-        : const CompilationOptions.js(),
+    entryPoint: generateBenchmarkEntryPoint(useWasm: useWasm),
+    compilationOptions:
+        useWasm
+            ? const CompilationOptions.wasm()
+            : const CompilationOptions.js(),
     treeShakeIcons: false,
-    initialPage: benchmarkInitialPage,
+    benchmarkPath: benchmarkPath(useWasm: useWasm),
   );
   stdout.writeln('Web benchmark tests finished.');
 
-  expect(
-    taskResult.scores.keys,
-    hasLength(DevToolsBenchmark.values.length),
-  );
+  expect(taskResult.scores.keys, hasLength(DevToolsBenchmark.values.length));
 
   for (final benchmarkName in DevToolsBenchmark.values.map((e) => e.id)) {
-    final expectedMetrics = metricList(useWasm: useWasm);
+    final expectedMetrics =
+        expectedBenchmarkMetrics(
+          useWasm: useWasm,
+        ).map((BenchmarkMetric metric) => metric.label).toList();
+    const expectedComputations = BenchmarkMetricComputation.values;
+    final scores = taskResult.scores[benchmarkName] ?? [];
     expect(
-      taskResult.scores[benchmarkName],
-      hasLength(expectedMetrics.length * valueList.length + 1),
+      scores,
+      hasLength(
+        expectedMetrics.length * expectedComputations.length +
+            _extraScores.length,
+      ),
     );
 
     for (final metricName in expectedMetrics) {
-      for (final valueName in valueList) {
+      for (final computation in expectedComputations) {
         expect(
-          taskResult.scores[benchmarkName]?.where(
-            (score) => score.metric == '$metricName.$valueName',
+          scores.where(
+            (score) => score.metric == '$metricName.${computation.name}',
           ),
           hasLength(1),
         );
@@ -87,11 +78,15 @@
     }
 
     expect(
-      taskResult.scores[benchmarkName]?.where(
-        (score) => score.metric == 'totalUiFrame.average',
-      ),
+      scores.where((score) => score.metric == totalUiFrameAverage),
       hasLength(1),
     );
+
+    final isWasmScore = scores.firstWhereOrNull(
+      (BenchmarkScore score) => score.metric == _isWasmScore,
+    );
+    expect(isWasmScore, isNotNull);
+    expect(isWasmScore!.value, useWasm ? 1 : 0);
   }
 
   expect(
diff --git a/packages/devtools_app/benchmark/scripts/args.dart b/packages/devtools_app/benchmark/scripts/args.dart
index ae699cf..cf007a3 100644
--- a/packages/devtools_app/benchmark/scripts/args.dart
+++ b/packages/devtools_app/benchmark/scripts/args.dart
@@ -29,7 +29,8 @@
   void addSaveToFileOption(BenchmarkResultsOutputType type) {
     addOption(
       BenchmarkArgument.saveToFile.flagName,
-      help: 'Saves the benchmark results to a ${type.name} file at the '
+      help:
+          'Saves the benchmark results to a ${type.name} file at the '
           'provided path (absolute).',
       valueHelp: '/Users/me/Downloads/output.${type.name}',
     );
@@ -39,7 +40,8 @@
     addOption(
       BenchmarkArgument.averageOf.flagName,
       defaultsTo: '1',
-      help: 'The number of times to run the benchmark. The returned results '
+      help:
+          'The number of times to run the benchmark. The returned results '
           'will be the average of all the benchmark runs when this value is '
           'greater than 1.',
       valueHelp: '5',
@@ -49,7 +51,8 @@
   void addBaselineOption({String? additionalHelp}) {
     addOption(
       BenchmarkArgument.baseline.flagName,
-      help: 'The baseline benchmark data to compare the test benchmark run to. '
+      help:
+          'The baseline benchmark data to compare the test benchmark run to. '
           '${additionalHelp ?? ''}',
       valueHelp: '/Users/me/Downloads/baseline.json',
     );
@@ -73,7 +76,4 @@
 }
 
 /// The file types that benchmark results may be written to.
-enum BenchmarkResultsOutputType {
-  csv,
-  json,
-}
+enum BenchmarkResultsOutputType { csv, json }
diff --git a/packages/devtools_app/benchmark/scripts/compare_benchmarks.dart b/packages/devtools_app/benchmark/scripts/compare_benchmarks.dart
index 515132a..02369fe 100644
--- a/packages/devtools_app/benchmark/scripts/compare_benchmarks.dart
+++ b/packages/devtools_app/benchmark/scripts/compare_benchmarks.dart
@@ -38,10 +38,12 @@
     }
   }
 
-  final baselineResults =
-      BenchmarkResults.parse(jsonDecode(baselineFile.readAsStringSync()));
-  final testResults =
-      BenchmarkResults.parse(jsonDecode(testFile.readAsStringSync()));
+  final baselineResults = BenchmarkResults.parse(
+    jsonDecode(baselineFile.readAsStringSync()),
+  );
+  final testResults = BenchmarkResults.parse(
+    jsonDecode(testFile.readAsStringSync()),
+  );
   compareBenchmarks(
     baselineResults,
     testResults,
diff --git a/packages/devtools_app/benchmark/scripts/dart2wasm_performance_diff.dart b/packages/devtools_app/benchmark/scripts/dart2wasm_performance_diff.dart
index da20751..f539d9f 100644
--- a/packages/devtools_app/benchmark/scripts/dart2wasm_performance_diff.dart
+++ b/packages/devtools_app/benchmark/scripts/dart2wasm_performance_diff.dart
@@ -42,13 +42,13 @@
 /// using the '--baseline' or '--test' arguments.
 ///
 /// Example usage:
-///  * dart run benchmark/scripts/run_benchmarks.dart
+///  * dart run benchmark/scripts/dart2wasm_performance_diff.dart
 ///
 /// Example usage that averages benchmark results over 5 runs:
-///  * dart run benchmark/scripts/run_benchmarks.dart --average-of=5
+///  * dart run benchmark/scripts/dart2wasm_performance_diff.dart --average-of=5
 ///
 /// Example usage that diffs against an existing basline:
-///  * dart run benchmark/scripts/run_benchmarks.dart --baseline=/Users/me/Downloads/baseline_run.json
+///  * dart run benchmark/scripts/dart2wasm_performance_diff.dart --baseline=/Users/me/Downloads/baseline_run.json
 void main(List<String> args) async {
   if (!Directory.current.path.contains('devtools_app')) {
     stderr.writeln(
@@ -88,7 +88,7 @@
 
 class CsvBuilder {
   CsvBuilder({this.saveToLocation})
-      : assert(saveToLocation == null || saveToLocation.endsWith('.csv'));
+    : assert(saveToLocation == null || saveToLocation.endsWith('.csv'));
 
   final _sb = StringBuffer();
 
@@ -108,26 +108,22 @@
     // TODO(kenz): automatically detect these and write them to the CSV.
     const flutter = '<enter manually by running \'flutter --version\'>';
     const devtools = '<enter manually by running \'git log\'>';
-    writeLines(
-      [
-        'Version info:',
-        'Flutter: $flutter',
-        'DevTools: $devtools',
-        '',
-        'Results:',
-      ],
-    );
+    writeLines([
+      'Version info:',
+      'Flutter: $flutter',
+      'DevTools: $devtools',
+      '',
+      'Results:',
+    ]);
 
     // Write the headers.
-    writeLine(
-      [
-        'Benchmark Name',
-        'Metric',
-        'Value (micros)',
-        'Delta (micros)',
-        'Delta (%)',
-      ],
-    );
+    writeLine([
+      'Benchmark Name',
+      'Metric',
+      'Value (micros)',
+      'Delta (micros)',
+      'Delta (%)',
+    ]);
   }
 
   void writeLine(List<String> content) {
@@ -157,11 +153,7 @@
       final downloadsDir = Directory.fromUri(
         Uri.parse(
           // We need the leading slash so that this is an absolute path.
-          '/${p.join(
-            currentDirectoryParts[0],
-            currentDirectoryParts[1],
-            'Downloads',
-          )}',
+          '/${p.join(currentDirectoryParts[0], currentDirectoryParts[1], 'Downloads')}',
         ),
       );
       if (downloadsDir.existsSync()) {
@@ -233,7 +225,8 @@
       )
       ..addOption(
         BenchmarkArgument.test.flagName,
-        help: 'The test benchmark data (dart2wasm) to use for this performance'
+        help:
+            'The test benchmark data (dart2wasm) to use for this performance'
             ' diff. When specified, this script will use the benchmark data at '
             'the specified path instead of generating a new benchmark run for '
             'the dart2wasm data. This file path should point to a JSON file that '
diff --git a/packages/devtools_app/benchmark/scripts/run_benchmarks.dart b/packages/devtools_app/benchmark/scripts/run_benchmarks.dart
index c130b18..ef556ea 100644
--- a/packages/devtools_app/benchmark/scripts/run_benchmarks.dart
+++ b/packages/devtools_app/benchmark/scripts/run_benchmarks.dart
@@ -55,12 +55,13 @@
     benchmarkResults.add(
       await serveWebBenchmark(
         benchmarkAppDirectory: projectRootDirectory(),
-        entryPoint: 'benchmark/test_infra/client.dart',
-        compilationOptions: useWasm
-            ? const CompilationOptions.wasm()
-            : const CompilationOptions.js(),
+        entryPoint: generateBenchmarkEntryPoint(useWasm: useWasm),
+        compilationOptions:
+            useWasm
+                ? const CompilationOptions.wasm()
+                : const CompilationOptions.js(),
         treeShakeIcons: false,
-        initialPage: benchmarkInitialPage,
+        benchmarkPath: benchmarkPath(useWasm: useWasm),
         headless: !useBrowser,
       ),
     );
@@ -86,8 +87,9 @@
   required String? saveToFileLocation,
 }) {
   final resultsAsMap = benchmarkResults.toJson();
-  final resultsAsJsonString =
-      const JsonEncoder.withIndent('  ').convert(resultsAsMap);
+  final resultsAsJsonString = const JsonEncoder.withIndent(
+    '  ',
+  ).convert(resultsAsMap);
 
   if (saveToFileLocation != null) {
     final location = Uri.parse(saveToFileLocation);
diff --git a/packages/devtools_app/benchmark/test_infra/automators/devtools_automator.dart b/packages/devtools_app/benchmark/test_infra/automators/devtools_automator.dart
index 4861327..034f380 100644
--- a/packages/devtools_app/benchmark/test_infra/automators/devtools_automator.dart
+++ b/packages/devtools_app/benchmark/test_infra/automators/devtools_automator.dart
@@ -9,8 +9,10 @@
 import 'package:devtools_app/devtools_app.dart';
 import 'package:devtools_test/helpers.dart';
 import 'package:devtools_test/test_data.dart';
+import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
+import 'package:web_benchmarks/client.dart';
 
 import '../common.dart';
 import '_cpu_profiler_automator.dart';
@@ -21,6 +23,7 @@
   DevToolsAutomater({
     required this.benchmark,
     required this.stopWarmingUpCallback,
+    required this.profile,
   });
 
   /// The current benchmark.
@@ -32,6 +35,9 @@
   /// as over.
   final void Function() stopWarmingUpCallback;
 
+  /// The profile collected for the running benchmark
+  final Profile profile;
+
   /// Whether the automation has ended.
   bool finished = false;
 
@@ -65,6 +71,12 @@
         await PerformanceScreenAutomator(controller).run();
     }
 
+    // Record whether we are in wasm mode or not. Ideally, we'd have a more
+    // first-class way to add metadata like this, but this will work for us to
+    // pass information about the environment back to the server for the
+    // purposes of our own tests.
+    profile.extraData['isWasm'] = kIsWasm ? 1 : 0;
+
     // At the end of the test, mark as finished.
     finished = true;
   }
diff --git a/packages/devtools_app/benchmark/test_infra/client.dart b/packages/devtools_app/benchmark/test_infra/client.dart
deleted file mode 100644
index cc59063..0000000
--- a/packages/devtools_app/benchmark/test_infra/client.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:web_benchmarks/client.dart';
-
-import 'common.dart';
-import 'devtools_recorder.dart';
-
-typedef RecorderFactory = Recorder Function();
-
-final benchmarks = <String, RecorderFactory>{
-  DevToolsBenchmark.navigateThroughOfflineScreens.id: () => DevToolsRecorder(
-        benchmark: DevToolsBenchmark.navigateThroughOfflineScreens,
-      ),
-  DevToolsBenchmark.offlineCpuProfilerScreen.id: () => DevToolsRecorder(
-        benchmark: DevToolsBenchmark.offlineCpuProfilerScreen,
-      ),
-  DevToolsBenchmark.offlinePerformanceScreen.id: () => DevToolsRecorder(
-        benchmark: DevToolsBenchmark.offlinePerformanceScreen,
-      ),
-};
-
-/// Runs the client of the DevTools web benchmarks.
-///
-/// When the DevTools web benchmarks are run, the server builds an app with this
-/// file as the entry point (see `run_benchmarks.dart`). The app automates
-/// the DevTools web app, records some performance data, and reports them.
-Future<void> main() async {
-  await runBenchmarks(benchmarks, initialPage: benchmarkInitialPage);
-}
diff --git a/packages/devtools_app/benchmark/test_infra/client/client_js.dart b/packages/devtools_app/benchmark/test_infra/client/client_js.dart
new file mode 100644
index 0000000..92ccf72
--- /dev/null
+++ b/packages/devtools_app/benchmark/test_infra/client/client_js.dart
@@ -0,0 +1,17 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:web_benchmarks/client.dart';
+
+import '../common.dart';
+import 'client_shared.dart';
+
+/// Runs the client of the DevTools web benchmarks.
+///
+/// When the DevTools web benchmarks are run, the server builds an app with this
+/// file as the entry point (see `run_benchmarks.dart`). The app automates
+/// the DevTools web app, records some performance data, and reports them.
+Future<void> main() async {
+  await runBenchmarks(benchmarks, benchmarkPath: benchmarkPath(useWasm: false));
+}
diff --git a/packages/devtools_app/benchmark/test_infra/client/client_shared.dart b/packages/devtools_app/benchmark/test_infra/client/client_shared.dart
new file mode 100644
index 0000000..4e584d9
--- /dev/null
+++ b/packages/devtools_app/benchmark/test_infra/client/client_shared.dart
@@ -0,0 +1,25 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:web_benchmarks/client.dart';
+
+import '../common.dart';
+import '../devtools_recorder.dart';
+
+typedef RecorderFactory = Recorder Function();
+
+final benchmarks = <String, RecorderFactory>{
+  DevToolsBenchmark.navigateThroughOfflineScreens.id:
+      () => DevToolsRecorder(
+        benchmark: DevToolsBenchmark.navigateThroughOfflineScreens,
+      ),
+  DevToolsBenchmark.offlineCpuProfilerScreen.id:
+      () => DevToolsRecorder(
+        benchmark: DevToolsBenchmark.offlineCpuProfilerScreen,
+      ),
+  DevToolsBenchmark.offlinePerformanceScreen.id:
+      () => DevToolsRecorder(
+        benchmark: DevToolsBenchmark.offlinePerformanceScreen,
+      ),
+};
diff --git a/packages/devtools_app/benchmark/test_infra/client/client_wasm.dart b/packages/devtools_app/benchmark/test_infra/client/client_wasm.dart
new file mode 100644
index 0000000..bc133d3
--- /dev/null
+++ b/packages/devtools_app/benchmark/test_infra/client/client_wasm.dart
@@ -0,0 +1,17 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:web_benchmarks/client.dart';
+
+import '../common.dart';
+import 'client_shared.dart';
+
+/// Runs the client of the DevTools web benchmarks.
+///
+/// When the DevTools web benchmarks are run, the server builds an app with this
+/// file as the entry point (see `run_benchmarks.dart`). The app automates
+/// the DevTools web app, records some performance data, and reports them.
+Future<void> main() async {
+  await runBenchmarks(benchmarks, benchmarkPath: benchmarkPath(useWasm: true));
+}
diff --git a/packages/devtools_app/benchmark/test_infra/common.dart b/packages/devtools_app/benchmark/test_infra/common.dart
index c3f5b46..856905d 100644
--- a/packages/devtools_app/benchmark/test_infra/common.dart
+++ b/packages/devtools_app/benchmark/test_infra/common.dart
@@ -4,11 +4,23 @@
 
 /// The initial page to load upon opening the DevTools benchmark app or
 /// reloading it in Chrome.
-//
-// We use an empty initial page so that the benchmark server does not attempt
-// to load the default page 'index.html', which will show up as "page not
-// found" in DevTools.
-const benchmarkInitialPage = '';
+///
+/// We use an empty initial page so that the benchmark server does not attempt
+/// to load the default page 'index.html', which will show up as "page not
+/// found" in DevTools.
+const _benchmarkInitialPage = '';
+
+const _wasmQueryParameters = {'wasm': 'true'};
+
+String benchmarkPath({required bool useWasm}) =>
+    Uri(
+      path: _benchmarkInitialPage,
+      queryParameters: useWasm ? _wasmQueryParameters : null,
+    ).toString();
+
+String generateBenchmarkEntryPoint({required bool useWasm}) {
+  return 'benchmark/test_infra/client/client_${useWasm ? 'wasm' : 'js'}.dart';
+}
 
 const devtoolsBenchmarkPrefix = 'devtools';
 
diff --git a/packages/devtools_app/benchmark/test_infra/devtools_recorder.dart b/packages/devtools_app/benchmark/test_infra/devtools_recorder.dart
index 2353fd7..34aba85 100644
--- a/packages/devtools_app/benchmark/test_infra/devtools_recorder.dart
+++ b/packages/devtools_app/benchmark/test_infra/devtools_recorder.dart
@@ -14,7 +14,7 @@
 /// A recorder that measures frame building durations for the DevTools.
 class DevToolsRecorder extends WidgetRecorder {
   DevToolsRecorder({required this.benchmark})
-      : super(name: benchmark.id, useCustomWarmUp: true);
+    : super(name: benchmark.id, useCustomWarmUp: true);
 
   /// The name of the DevTools benchmark to be run.
   ///
@@ -34,6 +34,7 @@
     _devToolsAutomator = DevToolsAutomater(
       benchmark: benchmark,
       stopWarmingUpCallback: profile.stopWarmingUp,
+      profile: profile,
     );
     return _devToolsAutomator!.createWidget();
   }
diff --git a/packages/devtools_app/benchmark/test_infra/project_root_directory.dart b/packages/devtools_app/benchmark/test_infra/project_root_directory.dart
index 968cd23..ee0f8c4 100644
--- a/packages/devtools_app/benchmark/test_infra/project_root_directory.dart
+++ b/packages/devtools_app/benchmark/test_infra/project_root_directory.dart
@@ -10,10 +10,10 @@
 
 bool _hasPubspec(Directory directory) {
   return directory.listSync().any(
-        (entity) =>
-            FileSystemEntity.isFileSync(entity.path) &&
-            path.basename(entity.path) == 'pubspec.yaml',
-      );
+    (entity) =>
+        FileSystemEntity.isFileSync(entity.path) &&
+        path.basename(entity.path) == 'pubspec.yaml',
+  );
 }
 
 Directory projectRootDirectory() {
diff --git a/packages/devtools_app/benchmark/web_bundle_size_test.dart b/packages/devtools_app/benchmark/web_bundle_size_test.dart
index 4cd93fc..31029ee 100644
--- a/packages/devtools_app/benchmark/web_bundle_size_test.dart
+++ b/packages/devtools_app/benchmark/web_bundle_size_test.dart
@@ -12,62 +12,58 @@
 
 // Benchmark size in kB.
 const bundleSizeBenchmark = 5200;
-const gzipBundleSizeBenchmark = 1500;
+const gzipBundleSizeBenchmark = 1550;
 
 void main() {
   group('Web Compile', () {
-    test(
-      'bundle size',
-      () async {
-        final js = path.join(
-          Directory.current.path,
-          'build',
-          'web',
-          'main.dart.js',
+    test('bundle size', () async {
+      final js = path.join(
+        Directory.current.path,
+        'build',
+        'web',
+        'main.dart.js',
+      );
+
+      _logStatus('Building DevTools web app in release mode...');
+      // These build arguments match the arguments used in the
+      // tool/lib/commands/build_release.dart command, which is how we build
+      // DevTools for release.
+      await _runProcess('flutter', [
+        'build',
+        'web',
+        '--web-renderer',
+        'canvaskit',
+        '--pwa-strategy=offline-first',
+        '--release',
+        '--no-tree-shake-icons',
+      ]);
+
+      _logStatus('Zipping bundle with gzip...');
+      await _runProcess('gzip', ['-k', '-f', js]);
+
+      final bundleSize = await _measureSize(js);
+      final gzipBundleSize = await _measureSize('$js.gz');
+      if (bundleSize > bundleSizeBenchmark) {
+        fail(
+          'The size the compiled web build "$js" was $bundleSize kB. This is '
+          'larger than the benchmark that was set at $bundleSizeBenchmark kB.'
+          '\n\n'
+          'The build size should be as minimal as possible to reduce the web '
+          'app\'s initial startup time. If this change is intentional, and'
+          ' expected, please increase the constant "bundleSizeBenchmark".',
         );
-
-        _logStatus('Building DevTools web app in release mode...');
-        // These build arguments match the arguments used in the
-        // tool/lib/commands/build_release.dart command, which is how we build
-        // DevTools for release.
-        await _runProcess('flutter', [
-          'build',
-          'web',
-          '--web-renderer',
-          'canvaskit',
-          '--pwa-strategy=offline-first',
-          '--release',
-          '--no-tree-shake-icons',
-        ]);
-
-        _logStatus('Zipping bundle with gzip...');
-        await _runProcess('gzip', ['-k', '-f', js]);
-
-        final bundleSize = await _measureSize(js);
-        final gzipBundleSize = await _measureSize('$js.gz');
-        if (bundleSize > bundleSizeBenchmark) {
-          fail(
-            'The size the compiled web build "$js" was $bundleSize kB. This is '
-            'larger than the benchmark that was set at $bundleSizeBenchmark kB.'
-            '\n\n'
-            'The build size should be as minimal as possible to reduce the web '
-            'app\'s initial startup time. If this change is intentional, and'
-            ' expected, please increase the constant "bundleSizeBenchmark".',
-          );
-        } else if (gzipBundleSize > gzipBundleSizeBenchmark) {
-          fail(
-            'The size the compiled and gzipped web build "$js" was'
-            ' $gzipBundleSize kB. This is larger than the benchmark that was '
-            'set at $gzipBundleSizeBenchmark kB.\n\n'
-            'The build size should be as minimal as possible to reduce the '
-            'web app\'s initial startup time. If this change is intentional, '
-            'and expected, please increase the constant '
-            '"gzipBundleSizeBenchmark".',
-          );
-        }
-      },
-      timeout: const Timeout(Duration(minutes: 5)),
-    );
+      } else if (gzipBundleSize > gzipBundleSizeBenchmark) {
+        fail(
+          'The size the compiled and gzipped web build "$js" was'
+          ' $gzipBundleSize kB. This is larger than the benchmark that was '
+          'set at $gzipBundleSizeBenchmark kB.\n\n'
+          'The build size should be as minimal as possible to reduce the '
+          'web app\'s initial startup time. If this change is intentional, '
+          'and expected, please increase the constant '
+          '"gzipBundleSizeBenchmark".',
+        );
+      }
+    }, timeout: const Timeout(Duration(minutes: 5)));
   });
 }
 
diff --git a/packages/devtools_app/integration_test/run_tests.dart b/packages/devtools_app/integration_test/run_tests.dart
index df7ec9a..e22a58f 100644
--- a/packages/devtools_app/integration_test/run_tests.dart
+++ b/packages/devtools_app/integration_test/run_tests.dart
@@ -55,22 +55,22 @@
     testRunnerArgs: testRunnerArgs,
     runTest: _runTest,
     newArgsGenerator: (args) => DevToolsAppTestRunnerArgs(args),
-    testIsSupported: (testFile) =>
-        testRunnerArgs.testAppDevice.supportsTest(testFile.path),
+    testIsSupported:
+        (testFile) => testRunnerArgs.testAppDevice.supportsTest(testFile.path),
     debugLogging: debugTestScript,
   );
 }
 
-Future<void> _runTest(
-  DevToolsAppTestRunnerArgs testRunnerArgs,
-) async {
+Future<void> _runTest(DevToolsAppTestRunnerArgs testRunnerArgs) async {
   final testTarget = testRunnerArgs.testTarget!;
   final testDevice = testRunnerArgs.testAppDevice.name;
 
   final disabledForAllDevices = _disabledTestsForDevice[_testDeviceAll]!;
   final disabledForDevice = _disabledTestsForDevice[testDevice] ?? {};
-  final disabled = {...disabledForAllDevices, ...disabledForDevice}
-      .any((t) => testTarget.endsWith(t));
+  final disabled = {
+    ...disabledForAllDevices,
+    ...disabledForDevice,
+  }.any((t) => testTarget.endsWith(t));
   if (disabled) {
     debugLog('Disabled test - skipping $testTarget for $testDevice.');
     return;
diff --git a/packages/devtools_app/integration_test/test/live_connection/debugger_panel_test.dart b/packages/devtools_app/integration_test/test/live_connection/debugger_panel_test.dart
index 0035177..b297886 100644
--- a/packages/devtools_app/integration_test/test/live_connection/debugger_panel_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/debugger_panel_test.dart
@@ -78,11 +78,7 @@
 
     // Verify that the gutter item and line item are aligned:
     expect(
-      areHorizontallyAligned(
-        gutter57Finder,
-        line57Finder,
-        tester: tester,
-      ),
+      areHorizontallyAligned(gutter57Finder, line57Finder, tester: tester),
       isTrue,
     );
 
@@ -113,11 +109,7 @@
 
     // Verify that the gutter item and line item are aligned:
     expect(
-      areHorizontallyAligned(
-        gutter30Finder,
-        line30Finder,
-        tester: tester,
-      ),
+      areHorizontallyAligned(gutter30Finder, line30Finder, tester: tester),
       isTrue,
     );
 
@@ -177,11 +169,7 @@
 
     // Verify that the gutter item and line item are aligned:
     expect(
-      areHorizontallyAligned(
-        gutter46Finder,
-        line46Finder,
-        tester: tester,
-      ),
+      areHorizontallyAligned(gutter46Finder, line46Finder, tester: tester),
       isTrue,
     );
 
@@ -229,14 +217,12 @@
     finder.first.evaluate().first.widget as T;
 
 Finder findLineItemWithText(String text) => find.ancestor(
-      of: find.textContaining(text),
-      matching: find.byType(LineItem),
-    );
+  of: find.textContaining(text),
+  matching: find.byType(LineItem),
+);
 
-Finder findGutterItemWithText(String text) => find.ancestor(
-      of: find.text(text),
-      matching: find.byType(GutterItem),
-    );
+Finder findGutterItemWithText(String text) =>
+    find.ancestor(of: find.text(text), matching: find.byType(GutterItem));
 
 bool isLineFocused(Finder lineItemFinder) {
   final lineWidget = getWidgetFromFinder<LineItem>(lineItemFinder);
@@ -244,11 +230,11 @@
 }
 
 Finder findStackFrameWithText(String text) => find.descendant(
-      of: find.byType(CallStack),
-      matching: find.richTextContaining(text),
-    );
+  of: find.byType(CallStack),
+  matching: find.richTextContaining(text),
+);
 
 Finder findBreakpointWithText(String text) => find.descendant(
-      of: find.byType(Breakpoints),
-      matching: find.richTextContaining(text),
-    );
+  of: find.byType(Breakpoints),
+  matching: find.richTextContaining(text),
+);
diff --git a/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart b/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart
index 728e79f..8dbc10b 100644
--- a/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart
@@ -45,30 +45,23 @@
     );
     expect(extensionService.availableExtensions.length, 3);
     expect(extensionService.visibleExtensions.length, 3);
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.none, // bar
-        ExtensionEnabledState.none, // baz
-        ExtensionEnabledState.none, // foo
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.none, // bar
+      ExtensionEnabledState.none, // baz
+      ExtensionEnabledState.none, // foo
+    ]);
 
     await connectToTestApp(tester, testApp);
 
     expect(extensionService.availableExtensions.length, 5);
     expect(extensionService.visibleExtensions.length, 5);
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.none, // bar
-        ExtensionEnabledState.none, // baz
-        ExtensionEnabledState.none, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-      closeMenuWhenDone: false,
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.none, // bar
+      ExtensionEnabledState.none, // baz
+      ExtensionEnabledState.none, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ], closeMenuWhenDone: false);
 
     await _verifyExtensionVisibilitySetting(tester);
 
@@ -80,16 +73,13 @@
       initialLoad: true,
     );
     await _answerEnableExtensionPrompt(tester, enable: true);
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.enabled, // bar
-        ExtensionEnabledState.none, // baz
-        ExtensionEnabledState.none, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.enabled, // bar
+      ExtensionEnabledState.none, // baz
+      ExtensionEnabledState.none, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ]);
 
     await _verifyContextMenuActionsAndDisable(tester);
 
@@ -100,16 +90,13 @@
       extensionIndex: 0,
       visible: false,
     );
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.disabled, // bar
-        ExtensionEnabledState.none, // baz
-        ExtensionEnabledState.none, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.disabled, // bar
+      ExtensionEnabledState.none, // baz
+      ExtensionEnabledState.none, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ]);
 
     // Baz extension. Hide immediately.
     await _switchToExtensionScreen(
@@ -126,16 +113,13 @@
       extensionIndex: 1,
       visible: false,
     );
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.disabled, // bar
-        ExtensionEnabledState.disabled, // baz
-        ExtensionEnabledState.none, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.disabled, // bar
+      ExtensionEnabledState.disabled, // baz
+      ExtensionEnabledState.none, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ]);
 
     // Re-enable Baz extension from the extensions settings menu.
     logStatus('verify we can re-enable an extension from the settings menu');
@@ -147,16 +131,13 @@
     expect(find.byType(EnableExtensionPrompt), findsNothing);
     expect(find.byType(EmbeddedExtensionView), findsOneWidget);
     expect(find.byType(HtmlElementView), findsOneWidget);
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.disabled, // bar
-        ExtensionEnabledState.enabled, // baz
-        ExtensionEnabledState.none, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.disabled, // bar
+      ExtensionEnabledState.enabled, // baz
+      ExtensionEnabledState.none, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ]);
 
     // Foo extension. Disable directly from settings menu.
     logStatus(
@@ -177,16 +158,13 @@
       extensionIndex: 2,
       visible: false,
     );
-    await _verifyExtensionsSettingsMenu(
-      tester,
-      [
-        ExtensionEnabledState.disabled, // bar
-        ExtensionEnabledState.enabled, // baz
-        ExtensionEnabledState.disabled, // foo
-        ExtensionEnabledState.none, // provider
-        ExtensionEnabledState.none, // some_tool
-      ],
-    );
+    await _verifyExtensionsSettingsMenu(tester, [
+      ExtensionEnabledState.disabled, // bar
+      ExtensionEnabledState.enabled, // baz
+      ExtensionEnabledState.disabled, // foo
+      ExtensionEnabledState.none, // provider
+      ExtensionEnabledState.none, // some_tool
+    ]);
   });
 }
 
@@ -251,10 +229,7 @@
     find.byType(EmbeddedExtensionView),
     enable ? findsOneWidget : findsNothing,
   );
-  expect(
-    find.byType(HtmlElementView),
-    enable ? findsOneWidget : findsNothing,
-  );
+  expect(find.byType(HtmlElementView), enable ? findsOneWidget : findsNothing);
 }
 
 Future<void> _verifyContextMenuActionsAndDisable(WidgetTester tester) async {
@@ -293,10 +268,11 @@
   await _openExtensionSettingsMenu(tester);
 
   expect(find.byType(ExtensionSetting), findsNWidgets(enabledStates.length));
-  final toggleButtonGroups = tester
-      .widgetList(find.byType(DevToolsToggleButtonGroup))
-      .cast<DevToolsToggleButtonGroup>()
-      .toList();
+  final toggleButtonGroups =
+      tester
+          .widgetList(find.byType(DevToolsToggleButtonGroup))
+          .cast<DevToolsToggleButtonGroup>()
+          .toList();
   for (int i = 0; i < toggleButtonGroups.length; i++) {
     logStatus('verify extension settings toggle button states (index $i)');
     final group = toggleButtonGroups[i];
@@ -337,10 +313,11 @@
     'changing the extension setting at index $extensionIndex to value $settingValue',
   );
   await _openExtensionSettingsMenu(tester);
-  final extensionSetting = tester
-      .widgetList(find.byType(DevToolsToggleButtonGroup))
-      .cast<DevToolsToggleButtonGroup>()
-      .toList()[extensionIndex];
+  final extensionSetting =
+      tester
+          .widgetList(find.byType(DevToolsToggleButtonGroup))
+          .cast<DevToolsToggleButtonGroup>()
+          .toList()[extensionIndex];
   await tester.tap(
     find.descendant(
       of: find.byWidget(extensionSetting),
diff --git a/packages/devtools_app/integration_test/test/live_connection/eval_and_browse_test.dart b/packages/devtools_app/integration_test/test/live_connection/eval_and_browse_test.dart
index ecabad1..5678d45 100644
--- a/packages/devtools_app/integration_test/test/live_connection/eval_and_browse_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/eval_and_browse_test.dart
@@ -86,17 +86,11 @@
     find.textContaining('MyApp, retained size '),
     next: find.text('references'),
   );
-  next = await tester.tapAndPump(
-    next!,
-    next: find.textContaining('static ('),
-  );
+  next = await tester.tapAndPump(next!, next: find.textContaining('static ('));
   next = await tester.tapAndPump(
     next!,
     description: 'text containing "static ("',
     next: find.text('inbound'),
   );
-  next = await tester.tapAndPump(
-    next!,
-    next: find.text('View'),
-  );
+  next = await tester.tapAndPump(next!, next: find.text('View'));
 }
diff --git a/packages/devtools_app/integration_test/test/live_connection/eval_and_inspect_test.dart b/packages/devtools_app/integration_test/test/live_connection/eval_and_inspect_test.dart
index cb6db1d..4dc2a4b 100644
--- a/packages/devtools_app/integration_test/test/live_connection/eval_and_inspect_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/eval_and_inspect_test.dart
@@ -45,10 +45,22 @@
 
     logStatus('testing variable assignment');
     await testAssignment(evalTester);
-
-    logStatus('testing eval on widget tree node');
-    await _testEvalOnWidgetTreeNode(evalTester);
   });
+
+  testWidgets(
+    'eval with scope on widget tree node',
+    (tester) async {
+      await pumpAndConnectDevTools(tester, testApp);
+
+      final evalTester = EvalTester(tester);
+      await evalTester.prepareInspectorUI();
+
+      logStatus('testing eval on widget tree node');
+      await _testEvalOnWidgetTreeNode(evalTester);
+    },
+    // https://github.com/flutter/devtools/issues/8389
+    skip: true,
+  );
 }
 
 Future<void> _testEvalOnWidgetTreeNode(EvalTester tester) async {
diff --git a/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart b/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
index fa32d98..7fc53ef 100644
--- a/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/memory_screen_helpers.dart
@@ -57,9 +57,7 @@
   }
 }
 
-Future<void> takeHeapSnapshot(
-  WidgetTester tester,
-) async {
+Future<void> takeHeapSnapshot(WidgetTester tester) async {
   logStatus('Started taking snapshot.');
   // Take snapshot.
   const snapshotDuration = Duration(seconds: 20);
diff --git a/packages/devtools_app/integration_test/test/live_connection/performance_screen_event_recording_test.dart b/packages/devtools_app/integration_test/test/live_connection/performance_screen_event_recording_test.dart
index dcc82cb..fc2841f 100644
--- a/packages/devtools_app/integration_test/test/live_connection/performance_screen_event_recording_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/performance_screen_event_recording_test.dart
@@ -24,88 +24,96 @@
     expect(testApp.vmServiceUri, isNotNull);
   });
 
-  testWidgets(
-    'can process and refresh timeline data',
-    (tester) async {
-      await pumpAndConnectDevTools(tester, testApp);
+  testWidgets('can process and refresh timeline data', (tester) async {
+    await pumpAndConnectDevTools(tester, testApp);
 
-      logStatus(
-        'Open the Performance screen and switch to the Timeline Events tab',
-      );
+    logStatus(
+      'Open the Performance screen and switch to the Timeline Events tab',
+    );
 
-      await switchToScreen(
-        tester,
-        tabIcon: ScreenMetaData.performance.icon,
-        tabIconAsset: ScreenMetaData.performance.iconAsset,
-        screenId: ScreenMetaData.performance.id,
-      );
-      await tester.pump(safePumpDuration);
+    await switchToScreen(
+      tester,
+      tabIcon: ScreenMetaData.performance.icon,
+      tabIconAsset: ScreenMetaData.performance.iconAsset,
+      screenId: ScreenMetaData.performance.id,
+    );
+    await tester.pump(safePumpDuration);
 
-      await tester.tap(find.widgetWithText(InkWell, 'Timeline Events'));
-      await tester.pumpAndSettle(longPumpDuration);
+    await tester.tap(find.widgetWithText(InkWell, 'Timeline Events'));
+    await tester.pumpAndSettle(longPumpDuration);
 
-      // Find the [PerformanceController] to access its data.
-      final performanceScreenFinder = find.byType(PerformanceScreenBody);
-      expect(performanceScreenFinder, findsOneWidget);
-      final screenState =
-          tester.state<PerformanceScreenBodyState>(performanceScreenFinder);
-      final performanceController = screenState.controller;
+    // Find the [PerformanceController] to access its data.
+    final performanceScreenFinder = find.byType(PerformanceScreenBody);
+    expect(performanceScreenFinder, findsOneWidget);
+    final screenState = tester.state<PerformanceScreenBodyState>(
+      performanceScreenFinder,
+    );
+    final performanceController = screenState.controller;
 
-      logStatus('Verifying that data is processed upon first load');
-      final initialTrace = Trace.fromBuffer(
-        performanceController.timelineEventsController.fullPerfettoTrace,
-      );
-      final initialTracePacket = List.of(initialTrace.packet, growable: false);
-      final initialTrackDescriptors =
-          initialTracePacket.where((e) => e.hasTrackDescriptor());
-      expect(initialTracePacket, isNotEmpty);
-      expect(initialTrackDescriptors, isNotEmpty);
+    logStatus('Verifying that data is processed upon first load');
+    final initialTrace = Trace.fromBuffer(
+      performanceController.timelineEventsController.fullPerfettoTrace,
+    );
+    final initialTracePacket = List.of(initialTrace.packet, growable: false);
+    final initialTrackDescriptors = initialTracePacket.where(
+      (e) => e.hasTrackDescriptor(),
+    );
+    expect(initialTracePacket, isNotEmpty);
+    expect(initialTrackDescriptors, isNotEmpty);
 
-      final trackEvents = initialTracePacket.where((e) => e.hasTrackEvent());
-      expect(trackEvents, isNotEmpty);
+    final trackEvents = initialTracePacket.where((e) => e.hasTrackEvent());
+    expect(trackEvents, isNotEmpty);
 
-      expect(
-        performanceController
-            .timelineEventsController.perfettoController.processor.uiTrackId,
-        isNotNull,
-        reason: 'Expected uiTrackId to be non-null',
-      );
-      expect(
-        performanceController.timelineEventsController.perfettoController
-            .processor.rasterTrackId,
-        isNotNull,
-        reason: 'Expected rasterTrackId to be non-null',
-      );
-      expect(
-        performanceController.timelineEventsController.perfettoController
-            .processor.frameRangeFromTimelineEvents,
-        isNotNull,
-        reason: 'Expected frameRangeFromTimelineEvents to be non-null',
-      );
+    expect(
+      performanceController
+          .timelineEventsController
+          .perfettoController
+          .processor
+          .uiTrackId,
+      isNotNull,
+      reason: 'Expected uiTrackId to be non-null',
+    );
+    expect(
+      performanceController
+          .timelineEventsController
+          .perfettoController
+          .processor
+          .rasterTrackId,
+      isNotNull,
+      reason: 'Expected rasterTrackId to be non-null',
+    );
+    expect(
+      performanceController
+          .timelineEventsController
+          .perfettoController
+          .processor
+          .frameRangeFromTimelineEvents,
+      isNotNull,
+      reason: 'Expected frameRangeFromTimelineEvents to be non-null',
+    );
 
-      logStatus(
-        'toggling the Performance Overlay to trigger new Flutter frames',
-      );
-      final performanceOverlayFinder = find.text('Performance Overlay');
-      expect(performanceOverlayFinder, findsOneWidget);
-      await tester.tap(performanceOverlayFinder);
-      await tester.pump(longPumpDuration);
+    logStatus('toggling the Performance Overlay to trigger new Flutter frames');
+    final performanceOverlayFinder = find.text('Performance Overlay');
+    expect(performanceOverlayFinder, findsOneWidget);
+    await tester.tap(performanceOverlayFinder);
+    await tester.pump(longPumpDuration);
 
-      logStatus('Refreshing the timeline to load new events');
-      await tester.tap(find.byType(RefreshTimelineEventsButton));
-      await tester.pump(longPumpDuration);
+    logStatus('Refreshing the timeline to load new events');
+    await tester.tap(find.byType(RefreshTimelineEventsButton));
+    await tester.pump(longPumpDuration);
 
-      logStatus('Verifying that we have recorded new events');
-      final refreshedTrace = Trace.fromBuffer(
-        performanceController.timelineEventsController.fullPerfettoTrace,
-      );
-      final refreshedTracePacket =
-          List.of(refreshedTrace.packet, growable: false);
-      expect(
-        refreshedTracePacket.length,
-        greaterThan(initialTracePacket.length),
-        reason: 'Expected new events to have been recorded, but none were.',
-      );
-    },
-  );
+    logStatus('Verifying that we have recorded new events');
+    final refreshedTrace = Trace.fromBuffer(
+      performanceController.timelineEventsController.fullPerfettoTrace,
+    );
+    final refreshedTracePacket = List.of(
+      refreshedTrace.packet,
+      growable: false,
+    );
+    expect(
+      refreshedTracePacket.length,
+      greaterThan(initialTracePacket.length),
+      reason: 'Expected new events to have been recorded, but none were.',
+    );
+  });
 }
diff --git a/packages/devtools_app/integration_test/test/live_connection/service_connection_test.dart b/packages/devtools_app/integration_test/test/live_connection/service_connection_test.dart
index d24793d..e60c1eb 100644
--- a/packages/devtools_app/integration_test/test/live_connection/service_connection_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/service_connection_test.dart
@@ -47,7 +47,8 @@
       // that are unintentionally added at start up.
       const Range(35, 70).contains(vmServiceCallCount),
       isTrue,
-      reason: 'Unexpected number of vm service calls upon connection: '
+      reason:
+          'Unexpected number of vm service calls upon connection: '
           '$vmServiceCallCount. If this is expected, please update this test '
           'to the new expected number of calls. Here are the calls for this '
           'test run:\n ${serviceConnection.serviceManager.service!.vmServiceCalls.toString()}',
@@ -71,7 +72,8 @@
         'getDartDevelopmentServiceVersion',
         'getVM',
       ]),
-      reason: 'Unexpected order of vm service calls upon connection. '
+      reason:
+          'Unexpected order of vm service calls upon connection. '
           'Here are the calls for this test run:\n '
           '${serviceConnection.serviceManager.service!.vmServiceCalls.toString()}',
     );
diff --git a/packages/devtools_app/integration_test/test/live_connection/service_extensions_test.dart b/packages/devtools_app/integration_test/test/live_connection/service_extensions_test.dart
index 9b89f47..5cfaae8 100644
--- a/packages/devtools_app/integration_test/test/live_connection/service_extensions_test.dart
+++ b/packages/devtools_app/integration_test/test/live_connection/service_extensions_test.dart
@@ -61,10 +61,7 @@
       await _verifyStringExtension(tester);
 
       logStatus('verify Flutter engine service extensions');
-      expect(
-        await serviceConnection.queryDisplayRefreshRate,
-        equals(60),
-      );
+      expect(await serviceConnection.queryDisplayRefreshRate, equals(60));
 
       logStatus('verify services that are registered to exactly one client');
       await _verifyHotReloadAndHotRestart();
@@ -96,11 +93,7 @@
     // Enable a service extension of each type (boolean, numeric, string).
     for (final ext in serviceExtensionsToEnable) {
       await serviceConnection.serviceManager.serviceExtensionManager
-          .setServiceExtensionState(
-        ext.$1,
-        enabled: true,
-        value: ext.$2,
-      );
+          .setServiceExtensionState(ext.$1, enabled: true, value: ext.$2);
     }
 
     logStatus('disconnecting from the test device');
@@ -264,11 +257,7 @@
 
   // Enable the service extension state from the service manager.
   await serviceConnection.serviceManager.serviceExtensionManager
-      .setServiceExtensionState(
-    extensionName,
-    enabled: true,
-    value: newValue,
-  );
+      .setServiceExtensionState(extensionName, enabled: true, value: newValue);
 
   await _verifyExtensionStateOnTestDevice(
     evalExpression: evalExpression,
@@ -302,8 +291,9 @@
   required (bool, Object?) expectedResultInServiceManager,
   required WidgetTester tester,
 }) async {
-  final serviceExtensionButtons = tester
-      .widgetList<ServiceExtensionButton>(find.byType(ServiceExtensionButton));
+  final serviceExtensionButtons = tester.widgetList<ServiceExtensionButton>(
+    find.byType(ServiceExtensionButton),
+  );
   final button = serviceExtensionButtons.firstWhereOrNull(
     (b) => b.extensionState.description.extension == extensionName,
   );
@@ -358,7 +348,8 @@
   required Object? value,
 }) async {
   final stateListenable = serviceConnection
-      .serviceManager.serviceExtensionManager
+      .serviceManager
+      .serviceExtensionManager
       .getServiceExtensionState(extensionName);
 
   // Wait for the service extension state to match the expected value.
diff --git a/packages/devtools_app/integration_test/test/offline/memory_offline_data_test.dart b/packages/devtools_app/integration_test/test/offline/memory_offline_data_test.dart
index f8ab910..a224503 100644
--- a/packages/devtools_app/integration_test/test/offline/memory_offline_data_test.dart
+++ b/packages/devtools_app/integration_test/test/offline/memory_offline_data_test.dart
@@ -14,27 +14,24 @@
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
-  testWidgets(
-    'Memory screen can load offline data',
-    (tester) async {
-      await pumpDevTools(tester);
-      logStatus('1 - pumped devtools');
-      await loadSampleData(tester, memoryFileName);
-      logStatus('2 - loaded sample data');
-      await tester.pumpAndSettle(longPumpDuration);
-      logStatus('3 - pumped and settled');
+  testWidgets('Memory screen can load offline data', (tester) async {
+    await pumpDevTools(tester);
+    logStatus('1 - pumped devtools');
+    await loadSampleData(tester, memoryFileName);
+    logStatus('2 - loaded sample data');
+    await tester.pumpAndSettle(longPumpDuration);
+    logStatus('3 - pumped and settled');
 
-      const diffTab = 'Diff Snapshots';
-      const profileTab = 'Profile Memory';
-      const traceTab = 'Trace Instances';
+    const diffTab = 'Diff Snapshots';
+    const profileTab = 'Profile Memory';
+    const traceTab = 'Trace Instances';
 
-      expect(find.text('_MyClass'), findsOneWidget);
-      logStatus('5 - found _MyClass');
+    expect(find.text('_MyClass'), findsOneWidget);
+    logStatus('5 - found _MyClass');
 
-      for (final tab in [diffTab, profileTab, traceTab]) {
-        expect(find.text(tab), findsOneWidget);
-        logStatus('6.$tab - found');
-      }
-    },
-  );
+    for (final tab in [diffTab, profileTab, traceTab]) {
+      expect(find.text(tab), findsOneWidget);
+      logStatus('6.$tab - found');
+    }
+  });
 }
diff --git a/packages/devtools_app/integration_test/test_infra/run/_in_file_args.dart b/packages/devtools_app/integration_test/test_infra/run/_in_file_args.dart
index dd6685e..137579d 100644
--- a/packages/devtools_app/integration_test/test_infra/run/_in_file_args.dart
+++ b/packages/devtools_app/integration_test/test_infra/run/_in_file_args.dart
@@ -7,10 +7,7 @@
 
 import '_test_app_driver.dart';
 
-enum TestFileArgItems {
-  experimentsOn,
-  appPath,
-}
+enum TestFileArgItems { experimentsOn, appPath }
 
 const _defaultFlutterAppPath = 'test/test_infra/fixtures/flutter_app';
 const _defaultDartCliAppPath = 'test/test_infra/fixtures/empty_app.dart';
@@ -41,11 +38,12 @@
   TestFileArgs.parse(
     Map<TestFileArgItems, dynamic> map, {
     required TestAppDevice testAppDevice,
-  })  : experimentsOn = map[TestFileArgItems.experimentsOn] ?? false,
-        appPath = map[TestFileArgItems.appPath] ??
-            (testAppDevice == TestAppDevice.cli
-                ? _defaultDartCliAppPath
-                : _defaultFlutterAppPath);
+  }) : experimentsOn = map[TestFileArgItems.experimentsOn] ?? false,
+       appPath =
+           map[TestFileArgItems.appPath] ??
+           (testAppDevice == TestAppDevice.cli
+               ? _defaultDartCliAppPath
+               : _defaultFlutterAppPath);
 
   /// If true, experiments will be enabled in the test.
   final bool experimentsOn;
@@ -62,18 +60,18 @@
 Map<TestFileArgItems, dynamic> _parseFileContent(String fileContent) {
   final matches = _argRegex.allMatches(fileContent);
 
-  final entries = matches.map<MapEntry<TestFileArgItems, dynamic>>(
-    (RegExpMatch m) {
-      final name = m.group(1) ?? '';
-      if (name.isEmpty) {
-        throw ArgumentError(
-          'Name of test argument should be provided: [${m.group(0)}].',
-        );
-      }
-      final value = m.group(2) ?? '';
-      return MapEntry(TestFileArgItems.values.byName(name), jsonDecode(value));
-    },
-  );
+  final entries = matches.map<MapEntry<TestFileArgItems, dynamic>>((
+    RegExpMatch m,
+  ) {
+    final name = m.group(1) ?? '';
+    if (name.isEmpty) {
+      throw ArgumentError(
+        'Name of test argument should be provided: [${m.group(0)}].',
+      );
+    }
+    final value = m.group(2) ?? '';
+    return MapEntry(TestFileArgItems.values.byName(name), jsonDecode(value));
+  });
 
   return Map.fromEntries(entries);
 }
diff --git a/packages/devtools_app/integration_test/test_infra/run/_test_app_driver.dart b/packages/devtools_app/integration_test/test_infra/run/_test_app_driver.dart
index 26dc043..7a8e45d 100644
--- a/packages/devtools_app/integration_test/test_infra/run/_test_app_driver.dart
+++ b/packages/devtools_app/integration_test/test_infra/run/_test_app_driver.dart
@@ -23,18 +23,14 @@
 
   @override
   Future<void> startProcess() async {
-    runProcess = await Process.start(
-      'flutter',
-      [
-        'run',
-        '--machine',
-        '-d',
-        testAppDevice.argName,
-        // Do not serve DevTools from Flutter Tools.
-        '--no-devtools',
-      ],
-      workingDirectory: testAppPath,
-    );
+    runProcess = await Process.start('flutter', [
+      'run',
+      '--machine',
+      '-d',
+      testAppDevice.argName,
+      // Do not serve DevTools from Flutter Tools.
+      '--no-devtools',
+    ], workingDirectory: testAppPath);
   }
 
   @override
@@ -50,19 +46,23 @@
       event: FlutterDaemonConstants.appDebugPort.key,
       timeout: IntegrationTestApp._appStartTimeout,
     );
-    final wsUriString = (debugPort[FlutterDaemonConstants.params.key]!
-        as Map<String, Object?>)[FlutterDaemonConstants.wsUri.key] as String;
+    final wsUriString =
+        (debugPort[FlutterDaemonConstants.params.key]!
+                as Map<String, Object?>)[FlutterDaemonConstants.wsUri.key]
+            as String;
     _vmServiceWsUri = Uri.parse(wsUriString);
 
     // Map to WS URI.
-    _vmServiceWsUri =
-        convertToWebSocketUrl(serviceProtocolUrl: _vmServiceWsUri);
+    _vmServiceWsUri = convertToWebSocketUrl(
+      serviceProtocolUrl: _vmServiceWsUri,
+    );
 
     // Now await the started event; if it had already happened the future will
     // have already completed.
     final startedResult = await started;
-    final params = startedResult[FlutterDaemonConstants.params.key]!
-        as Map<String, Object?>;
+    final params =
+        startedResult[FlutterDaemonConstants.params.key]!
+            as Map<String, Object?>;
     _currentRunningAppId = params[FlutterDaemonConstants.appId.key] as String?;
   }
 
@@ -72,10 +72,9 @@
       _debugPrint('Stopping app');
       await Future.any<void>(<Future<void>>[
         runProcess!.exitCode,
-        _sendFlutterDaemonRequest(
-          'app.stop',
-          <String, dynamic>{'appId': _currentRunningAppId},
-        ),
+        _sendFlutterDaemonRequest('app.stop', <String, dynamic>{
+          'appId': _currentRunningAppId,
+        }),
       ]).timeout(
         IOMixin.killTimeout,
         onTimeout: () {
@@ -140,9 +139,10 @@
     return _timeoutWithMessages<Map<String, Object?>>(
       () => response.future,
       timeout: timeout,
-      message: event != null
-          ? 'Did not receive expected $event event.'
-          : 'Did not receive response to request "$id".',
+      message:
+          event != null
+              ? 'Did not receive expected $event event.'
+              : 'Did not receive response to request "$id".',
     ).whenComplete(() => sub.cancel());
   }
 
@@ -171,13 +171,15 @@
       error.write(
         '${event != null ? '$event event' : 'response to request $id.'}.\n\n',
       );
-      final errorFromJson = (json[FlutterDaemonConstants.params.key]
-          as Map<String, Object?>?)?[FlutterDaemonConstants.error.key];
+      final errorFromJson =
+          (json[FlutterDaemonConstants.params.key]
+              as Map<String, Object?>?)?[FlutterDaemonConstants.error.key];
       if (errorFromJson != null) {
         error.write('$errorFromJson\n\n');
       }
-      final traceFromJson = (json[FlutterDaemonConstants.params.key]
-          as Map<String, Object?>?)?[FlutterDaemonConstants.trace.key];
+      final traceFromJson =
+          (json[FlutterDaemonConstants.params.key]
+              as Map<String, Object?>?)?[FlutterDaemonConstants.trace.key];
       if (traceFromJson != null) {
         error.write('$traceFromJson\n\n');
       }
@@ -200,9 +202,8 @@
 }
 
 class TestDartCliApp extends IntegrationTestApp {
-  TestDartCliApp({
-    String appPath = 'test/test_infra/fixtures/empty_app.dart',
-  }) : super(appPath, TestAppDevice.cli);
+  TestDartCliApp({String appPath = 'test/test_infra/fixtures/empty_app.dart'})
+    : super(appPath, TestAppDevice.cli);
 
   static const vmServicePrefix = 'The Dart VM service is listening on ';
 
@@ -212,15 +213,11 @@
     final parts = testAppPath.split(separator);
     final scriptName = parts.removeLast();
     final workingDir = parts.join(separator);
-    runProcess = await Process.start(
-      'dart',
-      [
-        '--observe=0',
-        'run',
-        scriptName,
-      ],
-      workingDirectory: workingDir,
-    );
+    runProcess = await Process.start('dart', [
+      '--observe=0',
+      'run',
+      scriptName,
+    ], workingDirectory: workingDir);
   }
 
   @override
@@ -232,8 +229,9 @@
     final parsedVmServiceUri = Uri.parse(vmServiceUri);
 
     // Map to WS URI.
-    _vmServiceWsUri =
-        convertToWebSocketUrl(serviceProtocolUrl: parsedVmServiceUri);
+    _vmServiceWsUri = convertToWebSocketUrl(
+      serviceProtocolUrl: parsedVmServiceUri,
+    );
   }
 
   Future<String> waitFor({required String message, Duration? timeout}) {
@@ -262,8 +260,9 @@
     required String message,
   }) async {
     if (message == vmServicePrefix && line.startsWith(vmServicePrefix)) {
-      final vmServiceUri = line
-          .substring(line.indexOf(vmServicePrefix) + vmServicePrefix.length);
+      final vmServiceUri = line.substring(
+        line.indexOf(vmServicePrefix) + vmServicePrefix.length,
+      );
       await subscription.cancel();
       response.complete(vmServiceUri);
     }
@@ -331,10 +330,8 @@
     _debugPrint('Waiting for process to end');
     return runProcess!.exitCode.timeout(
       IOMixin.killTimeout,
-      onTimeout: () => killGracefully(
-        runProcess!,
-        debugLogging: debugTestScript,
-      ),
+      onTimeout:
+          () => killGracefully(runProcess!, debugLogging: debugTestScript),
     );
   }
 
@@ -354,15 +351,18 @@
 
     final sub = _allMessages.stream.listen(logMessage);
 
-    return f().timeout(
-      timeout ?? _defaultTimeout,
-      onTimeout: () {
-        logMessage('<timed out>');
-        throw '$message';
-      },
-    ).catchError((Object? error) {
-      throw '$error\nReceived:\n${messages.toString()}';
-    }).whenComplete(() => sub.cancel());
+    return f()
+        .timeout(
+          timeout ?? _defaultTimeout,
+          onTimeout: () {
+            logMessage('<timed out>');
+            throw '$message';
+          },
+        )
+        .catchError((Object? error) {
+          throw '$error\nReceived:\n${messages.toString()}';
+        })
+        .whenComplete(() => sub.cancel());
   }
 
   String _debugPrint(String msg) {
@@ -379,15 +379,17 @@
 ///
 /// If the URI is already a VM Service WebSocket URI it will not be modified.
 Uri convertToWebSocketUrl({required Uri serviceProtocolUrl}) {
-  final isSecure = serviceProtocolUrl.isScheme('wss') ||
+  final isSecure =
+      serviceProtocolUrl.isScheme('wss') ||
       serviceProtocolUrl.isScheme('https');
   final scheme = isSecure ? 'wss' : 'ws';
 
-  final path = serviceProtocolUrl.path.endsWith('/ws')
-      ? serviceProtocolUrl.path
-      : (serviceProtocolUrl.path.endsWith('/')
-          ? '${serviceProtocolUrl.path}ws'
-          : '${serviceProtocolUrl.path}/ws');
+  final path =
+      serviceProtocolUrl.path.endsWith('/ws')
+          ? serviceProtocolUrl.path
+          : (serviceProtocolUrl.path.endsWith('/')
+              ? '${serviceProtocolUrl.path}ws'
+              : '${serviceProtocolUrl.path}/ws');
 
   return serviceProtocolUrl.replace(scheme: scheme, path: path);
 }
@@ -410,7 +412,7 @@
   daemonConnected(nameOverride: 'daemon.connected');
 
   const FlutterDaemonConstants({String? nameOverride})
-      : _nameOverride = nameOverride;
+    : _nameOverride = nameOverride;
 
   final String? _nameOverride;
 
@@ -447,11 +449,13 @@
     ],
   };
 
-  static final _argNameToDeviceMap =
-      TestAppDevice.values.fold(<String, TestAppDevice>{}, (map, device) {
-    map[device.argName] = device;
-    return map;
-  });
+  static final _argNameToDeviceMap = TestAppDevice.values.fold(
+    <String, TestAppDevice>{},
+    (map, device) {
+      map[device.argName] = device;
+      return map;
+    },
+  );
 
   static TestAppDevice? fromArgName(String argName) {
     return _argNameToDeviceMap[argName];
@@ -459,7 +463,8 @@
 
   bool supportsTest(String testPath) {
     final unsupportedTests = _unsupportedTestsForDevice[this] ?? [];
-    return unsupportedTests
-        .none((unsupportedTestPath) => testPath.endsWith(unsupportedTestPath));
+    return unsupportedTests.none(
+      (unsupportedTestPath) => testPath.endsWith(unsupportedTestPath),
+    );
   }
 }
diff --git a/packages/devtools_app/integration_test/test_infra/run/run_test.dart b/packages/devtools_app/integration_test/test_infra/run/run_test.dart
index 653bcf8..2de64e3 100644
--- a/packages/devtools_app/integration_test/test_infra/run/run_test.dart
+++ b/packages/devtools_app/integration_test/test_infra/run/run_test.dart
@@ -60,9 +60,7 @@
   // Run the flutter integration test.
   final testRunner = IntegrationTestRunner();
   try {
-    final testArgs = <String, Object>{
-      if (!offline) 'service_uri': testAppUri,
-    };
+    final testArgs = <String, Object>{if (!offline) 'service_uri': testAppUri};
     final testTarget = testRunnerArgs.testTarget!;
     debugLog('starting test run for $testTarget');
     await testRunner.run(
@@ -89,10 +87,11 @@
 
 class DevToolsAppTestRunnerArgs extends IntegrationTestRunnerArgs {
   DevToolsAppTestRunnerArgs(super.args, {super.verifyValidTarget = true})
-      : super(addExtraArgs: _addExtraArgs) {
-    testAppDevice = TestAppDevice.fromArgName(
-      argResults[_testAppDeviceArg] ?? TestAppDevice.flutterTester.argName,
-    )!;
+    : super(addExtraArgs: _addExtraArgs) {
+    testAppDevice =
+        TestAppDevice.fromArgName(
+          argResults[_testAppDeviceArg] ?? TestAppDevice.flutterTester.argName,
+        )!;
   }
 
   /// The type of device for the test app to run on.
@@ -116,7 +115,8 @@
     argParser
       ..addOption(
         _testAppUriArg,
-        help: 'The vm service connection to use for the app that DevTools will '
+        help:
+            'The vm service connection to use for the app that DevTools will '
             'connect to during the integration test. If left empty, a sample app '
             'will be spun up as part of the integration test process.',
       )
diff --git a/packages/devtools_app/lib/devtools.dart b/packages/devtools_app/lib/devtools.dart
index ec1b10b..8250352 100644
--- a/packages/devtools_app/lib/devtools.dart
+++ b/packages/devtools_app/lib/devtools.dart
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 /// The DevTools application version
-// This version should only be updated by running tools/update_version.dart
-// that updates all versions for DevTools.
-// Note: a regexp in tools/update_version.dart matches
-// the constant declaration `const version =`.
-// If you change the declaration you must also modify the regex in
-// tools/update_version.dart.
-const version = '2.40.2';
+///
+/// This version should only be updated by running the 'dt update-version'
+/// command that updates the version here and in 'devtools_app/pubspec.yaml'.
+///
+/// Note: a regexp in the `dt update-version' command logic matches the constant
+/// declaration `const version =`. If you change the declaration you must also
+/// modify the regex in the `dt update-version' command logic.
+const version = '2.42.0-dev.0';
diff --git a/packages/devtools_app/lib/devtools_app.dart b/packages/devtools_app/lib/devtools_app.dart
index c555876..a74c022 100644
--- a/packages/devtools_app/lib/devtools_app.dart
+++ b/packages/devtools_app/lib/devtools_app.dart
@@ -5,11 +5,11 @@
 export 'src/app.dart';
 export 'src/extensions/extension_service.dart';
 export 'src/extensions/extension_service_helpers.dart';
-export 'src/framework/app_bar.dart';
 export 'src/framework/home_screen.dart';
 export 'src/framework/notifications_view.dart';
-export 'src/framework/release_notes/release_notes.dart';
-export 'src/framework/status_line.dart';
+export 'src/framework/release_notes.dart';
+export 'src/framework/scaffold/app_bar.dart';
+export 'src/framework/scaffold/status_line.dart';
 export 'src/screens/app_size/app_size_controller.dart';
 export 'src/screens/app_size/app_size_screen.dart';
 export 'src/screens/debugger/breakpoint_manager.dart';
@@ -29,9 +29,6 @@
 export 'src/screens/inspector_shared/inspector_screen_controller.dart';
 export 'src/screens/logging/logging_controller.dart';
 export 'src/screens/logging/logging_screen.dart';
-export 'src/screens/logging/logging_screen_v2/logging_controller_v2.dart';
-export 'src/screens/logging/logging_screen_v2/logging_model.dart';
-export 'src/screens/logging/logging_screen_v2/logging_screen_v2.dart';
 export 'src/screens/memory/framework/memory_controller.dart';
 export 'src/screens/memory/framework/memory_screen.dart';
 export 'src/screens/network/network_controller.dart';
@@ -61,6 +58,8 @@
 export 'src/screens/vm_developer/vm_developer_tools_controller.dart';
 export 'src/screens/vm_developer/vm_developer_tools_screen.dart';
 export 'src/screens/vm_developer/vm_service_private_extensions.dart';
+export 'src/service/connected_app/connected_app.dart';
+export 'src/service/connected_app/connection_info.dart';
 export 'src/service/editor/editor_client.dart';
 export 'src/service/json_to_service_cache.dart';
 export 'src/service/service_extensions.dart';
@@ -71,11 +70,7 @@
 export 'src/service/vm_service_wrapper.dart';
 export 'src/shared/analytics/analytics.dart';
 export 'src/shared/analytics/analytics_controller.dart';
-export 'src/shared/banner_messages.dart';
 export 'src/shared/charts/treemap.dart';
-export 'src/shared/common_widgets.dart';
-export 'src/shared/connected_app.dart';
-export 'src/shared/connection_info.dart';
 export 'src/shared/console/console_service.dart';
 export 'src/shared/console/eval/eval_service.dart';
 export 'src/shared/console/eval/inspector_tree.dart';
@@ -85,12 +80,16 @@
 export 'src/shared/diagnostics/inspector_service.dart';
 export 'src/shared/environment_parameters/environment_parameters_base.dart';
 export 'src/shared/environment_parameters/environment_parameters_external.dart';
-export 'src/shared/error_badge_manager.dart';
+export 'src/shared/framework/routing.dart';
+export 'src/shared/framework/screen.dart';
 export 'src/shared/globals.dart';
 export 'src/shared/http/http_request_data.dart';
+export 'src/shared/managers/banner_messages.dart';
+export 'src/shared/managers/error_badge_manager.dart';
+export 'src/shared/managers/notifications.dart';
+export 'src/shared/managers/script_manager.dart';
 export 'src/shared/memory/gc_stats.dart';
-export 'src/shared/notifications.dart';
-export 'src/shared/offline_data.dart';
+export 'src/shared/offline/offline_data.dart';
 export 'src/shared/preferences/preferences.dart';
 export 'src/shared/primitives/listenable.dart';
 export 'src/shared/primitives/simple_items.dart';
@@ -98,12 +97,10 @@
 export 'src/shared/primitives/trace_event.dart';
 export 'src/shared/primitives/trees.dart';
 export 'src/shared/primitives/utils.dart';
-export 'src/shared/routing.dart';
-export 'src/shared/screen.dart';
-export 'src/shared/scripts/script_manager.dart';
-export 'src/shared/side_panel.dart';
+export 'src/shared/ui/common_widgets.dart';
 export 'src/shared/ui/filter.dart';
 export 'src/shared/ui/hover.dart';
 export 'src/shared/ui/icons.dart';
 export 'src/shared/ui/search.dart';
-export 'src/shared/utils.dart';
+export 'src/shared/ui/side_panel.dart';
+export 'src/shared/utils/utils.dart';
diff --git a/packages/devtools_app/lib/initialization.dart b/packages/devtools_app/lib/initialization.dart
index 9d69ca0..698bac9 100644
--- a/packages/devtools_app/lib/initialization.dart
+++ b/packages/devtools_app/lib/initialization.dart
@@ -7,12 +7,12 @@
 import 'package:flutter_web_plugins/url_strategy.dart';
 
 import 'src/app.dart';
-import 'src/framework/app_error_handling.dart';
 import 'src/framework/framework_core.dart';
 import 'src/screens/debugger/syntax_highlighter.dart';
 import 'src/shared/analytics/analytics_controller.dart';
 import 'src/shared/config_specific/logger/logger_helpers.dart';
 import 'src/shared/feature_flags.dart';
+import 'src/shared/framework/app_error_handling.dart';
 import 'src/shared/globals.dart';
 import 'src/shared/primitives/url_utils.dart';
 import 'src/shared/primitives/utils.dart';
diff --git a/packages/devtools_app/lib/src/app.dart b/packages/devtools_app/lib/src/app.dart
index 077418c..a5d295f 100644
--- a/packages/devtools_app/lib/src/app.dart
+++ b/packages/devtools_app/lib/src/app.dart
@@ -19,8 +19,8 @@
 import 'framework/home_screen.dart';
 import 'framework/initializer.dart';
 import 'framework/notifications_view.dart';
-import 'framework/release_notes/release_notes.dart';
-import 'framework/scaffold.dart';
+import 'framework/release_notes.dart';
+import 'framework/scaffold/scaffold.dart';
 import 'screens/app_size/app_size_controller.dart';
 import 'screens/app_size/app_size_screen.dart';
 import 'screens/debugger/debugger_controller.dart';
@@ -31,8 +31,6 @@
 import 'screens/inspector_shared/inspector_screen_controller.dart';
 import 'screens/logging/logging_controller.dart';
 import 'screens/logging/logging_screen.dart';
-import 'screens/logging/logging_screen_v2/logging_controller_v2.dart';
-import 'screens/logging/logging_screen_v2/logging_screen_v2.dart';
 import 'screens/memory/framework/memory_controller.dart';
 import 'screens/memory/framework/memory_screen.dart';
 import 'screens/network/network_controller.dart';
@@ -47,18 +45,18 @@
 import 'service/service_extension_widgets.dart';
 import 'shared/analytics/analytics.dart' as ga;
 import 'shared/analytics/analytics_controller.dart';
-import 'shared/common_widgets.dart';
 import 'shared/feature_flags.dart';
-import 'shared/framework_controller.dart';
+import 'shared/framework/framework_controller.dart';
+import 'shared/framework/routing.dart';
+import 'shared/framework/screen.dart';
 import 'shared/globals.dart';
-import 'shared/offline_data.dart';
-import 'shared/offline_screen.dart';
+import 'shared/offline/offline_data.dart';
+import 'shared/offline/offline_screen.dart';
+import 'shared/primitives/query_parameters.dart';
 import 'shared/primitives/utils.dart';
-import 'shared/query_parameters.dart';
-import 'shared/routing.dart';
-import 'shared/screen.dart';
+import 'shared/ui/common_widgets.dart';
 import 'shared/ui/hover.dart';
-import 'shared/utils.dart';
+import 'shared/utils/utils.dart';
 import 'standalone_ui/standalone_screen.dart';
 
 // Assign to true to use a sample implementation of a conditional screen.
@@ -90,15 +88,16 @@
     if (FeatureFlags.devToolsExtensions) {
       // TODO(https://github.com/flutter/devtools/issues/6273): stop special
       // casing the package:provider extension.
-      final containsProviderExtension = extensionService
-          .currentExtensions.value.visibleExtensions
-          .where((e) => e.name == 'provider')
-          .isNotEmpty;
-      final devToolsScreens = containsProviderExtension
-          ? _originalScreens
-              .where((s) => s.screenId != ScreenMetaData.provider.id)
-              .toList()
-          : _originalScreens;
+      final containsProviderExtension =
+          extensionService.currentExtensions.value.visibleExtensions
+              .where((e) => e.name == 'provider')
+              .isNotEmpty;
+      final devToolsScreens =
+          containsProviderExtension
+              ? _originalScreens
+                  .where((s) => s.screenId != ScreenMetaData.provider.id)
+                  .toList()
+              : _originalScreens;
       return [...devToolsScreens, ..._extensionScreens];
     }
     return _originalScreens;
@@ -107,10 +106,8 @@
   List<Screen> get _originalScreens =>
       widget.originalScreens.map((s) => s.screen).toList();
 
-  Iterable<Screen> get _extensionScreens =>
-      extensionService.visibleExtensions.map(
-        (e) => DevToolsScreen<void>(ExtensionScreen(e)).screen,
-      );
+  Iterable<Screen> get _extensionScreens => extensionService.visibleExtensions
+      .map((e) => DevToolsScreen<void>(ExtensionScreen(e)).screen);
 
   final hoverCardController = HoverCardController();
 
@@ -199,25 +196,13 @@
 
     // Provide the appropriate page route.
     if (pages.containsKey(page)) {
-      Widget widget = pages[page]!(
-        context,
-        page,
-        params,
-        state,
-      );
-      assert(
-        () {
-          widget = _AlternateCheckedModeBanner(
-            builder: (context) => pages[page]!(
-              context,
-              page,
-              params,
-              state,
-            ),
-          );
-          return true;
-        }(),
-      );
+      Widget widget = pages[page]!(context, page, params, state);
+      assert(() {
+        widget = _AlternateCheckedModeBanner(
+          builder: (context) => pages[page]!(context, page, params, state),
+        );
+        return true;
+      }());
       return MaterialPage(child: widget);
     }
 
@@ -239,7 +224,7 @@
     BuildContext _,
     String? page,
     DevToolsQueryParams queryParams,
-    DevToolsNavigationState? __,
+    DevToolsNavigationState? _,
   ) {
     final vmServiceUri = queryParams.vmServiceUri;
     final embedMode = queryParams.embedMode;
@@ -262,26 +247,29 @@
           preferences.vmDeveloperModeEnabled,
           extensionService.currentExtensions,
         ],
-        builder: (_, __, child) {
-          final screensInScaffold = _visibleScreens()
-              .where(
-                (s) => maybeIncludeOnlyEmbeddedScreen(
-                  s,
-                  page: page,
-                  embedMode: embedMode,
-                ),
-              )
-              .toList();
+        builder: (_, _, child) {
+          final screensInScaffold =
+              _visibleScreens()
+                  .where(
+                    (s) => maybeIncludeOnlyEmbeddedScreen(
+                      s,
+                      page: page,
+                      embedMode: embedMode,
+                    ),
+                  )
+                  .toList();
 
           removeHiddenScreens(screensInScaffold, queryParams);
 
           DevToolsScaffold scaffold;
 
-          final originalScreen =
-              _screens.firstWhereOrNull((s) => s.screenId == page);
+          final originalScreen = _screens.firstWhereOrNull(
+            (s) => s.screenId == page,
+          );
           final screenInOriginalScreens = originalScreen != null;
-          final screenInScaffoldScreens =
-              screensInScaffold.any((s) => s.screenId == page);
+          final screenInScaffoldScreens = screensInScaffold.any(
+            (s) => s.screenId == page,
+          );
           if (page != null &&
               screenInOriginalScreens &&
               !screenInScaffoldScreens) {
@@ -304,41 +292,49 @@
             scaffold = DevToolsScaffold.withChild(
               embedMode: embedMode,
               child: CenteredMessage(
-                message: 'No DevTools '
+                message:
+                    'No DevTools '
                     '${queryParams.hideAllExceptExtensions ? 'extensions' : 'screens'} '
                     'available for your project.',
               ),
             );
           } else {
-            final connectedToFlutterApp = serviceConnection
-                    .serviceManager.connectedApp?.isFlutterAppNow ??
+            final connectedToFlutterApp =
+                serviceConnection
+                    .serviceManager
+                    .connectedApp
+                    ?.isFlutterAppNow ??
                 false;
-            final connectedToDartWebApp = serviceConnection
-                    .serviceManager.connectedApp?.isDartWebAppNow ??
+            final connectedToDartWebApp =
+                serviceConnection
+                    .serviceManager
+                    .connectedApp
+                    ?.isDartWebAppNow ??
                 false;
             scaffold = DevToolsScaffold(
               embedMode: embedMode,
               page: page,
               screens: screensInScaffold,
-              actions: isEmbedded()
-                  ? []
-                  : [
-                      if (paramsContainVmServiceUri) ...[
-                        // Hide the hot reload button for Dart web apps, where the
-                        // hot reload service extension is not avilable and where the
-                        // [service.reloadServices] RPC is not implemented.
-                        // TODO(https://github.com/flutter/devtools/issues/6441): find
-                        // a way to show this for Dart web apps when supported.
-                        if (!connectedToDartWebApp)
-                          HotReloadButton(
-                            callOnVmServiceDirectly: !connectedToFlutterApp,
-                          ),
-                        // This button will hide itself based on whether the
-                        // hot restart service is available for the connected app.
-                        const HotRestartButton(),
+              actions:
+                  isEmbedded()
+                      ? []
+                      : [
+                        if (paramsContainVmServiceUri) ...[
+                          // Hide the hot reload button for Dart web apps, where the
+                          // hot reload service extension is not avilable and where the
+                          // [service.reloadServices] RPC is not implemented.
+                          // TODO(https://github.com/flutter/devtools/issues/6441): find
+                          // a way to show this for Dart web apps when supported.
+                          if (!connectedToDartWebApp)
+                            HotReloadButton(
+                              callOnVmServiceDirectly: !connectedToFlutterApp,
+                            ),
+                          // This button will hide itself based on whether the
+                          // hot restart service is available for the connected app.
+                          const HotRestartButton(),
+                        ],
+                        ...DevToolsScaffold.defaultActions(),
                       ],
-                      ...DevToolsScaffold.defaultActions(),
-                    ],
             );
           }
           return MultiProvider(
@@ -359,7 +355,7 @@
     return _routes ??= {
       homeScreenId: _buildTabbedPage,
       for (final screen in _screens) screen.screenId: _buildTabbedPage,
-      snapshotScreenId: (_, __, params, ___) {
+      snapshotScreenId: (_, _, params, _) {
         return DevToolsScaffold.withChild(
           key: UniqueKey(),
           embedMode: params.embedMode,
@@ -386,7 +382,7 @@
     //  the server which page they are rendering.
     return {
       for (final type in StandaloneScreenType.values)
-        type.name: (_, __, args, ___) => type.screen,
+        type.name: (_, _, args, _) => type.screen,
     };
   }
 
@@ -434,9 +430,7 @@
             Provider<AnalyticsController>.value(
               value: widget.analyticsController,
             ),
-            Provider<HoverCardController>.value(
-              value: hoverCardController,
-            ),
+            Provider<HoverCardController>.value(value: hoverCardController),
             Provider<ReleaseNotesController>.value(
               value: releaseNotesController,
             ),
@@ -456,8 +450,9 @@
       routeInformationParser: DevToolsRouteInformationParser(),
       // Disable default scrollbar behavior on web to fix duplicate scrollbars
       // bug, see https://github.com/flutter/flutter/issues/90697:
-      scrollBehavior:
-          const MaterialScrollBehavior().copyWith(scrollbars: !kIsWeb),
+      scrollBehavior: const MaterialScrollBehavior().copyWith(
+        scrollbars: !kIsWeb,
+      ),
     );
   }
 
@@ -507,7 +502,8 @@
     if (reason == ScreenDisabledReason.requiresDartLibrary) {
       // Special case for screens that require a library since the message
       // needs to be generated dynamically.
-      disabledMessage = 'The ${screen.title} screen requires library '
+      disabledMessage =
+          'The ${screen.title} screen requires library '
           '${screen.requiresLibrary}, but the library was not detected.';
     } else if (reason?.message case final String message) {
       disabledMessage = 'The ${screen.title} screen $message';
@@ -563,12 +559,13 @@
 
 /// A [WidgetBuilder] that takes an additional map of URL query parameters and
 /// args, as well a state not included in the URL.
-typedef UrlParametersBuilder = Widget Function(
-  BuildContext,
-  String?,
-  DevToolsQueryParams,
-  DevToolsNavigationState?,
-);
+typedef UrlParametersBuilder =
+    Widget Function(
+      BuildContext,
+      String?,
+      DevToolsQueryParams,
+      DevToolsNavigationState?,
+    );
 
 /// Displays the checked mode banner in the bottom end corner instead of the
 /// top end corner.
@@ -584,10 +581,8 @@
     return Banner(
       message: 'DEBUG',
       textDirection: TextDirection.ltr,
-      location: BannerLocation.topStart,
-      child: Builder(
-        builder: builder,
-      ),
+      location: BannerLocation.bottomEnd,
+      child: Builder(builder: builder),
     );
   }
 }
@@ -613,21 +608,15 @@
       child: Column(
         mainAxisSize: MainAxisSize.min,
         children: [
-          Text(
-            title,
-            style: theme.textTheme.titleLarge,
-          ),
+          Text(title, style: theme.textTheme.titleLarge),
           const SizedBox(height: denseSpacing),
           if (description != null)
-            Text(
-              description!,
-              style: theme.regularTextStyle,
-            ),
+            Text(description!, style: theme.regularTextStyle),
           if (embedMode == EmbedMode.none) ...[
             const SizedBox(height: defaultSpacing),
             ElevatedButton(
-              onPressed: () =>
-                  routerDelegate.navigateHome(clearScreenParam: true),
+              onPressed:
+                  () => routerDelegate.navigateHome(clearScreenParam: true),
               child: const Text('Go to Home screen'),
             ),
           ],
@@ -669,23 +658,18 @@
     ),
     DevToolsScreen<DebuggerController>(
       DebuggerScreen(),
-      createController: (routerDelegate) => DebuggerController(
-        routerDelegate: routerDelegate,
-      ),
+      createController:
+          (routerDelegate) =>
+              DebuggerController(routerDelegate: routerDelegate),
     ),
     DevToolsScreen<NetworkController>(
       NetworkScreen(),
       createController: (_) => NetworkController(),
     ),
-    FeatureFlags.loggingV2
-        ? DevToolsScreen<LoggingControllerV2>(
-            LoggingScreenV2(),
-            createController: (_) => LoggingControllerV2(),
-          )
-        : DevToolsScreen<LoggingController>(
-            LoggingScreen(),
-            createController: (_) => LoggingController(),
-          ),
+    DevToolsScreen<LoggingController>(
+      LoggingScreen(),
+      createController: (_) => LoggingController(),
+    ),
     DevToolsScreen<void>(ProviderScreen()),
     DevToolsScreen<AppSizeController>(
       AppSizeScreen(),
diff --git a/packages/devtools_app/lib/src/example/conditional_screen.dart b/packages/devtools_app/lib/src/example/conditional_screen.dart
index 5d53d0b..5cf49e2 100644
--- a/packages/devtools_app/lib/src/example/conditional_screen.dart
+++ b/packages/devtools_app/lib/src/example/conditional_screen.dart
@@ -7,10 +7,10 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
+import '../shared/framework/screen.dart';
 import '../shared/globals.dart';
-import '../shared/offline_data.dart';
-import '../shared/screen.dart';
-import '../shared/utils.dart';
+import '../shared/offline/offline_data.dart';
+import '../shared/utils/utils.dart';
 
 /// This is an example implementation of a conditional screen that supports
 /// offline mode and uses a provided controller [ExampleController].
@@ -19,13 +19,13 @@
 /// DevTools app.
 class ExampleConditionalScreen extends Screen {
   const ExampleConditionalScreen()
-      : super.conditional(
-          id: id,
-          requiresLibrary: 'package:flutter/',
-          title: 'Example',
-          icon: Icons.palette,
-          worksWithOfflineData: true,
-        );
+    : super.conditional(
+        id: id,
+        requiresLibrary: 'package:flutter/',
+        title: 'Example',
+        icon: Icons.palette,
+        worksWithOfflineData: true,
+      );
 
   static const id = 'example';
 
@@ -46,8 +46,10 @@
 class _ExampleConditionalScreenBodyState
     extends State<_ExampleConditionalScreenBody>
     with
-        ProvidedControllerMixin<ExampleController,
-            _ExampleConditionalScreenBody> {
+        ProvidedControllerMixin<
+          ExampleController,
+          _ExampleConditionalScreenBody
+        > {
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
@@ -73,8 +75,9 @@
     unawaited(_init());
   }
 
-  final data =
-      ValueNotifier<ExampleScreenData>(ExampleScreenData('Example screen'));
+  final data = ValueNotifier<ExampleScreenData>(
+    ExampleScreenData('Example screen'),
+  );
 
   final _initialized = Completer<void>();
 
diff --git a/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart b/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart
index 3c74841..ec923e5 100644
--- a/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart
+++ b/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart
@@ -13,10 +13,10 @@
 
 import '../../shared/development_helpers.dart';
 import '../../shared/globals.dart';
+import '../../shared/primitives/query_parameters.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/query_parameters.dart';
 import '../../shared/server/server.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'controller.dart';
 
 /// Incrementer for the extension iFrame view that will live for the entire
@@ -57,9 +57,10 @@
     );
     final queryParams = {
       ...DevToolsQueryParams.load().params,
-      ExtensionEventParameters.theme: isDarkThemeEnabled()
-          ? ExtensionEventParameters.themeValueDark
-          : ExtensionEventParameters.themeValueLight,
+      ExtensionEventParameters.theme:
+          isDarkThemeEnabled()
+              ? ExtensionEventParameters.themeValueDark
+              : ExtensionEventParameters.themeValueLight,
       if (dtdManager.uri != null) 'dtdUri': dtdManager.uri.toString(),
     };
     return Uri.parse(baseUri).copyWith(queryParameters: queryParams).toString();
@@ -84,12 +85,12 @@
     );
     _initialized = true;
 
-    _extensionIFrame = HTMLIFrameElement()
-      // This url is safe because we built it ourselves and it does not include
-      // any user input.
-      // ignore: unsafe_html
-      ..src = extensionUrl
-      ..allow = 'usb';
+    _extensionIFrame =
+        HTMLIFrameElement()
+          // This url is safe because we built it ourselves and it does not include
+          // any user input.
+          ..src = extensionUrl
+          ..allow = 'usb';
     _extensionIFrame.style
       ..border = 'none'
       ..height = '100%'
diff --git a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
index 7b5e63a..7fb7ea9 100644
--- a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
+++ b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
@@ -13,11 +13,11 @@
 import 'package:flutter/material.dart';
 import 'package:web/web.dart';
 
-import '../../shared/banner_messages.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart';
 import '../../shared/globals.dart';
-import '../../shared/utils.dart';
+import '../../shared/managers/banner_messages.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import '_controller_web.dart';
 import 'controller.dart';
 
@@ -60,9 +60,7 @@
           if (refreshing) {
             return const CenteredCircularProgressIndicator();
           }
-          return HtmlElementView(
-            viewType: _embeddedExtensionController.viewId,
-          );
+          return HtmlElementView(viewType: _embeddedExtensionController.viewId);
         },
       ),
     );
@@ -118,8 +116,9 @@
     );
 
     autoDisposeStreamSubscription(
-      embeddedExtensionController.extensionPostEventStream.stream
-          .listen((event) async {
+      embeddedExtensionController.extensionPostEventStream.stream.listen((
+        event,
+      ) async {
         final ready = await _pingExtensionUntilReady();
         if (ready) {
           switch (event.type) {
@@ -137,7 +136,8 @@
             'Something went wrong. The '
             '${embeddedExtensionController.extensionConfig.name} extension is '
             'not ready.',
-            reportExplanation: 'The extension did not respond to multiple '
+            reportExplanation:
+                'The extension did not respond to multiple '
                 'DevToolsExtensionEventType.ping events with the expected '
                 'DevToolsExtensionEventType.pong event.',
           );
@@ -147,9 +147,10 @@
 
     addAutoDisposeListener(preferences.darkModeEnabled, () {
       updateTheme(
-        theme: isDarkThemeEnabled()
-            ? ExtensionEventParameters.themeValueDark
-            : ExtensionEventParameters.themeValueLight,
+        theme:
+            isDarkThemeEnabled()
+                ? ExtensionEventParameters.themeValueDark
+                : ExtensionEventParameters.themeValueLight,
       );
     });
   }
@@ -182,9 +183,10 @@
     if (extensionEvent != null) {
       onEventReceived(
         extensionEvent,
-        onUnknownEvent: () => notificationService.push(
-          'Unknown event received from extension: $extensionEvent}',
-        ),
+        onUnknownEvent:
+            () => notificationService.push(
+              'Unknown event received from extension: $extensionEvent}',
+            ),
       );
     }
   }
@@ -197,13 +199,15 @@
   Future<bool> _pingExtensionUntilReady() async {
     var ready = true;
     if (!_extensionHandlerReady.isCompleted) {
-      _pollForExtensionHandlerReady =
-          Timer.periodic(const Duration(milliseconds: 200), (_) {
-        // Once the extension UI is ready, the extension will receive this
-        // [DevToolsExtensionEventType.ping] message and return a
-        // [DevToolsExtensionEventType.pong] message, handled in [_handleMessage].
-        ping();
-      });
+      _pollForExtensionHandlerReady = Timer.periodic(
+        const Duration(milliseconds: 200),
+        (_) {
+          // Once the extension UI is ready, the extension will receive this
+          // [DevToolsExtensionEventType.ping] message and return a
+          // [DevToolsExtensionEventType.pong] message, handled in [_handleMessage].
+          ping();
+        },
+      );
 
       await _extensionHandlerReady.future.timeout(
         _pollUntilReadyTimeout,
@@ -301,7 +305,7 @@
     final showBannerMessageEvent = ShowBannerMessageExtensionEvent.from(event);
     final bannerMessageType =
         BannerMessageType.parse(showBannerMessageEvent.bannerMessageType) ??
-            BannerMessageType.warning;
+        BannerMessageType.warning;
     final bannerMessage = BannerMessage(
       messageType: bannerMessageType,
       key: Key(
diff --git a/packages/devtools_app/lib/src/extensions/extension_screen.dart b/packages/devtools_app/lib/src/extensions/extension_screen.dart
index 0c3a9d8..af3c91e 100644
--- a/packages/devtools_app/lib/src/extensions/extension_screen.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_screen.dart
@@ -9,30 +9,30 @@
 
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
+import '../shared/framework/screen.dart';
 import '../shared/globals.dart';
-import '../shared/screen.dart';
+import '../shared/ui/common_widgets.dart';
 import 'embedded/controller.dart';
 import 'embedded/view.dart';
 import 'extension_screen_controls.dart';
 
 class ExtensionScreen extends Screen {
   ExtensionScreen(this.extensionConfig)
-      : super.conditional(
-          // TODO(kenz): we may need to ensure this is a unique id.
-          id: extensionConfig.screenId,
-          title: extensionConfig.name,
-          icon: extensionConfig.icon,
-          requiresConnection:
-              // We set this to false all the time when embedded because the
-              // available extensions are displayed in a tool window (IntelliJ
-              // and Android Studio) with or without any active debug sessions.
-              // This prevents a "DevTools Extensions" tool window in IntelliJ
-              // and Android Studio that appears to be missing extensions. When
-              // a connection is still required to use the extension, messaging
-              // for this is provided by the [EmbeddedExtensionView] widget.
-              isEmbedded() ? false : extensionConfig.requiresConnection,
-        );
+    : super.conditional(
+        // TODO(kenz): we may need to ensure this is a unique id.
+        id: extensionConfig.screenId,
+        title: extensionConfig.name,
+        icon: extensionConfig.icon,
+        requiresConnection:
+            // We set this to false all the time when embedded because the
+            // available extensions are displayed in a tool window (IntelliJ
+            // and Android Studio) with or without any active debug sessions.
+            // This prevents a "DevTools Extensions" tool window in IntelliJ
+            // and Android Studio that appears to be missing extensions. When
+            // a connection is still required to use the extension, messaging
+            // for this is provided by the [EmbeddedExtensionView] widget.
+            isEmbedded() ? false : extensionConfig.requiresConnection,
+      );
 
   final DevToolsExtensionConfig extensionConfig;
 
@@ -63,8 +63,9 @@
     ga.screen(
       gac.DevToolsExtensionEvents.extensionScreenName(widget.extensionConfig),
     );
-    extensionController =
-        createEmbeddedExtensionController(widget.extensionConfig)..init();
+    extensionController = createEmbeddedExtensionController(
+      widget.extensionConfig,
+    )..init();
   }
 
   @override
@@ -92,11 +93,7 @@
 }
 
 class ExtensionView extends StatelessWidget {
-  const ExtensionView({
-    super.key,
-    required this.controller,
-    required this.ext,
-  });
+  const ExtensionView({super.key, required this.controller, required this.ext});
 
   final EmbeddedExtensionController controller;
 
@@ -109,15 +106,15 @@
       children: [
         EmbeddedExtensionHeader(
           ext: ext,
-          onForceReload: () =>
-              controller.postMessage(DevToolsExtensionEventType.forceReload),
+          onForceReload:
+              () => controller.postMessage(
+                DevToolsExtensionEventType.forceReload,
+              ),
         ),
         const SizedBox(height: intermediateSpacing),
         Expanded(
           child: ValueListenableBuilder<ExtensionEnabledState>(
-            valueListenable: extensionService.enabledStateListenable(
-              ext.name,
-            ),
+            valueListenable: extensionService.enabledStateListenable(ext.name),
             builder: (context, activationState, _) {
               if (activationState == ExtensionEnabledState.enabled) {
                 return KeepAliveWrapper(
@@ -126,9 +123,7 @@
                   ),
                 );
               }
-              return EnableExtensionPrompt(
-                ext: controller.extensionConfig,
-              );
+              return EnableExtensionPrompt(ext: controller.extensionConfig);
             },
           ),
         ),
@@ -138,10 +133,8 @@
 }
 
 extension ExtensionConfigExtension on DevToolsExtensionConfig {
-  IconData get icon => IconData(
-        materialIconCodePoint,
-        fontFamily: 'MaterialIcons',
-      );
+  IconData get icon =>
+      IconData(materialIconCodePoint, fontFamily: 'MaterialIcons');
 
   String get screenId => '${name}_ext';
 }
diff --git a/packages/devtools_app/lib/src/extensions/extension_screen_controls.dart b/packages/devtools_app/lib/src/extensions/extension_screen_controls.dart
index 8c3afac..3818137 100644
--- a/packages/devtools_app/lib/src/extensions/extension_screen_controls.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_screen_controls.dart
@@ -11,9 +11,9 @@
 
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
+import '../shared/framework/routing.dart';
 import '../shared/globals.dart';
-import '../shared/routing.dart';
+import '../shared/ui/common_widgets.dart';
 
 class EmbeddedExtensionHeader extends StatelessWidget {
   const EmbeddedExtensionHeader({
@@ -41,8 +41,9 @@
             child: RichText(
               text: TextSpan(
                 text: 'package:$extensionName extension',
-                style: theme.regularTextStyle
-                    .copyWith(fontWeight: FontWeight.bold),
+                style: theme.regularTextStyle.copyWith(
+                  fontWeight: FontWeight.bold,
+                ),
                 children: [
                   TextSpan(
                     text: ' (v${ext.version})',
@@ -63,8 +64,8 @@
                     url: ext.issueTrackerLink,
                     gaScreenName:
                         gac.DevToolsExtensionEvents.extensionScreenId.name,
-                    gaSelectedItemDescription:
-                        gac.DevToolsExtensionEvents.extensionFeedback(ext),
+                    gaSelectedItemDescription: gac
+                        .DevToolsExtensionEvents.extensionFeedback(ext),
                   ),
                   context: context,
                 ),
@@ -167,10 +168,7 @@
               text: 'Are you sure you want to disable the ',
               style: theme.regularTextStyle,
               children: [
-                TextSpan(
-                  text: ext.displayName,
-                  style: theme.fixedFontStyle,
-                ),
+                TextSpan(text: ext.displayName, style: theme.fixedFontStyle),
                 const TextSpan(text: ' extension?'),
               ],
             ),
@@ -186,10 +184,7 @@
                   style: theme.boldTextStyle,
                 ),
                 WidgetSpan(
-                  child: Icon(
-                    Icons.extension_rounded,
-                    size: defaultIconSize,
-                  ),
+                  child: Icon(Icons.extension_rounded, size: defaultIconSize),
                 ),
                 const TextSpan(text: ' menu.'),
               ],
@@ -208,8 +203,9 @@
               extensionService.setExtensionEnabledState(ext, enable: false),
             );
             Navigator.of(context).pop(dialogDefaultContext);
-            DevToolsRouterDelegate.of(context)
-                .navigateHome(clearScreenParam: true);
+            DevToolsRouterDelegate.of(
+              context,
+            ).navigateHome(clearScreenParam: true);
           },
           child: const Text('YES, DISABLE'),
         ),
@@ -237,12 +233,10 @@
               text: 'The ',
               style: theme.regularTextStyle,
               children: [
-                TextSpan(
-                  text: ext.name,
-                  style: theme.fixedFontStyle,
-                ),
+                TextSpan(text: ext.name, style: theme.fixedFontStyle),
                 const TextSpan(
-                  text: ' extension has not been enabled. Do you want to enable'
+                  text:
+                      ' extension has not been enabled. Do you want to enable'
                       ' this extension?\nYou can always change this setting '
                       'later from the DevTools Extensions ',
                 ),
@@ -252,9 +246,7 @@
                     color: theme.colorScheme.onSurface,
                   ),
                 ),
-                const TextSpan(
-                  text: ' menu. ',
-                ),
+                const TextSpan(text: ' menu. '),
               ],
             ),
           ),
@@ -292,8 +284,9 @@
                       enable: false,
                     ),
                   );
-                  DevToolsRouterDelegate.of(context)
-                      .navigateHome(clearScreenParam: true);
+                  DevToolsRouterDelegate.of(
+                    context,
+                  ).navigateHome(clearScreenParam: true);
                 },
               ),
             ],
diff --git a/packages/devtools_app/lib/src/extensions/extension_service.dart b/packages/devtools_app/lib/src/extensions/extension_service.dart
index e393c1f..d27a29e 100644
--- a/packages/devtools_app/lib/src/extensions/extension_service.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_service.dart
@@ -18,20 +18,21 @@
 
 /// Data pattern containing a [List] of available extensions and a [List] of
 /// visible extensions.
-typedef DevToolsExtensionsGroup = ({
-  /// All the DevTools extensions, runtime and static, that are available for
-  /// the connected application, regardless of whether they have been enabled or
-  /// disabled by the user.
-  ///
-  /// This set of extensions will include one version of a DevTools extension
-  /// per package and will exclude any duplicates that have been marked as
-  /// ignored in [_maybeIgnoreExtensions].
-  List<DevToolsExtensionConfig> availableExtensions,
+typedef DevToolsExtensionsGroup =
+    ({
+      /// All the DevTools extensions, runtime and static, that are available for
+      /// the connected application, regardless of whether they have been enabled or
+      /// disabled by the user.
+      ///
+      /// This set of extensions will include one version of a DevTools extension
+      /// per package and will exclude any duplicates that have been marked as
+      /// ignored in [_maybeIgnoreExtensions].
+      List<DevToolsExtensionConfig> availableExtensions,
 
-  /// DevTools extensions that are visible in their own DevTools screen (i.e.
-  /// extensions that have not been manually disabled by the user).
-  List<DevToolsExtensionConfig> visibleExtensions,
-});
+      /// DevTools extensions that are visible in their own DevTools screen (i.e.
+      /// extensions that have not been manually disabled by the user).
+      List<DevToolsExtensionConfig> visibleExtensions,
+    });
 
 class ExtensionService extends DisposableController
     with AutoDisposeControllerMixin {
@@ -60,12 +61,10 @@
   /// from having to listen to two separate notifiers.
   ValueListenable<DevToolsExtensionsGroup> get currentExtensions =>
       _currentExtensions;
-  final _currentExtensions = ValueNotifier<DevToolsExtensionsGroup>(
-    (
-      availableExtensions: <DevToolsExtensionConfig>[],
-      visibleExtensions: <DevToolsExtensionConfig>[],
-    ),
-  );
+  final _currentExtensions = ValueNotifier<DevToolsExtensionsGroup>((
+    availableExtensions: <DevToolsExtensionConfig>[],
+    visibleExtensions: <DevToolsExtensionConfig>[],
+  ));
 
   /// All the DevTools extensions, runtime and static, that are available for
   /// the connected application, regardless of whether they have been enabled or
@@ -120,9 +119,7 @@
   ) {
     return _extensionEnabledStates.putIfAbsent(
       extensionName.toLowerCase(),
-      () => ValueNotifier<ExtensionEnabledState>(
-        ExtensionEnabledState.none,
-      ),
+      () => ValueNotifier<ExtensionEnabledState>(ExtensionEnabledState.none),
     );
   }
 
@@ -183,8 +180,9 @@
         serviceConnection.serviceManager.connectedState.value.connected &&
         serviceConnection.serviceManager.isolateManager.mainIsolate.value !=
             null) {
-      _appRoot = await serviceConnection.serviceManager
-          .connectedAppPackageRoot(dtdManager);
+      _appRoot = await serviceConnection.serviceManager.connectedAppPackageRoot(
+        dtdManager,
+      );
     }
 
     // TODO(kenz): gracefully handle app connections / disconnects when there
@@ -226,14 +224,16 @@
   // ignoring the static extension when there is a duplicate.
   void _deduplicateStaticExtensionsWithRuntimeExtensions() {
     if (runtimeExtensions.isEmpty) return;
-    for (final staticExtension
-        in staticExtensions.where((ext) => !isExtensionIgnored(ext))) {
+    for (final staticExtension in staticExtensions.where(
+      (ext) => !isExtensionIgnored(ext),
+    )) {
       // TODO(kenz): do we need to match on something other than name? Names
       // _should_ be unique since they match a pub package name, but this may
       // not always be true for extensions that are not published on pub or
       // extensions that do not follow best practices for naming.
-      final isRuntimeDuplicate =
-          runtimeExtensions.any((ext) => ext.name == staticExtension.name);
+      final isRuntimeDuplicate = runtimeExtensions.any(
+        (ext) => ext.name == staticExtension.name,
+      );
       if (isRuntimeDuplicate) {
         _log.fine(
           'ignoring duplicate static extension ${staticExtension.identifier} '
@@ -263,9 +263,10 @@
       );
       stateNotifier.value = stateFromOptionsFile;
 
-      final shouldIncludeInVisible = onlyIncludeEnabled
-          ? stateFromOptionsFile == ExtensionEnabledState.enabled
-          : stateFromOptionsFile != ExtensionEnabledState.disabled;
+      final shouldIncludeInVisible =
+          onlyIncludeEnabled
+              ? stateFromOptionsFile == ExtensionEnabledState.enabled
+              : stateFromOptionsFile != ExtensionEnabledState.disabled;
       if (shouldIncludeInVisible) {
         visible.add(extension);
       }
diff --git a/packages/devtools_app/lib/src/extensions/extension_service_helpers.dart b/packages/devtools_app/lib/src/extensions/extension_service_helpers.dart
index 0a607b3..48e3df0 100644
--- a/packages/devtools_app/lib/src/extensions/extension_service_helpers.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_service_helpers.dart
@@ -13,7 +13,7 @@
 void deduplicateExtensionsAndTakeLatest(
   List<DevToolsExtensionConfig> extensions, {
   required void Function(DevToolsExtensionConfig ext, {required bool ignore})
-      onSetIgnored,
+  onSetIgnored,
   Logger? logger,
   String extensionType = '',
 }) {
diff --git a/packages/devtools_app/lib/src/extensions/extension_settings.dart b/packages/devtools_app/lib/src/extensions/extension_settings.dart
index 1962410..0bd2ce6 100644
--- a/packages/devtools_app/lib/src/extensions/extension_settings.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_settings.dart
@@ -10,30 +10,34 @@
 
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
+import '../shared/framework/routing.dart';
 import '../shared/globals.dart';
-import '../shared/routing.dart';
+import '../shared/ui/common_widgets.dart';
 import 'extension_screen.dart';
 
 /// A [ScaffoldAction] that, when clicked, will open a dialog menu for
 /// managing DevTools extension states.
 class ExtensionSettingsAction extends ScaffoldAction {
   ExtensionSettingsAction({super.key, super.color})
-      : super(
-          iconAsset: 'icons/app_bar/devtools_extensions.png',
-          tooltip: 'DevTools Extensions',
-          onPressed: (context) {
-            unawaited(
-              showDialog(
-                context: context,
-                builder: (context) => ExtensionSettingsDialog(
-                  extensions: extensionService
-                      .currentExtensions.value.availableExtensions,
-                ),
-              ),
-            );
-          },
-        );
+    : super(
+        iconAsset: 'icons/app_bar/devtools_extensions.png',
+        tooltip: 'DevTools Extensions',
+        onPressed: (context) {
+          unawaited(
+            showDialog(
+              context: context,
+              builder:
+                  (context) => ExtensionSettingsDialog(
+                    extensions:
+                        extensionService
+                            .currentExtensions
+                            .value
+                            .availableExtensions,
+                  ),
+            ),
+          );
+        },
+      );
 }
 
 @visibleForTesting
@@ -72,21 +76,20 @@
             ),
             const PaddedDivider(),
             Expanded(
-              child: extensions.isEmpty
-                  ? Center(
-                      child: Text(
-                        'No extensions available.',
-                        style: theme.subtleTextStyle,
-                      ),
-                    )
-                  : _ExtensionsList(extensions: extensions),
+              child:
+                  extensions.isEmpty
+                      ? Center(
+                        child: Text(
+                          'No extensions available.',
+                          style: theme.subtleTextStyle,
+                        ),
+                      )
+                      : _ExtensionsList(extensions: extensions),
             ),
           ],
         ),
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
@@ -123,9 +126,9 @@
       child: ListView.builder(
         controller: scrollController,
         itemCount: widget.extensions.length,
-        itemBuilder: (context, index) => ExtensionSetting(
-          extension: widget.extensions[index],
-        ),
+        itemBuilder:
+            (context, index) =>
+                ExtensionSetting(extension: widget.extensions[index]),
       ),
     );
   }
@@ -142,35 +145,31 @@
     final buttonStates = [
       (
         title: 'Enabled',
-        isSelected: (ExtensionEnabledState state) =>
-            state == ExtensionEnabledState.enabled,
+        isSelected:
+            (ExtensionEnabledState state) =>
+                state == ExtensionEnabledState.enabled,
         onPressed: () {
           ga.select(
             gac.DevToolsExtensionEvents.extensionSettingsId.name,
             gac.DevToolsExtensionEvents.extensionEnableManual(extension),
           );
           unawaited(
-            extensionService.setExtensionEnabledState(
-              extension,
-              enable: true,
-            ),
+            extensionService.setExtensionEnabledState(extension, enable: true),
           );
         },
       ),
       (
         title: 'Disabled',
-        isSelected: (ExtensionEnabledState state) =>
-            state == ExtensionEnabledState.disabled,
+        isSelected:
+            (ExtensionEnabledState state) =>
+                state == ExtensionEnabledState.disabled,
         onPressed: () {
           ga.select(
             gac.DevToolsExtensionEvents.extensionSettingsId.name,
             gac.DevToolsExtensionEvents.extensionDisableManual(extension),
           );
           unawaited(
-            extensionService.setExtensionEnabledState(
-              extension,
-              enable: false,
-            ),
+            extensionService.setExtensionEnabledState(extension, enable: false),
           );
           final router = DevToolsRouterDelegate.of(context);
           if (router.currentConfiguration?.page == extension.screenId) {
diff --git a/packages/devtools_app/lib/src/framework/disconnect_observer.dart b/packages/devtools_app/lib/src/framework/disconnect_observer.dart
index 9b2152e..8982d89 100644
--- a/packages/devtools_app/lib/src/framework/disconnect_observer.dart
+++ b/packages/devtools_app/lib/src/framework/disconnect_observer.dart
@@ -9,13 +9,13 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
+import '../service/connected_app/connection_info.dart';
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
 import '../shared/config_specific/import_export/import_export.dart';
-import '../shared/connection_info.dart';
+import '../shared/framework/routing.dart';
 import '../shared/globals.dart';
-import '../shared/query_parameters.dart';
-import '../shared/routing.dart';
+import '../shared/primitives/query_parameters.dart';
 
 class DisconnectObserver extends StatefulWidget {
   const DisconnectObserver({
@@ -85,10 +85,7 @@
       return;
     }
     WidgetsBinding.instance.scheduleFrameCallback((_) {
-      ga.select(
-        gac.devToolsMain,
-        gac.appDisconnected,
-      );
+      ga.select(gac.devToolsMain, gac.appDisconnected);
       Overlay.of(context).insert(_createDisconnectedOverlay());
     });
   }
@@ -107,8 +104,11 @@
     hideDisconnectedOverlay();
     final args = <String, String?>{
       DevToolsQueryParams.vmServiceUriKey: null,
-      DevToolsQueryParams.offlineScreenIdKey: offlineDataController
-          .offlineDataJson[DevToolsExportKeys.activeScreenId.name] as String,
+      DevToolsQueryParams.offlineScreenIdKey:
+          offlineDataController.offlineDataJson[DevToolsExportKeys
+                  .activeScreenId
+                  .name]
+              as String,
     };
     await widget.routerDelegate.popRoute();
     widget.routerDelegate.navigate(snapshotScreenId, args);
@@ -117,36 +117,37 @@
   OverlayEntry _createDisconnectedOverlay() {
     final theme = Theme.of(context);
     currentDisconnectedOverlay = OverlayEntry(
-      builder: (context) => Material(
-        child: Container(
-          color: theme.colorScheme.surface,
-          child: Center(
-            child: Column(
-              children: [
-                const Spacer(),
-                Text('Disconnected', style: theme.textTheme.headlineMedium),
-                const SizedBox(height: defaultSpacing),
-                if (!isEmbedded())
-                  ConnectToNewAppButton(
-                    routerDelegate: widget.routerDelegate,
-                    onPressed: hideDisconnectedOverlay,
-                    gaScreen: gac.devToolsMain,
-                  )
-                else
-                  const Text('Run a new debug session to reconnect.'),
-                const Spacer(),
-                if (offlineDataController.offlineDataJson.isNotEmpty) ...[
-                  ElevatedButton(
-                    onPressed: _reviewHistory,
-                    child: const Text('Review recent data (offline)'),
-                  ),
-                  const Spacer(),
-                ],
-              ],
+      builder:
+          (context) => Material(
+            child: Container(
+              color: theme.colorScheme.surface,
+              child: Center(
+                child: Column(
+                  children: [
+                    const Spacer(),
+                    Text('Disconnected', style: theme.textTheme.headlineMedium),
+                    const SizedBox(height: defaultSpacing),
+                    if (!isEmbedded())
+                      ConnectToNewAppButton(
+                        routerDelegate: widget.routerDelegate,
+                        onPressed: hideDisconnectedOverlay,
+                        gaScreen: gac.devToolsMain,
+                      )
+                    else
+                      const Text('Run a new debug session to reconnect.'),
+                    const Spacer(),
+                    if (offlineDataController.offlineDataJson.isNotEmpty) ...[
+                      ElevatedButton(
+                        onPressed: _reviewHistory,
+                        child: const Text('Review recent data (offline)'),
+                      ),
+                      const Spacer(),
+                    ],
+                  ],
+                ),
+              ),
             ),
           ),
-        ),
-      ),
     );
     return currentDisconnectedOverlay!;
   }
diff --git a/packages/devtools_app/lib/src/framework/framework_core.dart b/packages/devtools_app/lib/src/framework/framework_core.dart
index c8aa115..0dbb0f0 100644
--- a/packages/devtools_app/lib/src/framework/framework_core.dart
+++ b/packages/devtools_app/lib/src/framework/framework_core.dart
@@ -17,20 +17,20 @@
 import '../screens/debugger/breakpoint_manager.dart';
 import '../service/service_manager.dart';
 import '../service/vm_service_wrapper.dart';
-import '../shared/banner_messages.dart';
 import '../shared/config_specific/framework_initialize/framework_initialize.dart';
 import '../shared/console/eval/eval_service.dart';
-import '../shared/framework_controller.dart';
+import '../shared/framework/app_error_handling.dart' as error_handling;
+import '../shared/framework/framework_controller.dart';
 import '../shared/globals.dart';
-import '../shared/notifications.dart';
-import '../shared/offline_data.dart';
+import '../shared/managers/banner_messages.dart';
+import '../shared/managers/notifications.dart';
+import '../shared/managers/script_manager.dart';
+import '../shared/managers/survey.dart';
+import '../shared/offline/offline_data.dart';
 import '../shared/preferences/preferences.dart';
 import '../shared/primitives/message_bus.dart';
-import '../shared/scripts/script_manager.dart';
 import '../shared/server/server.dart' as server;
-import '../shared/survey.dart';
-import '../shared/utils.dart';
-import 'app_error_handling.dart' as error_handling;
+import '../shared/utils/utils.dart';
 import 'theme_manager.dart';
 
 typedef ErrorReporter = void Function(String title, Object error);
@@ -119,25 +119,25 @@
         final service = await connect<VmServiceWrapper>(
           uri: uri,
           finishedCompleter: finishedCompleter,
-          serviceFactory: ({
-            // ignore: avoid-dynamic, mirrors types of [VmServiceFactory].
-            required Stream<dynamic> /*String|List<int>*/ inStream,
-            required void Function(String message) writeMessage,
-            Log? log,
-            DisposeHandler? disposeHandler,
-            Future? streamClosed,
-            String? wsUri,
-            bool trackFutures = false,
-          }) =>
-              VmServiceWrapper.defaultFactory(
-            inStream: inStream,
-            writeMessage: writeMessage,
-            log: log,
-            disposeHandler: disposeHandler,
-            streamClosed: streamClosed,
-            wsUri: wsUri,
-            trackFutures: integrationTestMode,
-          ),
+          serviceFactory:
+              ({
+                // ignore: avoid-dynamic, mirrors types of [VmServiceFactory].
+                required Stream<dynamic> /*String|List<int>*/ inStream,
+                required void Function(String message) writeMessage,
+                Log? log,
+                DisposeHandler? disposeHandler,
+                Future? streamClosed,
+                String? wsUri,
+                bool trackFutures = false,
+              }) => VmServiceWrapper.defaultFactory(
+                inStream: inStream,
+                writeMessage: writeMessage,
+                log: log,
+                disposeHandler: disposeHandler,
+                streamClosed: streamClosed,
+                wsUri: wsUri,
+                trackFutures: integrationTestMode,
+              ),
         );
 
         await serviceConnection.serviceManager.vmServiceOpened(
@@ -162,10 +162,7 @@
   }
 
   static void _defaultErrorReporter(String title, Object error) {
-    notificationService.pushError(
-      '$title, $error',
-      isReportable: false,
-    );
+    notificationService.pushError('$title, $error', isReportable: false);
   }
 }
 
@@ -191,9 +188,9 @@
       );
 
       if (dtdManager.connection.value != null) {
-        FrameworkCore._themeManager =
-            EditorThemeManager(dtdManager.connection.value!)
-              ..listenForThemeChanges();
+        FrameworkCore._themeManager = EditorThemeManager(
+          dtdManager.connection.value!,
+        )..listenForThemeChanges();
       }
     } else {
       _log.info('No DTD uri provided from the server during initialization.');
diff --git a/packages/devtools_app/lib/src/framework/home_screen.dart b/packages/devtools_app/lib/src/framework/home_screen.dart
index 136a653..3085070 100644
--- a/packages/devtools_app/lib/src/framework/home_screen.dart
+++ b/packages/devtools_app/lib/src/framework/home_screen.dart
@@ -12,25 +12,25 @@
 import 'package:provider/provider.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
+import '../service/connected_app/connection_info.dart';
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
 import '../shared/config_specific/import_export/import_export.dart';
-import '../shared/connection_info.dart';
+import '../shared/framework/routing.dart';
+import '../shared/framework/screen.dart';
 import '../shared/globals.dart';
 import '../shared/primitives/blocking_action_mixin.dart';
 import '../shared/primitives/utils.dart';
-import '../shared/routing.dart';
-import '../shared/screen.dart';
 import '../shared/title.dart';
 import '../shared/ui/vm_flag_widgets.dart';
 import 'framework_core.dart';
 
 class HomeScreen extends Screen {
   HomeScreen({this.sampleData = const []})
-      : super.fromMetaData(
-          ScreenMetaData.home,
-          titleGenerator: () => devToolsTitle.value,
-        );
+    : super.fromMetaData(
+        ScreenMetaData.home,
+        titleGenerator: () => devToolsTitle.value,
+      );
 
   static final id = ScreenMetaData.home.id;
 
@@ -64,7 +64,7 @@
   Widget build(BuildContext context) {
     final connected =
         serviceConnection.serviceManager.connectedState.value.connected &&
-            serviceConnection.serviceManager.connectedAppInitialized;
+        serviceConnection.serviceManager.connectedAppInitialized;
     return Scrollbar(
       child: ListView(
         children: [
@@ -104,8 +104,8 @@
             minScreenWidthForTextBeforeScaling:
                 _primaryMinScreenWidthForTextBeforeScaling,
             routerDelegate: DevToolsRouterDelegate.of(context),
-            onPressed: () =>
-                Navigator.of(context, rootNavigator: true).pop('dialog'),
+            onPressed:
+                () => Navigator.of(context, rootNavigator: true).pop('dialog'),
           ),
         ],
         child: const ConnectedAppSummary(narrowView: false),
@@ -137,12 +137,7 @@
       children: [
         Row(
           children: [
-            Expanded(
-              child: Text(
-                title,
-                style: textTheme.headlineMedium,
-              ),
-            ),
+            Expanded(child: Text(title, style: textTheme.headlineMedium)),
             ...actions,
           ],
         ),
@@ -239,10 +234,7 @@
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          Text(
-            'Connect to a Running App',
-            style: textTheme.titleMedium,
-          ),
+          Text('Connect to a Running App', style: textTheme.titleMedium),
           const SizedBox(height: denseRowSpacing),
           Text(
             'Enter a URL to a running Dart or Flutter application',
@@ -261,10 +253,7 @@
   }
 
   Future<void> _connectHelper() async {
-    ga.select(
-      gac.home,
-      gac.HomeScreenEvents.connectToApp.name,
-    );
+    ga.select(gac.home, gac.HomeScreenEvents.connectToApp.name);
 
     final uri = connectDialogController.text;
     if (uri.isEmpty) {
@@ -286,11 +275,13 @@
     // intuitive that we don't want to just cancel the route change or
     // notification if we are already on a different screen.
     final routerDelegate = DevToolsRouterDelegate.of(context);
-    final connected =
-        await FrameworkCore.initVmService(serviceUriAsString: uri);
+    final connected = await FrameworkCore.initVmService(
+      serviceUriAsString: uri,
+    );
     if (connected) {
-      final connectedUri =
-          Uri.parse(serviceConnection.serviceManager.serviceUri!);
+      final connectedUri = Uri.parse(
+        serviceConnection.serviceManager.serviceUri!,
+      );
       await routerDelegate.updateArgsIfChanged({'uri': '$connectedUri'});
       final shortUri = connectedUri.replace(path: '');
       notificationService.push('Successfully connected to $shortUri.');
@@ -304,10 +295,7 @@
 }
 
 class SampleDataDropDownButton extends StatefulWidget {
-  const SampleDataDropDownButton({
-    super.key,
-    this.sampleData = const [],
-  });
+  const SampleDataDropDownButton({super.key, this.sampleData = const []});
 
   final List<DevToolsJsonFile> sampleData;
 
@@ -325,19 +313,21 @@
       children: [
         RoundedDropDownButton<DevToolsJsonFile>(
           value: value,
-          items: [
-            for (final data in widget.sampleData) _buildMenuItem(data),
-          ],
-          onChanged: (file) => setState(() {
-            value = file;
-          }),
+          items: [for (final data in widget.sampleData) _buildMenuItem(data)],
+          onChanged:
+              (file) => setState(() {
+                value = file;
+              }),
         ),
         const SizedBox(width: defaultSpacing),
         ElevatedButton(
-          onPressed: value == null
-              ? null
-              : () => Provider.of<ImportController>(context, listen: false)
-                  .importData(value!),
+          onPressed:
+              value == null
+                  ? null
+                  : () => Provider.of<ImportController>(
+                    context,
+                    listen: false,
+                  ).importData(value!),
           child: const MaterialIconLabel(
             label: 'Load sample data',
             iconData: Icons.file_upload,
diff --git a/packages/devtools_app/lib/src/framework/initializer.dart b/packages/devtools_app/lib/src/framework/initializer.dart
index fd16af6..21b1f30 100644
--- a/packages/devtools_app/lib/src/framework/initializer.dart
+++ b/packages/devtools_app/lib/src/framework/initializer.dart
@@ -8,11 +8,11 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
+import '../service/connected_app/connection_info.dart';
 import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
-import '../shared/connection_info.dart';
+import '../shared/framework/routing.dart';
 import '../shared/globals.dart';
-import '../shared/routing.dart';
+import '../shared/ui/common_widgets.dart';
 
 // TODO(kenz): see if we can simplify this widget. Several things have changed
 // with DevTools routing and service management code since this widget was
@@ -79,9 +79,10 @@
           children: [
             const Spacer(),
             CenteredMessage(
-              message: _showConnectToNewAppButton
-                  ? 'Cannot connect to VM service.'
-                  : 'Waiting for VM service connection...',
+              message:
+                  _showConnectToNewAppButton
+                      ? 'Cannot connect to VM service.'
+                      : 'Waiting for VM service connection...',
             ),
             if (_showConnectToNewAppButton) ...[
               const SizedBox(height: defaultSpacing),
diff --git a/packages/devtools_app/lib/src/framework/notifications_view.dart b/packages/devtools_app/lib/src/framework/notifications_view.dart
index 43f4f7c..1349b70 100644
--- a/packages/devtools_app/lib/src/framework/notifications_view.dart
+++ b/packages/devtools_app/lib/src/framework/notifications_view.dart
@@ -9,10 +9,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/scheduler.dart';
 
-import '../shared/common_widgets.dart';
 import '../shared/globals.dart';
-import '../shared/notifications.dart';
+import '../shared/managers/notifications.dart';
 import '../shared/primitives/utils.dart';
+import '../shared/ui/common_widgets.dart';
 
 double get _notificationHeight => scaleByFontFactor(175.0);
 final _notificationWidth = _notificationHeight * goldenRatio;
@@ -69,10 +69,7 @@
         Overlay.of(context).insert(_overlayEntry!);
       });
 
-      addAutoDisposeListener(
-        notificationService.newTasks,
-        _processQueues,
-      );
+      addAutoDisposeListener(notificationService.newTasks, _processQueues);
     }
 
     _processQueues();
@@ -99,10 +96,7 @@
   void _push(NotificationMessage message) {
     setState(() {
       _notifications.add(
-        _Notification(
-          message: message,
-          remove: _removeNotification,
-        ),
+        _Notification(message: message, remove: _removeNotification),
       );
       _overlayEntry?.markNeedsBuild();
     });
@@ -145,9 +139,8 @@
 }
 
 class _NotificationOverlay extends StatelessWidget {
-  const _NotificationOverlay({
-    required List<_Notification> notifications,
-  }) : _notifications = notifications;
+  const _NotificationOverlay({required List<_Notification> notifications})
+    : _notifications = notifications;
 
   final List<_Notification> _notifications;
 
@@ -179,10 +172,7 @@
 }
 
 class _Notification extends StatefulWidget {
-  const _Notification({
-    required this.message,
-    required this.remove,
-  });
+  const _Notification({required this.message, required this.remove});
 
   final NotificationMessage message;
   final void Function(_Notification) remove;
@@ -204,10 +194,7 @@
       duration: const Duration(milliseconds: 400),
       vsync: this,
     );
-    curve = CurvedAnimation(
-      parent: controller,
-      curve: Curves.easeInOutCirc,
-    );
+    curve = CurvedAnimation(parent: controller, curve: Curves.easeInOutCirc);
 
     // Set up a timer that reverses the entrance animation, and tells the widget
     // to remove itself when the exit animation is completed.
@@ -239,16 +226,14 @@
     return AnimatedBuilder(
       animation: controller,
       builder: (context, child) {
-        return Opacity(
-          opacity: curve.value,
-          child: child,
-        );
+        return Opacity(opacity: curve.value, child: child);
       },
       child: Card(
         color: theme.colorScheme.secondaryContainer,
         margin: const EdgeInsets.only(bottom: densePadding),
         child: DefaultTextStyle(
-          style: theme.snackBarTheme.contentTextStyle ??
+          style:
+              theme.snackBarTheme.contentTextStyle ??
               theme.textTheme.titleMedium!,
           child: Padding(
             padding: const EdgeInsets.all(denseSpacing),
@@ -257,24 +242,18 @@
               children: [
                 widget.message.isDismissible
                     ? Row(
-                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                        crossAxisAlignment: CrossAxisAlignment.start,
-                        children: [
-                          Flexible(
-                            child: _NotificationMessage(
-                              widget: widget,
-                            ),
-                          ),
-                          _DismissAction(
-                            onPressed: () {
-                              widget.remove(widget);
-                            },
-                          ),
-                        ],
-                      )
-                    : _NotificationMessage(
-                        widget: widget,
-                      ),
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Flexible(child: _NotificationMessage(widget: widget)),
+                        _DismissAction(
+                          onPressed: () {
+                            widget.remove(widget);
+                          },
+                        ),
+                      ],
+                    )
+                    : _NotificationMessage(widget: widget),
                 const SizedBox(height: defaultSpacing),
                 _NotificationActions(actions: widget.message.actions),
               ],
@@ -287,9 +266,7 @@
 }
 
 class _DismissAction extends StatelessWidget {
-  const _DismissAction({
-    required this.onPressed,
-  });
+  const _DismissAction({required this.onPressed});
 
   final void Function() onPressed;
 
@@ -297,28 +274,22 @@
   Widget build(BuildContext context) {
     return Align(
       alignment: Alignment.topRight,
-      child: IconButton(
-        icon: const Icon(
-          Icons.close,
-        ),
-        onPressed: onPressed,
-      ),
+      child: IconButton(icon: const Icon(Icons.close), onPressed: onPressed),
     );
   }
 }
 
 class _NotificationMessage extends StatelessWidget {
-  const _NotificationMessage({
-    required this.widget,
-  });
+  const _NotificationMessage({required this.widget});
 
   final _Notification widget;
 
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
-    final textStyle =
-        theme.regularTextStyleWithColor(theme.colorScheme.onSecondaryContainer);
+    final textStyle = theme.regularTextStyleWithColor(
+      theme.colorScheme.onSecondaryContainer,
+    );
     return Padding(
       padding: const EdgeInsets.only(
         left: denseSpacing,
@@ -327,9 +298,10 @@
       ),
       child: Text(
         widget.message.text,
-        style: widget.message.isError
-            ? textStyle.copyWith(color: theme.colorScheme.error)
-            : textStyle,
+        style:
+            widget.message.isError
+                ? textStyle.copyWith(color: theme.colorScheme.error)
+                : textStyle,
         overflow: TextOverflow.visible,
         maxLines: 10,
       ),
diff --git a/packages/devtools_app/lib/src/framework/release_notes/release_notes.dart b/packages/devtools_app/lib/src/framework/release_notes.dart
similarity index 91%
rename from packages/devtools_app/lib/src/framework/release_notes/release_notes.dart
rename to packages/devtools_app/lib/src/framework/release_notes.dart
index b774034..1261eab 100644
--- a/packages/devtools_app/lib/src/framework/release_notes/release_notes.dart
+++ b/packages/devtools_app/lib/src/framework/release_notes.dart
@@ -12,11 +12,11 @@
 import 'package:http/http.dart' as http;
 import 'package:logging/logging.dart';
 
-import '../../shared/primitives/url_utils.dart';
-import '../../shared/server/server.dart' as server;
-import '../../shared/side_panel.dart';
-import '../../shared/utils.dart';
-import '../../standalone_ui/standalone_screen.dart';
+import '../shared/primitives/url_utils.dart';
+import '../shared/server/server.dart' as server;
+import '../shared/ui/side_panel.dart';
+import '../shared/utils/utils.dart';
+import '../standalone_ui/standalone_screen.dart';
 
 final _log = Logger('release_notes');
 
@@ -39,14 +39,12 @@
 final _flutterDocsSite = Uri.https('docs.flutter.dev');
 
 class ReleaseNotesViewer extends SidePanelViewer {
-  const ReleaseNotesViewer({
-    required super.controller,
-    super.child,
-  }) : super(
-          key: releaseNotesKey,
-          title: 'What\'s new in DevTools?',
-          textIfMarkdownDataEmpty: 'Stay tuned for updates.',
-        );
+  const ReleaseNotesViewer({required super.controller, super.child})
+    : super(
+        key: releaseNotesKey,
+        title: 'What\'s new in DevTools?',
+        textIfMarkdownDataEmpty: 'Stay tuned for updates.',
+      );
 }
 
 class ReleaseNotesController extends SidePanelController {
@@ -121,8 +119,9 @@
     // Release notes will be hosted on the Flutter website with a version number
     // that does not contain any build metadata.
     final parsedDevToolsVersion = SemanticVersion.parse(devToolsVersion);
-    final checkVersion =
-        latestVersionToCheckForReleaseNotes(parsedDevToolsVersion);
+    final checkVersion = latestVersionToCheckForReleaseNotes(
+      parsedDevToolsVersion,
+    );
 
     _log.fine(
       'attempting to fetch and show release notes for DevTools $checkVersion '
@@ -174,8 +173,10 @@
         } catch (_) {
           // This can very infrequently fail due to CDN or caching issues,
           // or if the upstream file has an incorrect link.
-          _log.info('Failed to retrieve release notes for v$releaseToCheck, '
-              'despite indication it is live at $releaseNotePath.');
+          _log.info(
+            'Failed to retrieve release notes for v$releaseToCheck, '
+            'despite indication it is live at $releaseNotePath.',
+          );
           // If we couldn't retrieve this page, keep going to
           // try with earlier patch versions.
           continue;
@@ -190,9 +191,7 @@
         toggleVisibility(true);
         if (server.isDevToolsServerAvailable) {
           // Only set the last release notes version if we are not debugging.
-          unawaited(
-            server.setLastShownReleaseNotesVersion(releaseToCheck),
-          );
+          unawaited(server.setLastShownReleaseNotesVersion(releaseToCheck));
         }
         return;
       }
diff --git a/packages/devtools_app/lib/src/framework/report_feedback_button.dart b/packages/devtools_app/lib/src/framework/report_feedback_button.dart
deleted file mode 100644
index e1e63cd..0000000
--- a/packages/devtools_app/lib/src/framework/report_feedback_button.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/material.dart';
-
-import '../shared/analytics/analytics.dart' as ga;
-import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
-import '../shared/globals.dart';
-import '../shared/utils.dart';
-
-/// Button that, when clicked, will open the DevTools issue tracker in the
-/// browser.
-class ReportFeedbackButton extends ScaffoldAction {
-  ReportFeedbackButton({super.key, super.color})
-      : super(
-          icon: Icons.bug_report_outlined,
-          tooltip: 'Report feedback',
-          onPressed: (_) {
-            ga.select(
-              gac.devToolsMain,
-              gac.feedbackButton,
-            );
-            unawaited(
-              launchUrlWithErrorHandling(
-                devToolsEnvironmentParameters.issueTrackerLink().url,
-              ),
-            );
-          },
-        );
-}
diff --git a/packages/devtools_app/lib/src/framework/about_dialog.dart b/packages/devtools_app/lib/src/framework/scaffold/about_dialog.dart
similarity index 74%
rename from packages/devtools_app/lib/src/framework/about_dialog.dart
rename to packages/devtools_app/lib/src/framework/scaffold/about_dialog.dart
index 815b199..04a8e59 100644
--- a/packages/devtools_app/lib/src/framework/about_dialog.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/about_dialog.dart
@@ -8,11 +8,11 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
-import '../shared/globals.dart';
-import '../shared/utils.dart';
-import 'release_notes/release_notes.dart';
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/globals.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
+import '../release_notes.dart';
 
 class DevToolsAboutDialog extends StatelessWidget {
   const DevToolsAboutDialog(this.releaseNotesController, {super.key});
@@ -33,12 +33,11 @@
               SelectableText('DevTools version $devToolsVersion'),
               const Text(' - '),
               InkWell(
-                child: Text(
-                  'release notes',
-                  style: theme.linkTextStyle,
-                ),
-                onTap: () =>
-                    unawaited(releaseNotesController.openLatestReleaseNotes()),
+                child: Text('release notes', style: theme.linkTextStyle),
+                onTap:
+                    () => unawaited(
+                      releaseNotesController.openLatestReleaseNotes(),
+                    ),
               ),
             ],
           ),
@@ -60,17 +59,11 @@
             ],
           ),
           const Wrap(
-            children: [
-              Text('connect with us on '),
-              _DiscordLink(),
-              Text('.'),
-            ],
+            children: [Text('connect with us on '), _DiscordLink(), Text('.')],
           ),
         ],
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
@@ -135,18 +128,19 @@
 
 class OpenAboutAction extends ScaffoldAction {
   OpenAboutAction({super.key, super.color})
-      : super(
-          icon: Icons.help_outline,
-          tooltip: 'About DevTools',
-          onPressed: (context) {
-            unawaited(
-              showDialog(
-                context: context,
-                builder: (context) => DevToolsAboutDialog(
-                  Provider.of<ReleaseNotesController>(context),
-                ),
-              ),
-            );
-          },
-        );
+    : super(
+        icon: Icons.help_outline,
+        tooltip: 'About DevTools',
+        onPressed: (context) {
+          unawaited(
+            showDialog(
+              context: context,
+              builder:
+                  (context) => DevToolsAboutDialog(
+                    Provider.of<ReleaseNotesController>(context),
+                  ),
+            ),
+          );
+        },
+      );
 }
diff --git a/packages/devtools_app/lib/src/framework/app_bar.dart b/packages/devtools_app/lib/src/framework/scaffold/app_bar.dart
similarity index 96%
rename from packages/devtools_app/lib/src/framework/app_bar.dart
rename to packages/devtools_app/lib/src/framework/scaffold/app_bar.dart
index 64c3a3e..6ff65fa 100644
--- a/packages/devtools_app/lib/src/framework/app_bar.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/app_bar.dart
@@ -8,9 +8,9 @@
 import 'package:flutter/material.dart';
 import 'package:pointer_interceptor/pointer_interceptor.dart';
 
-import '../shared/common_widgets.dart';
-import '../shared/primitives/utils.dart';
-import '../shared/screen.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
 
 class DevToolsAppBar extends StatelessWidget {
   const DevToolsAppBar({
@@ -93,10 +93,7 @@
       flexibleSpace = Align(
         alignment: Alignment.centerLeft,
         child: Padding(
-          padding: EdgeInsets.only(
-            top: densePadding,
-            right: rightPadding,
-          ),
+          padding: EdgeInsets.only(top: densePadding, right: rightPadding),
           child: Row(
             mainAxisSize: MainAxisSize.min,
             children: [
@@ -223,7 +220,7 @@
 @visibleForTesting
 class SelectedTabWrapper extends StatelessWidget {
   SelectedTabWrapper({super.key, required this.child, double? width})
-      : width = width ?? buttonMinWidth;
+    : width = width ?? buttonMinWidth;
 
   final Widget child;
 
diff --git a/packages/devtools_app/lib/src/framework/scaffold/report_feedback_button.dart b/packages/devtools_app/lib/src/framework/scaffold/report_feedback_button.dart
new file mode 100644
index 0000000..a2b2dd6
--- /dev/null
+++ b/packages/devtools_app/lib/src/framework/scaffold/report_feedback_button.dart
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+
+import '../../shared/analytics/analytics.dart' as ga;
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/globals.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
+
+/// Button that, when clicked, will open the DevTools issue tracker in the
+/// browser.
+class ReportFeedbackButton extends ScaffoldAction {
+  ReportFeedbackButton({super.key, super.color})
+    : super(
+        icon: Icons.bug_report_outlined,
+        tooltip: 'Report feedback',
+        onPressed: (_) {
+          ga.select(gac.devToolsMain, gac.feedbackButton);
+          unawaited(
+            launchUrlWithErrorHandling(
+              devToolsEnvironmentParameters.issueTrackerLink().url,
+            ),
+          );
+        },
+      );
+}
diff --git a/packages/devtools_app/lib/src/framework/scaffold.dart b/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
similarity index 73%
rename from packages/devtools_app/lib/src/framework/scaffold.dart
rename to packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
index d8a441b..3ef9d40 100644
--- a/packages/devtools_app/lib/src/framework/scaffold.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/scaffold.dart
@@ -8,21 +8,21 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../app.dart';
-import '../extensions/extension_settings.dart';
-import '../screens/debugger/debugger_screen.dart';
-import '../shared/analytics/prompt.dart';
-import '../shared/banner_messages.dart';
-import '../shared/config_specific/drag_and_drop/drag_and_drop.dart';
-import '../shared/config_specific/import_export/import_export.dart';
-import '../shared/console/widgets/console_pane.dart';
-import '../shared/feature_flags.dart';
-import '../shared/framework_controller.dart';
-import '../shared/globals.dart';
-import '../shared/query_parameters.dart';
-import '../shared/routing.dart';
-import '../shared/screen.dart';
-import '../shared/title.dart';
+import '../../app.dart';
+import '../../extensions/extension_settings.dart';
+import '../../screens/debugger/debugger_screen.dart';
+import '../../shared/analytics/prompt.dart';
+import '../../shared/config_specific/drag_and_drop/drag_and_drop.dart';
+import '../../shared/config_specific/import_export/import_export.dart';
+import '../../shared/console/widgets/console_pane.dart';
+import '../../shared/feature_flags.dart';
+import '../../shared/framework/framework_controller.dart';
+import '../../shared/framework/routing.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/globals.dart';
+import '../../shared/managers/banner_messages.dart';
+import '../../shared/primitives/query_parameters.dart';
+import '../../shared/title.dart';
 import 'about_dialog.dart';
 import 'app_bar.dart';
 import 'report_feedback_button.dart';
@@ -50,33 +50,33 @@
     EmbedMode embedMode = EmbedMode.none,
     List<Widget>? actions,
   }) : this(
-          key: key,
-          screens: [SimpleScreen(child)],
-          actions: actions,
-          embedMode: embedMode,
-        );
+         key: key,
+         screens: [SimpleScreen(child)],
+         actions: actions,
+         embedMode: embedMode,
+       );
 
   static List<Widget> defaultActions({Color? color}) => [
-        OpenSettingsAction(color: color),
-        if (FeatureFlags.devToolsExtensions &&
-            !DevToolsQueryParams.load().hideExtensions)
-          ExtensionSettingsAction(color: color),
-        ReportFeedbackButton(color: color),
-        OpenAboutAction(color: color),
-      ];
+    OpenSettingsAction(color: color),
+    if (FeatureFlags.devToolsExtensions &&
+        !DevToolsQueryParams.load().hideExtensions)
+      ExtensionSettingsAction(color: color),
+    ReportFeedbackButton(color: color),
+    OpenAboutAction(color: color),
+  ];
 
   /// The padding around the content in the DevTools UI.
   EdgeInsets get appPadding => EdgeInsets.fromLTRB(
-        horizontalPadding.left,
-        isEmbedded() ? 2.0 : intermediateSpacing,
-        horizontalPadding.right,
-        isEmbedded() ? 2.0 : intermediateSpacing,
-      );
+    horizontalPadding.left,
+    isEmbedded() ? 2.0 : intermediateSpacing,
+    horizontalPadding.right,
+    isEmbedded() ? 2.0 : intermediateSpacing,
+  );
 
   /// Horizontal padding around the content in the DevTools UI.
   static EdgeInsets get horizontalPadding => EdgeInsets.symmetric(
-        horizontal: isEmbedded() ? densePadding : largeSpacing,
-      );
+    horizontal: isEmbedded() ? densePadding : largeSpacing,
+  );
 
   /// All of the [Screen]s that it's possible to navigate to from this Scaffold.
   final List<Screen> screens;
@@ -138,17 +138,19 @@
       // Stay on the current tab if possible when the collection of tabs changes.
       if (_tabController != null &&
           widget.screens.contains(oldWidget.screens[_tabController!.index])) {
-        newIndex =
-            widget.screens.indexOf(oldWidget.screens[_tabController!.index]);
+        newIndex = widget.screens.indexOf(
+          oldWidget.screens[_tabController!.index],
+        );
       }
       // Create a new tab controller to reflect the changed tabs.
       _setupTabController(startingIndex: newIndex);
     } else if (widget.screens[_tabController!.index].screenId != widget.page) {
       // If the page changed (eg. the route was modified by pressing back in the
       // browser), animate to the new one.
-      var newIndex = widget.page == null
-          ? 0 // When there's no supplied page, we show the first one.
-          : widget.screens.indexWhere((t) => t.screenId == widget.page);
+      var newIndex =
+          widget.page == null
+              ? 0 // When there's no supplied page, we show the first one.
+              : widget.screens.indexWhere((t) => t.screenId == widget.page);
       // Ensure the returned index is in range, otherwise set to 0.
       if (newIndex == -1) {
         newIndex = 0;
@@ -182,8 +184,9 @@
     );
 
     if (widget.page != null) {
-      final initialIndex =
-          widget.screens.indexWhere((screen) => screen.screenId == widget.page);
+      final initialIndex = widget.screens.indexWhere(
+        (screen) => screen.screenId == widget.page,
+      );
       if (initialIndex != -1) {
         _tabController!.index = initialIndex;
       }
@@ -243,8 +246,9 @@
   void _showPageById(String pageId) {
     final existingTabIndex = _tabController!.index;
 
-    final newIndex =
-        widget.screens.indexWhere((screen) => screen.screenId == pageId);
+    final newIndex = widget.screens.indexWhere(
+      (screen) => screen.screenId == pageId,
+    );
 
     if (newIndex != -1 && newIndex != existingTabIndex) {
       DevToolsRouterDelegate.of(context).navigateIfNotCurrent(pageId);
@@ -278,11 +282,7 @@
         Align(
           alignment: Alignment.topLeft,
           child: FocusScope(
-            child: AnalyticsPrompt(
-              child: BannerMessages(
-                screen: screen,
-              ),
-            ),
+            child: AnalyticsPrompt(child: BannerMessages(screen: screen)),
           ),
         ),
     ];
@@ -296,7 +296,9 @@
         ),
         if (serviceConnection.serviceManager.connectedAppInitialized &&
             !serviceConnection
-                .serviceManager.connectedApp!.isProfileBuildNow! &&
+                .serviceManager
+                .connectedApp!
+                .isProfileBuildNow! &&
             !offlineDataController.showingOfflineData.value &&
             _currentScreen.showFloatingDebuggerControls)
           Container(
@@ -311,65 +313,68 @@
       builder: (context, _) {
         final showConsole =
             serviceConnection.serviceManager.connectedAppInitialized &&
-                !offlineDataController.showingOfflineData.value &&
-                _currentScreen.showConsole(widget.embedMode);
+            !offlineDataController.showingOfflineData.value &&
+            _currentScreen.showConsole(widget.embedMode);
         final containsSingleSimpleScreen =
             widget.screens.length == 1 && widget.screens.first is SimpleScreen;
-        final showAppBar = widget.embedMode == EmbedMode.none ||
+        final showAppBar =
+            widget.embedMode == EmbedMode.none ||
             (widget.embedMode == EmbedMode.embedMany &&
                 !containsSingleSimpleScreen);
         return DragAndDrop(
           handleDrop: _importController.importData,
           child: KeyboardShortcuts(
-            keyboardShortcuts: _currentScreen.buildKeyboardShortcuts(
-              context,
-            ),
+            keyboardShortcuts: _currentScreen.buildKeyboardShortcuts(context),
             child: Scaffold(
-              appBar: showAppBar
-                  ? PreferredSize(
-                      preferredSize: Size.fromHeight(defaultToolbarHeight),
-                      // Place the AppBar inside of a Hero widget to keep it the same across
-                      // route transitions.
-                      child: Hero(
-                        tag: _appBarTag,
-                        child: DevToolsAppBar(
-                          tabController: _tabController,
-                          screens: widget.screens,
-                          actions: widget.actions,
+              appBar:
+                  showAppBar
+                      ? PreferredSize(
+                        preferredSize: Size.fromHeight(defaultToolbarHeight),
+                        // Place the AppBar inside of a Hero widget to keep it the same across
+                        // route transitions.
+                        child: Hero(
+                          tag: _appBarTag,
+                          child: DevToolsAppBar(
+                            tabController: _tabController,
+                            screens: widget.screens,
+                            actions: widget.actions,
+                          ),
                         ),
-                      ),
-                    )
-                  : null,
+                      )
+                      : null,
               body: OutlineDecoration.onlyTop(
                 child: Padding(
                   padding: widget.appPadding,
-                  child: showConsole
-                      ? SplitPane(
-                          axis: Axis.vertical,
-                          splitters: [
-                            ConsolePaneHeader(),
-                          ],
-                          initialFractions: const [0.8, 0.2],
-                          children: [
-                            Padding(
-                              padding: const EdgeInsets.only(
-                                bottom: intermediateSpacing,
+                  child:
+                      showConsole
+                          ? SplitPane(
+                            axis: Axis.vertical,
+                            splitters: [ConsolePaneHeader()],
+                            initialFractions: const [0.8, 0.2],
+                            children: [
+                              Padding(
+                                padding: const EdgeInsets.only(
+                                  bottom: intermediateSpacing,
+                                ),
+                                child: content,
                               ),
-                              child: content,
-                            ),
-                            RoundedOutlinedBorder.onlyBottom(
-                              child: const ConsolePane(),
-                            ),
-                          ],
-                        )
-                      : content,
+                              RoundedOutlinedBorder.onlyBottom(
+                                child: const ConsolePane(),
+                              ),
+                            ],
+                          )
+                          : content,
                 ),
               ),
               bottomNavigationBar: StatusLine(
                 currentScreen: _currentScreen,
                 isEmbedded: widget.embedMode.embedded,
-                isConnected: serviceConnection
-                        .serviceManager.connectedState.value.connected &&
+                isConnected:
+                    serviceConnection
+                        .serviceManager
+                        .connectedState
+                        .value
+                        .connected &&
                     serviceConnection.serviceManager.connectedAppInitialized,
               ),
             ),
@@ -426,11 +431,7 @@
 }
 
 class SimpleScreen extends Screen {
-  SimpleScreen(this.child)
-      : super(
-          id,
-          showFloatingDebuggerControls: false,
-        );
+  SimpleScreen(this.child) : super(id, showFloatingDebuggerControls: false);
 
   static final id = ScreenMetaData.simple.id;
 
diff --git a/packages/devtools_app/lib/src/framework/settings_dialog.dart b/packages/devtools_app/lib/src/framework/scaffold/settings_dialog.dart
similarity index 80%
rename from packages/devtools_app/lib/src/framework/settings_dialog.dart
rename to packages/devtools_app/lib/src/framework/scaffold/settings_dialog.dart
index becc698..7d0a0d7 100644
--- a/packages/devtools_app/lib/src/framework/settings_dialog.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/settings_dialog.dart
@@ -8,29 +8,29 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../shared/analytics/analytics_controller.dart';
-import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
-import '../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart';
-import '../shared/feature_flags.dart';
-import '../shared/globals.dart';
-import '../shared/log_storage.dart';
-import '../shared/server/server.dart';
+import '../../shared/analytics/analytics_controller.dart';
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart';
+import '../../shared/feature_flags.dart';
+import '../../shared/globals.dart';
+import '../../shared/log_storage.dart';
+import '../../shared/server/server.dart';
+import '../../shared/ui/common_widgets.dart';
 
 class OpenSettingsAction extends ScaffoldAction {
   OpenSettingsAction({super.key, super.color})
-      : super(
-          icon: Icons.settings_outlined,
-          tooltip: 'Settings',
-          onPressed: (context) {
-            unawaited(
-              showDialog(
-                context: context,
-                builder: (context) => const SettingsDialog(),
-              ),
-            );
-          },
-        );
+    : super(
+        icon: Icons.settings_outlined,
+        tooltip: 'Settings',
+        onPressed: (context) {
+          unawaited(
+            showDialog(
+              context: context,
+              builder: (context) => const SettingsDialog(),
+            ),
+          );
+        },
+      );
 }
 
 class SettingsDialog extends StatelessWidget {
@@ -61,9 +61,10 @@
               child: CheckboxSetting(
                 title: 'Enable analytics',
                 notifier: analyticsController.analyticsEnabled,
-                onChanged: (enable) => unawaited(
-                  analyticsController.toggleAnalyticsEnabled(enable),
-                ),
+                onChanged:
+                    (enable) => unawaited(
+                      analyticsController.toggleAnalyticsEnabled(enable),
+                    ),
                 gaScreen: gac.settingsDialog,
                 gaItem: gac.analytics,
               ),
@@ -98,8 +99,9 @@
             if (FeatureFlags.inspectorV2)
               Flexible(
                 child: CheckboxSetting(
-                  notifier: preferences.inspector.inspectorV2Enabled
-                      as ValueNotifier<bool?>,
+                  notifier:
+                      preferences.inspector.inspectorV2Enabled
+                          as ValueNotifier<bool?>,
                   title: 'Enable the new Inspector',
                   description: 'Try out the redesigned Flutter Inspector.',
                   gaItem: gac.inspectorV2Enabled,
@@ -111,9 +113,7 @@
           const _VerboseLoggingSetting(),
         ],
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
@@ -146,10 +146,11 @@
               gaSelection: gac.copyLogs,
               minScreenWidthForTextBeforeScaling:
                   _minScreenWidthForTextBeforeScaling,
-              onPressed: () async => await copyToClipboard(
-                LogStorage.root.toString(),
-                successMessage: 'Successfully copied logs',
-              ),
+              onPressed:
+                  () async => await copyToClipboard(
+                    LogStorage.root.toString(),
+                    successMessage: 'Successfully copied logs',
+                  ),
             ),
             const SizedBox(width: denseSpacing),
             ClearButton(
diff --git a/packages/devtools_app/lib/src/framework/status_line.dart b/packages/devtools_app/lib/src/framework/scaffold/status_line.dart
similarity index 82%
rename from packages/devtools_app/lib/src/framework/status_line.dart
rename to packages/devtools_app/lib/src/framework/scaffold/status_line.dart
index 298d921..8143a92 100644
--- a/packages/devtools_app/lib/src/framework/status_line.dart
+++ b/packages/devtools_app/lib/src/framework/scaffold/status_line.dart
@@ -7,13 +7,13 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
-import '../shared/globals.dart';
-import '../shared/primitives/utils.dart';
-import '../shared/screen.dart';
-import '../shared/ui/utils.dart';
-import '../shared/utils.dart';
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/framework/screen.dart';
+import '../../shared/globals.dart';
+import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'scaffold.dart';
 
 /// The status line widget displayed at the bottom of DevTools.
@@ -38,9 +38,9 @@
 
   /// The padding around the footer in the DevTools UI.
   EdgeInsets get padding => const EdgeInsets.symmetric(
-        horizontal: defaultSpacing,
-        vertical: densePadding,
-      );
+    horizontal: defaultSpacing,
+    vertical: densePadding,
+  );
 
   @override
   Widget build(BuildContext context) {
@@ -136,9 +136,10 @@
             description = vm.deviceDisplay;
           }
 
-          final color = highlightForConnection
-              ? theme.colorScheme.onPrimary
-              : theme.regularTextStyle.color;
+          final color =
+              highlightForConnection
+                  ? theme.colorScheme.onPrimary
+                  : theme.regularTextStyle.color;
 
           return Row(
             mainAxisAlignment: MainAxisAlignment.end,
@@ -149,11 +150,12 @@
                   return SizedBox(
                     width: smallProgressSize,
                     height: smallProgressSize,
-                    child: isBusy
-                        ? SmallCircularProgressIndicator(
-                            valueColor: AlwaysStoppedAnimation<Color?>(color),
-                          )
-                        : const SizedBox(),
+                    child:
+                        isBusy
+                            ? SmallCircularProgressIndicator(
+                              valueColor: AlwaysStoppedAnimation<Color?>(color),
+                            )
+                            : const SizedBox(),
                   );
                 },
               ),
@@ -162,10 +164,12 @@
                 message: 'Connected device',
                 child: Text(
                   description,
-                  style: highlightForConnection
-                      ? theme.regularTextStyle
-                          .copyWith(color: theme.colorScheme.onPrimary)
-                      : theme.regularTextStyle,
+                  style:
+                      highlightForConnection
+                          ? theme.regularTextStyle.copyWith(
+                            color: theme.colorScheme.onPrimary,
+                          )
+                          : theme.regularTextStyle,
                   overflow: TextOverflow.clip,
                 ),
               ),
@@ -175,18 +179,13 @@
           return child!;
         }
       },
-      child: screenWidth <= MediaSize.xxs
-          ? DevToolsTooltip(
-              message: noConnectionMsg,
-              child: Icon(
-                Icons.warning_amber_rounded,
-                size: actionsIconSize,
-              ),
-            )
-          : Text(
-              noConnectionMsg,
-              style: theme.regularTextStyle,
-            ),
+      child:
+          screenWidth <= MediaSize.xxs
+              ? DevToolsTooltip(
+                message: noConnectionMsg,
+                child: Icon(Icons.warning_amber_rounded, size: actionsIconSize),
+              )
+              : Text(noConnectionMsg, style: theme.regularTextStyle),
     );
   }
 }
@@ -216,7 +215,8 @@
       icon: Icons.library_books_outlined,
       link: GaLink(
         display: screenWidth <= MediaSize.xs ? 'Docs' : 'Read docs',
-        url: screen.docsUrl ??
+        url:
+            screen.docsUrl ??
             'https://docs.flutter.dev/tools/devtools/$docPageId',
         gaScreenName: screen.screenId,
         gaSelectedItemDescription: gac.documentationLink,
@@ -270,10 +270,7 @@
   Widget build(BuildContext context) {
     final isolateManager = serviceConnection.serviceManager.isolateManager;
     return MultiValueListenableBuilder(
-      listenables: [
-        isolateManager.isolates,
-        isolateManager.selectedIsolate,
-      ],
+      listenables: [isolateManager.isolates, isolateManager.selectedIsolate],
       builder: (context, values, _) {
         final theme = Theme.of(context);
         final isolates = values.first as List<IsolateRef>;
@@ -282,17 +279,17 @@
           tooltip: 'Selected Isolate',
           initialValue: selectedIsolateRef,
           onSelected: isolateManager.selectIsolate,
-          itemBuilder: (BuildContext context) => isolates.map(
-            (ref) {
-              return PopupMenuItem<IsolateRef>(
-                value: ref,
-                child: IsolateOption(
-                  ref,
-                  color: theme.colorScheme.onSurface,
-                ),
-              );
-            },
-          ).toList(),
+          itemBuilder:
+              (BuildContext context) =>
+                  isolates.map((ref) {
+                    return PopupMenuItem<IsolateRef>(
+                      value: ref,
+                      child: IsolateOption(
+                        ref,
+                        color: theme.colorScheme.onSurface,
+                      ),
+                    );
+                  }).toList(),
           child: IsolateOption(
             isolateManager.selectedIsolate.value,
             color: theme.colorScheme.onPrimary,
@@ -304,11 +301,7 @@
 }
 
 class IsolateOption extends StatelessWidget {
-  const IsolateOption(
-    this.ref, {
-    required this.color,
-    super.key,
-  });
+  const IsolateOption(this.ref, {required this.color, super.key});
 
   final IsolateRef? ref;
 
diff --git a/packages/devtools_app/lib/src/framework/theme_manager.dart b/packages/devtools_app/lib/src/framework/theme_manager.dart
index 4e582ef..54951f0 100644
--- a/packages/devtools_app/lib/src/framework/theme_manager.dart
+++ b/packages/devtools_app/lib/src/framework/theme_manager.dart
@@ -44,11 +44,10 @@
     // Handle themeChanged from postMessage.
     autoDisposeStreamSubscription(
       onPostMessage.listen((event) {
-        if (event.data
-            case {
-              'method': 'editor.themeChanged',
-              'params': {'theme': final Map themeJson}
-            }) {
+        if (event.data case {
+          'method': 'editor.themeChanged',
+          'params': {'theme': final Map themeJson},
+        }) {
           try {
             _updateTheme(
               EditorTheme.fromJson(themeJson.cast<String, Object?>()),
@@ -73,8 +72,10 @@
     }
 
     if (newTheme.backgroundColor != null) {
-      final newBackgroundColor =
-          tryParseColor(newTheme.backgroundColor!, logger: _log);
+      final newBackgroundColor = tryParseColor(
+        newTheme.backgroundColor!,
+        logger: _log,
+      );
       if (newBackgroundColor != null &&
           newBackgroundColor != currentTheme.backgroundColor) {
         updateQueryParameter(
@@ -85,8 +86,10 @@
     }
 
     if (newTheme.foregroundColor != null) {
-      final newForegroundColor =
-          tryParseColor(newTheme.foregroundColor!, logger: _log);
+      final newForegroundColor = tryParseColor(
+        newTheme.foregroundColor!,
+        logger: _log,
+      );
       if (newForegroundColor != null &&
           newForegroundColor != currentTheme.foregroundColor) {
         updateQueryParameter(
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
index 237fdd1..36be994 100644
--- a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
@@ -101,8 +101,9 @@
   /// The node set as the analysis tab root.
   ///
   /// Used to build the treemap and the tree table for the analysis tab.
-  final analysisRoot =
-      ValueNotifier<Selection<TreemapNode?>>(Selection.empty());
+  final analysisRoot = ValueNotifier<Selection<TreemapNode?>>(
+    Selection.empty(),
+  );
 
   ValueListenable<bool> get isDeferredApp => _isDeferredApp;
   final _isDeferredApp = ValueNotifier<bool>(false);
@@ -121,13 +122,14 @@
 
     final programInfoNode =
         _analysisCallGraph?.program.lookup(newAnalysisRoot.packagePath()) ??
-            _analysisCallGraph?.program.root;
+        _analysisCallGraph?.program.root;
 
     // If [programInfoNode is null, we don't have any call graph information
     // about [newRoot].
     if (programInfoNode != null) {
-      _analysisCallGraphRoot.value =
-          _analysisCallGraph!.lookup(programInfoNode);
+      _analysisCallGraphRoot.value = _analysisCallGraph!.lookup(
+        programInfoNode,
+      );
     }
   }
 
@@ -169,8 +171,9 @@
     } else if (oldProgramInfoNode != null) {
       _diffCallGraphRoot.value = _oldDiffCallGraph!.lookup(oldProgramInfoNode);
     } else if (newProgramInfoNodeRoot != null) {
-      _diffCallGraphRoot.value =
-          _newDiffCallGraph!.lookup(newProgramInfoNodeRoot);
+      _diffCallGraphRoot.value = _newDiffCallGraph!.lookup(
+        newProgramInfoNodeRoot,
+      );
     }
   }
 
@@ -272,8 +275,9 @@
     return _activeDiffTreeType;
   }
 
-  final _activeDiffTreeType =
-      ValueNotifier<DiffTreeType>(DiffTreeType.combined);
+  final _activeDiffTreeType = ValueNotifier<DiffTreeType>(
+    DiffTreeType.combined,
+  );
 
   void changeActiveDiffTreeType(DiffTreeType newDiffTreeType) {
     _activeDiffTreeType.value = newDiffTreeType;
@@ -374,13 +378,12 @@
     return jsonFile;
   }
 
-  Map<String, dynamic> _extractDeferredUnits(
-    Map<String, dynamic> jsonFile,
-  ) {
+  Map<String, dynamic> _extractDeferredUnits(Map<String, dynamic> jsonFile) {
     if (_hasDeferredInfo(jsonFile)) {
-      jsonFile['children'] = _extractChildren(jsonFile)
-          .where((child) => child['isDeferred'] == true)
-          .toList();
+      jsonFile['children'] =
+          _extractChildren(
+            jsonFile,
+          ).where((child) => child['isDeferred'] == true).toList();
       jsonFile['n'] = _deferredNodeName;
     }
     return jsonFile;
@@ -445,15 +448,19 @@
           newEntireAppFileJson = _wrapInArtificialRoot(newFileJson);
         }
 
-        final oldMainOnlyFileJson =
-            _extractMainUnit(Map.from(oldEntireAppFileJson));
-        final newMainOnlyFileJson =
-            _extractMainUnit(Map.from(newEntireAppFileJson));
+        final oldMainOnlyFileJson = _extractMainUnit(
+          Map.from(oldEntireAppFileJson),
+        );
+        final newMainOnlyFileJson = _extractMainUnit(
+          Map.from(newEntireAppFileJson),
+        );
 
-        final oldDeferredOnlyFileJson =
-            _extractDeferredUnits(Map.from(oldEntireAppFileJson));
-        final newDeferredOnlyFileJson =
-            _extractDeferredUnits(Map.from(newEntireAppFileJson));
+        final oldDeferredOnlyFileJson = _extractDeferredUnits(
+          Map.from(oldEntireAppFileJson),
+        );
+        final newDeferredOnlyFileJson = _extractDeferredUnits(
+          Map.from(newEntireAppFileJson),
+        );
 
         diffMap = _generateDiffMapFromAnalyzeSizeFiles(
           oldFileJson: oldEntireAppFileJson,
@@ -683,9 +690,10 @@
 
     // Given a child, build its subtree.
     for (final child in rawChildren) {
-      final childTreemapNode = showDiff
-          ? generateDiffTree(child, diffTreeType!)
-          : generateTree(child);
+      final childTreemapNode =
+          showDiff
+              ? generateDiffTree(child, diffTreeType!)
+              : generateTree(child);
       if (childTreemapNode == null) {
         continue;
       }
@@ -697,11 +705,11 @@
     return totalByteSize == 0 && skipNodesWithNoByteSizeChange
         ? null
         : _buildNode(
-            treeJson,
-            totalByteSize,
-            children: treemapNodeChildren,
-            showDiff: showDiff,
-          );
+          treeJson,
+          totalByteSize,
+          children: treemapNodeChildren,
+          showDiff: showDiff,
+        );
   }
 
   TreemapNode _buildNode(
@@ -748,8 +756,9 @@
     final data = this.data;
     if (data is Map<String, Object?>) {
       final type = data['type'] as String?;
-      return AppSizeJsonFileExtension._supportedAnalyzeSizePlatforms
-          .contains(type);
+      return AppSizeJsonFileExtension._supportedAnalyzeSizePlatforms.contains(
+        type,
+      );
     }
     return false;
   }
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
index 037ae70..f76213b 100644
--- a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
@@ -12,16 +12,16 @@
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
 import '../../shared/charts/treemap.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/config_specific/drag_and_drop/drag_and_drop.dart';
-import '../../shared/file_import.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
+import '../../shared/primitives/query_parameters.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/query_parameters.dart';
-import '../../shared/screen.dart';
 import '../../shared/server/server.dart' as server;
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/file_import.dart';
 import '../../shared/ui/tab.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'app_size_controller.dart';
 import 'app_size_table.dart';
 import 'code_size_attribution.dart';
@@ -203,14 +203,12 @@
                     AppUnitDropdown(
                       value: controller.selectedAppUnit.value,
                       onChanged: (newAppUnit) {
-                        setState(
-                          () {
-                            controller.changeSelectedAppUnit(
-                              newAppUnit!,
-                              currentTab.key!,
-                            );
-                          },
-                        );
+                        setState(() {
+                          controller.changeSelectedAppUnit(
+                            newAppUnit!,
+                            currentTab.key!,
+                          );
+                        });
                       },
                     ),
                   if (currentTab.key == AppSizeScreen.diffTabKey) ...[
@@ -226,9 +224,7 @@
                   ClearButton(
                     gaScreen: gac.appSize,
                     gaSelection: gac.clear,
-                    onPressed: () => controller.clear(
-                      currentTab.key!,
-                    ),
+                    onPressed: () => controller.clear(currentTab.key!),
                   ),
                 ],
               ),
@@ -236,10 +232,7 @@
                 child: TabBarView(
                   physics: defaultTabBarViewPhysics,
                   controller: _tabController,
-                  children: const [
-                    AnalysisView(),
-                    DiffView(),
-                  ],
+                  children: const [AnalysisView(), DiffView()],
                 ),
               ),
             ],
@@ -327,7 +320,8 @@
 
   // TODO(kenz): add links to documentation on how to generate these files, and
   // mention the import file button once it is hooked up to a file picker.
-  static const importInstructions = 'Drag and drop an AOT snapshot or'
+  static const importInstructions =
+      'Drag and drop an AOT snapshot or'
       ' size analysis file for debugging';
 
   @override
@@ -362,19 +356,20 @@
     return Column(
       children: [
         Expanded(
-          child: analysisRootLocal == null
-              ? _buildImportFileView()
-              : _AppSizeView(
-                  title: _generateSingleFileHeaderText(),
-                  treemapKey: AppSizeScreen.analysisViewTreemapKey,
-                  treemapRoot: analysisRootLocal,
-                  onRootChangedCallback: controller.changeAnalysisRoot,
-                  analysisTable: AppSizeAnalysisTable(
-                    rootNode: analysisRootLocal.root,
-                    controller: controller,
+          child:
+              analysisRootLocal == null
+                  ? _buildImportFileView()
+                  : _AppSizeView(
+                    title: _generateSingleFileHeaderText(),
+                    treemapKey: AppSizeScreen.analysisViewTreemapKey,
+                    treemapRoot: analysisRootLocal,
+                    onRootChangedCallback: controller.changeAnalysisRoot,
+                    analysisTable: AppSizeAnalysisTable(
+                      rootNode: analysisRootLocal.root,
+                      controller: controller,
+                    ),
+                    callGraphRoot: controller.analysisCallGraphRoot.value,
                   ),
-                  callGraphRoot: controller.analysisCallGraphRoot.value,
-                ),
         ),
       ],
     );
@@ -382,9 +377,10 @@
 
   String _generateSingleFileHeaderText() {
     final analysisFile = controller.analysisJsonFile.value!;
-    String output = analysisFile.isAnalyzeSizeFile
-        ? 'Total size analysis: '
-        : 'Dart AOT snapshot: ';
+    String output =
+        analysisFile.isAnalyzeSizeFile
+            ? 'Total size analysis: '
+            : 'Dart AOT snapshot: ';
     output += analysisFile.displayText;
     return output;
   }
@@ -396,26 +392,26 @@
         return processing
             ? const CenteredMessage(message: AppSizeScreen.loadingMessage)
             : Column(
-                children: [
-                  Flexible(
-                    child: FileImportContainer(
-                      instructions: AnalysisView.importInstructions,
-                      actionText: 'Analyze Size',
-                      gaScreen: gac.appSize,
-                      gaSelectionImport: gac.importFileSingle,
-                      gaSelectionAction: gac.analyzeSingle,
-                      onAction: (jsonFile) {
-                        controller.loadTreeFromJsonFile(
-                          jsonFile: jsonFile,
-                          onError: (error) {
-                            if (mounted) notificationService.push(error);
-                          },
-                        );
-                      },
-                    ),
+              children: [
+                Flexible(
+                  child: FileImportContainer(
+                    instructions: AnalysisView.importInstructions,
+                    actionText: 'Analyze Size',
+                    gaScreen: gac.appSize,
+                    gaSelectionImport: gac.importFileSingle,
+                    gaSelectionAction: gac.analyzeSingle,
+                    onAction: (jsonFile) {
+                      controller.loadTreeFromJsonFile(
+                        jsonFile: jsonFile,
+                        onError: (error) {
+                          if (mounted) notificationService.push(error);
+                        },
+                      );
+                    },
                   ),
-                ],
-              );
+                ),
+              ],
+            );
       },
     );
   }
@@ -426,9 +422,11 @@
 
   // TODO(kenz): add links to documentation on how to generate these files, and
   // mention the import file button once it is hooked up to a file picker.
-  static const importOldInstructions = 'Drag and drop an original (old) AOT '
+  static const importOldInstructions =
+      'Drag and drop an original (old) AOT '
       'snapshot or size analysis file for debugging';
-  static const importNewInstructions = 'Drag and drop a modified (new) AOT '
+  static const importNewInstructions =
+      'Drag and drop a modified (new) AOT '
       'snapshot or size analysis file for debugging';
 
   @override
@@ -465,16 +463,17 @@
     return Column(
       children: [
         Expanded(
-          child: diffRootLocal == null
-              ? _buildImportDiffView()
-              : _AppSizeView(
-                  title: _generateDualFileHeaderText(),
-                  treemapKey: AppSizeScreen.diffViewTreemapKey,
-                  treemapRoot: diffRootLocal,
-                  onRootChangedCallback: controller.changeDiffRoot,
-                  analysisTable: AppSizeDiffTable(rootNode: diffRootLocal),
-                  callGraphRoot: controller.diffCallGraphRoot.value,
-                ),
+          child:
+              diffRootLocal == null
+                  ? _buildImportDiffView()
+                  : _AppSizeView(
+                    title: _generateDualFileHeaderText(),
+                    treemapKey: AppSizeScreen.diffViewTreemapKey,
+                    treemapRoot: diffRootLocal,
+                    onRootChangedCallback: controller.changeDiffRoot,
+                    analysisTable: AppSizeDiffTable(rootNode: diffRootLocal),
+                    callGraphRoot: controller.diffCallGraphRoot.value,
+                  ),
         ),
       ],
     );
@@ -484,9 +483,10 @@
     final oldFile = controller.oldDiffJsonFile.value!;
     final newFile = controller.newDiffJsonFile.value!;
     String output = 'Diffing ';
-    output += oldFile.isAnalyzeSizeFile
-        ? 'total size analyses: '
-        : 'Dart AOT snapshots: ';
+    output +=
+        oldFile.isAnalyzeSizeFile
+            ? 'total size analyses: '
+            : 'Dart AOT snapshots: ';
     output += oldFile.displayText;
     output += ' (OLD)    vs    (NEW) ';
     output += newFile.displayText;
@@ -500,30 +500,31 @@
         return processing
             ? const CenteredMessage(message: AppSizeScreen.loadingMessage)
             : Column(
-                crossAxisAlignment: CrossAxisAlignment.stretch,
-                children: [
-                  Expanded(
-                    child: DualFileImportContainer(
-                      firstFileTitle: 'Old',
-                      secondFileTitle: 'New',
-                      // TODO(kenz): perhaps bold "original" and "modified".
-                      firstInstructions: DiffView.importOldInstructions,
-                      secondInstructions: DiffView.importNewInstructions,
-                      actionText: 'Analyze Diff',
-                      gaScreen: gac.appSize,
-                      gaSelectionImportFirst: gac.importFileDiffFirst,
-                      gaSelectionImportSecond: gac.importFileDiffSecond,
-                      gaSelectionAction: gac.analyzeDiff,
-                      onAction: (oldFile, newFile, onError) =>
-                          controller.loadDiffTreeFromJsonFiles(
-                        oldFile: oldFile,
-                        newFile: newFile,
-                        onError: onError,
-                      ),
-                    ),
+              crossAxisAlignment: CrossAxisAlignment.stretch,
+              children: [
+                Expanded(
+                  child: DualFileImportContainer(
+                    firstFileTitle: 'Old',
+                    secondFileTitle: 'New',
+                    // TODO(kenz): perhaps bold "original" and "modified".
+                    firstInstructions: DiffView.importOldInstructions,
+                    secondInstructions: DiffView.importNewInstructions,
+                    actionText: 'Analyze Diff',
+                    gaScreen: gac.appSize,
+                    gaSelectionImportFirst: gac.importFileDiffFirst,
+                    gaSelectionImportSecond: gac.importFileDiffSecond,
+                    gaSelectionAction: gac.analyzeDiff,
+                    onAction:
+                        (oldFile, newFile, onError) =>
+                            controller.loadDiffTreeFromJsonFiles(
+                              oldFile: oldFile,
+                              newFile: newFile,
+                              onError: onError,
+                            ),
                   ),
-                ],
-              );
+                ),
+              ],
+            );
       },
     );
   }
@@ -589,9 +590,7 @@
                   OutlineDecoration.onlyTop(
                     child: Row(
                       children: [
-                        Flexible(
-                          child: analysisTable,
-                        ),
+                        Flexible(child: analysisTable),
                         if (callGraphRoot != null)
                           Flexible(
                             child: OutlineDecoration.onlyLeft(
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_table.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_table.dart
index 17807d6..a6eeb2c 100644
--- a/packages/devtools_app/lib/src/screens/app_size/app_size_table.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/app_size_table.dart
@@ -19,18 +19,20 @@
     required AppSizeController controller,
   }) {
     final treeColumn = _NameColumn(
-      currentRootLevel: controller.isDeferredApp.value
-          ? rootNode.children.first.level
-          : rootNode.level,
+      currentRootLevel:
+          controller.isDeferredApp.value
+              ? rootNode.children.first.level
+              : rootNode.level,
     );
     final sizeColumn = _SizeColumn();
     final columns = List<ColumnData<TreemapNode>>.unmodifiable([
       treeColumn,
       sizeColumn,
       _SizePercentageColumn(
-        totalSize: controller.isDeferredApp.value
-            ? rootNode.children[0].root.byteSize
-            : rootNode.root.byteSize,
+        totalSize:
+            controller.isDeferredApp.value
+                ? rootNode.children[0].root.byteSize
+                : rootNode.root.byteSize,
       ),
     ]);
 
@@ -99,11 +101,11 @@
 
 class _SizeColumn extends ColumnData<TreemapNode> {
   _SizeColumn()
-      : super(
-          'Size',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(100.0),
-        );
+    : super(
+        'Size',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(100.0),
+      );
 
   @override
   Comparable getValue(TreemapNode dataObject) => dataObject.byteSize;
@@ -123,11 +125,11 @@
 
 class _SizePercentageColumn extends ColumnData<TreemapNode> {
   _SizePercentageColumn({required this.totalSize})
-      : super(
-          '% of Total Size',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(100.0),
-        );
+    : super(
+        '% of Total Size',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(100.0),
+      );
 
   final int totalSize;
 
@@ -156,12 +158,7 @@
       diffColumn,
     ]);
 
-    return AppSizeDiffTable._(
-      rootNode,
-      treeColumn,
-      diffColumn,
-      columns,
-    );
+    return AppSizeDiffTable._(rootNode, treeColumn, diffColumn, columns);
   }
 
   const AppSizeDiffTable._(
@@ -198,17 +195,17 @@
 //                   other columns.
 class _DiffColumn extends ColumnData<TreemapNode> {
   _DiffColumn()
-      : super(
-          'Change',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(100.0),
-        );
+    : super(
+        'Change',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(100.0),
+      );
 
   // Ensure sort by absolute size.
   @override
   int getValue(TreemapNode dataObject) => dataObject.unsignedByteSize;
 
-// TODO(peterdjlee): Add up or down arrows indicating increase or decrease for display value.
+  // TODO(peterdjlee): Add up or down arrows indicating increase or decrease for display value.
   @override
   String getDisplayValue(TreemapNode dataObject) => dataObject.prettyByteSize();
 
diff --git a/packages/devtools_app/lib/src/screens/app_size/code_size_attribution.dart b/packages/devtools_app/lib/src/screens/app_size/code_size_attribution.dart
index ad55739..227d0b0 100644
--- a/packages/devtools_app/lib/src/screens/app_size/code_size_attribution.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/code_size_attribution.dart
@@ -7,11 +7,11 @@
 import 'package:vm_snapshot_analysis/precompiler_trace.dart';
 import 'package:vm_snapshot_analysis/program_info.dart';
 
-import '../../shared/common_widgets.dart';
 import '../../shared/primitives/trees.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/table/table.dart';
 import '../../shared/table/table_data.dart';
+import '../../shared/ui/common_widgets.dart';
 
 class CallGraphWithDominators extends StatefulWidget {
   const CallGraphWithDominators({super.key, required this.callGraphRoot});
@@ -31,16 +31,18 @@
   @override
   void initState() {
     super.initState();
-    dominatorTreeRoot =
-        DominatorTreeNode.from(widget.callGraphRoot.dominatorRoot);
+    dominatorTreeRoot = DominatorTreeNode.from(
+      widget.callGraphRoot.dominatorRoot,
+    );
   }
 
   @override
   void didUpdateWidget(covariant CallGraphWithDominators oldWidget) {
     super.didUpdateWidget(oldWidget);
     if (oldWidget.callGraphRoot != widget.callGraphRoot) {
-      dominatorTreeRoot =
-          DominatorTreeNode.from(widget.callGraphRoot.dominatorRoot);
+      dominatorTreeRoot = DominatorTreeNode.from(
+        widget.callGraphRoot.dominatorRoot,
+      );
     }
   }
 
@@ -62,12 +64,13 @@
           ],
         ),
         Expanded(
-          child: showCallGraph
-              ? CallGraphView(node: widget.callGraphRoot)
-              : DominatorTree(
-                  dominatorTreeRoot: dominatorTreeRoot,
-                  selectedNode: widget.callGraphRoot,
-                ),
+          child:
+              showCallGraph
+                  ? CallGraphView(node: widget.callGraphRoot)
+                  : DominatorTree(
+                    dominatorTreeRoot: dominatorTreeRoot,
+                    selectedNode: widget.callGraphRoot,
+                  ),
         ),
       ],
     );
@@ -225,9 +228,10 @@
   Widget build(BuildContext context) {
     return FlatTable<CallGraphNode>(
       keyFactory: (CallGraphNode node) => ValueKey<CallGraphNode>(node),
-      data: tableType == _CallGraphTableType.from
-          ? selectedNode.pred
-          : selectedNode.succ,
+      data:
+          tableType == _CallGraphTableType.from
+              ? selectedNode.pred
+              : selectedNode.succ,
       dataKey: tableType.dataKey,
       columns: [tableColumn],
       onItemSelected: onNodeSelected,
@@ -311,9 +315,10 @@
 
 extension CallGraphNodeDisplay on CallGraphNode {
   String get display {
-    final displayText = data is ProgramInfoNode
-        ? (data as ProgramInfoNode).qualifiedName
-        : data.toString();
+    final displayText =
+        data is ProgramInfoNode
+            ? (data as ProgramInfoNode).qualifiedName
+            : data.toString();
     if (displayText == '@shared') {
       // Special case '@shared' because this is the name of the call graph root,
       // and '@root' has a more intuitive meaning.
diff --git a/packages/devtools_app/lib/src/screens/debugger/breakpoint_manager.dart b/packages/devtools_app/lib/src/screens/debugger/breakpoint_manager.dart
index 962b074..45c3e56 100644
--- a/packages/devtools_app/lib/src/screens/debugger/breakpoint_manager.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/breakpoint_manager.dart
@@ -27,7 +27,7 @@
   final _breakpoints = ValueNotifier<List<Breakpoint>>([]);
 
   ValueListenable<List<BreakpointAndSourcePosition>>
-      get breakpointsWithLocation => _breakpointsWithLocation;
+  get breakpointsWithLocation => _breakpointsWithLocation;
   final _breakpointsWithLocation =
       ValueNotifier<List<BreakpointAndSourcePosition>>([]);
 
@@ -70,8 +70,9 @@
       return;
     }
 
-    final breakpointsForIsolate =
-        await _getBreakpointsForIsolate(_isolateRefId);
+    final breakpointsForIsolate = await _getBreakpointsForIsolate(
+      _isolateRefId,
+    );
     if (breakpointsForIsolate.isNotEmpty) {
       // If the isolate already has breakpoints, then update them:
       await _updateBreakpoints(
@@ -87,8 +88,9 @@
     }
 
     // Resume the isolate now that the breakpoints have been set:
-    await serviceConnection.serviceManager.isolateManager
-        .resumeIsolate(isolateRef);
+    await serviceConnection.serviceManager.isolateManager.resumeIsolate(
+      isolateRef,
+    );
   }
 
   void clearCache({required bool isServiceShutdown}) {
@@ -152,19 +154,20 @@
     _breakpointsWithLocation.value = [];
   }
 
-  void _updateAfterIsolateReload(
-    Event _,
-  ) async {
+  void _updateAfterIsolateReload(Event _) async {
     // TODO(devoncarew): We need to coordinate this with other debugger clients
     // as well as pause before re-setting the breakpoints.
     // Refresh the list of scripts.
     final previousScriptRefs = scriptManager.sortedScripts.value;
-    final currentScriptRefs =
-        await scriptManager.retrieveAndSortScripts(_isolateRef!);
-    final removedScripts = Set<ScriptRef>.of(previousScriptRefs)
-        .difference(Set<ScriptRef>.of(currentScriptRefs));
-    final addedScripts = Set<ScriptRef>.of(currentScriptRefs)
-        .difference(Set<ScriptRef>.of(previousScriptRefs));
+    final currentScriptRefs = await scriptManager.retrieveAndSortScripts(
+      _isolateRef!,
+    );
+    final removedScripts = Set<ScriptRef>.of(
+      previousScriptRefs,
+    ).difference(Set<ScriptRef>.of(currentScriptRefs));
+    final addedScripts = Set<ScriptRef>.of(
+      currentScriptRefs,
+    ).difference(Set<ScriptRef>.of(previousScriptRefs));
     final breakpointsToRemove = <BreakpointAndSourcePosition>[];
 
     // Find all breakpoints set in files where we have newer versions of those
@@ -246,20 +249,19 @@
     required List<BreakpointAndSourcePosition> breakpoints,
     required IsolateRef isolateRef,
   }) async {
-    final bpScriptUris = breakpoints.fold(
-      <String>{},
-      (scriptSet, breakpoint) {
-        final scriptUri = breakpoint.scriptUri;
-        if (scriptUri != null) {
-          scriptSet.add(scriptUri);
-        }
-        return scriptSet;
-      },
-    );
+    final bpScriptUris = breakpoints.fold(<String>{}, (scriptSet, breakpoint) {
+      final scriptUri = breakpoint.scriptUri;
+      if (scriptUri != null) {
+        scriptSet.add(scriptUri);
+      }
+      return scriptSet;
+    });
 
     final newScripts = await scriptManager.retrieveAndSortScripts(isolateRef);
-    final scriptUriToRef =
-        newScripts.fold(<String, ScriptRef>{}, (scriptMap, script) {
+    final scriptUriToRef = newScripts.fold(<String, ScriptRef>{}, (
+      scriptMap,
+      script,
+    ) {
       final scriptUri = script.uri;
       if (scriptUri != null && bpScriptUris.contains(scriptUri)) {
         scriptMap[scriptUri] = script;
@@ -278,8 +280,10 @@
     final key = script.id;
     if (key == null) return [];
     if (!_breakPositionsMap.containsKey(key)) {
-      _breakPositionsMap[key] =
-          await _getBreakablePositions(isolateRef, script);
+      _breakPositionsMap[key] = await _getBreakablePositions(
+        isolateRef,
+        script,
+      );
     }
 
     return _breakPositionsMap[key] ?? [];
@@ -342,18 +346,16 @@
       // that knows how to notify when performing a list edit operation.
       case EventKind.kBreakpointAdded:
         final breakpoint = event.breakpoint!;
-        final isDuplicate =
-            _breakpoints.value.any((bp) => bp.id == breakpoint.id);
+        final isDuplicate = _breakpoints.value.any(
+          (bp) => bp.id == breakpoint.id,
+        );
         if (isDuplicate) break;
         _breakpoints.value = [..._breakpoints.value, breakpoint];
 
-        await breakpointManager
-            .createBreakpointWithLocation(breakpoint)
-            .then((bp) {
-          final list = [
-            ..._breakpointsWithLocation.value,
-            bp,
-          ]..sort();
+        await breakpointManager.createBreakpointWithLocation(breakpoint).then((
+          bp,
+        ) {
+          final list = [..._breakpointsWithLocation.value, bp]..sort();
           _breakpointsWithLocation.value = list;
         });
 
@@ -366,9 +368,9 @@
           breakpoint,
         ];
 
-        await breakpointManager
-            .createBreakpointWithLocation(breakpoint)
-            .then((bp) {
+        await breakpointManager.createBreakpointWithLocation(breakpoint).then((
+          bp,
+        ) {
           final list = _breakpointsWithLocation.value;
           // Remove the bp with the older, unresolved information from the list.
           list.removeWhere((breakpoint) => breakpoint.id == bp.id);
@@ -386,8 +388,11 @@
         // the restart. Note we only track hot restarts triggered by DevTools,
         // if a hot-restart was triggered by another client we won't know.
         // See https://github.com/flutter/flutter/issues/134470
-        final hotRestartInProgress = serviceConnection
-            .serviceManager.isolateManager.hotRestartInProgress;
+        final hotRestartInProgress =
+            serviceConnection
+                .serviceManager
+                .isolateManager
+                .hotRestartInProgress;
         if (hotRestartInProgress) break;
 
         final breakpoint = event.breakpoint;
diff --git a/packages/devtools_app/lib/src/screens/debugger/breakpoints.dart b/packages/devtools_app/lib/src/screens/debugger/breakpoints.dart
index 85dd39b..7da9046 100644
--- a/packages/devtools_app/lib/src/screens/debugger/breakpoints.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/breakpoints.dart
@@ -5,10 +5,10 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart' hide Badge;
 
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import 'common.dart';
 import 'debugger_controller.dart';
 import 'debugger_model.dart';
@@ -46,10 +46,7 @@
           itemCount: breakpoints.length,
           itemExtent: defaultListItemHeight,
           itemBuilder: (_, index) {
-            return buildBreakpoint(
-              breakpoints[index],
-              selectedBreakpoint,
-            );
+            return buildBreakpoint(breakpoints[index], selectedBreakpoint);
           },
         );
       },
@@ -91,9 +88,10 @@
                     children: [
                       TextSpan(
                         text: ' (${bp.scriptUri})',
-                        style: isSelected
-                            ? theme.selectedSubtleTextStyle
-                            : theme.subtleTextStyle,
+                        style:
+                            isSelected
+                                ? theme.selectedSubtleTextStyle
+                                : theme.subtleTextStyle,
                       ),
                     ],
                   ),
diff --git a/packages/devtools_app/lib/src/screens/debugger/call_stack.dart b/packages/devtools_app/lib/src/screens/debugger/call_stack.dart
index 58a471a..59fed8e 100644
--- a/packages/devtools_app/lib/src/screens/debugger/call_stack.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/call_stack.dart
@@ -9,9 +9,9 @@
 import 'package:flutter/material.dart' hide Stack;
 import 'package:vm_service/vm_service.dart';
 
-import '../../shared/common_widgets.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import 'debugger_controller.dart';
 import 'debugger_model.dart';
 
@@ -59,10 +59,7 @@
     );
   }
 
-  Widget _buildStackFrame(
-    StackFrameAndSourcePosition frame,
-    bool selected,
-  ) {
+  Widget _buildStackFrame(StackFrameAndSourcePosition frame, bool selected) {
     final theme = Theme.of(context);
 
     Widget child;
@@ -79,10 +76,7 @@
           const SizedBox(width: defaultSpacing, child: Divider()),
           Padding(
             padding: const EdgeInsets.symmetric(horizontal: densePadding),
-            child: Text(
-              frameDescription,
-              style: theme.regularTextStyle,
-            ),
+            child: Text(frameDescription, style: theme.regularTextStyle),
           ),
           const Expanded(child: Divider()),
         ],
@@ -98,9 +92,10 @@
             if (locationDescription != null)
               TextSpan(
                 text: ' $locationDescription',
-                style: selected
-                    ? theme.selectedSubtleTextStyle
-                    : theme.subtleTextStyle,
+                style:
+                    selected
+                        ? theme.selectedSubtleTextStyle
+                        : theme.subtleTextStyle,
               ),
           ],
         ),
@@ -124,12 +119,13 @@
     return isAsyncBreak
         ? result
         : DevToolsTooltip(
-            message: locationDescription == null
-                ? frameDescription
-                : '$frameDescription $locationDescription',
-            waitDuration: tooltipWaitLong,
-            child: result,
-          );
+          message:
+              locationDescription == null
+                  ? frameDescription
+                  : '$frameDescription $locationDescription',
+          waitDuration: tooltipWaitLong,
+          child: result,
+        );
   }
 
   Future<void> _onStackFrameSelected(StackFrameAndSourcePosition frame) async {
diff --git a/packages/devtools_app/lib/src/screens/debugger/codeview.dart b/packages/devtools_app/lib/src/screens/debugger/codeview.dart
index cfa38e0..64ebb7f 100644
--- a/packages/devtools_app/lib/src/screens/debugger/codeview.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/codeview.dart
@@ -14,20 +14,20 @@
 import 'package:logging/logging.dart';
 import 'package:vm_service/vm_service.dart' hide Stack;
 
-import '../../shared/common_widgets.dart';
 import '../../shared/console/widgets/expandable_variable.dart';
 import '../../shared/diagnostics/dart_object_node.dart';
 import '../../shared/diagnostics/primitives/source_location.dart';
 import '../../shared/diagnostics/tree_builder.dart';
 import '../../shared/globals.dart';
-import '../../shared/history_viewport.dart';
 import '../../shared/primitives/flutter_widgets/linked_scroll_controller.dart';
 import '../../shared/primitives/listenable.dart';
 import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/history_viewport.dart';
 import '../../shared/ui/hover.dart';
 import '../../shared/ui/search.dart';
 import '../../shared/ui/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import '../vm_developer/vm_service_private_extensions.dart';
 import 'breakpoints.dart';
 import 'codeview_controller.dart';
@@ -39,8 +39,9 @@
 
 final _log = Logger('codeview');
 
-final debuggerCodeViewFileOpenerKey =
-    GlobalKey(debugLabel: 'DebuggerCodeViewFileOpenerKey');
+final debuggerCodeViewFileOpenerKey = GlobalKey(
+  debugLabel: 'DebuggerCodeViewFileOpenerKey',
+);
 
 // TODO(kenz): consider moving lines / pausedPositions calculations to the
 // controller.
@@ -57,11 +58,13 @@
     this.enableHistory = true,
   });
 
-  static const debuggerCodeViewHorizontalScrollbarKey =
-      Key('debuggerCodeViewHorizontalScrollbarKey');
+  static const debuggerCodeViewHorizontalScrollbarKey = Key(
+    'debuggerCodeViewHorizontalScrollbarKey',
+  );
 
-  static const debuggerCodeViewVerticalScrollbarKey =
-      Key('debuggerCodeViewVerticalScrollbarKey');
+  static const debuggerCodeViewVerticalScrollbarKey = Key(
+    'debuggerCodeViewVerticalScrollbarKey',
+  );
 
   static double get rowHeight => scaleByFontFactor(16.0);
 
@@ -171,8 +174,9 @@
     profileController?.dispose();
     textController.dispose();
     horizontalController.dispose();
-    widget.codeViewController.scriptLocation
-        .removeListener(_handleScriptLocationChanged);
+    widget.codeViewController.scriptLocation.removeListener(
+      _handleScriptLocationChanged,
+    );
     super.dispose();
   }
 
@@ -225,7 +229,8 @@
       final lineCount = parsedScript?.lineCount;
       if (lineCount != null && lineCount * CodeView.rowHeight > extent) {
         final lineIndex = line - 1;
-        var scrollPosition = lineIndex * CodeView.rowHeight -
+        var scrollPosition =
+            lineIndex * CodeView.rowHeight -
             ((extent - CodeView.rowHeight) / 2);
         scrollPosition = scrollPosition.clamp(0.0, position.extentTotal);
         if (animate) {
@@ -246,8 +251,8 @@
     verticalController.hasAttachedControllers
         ? updateScrollPositionImpl()
         : WidgetsBinding.instance.addPostFrameCallback(
-            (_) => updateScrollPositionImpl(),
-          );
+          (_) => updateScrollPositionImpl(),
+        );
   }
 
   @override
@@ -303,31 +308,18 @@
           currentLine.add(span);
           if (span.toPlainText() == '\n') {
             lines.add(
-              TextSpan(
-                style: theme.fixedFontStyle,
-                children: currentLine,
-              ),
+              TextSpan(style: theme.fixedFontStyle, children: currentLine),
             );
             currentLine = <InlineSpan>[];
           }
           return true;
         });
-        lines.add(
-          TextSpan(
-            style: theme.fixedFontStyle,
-            children: currentLine,
-          ),
-        );
+        lines.add(TextSpan(style: theme.fixedFontStyle, children: currentLine));
       } else {
-        lines.addAll(
-          [
-            for (final line in scriptSource.split('\n'))
-              TextSpan(
-                style: theme.fixedFontStyle,
-                text: line,
-              ),
-          ],
-        );
+        lines.addAll([
+          for (final line in scriptSource.split('\n'))
+            TextSpan(style: theme.fixedFontStyle, text: line),
+        ]);
       }
     }
 
@@ -341,10 +333,11 @@
             thumbVisibility: true,
             // Only listen for vertical scroll notifications (ignore those
             // from the nested horizontal SingleChildScrollView):
-            notificationPredicate: (ScrollNotification notification) =>
-                notification.depth == 1,
+            notificationPredicate:
+                (ScrollNotification notification) => notification.depth == 1,
             child: ValueListenableBuilder<StackFrameAndSourcePosition?>(
-              valueListenable: widget.debuggerController?.selectedStackFrame ??
+              valueListenable:
+                  widget.debuggerController?.selectedStackFrame ??
                   const FixedValueListenable<StackFrameAndSourcePosition?>(
                     null,
                   ),
@@ -379,8 +372,9 @@
                               );
 
                               return Scrollbar(
-                                key: CodeView
-                                    .debuggerCodeViewHorizontalScrollbarKey,
+                                key:
+                                    CodeView
+                                        .debuggerCodeViewHorizontalScrollbarKey,
                                 thumbVisibility: true,
                                 controller: horizontalController,
                                 child: SingleChildScrollView(
@@ -398,13 +392,18 @@
                                           widget.codeViewController,
                                       scrollController: textController,
                                       lines: lines,
-                                      selectedFrameNotifier: widget
-                                          .debuggerController
-                                          ?.selectedStackFrame,
-                                      searchMatchesNotifier: widget
-                                          .codeViewController.searchMatches,
-                                      activeSearchMatchNotifier: widget
-                                          .codeViewController.activeSearchMatch,
+                                      selectedFrameNotifier:
+                                          widget
+                                              .debuggerController
+                                              ?.selectedStackFrame,
+                                      searchMatchesNotifier:
+                                          widget
+                                              .codeViewController
+                                              .searchMatches,
+                                      activeSearchMatchNotifier:
+                                          widget
+                                              .codeViewController
+                                              .activeSearchMatch,
                                       showProfileInformation:
                                           showProfileInformation,
                                     ),
@@ -441,24 +440,25 @@
           return scriptUri;
         },
         titleIcon: Icons.search,
-        onTitleTap: () => widget.codeViewController
-          ..toggleFileOpenerVisibility(true)
-          ..toggleSearchInFileVisibility(false),
+        onTitleTap:
+            () =>
+                widget.codeViewController
+                  ..toggleFileOpenerVisibility(true)
+                  ..toggleSearchInFileVisibility(false),
         controls: [
           ScriptPopupMenu(widget.codeViewController),
           ScriptHistoryPopupMenu(
             itemBuilder: _buildScriptMenuFromHistory,
             onSelected: (scriptRef) async {
-              await widget.codeViewController
-                  .showScriptLocation(ScriptLocation(scriptRef));
+              await widget.codeViewController.showScriptLocation(
+                ScriptLocation(scriptRef),
+              );
             },
             enabled: widget.codeViewController.scriptsHistory.hasScripts,
           ),
         ],
         contentBuilder: (context, ScriptRef? scriptRef) {
-          return Expanded(
-            child: contentBuilder(context, scriptRef),
-          );
+          return Expanded(child: contentBuilder(context, scriptRef));
         },
       );
     }
@@ -471,9 +471,7 @@
       width: extraWideSearchFieldWidth,
       height: defaultTextFieldHeight,
       padding: EdgeInsets.zero,
-      child: FileSearchField(
-        codeViewController: widget.codeViewController,
-      ),
+      child: FileSearchField(codeViewController: widget.codeViewController),
     );
   }
 
@@ -486,8 +484,8 @@
         searchFieldEnabled: parsedScript != null,
         shouldRequestFocus: true,
         searchFieldWidth: wideSearchFieldWidth,
-        onClose: () =>
-            widget.codeViewController.toggleSearchInFileVisibility(false),
+        onClose:
+            () => widget.codeViewController.toggleSearchInFileVisibility(false),
       ),
     );
   }
@@ -500,35 +498,33 @@
     return widget.codeViewController.scriptsHistory.openedScripts
         .take(scriptHistorySize)
         .map((scriptRef) {
-      return PopupMenuItem(
-        value: scriptRef,
-        child: Column(
-          mainAxisAlignment: MainAxisAlignment.center,
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            Text(
-              ScriptRefUtils.fileName(scriptRef),
-              maxLines: 1,
-              overflow: TextOverflow.ellipsis,
+          return PopupMenuItem(
+            value: scriptRef,
+            child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Text(
+                  ScriptRefUtils.fileName(scriptRef),
+                  maxLines: 1,
+                  overflow: TextOverflow.ellipsis,
+                ),
+                Text(
+                  scriptRef.uri ?? '',
+                  overflow: TextOverflow.ellipsis,
+                  maxLines: 1,
+                  style: Theme.of(context).subtleTextStyle,
+                ),
+              ],
             ),
-            Text(
-              scriptRef.uri ?? '',
-              overflow: TextOverflow.ellipsis,
-              maxLines: 1,
-              style: Theme.of(context).subtleTextStyle,
-            ),
-          ],
-        ),
-      );
-    }).toList();
+          );
+        })
+        .toList();
   }
 }
 
 class CodeViewEmptyState extends StatelessWidget {
-  const CodeViewEmptyState({
-    super.key,
-    required this.widget,
-  });
+  const CodeViewEmptyState({super.key, required this.widget});
 
   final CodeView widget;
 
@@ -539,8 +535,8 @@
     return Center(
       child: ElevatedButton(
         autofocus: true,
-        onPressed: () =>
-            widget.codeViewController.toggleFileOpenerVisibility(true),
+        onPressed:
+            () => widget.codeViewController.toggleFileOpenerVisibility(true),
         child: Text(
           'Open a file ($openFileKeySetDescription)',
           style: theme.textTheme.titleMedium,
@@ -605,9 +601,7 @@
                 ),
               ],
             ),
-            const Center(
-              child: VerticalDivider(),
-            ),
+            const Center(child: VerticalDivider()),
           ],
         ),
       ),
@@ -638,28 +632,20 @@
                 Expanded(
                   child: DevToolsTooltip(
                     message: totalTimeTooltip,
-                    child: const Text(
-                      'Total %',
-                      textAlign: TextAlign.center,
-                    ),
+                    child: const Text('Total %', textAlign: TextAlign.center),
                   ),
                 ),
                 const SizedBox(width: denseSpacing),
                 Expanded(
                   child: DevToolsTooltip(
                     message: selfTimeTooltip,
-                    child: const Text(
-                      'Self %',
-                      textAlign: TextAlign.center,
-                    ),
+                    child: const Text('Self %', textAlign: TextAlign.center),
                   ),
                 ),
               ],
             ),
           ),
-          const Divider(
-            height: 0,
-          ),
+          const Divider(height: 0),
         ],
       ),
     );
@@ -667,10 +653,7 @@
 }
 
 class ProfileInformationGutterItem extends StatelessWidget {
-  const ProfileInformationGutterItem({
-    super.key,
-    required this.profilerData,
-  });
+  const ProfileInformationGutterItem({super.key, required this.profilerData});
 
   final ProfileReportEntry profilerData;
 
@@ -728,9 +711,7 @@
       message: hoverText,
       child: Container(
         color: color,
-        padding: const EdgeInsets.symmetric(
-          horizontal: densePadding,
-        ),
+        padding: const EdgeInsets.symmetric(horizontal: densePadding),
         child: Text(
           '${percentage.toStringAsFixed(2)} %',
           textAlign: TextAlign.end,
@@ -783,7 +764,8 @@
     //   - each character in the longest line number
     //   - one for the breakpoint dot
     //   - two for the paused arrow
-    final gutterWidth = assumedMonospaceCharacterWidth * 4 +
+    final gutterWidth =
+        assumedMonospaceCharacterWidth * 4 +
         assumedMonospaceCharacterWidth *
             (defaultEpsilon + math.log(math.max(lines.length, 100)) / math.ln10)
                 .truncateToDouble();
@@ -820,14 +802,14 @@
         !showProfileInformation
             ? const SizedBox(width: denseSpacing)
             : Padding(
-                padding: const EdgeInsets.only(right: denseSpacing),
-                child: ProfileInformationGutter(
-                  scrollController: profileController!,
-                  lineCount: lineCount,
-                  lineOffset: lineOffset,
-                  sourceReport: sourceReport,
-                ),
+              padding: const EdgeInsets.only(right: denseSpacing),
+              child: ProfileInformationGutter(
+                scrollController: profileController!,
+                lineCount: lineCount,
+                lineOffset: lineOffset,
+                sourceReport: sourceReport,
               ),
+            ),
       ],
     );
   }
@@ -875,8 +857,9 @@
   Widget build(BuildContext context) {
     final bpLineSet = Set.of(breakpoints.map((bp) => bp.line));
     final theme = Theme.of(context);
-    final coverageLines =
-        sourceReport.coverageHitLines.union(sourceReport.coverageMissedLines);
+    final coverageLines = sourceReport.coverageHitLines.union(
+      sourceReport.coverageMissedLines,
+    );
     // Used to account for the presence of `_ProfileInformationGutterHeader` at
     // the top of the profiler gutter columns. Everything needs to be shifted
     // down a single line so profiling information for line 1 isn't hidden by
@@ -958,9 +941,10 @@
     TextStyle? coverageTextStyleOverride;
     final hasCoverage = coverageHit;
     if (hasCoverage != null) {
-      color = hasCoverage
-          ? theme.colorScheme.coverageHitColor
-          : theme.colorScheme.coverageMissColor;
+      color =
+          hasCoverage
+              ? theme.colorScheme.coverageHitColor
+              : theme.colorScheme.coverageMissColor;
       coverageTextStyleOverride = theme.regularTextStyleWithColor(
         theme.colorScheme.coverageAndPerformanceTextColor,
       );
@@ -1110,12 +1094,11 @@
                 lineContents: widget.lines[dataIndex],
                 pausedFrame: isPausedLine ? pausedFrame : null,
                 focused: isPausedLine || isFocusedLine,
-                searchMatches: _searchMatchesForLine(
-                  dataIndex,
-                ),
-                activeSearchMatch: activeSearch?.position.line == dataIndex
-                    ? activeSearch
-                    : null,
+                searchMatches: _searchMatchesForLine(dataIndex),
+                activeSearchMatch:
+                    activeSearch?.position.line == dataIndex
+                        ? activeSearch
+                        : null,
               );
             },
           );
@@ -1133,9 +1116,11 @@
   void _maybeScrollToLine(int? lineNumber) {
     if (lineNumber == null) return;
 
-    final isOutOfViewTop = lineNumber * CodeView.rowHeight <
+    final isOutOfViewTop =
+        lineNumber * CodeView.rowHeight <
         widget.scrollController.offset + CodeView.rowHeight;
-    final isOutOfViewBottom = lineNumber * CodeView.rowHeight >
+    final isOutOfViewBottom =
+        lineNumber * CodeView.rowHeight >
         widget.scrollController.offset + widget.height - CodeView.rowHeight;
 
     if (isOutOfViewTop || isOutOfViewBottom) {
@@ -1185,16 +1170,17 @@
   }) async {
     if (!serviceConnection.serviceManager.isMainIsolatePaused) return null;
 
-    final word = wordForHover(
-      event.localPosition.dx,
-      widget.lineContents,
-    );
+    final word = wordForHover(event.localPosition.dx, widget.lineContents);
 
-    if (word != '') {
+    if (word.isNotEmpty && !isPrimitiveValueOrNull(word)) {
       try {
         final response = await evalService.evalAtCurrentFrame(word);
-        final isolateRef = serviceConnection
-            .serviceManager.isolateManager.selectedIsolate.value;
+        final isolateRef =
+            serviceConnection
+                .serviceManager
+                .isolateManager
+                .selectedIsolate
+                .value;
         if (response is! InstanceRef) return null;
         final variable = DartObjectNode.fromValue(
           value: response,
@@ -1203,11 +1189,7 @@
         await buildVariablesTree(variable);
         return HoverCardData(
           title: word,
-          contents: Material(
-            child: ExpandableVariable(
-              variable: variable,
-            ),
-          ),
+          contents: Material(child: ExpandableVariable(variable: variable)),
           width: LineItem._hoverWidth,
         );
       } catch (_) {
@@ -1285,23 +1267,22 @@
   }
 
   Widget _hoverableLine() => HoverCardTooltip.async(
-        enabled: () => true,
-        asyncTimeout: 100,
-        asyncGenerateHoverCardData: _generateHoverCardData,
-        child: Text.rich(
-          searchAwareLineContents(),
-          maxLines: 1,
-        ),
-      );
+    enabled: () => true,
+    asyncTimeout: 100,
+    asyncGenerateHoverCardData: _generateHoverCardData,
+    child: Text.rich(searchAwareLineContents(), maxLines: 1),
+  );
 
   TextSpan searchAwareLineContents() {
     // If syntax highlighting is disabled for the script, then
     // `widget.lineContents` is simply a `TextSpan` with no children.
     final lineContents = widget.lineContents.children ?? [widget.lineContents];
-    final activeSearchAwareContents =
-        _activeSearchAwareLineContents(lineContents);
-    final allSearchAwareContents =
-        _searchMatchAwareLineContents(activeSearchAwareContents!);
+    final activeSearchAwareContents = _activeSearchAwareLineContents(
+      lineContents,
+    );
+    final allSearchAwareContents = _searchMatchAwareLineContents(
+      activeSearchAwareContents!,
+    );
     return TextSpan(
       children: allSearchAwareContents,
       style: widget.lineContents.style,
@@ -1332,23 +1313,19 @@
           ),
         );
 
-        final matchStyle =
-            (span.style ?? DefaultTextStyle.of(context).style).copyWith(
-          color: Colors.black,
-          backgroundColor: matchColor,
-        );
+        final matchStyle = (span.style ?? DefaultTextStyle.of(context).style)
+            .copyWith(color: Colors.black, backgroundColor: matchColor);
 
         if (matchEndInSpan <= spanText.length) {
-          final matchText =
-              spanText.substring(matchStartInSpan, matchEndInSpan);
+          final matchText = spanText.substring(
+            matchStartInSpan,
+            matchEndInSpan,
+          );
           final trailingText = spanText.substring(matchEndInSpan);
           // Add the match and any part of [span] that occurs after the search
           // match.
           contentsWithMatch.addAll([
-            TextSpan(
-              text: matchText,
-              style: matchStyle,
-            ),
+            TextSpan(text: matchText, style: matchStyle),
             if (trailingText.isNotEmpty)
               TextSpan(
                 text: spanText.substring(matchEndInSpan),
@@ -1425,17 +1402,16 @@
   Widget build(BuildContext context) {
     return PopupMenuButton<ScriptPopupMenuOption>(
       onSelected: (option) => option.onSelected(context, _controller),
-      itemBuilder: (_) => [
-        for (final menuOption in defaultScriptPopupMenuOptions)
-          menuOption.build(),
-        for (final extensionMenuOption in devToolsEnvironmentParameters
-            .buildExtraDebuggerScriptPopupMenuOptions())
-          extensionMenuOption.build(),
-      ],
-      child: Icon(
-        Icons.more_vert,
-        size: actionsIconSize,
-      ),
+      itemBuilder:
+          (_) => [
+            for (final menuOption in defaultScriptPopupMenuOptions)
+              menuOption.build(),
+            for (final extensionMenuOption
+                in devToolsEnvironmentParameters
+                    .buildExtraDebuggerScriptPopupMenuOptions())
+              extensionMenuOption.build(),
+          ],
+      child: Icon(Icons.more_vert, size: actionsIconSize),
     );
   }
 }
@@ -1465,10 +1441,7 @@
         actionsIconSize + denseSpacing,
         buttonMinWidth + denseSpacing,
       ),
-      child: Icon(
-        Icons.history,
-        size: actionsIconSize,
-      ),
+      child: Icon(Icons.history, size: actionsIconSize),
     );
   }
 }
@@ -1493,11 +1466,7 @@
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         children: [
           Text(label),
-          if (icon != null)
-            Icon(
-              icon,
-              size: actionsIconSize,
-            ),
+          if (icon != null) Icon(icon, size: actionsIconSize),
         ],
       ),
     );
@@ -1514,9 +1483,12 @@
 final copyPackagePathOption = ScriptPopupMenuOption(
   label: 'Copy package path',
   icon: Icons.content_copy,
-  onSelected: (_, controller) => Clipboard.setData(
-    ClipboardData(text: controller.scriptLocation.value?.scriptRef.uri ?? ''),
-  ),
+  onSelected:
+      (_, controller) => Clipboard.setData(
+        ClipboardData(
+          text: controller.scriptLocation.value?.scriptRef.uri ?? '',
+        ),
+      ),
 );
 
 final copyFilePathOption = ScriptPopupMenuOption(
@@ -1525,9 +1497,7 @@
   onSelected: (_, controller) {
     unawaited(() async {
       final filePath = await fetchScriptLocationFullFilePath(controller);
-      await Clipboard.setData(
-        ClipboardData(text: filePath ?? ''),
-      );
+      await Clipboard.setData(ClipboardData(text: filePath ?? ''));
     }());
   },
 );
@@ -1539,23 +1509,22 @@
   String? filePath;
   final packagePath = controller.scriptLocation.value!.scriptRef.uri;
   if (packagePath != null) {
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
-    filePath =
-        serviceConnection.serviceManager.resolvedUriManager.lookupFileUri(
-      isolateId,
-      packagePath,
-    );
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
+    filePath = serviceConnection.serviceManager.resolvedUriManager
+        .lookupFileUri(isolateId, packagePath);
     if (filePath == null) {
       await serviceConnection.serviceManager.resolvedUriManager.fetchFileUris(
         isolateId,
         [packagePath],
       );
-      filePath =
-          serviceConnection.serviceManager.resolvedUriManager.lookupFileUri(
-        isolateId,
-        packagePath,
-      );
+      filePath = serviceConnection.serviceManager.resolvedUriManager
+          .lookupFileUri(isolateId, packagePath);
     }
   }
   return filePath;
@@ -1626,9 +1595,7 @@
           ),
         ],
       ),
-      actions: const [
-        DialogCancelButton(),
-      ],
+      actions: const [DialogCancelButton()],
     );
   }
 }
@@ -1655,12 +1622,7 @@
       valueListenable: isVisibleListenable,
       builder: (context, isVisible, _) {
         return isVisible
-            ? Positioned(
-                top: top,
-                left: left,
-                right: right,
-                child: child,
-              )
+            ? Positioned(top: top, left: left, right: right, child: child)
             : const SizedBox.shrink();
       },
     );
diff --git a/packages/devtools_app/lib/src/screens/debugger/codeview_controller.dart b/packages/devtools_app/lib/src/screens/debugger/codeview_controller.dart
index a314a74..b5e251c 100644
--- a/packages/devtools_app/lib/src/screens/debugger/codeview_controller.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/codeview_controller.dart
@@ -10,14 +10,14 @@
 import 'package:logging/logging.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../framework/app_error_handling.dart';
 import '../../shared/diagnostics/primitives/source_location.dart';
+import '../../shared/framework/app_error_handling.dart';
+import '../../shared/framework/routing.dart';
 import '../../shared/globals.dart';
-import '../../shared/notifications.dart';
+import '../../shared/managers/notifications.dart';
 import '../../shared/primitives/history_manager.dart';
-import '../../shared/routing.dart';
 import '../../shared/ui/search.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import '../vm_developer/vm_service_private_extensions.dart';
 import 'debugger_model.dart';
 import 'program_explorer_controller.dart';
@@ -60,8 +60,9 @@
   }
 
   Future<void> _handleNavigationEvent(DevToolsNavigationState state) async {
-    final processedState =
-        CodeViewSourceLocationNavigationState._fromState(state);
+    final processedState = CodeViewSourceLocationNavigationState._fromState(
+      state,
+    );
     final object = processedState.object;
     _navigationInProgress = true;
     await showScriptLocation(processedState.location, focusLine: true);
@@ -156,9 +157,7 @@
     await _maybeSetUpProgramExplorer();
     addAutoDisposeListener(
       currentScriptRef,
-      () => unawaited(
-        _maybeSetUpProgramExplorer(),
-      ),
+      () => unawaited(_maybeSetUpProgramExplorer()),
     );
   }
 
@@ -192,8 +191,10 @@
     // opener will close automatically when its focus is lost.
     toggleFileOpenerVisibility(false);
 
-    final succeeded =
-        await _showScriptLocation(scriptLocation, focusLine: focusLine);
+    final succeeded = await _showScriptLocation(
+      scriptLocation,
+      focusLine: focusLine,
+    );
 
     if (succeeded) {
       // Update the scripts history (and make sure we don't react to the
@@ -211,10 +212,7 @@
     }
     final isolateRef =
         serviceConnection.serviceManager.isolateManager.selectedIsolate.value!;
-    final processedReport = await _getSourceReport(
-      isolateRef,
-      current.script,
-    );
+    final processedReport = await _getSourceReport(isolateRef, current.script);
 
     parsedScript.value = ParsedScript(
       script: current.script,
@@ -275,18 +273,15 @@
     final hitLines = <int>{};
     final missedLines = <int>{};
     try {
-      final report =
-          await serviceConnection.serviceManager.service!.getSourceReport(
-        isolateRef.id!,
-        // TODO(bkonyi): make _Profile a public report type.
-        // See https://github.com/dart-lang/sdk/issues/50641
-        const [
-          SourceReportKind.kCoverage,
-          '_Profile',
-        ],
-        scriptId: script.id!,
-        reportLines: true,
-      );
+      final report = await serviceConnection.serviceManager.service!
+          .getSourceReport(
+            isolateRef.id!,
+            // TODO(bkonyi): make _Profile a public report type.
+            // See https://github.com/dart-lang/sdk/issues/50641
+            const [SourceReportKind.kCoverage, '_Profile'],
+            scriptId: script.id!,
+            reportLines: true,
+          );
 
       for (final range in report.ranges!) {
         final coverage = range.coverage!;
@@ -298,11 +293,11 @@
       return ProcessedSourceReport(
         coverageHitLines: hitLines,
         coverageMissedLines: missedLines,
-        profilerEntries:
-            profileReport.profileRanges.fold<Map<int, ProfileReportEntry>>(
-          {},
-          (last, e) => last..addAll(e.entries),
-        ),
+        profilerEntries: profileReport.profileRanges
+            .fold<Map<int, ProfileReportEntry>>(
+              {},
+              (last, e) => last..addAll(e.entries),
+            ),
       );
     } catch (e, st) {
       // Ignore - not supported for all vm service implementations.
@@ -344,10 +339,7 @@
       _log.warning(e, e, st);
     }
 
-    final processedReport = await _getSourceReport(
-      isolateRef,
-      script,
-    );
+    final processedReport = await _getSourceReport(isolateRef, script);
 
     return ParsedScript(
       script: script,
@@ -384,8 +376,9 @@
     if (isWebApp && enableSourceMapsLink != null) {
       final enableSourceMapsAction = NotificationAction(
         label: 'Enable sourcemaps',
-        onPressed: () =>
-            unawaited(launchUrlWithErrorHandling(enableSourceMapsLink.url)),
+        onPressed:
+            () =>
+                unawaited(launchUrlWithErrorHandling(enableSourceMapsLink.url)),
       );
       notificationService.pushNotification(
         NotificationMessage(
@@ -437,9 +430,9 @@
   });
 
   const ProcessedSourceReport.empty()
-      : coverageHitLines = const <int>{},
-        coverageMissedLines = const <int>{},
-        profilerEntries = const <int, ProfileReportEntry>{};
+    : coverageHitLines = const <int>{},
+      coverageMissedLines = const <int>{},
+      profilerEntries = const <int, ProfileReportEntry>{};
 
   final Set<int> coverageHitLines;
   final Set<int> coverageMissedLines;
@@ -502,21 +495,18 @@
     required int line,
     ObjRef? object,
   }) : super(
-          kind: type,
-          state: <String, String?>{
-            _kScriptId: script.id,
-            _kUri: script.uri,
-            _kLine: line.toString(),
-            if (object != null) _kObject: json.encode(object.json),
-          },
-        );
+         kind: type,
+         state: <String, String?>{
+           _kScriptId: script.id,
+           _kUri: script.uri,
+           _kLine: line.toString(),
+           if (object != null) _kObject: json.encode(object.json),
+         },
+       );
 
   CodeViewSourceLocationNavigationState._fromState(
     DevToolsNavigationState state,
-  ) : super(
-          kind: type,
-          state: state.state,
-        );
+  ) : super(kind: type, state: state.state);
 
   static CodeViewSourceLocationNavigationState? fromState(
     DevToolsNavigationState? state,
@@ -531,10 +521,7 @@
   static const _kObject = 'object';
   static const type = 'codeViewSourceLocation';
 
-  ScriptRef get script => ScriptRef(
-        id: state[_kScriptId]!,
-        uri: state[_kUri],
-      );
+  ScriptRef get script => ScriptRef(id: state[_kScriptId]!, uri: state[_kUri]);
 
   int get line => int.parse(state[_kLine]!);
 
@@ -546,10 +533,8 @@
     return createServiceObject(json.decode(obj), const []) as ObjRef?;
   }
 
-  ScriptLocation get location => ScriptLocation(
-        script,
-        location: SourcePosition(line: line, column: 1),
-      );
+  ScriptLocation get location =>
+      ScriptLocation(script, location: SourcePosition(line: line, column: 1));
 
   @override
   String toString() {
diff --git a/packages/devtools_app/lib/src/screens/debugger/controls.dart b/packages/devtools_app/lib/src/screens/debugger/controls.dart
index a001413..d9dea0e 100644
--- a/packages/devtools_app/lib/src/screens/debugger/controls.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/controls.dart
@@ -11,10 +11,10 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import 'debugger_controller.dart';
 
 class DebuggingControls extends StatefulWidget {
@@ -36,7 +36,10 @@
     if (!initController()) return;
     addAutoDisposeListener(
       serviceConnection
-          .serviceManager.isolateManager.mainIsolateState?.isPaused,
+          .serviceManager
+          .isolateManager
+          .mainIsolateState
+          ?.isPaused,
     );
     addAutoDisposeListener(controller.resuming);
     addAutoDisposeListener(controller.stackFramesWithLocation);
@@ -47,13 +50,14 @@
     final resuming = controller.resuming.value;
     final hasStackFrames = controller.stackFramesWithLocation.value.isNotEmpty;
     final isSystemIsolate = controller.isSystemIsolate;
-    final canStep = serviceConnection.serviceManager.isMainIsolatePaused &&
+    final canStep =
+        serviceConnection.serviceManager.isMainIsolatePaused &&
         !resuming &&
         hasStackFrames &&
         !isSystemIsolate;
     final isVmApp =
         serviceConnection.serviceManager.connectedApp?.isRunningOnDartVM ??
-            false;
+        false;
     return SizedBox(
       height: defaultButtonHeight,
       child: Row(
@@ -89,18 +93,20 @@
           icon: Codicons.debugPause,
           autofocus: true,
           // Disable when paused or selected isolate is a system isolate.
-          onPressed: (isPaused || isSystemIsolate)
-              ? null
-              : () => unawaited(controller.pause()),
+          onPressed:
+              (isPaused || isSystemIsolate)
+                  ? null
+                  : () => unawaited(controller.pause()),
         ),
         ButtonGroupItemData(
           tooltip: 'Resume',
           icon: Codicons.debugContinue,
           // Enable while paused + not resuming and selected isolate is not
           // a system isolate.
-          onPressed: ((isPaused && !resuming) && !isSystemIsolate)
-              ? () => unawaited(controller.resume())
-              : null,
+          onPressed:
+              ((isPaused && !resuming) && !isSystemIsolate)
+                  ? () => unawaited(controller.resume())
+                  : null,
         ),
       ],
     );
@@ -139,9 +145,10 @@
           label: 'File Explorer',
           onPressed: controller.codeViewController.toggleLibrariesVisible,
           gaScreen: gac.debugger,
-          gaSelection: visible
-              ? gac.DebuggerEvents.hideFileExplorer.name
-              : gac.DebuggerEvents.showFileExplorer.name,
+          gaSelection:
+              visible
+                  ? gac.DebuggerEvents.hideFileExplorer.name
+                  : gac.DebuggerEvents.showFileExplorer.name,
           minScreenWidthForTextBeforeScaling:
               DebuggingControls.minWidthBeforeScaling,
         );
@@ -151,10 +158,7 @@
 }
 
 class CodeStatisticsControls extends StatelessWidget {
-  const CodeStatisticsControls({
-    super.key,
-    required this.controller,
-  });
+  const CodeStatisticsControls({super.key, required this.controller});
 
   final DebuggerController controller;
 
@@ -198,11 +202,12 @@
               tooltip: 'Refresh statistics',
               gaScreen: gac.debugger,
               gaSelection: gac.DebuggerEvents.refreshStatistics.name,
-              onPressed: showCodeCoverage || showProfileInformation
-                  ? () => unawaited(
+              onPressed:
+                  showCodeCoverage || showProfileInformation
+                      ? () => unawaited(
                         controller.codeViewController.refreshCodeStatistics(),
                       )
-                  : null,
+                      : null,
             ),
           ],
         );
@@ -212,10 +217,7 @@
 }
 
 class _CodeStatsControl extends StatelessWidget {
-  const _CodeStatsControl({
-    required this.icon,
-    required this.tooltip,
-  });
+  const _CodeStatsControl({required this.icon, required this.tooltip});
 
   final IconData icon;
   final String tooltip;
@@ -226,26 +228,21 @@
       message: tooltip,
       child: Container(
         padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
-        child: Icon(
-          icon,
-          size: defaultIconSize,
-        ),
+        child: Icon(icon, size: defaultIconSize),
       ),
     );
   }
 }
 
 class BreakOnExceptionsControl extends StatelessWidget {
-  const BreakOnExceptionsControl({
-    super.key,
-    required this.controller,
-  });
+  const BreakOnExceptionsControl({super.key, required this.controller});
 
   final DebuggerController controller;
 
   @override
   Widget build(BuildContext context) {
-    final isInSmallMode = MediaQuery.of(context).size.width <
+    final isInSmallMode =
+        MediaQuery.of(context).size.width <
         DebuggingControls.minWidthBeforeScaling;
     return ValueListenableBuilder<String?>(
       valueListenable: controller.exceptionPauseMode,
@@ -253,19 +250,18 @@
         return RoundedDropDownButton<ExceptionMode>(
           value: ExceptionMode.from(modeId),
           // Cannot set exception pause mode for system isolates.
-          onChanged: controller.isSystemIsolate
-              ? null
-              : (ExceptionMode? mode) {
-                  unawaited(controller.setIsolatePauseMode(mode!.id));
-                },
+          onChanged:
+              controller.isSystemIsolate
+                  ? null
+                  : (ExceptionMode? mode) {
+                    unawaited(controller.setIsolatePauseMode(mode!.id));
+                  },
           isDense: true,
           items: [
             for (final mode in ExceptionMode.modes)
               DropdownMenuItem<ExceptionMode>(
                 value: mode,
-                child: Text(
-                  isInSmallMode ? mode.name : mode.description,
-                ),
+                child: Text(isInSmallMode ? mode.name : mode.description),
               ),
           ],
         );
diff --git a/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart b/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
index ddbb47c..efd69a9 100644
--- a/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
@@ -20,10 +20,10 @@
 import '../../shared/diagnostics/primitives/source_location.dart';
 import '../../shared/diagnostics/tree_builder.dart';
 import '../../shared/feature_flags.dart';
+import '../../shared/framework/routing.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/message_bus.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/routing.dart';
 import 'codeview_controller.dart';
 import 'debugger_model.dart';
 
@@ -148,7 +148,7 @@
       ValueNotifier<List<StackFrameAndSourcePosition>>([]);
 
   ValueListenable<List<StackFrameAndSourcePosition>>
-      get stackFramesWithLocation => _stackFramesWithLocation;
+  get stackFramesWithLocation => _stackFramesWithLocation;
 
   final _selectedStackFrame = ValueNotifier<StackFrameAndSourcePosition?>(null);
 
@@ -160,8 +160,9 @@
   ValueListenable<BreakpointAndSourcePosition?> get selectedBreakpoint =>
       _selectedBreakpoint;
 
-  final _exceptionPauseMode =
-      ValueNotifier<String>(ExceptionPauseMode.kUnhandled);
+  final _exceptionPauseMode = ValueNotifier<String>(
+    ExceptionPauseMode.kUnhandled,
+  );
 
   ValueListenable<String?> get exceptionPauseMode => _exceptionPauseMode;
 
@@ -235,9 +236,10 @@
     return _service
         .resume(
           _isolateRefId,
-          step: useAsyncStepping
-              ? StepOption.kOverAsyncSuspension
-              : StepOption.kOver,
+          step:
+              useAsyncStepping
+                  ? StepOption.kOverAsyncSuspension
+                  : StepOption.kOver,
         )
         .whenComplete(() => _debugTimingLog.log('stepOver() completed'));
   }
@@ -255,10 +257,7 @@
   }
 
   Future<void> setIsolatePauseMode(String mode) async {
-    await _service.setIsolatePauseMode(
-      _isolateRefId,
-      exceptionPauseMode: mode,
-    );
+    await _service.setIsolatePauseMode(_isolateRefId, exceptionPauseMode: mode);
     _exceptionPauseMode.value = mode;
   }
 
@@ -326,12 +325,15 @@
     if (theIsolateRef == null) return;
     // Refresh the list of scripts.
     final previousScriptRefs = scriptManager.sortedScripts.value;
-    final currentScriptRefs =
-        await scriptManager.retrieveAndSortScripts(theIsolateRef);
-    final removedScripts =
-        Set.of(previousScriptRefs).difference(Set.of(currentScriptRefs));
-    final addedScripts =
-        Set.of(currentScriptRefs).difference(Set.of(previousScriptRefs));
+    final currentScriptRefs = await scriptManager.retrieveAndSortScripts(
+      theIsolateRef,
+    );
+    final removedScripts = Set.of(
+      previousScriptRefs,
+    ).difference(Set.of(currentScriptRefs));
+    final addedScripts = Set.of(
+      currentScriptRefs,
+    ).difference(Set.of(previousScriptRefs));
 
     // TODO(devoncarew): Show a message in the logging view.
 
@@ -347,8 +349,9 @@
     // Redirect the current editor screen if necessary.
     if (removedScripts.contains(codeViewController.currentScriptRef.value)) {
       final uri = codeViewController.currentScriptRef.value!.uri;
-      final newScriptRef =
-          addedScripts.firstWhereOrNull((script) => script.uri == uri);
+      final newScriptRef = addedScripts.firstWhereOrNull(
+        (script) => script.uri == uri,
+      );
 
       if (newScriptRef != null) {
         // Display the script location.
@@ -399,29 +402,23 @@
         _log.warning(
           'Pause event has no frame. This likely indicates a DWDS bug.',
         );
-        await _populateFrameInfo(
-          [
-            await _createStackFrameWithLocation(
-              Frame(
-                code: CodeRef(
-                  name: 'No Dart frames found, likely paused in JS.',
-                  kind: CodeKind.kTag,
-                  id: DateTime.now().microsecondsSinceEpoch.toString(),
-                ),
+        await _populateFrameInfo([
+          await _createStackFrameWithLocation(
+            Frame(
+              code: CodeRef(
+                name: 'No Dart frames found, likely paused in JS.',
+                kind: CodeKind.kTag,
+                id: DateTime.now().microsecondsSinceEpoch.toString(),
               ),
             ),
-          ],
-          truncated: true,
-        );
+          ),
+        ], truncated: true);
         ga.select(gac.debugger, gac.DebuggerEvents.pausedWithNoFrames.name);
         return;
       }
-      await _populateFrameInfo(
-        [
-          await _createStackFrameWithLocation(topFrame),
-        ],
-        truncated: true,
-      );
+      await _populateFrameInfo([
+        await _createStackFrameWithLocation(topFrame),
+      ], truncated: true);
       unawaited(_getFullStack());
       return;
     }
@@ -431,15 +428,10 @@
     const initialFrameRequestCount = 12;
 
     _getStackOperation = CancelableOperation.fromFuture(
-      _getStackInfo(
-        limit: initialFrameRequestCount,
-      ),
+      _getStackInfo(limit: initialFrameRequestCount),
     );
     final stackInfo = await _getStackOperation!.value;
-    await _populateFrameInfo(
-      stackInfo.frames,
-      truncated: stackInfo.truncated,
-    );
+    await _populateFrameInfo(stackInfo.frames, truncated: stackInfo.truncated);
 
     // In the background, populate the rest of the frames.
     if (stackInfo.truncated) {
@@ -450,8 +442,9 @@
   Future<_StackInfo> _getStackInfo({int? limit}) async {
     _debugTimingLog.log('getStack() with limit: $limit');
     final stack = await _service.getStack(_isolateRefId, limit: limit);
-    _debugTimingLog
-        .log('getStack() completed (frames: ${stack.frames!.length})');
+    _debugTimingLog.log(
+      'getStack() completed (frames: ${stack.frames!.length})',
+    );
 
     final frames = _framesForCallStack(
       stack.frames ?? [],
@@ -494,8 +487,9 @@
   Future<void> _populateScripts(Isolate isolate) async {
     final theIsolateRef = _isolate.value;
     if (theIsolateRef == null) return;
-    final scriptRefs =
-        await scriptManager.retrieveAndSortScripts(theIsolateRef);
+    final scriptRefs = await scriptManager.retrieveAndSortScripts(
+      theIsolateRef,
+    );
 
     // Update the selected script.
     final mainScriptRef = scriptRefs.firstWhereOrNull((ref) {
@@ -566,14 +560,10 @@
       return [];
     }
 
-    final variables = frame.vars!
-        .map(
-          (v) => DartObjectNode.create(
-            v,
-            _isolate.value,
-          ),
-        )
-        .toList();
+    final variables =
+        frame.vars!
+            .map((v) => DartObjectNode.create(v, _isolate.value))
+            .toList();
     // TODO(jacobr): would be nice to be able to remove this call to unawaited
     // but it would require a significant refactor.
     variables
@@ -606,8 +596,13 @@
   }
 
   Future<dap.StackFrame?> _fetchDapFrame(Frame vmFrame) async {
-    final isolateNumber = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value?.number;
+    final isolateNumber =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value
+            ?.number;
     final frameIndex = vmFrame.index;
     if (isolateNumber == null || frameIndex == null) return null;
 
@@ -625,18 +620,14 @@
 
   Future<List<dap.Scope>> _fetchDapScopes(int frameId) async {
     final scopesResponse = await _service.dapScopesRequest(
-      dap.ScopesArguments(
-        frameId: frameId,
-      ),
+      dap.ScopesArguments(frameId: frameId),
     );
     return scopesResponse?.scopes ?? [];
   }
 
   Future<List<dap.Variable>> _fetchDapVariables(int variablesReference) async {
     final variablesResponse = await _service.dapVariablesRequest(
-      dap.VariablesArguments(
-        variablesReference: variablesReference,
-      ),
+      dap.VariablesArguments(variablesReference: variablesReference),
     );
     return variablesResponse?.variables ?? [];
   }
@@ -665,10 +656,7 @@
       );
 
       newFrame.vars = [
-        BoundVariable(
-          name: '<exception>',
-          value: reportedException,
-        ),
+        BoundVariable(name: '<exception>', value: reportedException),
         ...frame.vars ?? [],
       ];
 
diff --git a/packages/devtools_app/lib/src/screens/debugger/debugger_model.dart b/packages/devtools_app/lib/src/screens/debugger/debugger_model.dart
index e7c4994..5509726 100644
--- a/packages/devtools_app/lib/src/screens/debugger/debugger_model.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/debugger_model.dart
@@ -24,10 +24,7 @@
 
 /// A tuple of a script and an optional location.
 class ScriptLocation {
-  ScriptLocation(
-    this.scriptRef, {
-    this.location,
-  });
+  ScriptLocation(this.scriptRef, {this.location});
 
   final ScriptRef scriptRef;
 
@@ -186,10 +183,7 @@
 
 /// A tuple of a stack frame and a source position.
 class StackFrameAndSourcePosition {
-  StackFrameAndSourcePosition(
-    this.frame, {
-    this.position,
-  });
+  StackFrameAndSourcePosition(this.frame, {this.position});
 
   final Frame frame;
 
diff --git a/packages/devtools_app/lib/src/screens/debugger/debugger_screen.dart b/packages/devtools_app/lib/src/screens/debugger/debugger_screen.dart
index 75b6e85..7ef7814 100644
--- a/packages/devtools_app/lib/src/screens/debugger/debugger_screen.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/debugger_screen.dart
@@ -16,15 +16,15 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/banner_messages.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/diagnostics/primitives/source_location.dart';
+import '../../shared/framework/routing.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/banner_messages.dart';
 import '../../shared/primitives/listenable.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/routing.dart';
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import 'breakpoints.dart';
 import 'call_stack.dart';
 import 'codeview.dart';
@@ -39,10 +39,10 @@
 
 class DebuggerScreen extends Screen {
   DebuggerScreen()
-      : super.fromMetaData(
-          ScreenMetaData.debugger,
-          showFloatingDebuggerControls: false,
-        );
+    : super.fromMetaData(
+        ScreenMetaData.debugger,
+        showFloatingDebuggerControls: false,
+      );
 
   static final id = ScreenMetaData.debugger.id;
 
@@ -98,8 +98,10 @@
 class _DebuggerScreenBodyWrapperState extends State<_DebuggerScreenBodyWrapper>
     with
         AutoDisposeMixin,
-        ProvidedControllerMixin<DebuggerController,
-            _DebuggerScreenBodyWrapper> {
+        ProvidedControllerMixin<
+          DebuggerController,
+          _DebuggerScreenBodyWrapper
+        > {
   late bool _shownFirstScript;
 
   @override
@@ -158,10 +160,7 @@
       axis: Axis.horizontal,
       initialFractions: const [0.25, 0.75],
       children: [
-        const RoundedOutlinedBorder(
-          clip: true,
-          child: DebuggerWindows(),
-        ),
+        const RoundedOutlinedBorder(clip: true, child: DebuggerWindows()),
         DebuggerSourceAndControls(
           shownFirstScript: shownFirstScript,
           setShownFirstScript: setShownFirstScript,
@@ -195,10 +194,10 @@
               actions: [
                 CopyToClipboardControl(
                   dataProvider: () {
-                    final callStackList = controller
-                        .stackFramesWithLocation.value
-                        .map((frame) => frame.callStackDisplay)
-                        .toList();
+                    final callStackList =
+                        controller.stackFramesWithLocation.value
+                            .map((frame) => frame.callStackDisplay)
+                            .toList();
                     for (var i = 0; i < callStackList.length; i++) {
                       callStackList[i] = '#$i ${callStackList[i]}';
                     }
@@ -218,11 +217,7 @@
               roundedTopBorder: false,
             ),
           ],
-          children: const [
-            CallStack(),
-            Variables(),
-            Breakpoints(),
-          ],
+          children: const [CallStack(), Variables(), Breakpoints()],
         );
       },
     );
@@ -245,9 +240,10 @@
               child: ToolbarAction(
                 icon: Icons.delete,
                 size: defaultIconSize,
-                onPressed: breakpoints.isNotEmpty
-                    ? () => unawaited(breakpointManager.clearBreakpoints())
-                    : null,
+                onPressed:
+                    breakpoints.isNotEmpty
+                        ? () => unawaited(breakpointManager.clearBreakpoints())
+                        : null,
               ),
             ),
           ],
@@ -294,8 +290,8 @@
                       child: ProgramExplorer(
                         controller:
                             codeViewController.programExplorerController,
-                        onNodeSelected: (node) =>
-                            _onNodeSelected(context, node),
+                        onNodeSelected:
+                            (node) => _onNodeSelected(context, node),
                       ),
                     ),
                   ],
@@ -315,10 +311,7 @@
                 if (scriptRef != null &&
                     parsedScript != null &&
                     !shownFirstScript()) {
-                  ga.timeEnd(
-                    DebuggerScreen.id,
-                    gac.pageReady,
-                  );
+                  ga.timeEnd(DebuggerScreen.id, gac.pageReady);
                   unawaited(
                     serviceConnection.sendDwdsEvent(
                       screen: DebuggerScreen.id,
@@ -333,9 +326,10 @@
                   debuggerController: controller,
                   scriptRef: scriptRef,
                   parsedScript: parsedScript,
-                  onSelected: (script, line) => unawaited(
-                    breakpointManager.toggleBreakpoint(script, line),
-                  ),
+                  onSelected:
+                      (script, line) => unawaited(
+                        breakpointManager.toggleBreakpoint(script, line),
+                      ),
                 );
               },
             ),
@@ -372,10 +366,7 @@
 class GoToLineNumberAction extends Action<GoToLineNumberIntent> {
   @override
   void invoke(GoToLineNumberIntent intent) {
-    showGoToLineDialog(
-      intent._context,
-      intent._controller.codeViewController,
-    );
+    showGoToLineDialog(intent._context, intent._controller.codeViewController);
     intent._controller.codeViewController
       ..toggleFileOpenerVisibility(false)
       ..toggleSearchInFileVisibility(false);
@@ -428,10 +419,7 @@
 }
 
 class DebuggerStatus extends StatefulWidget {
-  const DebuggerStatus({
-    super.key,
-    required this.controller,
-  });
+  const DebuggerStatus({super.key, required this.controller});
 
   final DebuggerController controller;
 
@@ -465,20 +453,17 @@
   void _updateStatusOnPause() {
     addAutoDisposeListener(
       serviceConnection
-          .serviceManager.isolateManager.mainIsolateState?.isPaused,
-      () => unawaited(
-        _updateStatus(),
-      ),
+          .serviceManager
+          .isolateManager
+          .mainIsolateState
+          ?.isPaused,
+      () => unawaited(_updateStatus()),
     );
   }
 
   @override
   Widget build(BuildContext context) {
-    return Text(
-      _status,
-      maxLines: 1,
-      overflow: TextOverflow.ellipsis,
-    );
+    return Text(_status, maxLines: 1, overflow: TextOverflow.ellipsis);
   }
 
   Future<void> _updateStatus() async {
@@ -554,7 +539,10 @@
     controlHeight = _isPaused ? defaultButtonHeight : 0.0;
     addAutoDisposeListener(
       serviceConnection
-          .serviceManager.isolateManager.mainIsolateState?.isPaused,
+          .serviceManager
+          .isolateManager
+          .mainIsolateState
+          ?.isPaused,
       () {
         setState(() {
           if (_isPaused) {
@@ -588,9 +576,7 @@
             Container(
               height: defaultButtonHeight,
               alignment: Alignment.centerLeft,
-              padding: const EdgeInsets.symmetric(
-                horizontal: defaultSpacing,
-              ),
+              padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
               child: Text(
                 'Main isolate is paused in the debugger',
                 style: TextStyle(color: colorScheme.onWarningContainer),
diff --git a/packages/devtools_app/lib/src/screens/debugger/file_search.dart b/packages/devtools_app/lib/src/screens/debugger/file_search.dart
index efb1ac7..9d8b81d 100644
--- a/packages/devtools_app/lib/src/screens/debugger/file_search.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/file_search.dart
@@ -21,10 +21,7 @@
 final _fileNamesCache = <String, String>{};
 
 class FileSearchField extends StatefulWidget {
-  const FileSearchField({
-    super.key,
-    required this.codeViewController,
-  });
+  const FileSearchField({super.key, required this.codeViewController});
 
   final CodeViewController codeViewController;
 
@@ -98,9 +95,10 @@
 
     // If the current query is a continuation of the previous query, then
     // filter down the previous matches. Otherwise search through all scripts:
-    final scripts = currentQuery.startsWith(previousQuery)
-        ? _searchResults.scriptRefs
-        : scriptManager.sortedScripts.value;
+    final scripts =
+        currentQuery.startsWith(previousQuery)
+            ? _searchResults.scriptRefs
+            : scriptManager.sortedScripts.value;
 
     final searchResults = _createSearchResults(currentQuery, scripts);
     if (searchResults.scriptRefs.isEmpty) {
@@ -137,8 +135,9 @@
       return;
     }
     final scriptRef = _scriptsCache[scriptUri]!;
-    await widget.codeViewController
-        .showScriptLocation(ScriptLocation(scriptRef));
+    await widget.codeViewController.showScriptLocation(
+      ScriptLocation(scriptRef),
+    );
     _onClose();
   }
 
@@ -196,12 +195,13 @@
 
     final fileName = _fileName(scriptUri);
     final fileNameIndex = scriptUri.lastIndexOf(fileName);
-    final matchedSegments = _findExactSegments(fileName)
-        .map(
-          (range) =>
-              Range(range.begin + fileNameIndex, range.end + fileNameIndex),
-        )
-        .toList();
+    final matchedSegments =
+        _findExactSegments(fileName)
+            .map(
+              (range) =>
+                  Range(range.begin + fileNameIndex, range.end + fileNameIndex),
+            )
+            .toList();
     return AutoCompleteMatch(scriptUri, matchedSegments: matchedSegments);
   }
 
@@ -241,17 +241,22 @@
 
     List<Range> matchedSegments;
     if (isMultiToken) {
-      matchedSegments =
-          _findFuzzySegments(scriptUri, query.replaceAll(' ', ''));
+      matchedSegments = _findFuzzySegments(
+        scriptUri,
+        query.replaceAll(' ', ''),
+      );
     } else {
       final fileName = _fileName(scriptUri);
       final fileNameIndex = scriptUri.lastIndexOf(fileName);
-      matchedSegments = _findFuzzySegments(fileName, query)
-          .map(
-            (range) =>
-                Range(range.begin + fileNameIndex, range.end + fileNameIndex),
-          )
-          .toList();
+      matchedSegments =
+          _findFuzzySegments(fileName, query)
+              .map(
+                (range) => Range(
+                  range.begin + fileNameIndex,
+                  range.end + fileNameIndex,
+                ),
+              )
+              .toList();
     }
 
     return AutoCompleteMatch(scriptUri, matchedSegments: matchedSegments);
@@ -264,8 +269,9 @@
       final end = start + token.length;
       matchedSegments.add(Range(start, end));
     }
-    matchedSegments
-        .sort((rangeA, rangeB) => rangeA.begin.compareTo(rangeB.begin));
+    matchedSegments.sort(
+      (rangeA, rangeB) => rangeA.begin.compareTo(rangeB.begin),
+    );
 
     return matchedSegments;
   }
@@ -335,9 +341,9 @@
     required List<ScriptRef> exactFileNameMatches,
     required List<ScriptRef> exactFullPathMatches,
     required List<ScriptRef> fuzzyMatches,
-  })  : _exactFileNameMatches = exactFileNameMatches,
-        _exactFullPathMatches = exactFullPathMatches,
-        _fuzzyMatches = fuzzyMatches;
+  }) : _exactFileNameMatches = exactFileNameMatches,
+       _exactFullPathMatches = exactFullPathMatches,
+       _fuzzyMatches = fuzzyMatches;
 
   final List<ScriptRef> allScripts;
   final FileQuery query;
@@ -347,23 +353,27 @@
 
   FileSearchResults get topMatches => _buildTopMatches();
 
-  List<ScriptRef> get scriptRefs => query.isEmpty
-      ? allScripts
-      : [
-          ..._exactFileNameMatches,
-          ..._exactFullPathMatches,
-          ..._fuzzyMatches,
-        ];
+  List<ScriptRef> get scriptRefs =>
+      query.isEmpty
+          ? allScripts
+          : [
+            ..._exactFileNameMatches,
+            ..._exactFullPathMatches,
+            ..._fuzzyMatches,
+          ];
 
-  List<AutoCompleteMatch> get autoCompleteMatches => query.isEmpty
-      ? allScripts.map((script) => AutoCompleteMatch(script.uri!)).toList()
-      : [
-          ..._exactFileNameMatches
-              .map(query.createExactFileNameAutoCompleteMatch),
-          ..._exactFullPathMatches
-              .map(query.createExactFullPathAutoCompleteMatch),
-          ..._fuzzyMatches.map(query.createFuzzyMatchAutoCompleteMatch),
-        ];
+  List<AutoCompleteMatch> get autoCompleteMatches =>
+      query.isEmpty
+          ? allScripts.map((script) => AutoCompleteMatch(script.uri!)).toList()
+          : [
+            ..._exactFileNameMatches.map(
+              query.createExactFileNameAutoCompleteMatch,
+            ),
+            ..._exactFullPathMatches.map(
+              query.createExactFullPathAutoCompleteMatch,
+            ),
+            ..._fuzzyMatches.map(query.createFuzzyMatchAutoCompleteMatch),
+          ];
 
   FileSearchResults copyWith({
     List<ScriptRef>? allScripts,
@@ -383,9 +393,7 @@
 
   FileSearchResults _buildTopMatches() {
     if (query.isEmpty) {
-      return copyWith(
-        allScripts: allScripts.sublist(0, numOfMatchesToShow),
-      );
+      return copyWith(allScripts: allScripts.sublist(0, numOfMatchesToShow));
     }
 
     if (scriptRefs.length <= numOfMatchesToShow) {
diff --git a/packages/devtools_app/lib/src/screens/debugger/key_sets.dart b/packages/devtools_app/lib/src/screens/debugger/key_sets.dart
index fa11d8f..59e9db1 100644
--- a/packages/devtools_app/lib/src/screens/debugger/key_sets.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/key_sets.dart
@@ -15,8 +15,9 @@
   LogicalKeyboardKey.keyG,
 );
 
-final goToLineNumberKeySetDescription =
-    goToLineNumberKeySet.describeKeys(isMacOS: HostPlatform.instance.isMacOS);
+final goToLineNumberKeySetDescription = goToLineNumberKeySet.describeKeys(
+  isMacOS: HostPlatform.instance.isMacOS,
+);
 
 final searchInFileKeySet = LogicalKeySet(
   HostPlatform.instance.isMacOS
@@ -25,9 +26,7 @@
   LogicalKeyboardKey.keyF,
 );
 
-final escapeKeySet = LogicalKeySet(
-  LogicalKeyboardKey.escape,
-);
+final escapeKeySet = LogicalKeySet(LogicalKeyboardKey.escape);
 
 final openFileKeySet = LogicalKeySet(
   HostPlatform.instance.isMacOS
@@ -36,5 +35,6 @@
   LogicalKeyboardKey.keyP,
 );
 
-final openFileKeySetDescription =
-    openFileKeySet.describeKeys(isMacOS: HostPlatform.instance.isMacOS);
+final openFileKeySetDescription = openFileKeySet.describeKeys(
+  isMacOS: HostPlatform.instance.isMacOS,
+);
diff --git a/packages/devtools_app/lib/src/screens/debugger/program_explorer.dart b/packages/devtools_app/lib/src/screens/debugger/program_explorer.dart
index 9c5bc77..3074b18 100644
--- a/packages/devtools_app/lib/src/screens/debugger/program_explorer.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/program_explorer.dart
@@ -10,11 +10,11 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart' hide Stack;
 
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/tree.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/tree_view.dart';
 import 'program_explorer_controller.dart';
 import 'program_explorer_model.dart';
 
@@ -24,10 +24,7 @@
 double get _selectedNodeTopSpacing => defaultTreeViewRowHeight * 3;
 
 class _ProgramExplorerRow extends StatelessWidget {
-  const _ProgramExplorerRow({
-    required this.node,
-    this.onTap,
-  });
+  const _ProgramExplorerRow({required this.node, this.onTap});
 
   final VMServiceObjectNode node;
   final VoidCallback? onTap;
@@ -52,9 +49,7 @@
         onTap: onTap,
         child: Row(
           children: [
-            ProgramStructureIcon(
-              object: node.object,
-            ),
+            ProgramStructureIcon(object: node.object),
             const SizedBox(width: densePadding),
             Flexible(
               child: Text(
@@ -202,10 +197,7 @@
 }
 
 class ProgramStructureIcon extends StatelessWidget {
-  const ProgramStructureIcon({
-    super.key,
-    required this.object,
-  });
+  const ProgramStructureIcon({super.key, required this.object});
 
   final ObjRef? object;
 
@@ -251,40 +243,35 @@
       height: defaultIconSize,
       width: defaultIconSize,
       child: Container(
-        decoration: icon == null
-            ? BoxDecoration(
-                color: color,
-                shape: BoxShape.circle,
-              )
-            : null,
-        child: icon == null
-            ? Center(
-                child: Text(
-                  character!,
-                  style: TextStyle(
-                    height: 1,
-                    fontFamily: theme.fixedFontStyle.fontFamily,
-                    color: theme.colorScheme.defaultBackgroundColor,
-                    fontSize: smallFontSize,
+        decoration:
+            icon == null
+                ? BoxDecoration(color: color, shape: BoxShape.circle)
+                : null,
+        child:
+            icon == null
+                ? Center(
+                  child: Text(
+                    character!,
+                    style: TextStyle(
+                      height: 1,
+                      fontFamily: theme.fixedFontStyle.fontFamily,
+                      color: theme.colorScheme.defaultBackgroundColor,
+                      fontSize: smallFontSize,
+                    ),
+                    // Required to center the individual character within the
+                    // shape. Since letters like 'm' are shorter than letters
+                    // like 'f', there's padding applied to the top of shorter
+                    // characters in order for everything to align properly.
+                    // Since we're only dealing with individual characters, we
+                    // want to disable this behavior so shorter characters don't
+                    // appear to be slightly below center.
+                    textHeightBehavior: TextHeightBehavior(
+                      applyHeightToFirstAscent: isShortCharacter!,
+                      applyHeightToLastDescent: false,
+                    ),
                   ),
-                  // Required to center the individual character within the
-                  // shape. Since letters like 'm' are shorter than letters
-                  // like 'f', there's padding applied to the top of shorter
-                  // characters in order for everything to align properly.
-                  // Since we're only dealing with individual characters, we
-                  // want to disable this behavior so shorter characters don't
-                  // appear to be slightly below center.
-                  textHeightBehavior: TextHeightBehavior(
-                    applyHeightToFirstAscent: isShortCharacter!,
-                    applyHeightToLastDescent: false,
-                  ),
-                ),
-              )
-            : Icon(
-                icon,
-                size: defaultIconSize,
-                color: color,
-              ),
+                )
+                : Icon(icon, size: defaultIconSize, color: color),
       ),
     );
   }
@@ -308,10 +295,11 @@
 class _FileExplorerState extends State<_FileExplorer> with AutoDisposeMixin {
   late final ScrollController _scrollController;
 
-  double get selectedNodeOffset => widget.controller.selectedNodeIndex.value ==
-          -1
-      ? -1
-      : widget.controller.selectedNodeIndex.value * defaultTreeViewRowHeight;
+  double get selectedNodeOffset =>
+      widget.controller.selectedNodeIndex.value == -1
+          ? -1
+          : widget.controller.selectedNodeIndex.value *
+              defaultTreeViewRowHeight;
 
   @override
   void initState() {
@@ -402,9 +390,8 @@
               },
             );
           },
-          emptyTreeViewBuilder: () => const Center(
-            child: Text('Nothing to inspect'),
-          ),
+          emptyTreeViewBuilder:
+              () => const Center(child: Text('Nothing to inspect')),
         );
       },
     );
@@ -436,13 +423,14 @@
         if (!initialized) {
           body = const CenteredCircularProgressIndicator();
         } else {
-          final fileExplorerHeader = displayHeader
-              ? AreaPaneHeader(
-                  title: Text(title),
-                  includeTopBorder: false,
-                  roundedTopBorder: false,
-                )
-              : const BlankHeader();
+          final fileExplorerHeader =
+              displayHeader
+                  ? AreaPaneHeader(
+                    title: Text(title),
+                    includeTopBorder: false,
+                    roundedTopBorder: false,
+                  )
+                  : const BlankHeader();
           final fileExplorer = _FileExplorer(
             controller: controller,
             onItemExpanded: onItemExpanded,
@@ -460,33 +448,35 @@
               //
               // See https://github.com/flutter/devtools/issues/3447.
               return serviceConnection
-                      .serviceManager.connectedApp!.isDartWebAppNow!
+                      .serviceManager
+                      .connectedApp!
+                      .isDartWebAppNow!
                   ? Column(
-                      children: [
-                        fileExplorerHeader,
-                        Expanded(child: fileExplorer),
-                      ],
-                    )
+                    children: [
+                      fileExplorerHeader,
+                      Expanded(child: fileExplorer),
+                    ],
+                  )
                   : FlexSplitColumn(
-                      totalHeight: constraints.maxHeight,
-                      initialFractions: const [0.7, 0.3],
-                      minSizes: const [0.0, 0.0],
-                      headers: <PreferredSizeWidget>[
-                        fileExplorerHeader as PreferredSizeWidget,
-                        const AreaPaneHeader(
-                          title: Text('Outline'),
-                          roundedTopBorder: false,
-                        ),
-                      ],
-                      children: [
-                        fileExplorer,
-                        _ProgramOutlineView(
-                          controller: controller,
-                          onItemExpanded: onItemExpanded,
-                          onItemSelected: onItemSelected,
-                        ),
-                      ],
-                    );
+                    totalHeight: constraints.maxHeight,
+                    initialFractions: const [0.7, 0.3],
+                    minSizes: const [0.0, 0.0],
+                    headers: <PreferredSizeWidget>[
+                      fileExplorerHeader as PreferredSizeWidget,
+                      const AreaPaneHeader(
+                        title: Text('Outline'),
+                        roundedTopBorder: false,
+                      ),
+                    ],
+                    children: [
+                      fileExplorer,
+                      _ProgramOutlineView(
+                        controller: controller,
+                        onItemExpanded: onItemExpanded,
+                        onItemSelected: onItemSelected,
+                      ),
+                    ],
+                  );
             },
           );
         }
diff --git a/packages/devtools_app/lib/src/screens/debugger/program_explorer_controller.dart b/packages/devtools_app/lib/src/screens/debugger/program_explorer_controller.dart
index b70f789..99f0ab2 100644
--- a/packages/devtools_app/lib/src/screens/debugger/program_explorer_controller.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/program_explorer_controller.dart
@@ -17,9 +17,7 @@
     with AutoDisposeControllerMixin {
   /// [showCodeNodes] controls whether or not [Code] nodes are displayed in the
   /// outline view.
-  ProgramExplorerController({
-    this.showCodeNodes = false,
-  });
+  ProgramExplorerController({this.showCodeNodes = false});
 
   /// The outline view nodes for the currently selected library.
   ValueListenable<List<VMServiceObjectNode>> get outlineNodes => _outlineNodes;
@@ -73,22 +71,20 @@
 
     final isolate =
         serviceConnection.serviceManager.isolateManager.selectedIsolate.value;
-    final libraries = isolate != null
-        ? serviceConnection.serviceManager.isolateManager
-            .isolateState(isolate)
-            .isolateNow!
-            .libraries!
-        : <LibraryRef>[];
+    final libraries =
+        isolate != null
+            ? serviceConnection.serviceManager.isolateManager
+                .isolateState(isolate)
+                .isolateNow!
+                .libraries!
+            : <LibraryRef>[];
 
     if (scriptManager.sortedScripts.value.isEmpty && isolate != null) {
       await scriptManager.retrieveAndSortScripts(isolate);
     }
 
     // Build the initial tree.
-    final nodes = VMServiceObjectNode.createRootsFrom(
-      this,
-      libraries,
-    );
+    final nodes = VMServiceObjectNode.createRootsFrom(this, libraries);
     rootObjectNodesInternal.replaceAll(nodes);
     _initialized.value = true;
   }
@@ -97,10 +93,7 @@
     // Re-initialize after reload.
     // TODO(elliette): If file was opened from before the reload, we should try
     // to open that one instead of the entrypoint file.
-    addAutoDisposeListener(
-      scriptManager.sortedScripts,
-      refresh,
-    );
+    addAutoDisposeListener(scriptManager.sortedScripts, refresh);
   }
 
   Future<void> selectScriptNode(ScriptRef? script) async {
@@ -150,9 +143,10 @@
       final matchingNode = depthFirstTraversal(
         node,
         returnCondition: matchingNodeCondition,
-        exploreChildrenCondition: includeCollapsedNodes
-            ? null
-            : (VMServiceObjectNode node) => node.isExpanded,
+        exploreChildrenCondition:
+            includeCollapsedNodes
+                ? null
+                : (VMServiceObjectNode node) => node.isExpanded,
         action: (VMServiceObjectNode _) => index++,
       );
       if (matchingNode != null) return index;
@@ -251,14 +245,17 @@
   Future<void> populateNode(VMServiceObjectNode node) async {
     final object = node.object;
     final service = serviceConnection.serviceManager.service;
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id;
 
     Future<List<Obj>> getObjects(Iterable<ObjRef> objs) {
       return Future.wait(
-        objs.map(
-          (o) => service!.getObject(isolateId!, o.id!),
-        ),
+        objs.map((o) => service!.getObject(isolateId!, o.id!)),
       );
     }
 
@@ -285,10 +282,12 @@
                   // fetching it again.
                   if (func.unoptimizedCode != null &&
                       func.unoptimizedCode?.id! != code.id!) {
-                    unoptimizedCode = await service.getObject(
-                      isolateId,
-                      func.unoptimizedCode!.id!,
-                    ) as Code;
+                    unoptimizedCode =
+                        await service.getObject(
+                              isolateId,
+                              func.unoptimizedCode!.id!,
+                            )
+                            as Code;
                   }
                   func.unoptimizedCode = unoptimizedCode;
                 }
@@ -332,8 +331,13 @@
   /// which is the source location of the target [object].
   Future<VMServiceObjectNode> searchFileExplorer(ObjRef object) async {
     final service = serviceConnection.serviceManager.service!;
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
 
     // If `object` is a library, it will always be a root node and is simple to
     // find.
@@ -370,10 +374,7 @@
     // If we couldn't find the target library as a root node, it's possible we
     // have a library defined using the `library` keyword by the user, which
     // will likely be under a directory node.
-    libNode ??= breadthFirstSearchObject(
-      scriptObj,
-      rootObjectNodes.value,
-    );
+    libNode ??= breadthFirstSearchObject(scriptObj, rootObjectNodes.value);
 
     // If the object's owning script URI is the same as the target library URI,
     // return the library node as the match.
diff --git a/packages/devtools_app/lib/src/screens/debugger/program_explorer_model.dart b/packages/devtools_app/lib/src/screens/debugger/program_explorer_model.dart
index 2145314..282ddad 100644
--- a/packages/devtools_app/lib/src/screens/debugger/program_explorer_model.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/program_explorer_model.dart
@@ -60,11 +60,7 @@
       return _outline;
     }
 
-    final root = VMServiceObjectNode(
-      controller,
-      '<root>',
-      ObjRef(id: '0'),
-    );
+    final root = VMServiceObjectNode(controller, '<root>', ObjRef(id: '0'));
 
     String uri;
     Library lib;
@@ -84,15 +80,17 @@
       // script's URI.
       if (libNode == null) {
         final service = serviceConnection.serviceManager.service!;
-        final isolate = serviceConnection
-            .serviceManager.isolateManager.selectedIsolate.value!;
+        final isolate =
+            serviceConnection
+                .serviceManager
+                .isolateManager
+                .selectedIsolate
+                .value!;
         final libRef = serviceConnection.serviceManager.isolateManager
             .isolateState(isolate)
             .isolateNow!
             .libraries!
-            .firstWhere(
-              (lib) => script!.uri!.startsWith(lib.uri!),
-            );
+            .firstWhere((lib) => script!.uri!.startsWith(lib.uri!));
         lib = await service.getObject(isolate.id!, libRef.id!) as Library;
       } else {
         lib = libNode.object as Library;
@@ -118,11 +116,7 @@
     }
     for (final function in lib.functions!) {
       if (function.location?.script?.uri == uri) {
-        final node = VMServiceObjectNode(
-          controller,
-          function.name,
-          function,
-        );
+        final node = VMServiceObjectNode(controller, function.name, function);
         await controller.populateNode(node);
         _buildCodeNodes(node.object as Func, node);
         root.addChild(node);
@@ -131,11 +125,7 @@
 
     for (final field in lib.variables!) {
       if (field.location?.script?.uri == uri) {
-        final node = VMServiceObjectNode(
-          controller,
-          field.name,
-          field,
-        );
+        final node = VMServiceObjectNode(controller, field.name, field);
         await controller.populateNode(node);
         root.addChild(node);
       }
@@ -178,9 +168,10 @@
     // Clear out the _childrenAsMap map.
     root._trimChildrenAsMapEntries();
 
-    final processed = root.children
-        .map((e) => e._collapseSingleChildDirectoryNodes())
-        .toList();
+    final processed =
+        root.children
+            .map((e) => e._collapseSingleChildDirectoryNodes())
+            .toList();
     root.children.clear();
     root.addAllChildren(processed);
 
@@ -194,8 +185,14 @@
     // Place the root library's parent node at the top of the explorer if it's
     // part of a package. Otherwise, it's a file path and its directory should
     // appear near the top of the list anyway.
-    final rootLibUri = serviceConnection.serviceManager.isolateManager
-        .mainIsolateState?.isolateNow?.rootLib?.uri;
+    final rootLibUri =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .mainIsolateState
+            ?.isolateNow
+            ?.rootLib
+            ?.uri;
     if (rootLibUri != null) {
       if (rootLibUri.startsWith('package:') ||
           rootLibUri.startsWith('google3:')) {
@@ -224,11 +221,7 @@
 
     for (final part in parts) {
       // Directory nodes shouldn't be selectable unless they're a library node.
-      node = node._lookupOrCreateChild(
-        part,
-        null,
-        isSelectable: false,
-      );
+      node = node._lookupOrCreateChild(part, null, isSelectable: false);
     }
 
     node = node._lookupOrCreateChild(name, script);
@@ -251,13 +244,7 @@
     }
     final code = function.code;
     if (code != null) {
-      node.addChild(
-        VMServiceObjectNode(
-          controller,
-          code.name,
-          code,
-        ),
-      );
+      node.addChild(VMServiceObjectNode(controller, code.name, code));
       final unoptimizedCode = function.unoptimizedCode;
       // It's possible for `function.code` to be unoptimized code, so don't
       // create a duplicate node in that situation.
@@ -314,11 +301,8 @@
       }
       return this;
     }
-    final updated = children
-        .map(
-          (e) => e._collapseSingleChildDirectoryNodes(),
-        )
-        .toList();
+    final updated =
+        children.map((e) => e._collapseSingleChildDirectoryNodes()).toList();
     children.clear();
     addAllChildren(updated);
     return this;
@@ -361,14 +345,12 @@
 
     if (scriptRef != null) {
       final fetchedScript = await scriptManager.getScript(scriptRef);
-      final position = tokenPos == 0
-          ? null
-          : SourcePosition.calculatePosition(fetchedScript!, tokenPos!);
+      final position =
+          tokenPos == 0
+              ? null
+              : SourcePosition.calculatePosition(fetchedScript!, tokenPos!);
 
-      location = ScriptLocation(
-        scriptRef,
-        location: position,
-      );
+      location = ScriptLocation(scriptRef, location: position);
     }
   }
 
diff --git a/packages/devtools_app/lib/src/screens/debugger/span_parser.dart b/packages/devtools_app/lib/src/screens/debugger/span_parser.dart
index 0dd8fc5..4355d71 100644
--- a/packages/devtools_app/lib/src/screens/debugger/span_parser.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/span_parser.dart
@@ -17,8 +17,11 @@
     final scopeStack = ScopeStack();
     final scanner = LineScanner(src);
     while (!scanner.isDone) {
-      final foundMatch =
-          grammar.topLevelMatcher.scan(grammar, scanner, scopeStack);
+      final foundMatch = grammar.topLevelMatcher.scan(
+        grammar,
+        scanner,
+        scopeStack,
+      );
       if (!foundMatch && !scanner.isDone) {
         // Found no match, move forward by a character and try again.
         scanner.readChar();
@@ -80,8 +83,8 @@
     required this.scopes,
     required ScopeStackLocation startLocation,
     required ScopeStackLocation endLocation,
-  })  : _startLocation = startLocation,
-        _endLocation = endLocation;
+  }) : _startLocation = startLocation,
+       _endLocation = endLocation;
 
   ScopeStackLocation get startLocation => _startLocation;
   ScopeStackLocation get endLocation => _endLocation;
@@ -162,8 +165,9 @@
 /// using the 'includes' keyword.
 class Repository {
   Repository.build(Map<String, Object?> grammarJson) {
-    final repositoryJson = (grammarJson['repository'] as Map<String, Object?>?)
-        ?.cast<String, Map<String, Object?>>();
+    final repositoryJson =
+        (grammarJson['repository'] as Map<String, Object?>?)
+            ?.cast<String, Map<String, Object?>>();
     if (repositoryJson == null) {
       return;
     }
@@ -225,8 +229,9 @@
         final startOffset = match.indexOf(captureText);
         final capture = captures[i.toString()] as Map<String, Object?>;
         final captureStartLocation = matchStartLocation.offset(startOffset);
-        final captureEndLocation =
-            captureStartLocation.offset(captureText.length);
+        final captureEndLocation = captureStartLocation.offset(
+          captureText.length,
+        );
         final captureName = capture['name'] as String?;
 
         scopeStack.push(captureName, captureStartLocation);
@@ -237,8 +242,9 @@
             scanner.substring(0, captureEndLocation.position),
             position: captureStartLocation.position,
           );
-          GrammarMatcher.fromJson(capture)
-              .scan(grammar, captureScanner, scopeStack);
+          GrammarMatcher.fromJson(
+            capture,
+          ).scan(grammar, captureScanner, scopeStack);
         }
 
         scopeStack.pop(captureName, captureEndLocation);
@@ -252,10 +258,11 @@
 /// A simple matcher which matches a single line.
 class _SimpleMatcher extends GrammarMatcher {
   _SimpleMatcher(super.json)
-      : match = RegExp(json['match'] as String, multiLine: true),
-        captures = (json['captures'] as Map<String, Object?>?)
-            ?.cast<String, Map<String, Object?>>(),
-        super._();
+    : match = RegExp(json['match'] as String, multiLine: true),
+      captures =
+          (json['captures'] as Map<String, Object?>?)
+              ?.cast<String, Map<String, Object?>>(),
+      super._();
 
   static bool isType(Map<String, Object?> json) {
     return json.containsKey('match');
@@ -289,22 +296,25 @@
 
 class _MultilineMatcher extends GrammarMatcher {
   _MultilineMatcher(super.json)
-      : begin = RegExp(json['begin'] as String, multiLine: true),
-        beginCaptures = json['beginCaptures'] as Map<String, Object?>?,
-        contentName = json['contentName'] as String?,
-        end = json['end'] == null
-            ? null
-            : RegExp(json['end'] as String, multiLine: true),
-        endCaptures = json['endCaptures'] as Map<String, Object?>?,
-        captures = json['captures'] as Map<String, Object?>?,
-        whileCond = json['while'] == null
-            ? null
-            : RegExp(json['while'] as String, multiLine: true),
-        patterns = (json['patterns'] as List<Object?>?)
-            ?.cast<Map<String, Object?>>()
-            .map((e) => GrammarMatcher.fromJson(e))
-            .toList(),
-        super._();
+    : begin = RegExp(json['begin'] as String, multiLine: true),
+      beginCaptures = json['beginCaptures'] as Map<String, Object?>?,
+      contentName = json['contentName'] as String?,
+      end =
+          json['end'] == null
+              ? null
+              : RegExp(json['end'] as String, multiLine: true),
+      endCaptures = json['endCaptures'] as Map<String, Object?>?,
+      captures = json['captures'] as Map<String, Object?>?,
+      whileCond =
+          json['while'] == null
+              ? null
+              : RegExp(json['while'] as String, multiLine: true),
+      patterns =
+          (json['patterns'] as List<Object?>?)
+              ?.cast<Map<String, Object?>>()
+              .map((e) => GrammarMatcher.fromJson(e))
+              .toList(),
+      super._();
 
   static bool isType(Map<String, Object?> json) {
     return json.containsKey('begin') &&
@@ -482,11 +492,7 @@
       }
 
       // Now, split any spans produced whileContentBeginMarker by `whileCond`.
-      scopeStack.splitFromMarker(
-        scanner,
-        whileContentBeginMarker,
-        whileCond!,
-      );
+      scopeStack.splitFromMarker(scanner, whileContentBeginMarker, whileCond!);
     } else {
       throw StateError(
         "One of 'end' or 'while' must be provided for rule: $name",
@@ -517,11 +523,12 @@
 
 class _PatternMatcher extends GrammarMatcher {
   _PatternMatcher(super.json)
-      : patterns = (json['patterns'] as List<Object?>?)
-            ?.cast<Map<String, Object?>>()
-            .map((e) => GrammarMatcher.fromJson(e))
-            .toList(),
-        super._();
+    : patterns =
+          (json['patterns'] as List<Object?>?)
+              ?.cast<Map<String, Object?>>()
+              .map((e) => GrammarMatcher.fromJson(e))
+              .toList(),
+      super._();
 
   static bool isType(Map<String, Object?> json) {
     return json.containsKey('patterns');
@@ -554,9 +561,7 @@
 /// `patterns` array. Allows for executing rules defined within a
 /// [Repository].
 class _IncludeMatcher extends GrammarMatcher {
-  _IncludeMatcher(String include)
-      : include = include.substring(1),
-        super._({});
+  _IncludeMatcher(String include) : include = include.substring(1), super._({});
 
   final String include;
 
@@ -575,9 +580,7 @@
 
   @override
   Map<String, Object?> toJson() {
-    return {
-      'include': include,
-    };
+    return {'include': include};
   }
 }
 
@@ -673,8 +676,9 @@
     _nextLocation = begin.location;
 
     // Add the split spans individually.
-    for (final span in spansToSplit
-        .expand((spanToSplit) => spanToSplit.split(scanner, condition))) {
+    for (final span in spansToSplit.expand(
+      (spanToSplit) => spanToSplit.split(scanner, condition),
+    )) {
       // To handler spans with multiple scopes, we need to push each scope, and
       // then pop each scope. We cannot use `add`.
       for (final scope in span.scopes) {
@@ -686,10 +690,7 @@
     }
   }
 
-  void _produceSpan(
-    Set<String> scopes, {
-    required ScopeStackLocation end,
-  }) {
+  void _produceSpan(Set<String> scopes, {required ScopeStackLocation end}) {
     // Don't produce zero-width spans.
     if (end.position == _nextLocation.position) return;
 
@@ -733,10 +734,7 @@
 ///
 /// Used for back-tracking when handling nested multiline tokens.
 class ScopeStackMarker {
-  ScopeStackMarker({
-    required this.spanIndex,
-    required this.location,
-  });
+  ScopeStackMarker({required this.spanIndex, required this.location});
 
   final int spanIndex;
   final ScopeStackLocation location;
diff --git a/packages/devtools_app/lib/src/screens/debugger/syntax_highlighter.dart b/packages/devtools_app/lib/src/screens/debugger/syntax_highlighter.dart
index b1f92e8..db1fc64 100644
--- a/packages/devtools_app/lib/src/screens/debugger/syntax_highlighter.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/syntax_highlighter.dart
@@ -18,10 +18,8 @@
 class SyntaxHighlighter {
   SyntaxHighlighter({String? source}) : source = source ?? '';
 
-  SyntaxHighlighter.withGrammar({
-    Grammar? grammar,
-    String? source,
-  }) : source = source ?? '' {
+  SyntaxHighlighter.withGrammar({Grammar? grammar, String? source})
+    : source = source ?? '' {
     _grammar = grammar;
   }
 
@@ -113,10 +111,7 @@
 
   /// Enters a new scope for a span of text. Returns a [List<TextSpan>]
   /// containing the stylized text from within the scope.
-  List<TextSpan> _scope(
-    ScopeSpan currentScope,
-    List<ScopeSpan> scopes,
-  ) {
+  List<TextSpan> _scope(ScopeSpan currentScope, List<ScopeSpan> scopes) {
     return _highlightLoopHelper(
       currentScope: currentScope,
       loopCondition: () => currentScope.contains(_currentPosition),
@@ -143,27 +138,14 @@
           _currentPosition,
         );
         if (text.isNotEmpty) {
-          sourceSpans.add(
-            TextSpan(
-              style: _getStyleForSpan(),
-              text: text,
-            ),
-          );
+          sourceSpans.add(TextSpan(style: _getStyleForSpan(), text: text));
         }
-        sourceSpans.addAll(
-          _scope(
-            scopes.removeAt(0),
-            scopes,
-          ),
-        );
+        sourceSpans.addAll(_scope(scopes.removeAt(0), scopes));
         // Reset the beginning of the current span to the first position after
         // the close of the span that was just processed.
         currentScopeBegin = _currentPosition;
       } else if (_atNewline()) {
-        currentScopeBegin = _processNewlines(
-          sourceSpans,
-          currentScopeBegin!,
-        );
+        currentScopeBegin = _processNewlines(sourceSpans, currentScopeBegin!);
       } else {
         ++_currentPosition;
       }
@@ -175,12 +157,7 @@
       _currentPosition,
     );
     if (text.isNotEmpty) {
-      sourceSpans.add(
-        TextSpan(
-          style: _getStyleForSpan(),
-          text: text,
-        ),
-      );
+      sourceSpans.add(TextSpan(style: _getStyleForSpan(), text: text));
     }
     if (currentScope != null) {
       _spanStack.removeLast();
@@ -201,10 +178,7 @@
       sourceSpans.add(
         TextSpan(
           style: _getStyleForSpan(),
-          text: _processedSource.substring(
-            currentScopeBegin,
-            _currentPosition,
-          ),
+          text: _processedSource.substring(currentScopeBegin, _currentPosition),
         ),
       );
     }
@@ -220,9 +194,7 @@
   }
 
   Map<String, TextStyle> _buildSyntaxColorTable(ThemeData theme) {
-    final commentStyle = TextStyle(
-      color: theme.colorScheme.commentSyntaxColor,
-    );
+    final commentStyle = TextStyle(color: theme.colorScheme.commentSyntaxColor);
     final functionStyle = TextStyle(
       color: theme.colorScheme.functionSyntaxColor,
     );
@@ -238,9 +210,7 @@
     final variableStyle = TextStyle(
       color: theme.colorScheme.variableSyntaxColor,
     );
-    final stringStyle = TextStyle(
-      color: theme.colorScheme.stringSyntaxColor,
-    );
+    final stringStyle = TextStyle(color: theme.colorScheme.stringSyntaxColor);
     final numericConstantStyle = TextStyle(
       color: theme.colorScheme.numericConstantSyntaxColor,
     );
@@ -268,13 +238,9 @@
       'variable.language.dart',
     ];
 
-    const numericConstantScopes = <String>[
-      'constant.numeric.dart',
-    ];
+    const numericConstantScopes = <String>['constant.numeric.dart'];
 
-    const functionScopes = <String>[
-      'entity.name.function.dart',
-    ];
+    const functionScopes = <String>['entity.name.function.dart'];
 
     const controlFlowScopes = <String>[
       'keyword.control.catch-exception.dart',
@@ -308,9 +274,7 @@
       List<String> scopes,
       TextStyle style,
     ) {
-      return {
-        for (final scope in scopes) scope: style,
-      };
+      return {for (final scope in scopes) scope: style};
     }
 
     return <String, TextStyle>{
diff --git a/packages/devtools_app/lib/src/screens/debugger/variables.dart b/packages/devtools_app/lib/src/screens/debugger/variables.dart
index 1347fde..de9b07f 100644
--- a/packages/devtools_app/lib/src/screens/debugger/variables.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/variables.dart
@@ -16,7 +16,7 @@
 import '../../shared/diagnostics/tree_builder.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/globals.dart';
-import '../../shared/tree.dart';
+import '../../shared/ui/tree_view.dart';
 
 class Variables extends StatelessWidget {
   const Variables({super.key});
@@ -36,18 +36,15 @@
     } else {
       return TreeView<DartObjectNode>(
         dataRootsListenable: serviceConnection.appState.variables,
-        dataDisplayProvider: (variable, onPressed) => DisplayProvider(
-          variable: variable,
-          onTap: onPressed,
-        ),
+        dataDisplayProvider:
+            (variable, onPressed) =>
+                DisplayProvider(variable: variable, onTap: onPressed),
         onItemSelected: onItemPressed,
       );
     }
   }
 
-  Future<void> onDapItemExpanded(
-    DapObjectNode node,
-  ) async {
+  Future<void> onDapItemExpanded(DapObjectNode node) async {
     if (node.isExpanded) {
       for (final child in node.children) {
         await child.fetchChildren();
@@ -55,9 +52,7 @@
     }
   }
 
-  Future<void> onItemPressed(
-    DartObjectNode v,
-  ) async {
+  Future<void> onItemPressed(DartObjectNode v) async {
     // On expansion, lazily build the variables tree for performance reasons.
     if (v.isExpanded) {
       await Future.wait(v.children.map(buildVariablesTree));
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_link_list_view.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_link_list_view.dart
index 4be5da0..951c8b5 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_link_list_view.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_link_list_view.dart
@@ -7,14 +7,14 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../../shared/common_widgets.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/table/table.dart';
 import '../../shared/table/table_data.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/tab.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'deep_links_controller.dart';
 import 'deep_links_model.dart';
 import 'validation_details_view.dart';
@@ -23,11 +23,7 @@
 const _kSearchFieldFullWidth = 314.0;
 const _kSearchFieldSplitScreenWidth = 280.0;
 
-enum TableViewType {
-  domainView,
-  pathView,
-  singleUrlView,
-}
+enum TableViewType { domainView, pathView, singleUrlView }
 
 /// A view that display all deep links for the app.
 class DeepLinkListView extends StatefulWidget {
@@ -144,32 +140,28 @@
         if (displayOptions.showSplitScreen) {
           return Row(
             children: [
-              Expanded(
-                child: _AllDeepLinkDataTable(controller: controller),
-              ),
-              VerticalDivider(
-                width: 1.0,
-                color: Theme.of(context).focusColor,
-              ),
+              Expanded(child: _AllDeepLinkDataTable(controller: controller)),
+              VerticalDivider(width: 1.0, color: Theme.of(context).focusColor),
               Expanded(
                 child: ValueListenableBuilder<LinkData?>(
                   valueListenable: controller.selectedLink,
-                  builder: (context, _, __) => TabBarView(
-                    children: [
-                      ValidationDetailView(
-                        controller: controller,
-                        viewType: TableViewType.domainView,
+                  builder:
+                      (context, _, _) => TabBarView(
+                        children: [
+                          ValidationDetailView(
+                            controller: controller,
+                            viewType: TableViewType.domainView,
+                          ),
+                          ValidationDetailView(
+                            controller: controller,
+                            viewType: TableViewType.pathView,
+                          ),
+                          ValidationDetailView(
+                            controller: controller,
+                            viewType: TableViewType.singleUrlView,
+                          ),
+                        ],
                       ),
-                      ValidationDetailView(
-                        controller: controller,
-                        viewType: TableViewType.pathView,
-                      ),
-                      ValidationDetailView(
-                        controller: controller,
-                        viewType: TableViewType.singleUrlView,
-                      ),
-                    ],
-                  ),
                 ),
               ),
             ],
@@ -183,9 +175,7 @@
               pathErrorCount: displayOptions.pathErrorCount,
               controller: controller,
             ),
-            Expanded(
-              child: _AllDeepLinkDataTable(controller: controller),
-            ),
+            Expanded(child: _AllDeepLinkDataTable(controller: controller)),
           ],
         );
       },
@@ -236,8 +226,9 @@
           ],
         ],
         selectionNotifier: controller.selectedLink,
-        defaultSortColumn: (viewType == TableViewType.pathView ? path : domain)
-            as ColumnData<LinkData>,
+        defaultSortColumn:
+            (viewType == TableViewType.pathView ? path : domain)
+                as ColumnData<LinkData>,
         defaultSortDirection: SortDirection.ascending,
         sortOriginalData: true,
         onItemSelected: (linkdata) {
@@ -279,8 +270,12 @@
             _ConfigurationDropdown(
               title: 'iOS Configuration:',
               valueListenable: controller.selectedIosConfigurationIndex,
-              configurations: controller
-                  .selectedProject.value!.iosBuildOptions.configurations,
+              configurations:
+                  controller
+                      .selectedProject
+                      .value!
+                      .iosBuildOptions
+                      .configurations,
               onChanged: controller.updateSelectedIosConfigurationIndex,
             ),
             const SizedBox(width: denseSpacing),
@@ -313,7 +308,7 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder(
       valueListenable: valueListenable,
-      builder: (_, index, __) {
+      builder: (_, index, _) {
         return Row(
           children: [
             Text(title),
@@ -337,9 +332,7 @@
 }
 
 class _AllDeepLinkDataTable extends StatelessWidget {
-  const _AllDeepLinkDataTable({
-    required this.controller,
-  });
+  const _AllDeepLinkDataTable({required this.controller});
 
   final DeepLinksController controller;
 
@@ -359,19 +352,18 @@
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Padding(
-                  padding:
-                      const EdgeInsets.symmetric(horizontal: defaultSpacing),
-                  child: Text(
-                    'All deep links',
-                    style: textTheme.titleMedium,
+                  padding: const EdgeInsets.symmetric(
+                    horizontal: defaultSpacing,
                   ),
+                  child: Text('All deep links', style: textTheme.titleMedium),
                 ),
                 Padding(
                   padding: const EdgeInsets.symmetric(horizontal: denseSpacing),
                   child: SizedBox(
-                    width: controller.displayOptions.showSplitScreen
-                        ? _kSearchFieldSplitScreenWidth
-                        : _kSearchFieldFullWidth,
+                    width:
+                        controller.displayOptions.showSplitScreen
+                            ? _kSearchFieldSplitScreenWidth
+                            : _kSearchFieldFullWidth,
                     child: DevToolsClearableTextField(
                       labelText: '',
                       hintText: 'Search a URL, domain or path',
@@ -391,14 +383,8 @@
           height: defaultHeaderHeight,
           child: TabBar(
             tabs: [
-              DevToolsTab.create(
-                tabName: 'Domain view',
-                gaPrefix: gaPrefix,
-              ),
-              DevToolsTab.create(
-                tabName: 'Path view',
-                gaPrefix: gaPrefix,
-              ),
+              DevToolsTab.create(tabName: 'Domain view', gaPrefix: gaPrefix),
+              DevToolsTab.create(tabName: 'Path view', gaPrefix: gaPrefix),
               DevToolsTab.create(
                 tabName: 'Single URL view',
                 gaPrefix: gaPrefix,
@@ -411,25 +397,26 @@
         Expanded(
           child: ValueListenableBuilder<ValidatedLinkDatas>(
             valueListenable: controller.displayLinkDatasNotifier,
-            builder: (context, linkDatas, _) => TabBarView(
-              children: [
-                _DataTable(
-                  viewType: TableViewType.domainView,
-                  linkDatas: linkDatas.byDomain,
-                  controller: controller,
+            builder:
+                (context, linkDatas, _) => TabBarView(
+                  children: [
+                    _DataTable(
+                      viewType: TableViewType.domainView,
+                      linkDatas: linkDatas.byDomain,
+                      controller: controller,
+                    ),
+                    _DataTable(
+                      viewType: TableViewType.pathView,
+                      linkDatas: linkDatas.byPath,
+                      controller: controller,
+                    ),
+                    _DataTable(
+                      viewType: TableViewType.singleUrlView,
+                      linkDatas: linkDatas.all,
+                      controller: controller,
+                    ),
+                  ],
                 ),
-                _DataTable(
-                  viewType: TableViewType.pathView,
-                  linkDatas: linkDatas.byPath,
-                  controller: controller,
-                ),
-                _DataTable(
-                  viewType: TableViewType.singleUrlView,
-                  linkDatas: linkDatas.all,
-                  controller: controller,
-                ),
-              ],
-            ),
           ),
         ),
       ],
@@ -471,8 +458,9 @@
                     controller.updateDisplayOptions(showSplitScreen: true);
                   },
                   child: const Padding(
-                    padding:
-                        EdgeInsets.symmetric(horizontal: intermediateSpacing),
+                    padding: EdgeInsets.symmetric(
+                      horizontal: intermediateSpacing,
+                    ),
                     child: Text('Fix domain'),
                   ),
                 ),
@@ -492,8 +480,9 @@
                     controller.updateDisplayOptions(showSplitScreen: true);
                   },
                   child: const Padding(
-                    padding:
-                        EdgeInsets.symmetric(horizontal: intermediateSpacing),
+                    padding: EdgeInsets.symmetric(
+                      horizontal: intermediateSpacing,
+                    ),
                     child: Text('Fix path'),
                   ),
                 ),
@@ -543,10 +532,7 @@
                   crossAxisAlignment: CrossAxisAlignment.start,
                   children: [
                     Text(title),
-                    Text(
-                      description,
-                      style: theme.subtleTextStyle,
-                    ),
+                    Text(description, style: theme.subtleTextStyle),
                     Expanded(
                       child: Align(
                         alignment: Alignment.bottomRight,
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_controller.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_controller.dart
index 828348c..a2b5629 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_controller.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_controller.dart
@@ -175,10 +175,7 @@
         domain: linkData.domain,
         path: linkData.path,
         scheme: linkData.scheme.union(previousRecord?.scheme ?? {}),
-        os: {
-          if (previousRecord != null) ...previousRecord.os,
-          ...linkData.os,
-        },
+        os: {if (previousRecord != null) ...previousRecord.os, ...linkData.os},
         associatedDomains: [
           ...previousRecord?.associatedDomains ?? [],
           if (linkData.domain != null) linkData.domain!,
@@ -202,10 +199,7 @@
         domain: linkData.domain,
         path: linkData.path,
         scheme: linkData.scheme.union(previousRecord?.scheme ?? {}),
-        os: {
-          if (previousRecord != null) ...previousRecord.os,
-          ...linkData.os,
-        },
+        os: {if (previousRecord != null) ...previousRecord.os, ...linkData.os},
         associatedPath: [
           ...previousRecord?.associatedPath ?? [],
           if (linkData.path != null && !linkData.path!.isExcluded)
@@ -306,8 +300,9 @@
     if (selectedProject.value!.androidVariants.isEmpty) {
       return;
     }
-    final variant = selectedProject
-        .value!.androidVariants[selectedAndroidVariantIndex.value];
+    final variant =
+        selectedProject.value!.androidVariants[selectedAndroidVariantIndex
+            .value];
     await ga.timeAsync(
       gac.deeplink,
       gac.AnalyzeFlutterProject.loadAppLinks.name,
@@ -322,9 +317,8 @@
           ga.impression(
             gac.deeplink,
             gac.AnalyzeFlutterProject.androidAppLinksSettingsLoaded.name,
-            screenMetricsProvider: () => DeepLinkScreenMetrics(
-              androidAppId: result.applicationId,
-            ),
+            screenMetricsProvider:
+                () => DeepLinkScreenMetrics(androidAppId: result.applicationId),
           );
         } catch (_) {
           ga.select(
@@ -360,9 +354,9 @@
           ga.impression(
             gac.deeplink,
             gac.AnalyzeFlutterProject.iosUniversalLinkSettingsLoaded.name,
-            screenMetricsProvider: () => DeepLinkScreenMetrics(
-              iosBundleId: result.bundleIdentifier,
-            ),
+            screenMetricsProvider:
+                () =>
+                    DeepLinkScreenMetrics(iosBundleId: result.bundleIdentifier),
           );
         } catch (_) {
           pagePhase.value = PagePhase.validationErrorPage;
@@ -425,8 +419,9 @@
         domainPathToLinkData[domainAndPath] = LinkData(
           domain: appLink.host,
           path: Path(path: appLink.path),
-          pathErrors:
-              _getPathErrorsFromIntentFilterChecks(appLink.intentFilterChecks),
+          pathErrors: _getPathErrorsFromIntentFilterChecks(
+            appLink.intentFilterChecks,
+          ),
           os: {PlatformOS.android},
           scheme: {if (scheme != null) scheme},
         );
@@ -481,8 +476,9 @@
   final generatedAssetLinksForSelectedLink =
       ValueNotifier<GenerateAssetLinksResult?>(null);
 
-  final displayOptionsNotifier =
-      ValueNotifier<DisplayOptions>(DisplayOptions());
+  final displayOptionsNotifier = ValueNotifier<DisplayOptions>(
+    DisplayOptions(),
+  );
 
   /// The [TextEditingController] for the search text field.
   final textEditingController = TextEditingController();
@@ -491,8 +487,10 @@
   bool addLocalFingerprint(String fingerprint) {
     // A valid fingerprint consists of 32 pairs of hexadecimal digits separated by colons.
     bool isValidFingerprint(String input) {
-      final pattern =
-          RegExp(r'^([0-9a-f]{2}:){31}[0-9a-f]{2}$', caseSensitive: false);
+      final pattern = RegExp(
+        r'^([0-9a-f]{2}:){31}[0-9a-f]{2}$',
+        caseSensitive: false,
+      );
       return pattern.hasMatch(input);
     }
 
@@ -511,23 +509,22 @@
     generatedAssetLinksForSelectedLink.value = null;
     final domain = selectedLink.value!.domain;
     if (domain != null) {
-      generatedAssetLinksForSelectedLink.value =
-          await deepLinksService.generateAssetLinks(
-        domain: domain,
-        applicationId: applicationId,
-        localFingerprint: localFingerprint.value,
-      );
+      generatedAssetLinksForSelectedLink.value = await deepLinksService
+          .generateAssetLinks(
+            domain: domain,
+            applicationId: applicationId,
+            localFingerprint: localFingerprint.value,
+          );
     }
   }
 
   Future<List<LinkData>> _validateDomain(List<LinkData> rawLinkdatas) async {
-    final domains = rawLinkdatas
-        .where(
-          (linkdata) => linkdata.domain != null,
-        )
-        .map((linkdata) => linkdata.domain!)
-        .toSet()
-        .toList();
+    final domains =
+        rawLinkdatas
+            .where((linkdata) => linkdata.domain != null)
+            .map((linkdata) => linkdata.domain!)
+            .toSet()
+            .toList();
 
     Map<String, List<DomainError>> androidDomainErrors = {};
     Map<String, List<DomainError>> iosDomainErrors = {};
@@ -568,12 +565,16 @@
         if (linkdata.os.contains(PlatformOS.ios))
           ...(iosDomainErrors[linkdata.domain] ?? []),
       ];
-      final hasAndroidAssetLinksFile = !(androidDomainErrors[linkdata.domain]
-              ?.contains(AndroidDomainError.existence) ??
-          false);
-      final hasIosAasaFile = !(iosDomainErrors[linkdata.domain]
-              ?.contains(IosDomainError.existence) ??
-          false);
+      final hasAndroidAssetLinksFile =
+          !(androidDomainErrors[linkdata.domain]?.contains(
+                AndroidDomainError.existence,
+              ) ??
+              false);
+      final hasIosAasaFile =
+          !(iosDomainErrors[linkdata.domain]?.contains(
+                IosDomainError.existence,
+              ) ??
+              false);
 
       if (linkdata.os.contains(PlatformOS.ios)) {
         final iosPaths = iosDomainPaths[linkdata.domain] ?? <Path>[];
@@ -644,19 +645,13 @@
       if (FeatureFlags.deepLinkIosCheck) ..._rawIosLinkDatas,
     ];
     if (linkdata.isEmpty) {
-      ga.select(
-        gac.deeplink,
-        gac.AnalyzeFlutterProject.flutterNoAppLink.name,
-      );
+      ga.select(gac.deeplink, gac.AnalyzeFlutterProject.flutterNoAppLink.name);
       pagePhase.value = PagePhase.noLinks;
       return;
     }
 
     // There are deep links to validate.
-    ga.select(
-      gac.deeplink,
-      gac.AnalyzeFlutterProject.flutterHasAppLinks.name,
-    );
+    ga.select(gac.deeplink, gac.AnalyzeFlutterProject.flutterHasAppLinks.name);
     linkdata = await _validateDomain(linkdata);
     if (pagePhase.value == PagePhase.validationErrorPage) {
       return;
@@ -673,12 +668,14 @@
       byPath: linkDatasByPath(linkdata),
     );
     displayOptionsNotifier.value = displayOptionsNotifier.value.copyWith(
-      domainErrorCount: validatedLinkDatas.byDomain
-          .where((element) => element.domainErrors.isNotEmpty)
-          .length,
-      pathErrorCount: validatedLinkDatas.byPath
-          .where((element) => element.pathErrors.isNotEmpty)
-          .length,
+      domainErrorCount:
+          validatedLinkDatas.byDomain
+              .where((element) => element.domainErrors.isNotEmpty)
+              .length,
+      pathErrorCount:
+          validatedLinkDatas.byPath
+              .where((element) => element.pathErrors.isNotEmpty)
+              .length,
     );
     applyFilters();
 
@@ -697,17 +694,20 @@
     late final LinkData linkdata;
     switch (viewType) {
       case TableViewType.domainView:
-        linkdata = linkDatas.byDomain
+        linkdata =
+            linkDatas.byDomain
                 .where((e) => e.domainErrors.isNotEmpty)
                 .firstOrNull ??
             linkDatas.byDomain.first;
       case TableViewType.pathView:
-        linkdata = linkDatas.byPath
+        linkdata =
+            linkDatas.byPath
                 .where((e) => e.pathErrors.isNotEmpty)
                 .firstOrNull ??
             linkDatas.byPath.first;
       case TableViewType.singleUrlView:
-        linkdata = linkDatas.all
+        linkdata =
+            linkDatas.all
                 .where(
                   (e) => e.domainErrors.isNotEmpty || e.pathErrors.isNotEmpty,
                 )
@@ -718,8 +718,9 @@
   }
 
   set searchContent(String content) {
-    displayOptionsNotifier.value =
-        displayOptionsNotifier.value.copyWith(searchContent: content);
+    displayOptionsNotifier.value = displayOptionsNotifier.value.copyWith(
+      searchContent: content,
+    );
     applyFilters();
   }
 
@@ -740,12 +741,16 @@
       pathSortingOption: pathSortingOption,
     );
     if (addedFilter != null) {
-      displayOptionsNotifier.value =
-          displayOptionsNotifier.value.updateFilter(addedFilter, true);
+      displayOptionsNotifier.value = displayOptionsNotifier.value.updateFilter(
+        addedFilter,
+        true,
+      );
     }
     if (removedFilter != null) {
-      displayOptionsNotifier.value =
-          displayOptionsNotifier.value.updateFilter(removedFilter, false);
+      displayOptionsNotifier.value = displayOptionsNotifier.value.updateFilter(
+        removedFilter,
+        false,
+      );
     }
 
     if (addedFilter != null || removedFilter != null) {
@@ -764,34 +769,38 @@
   @visibleForTesting
   List<LinkData> getFilterredLinks(List<LinkData> linkDatas) {
     final searchContent = displayOptions.searchContent;
-    linkDatas = linkDatas.where((linkData) {
-      if (searchContent.isNotEmpty &&
-          !linkData.matchesSearchToken(
-            RegExp(searchContent, caseSensitive: false),
-          )) {
-        return false;
-      }
+    linkDatas =
+        linkDatas.where((linkData) {
+          if (searchContent.isNotEmpty &&
+              !linkData.matchesSearchToken(
+                RegExp(searchContent, caseSensitive: false),
+              )) {
+            return false;
+          }
 
-      if (!((linkData.os.contains(PlatformOS.android) &&
-              displayOptions.filters.contains(FilterOption.android)) ||
-          (linkData.os.contains(PlatformOS.ios) &&
-              displayOptions.filters.contains(FilterOption.ios)))) {
-        return false;
-      }
+          if (!((linkData.os.contains(PlatformOS.android) &&
+                  displayOptions.filters.contains(FilterOption.android)) ||
+              (linkData.os.contains(PlatformOS.ios) &&
+                  displayOptions.filters.contains(FilterOption.ios)))) {
+            return false;
+          }
 
-      if (!((linkData.domainErrors.isNotEmpty &&
-              displayOptions.filters
-                  .contains(FilterOption.failedDomainCheck)) ||
-          (linkData.pathErrors.isNotEmpty &&
-              displayOptions.filters.contains(FilterOption.failedPathCheck)) ||
-          (linkData.domainErrors.isEmpty &&
-              linkData.pathErrors.isEmpty &&
-              displayOptions.filters.contains(FilterOption.noIssue)))) {
-        return false;
-      }
+          if (!((linkData.domainErrors.isNotEmpty &&
+                  displayOptions.filters.contains(
+                    FilterOption.failedDomainCheck,
+                  )) ||
+              (linkData.pathErrors.isNotEmpty &&
+                  displayOptions.filters.contains(
+                    FilterOption.failedPathCheck,
+                  )) ||
+              (linkData.domainErrors.isEmpty &&
+                  linkData.pathErrors.isEmpty &&
+                  displayOptions.filters.contains(FilterOption.noIssue)))) {
+            return false;
+          }
 
-      return true;
-    }).toList();
+          return true;
+        }).toList();
 
     return linkDatas;
   }
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_model.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_model.dart
index 3edd79b..1b8f0b8 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_model.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_model.dart
@@ -170,14 +170,13 @@
 
   static IosDomainError iosFileFormatDomainError({
     required List<AASAfileFormatSubCheck> subcheckErrors,
-  }) =>
-      IosDomainError(
-        'Apple-App-Site-Association file format is incorrect',
-        'This test checks that your Apple-App-Site-Association file '
-            'follows the correct format guidelines.',
-        'Ensure your Apple-App-Site-Association file follows the correct format guidelines.',
-        subcheckErrors: subcheckErrors,
-      );
+  }) => IosDomainError(
+    'Apple-App-Site-Association file format is incorrect',
+    'This test checks that your Apple-App-Site-Association file '
+        'follows the correct format guidelines.',
+    'Ensure your Apple-App-Site-Association file follows the correct format guidelines.',
+    subcheckErrors: subcheckErrors,
+  );
 
   final List<AASAfileFormatSubCheck> subcheckErrors;
 }
@@ -191,7 +190,7 @@
 
 class AASAfileFormatSubCheck extends CommonError {
   const AASAfileFormatSubCheck(String title, String explanation)
-      : super(title, explanation, '');
+    : super(title, explanation, '');
 
   static final appLinksFormat = AASAfileFormatSubCheck(
     'Applinks format',
@@ -386,10 +385,7 @@
     required this.byDomain,
     required this.byPath,
   });
-  ValidatedLinkDatas.empty()
-      : all = [],
-        byDomain = [],
-        byPath = [];
+  ValidatedLinkDatas.empty() : all = [], byDomain = [], byPath = [];
   final List<LinkData> all;
   final List<LinkData> byDomain;
   final List<LinkData> byPath;
@@ -542,12 +538,7 @@
             ),
           ),
         const SizedBox(width: denseSpacing),
-        Flexible(
-          child: Text(
-            text,
-            overflow: TextOverflow.ellipsis,
-          ),
-        ),
+        Flexible(child: Text(text, overflow: TextOverflow.ellipsis)),
       ],
     );
   }
@@ -556,8 +547,8 @@
 class DomainColumn extends ColumnData<LinkData>
     implements ColumnRenderer<LinkData>, ColumnHeaderRenderer<LinkData> {
   DomainColumn(this.controller)
-      : sortingOption = controller.displayOptions.domainSortingOption,
-        super.wide('Domain');
+    : sortingOption = controller.displayOptions.domainSortingOption,
+      super.wide('Domain');
 
   DeepLinksController controller;
   SortingOption? sortingOption;
@@ -578,12 +569,10 @@
       children: [
         const Text('Domain'),
         PopupMenuButton<SortingOption>(
-          itemBuilder: (BuildContext context) =>
-              _buildPopupMenuSortingEntries(controller, isPath: false),
-          child: Icon(
-            Icons.arrow_drop_down,
-            size: actionsIconSize,
-          ),
+          itemBuilder:
+              (BuildContext context) =>
+                  _buildPopupMenuSortingEntries(controller, isPath: false),
+          child: Icon(Icons.arrow_drop_down, size: actionsIconSize),
         ),
       ],
     );
@@ -603,20 +592,16 @@
     return dataObject.domain == null
         ? Text('missing domain', style: Theme.of(context).errorTextStyle)
         : _ErrorAwareText(
-            isError: dataObject.domainErrors.isNotEmpty,
-            controller: controller,
-            text: dataObject.domain!,
-            link: dataObject,
-          );
+          isError: dataObject.domainErrors.isNotEmpty,
+          controller: controller,
+          text: dataObject.domain!,
+          link: dataObject,
+        );
   }
 
   @override
-  int compare(LinkData a, LinkData b) => _compareLinkData(
-        a,
-        b,
-        sortingOption: sortingOption,
-        compareDomain: true,
-      );
+  int compare(LinkData a, LinkData b) =>
+      _compareLinkData(a, b, sortingOption: sortingOption, compareDomain: true);
 
   @override
   String get config => '$title $sortingOption';
@@ -625,8 +610,8 @@
 class PathColumn extends ColumnData<LinkData>
     implements ColumnRenderer<LinkData>, ColumnHeaderRenderer<LinkData> {
   PathColumn(this.controller)
-      : sortingOption = controller.displayOptions.pathSortingOption,
-        super.wide('Path');
+    : sortingOption = controller.displayOptions.pathSortingOption,
+      super.wide('Path');
 
   DeepLinksController controller;
   SortingOption? sortingOption;
@@ -647,12 +632,10 @@
       children: [
         const Text('Path'),
         PopupMenuButton<SortingOption>(
-          itemBuilder: (BuildContext context) =>
-              _buildPopupMenuSortingEntries(controller, isPath: true),
-          child: Icon(
-            Icons.arrow_drop_down,
-            size: actionsIconSize,
-          ),
+          itemBuilder:
+              (BuildContext context) =>
+                  _buildPopupMenuSortingEntries(controller, isPath: true),
+          child: Icon(Icons.arrow_drop_down, size: actionsIconSize),
         ),
       ],
     );
@@ -680,11 +663,11 @@
 
   @override
   int compare(LinkData a, LinkData b) => _compareLinkData(
-        a,
-        b,
-        sortingOption: sortingOption,
-        compareDomain: false,
-      );
+    a,
+    b,
+    sortingOption: sortingOption,
+    compareDomain: false,
+  );
 
   @override
   String get config => '$title $sortingOption';
@@ -735,10 +718,7 @@
               _buildPopupMenuFilterEntry(controller, FilterOption.custom),
             ];
           },
-          child: Icon(
-            Icons.arrow_drop_down,
-            size: actionsIconSize,
-          ),
+          child: Icon(Icons.arrow_drop_down, size: actionsIconSize),
         ),
       ],
     );
@@ -790,10 +770,7 @@
               _buildPopupMenuFilterEntry(controller, FilterOption.ios),
             ];
           },
-          child: Icon(
-            Icons.arrow_drop_down,
-            size: actionsIconSize,
-          ),
+          child: Icon(Icons.arrow_drop_down, size: actionsIconSize),
         ),
       ],
     );
@@ -866,10 +843,7 @@
               _buildPopupMenuFilterEntry(controller, FilterOption.noIssue),
             ];
           },
-          child: Icon(
-            Icons.arrow_drop_down,
-            size: actionsIconSize,
-          ),
+          child: Icon(Icons.arrow_drop_down, size: actionsIconSize),
         ),
       ],
     );
@@ -902,11 +876,7 @@
 
 class NavigationColumn extends ColumnData<LinkData>
     implements ColumnRenderer<LinkData> {
-  NavigationColumn()
-      : super(
-          '',
-          fixedWidthPx: scaleByFontFactor(40),
-        );
+  NavigationColumn() : super('', fixedWidthPx: scaleByFontFactor(40));
 
   @override
   bool get supportsSorting => false;
@@ -938,13 +908,15 @@
       children: [
         ValueListenableBuilder<DisplayOptions>(
           valueListenable: controller.displayOptionsNotifier,
-          builder: (context, option, _) => Checkbox(
-            value: option.filters.contains(filterOption),
-            onChanged: (bool? checked) => controller.updateDisplayOptions(
-              removedFilter: checked! ? null : filterOption,
-              addedFilter: checked ? filterOption : null,
-            ),
-          ),
+          builder:
+              (context, option, _) => Checkbox(
+                value: option.filters.contains(filterOption),
+                onChanged:
+                    (bool? checked) => controller.updateDisplayOptions(
+                      removedFilter: checked! ? null : filterOption,
+                      addedFilter: checked ? filterOption : null,
+                    ),
+              ),
         ),
         Text(filterOption.description),
       ],
@@ -962,16 +934,8 @@
       SortingOption.errorOnTop,
       isPath: isPath,
     ),
-    _buildPopupMenuSortingEntry(
-      controller,
-      SortingOption.aToZ,
-      isPath: isPath,
-    ),
-    _buildPopupMenuSortingEntry(
-      controller,
-      SortingOption.zToA,
-      isPath: isPath,
-    ),
+    _buildPopupMenuSortingEntry(controller, SortingOption.aToZ, isPath: isPath),
+    _buildPopupMenuSortingEntry(controller, SortingOption.zToA, isPath: isPath),
   ];
 }
 
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_screen.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_screen.dart
index 4bc9d13..122262e 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_screen.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_screen.dart
@@ -6,8 +6,8 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/utils/utils.dart';
 import 'deep_link_list_view.dart';
 import 'deep_links_controller.dart';
 import 'deep_links_model.dart';
@@ -56,7 +56,7 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder(
       valueListenable: controller.selectedProject,
-      builder: (_, FlutterProject? project, __) {
+      builder: (_, FlutterProject? project, _) {
         return project == null
             ? const SelectProjectView()
             : const DeepLinkListView();
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_services.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_services.dart
index 868a6fc..0f07bca 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_services.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/deep_links_services.dart
@@ -161,15 +161,17 @@
 
       final result = json.decode(response.body) as Map<String, Object?>;
 
-      final validationResult = (result[_androidValidationResultKey] as List)
-          .cast<Map<String, Object?>>();
+      final validationResult =
+          (result[_androidValidationResultKey] as List)
+              .cast<Map<String, Object?>>();
       googlePlayFingerprintsAvailable =
           result[_googlePlayFingerprintsAvailabilityKey] ==
-              _googlePlayFingerprintsAvailableValue;
+          _googlePlayFingerprintsAvailableValue;
       for (final domainResult in validationResult) {
         final domainName = domainResult[_domainNameKey] as String;
-        final failedChecks = (domainResult[_failedChecksKey] as List?)
-            ?.cast<Map<String, Object?>>();
+        final failedChecks =
+            (domainResult[_failedChecksKey] as List?)
+                ?.cast<Map<String, Object?>>();
         if (failedChecks != null) {
           for (final failedCheck in failedChecks) {
             final checkName = failedCheck[_checkNameKey] as String;
@@ -205,23 +207,22 @@
         Uri.parse(iosDomainValidationURL),
         headers: postHeader,
         body: jsonEncode({
-          _appIdKey: {
-            _bundleIdKey: bundleId,
-            _teamIdKey: teamId,
-          },
+          _appIdKey: {_bundleIdKey: bundleId, _teamIdKey: teamId},
           _universalLinkDomainsKey: [
             for (final domain in domainList) {_iosDomainNameKey: domain},
           ],
         }),
       );
       final result = json.decode(response.body) as Map<String, Object?>;
-      final validationResult = (result[_iosValidationResultsKey] as List)
-          .cast<Map<String, Object?>>();
+      final validationResult =
+          (result[_iosValidationResultsKey] as List)
+              .cast<Map<String, Object?>>();
 
       for (final domainResult in validationResult) {
         if (domainResult[_domainNameKey] case final String domainName) {
-          final failedChecks = (domainResult[_failedChecksKey] as List?)
-              ?.cast<Map<String, Object?>>();
+          final failedChecks =
+              (domainResult[_failedChecksKey] as List?)
+                  ?.cast<Map<String, Object?>>();
           if (failedChecks != null) {
             for (final failedCheck in failedChecks) {
               final checkName = failedCheck[_checkNameKey] as String;
@@ -233,15 +234,17 @@
                       <AASAfileFormatSubCheck>[];
 
                   // Adds sub checks for file format error.
-                  final subChecks = (failedCheck[_subCheckResultsKey] as List?)
-                      ?.cast<Map<String, Object?>>();
+                  final subChecks =
+                      (failedCheck[_subCheckResultsKey] as List?)
+                          ?.cast<Map<String, Object?>>();
                   for (final subCheck in (subChecks ?? <Map>[])) {
                     final subCheckName = subCheck[_checkNameKey] as String;
                     final subCheckResultType =
                         subCheck[_resultTypeKey] as String;
                     if (subCheckResultType != _passedKey) {
-                      failedAasaFileFormatSubCheck
-                          .add(aasaFileFormatSubCheck[subCheckName]!);
+                      failedAasaFileFormatSubCheck.add(
+                        aasaFileFormatSubCheck[subCheckName]!,
+                      );
                     }
                   }
 
@@ -261,25 +264,30 @@
             }
           }
 
-          final aasaAppPaths = (domainResult[_aasaAppPathsKey] as List?)
-              ?.cast<Map<String, Object?>>();
+          final aasaAppPaths =
+              (domainResult[_aasaAppPathsKey] as List?)
+                  ?.cast<Map<String, Object?>>();
           if (aasaAppPaths != null) {
             for (final aasaAppPath in aasaAppPaths) {
-              final aasaPaths = (aasaAppPath[_aasaPathsKey] as List?)
-                  ?.cast<Map<String, Object?>>();
+              final aasaPaths =
+                  (aasaAppPath[_aasaPathsKey] as List?)
+                      ?.cast<Map<String, Object?>>();
               if (aasaPaths != null) {
                 for (final aasaPath in aasaPaths) {
                   final path = aasaPath[_pathKey] as String?;
                   if (path.isNullOrEmpty) {
                     continue;
                   }
-                  final rawQueryParams = (aasaPath[_queryParamsKey] as List?)
-                      ?.cast<Map<String, Object?>>();
+                  final rawQueryParams =
+                      (aasaPath[_queryParamsKey] as List?)
+                          ?.cast<Map<String, Object?>>();
                   final queryParams = <String, String>{
                     for (final item in rawQueryParams ?? <Map>[])
                       item[_keyKey] as String: item[_valueKey] as String,
                   };
-                  paths.putIfAbsent(domainName, () => <Path>[]).add(
+                  paths
+                      .putIfAbsent(domainName, () => <Path>[])
+                      .add(
                         Path(
                           path: path!,
                           queryParams: queryParams,
@@ -295,11 +303,7 @@
         }
       }
     }
-    return ValidateIosDomainResult(
-      errorCode,
-      domainErrors,
-      paths,
-    );
+    return ValidateIosDomainResult(errorCode, domainErrors, paths);
   }
 
   // The request can take 1000 domains at most, split domains to make a few calls in serial with a batch of _domainBatchSize.
@@ -323,13 +327,11 @@
     final response = await client.post(
       Uri.parse(assetLinksGenerationURL),
       headers: postHeader,
-      body: jsonEncode(
-        {
-          _packageNameKey: applicationId,
-          _domainsKey: [domain],
-          if (localFingerprint != null) _fingerprintsKey: [localFingerprint],
-        },
-      ),
+      body: jsonEncode({
+        _packageNameKey: applicationId,
+        _domainsKey: [domain],
+        if (localFingerprint != null) _fingerprintsKey: [localFingerprint],
+      }),
     );
     final result = json.decode(response.body) as Map<String, Object?>;
     final errorCode = (result[_errorCodeKey] as String?) ?? '';
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/root_selector.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/root_selector.dart
index 510c966..6bba82e 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/root_selector.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/root_selector.dart
@@ -102,12 +102,11 @@
         isDense: true,
         isExpanded: true,
         value: selectedUri,
-        items: [
-          for (final uri in widget.projectRoots) _buildMenuItem(uri),
-        ],
-        onChanged: (uri) => setState(() {
-          selectedUri = uri;
-        }),
+        items: [for (final uri in widget.projectRoots) _buildMenuItem(uri)],
+        onChanged:
+            (uri) => setState(() {
+              selectedUri = uri;
+            }),
       ),
     );
   }
@@ -118,10 +117,7 @@
       child: DevToolsTooltip(
         message: uri.path,
         waitDuration: tooltipWaitExtraLong,
-        child: Text(
-          uri.path,
-          overflow: TextOverflow.ellipsis,
-        ),
+        child: Text(uri.path, overflow: TextOverflow.ellipsis),
       ),
     );
   }
@@ -152,15 +148,8 @@
       mainAxisAlignment: MainAxisAlignment.center,
       children: [
         const Spacer(),
-        Flexible(
-          flex: 8,
-          fit: FlexFit.tight,
-          child: child,
-        ),
-        if (showButtonInRow) ...[
-          const SizedBox(width: defaultSpacing),
-          button,
-        ],
+        Flexible(flex: 8, fit: FlexFit.tight, child: child),
+        if (showButtonInRow) ...[const SizedBox(width: defaultSpacing), button],
         const Spacer(),
       ],
     );
@@ -170,11 +159,7 @@
     if (!showButtonInRow) {
       content = Column(
         mainAxisSize: MainAxisSize.min,
-        children: [
-          content,
-          const SizedBox(height: defaultSpacing),
-          button,
-        ],
+        children: [content, const SizedBox(height: defaultSpacing), button],
       );
     }
 
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/select_project_view.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/select_project_view.dart
index 9755a3b..c87ee27 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/select_project_view.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/select_project_view.dart
@@ -14,7 +14,7 @@
 import '../../../shared/globals.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/server/server.dart' as server;
-import '../../../shared/utils.dart';
+import '../../../shared/utils/utils.dart';
 import '../deep_links_controller.dart';
 import '../deep_links_model.dart';
 import 'root_selector.dart';
@@ -99,9 +99,7 @@
           content: Text(
             'It looks like you have selected a non-Flutter project. Please select a Flutter project instead.',
           ),
-          actions: [
-            DialogCloseButton(),
-          ],
+          actions: [DialogCloseButton()],
         );
       },
     );
@@ -202,10 +200,7 @@
         mainAxisAlignment: MainAxisAlignment.center,
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          Text(
-            'Project loading...',
-            style: theme.regularTextStyle,
-          ),
+          Text('Project loading...', style: theme.regularTextStyle),
           Container(
             width: _kLinearProgressIndicatorWidth,
             padding: const EdgeInsets.symmetric(vertical: densePadding),
diff --git a/packages/devtools_app/lib/src/screens/deep_link_validation/validation_details_view.dart b/packages/devtools_app/lib/src/screens/deep_link_validation/validation_details_view.dart
index 7bfe8c8..3743c37 100644
--- a/packages/devtools_app/lib/src/screens/deep_link_validation/validation_details_view.dart
+++ b/packages/devtools_app/lib/src/screens/deep_link_validation/validation_details_view.dart
@@ -7,10 +7,10 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/common_widgets.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/ui/colors.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/utils/utils.dart';
 import 'deep_link_list_view.dart';
 import 'deep_links_controller.dart';
 import 'deep_links_model.dart';
@@ -115,8 +115,8 @@
               style: Theme.of(context).textTheme.titleMedium,
             ),
             IconButton(
-              onPressed: () =>
-                  controller.updateDisplayOptions(showSplitScreen: false),
+              onPressed:
+                  () => controller.updateDisplayOptions(showSplitScreen: false),
               icon: const Icon(Icons.close),
             ),
           ],
@@ -127,9 +127,7 @@
 }
 
 class _DomainCheckTable extends StatelessWidget {
-  const _DomainCheckTable({
-    required this.controller,
-  });
+  const _DomainCheckTable({required this.controller});
 
   final DeepLinksController controller;
 
@@ -142,7 +140,7 @@
       builder: (context, localFingerprint, _) {
         final fingerprintExists =
             controller.googlePlayFingerprintsAvailability.value ||
-                localFingerprint != null;
+            localFingerprint != null;
         return Column(
           crossAxisAlignment: CrossAxisAlignment.stretch,
           children: [
@@ -156,7 +154,8 @@
                 initiallyExpanded: !fingerprintExists,
                 checkName: 'Digital assets link file',
                 status: _CheckStatusText(
-                  hasError: !fingerprintExists ||
+                  hasError:
+                      !fingerprintExists ||
                       linkData.domainErrors.any((e) => e is AndroidDomainError),
                 ),
                 children: <Widget>[
@@ -174,24 +173,26 @@
                 os: PlatformOS.ios,
                 checkName: 'Apple-App-Site-Association file',
                 status: _CheckStatusText(
-                  hasError:
-                      linkData.domainErrors.any((e) => e is IosDomainError),
+                  hasError: linkData.domainErrors.any(
+                    (e) => e is IosDomainError,
+                  ),
                 ),
                 children: <Widget>[
                   for (final error
                       in linkData.domainErrors.whereType<IosDomainError>())
                     _IssuesBorderWrap(
-                      children: error == IosDomainError.existence
-                          ? [
-                              _FailureDetails(
-                                errors: [error],
-                                oneFixGuideForAll:
-                                    'To fix this issue, add an Apple-App-Site-Association file at the following location: '
-                                    'https://${controller.selectedLink.value!.domain}/.well-known/apple-app-site-association',
-                              ),
-                              const SizedBox(height: denseSpacing),
-                              _CodeCard(
-                                content: '''{
+                      children:
+                          error == IosDomainError.existence
+                              ? [
+                                _FailureDetails(
+                                  errors: [error],
+                                  oneFixGuideForAll:
+                                      'To fix this issue, add an Apple-App-Site-Association file at the following location: '
+                                      'https://${controller.selectedLink.value!.domain}/.well-known/apple-app-site-association',
+                                ),
+                                const SizedBox(height: denseSpacing),
+                                _CodeCard(
+                                  content: '''{
   "applinks": {
     "details": [
       {
@@ -207,13 +208,13 @@
     ]
   }
 }''',
-                              ),
-                            ]
-                          : [
-                              _FailureDetails(
-                                errors: [error, ...error.subcheckErrors],
-                              ),
-                            ],
+                                ),
+                              ]
+                              : [
+                                _FailureDetails(
+                                  errors: [error, ...error.subcheckErrors],
+                                ),
+                              ],
                     ),
                 ],
               ),
@@ -228,19 +229,16 @@
 /// There is a general fix for the asset links json file issues:
 /// Update it with the generated asset link file.
 class _AssetLinksJsonFileIssues extends StatelessWidget {
-  const _AssetLinksJsonFileIssues({
-    required this.controller,
-  });
+  const _AssetLinksJsonFileIssues({required this.controller});
 
   final DeepLinksController controller;
 
   @override
   Widget build(BuildContext context) {
-    final errors = controller.selectedLink.value!.domainErrors
-        .where(
-          (error) => domainAssetLinksJsonFileErrors.contains(error),
-        )
-        .toList();
+    final errors =
+        controller.selectedLink.value!.domainErrors
+            .where((error) => domainAssetLinksJsonFileErrors.contains(error))
+            .toList();
     return ExpansionTile(
       controlAffinity: ListTileControlAffinity.leading,
       title: _VerifiedOrErrorText(
@@ -276,9 +274,10 @@
 
   @override
   Widget build(BuildContext context) {
-    final errors = controller.selectedLink.value!.domainErrors
-        .where((error) => domainAndroidHostingErrors.contains(error))
-        .toList();
+    final errors =
+        controller.selectedLink.value!.domainErrors
+            .where((error) => domainAndroidHostingErrors.contains(error))
+            .toList();
     return ExpansionTile(
       controlAffinity: ListTileControlAffinity.leading,
       title: _VerifiedOrErrorText(
@@ -298,9 +297,7 @@
 }
 
 class _Fingerprint extends StatelessWidget {
-  const _Fingerprint({
-    required this.controller,
-  });
+  const _Fingerprint({required this.controller});
 
   final DeepLinksController controller;
 
@@ -328,10 +325,7 @@
     return ExpansionTile(
       controlAffinity: ListTileControlAffinity.leading,
       initiallyExpanded: isError,
-      title: _VerifiedOrErrorText(
-        title,
-        isError: isError,
-      ),
+      title: _VerifiedOrErrorText(title, isError: isError),
       children: [
         _IssuesBorderWrap(
           children: [
@@ -343,9 +337,7 @@
               const SizedBox(height: denseSpacing),
             ],
             if (isError) ...[
-              const Text(
-                'Issue: no fingerprint detected locally or on PDC',
-              ),
+              const Text('Issue: no fingerprint detected locally or on PDC'),
               const SizedBox(height: denseSpacing),
               const Text('Fix guide:'),
               const SizedBox(height: denseSpacing),
@@ -367,9 +359,7 @@
 }
 
 class _LocalFingerprint extends StatelessWidget {
-  const _LocalFingerprint({
-    required this.controller,
-  });
+  const _LocalFingerprint({required this.controller});
 
   final DeepLinksController controller;
 
@@ -382,39 +372,38 @@
         const SizedBox(height: intermediateSpacing),
         controller.localFingerprint.value == null
             ? TextField(
-                decoration: const InputDecoration(
-                  labelText: 'Enter your local fingerprint',
-                  hintText:
-                      'eg: A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4',
-                  filled: true,
-                ),
-                onSubmitted: (fingerprint) async {
-                  final validFingerprintAdded =
-                      controller.addLocalFingerprint(fingerprint);
-
-                  if (!validFingerprintAdded) {
-                    await showDialog(
-                      context: context,
-                      builder: (_) {
-                        return const DevToolsDialog(
-                          title: Text('This is not a valid fingerprint'),
-                          content: Text(
-                            'A valid fingerprint consists of 32 pairs of hexadecimal digits separated by colons.'
-                            'It should be the same encoding and format as in the assetlinks.json',
-                          ),
-                          actions: [
-                            DialogCloseButton(),
-                          ],
-                        );
-                      },
-                    );
-                  }
-                },
-              )
-            : _CodeCard(
-                content: controller.localFingerprint.value,
-                hasCopyAction: false,
+              decoration: const InputDecoration(
+                labelText: 'Enter your local fingerprint',
+                hintText:
+                    'eg: A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4',
+                filled: true,
               ),
+              onSubmitted: (fingerprint) async {
+                final validFingerprintAdded = controller.addLocalFingerprint(
+                  fingerprint,
+                );
+
+                if (!validFingerprintAdded) {
+                  await showDialog(
+                    context: context,
+                    builder: (_) {
+                      return const DevToolsDialog(
+                        title: Text('This is not a valid fingerprint'),
+                        content: Text(
+                          'A valid fingerprint consists of 32 pairs of hexadecimal digits separated by colons.'
+                          'It should be the same encoding and format as in the assetlinks.json',
+                        ),
+                        actions: [DialogCloseButton()],
+                      );
+                    },
+                  );
+                }
+              },
+            )
+            : _CodeCard(
+              content: controller.localFingerprint.value,
+              hasCopyAction: false,
+            ),
       ],
     );
   }
@@ -442,10 +431,7 @@
 }
 
 class _CodeCard extends StatelessWidget {
-  const _CodeCard({
-    this.content,
-    this.hasCopyAction = true,
-  });
+  const _CodeCard({this.content, this.hasCopyAction = true});
 
   final String? content;
   final bool hasCopyAction;
@@ -457,26 +443,25 @@
       elevation: 0.0,
       child: Padding(
         padding: const EdgeInsets.all(denseSpacing),
-        child: content != null
-            ? Row(
-                mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  Flexible(child: SelectionArea(child: Text(content!))),
-                  if (hasCopyAction)
-                    CopyToClipboardControl(dataProvider: () => content),
-                ],
-              )
-            : const CenteredCircularProgressIndicator(),
+        child:
+            content != null
+                ? Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Flexible(child: SelectionArea(child: Text(content!))),
+                    if (hasCopyAction)
+                      CopyToClipboardControl(dataProvider: () => content),
+                  ],
+                )
+                : const CenteredCircularProgressIndicator(),
       ),
     );
   }
 }
 
 class _GenerateAssetLinksPanel extends StatelessWidget {
-  const _GenerateAssetLinksPanel({
-    required this.controller,
-  });
+  const _GenerateAssetLinksPanel({required this.controller});
 
   final DeepLinksController controller;
 
@@ -485,17 +470,13 @@
     final theme = Theme.of(context);
     return ValueListenableBuilder(
       valueListenable: controller.generatedAssetLinksForSelectedLink,
-      builder: (
-        _,
-        GenerateAssetLinksResult? generatedAssetLinks,
-        __,
-      ) {
+      builder: (_, GenerateAssetLinksResult? generatedAssetLinks, _) {
         return (generatedAssetLinks != null &&
                 generatedAssetLinks.errorCode.isNotEmpty)
             ? Text(
-                'Not able to generate assetlinks.json, because the app ${controller.applicationId} is not uploaded to Google Play.',
-                style: theme.subtleTextStyle,
-              )
+              'Not able to generate assetlinks.json, because the app ${controller.applicationId} is not uploaded to Google Play.',
+              style: theme.subtleTextStyle,
+            )
             : _CodeCard(content: generatedAssetLinks?.generatedString);
       },
     );
@@ -503,10 +484,7 @@
 }
 
 class _FailureDetails extends StatelessWidget {
-  const _FailureDetails({
-    required this.errors,
-    this.oneFixGuideForAll,
-  });
+  const _FailureDetails({required this.errors, this.oneFixGuideForAll});
 
   final List<CommonError> errors;
   final String? oneFixGuideForAll;
@@ -520,28 +498,19 @@
           const SizedBox(height: densePadding),
           Text('Issue: ${error.title}'),
           const SizedBox(height: densePadding),
-          Text(
-            error.explanation,
-            style: Theme.of(context).subtleTextStyle,
-          ),
+          Text(error.explanation, style: Theme.of(context).subtleTextStyle),
           if (oneFixGuideForAll == null) ...[
             const SizedBox(height: defaultSpacing),
             const Text('Fix guide:'),
             const SizedBox(height: densePadding),
-            Text(
-              error.fixDetails,
-              style: Theme.of(context).subtleTextStyle,
-            ),
+            Text(error.fixDetails, style: Theme.of(context).subtleTextStyle),
           ],
         ],
         if (oneFixGuideForAll != null) ...[
           const SizedBox(height: defaultSpacing),
           const Text('Fix guide:'),
           const SizedBox(height: densePadding),
-          Text(
-            oneFixGuideForAll!,
-            style: Theme.of(context).subtleTextStyle,
-          ),
+          Text(oneFixGuideForAll!, style: Theme.of(context).subtleTextStyle),
         ],
       ],
     );
@@ -549,9 +518,7 @@
 }
 
 class _DomainAssociatedLinksPanel extends StatelessWidget {
-  const _DomainAssociatedLinksPanel({
-    required this.controller,
-  });
+  const _DomainAssociatedLinksPanel({required this.controller});
 
   final DeepLinksController controller;
 
@@ -562,10 +529,7 @@
     return Column(
       crossAxisAlignment: CrossAxisAlignment.stretch,
       children: [
-        Text(
-          'Associated deep link URL',
-          style: theme.textTheme.titleMedium,
-        ),
+        Text('Associated deep link URL', style: theme.textTheme.titleMedium),
         Card(
           color: theme.colorScheme.surface,
           shape: const RoundedRectangleBorder(),
@@ -573,27 +537,28 @@
             padding: const EdgeInsets.all(denseSpacing),
             child: Column(
               crossAxisAlignment: CrossAxisAlignment.start,
-              children: linkData.associatedPath
-                  .map(
-                    (path) => Padding(
-                      padding: const EdgeInsets.symmetric(
-                        vertical: denseRowSpacing,
-                      ),
-                      child: Row(
-                        children: <Widget>[
-                          if (linkData.domainErrors.isNotEmpty)
-                            Icon(
-                              Icons.error,
-                              color: theme.colorScheme.error,
-                              size: defaultIconSize,
-                            ),
-                          const SizedBox(width: denseSpacing),
-                          Text('${linkData.domain}$path'),
-                        ],
-                      ),
-                    ),
-                  )
-                  .toList(),
+              children:
+                  linkData.associatedPath
+                      .map(
+                        (path) => Padding(
+                          padding: const EdgeInsets.symmetric(
+                            vertical: denseRowSpacing,
+                          ),
+                          child: Row(
+                            children: <Widget>[
+                              if (linkData.domainErrors.isNotEmpty)
+                                Icon(
+                                  Icons.error,
+                                  color: theme.colorScheme.error,
+                                  size: defaultIconSize,
+                                ),
+                              const SizedBox(width: denseSpacing),
+                              Text('${linkData.domain}$path'),
+                            ],
+                          ),
+                        ),
+                      )
+                      .toList(),
             ),
           ),
         ),
@@ -630,10 +595,7 @@
       crossAxisAlignment: CrossAxisAlignment.stretch,
       children: [
         const SizedBox(height: intermediateSpacing),
-        Text(
-          'App check',
-          style: theme.textTheme.titleMedium,
-        ),
+        Text('App check', style: theme.textTheme.titleMedium),
         const SizedBox(height: intermediateSpacing),
         const _CheckTableHeader(),
         const Divider(height: 1.0),
@@ -691,10 +653,7 @@
       crossAxisAlignment: CrossAxisAlignment.stretch,
       children: [
         const SizedBox(height: intermediateSpacing),
-        Text(
-          'App check',
-          style: theme.textTheme.titleMedium,
-        ),
+        Text('App check', style: theme.textTheme.titleMedium),
         const SizedBox(height: intermediateSpacing),
         const _CheckTableHeader(),
         const Divider(height: 1.0),
@@ -714,9 +673,10 @@
   @override
   Widget build(BuildContext context) {
     final linkData = controller.selectedLink.value!;
-    final errors = manifestFileErrors
-        .where((error) => linkData.pathErrors.contains(error))
-        .toList();
+    final errors =
+        manifestFileErrors
+            .where((error) => linkData.pathErrors.contains(error))
+            .toList();
 
     return _CheckExpansionTile(
       os: PlatformOS.android,
@@ -877,14 +837,11 @@
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
     return hasError
-        ? Text(
-            'Check failed',
-            style: theme.errorTextStyle,
-          )
+        ? Text('Check failed', style: theme.errorTextStyle)
         : Text(
-            'No issues found',
-            style: TextStyle(color: theme.colorScheme.green),
-          );
+          'No issues found',
+          style: TextStyle(color: theme.colorScheme.green),
+        );
   }
 }
 
@@ -899,15 +856,15 @@
       children: [
         isError
             ? Icon(
-                Icons.error,
-                color: Theme.of(context).colorScheme.error,
-                size: defaultIconSize,
-              )
+              Icons.error,
+              color: Theme.of(context).colorScheme.error,
+              size: defaultIconSize,
+            )
             : Icon(
-                Icons.verified,
-                color: Theme.of(context).colorScheme.green,
-                size: defaultIconSize,
-              ),
+              Icons.verified,
+              color: Theme.of(context).colorScheme.green,
+              size: defaultIconSize,
+            ),
         const SizedBox(width: denseSpacing),
         Text(text),
       ],
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_breadcrumbs.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_breadcrumbs.dart
index ff5b907..19d876b 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_breadcrumbs.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_breadcrumbs.dart
@@ -5,10 +5,10 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/common_widgets.dart';
 import '../../shared/console/eval/inspector_tree.dart';
-import '../../shared/diagnostics_text_styles.dart';
+import '../../shared/primitives/diagnostics_text_styles.dart';
 import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
 
 class InspectorBreadcrumbNavigator extends StatelessWidget {
   const InspectorBreadcrumbNavigator({
@@ -37,21 +37,19 @@
       child: Padding(
         padding: const EdgeInsets.symmetric(horizontal: 6),
         child: Row(
-          children: breadcrumbs.map((item) {
-            if (item.isChevron) {
-              return Icon(
-                Icons.chevron_right,
-                size: defaultIconSize,
-              );
-            }
+          children:
+              breadcrumbs.map((item) {
+                if (item.isChevron) {
+                  return Icon(Icons.chevron_right, size: defaultIconSize);
+                }
 
-            return Flexible(
-              child: _InspectorBreadcrumb(
-                data: item,
-                onTap: () => onTap(item.node),
-              ),
-            );
-          }).toList(),
+                return Flexible(
+                  child: _InspectorBreadcrumb(
+                    data: item,
+                    onTap: () => onTap(item.node),
+                  ),
+                );
+              }).toList(),
         ),
       ),
     );
@@ -61,33 +59,32 @@
     List<InspectorTreeNode> nodes,
   ) {
     final lastNode = nodes.safeLast;
-    final items = nodes.map((node) {
-      return _InspectorBreadcrumbData.wrap(
-        node: node,
-        isSelected: node == lastNode,
-      );
-    }).toList();
+    final items =
+        nodes.map((node) {
+          return _InspectorBreadcrumbData.wrap(
+            node: node,
+            isSelected: node == lastNode,
+          );
+        }).toList();
     List<_InspectorBreadcrumbData> breadcrumbs;
-    breadcrumbs = items.length > _maxNumberOfBreadcrumbs
-        ? [
-            items[0],
-            _InspectorBreadcrumbData.more(),
-            ...items.sublist(
-              items.length - _maxNumberOfBreadcrumbs + 1,
-              items.length,
-            ),
-          ]
-        : items;
+    breadcrumbs =
+        items.length > _maxNumberOfBreadcrumbs
+            ? [
+              items[0],
+              _InspectorBreadcrumbData.more(),
+              ...items.sublist(
+                items.length - _maxNumberOfBreadcrumbs + 1,
+                items.length,
+              ),
+            ]
+            : items;
 
     return breadcrumbs.joinWith(_InspectorBreadcrumbData.chevron());
   }
 }
 
 class _InspectorBreadcrumb extends StatelessWidget {
-  const _InspectorBreadcrumb({
-    required this.data,
-    required this.onTap,
-  });
+  const _InspectorBreadcrumb({required this.data, required this.onTap});
 
   static const _iconScale = 0.75;
 
@@ -100,19 +97,21 @@
       data.text,
       maxLines: 1,
       overflow: TextOverflow.ellipsis,
-      style: DiagnosticsTextStyles.regular(Theme.of(context).colorScheme)
-          .copyWith(fontSize: scaleByFontFactor(11)),
+      style: DiagnosticsTextStyles.regular(
+        Theme.of(context).colorScheme,
+      ).copyWith(fontSize: scaleByFontFactor(11)),
     );
 
-    final icon = data.icon == null
-        ? null
-        : Transform.scale(
-            scale: _iconScale,
-            child: Padding(
-              padding: const EdgeInsets.only(right: iconPadding),
-              child: data.icon,
-            ),
-          );
+    final icon =
+        data.icon == null
+            ? null
+            : Transform.scale(
+              scale: _iconScale,
+              child: Padding(
+                padding: const EdgeInsets.only(right: iconPadding),
+                child: data.icon,
+              ),
+            );
 
     return InkWell(
       onTap: data.isClickable ? onTap : null,
@@ -124,16 +123,14 @@
         ),
         decoration: BoxDecoration(
           borderRadius: defaultBorderRadius,
-          color: data.isSelected
-              ? Theme.of(context).colorScheme.selectedRowBackgroundColor
-              : Colors.transparent,
+          color:
+              data.isSelected
+                  ? Theme.of(context).colorScheme.selectedRowBackgroundColor
+                  : Colors.transparent,
         ),
         child: Row(
           mainAxisSize: MainAxisSize.min,
-          children: [
-            if (icon != null) icon,
-            Flexible(child: text),
-          ],
+          children: [if (icon != null) icon, Flexible(child: text)],
         ),
       ),
     );
@@ -191,10 +188,7 @@
 
   Widget? get icon {
     if (alternativeIcon != null) {
-      return Icon(
-        _breadcrumbSeparatorIcon,
-        size: defaultIconSize,
-      );
+      return Icon(_breadcrumbSeparatorIcon, size: defaultIconSize);
     }
 
     return node?.diagnostic?.icon;
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
index 33d7161..b0ef730 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
@@ -29,8 +29,8 @@
 import '../../shared/diagnostics/inspector_service.dart';
 import '../../shared/diagnostics/primitives/instance_ref.dart';
 import '../../shared/globals.dart';
+import '../../shared/primitives/query_parameters.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/query_parameters.dart';
 import '../inspector_shared/inspector_screen.dart';
 import 'inspector_tree_controller.dart';
 
@@ -51,9 +51,7 @@
     unawaited(_init(detailsTree: detailsTree));
   }
 
-  Future<void> _init({
-    InspectorTreeController? detailsTree,
-  }) async {
+  Future<void> _init({InspectorTreeController? detailsTree}) async {
     _refreshRateLimiter = RateLimiter(refreshFramesPerSecond, refresh);
 
     inspectorTree.config = InspectorTreeConfig(
@@ -62,14 +60,15 @@
       onExpand: _onExpand,
       onClientActiveChange: _onClientChange,
     );
-    details = isSummaryTree
-        ? InspectorController(
-            inspectorTree: detailsTree!,
-            treeType: treeType,
-            parent: this,
-            isSummaryTree: false,
-          )
-        : null;
+    details =
+        isSummaryTree
+            ? InspectorController(
+              inspectorTree: detailsTree!,
+              treeType: treeType,
+              parent: this,
+              isSummaryTree: false,
+            )
+            : null;
 
     await serviceConnection.serviceManager.onServiceAvailable;
 
@@ -105,10 +104,10 @@
         if (_supportsToggleSelectWidgetMode.value) {
           serviceConnection.serviceManager.serviceExtensionManager
               .setServiceExtensionState(
-            extensions.enableOnDeviceInspector.extension,
-            enabled: true,
-            value: true,
-          );
+                extensions.enableOnDeviceInspector.extension,
+                enabled: true,
+                value: true,
+              );
         }
       });
     }
@@ -150,9 +149,10 @@
 
   IsolateRef? _mainIsolate;
 
-  ValueListenable<bool> get _supportsToggleSelectWidgetMode =>
-      serviceConnection.serviceManager.serviceExtensionManager
-          .hasServiceExtension(extensions.toggleSelectWidgetMode.extension);
+  ValueListenable<bool> get _supportsToggleSelectWidgetMode => serviceConnection
+      .serviceManager
+      .serviceExtensionManager
+      .hasServiceExtension(extensions.toggleSelectWidgetMode.extension);
 
   void _onClientChange(bool added) {
     if (!added && _clientCount == 0) {
@@ -447,9 +447,10 @@
     treeGroups.cancelNext();
     try {
       final group = treeGroups.next;
-      final node = await (detailsSubtree
-          ? group.getDetailsSubtree(subtreeRoot, subtreeDepth: subtreeDepth)
-          : group.getRoot(treeType, isSummaryTree: true));
+      final node =
+          await (detailsSubtree
+              ? group.getDetailsSubtree(subtreeRoot, subtreeDepth: subtreeDepth)
+              : group.getRoot(treeType, isSummaryTree: true));
       if (node == null || group.disposed || _disposed) {
         return;
       }
@@ -725,15 +726,17 @@
   void _updateSelectedErrorFromNode(InspectorTreeNode? node) {
     final inspectorRef = node?.diagnostic?.valueRef.id;
 
-    final errors = serviceConnection.errorBadgeManager
-        .erroredItemsForPage(InspectorScreen.id)
-        .value;
+    final errors =
+        serviceConnection.errorBadgeManager
+            .erroredItemsForPage(InspectorScreen.id)
+            .value;
 
     // Check whether the node that was just selected has any errors associated
     // with it.
-    var errorIndex = inspectorRef != null
-        ? errors.keys.toList().indexOf(inspectorRef)
-        : null;
+    var errorIndex =
+        inspectorRef != null
+            ? errors.keys.toList().indexOf(inspectorRef)
+            : null;
     if (errorIndex == -1) {
       errorIndex = null;
     }
@@ -743,8 +746,10 @@
     if (errorIndex != null) {
       // Mark the error as "seen" as this will render slightly differently
       // so the user can track which errored nodes they've viewed.
-      serviceConnection.errorBadgeManager
-          .markErrorAsRead(InspectorScreen.id, errors[inspectorRef!]!);
+      serviceConnection.errorBadgeManager.markErrorAsRead(
+        InspectorScreen.id,
+        errors[inspectorRef!]!,
+      );
       // Also clear the error badge since new errors may have arrived while
       // the inspector was visible (normally they're cleared when visiting
       // the screen) and visiting an errored node seems an appropriate
@@ -757,9 +762,10 @@
   void selectErrorByIndex(int index) {
     _selectedErrorIndex.value = index;
 
-    final errors = serviceConnection.errorBadgeManager
-        .erroredItemsForPage(InspectorScreen.id)
-        .value;
+    final errors =
+        serviceConnection.errorBadgeManager
+            .erroredItemsForPage(InspectorScreen.id)
+            .value;
 
     unawaited(
       updateSelectionFromService(
@@ -807,7 +813,8 @@
       unawaited(_addNodeToConsole(node));
 
       // Don't reroot if the selected value is already visible in the details tree.
-      final maybeReroot = isSummaryTree &&
+      final maybeReroot =
+          isSummaryTree &&
           details != null &&
           selectedDiagnostic != null &&
           !details!.hasDiagnosticsValue(selectedDiagnostic!.valueRef);
@@ -824,8 +831,9 @@
         if (isSummaryTree && detailsLocal != null) {
           detailsLocal.selectAndShowNode(selectedDiagnostic);
         } else if (parantLocal != null) {
-          parantLocal
-              .selectAndShowNode(firstAncestorInParentTree(selectedNode.value));
+          parantLocal.selectAndShowNode(
+            firstAncestorInParentTree(selectedNode.value),
+          );
         }
       }
     }
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_data_models.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_data_models.dart
index 9cc4d96..e7310fc 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_data_models.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_data_models.dart
@@ -72,17 +72,18 @@
   }
 
   List<double> transformToRenderSize(double largestRenderSize) => [
-        for (final s in sizes)
-          (s - smallestSize) *
-                  (largestRenderSize - smallestRenderSize) /
-                  (largestSize - smallestSize) +
-              smallestRenderSize,
-      ];
+    for (final s in sizes)
+      (s - smallestSize) *
+              (largestRenderSize - smallestRenderSize) /
+              (largestSize - smallestSize) +
+          smallestRenderSize,
+  ];
 
   var renderSizes = transformToRenderSize(largestRenderSize);
 
   if (useMaxSizeAvailable && sum(renderSizes) < maxSizeAvailable) {
-    largestRenderSize = (maxSizeAvailable - n * smallestRenderSize) *
+    largestRenderSize =
+        (maxSizeAvailable - n * smallestRenderSize) *
             (largestSize - smallestSize) /
             sum([for (final s in sizes) s - smallestSize]) +
         smallestRenderSize;
@@ -95,19 +96,21 @@
 /// Represents parsed layout information for a specific [RemoteDiagnosticsNode].
 class LayoutProperties {
   LayoutProperties(this.node, {int copyLevel = 1})
-      : description = node.description,
-        size = node.size!,
-        constraints = node.constraints,
-        isFlex = node.isFlex,
-        flexFactor = node.flexFactor,
-        flexFit = node.flexFit,
-        children = copyLevel == 0
-            ? []
-            : node.childrenNow
-                .map(
-                  (child) => LayoutProperties(child, copyLevel: copyLevel - 1),
-                )
-                .toList(growable: false) {
+    : description = node.description,
+      size = node.size!,
+      constraints = node.constraints,
+      isFlex = node.isFlex,
+      flexFactor = node.flexFactor,
+      flexFit = node.flexFit,
+      children =
+          copyLevel == 0
+              ? []
+              : node.childrenNow
+                  .map(
+                    (child) =>
+                        LayoutProperties(child, copyLevel: copyLevel - 1),
+                  )
+                  .toList(growable: false) {
     for (final child in children) {
       child.parent = this;
     }
@@ -170,10 +173,10 @@
     if (constraintsLocal == null) return '';
     return constraintsLocal.hasBoundedWidth
         ? describeAxis(
-            constraintsLocal.minWidth,
-            constraintsLocal.maxWidth,
-            'w',
-          )
+          constraintsLocal.minWidth,
+          constraintsLocal.maxWidth,
+          'w',
+        )
         : 'width is unconstrained';
   }
 
@@ -182,10 +185,10 @@
     if (constraintsLocal == null) return '';
     return constraintsLocal.hasBoundedHeight
         ? describeAxis(
-            constraintsLocal.minHeight,
-            constraintsLocal.maxHeight,
-            'h',
-          )
+          constraintsLocal.minHeight,
+          constraintsLocal.maxHeight,
+          'h',
+        )
         : 'height is unconstrained';
   }
 
@@ -248,10 +251,7 @@
 ///
 /// See also:
 /// * [OverflowIndicatorPainter]
-enum OverflowSide {
-  right,
-  bottom,
-}
+enum OverflowSide { right, bottom }
 
 // TODO(jacobr): is it possible to overflow on multiple sides?
 // TODO(jacobr): do we need to worry about overflowing on the left side in RTL
@@ -356,8 +356,9 @@
 
   static FlexLayoutProperties _buildNode(RemoteDiagnosticsNode node) {
     final renderObjectJson = node.renderObject!.json;
-    final properties = (renderObjectJson['properties'] as List<Object?>)
-        .cast<Map<String, Object?>>();
+    final properties =
+        (renderObjectJson['properties'] as List<Object?>)
+            .cast<Map<String, Object?>>();
 
     final data = {
       for (final property in properties)
@@ -367,16 +368,19 @@
     return FlexLayoutProperties._fromNode(
       node,
       direction: _directionUtils.enumEntry(data['direction']) ?? Axis.vertical,
-      mainAxisAlignment:
-          _mainAxisAlignmentUtils.enumEntry(data['mainAxisAlignment']),
+      mainAxisAlignment: _mainAxisAlignmentUtils.enumEntry(
+        data['mainAxisAlignment'],
+      ),
       mainAxisSize: _mainAxisSizeUtils.enumEntry(data['mainAxisSize']),
-      crossAxisAlignment:
-          _crossAxisAlignmentUtils.enumEntry(data['crossAxisAlignment']),
-      textDirection: _textDirectionUtils.enumEntry(data['textDirection']) ??
+      crossAxisAlignment: _crossAxisAlignmentUtils.enumEntry(
+        data['crossAxisAlignment'],
+      ),
+      textDirection:
+          _textDirectionUtils.enumEntry(data['textDirection']) ??
           TextDirection.ltr,
       verticalDirection:
           _verticalDirectionUtils.enumEntry(data['verticalDirection']) ??
-              VerticalDirection.down,
+          VerticalDirection.down,
       textBaseline: _textBaselineUtils.enumEntry(data['textBaseline']),
     );
   }
@@ -417,10 +421,11 @@
 
   num get totalFlex {
     if (children.isEmpty) return 0;
-    _totalFlex ??= children
-        .map((child) => child.flexFactor ?? 0)
-        .reduce((value, element) => value + element)
-        .toInt();
+    _totalFlex ??=
+        children
+            .map((child) => child.flexFactor ?? 0)
+            .reduce((value, element) => value + element)
+            .toInt();
     return _totalFlex!;
   }
 
@@ -554,11 +559,12 @@
         );
       } else {
         // uniform cross axis sizes.
-        double size = crossAxisAlignment == CrossAxisAlignment.stretch
-            ? maxSizeAvailable(axis)
-            : largestSize /
-                math.max(dimension(axis), 1.0) *
-                maxSizeAvailable(axis);
+        double size =
+            crossAxisAlignment == CrossAxisAlignment.stretch
+                ? maxSizeAvailable(axis)
+                : largestSize /
+                    math.max(dimension(axis), 1.0) *
+                    maxSizeAvailable(axis);
         size = math.max(size, smallestRenderSize(axis));
         return sizes.map((_) => size).toList();
       }
@@ -567,9 +573,10 @@
     final widths = renderSizes(Axis.horizontal);
     final heights = renderSizes(Axis.vertical);
 
-    final renderFreeSpace = freeSpace > 0.0
-        ? (isMainAxisHorizontal ? widths.last : heights.last)
-        : 0.0;
+    final renderFreeSpace =
+        freeSpace > 0.0
+            ? (isMainAxisHorizontal ? widths.last : heights.last)
+            : 0.0;
 
     final renderLeadingSpace = leadingSpace(renderFreeSpace);
     final renderBetweenSpace = betweenSpace(renderFreeSpace);
@@ -616,11 +623,11 @@
     for (var i = 0; i < children.length; ++i) {
       childrenRenderProps.add(
         RenderProperties(
-          axis: direction,
-          size: Size(widths[i], heights[i]),
-          offset: Offset.zero,
-          realSize: displayChildren[i].size,
-        )
+            axis: direction,
+            size: Size(widths[i], heights[i]),
+            offset: Offset.zero,
+            realSize: displayChildren[i].size,
+          )
           ..mainAxisOffset = calculateMainAxisOffset(i)
           ..crossAxisOffset = calculateCrossAxisOffset(i)
           ..layoutProperties = displayChildren[i],
@@ -661,7 +668,8 @@
         displayMainAxisAlignment != MainAxisAlignment.end) {
       spaces.add(
         renderPropsWithFullCrossAxisDimension.clone()
-          ..mainAxisOffset = childrenRenderProps.last.mainAxisDimension +
+          ..mainAxisOffset =
+              childrenRenderProps.last.mainAxisDimension +
               childrenRenderProps.last.mainAxisOffset
           ..mainAxisDimension = renderLeadingSpace
           ..mainAxisRealDimension = actualLeadingSpace,
@@ -699,13 +707,15 @@
         spaces.add(space.clone()..crossAxisOffset = 0.0);
         spaces.add(
           space.clone()
-            ..crossAxisOffset = renderProperties.crossAxisDimension +
+            ..crossAxisOffset =
+                renderProperties.crossAxisDimension +
                 renderProperties.crossAxisOffset,
         );
       } else {
-        space.crossAxisOffset = crossAxisAlignment == CrossAxisAlignment.end
-            ? 0
-            : renderProperties.crossAxisDimension;
+        space.crossAxisOffset =
+            crossAxisAlignment == CrossAxisAlignment.end
+                ? 0
+                : renderProperties.crossAxisDimension;
         spaces.add(space);
       }
     }
@@ -713,18 +723,24 @@
   }
 
   static final _directionUtils = EnumUtils<Axis>(Axis.values);
-  static final _mainAxisAlignmentUtils =
-      EnumUtils<MainAxisAlignment>(MainAxisAlignment.values);
-  static final _mainAxisSizeUtils =
-      EnumUtils<MainAxisSize>(MainAxisSize.values);
-  static final _crossAxisAlignmentUtils =
-      EnumUtils<CrossAxisAlignment>(CrossAxisAlignment.values);
-  static final _textDirectionUtils =
-      EnumUtils<TextDirection>(TextDirection.values);
-  static final _verticalDirectionUtils =
-      EnumUtils<VerticalDirection>(VerticalDirection.values);
-  static final _textBaselineUtils =
-      EnumUtils<TextBaseline>(TextBaseline.values);
+  static final _mainAxisAlignmentUtils = EnumUtils<MainAxisAlignment>(
+    MainAxisAlignment.values,
+  );
+  static final _mainAxisSizeUtils = EnumUtils<MainAxisSize>(
+    MainAxisSize.values,
+  );
+  static final _crossAxisAlignmentUtils = EnumUtils<CrossAxisAlignment>(
+    CrossAxisAlignment.values,
+  );
+  static final _textDirectionUtils = EnumUtils<TextDirection>(
+    TextDirection.values,
+  );
+  static final _verticalDirectionUtils = EnumUtils<VerticalDirection>(
+    VerticalDirection.values,
+  );
+  static final _textBaselineUtils = EnumUtils<TextBaseline>(
+    TextBaseline.values,
+  );
 }
 
 /// RenderProperties contains information for rendering a [LayoutProperties] node
@@ -736,12 +752,12 @@
     Size? realSize,
     this.layoutProperties,
     this.isFreeSpace = false,
-  })  : width = size?.width ?? 0.0,
-        height = size?.height ?? 0.0,
-        realWidth = realSize?.width ?? 0.0,
-        realHeight = realSize?.height ?? 0.0,
-        dx = offset?.dx ?? 0.0,
-        dy = offset?.dy ?? 0.0;
+  }) : width = size?.width ?? 0.0,
+       height = size?.height ?? 0.0,
+       realWidth = realSize?.width ?? 0.0,
+       realHeight = realSize?.height ?? 0.0,
+       dx = offset?.dx ?? 0.0,
+       dy = offset?.dy ?? 0.0;
 
   final Axis axis;
 
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart
index 8497e43..bbf47db 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart
@@ -13,11 +13,11 @@
 import '../../service/service_extensions.dart' as extensions;
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/console/eval/inspector_tree.dart';
-import '../../shared/error_badge_manager.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/error_badge_manager.dart';
 import '../../shared/primitives/blocking_action_mixin.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/search.dart';
 import '../inspector_shared/inspector_controls.dart';
 import '../inspector_shared/inspector_screen.dart';
@@ -143,19 +143,14 @@
       initialFractions: const [0.33, 0.67],
       children: [
         summaryTree,
-        InspectorDetails(
-          detailsTree: detailsTree,
-          controller: controller,
-        ),
+        InspectorDetails(detailsTree: detailsTree, controller: controller),
       ],
     );
     return Column(
       children: <Widget>[
         const InspectorControls(),
         const SizedBox(height: intermediateSpacing),
-        Expanded(
-          child: widgetTrees,
-        ),
+        Expanded(child: widgetTrees),
       ],
     );
   }
@@ -171,25 +166,28 @@
                 constraints: constraints,
                 onRefreshInspectorPressed: _refreshInspector,
                 onSearchVisibleToggle: _onSearchVisibleToggle,
-                searchFieldBuilder: () =>
-                    StatelessSearchField<InspectorTreeRow>(
-                  controller: _summaryTreeController,
-                  searchFieldEnabled: true,
-                  shouldRequestFocus: searchVisible,
-                  supportsNavigation: true,
-                  onClose: _onSearchVisibleToggle,
-                ),
+                searchFieldBuilder:
+                    () => StatelessSearchField<InspectorTreeRow>(
+                      controller: _summaryTreeController,
+                      searchFieldEnabled: true,
+                      shouldRequestFocus: searchVisible,
+                      supportsNavigation: true,
+                      onClose: _onSearchVisibleToggle,
+                    ),
               ),
               Expanded(
                 child: ValueListenableBuilder(
                   valueListenable: serviceConnection.errorBadgeManager
                       .erroredItemsForPage(InspectorScreen.id),
-                  builder:
-                      (_, LinkedHashMap<String, DevToolsError> errors, __) {
-                    final inspectableErrors = errors.map(
-                      (key, value) =>
-                          MapEntry(key, value as InspectableWidgetError),
-                    ) as LinkedHashMap<String, InspectableWidgetError>;
+                  builder: (_, LinkedHashMap<String, DevToolsError> errors, _) {
+                    final inspectableErrors =
+                        errors.map(
+                              (key, value) => MapEntry(
+                                key,
+                                value as InspectableWidgetError,
+                              ),
+                            )
+                            as LinkedHashMap<String, InspectableWidgetError>;
                     return Stack(
                       children: [
                         InspectorTree(
@@ -203,15 +201,17 @@
                         if (errors.isNotEmpty)
                           ValueListenableBuilder<int?>(
                             valueListenable: controller.selectedErrorIndex,
-                            builder: (_, selectedErrorIndex, __) => Positioned(
-                              top: 0,
-                              right: 0,
-                              child: ErrorNavigator(
-                                errors: inspectableErrors,
-                                errorIndex: selectedErrorIndex,
-                                onSelectError: controller.selectErrorByIndex,
-                              ),
-                            ),
+                            builder:
+                                (_, selectedErrorIndex, _) => Positioned(
+                                  top: 0,
+                                  right: 0,
+                                  child: ErrorNavigator(
+                                    errors: inspectableErrors,
+                                    errorIndex: selectedErrorIndex,
+                                    onSelectError:
+                                        controller.selectErrorByIndex,
+                                  ),
+                                ),
                           ),
                       ],
                     );
@@ -274,14 +274,8 @@
         if (!controller.firstInspectorTreeLoadCompleted) {
           // We do not want to complete this timing operation because the force
           // refresh will skew the results.
-          ga.cancelTimingOperation(
-            InspectorScreen.id,
-            gac.pageReady,
-          );
-          ga.select(
-            gac.inspector,
-            gac.refreshEmptyTree,
-          );
+          ga.cancelTimingOperation(InspectorScreen.id, gac.pageReady);
+          ga.select(gac.inspector, gac.refreshEmptyTree);
           controller.firstInspectorTreeLoadCompleted = true;
         }
         await controller.onForceRefresh();
@@ -325,18 +319,18 @@
               ),
               ...!isSearchVisible
                   ? [
-                      const Spacer(),
-                      ToolbarAction(
-                        icon: Icons.search,
-                        onPressed: onSearchVisibleToggle,
-                        tooltip: 'Search Tree',
-                      ),
-                    ]
+                    const Spacer(),
+                    ToolbarAction(
+                      icon: Icons.search,
+                      onPressed: onSearchVisibleToggle,
+                      tooltip: 'Search Tree',
+                    ),
+                  ]
                   : [
-                      constraints.maxWidth >= _searchBreakpoint
-                          ? _buildSearchControls()
-                          : const Spacer(),
-                    ],
+                    constraints.maxWidth >= _searchBreakpoint
+                        ? _buildSearchControls()
+                        : const Spacer(),
+                  ],
               ToolbarAction(
                 icon: Icons.refresh,
                 onPressed: onRefreshInspectorPressed,
@@ -346,10 +340,7 @@
           ),
         ),
         if (isSearchVisible && constraints.maxWidth < _searchBreakpoint)
-          _controlsContainer(
-            context,
-            Row(children: [_buildSearchControls()]),
-          ),
+          _controlsContainer(context, Row(children: [_buildSearchControls()])),
       ],
     );
   }
@@ -358,9 +349,7 @@
     return Container(
       height: defaultHeaderHeight,
       decoration: BoxDecoration(
-        border: Border(
-          bottom: defaultBorderSide(Theme.of(context)),
-        ),
+        border: Border(bottom: defaultBorderSide(Theme.of(context))),
       ),
       child: child,
     );
@@ -393,9 +382,10 @@
   @override
   Widget build(BuildContext context) {
     final colorScheme = Theme.of(context).colorScheme;
-    final label = errorIndex != null
-        ? 'Error ${errorIndex! + 1}/${errors.length}'
-        : 'Errors: ${errors.length}';
+    final label =
+        errorIndex != null
+            ? 'Error ${errorIndex! + 1}/${errors.length}'
+            : 'Errors: ${errors.length}';
     return Container(
       color: colorScheme.errorContainer,
       child: Padding(
@@ -409,9 +399,7 @@
               padding: const EdgeInsets.only(right: denseSpacing),
               child: Text(
                 label,
-                style: TextStyle(
-                  color: colorScheme.onErrorContainer,
-                ),
+                style: TextStyle(color: colorScheme.onErrorContainer),
               ),
             ),
             _ErrorNavigatorButton(
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_details_tab.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_details_tab.dart
index a73c7b2..0ece63e 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_details_tab.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_details_tab.dart
@@ -9,10 +9,10 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/preferences/preferences.dart';
 import '../../shared/primitives/blocking_action_mixin.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/tab.dart';
 import 'inspector_controller.dart';
 import 'inspector_screen_body.dart';
@@ -72,10 +72,7 @@
 }
 
 class InspectorExpandCollapseButtons extends StatefulWidget {
-  const InspectorExpandCollapseButtons({
-    super.key,
-    required this.controller,
-  });
+  const InspectorExpandCollapseButtons({super.key, required this.controller});
 
   final InspectorController controller;
 
@@ -85,7 +82,8 @@
 }
 
 class _InspectorExpandCollapseButtonsState
-    extends State<InspectorExpandCollapseButtons> with BlockingActionMixin {
+    extends State<InspectorExpandCollapseButtons>
+    with BlockingActionMixin {
   bool get enableButtons => !actionInProgress;
 
   @override
@@ -93,9 +91,7 @@
     return Container(
       alignment: Alignment.centerRight,
       decoration: BoxDecoration(
-        border: Border(
-          left: defaultBorderSide(Theme.of(context)),
-        ),
+        border: Border(left: defaultBorderSide(Theme.of(context))),
       ),
       child: Row(
         mainAxisAlignment: MainAxisAlignment.end,
@@ -141,10 +137,7 @@
   }
 
   void _onCollapseClick() {
-    ga.select(
-      gac.inspector,
-      gac.collapseAll,
-    );
+    ga.select(gac.inspector, gac.collapseAll);
     widget.controller.collapseDetailsToSelected();
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
index 3da2d24..0448d09 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
@@ -16,19 +16,19 @@
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
 import '../../shared/analytics/metrics.dart';
-import '../../shared/collapsible_mixin.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/console/eval/inspector_tree.dart';
 import '../../shared/console/widgets/description.dart';
 import '../../shared/diagnostics/diagnostics_node.dart';
-import '../../shared/diagnostics_text_styles.dart';
-import '../../shared/error_badge_manager.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/error_badge_manager.dart';
+import '../../shared/primitives/collapsible_mixin.dart';
+import '../../shared/primitives/diagnostics_text_styles.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/search.dart';
 import '../../shared/ui/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'inspector_breadcrumbs.dart';
 import 'inspector_controller.dart';
 
@@ -109,11 +109,12 @@
       gac.inspector,
       gac.inspectorTreeControllerInitialized,
       nonInteraction: true,
-      screenMetricsProvider: () => InspectorScreenMetrics.legacy(
-        inspectorTreeControllerId: gaId,
-        rootSetCount: _rootSetCount,
-        rowCount: _root?.subtreeSize,
-      ),
+      screenMetricsProvider:
+          () => InspectorScreenMetrics.legacy(
+            inspectorTreeControllerId: gaId,
+            rootSetCount: _rootSetCount,
+            rowCount: _root?.subtreeSize,
+          ),
     );
   }
 
@@ -170,11 +171,12 @@
         gac.inspector,
         gac.inspectorTreeControllerRootChange,
         nonInteraction: true,
-        screenMetricsProvider: () => InspectorScreenMetrics.legacy(
-          inspectorTreeControllerId: gaId,
-          rootSetCount: ++_rootSetCount,
-          rowCount: _root?.subtreeSize,
-        ),
+        screenMetricsProvider:
+            () => InspectorScreenMetrics.legacy(
+              inspectorTreeControllerId: gaId,
+              rootSetCount: ++_rootSetCount,
+              rowCount: _root?.subtreeSize,
+            ),
       );
     });
   }
@@ -339,12 +341,15 @@
 
     final rootLocal = root!;
 
-    selection = rootLocal
-        .getRow(
-          (rootLocal.getRowIndex(selection!) + indexOffset)
-              .clamp(0, numRows - 1),
-        )
-        ?.node;
+    selection =
+        rootLocal
+            .getRow(
+              (rootLocal.getRowIndex(selection!) + indexOffset).clamp(
+                0,
+                numRows - 1,
+              ),
+            )
+            ?.node;
   }
 
   double get horizontalPadding => 10.0;
@@ -442,10 +447,7 @@
 
   void onSelectNode(InspectorTreeNode? node) {
     selection = node;
-    ga.select(
-      gac.inspector,
-      gac.treeNodeSelection,
-    );
+    ga.select(gac.inspector, gac.treeNodeSelection);
     expandPath(node);
   }
 
@@ -550,8 +552,9 @@
     if (diagnosticsNode.hasChildren ||
         diagnosticsNode.inlineProperties.isNotEmpty) {
       if (diagnosticsNode.childrenReady || !diagnosticsNode.hasChildren) {
-        final styleIsMultiline =
-            expandPropertiesByDefault(diagnosticsNode.style);
+        final styleIsMultiline = expandPropertiesByDefault(
+          diagnosticsNode.style,
+        );
         setupChildren(
           diagnosticsNode,
           node,
@@ -655,8 +658,9 @@
     if (searchPreviousMatches) {
       final previousMatches = searchMatches.value;
       for (final previousMatch in previousMatches) {
-        if (previousMatch.node.diagnostic!.searchValue
-            .caseInsensitiveContains(search)) {
+        if (previousMatch.node.diagnostic!.searchValue.caseInsensitiveContains(
+          search,
+        )) {
           matches.add(previousMatch);
         }
       }
@@ -671,21 +675,17 @@
     if (search.isEmpty ||
         inspectorService == null ||
         inspectorService.isDisposed) {
-      assert(
-        () {
-          debugPrint('Search completed, no search');
-          return true;
-        }(),
-      );
+      assert(() {
+        debugPrint('Search completed, no search');
+        return true;
+      }());
       return matches;
     }
 
-    assert(
-      () {
-        debugPrint('Search started: $_searchTarget');
-        return true;
-      }(),
-    );
+    assert(() {
+      debugPrint('Search started: $_searchTarget');
+      return true;
+    }());
 
     for (final row in _searchableCachedRows) {
       final diagnostic = row!.node.diagnostic;
@@ -702,14 +702,12 @@
       // Widget search end
     }
 
-    assert(
-      () {
-        debugPrint(
-          'Search completed with $debugStatsWidgets widgets, $debugStatsSearchOps ops',
-        );
-        return true;
-      }(),
-    );
+    assert(() {
+      debugPrint(
+        'Search completed with $debugStatsWidgets widgets, $debugStatsSearchOps ops',
+      );
+      return true;
+    }());
 
     return matches;
   }
@@ -837,21 +835,21 @@
   }
 
   // TODO(devoncarew): Commented out as per flutter/devtools/pull/2001.
-//  void _onScrollYChange() {
-//    if (controller == null) return;
-//
-//    // If the vertical position  is already being animated we should not trigger
-//    // a new animation of the horizontal position as a more direct animation of
-//    // the horizontal position has already been triggered.
-//    if (currentAnimateY != null) return;
-//
-//    final x = _computeTargetX(_scrollControllerY.offset);
-//    _scrollControllerX.animateTo(
-//      x,
-//      duration: defaultDuration,
-//      curve: defaultCurve,
-//    );
-//  }
+  //  void _onScrollYChange() {
+  //    if (controller == null) return;
+  //
+  //    // If the vertical position  is already being animated we should not trigger
+  //    // a new animation of the horizontal position as a more direct animation of
+  //    // the horizontal position has already been triggered.
+  //    if (currentAnimateY != null) return;
+  //
+  //    final x = _computeTargetX(_scrollControllerY.offset);
+  //    _scrollControllerX.animateTo(
+  //      x,
+  //      duration: defaultDuration,
+  //      curve: defaultCurve,
+  //    );
+  //  }
 
   @override
   Future<void> scrollToRect(Rect rect) async {
@@ -862,12 +860,14 @@
 
     final initialX = rect.left;
     final initialY = rect.top;
-    final yOffsetAtViewportTop = _scrollControllerY.hasClients
-        ? _scrollControllerY.offset
-        : _scrollControllerY.initialScrollOffset;
-    final xOffsetAtViewportLeft = _scrollControllerX.hasClients
-        ? _scrollControllerX.offset
-        : _scrollControllerX.initialScrollOffset;
+    final yOffsetAtViewportTop =
+        _scrollControllerY.hasClients
+            ? _scrollControllerY.offset
+            : _scrollControllerY.initialScrollOffset;
+    final xOffsetAtViewportLeft =
+        _scrollControllerX.hasClients
+            ? _scrollControllerX.offset
+            : _scrollControllerX.initialScrollOffset;
 
     final viewPortInScrollControllerSpace = Rect.fromLTWH(
       xOffsetAtViewportLeft,
@@ -878,7 +878,7 @@
 
     final isRectInViewPort =
         viewPortInScrollControllerSpace.contains(rect.topLeft) &&
-            viewPortInScrollControllerSpace.contains(rect.bottomRight);
+        viewPortInScrollControllerSpace.contains(rect.bottomRight);
     if (isRectInViewPort) {
       // The rect is already in view, don't scroll
       return;
@@ -948,9 +948,7 @@
 
   /// Pad [initialY] so that a row would be placed in the vertical center of
   /// the screen.
-  double _padTargetY({
-    required double initialY,
-  }) {
+  double _padTargetY({required double initialY}) {
     return initialY - (safeViewportHeight / 2) + inspectorRowHeight / 2;
   }
 
@@ -1025,7 +1023,8 @@
             controller: _scrollControllerX,
             child: ConstrainedBox(
               constraints: BoxConstraints(
-                maxWidth: treeControllerLocal.rowWidth +
+                maxWidth:
+                    treeControllerLocal.rowWidth +
                     treeControllerLocal.maxRowIndent,
               ),
               // TODO(kenz): this scrollbar needs to be sticky to the right side of
@@ -1046,27 +1045,28 @@
                     offsetControllerViewportDimension: viewportWidth,
                     child: ListView.custom(
                       itemExtent: inspectorRowHeight,
-                      childrenDelegate: SliverChildBuilderDelegate(
-                        (context, index) {
-                          if (index == treeControllerLocal.numRows) {
-                            return SizedBox(height: inspectorRowHeight);
-                          }
-                          final row = treeControllerLocal.getCachedRow(index)!;
-                          final inspectorRef = row.node.diagnostic?.valueRef.id;
-                          return _InspectorTreeRowWidget(
-                            key: PageStorageKey(row.node),
-                            inspectorTreeState: this,
-                            row: row,
-                            scrollControllerX: _scrollControllerX,
-                            viewportWidth: viewportWidth,
-                            error: widget.widgetErrors != null &&
-                                    inspectorRef != null
-                                ? widget.widgetErrors![inspectorRef]
-                                : null,
-                          );
-                        },
-                        childCount: treeControllerLocal.numRows + 1,
-                      ),
+                      childrenDelegate: SliverChildBuilderDelegate((
+                        context,
+                        index,
+                      ) {
+                        if (index == treeControllerLocal.numRows) {
+                          return SizedBox(height: inspectorRowHeight);
+                        }
+                        final row = treeControllerLocal.getCachedRow(index)!;
+                        final inspectorRef = row.node.diagnostic?.valueRef.id;
+                        return _InspectorTreeRowWidget(
+                          key: PageStorageKey(row.node),
+                          inspectorTreeState: this,
+                          row: row,
+                          scrollControllerX: _scrollControllerX,
+                          viewportWidth: viewportWidth,
+                          error:
+                              widget.widgetErrors != null &&
+                                      inspectorRef != null
+                                  ? widget.widgetErrors![inspectorRef]
+                                  : null,
+                        );
+                      }, childCount: treeControllerLocal.numRows + 1),
                       controller: _scrollControllerY,
                     ),
                   ),
@@ -1102,9 +1102,10 @@
   bool get wantKeepAlive => true;
 }
 
-Paint _defaultPaint(ColorScheme colorScheme) => Paint()
-  ..color = colorScheme.treeGuidelineColor
-  ..strokeWidth = chartLineStrokeWidth;
+Paint _defaultPaint(ColorScheme colorScheme) =>
+    Paint()
+      ..color = colorScheme.treeGuidelineColor
+      ..strokeWidth = chartLineStrokeWidth;
 
 /// Custom painter that draws lines indicating how parent and child rows are
 /// connected to each other.
@@ -1142,11 +1143,13 @@
     // If this row is itself connected to a parent then draw the L shaped line
     // to make that connection.
     if (row.lineToParent) {
-      currentX = _controller.getDepthIndent(row.depth - 1) -
+      currentX =
+          _controller.getDepthIndent(row.depth - 1) -
           inspectorColumnWidth * 0.5;
-      final width = showExpandCollapse
-          ? inspectorColumnWidth * 0.5
-          : inspectorColumnWidth;
+      final width =
+          showExpandCollapse
+              ? inspectorColumnWidth * 0.5
+              : inspectorColumnWidth;
       canvas.drawLine(
         Offset(currentX, 0.0),
         Offset(currentX, inspectorRowHeight * 0.5),
@@ -1215,9 +1218,10 @@
 
     Color? backgroundColor;
     if (row.isSelected) {
-      backgroundColor = hasError
-          ? colorScheme.errorContainer
-          : colorScheme.selectedRowBackgroundColor;
+      backgroundColor =
+          hasError
+              ? colorScheme.errorContainer
+              : colorScheme.selectedRowBackgroundColor;
     }
 
     final node = row.node;
@@ -1234,19 +1238,16 @@
               children: [
                 node.showExpandCollapse
                     ? InkWell(
-                        onTap: onToggle,
-                        child: RotationTransition(
-                          turns: expandArrowAnimation,
-                          child: Icon(
-                            Icons.expand_more,
-                            size: defaultIconSize,
-                          ),
-                        ),
-                      )
-                    : const SizedBox(
-                        width: defaultSpacing,
-                        height: defaultSpacing,
+                      onTap: onToggle,
+                      child: RotationTransition(
+                        turns: expandArrowAnimation,
+                        child: Icon(Icons.expand_more, size: defaultIconSize),
                       ),
+                    )
+                    : const SizedBox(
+                      width: defaultSpacing,
+                      height: defaultSpacing,
+                    ),
                 Expanded(
                   child: Container(
                     color: backgroundColor,
@@ -1268,11 +1269,11 @@
                           nodeDescriptionHighlightStyle:
                               searchValue.isEmpty || !row.isSearchMatch
                                   ? DiagnosticsTextStyles.regular(
-                                      Theme.of(context).colorScheme,
-                                    )
+                                    Theme.of(context).colorScheme,
+                                  )
                                   : row.isSelected
-                                      ? theme.searchMatchHighlightStyleFocused
-                                      : theme.searchMatchHighlightStyle,
+                                  ? theme.searchMatchHighlightStyleFocused
+                                  : theme.searchMatchHighlightStyle,
                         ),
                       ),
                     ),
@@ -1287,8 +1288,10 @@
 
     // Wrap with tooltip if there is an error for this node's widget.
     if (hasError) {
-      rowWidget =
-          DevToolsTooltip(message: error!.errorMessage, child: rowWidget);
+      rowWidget = DevToolsTooltip(
+        message: error!.errorMessage,
+        child: rowWidget,
+      );
     }
 
     return CustomPaint(
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/box/box.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/box/box.dart
index 2b29b77..567a3c5 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/box/box.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/box/box.dart
@@ -21,10 +21,7 @@
 
 /// Layout visualizer for a widget with a box-layout.
 class BoxLayoutExplorerWidget extends LayoutExplorerWidget {
-  const BoxLayoutExplorerWidget(
-    super.inspectorController, {
-    super.key,
-  });
+  const BoxLayoutExplorerWidget(super.inspectorController, {super.key});
 
   static bool shouldDisplay(RemoteDiagnosticsNode _) {
     // Pretend this layout explorer is always available. This layout explorer
@@ -41,8 +38,9 @@
       BoxLayoutExplorerWidgetState();
 }
 
-class BoxLayoutExplorerWidgetState extends LayoutExplorerWidgetState<
-    BoxLayoutExplorerWidget, LayoutProperties> {
+class BoxLayoutExplorerWidgetState
+    extends
+        LayoutExplorerWidgetState<BoxLayoutExplorerWidget, LayoutProperties> {
   @override
   RemoteDiagnosticsNode? getRoot(RemoteDiagnosticsNode? node) {
     final nodeLocal = node;
@@ -79,9 +77,10 @@
     setState(() {
       // This implementation will need to change if we support showing more than
       // a single widget in the box visualization for the layout explorer.
-      highlighted = newProperties != null && selectedNode == newProperties.node
-          ? newProperties
-          : null;
+      highlighted =
+          newProperties != null && selectedNode == newProperties.node
+              ? newProperties
+              : null;
     });
   }
 
@@ -169,7 +168,8 @@
 
     final theme = Theme.of(context);
     final colorScheme = theme.colorScheme;
-    final parentProperties = this.parentProperties ??
+    final parentProperties =
+        this.parentProperties ??
         propertiesLocal; // Fall back to this node's properties if there is no parent.
 
     final parentSize = parentProperties.size;
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
index 42df234..bfec93a 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
@@ -8,10 +8,10 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/diagnostics/diagnostics_node.dart';
 import '../../../../shared/diagnostics/inspector_service.dart';
 import '../../../../shared/primitives/math_utils.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../inspector_data_models.dart';
 import '../ui/arrow.dart';
 import '../ui/free_space.dart';
@@ -29,10 +29,7 @@
 double get alignmentDropdownMaxSize => scaleByFontFactor(140.0);
 
 class FlexLayoutExplorerWidget extends LayoutExplorerWidget {
-  const FlexLayoutExplorerWidget(
-    super.inspectorController, {
-    super.key,
-  });
+  const FlexLayoutExplorerWidget(super.inspectorController, {super.key});
 
   static bool shouldDisplay(RemoteDiagnosticsNode node) {
     return (node.isFlex) || (node.parent?.isFlex ?? false);
@@ -43,8 +40,12 @@
       FlexLayoutExplorerWidgetState();
 }
 
-class FlexLayoutExplorerWidgetState extends LayoutExplorerWidgetState<
-    FlexLayoutExplorerWidget, FlexLayoutProperties> {
+class FlexLayoutExplorerWidgetState
+    extends
+        LayoutExplorerWidgetState<
+          FlexLayoutExplorerWidget,
+          FlexLayoutProperties
+        > {
   final scrollController = ScrollController();
 
   Axis get direction => properties!.direction;
@@ -57,9 +58,10 @@
           ? colorScheme.mainAxisColor
           : colorScheme.crossAxisColor;
 
-  Color verticalColor(ColorScheme colorScheme) => properties!.isMainAxisVertical
-      ? colorScheme.mainAxisColor
-      : colorScheme.crossAxisColor;
+  Color verticalColor(ColorScheme colorScheme) =>
+      properties!.isMainAxisVertical
+          ? colorScheme.mainAxisColor
+          : colorScheme.crossAxisColor;
 
   Color horizontalTextColor(ColorScheme colorScheme) =>
       properties!.isMainAxisHorizontal
@@ -120,9 +122,10 @@
 
   Widget _buildAxisAlignmentDropdown(Axis axis, ThemeData theme) {
     final colorScheme = theme.colorScheme;
-    final color = axis == direction
-        ? colorScheme.mainAxisTextColor
-        : colorScheme.crossAxisTextColor;
+    final color =
+        axis == direction
+            ? colorScheme.mainAxisTextColor
+            : colorScheme.crossAxisTextColor;
     List<Enum> alignmentEnumEntries;
     Object? selected;
     final propertiesLocal = properties!;
@@ -150,9 +153,10 @@
           isExpanded: true,
           // Avoid showing an underline for the main axis and cross-axis drop downs.
           underline: const SizedBox(),
-          iconEnabledColor: axis == propertiesLocal.direction
-              ? colorScheme.mainAxisColor
-              : colorScheme.crossAxisColor,
+          iconEnabledColor:
+              axis == propertiesLocal.direction
+                  ? colorScheme.mainAxisColor
+                  : colorScheme.crossAxisColor,
           selectedItemBuilder: (context) {
             return [
               for (final alignment in alignmentEnumEntries)
@@ -173,13 +177,13 @@
                       child: Image.asset(
                         (axis == direction)
                             ? mainAxisAssetImageUrl(
-                                direction,
-                                alignment as MainAxisAlignment,
-                              )
+                              direction,
+                              alignment as MainAxisAlignment,
+                            )
                             : crossAxisAssetImageUrl(
-                                direction,
-                                alignment as CrossAxisAlignment,
-                              ),
+                              direction,
+                              alignment as CrossAxisAlignment,
+                            ),
                         height: axisAlignmentAssetImageHeight,
                         fit: BoxFit.fitHeight,
                         color: color,
@@ -212,13 +216,13 @@
                         child: Image.asset(
                           (axis == direction)
                               ? mainAxisAssetImageUrl(
-                                  direction,
-                                  alignment as MainAxisAlignment,
-                                )
+                                direction,
+                                alignment as MainAxisAlignment,
+                              )
                               : crossAxisAssetImageUrl(
-                                  direction,
-                                  alignment as CrossAxisAlignment,
-                                ),
+                                direction,
+                                alignment as CrossAxisAlignment,
+                              ),
                           fit: BoxFit.fitHeight,
                           color: color,
                         ),
@@ -234,13 +238,14 @@
             // if the axis is the main axis the type should be [MainAxisAlignment]
             // if the axis is the cross axis the type should be [CrossAxisAlignment]
             FlexLayoutProperties changedProperties;
-            changedProperties = axis == direction
-                ? propertiesLocal.copyWith(
-                    mainAxisAlignment: newSelection as MainAxisAlignment?,
-                  )
-                : propertiesLocal.copyWith(
-                    crossAxisAlignment: newSelection as CrossAxisAlignment?,
-                  );
+            changedProperties =
+                axis == direction
+                    ? propertiesLocal.copyWith(
+                      mainAxisAlignment: newSelection as MainAxisAlignment?,
+                    )
+                    : propertiesLocal.copyWith(
+                      crossAxisAlignment: newSelection as CrossAxisAlignment?,
+                    );
             final valueRef = propertiesLocal.node.valueRef;
             markAsDirty();
             await objectGroup!.invokeSetFlexProperties(
@@ -449,9 +454,7 @@
 
     final contents = Container(
       decoration: BoxDecoration(
-        border: Border.all(
-          color: theme.primaryColorLight,
-        ),
+        border: Border.all(color: theme.primaryColorLight),
       ),
       margin: const EdgeInsets.only(top: margin, left: margin),
       child: LayoutBuilder(
@@ -463,21 +466,23 @@
             return axis == Axis.horizontal ? maxWidth : maxHeight;
           }
 
-          final childrenAndMainAxisSpacesRenderProps =
-              widget.properties.childrenRenderProperties(
-            smallestRenderWidth: minRenderWidth,
-            largestRenderWidth: defaultMaxRenderWidth,
-            smallestRenderHeight: minRenderHeight,
-            largestRenderHeight: defaultMaxRenderHeight,
-            maxSizeAvailable: maxSizeAvailable,
-          );
+          final childrenAndMainAxisSpacesRenderProps = widget.properties
+              .childrenRenderProperties(
+                smallestRenderWidth: minRenderWidth,
+                largestRenderWidth: defaultMaxRenderWidth,
+                smallestRenderHeight: minRenderHeight,
+                largestRenderHeight: defaultMaxRenderHeight,
+                maxSizeAvailable: maxSizeAvailable,
+              );
 
-          final renderProperties = childrenAndMainAxisSpacesRenderProps
-              .where((renderProps) => !renderProps.isFreeSpace)
-              .toList();
-          final mainAxisSpaces = childrenAndMainAxisSpacesRenderProps
-              .where((renderProps) => renderProps.isFreeSpace)
-              .toList();
+          final renderProperties =
+              childrenAndMainAxisSpacesRenderProps
+                  .where((renderProps) => !renderProps.isFreeSpace)
+                  .toList();
+          final mainAxisSpaces =
+              childrenAndMainAxisSpacesRenderProps
+                  .where((renderProps) => renderProps.isFreeSpace)
+                  .toList();
           final crossAxisSpaces = widget.properties.crossAxisSpaces(
             childrenRenderProperties: renderProperties,
             maxSizeAvailable: maxSizeAvailable,
@@ -523,22 +528,27 @@
               scrollDirection: widget.properties.direction,
               controller: widget.scrollController,
               child: ConstrainedBox(
-                constraints: BoxConstraints(
-                  minWidth: maxWidth,
-                  minHeight: maxHeight,
-                  maxWidth: widget.direction == Axis.horizontal
-                      ? sum(
-                          childrenAndMainAxisSpacesRenderProps
-                              .map((renderSize) => renderSize.width),
-                        )
-                      : maxWidth,
-                  maxHeight: widget.direction == Axis.vertical
-                      ? sum(
-                          childrenAndMainAxisSpacesRenderProps
-                              .map((renderSize) => renderSize.height),
-                        )
-                      : maxHeight,
-                ).normalize(),
+                constraints:
+                    BoxConstraints(
+                      minWidth: maxWidth,
+                      minHeight: maxHeight,
+                      maxWidth:
+                          widget.direction == Axis.horizontal
+                              ? sum(
+                                childrenAndMainAxisSpacesRenderProps.map(
+                                  (renderSize) => renderSize.width,
+                                ),
+                              )
+                              : maxWidth,
+                      maxHeight:
+                          widget.direction == Axis.vertical
+                              ? sum(
+                                childrenAndMainAxisSpacesRenderProps.map(
+                                  (renderSize) => renderSize.height,
+                                ),
+                              )
+                              : maxHeight,
+                    ).normalize(),
                 child: Stack(
                   children: [
                     LayoutExplorerBackground(colorScheme: colorScheme),
@@ -596,10 +606,7 @@
 
   void onChangeFlexFit(FlexFit? newFlexFit) async {
     state.markAsDirty();
-    await objectGroup!.invokeSetFlexFit(
-      properties.node.valueRef,
-      newFlexFit!,
-    );
+    await objectGroup!.invokeSetFlexFit(properties.node.valueRef, newFlexFit!);
   }
 
   Widget _buildFlexFactorChangerDropdown(
@@ -611,9 +618,10 @@
     Widget buildMenuitemChild(int? flexFactor) {
       return Text(
         'flex: $flexFactor',
-        style: flexFactor == propertiesLocal.flexFactor
-            ? theme.boldTextStyle.copyWith(color: emphasizedTextColor)
-            : theme.regularTextStyleWithColor(emphasizedTextColor),
+        style:
+            flexFactor == propertiesLocal.flexFactor
+                ? theme.boldTextStyle.copyWith(color: emphasizedTextColor)
+                : theme.regularTextStyleWithColor(emphasizedTextColor),
       );
     }
 
@@ -638,9 +646,9 @@
 
   Widget _buildFlexFitChangerDropdown(ThemeData theme) {
     Widget flexFitDescription(FlexFit flexFit) => Text(
-          'fit: ${flexFit.name}',
-          style: theme.regularTextStyleWithColor(emphasizedTextColor),
-        );
+      'fit: ${flexFit.name}',
+      style: theme.regularTextStyleWithColor(emphasizedTextColor),
+    );
 
     final propertiesLocal = properties;
 
@@ -673,14 +681,13 @@
     // TODO(https://github.com/flutter/devtools/issues/4058) allow more dynamic
     // flex factor input
     final currentFlexFactor = properties.flexFactor?.toInt() ?? 0;
-    final currentMaxFlexFactor =
-        math.max(currentFlexFactor, maximumFlexFactorOptions);
+    final currentMaxFlexFactor = math.max(
+      currentFlexFactor,
+      maximumFlexFactorOptions,
+    );
 
     return Container(
-      margin: const EdgeInsets.only(
-        top: margin,
-        left: margin,
-      ),
+      margin: const EdgeInsets.only(top: margin, left: margin),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.end,
         children: [
@@ -718,15 +725,20 @@
       final horizontal = rootLocal.isMainAxisHorizontal;
 
       late Size size;
-      size = propertiesLocal.hasFlexFactor
-          ? SizeTween(
-              begin: Size(
-                horizontal ? minRenderWidth - entranceMargin : renderSize.width,
-                vertical ? minRenderHeight - entranceMargin : renderSize.height,
-              ),
-              end: renderSize,
-            ).evaluate(state.entranceCurve)!
-          : renderSize;
+      size =
+          propertiesLocal.hasFlexFactor
+              ? SizeTween(
+                begin: Size(
+                  horizontal
+                      ? minRenderWidth - entranceMargin
+                      : renderSize.width,
+                  vertical
+                      ? minRenderHeight - entranceMargin
+                      : renderSize.height,
+                ),
+                end: renderSize,
+              ).evaluate(state.entranceCurve)!
+              : renderSize;
       // Not-expanded widgets enter much faster.
       return Opacity(
         opacity: min([state.entranceCurve.value * 5, 1.0]),
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/utils.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/utils.dart
index 57868d1..feb98ee 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/utils.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/utils.dart
@@ -23,11 +23,7 @@
 class AnimatedFlexLayoutProperties
     extends AnimatedLayoutProperties<FlexLayoutProperties>
     implements FlexLayoutProperties {
-  AnimatedFlexLayoutProperties(
-    super.begin,
-    super.end,
-    super.animation,
-  );
+  AnimatedFlexLayoutProperties(super.begin, super.end, super.animation);
 
   @override
   CrossAxisAlignment? get crossAxisAlignment => end.crossAxisAlignment;
@@ -85,7 +81,8 @@
   }
 
   @override
-  double get crossAxisDimension => lerpDouble(
+  double get crossAxisDimension =>
+      lerpDouble(
         begin.crossAxisDimension,
         end.crossAxisDimension,
         animation.value,
@@ -119,7 +116,8 @@
   bool get isMainAxisVertical => end.isMainAxisVertical;
 
   @override
-  double get mainAxisDimension => lerpDouble(
+  double get mainAxisDimension =>
+      lerpDouble(
         begin.mainAxisDimension,
         end.mainAxisDimension,
         animation.value,
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/arrow.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/arrow.dart
index a90b5e0..82ac3bb 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/arrow.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/arrow.dart
@@ -11,16 +11,12 @@
 const defaultArrowStrokeWidth = 2.0;
 const defaultDistanceToArrow = 4.0;
 
-enum ArrowType {
-  up,
-  left,
-  right,
-  down,
-}
+enum ArrowType { up, left, right, down }
 
-Axis axis(ArrowType type) => (type == ArrowType.up || type == ArrowType.down)
-    ? Axis.vertical
-    : Axis.horizontal;
+Axis axis(ArrowType type) =>
+    (type == ArrowType.up || type == ArrowType.down)
+        ? Axis.vertical
+        : Axis.horizontal;
 
 /// Widget that draws a bidirectional arrow around another widget.
 ///
@@ -35,12 +31,12 @@
     double? arrowHeadSize,
     this.arrowStrokeWidth = defaultArrowStrokeWidth,
     this.childMarginFromArrow = defaultDistanceToArrow,
-  })  : assert(childMarginFromArrow > 0.0),
-        direction = axis(type),
-        isBidirectional = false,
-        startArrowType = type,
-        endArrowType = type,
-        arrowHeadSize = arrowHeadSize ?? defaultIconSize;
+  }) : assert(childMarginFromArrow > 0.0),
+       direction = axis(type),
+       isBidirectional = false,
+       startArrowType = type,
+       endArrowType = type,
+       arrowHeadSize = arrowHeadSize ?? defaultIconSize;
 
   const ArrowWrapper.bidirectional({
     super.key,
@@ -50,13 +46,13 @@
     required this.arrowHeadSize,
     this.arrowStrokeWidth = defaultArrowStrokeWidth,
     this.childMarginFromArrow = defaultDistanceToArrow,
-  })  : assert(arrowHeadSize >= 0.0),
-        assert(childMarginFromArrow >= 0.0),
-        isBidirectional = true,
-        startArrowType =
-            direction == Axis.horizontal ? ArrowType.left : ArrowType.up,
-        endArrowType =
-            direction == Axis.horizontal ? ArrowType.right : ArrowType.down;
+  }) : assert(arrowHeadSize >= 0.0),
+       assert(childMarginFromArrow >= 0.0),
+       isBidirectional = true,
+       startArrowType =
+           direction == Axis.horizontal ? ArrowType.left : ArrowType.up,
+       endArrowType =
+           direction == Axis.horizontal ? ArrowType.right : ArrowType.down;
 
   final Color arrowColor;
   final double arrowHeadSize;
@@ -96,10 +92,11 @@
               headSize: arrowHeadSize,
               strokeWidth: arrowStrokeWidth,
               type: startArrowType,
-              shouldDrawHead: isBidirectional
-                  ? true
-                  : (startArrowType == ArrowType.left ||
-                      startArrowType == ArrowType.up),
+              shouldDrawHead:
+                  isBidirectional
+                      ? true
+                      : (startArrowType == ArrowType.left ||
+                          startArrowType == ArrowType.up),
             ),
           ),
         ),
@@ -115,10 +112,11 @@
               headSize: arrowHeadSize,
               strokeWidth: arrowStrokeWidth,
               type: endArrowType,
-              shouldDrawHead: isBidirectional
-                  ? true
-                  : (endArrowType == ArrowType.right ||
-                      endArrowType == ArrowType.down),
+              shouldDrawHead:
+                  isBidirectional
+                      ? true
+                      : (endArrowType == ArrowType.right ||
+                          endArrowType == ArrowType.down),
             ),
           ),
         ),
@@ -137,15 +135,15 @@
     this.shouldDrawHead = true,
     this.strokeWidth = defaultArrowStrokeWidth,
     required this.type,
-  })  : assert(headSize > 0.0),
-        assert(strokeWidth > 0.0),
-        _painter = _ArrowPainter(
-          headSize: headSize,
-          color: color,
-          strokeWidth: strokeWidth,
-          type: type,
-          shouldDrawHead: shouldDrawHead,
-        );
+  }) : assert(headSize > 0.0),
+       assert(strokeWidth > 0.0),
+       _painter = _ArrowPainter(
+         headSize: headSize,
+         color: color,
+         strokeWidth: strokeWidth,
+         type: type,
+         shouldDrawHead: shouldDrawHead,
+       );
 
   final Color color;
 
@@ -162,10 +160,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return CustomPaint(
-      painter: _painter,
-      child: Container(),
-    );
+    return CustomPaint(painter: _painter, child: Container());
   }
 }
 
@@ -177,7 +172,7 @@
     this.shouldDrawHead = true,
     required this.type,
   }) : // the height of an equilateral triangle
-        headHeight = 0.5 * sqrt(3) * headSize;
+       headHeight = 0.5 * sqrt(3) * headSize;
 
   final Color color;
   final double headSize;
@@ -204,9 +199,10 @@
 
   @override
   void paint(Canvas canvas, Size size) {
-    final paint = Paint()
-      ..color = color
-      ..strokeWidth = strokeWidth;
+    final paint =
+        Paint()
+          ..color = color
+          ..strokeWidth = strokeWidth;
 
     final originX = size.width / 2, originY = size.height / 2;
     Offset lineStartingPoint = Offset.zero;
@@ -239,11 +235,12 @@
           p3 = Offset(originX + headSizeDividedBy2, startingY);
           break;
       }
-      final path = Path()
-        ..moveTo(p1.dx, p1.dy)
-        ..lineTo(p2.dx, p2.dy)
-        ..lineTo(p3.dx, p3.dy)
-        ..close();
+      final path =
+          Path()
+            ..moveTo(p1.dx, p1.dy)
+            ..lineTo(p2.dx, p2.dy)
+            ..lineTo(p3.dx, p3.dy)
+            ..close();
       canvas.drawPath(path, paint);
 
       switch (type) {
@@ -281,10 +278,6 @@
           break;
       }
     }
-    canvas.drawLine(
-      lineStartingPoint,
-      lineEndingPoint,
-      paint,
-    );
+    canvas.drawLine(lineStartingPoint, lineEndingPoint, paint);
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/dimension.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/dimension.dart
index 490dc21..c721081 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/dimension.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/dimension.dart
@@ -15,9 +15,10 @@
   final text = Text.rich(
     description,
     textAlign: TextAlign.center,
-    style: overflow
-        ? overflowingDimensionIndicatorTextStyle(colorScheme)
-        : dimensionIndicatorTextStyle,
+    style:
+        overflow
+            ? overflowingDimensionIndicatorTextStyle(colorScheme)
+            : dimensionIndicatorTextStyle,
     overflow: TextOverflow.ellipsis,
   );
   if (overflow) {
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/free_space.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/free_space.dart
index 073d5a0..3480489 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/free_space.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/free_space.dart
@@ -12,10 +12,7 @@
 import 'theme.dart';
 
 class FreeSpaceVisualizerWidget extends StatelessWidget {
-  const FreeSpaceVisualizerWidget(
-    this.renderProperties, {
-    super.key,
-  });
+  const FreeSpaceVisualizerWidget(this.renderProperties, {super.key});
 
   final RenderProperties renderProperties;
 
@@ -32,9 +29,7 @@
       children: [
         Flexible(
           child: dimensionDescription(
-            TextSpan(
-              text: widthDescription,
-            ),
+            TextSpan(text: widthDescription),
             false,
             colorScheme,
           ),
@@ -109,9 +104,7 @@
       children: [
         Flexible(
           child: dimensionDescription(
-            TextSpan(
-              text: widthDescription,
-            ),
+            TextSpan(text: widthDescription),
             false,
             colorScheme,
           ),
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
index 743e74a..4aedc75 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
@@ -20,17 +20,17 @@
 
 /// Base class for layout widgets for all widget types.
 abstract class LayoutExplorerWidget extends StatefulWidget {
-  const LayoutExplorerWidget(
-    this.inspectorController, {
-    super.key,
-  });
+  const LayoutExplorerWidget(this.inspectorController, {super.key});
 
   final InspectorController inspectorController;
 }
 
 /// Base class for state objects for layout widgets for all widget types.
-abstract class LayoutExplorerWidgetState<W extends LayoutExplorerWidget,
-        L extends LayoutProperties> extends State<W>
+abstract class LayoutExplorerWidgetState<
+  W extends LayoutExplorerWidget,
+  L extends LayoutProperties
+>
+    extends State<W>
     with TickerProviderStateMixin
     implements InspectorServiceClient {
   LayoutExplorerWidgetState() {
@@ -128,8 +128,9 @@
   }
 
   void _unregisterInspectorControllerService() {
-    inspectorController.selectedNode
-        .removeListener(_onSelectionChangedCallback);
+    inspectorController.selectedNode.removeListener(
+      _onSelectionChangedCallback,
+    );
     inspectorService?.removeClient(this);
   }
 
@@ -231,9 +232,8 @@
   }
 
   void _initAnimationStates() {
-    entranceController = longAnimationController(
-      this,
-    )..addStatusListener((status) {
+    entranceController = longAnimationController(this)
+      ..addStatusListener((status) {
         if (status == AnimationStatus.dismissed) {
           setState(() {
             _previousProperties = null;
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/overflow_indicator_painter.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/overflow_indicator_painter.dart
index 2b1b577..eb83266 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/overflow_indicator_painter.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/overflow_indicator_painter.dart
@@ -34,14 +34,15 @@
   /// since all of them are private.
   static const black = Color(0xBF000000);
   static const yellow = Color(0xBFFFFF00);
-  static final indicatorPaint = Paint()
-    ..shader = ui.Gradient.linear(
-      const Offset(0.0, 0.0),
-      const Offset(10.0, 10.0),
-      <Color>[black, yellow, yellow, black],
-      <double>[0.25, 0.25, 0.75, 0.75],
-      TileMode.repeated,
-    );
+  static final indicatorPaint =
+      Paint()
+        ..shader = ui.Gradient.linear(
+          const Offset(0.0, 0.0),
+          const Offset(10.0, 10.0),
+          <Color>[black, yellow, yellow, black],
+          <double>[0.25, 0.25, 0.75, 0.75],
+          TileMode.repeated,
+        );
 
   @override
   void paint(Canvas canvas, Size size) {
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/theme.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/theme.dart
index 0ebf49b..bcc8757 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/theme.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/theme.dart
@@ -133,12 +133,7 @@
   return Container(
     height: 1.0,
     decoration: const BoxDecoration(
-      border: Border(
-        bottom: BorderSide(
-          color: textColor,
-          width: 0.0,
-        ),
-      ),
+      border: Border(bottom: BorderSide(color: textColor, width: 0.0)),
     ),
   );
 }
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/utils.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/utils.dart
index 4c31bc2..7df3b6b 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/utils.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/utils.dart
@@ -32,12 +32,12 @@
     this.bottomHeight,
     this.center,
   }) : assert(
-          left != null ||
-              top != null ||
-              right != null ||
-              bottom != null ||
-              center != null,
-        );
+         left != null ||
+             top != null ||
+             right != null ||
+             bottom != null ||
+             center != null,
+       );
 
   final Widget? center;
   final Widget? top;
@@ -160,17 +160,19 @@
                 width:
                     isSelected ? _borderSelectedWidth : _borderUnselectedWidth,
               ),
-              color: isSelected
-                  ? theme.canvasColor.brighten()
-                  : theme.canvasColor.darken(),
-              boxShadow: isSelected
-                  ? [
-                      BoxShadow(
-                        color: Colors.black.withAlpha(255 ~/ 2),
-                        blurRadius: 20,
-                      ),
-                    ]
-                  : null,
+              color:
+                  isSelected
+                      ? theme.canvasColor.brighten()
+                      : theme.canvasColor.darken(),
+              boxShadow:
+                  isSelected
+                      ? [
+                        BoxShadow(
+                          color: Colors.black.withAlpha(255 ~/ 2),
+                          blurRadius: 20,
+                        ),
+                      ]
+                      : null,
             ),
             child: Stack(
               children: [
@@ -185,12 +187,14 @@
                   ),
                 Container(
                   margin: EdgeInsets.only(
-                    right: overflowSide == OverflowSide.right
-                        ? _overflowIndicatorSize
-                        : 0.0,
-                    bottom: overflowSide == OverflowSide.bottom
-                        ? _overflowIndicatorSize
-                        : 0.0,
+                    right:
+                        overflowSide == OverflowSide.right
+                            ? _overflowIndicatorSize
+                            : 0.0,
+                    bottom:
+                        overflowSide == OverflowSide.bottom
+                            ? _overflowIndicatorSize
+                            : 0.0,
                   ),
                   child: Column(
                     crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -204,10 +208,11 @@
                             Flexible(
                               child: Container(
                                 constraints: BoxConstraints(
-                                  maxWidth: largeTitle
-                                      ? defaultMaxRenderWidth
-                                      : minRenderWidth *
-                                          widgetTitleMaxWidthPercentage,
+                                  maxWidth:
+                                      largeTitle
+                                          ? defaultMaxRenderWidth
+                                          : minRenderWidth *
+                                              widgetTitleMaxWidthPercentage,
                                 ),
                                 decoration: BoxDecoration(color: borderColor),
                                 padding: const EdgeInsets.all(4.0),
@@ -242,15 +247,15 @@
 class AnimatedLayoutProperties<T extends LayoutProperties>
     implements LayoutProperties {
   AnimatedLayoutProperties(this.begin, this.end, this.animation)
-      : assert(begin.children.length == end.children.length),
-        _children = [
-          for (var i = 0; i < begin.children.length; i++)
-            AnimatedLayoutProperties(
-              begin.children[i],
-              end.children[i],
-              animation,
-            ),
-        ];
+    : assert(begin.children.length == end.children.length),
+      _children = [
+        for (var i = 0; i < begin.children.length; i++)
+          AnimatedLayoutProperties(
+            begin.children[i],
+            end.children[i],
+            animation,
+          ),
+      ];
 
   final T begin;
   final T end;
@@ -313,10 +318,10 @@
     final constraintsLocal = constraints!;
     return constraintsLocal.hasBoundedWidth
         ? LayoutProperties.describeAxis(
-            constraintsLocal.minWidth,
-            constraintsLocal.maxWidth,
-            'w',
-          )
+          constraintsLocal.minWidth,
+          constraintsLocal.maxWidth,
+          'w',
+        )
         : 'w=unconstrained';
   }
 
@@ -325,10 +330,10 @@
     final constraintsLocal = constraints!;
     return constraintsLocal.hasBoundedHeight
         ? LayoutProperties.describeAxis(
-            constraintsLocal.minHeight,
-            constraintsLocal.maxHeight,
-            'h',
-          )
+          constraintsLocal.minHeight,
+          constraintsLocal.maxHeight,
+          'h',
+        )
         : 'h=unconstrained';
   }
 
@@ -416,10 +421,7 @@
 }
 
 class LayoutExplorerBackground extends StatelessWidget {
-  const LayoutExplorerBackground({
-    super.key,
-    required this.colorScheme,
-  });
+  const LayoutExplorerBackground({super.key, required this.colorScheme});
 
   final ColorScheme colorScheme;
 
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widget_constraints.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widget_constraints.dart
index 39f9f61..6dc4cc2 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widget_constraints.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widget_constraints.dart
@@ -30,7 +30,8 @@
   @override
   Widget build(BuildContext context) {
     final propertiesLocal = properties;
-    final showChildrenWidthsSum = propertiesLocal is FlexLayoutProperties &&
+    final showChildrenWidthsSum =
+        propertiesLocal is FlexLayoutProperties &&
         propertiesLocal.isOverflowWidth;
     final bottomHeight = widthAndConstraintIndicatorSize;
     final rightWidth = heightAndConstraintIndicatorSize;
@@ -43,24 +44,22 @@
       child: dimensionDescription(
         TextSpan(
           children: [
-            TextSpan(
-              text: propertiesLocal.describeHeight(),
-            ),
+            TextSpan(text: propertiesLocal.describeHeight()),
             if (propertiesLocal.constraints != null) ...[
               if (!showOverflowHeight) const TextSpan(text: '\n'),
               TextSpan(
                 text: ' (${propertiesLocal.describeHeightConstraints()})',
-                style: propertiesLocal.constraints!.hasBoundedHeight ||
-                        !warnIfUnconstrained
-                    ? null
-                    : TextStyle(
-                        color: colorScheme.unconstrainedColor,
-                      ),
+                style:
+                    propertiesLocal.constraints!.hasBoundedHeight ||
+                            !warnIfUnconstrained
+                        ? null
+                        : TextStyle(color: colorScheme.unconstrainedColor),
               ),
             ],
             if (showOverflowHeight)
               TextSpan(
-                text: '\nchildren take: '
+                text:
+                    '\nchildren take: '
                     '${toStringAsFixed(sum(propertiesLocal.childrenHeights))}',
               ),
           ],
@@ -95,10 +94,7 @@
                   ),
                 ),
               ),
-              if (displayHeightOutsideArrow)
-                Flexible(
-                  child: heightDescription,
-                ),
+              if (displayHeightOutsideArrow) Flexible(child: heightDescription),
             ],
           );
         },
@@ -113,15 +109,17 @@
             if (!showChildrenWidthsSum) const TextSpan(text: '\n'),
             TextSpan(
               text: '(${propertiesLocal.describeWidthConstraints()})',
-              style: propertiesLocal.constraints!.hasBoundedWidth ||
-                      !warnIfUnconstrained
-                  ? null
-                  : TextStyle(color: colorScheme.unconstrainedColor),
+              style:
+                  propertiesLocal.constraints!.hasBoundedWidth ||
+                          !warnIfUnconstrained
+                      ? null
+                      : TextStyle(color: colorScheme.unconstrainedColor),
             ),
           ],
           if (showChildrenWidthsSum)
             TextSpan(
-              text: '\nchildren take '
+              text:
+                  '\nchildren take '
                   '${toStringAsFixed(sum(propertiesLocal.childrenWidths))}',
             ),
         ],
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widgets_theme.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widgets_theme.dart
index 77ca926..2128ca9 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widgets_theme.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/widgets_theme.dart
@@ -5,10 +5,7 @@
 import 'package:flutter/material.dart';
 
 class WidgetTheme {
-  const WidgetTheme({
-    this.iconAsset,
-    this.color = otherWidgetColor,
-  });
+  const WidgetTheme({this.iconAsset, this.color = otherWidgetColor});
 
   final String? iconAsset;
   final Color color;
@@ -175,15 +172,17 @@
     'Tab': tabTheme,
     'TabBar': tabTheme,
     'TabBarView': tabTheme,
-    'BottomNavigationBar':
-        WidgetTheme(iconAsset: WidgetIcons.bottomNavigationBar),
+    'BottomNavigationBar': WidgetTheme(
+      iconAsset: WidgetIcons.bottomNavigationBar,
+    ),
     'CupertinoTabScaffold': tabTheme,
     'CupertinoTabView': tabTheme,
 
     // Other
     'Scaffold': WidgetTheme(iconAsset: WidgetIcons.scaffold),
-    'CircularProgressIndicator':
-        WidgetTheme(iconAsset: WidgetIcons.circularProgress),
+    'CircularProgressIndicator': WidgetTheme(
+      iconAsset: WidgetIcons.circularProgress,
+    ),
     'Card': WidgetTheme(iconAsset: WidgetIcons.card),
     'Divider': WidgetTheme(iconAsset: WidgetIcons.divider),
     'AlertDialog': WidgetTheme(iconAsset: WidgetIcons.alertDialog),
diff --git a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_controls.dart b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_controls.dart
index 9f62228..3509d30 100644
--- a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_controls.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_controls.dart
@@ -10,9 +10,9 @@
 import '../../service/service_extension_widgets.dart';
 import '../../service/service_extensions.dart' as extensions;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/globals.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../inspector_shared/inspector_settings_dialog.dart';
 import '../inspector_v2/inspector_controller.dart' as v2;
 
@@ -35,11 +35,10 @@
       children: [
         ValueListenableBuilder<bool>(
           valueListenable: serviceConnection
-              .serviceManager.serviceExtensionManager
-              .hasServiceExtension(
-            extensions.toggleSelectWidgetMode.extension,
-          ),
-          builder: (_, selectModeSupported, __) {
+              .serviceManager
+              .serviceExtensionManager
+              .hasServiceExtension(extensions.toggleSelectWidgetMode.extension),
+          builder: (_, selectModeSupported, _) {
             return ServiceExtensionButtonGroup(
               fillColor: activeButtonColor,
               extensions: [
@@ -128,10 +127,7 @@
 /// Toggle button that allows showing/hiding the implementation widgets in the
 /// widget tree.
 class ShowImplementationWidgetsButton extends StatelessWidget {
-  const ShowImplementationWidgetsButton({
-    super.key,
-    required this.controller,
-  });
+  const ShowImplementationWidgetsButton({super.key, required this.controller});
 
   final v2.InspectorController controller;
 
@@ -145,12 +141,13 @@
           isSelected: !isHidden,
           message:
               'Show widgets created by the Flutter framework or other packages.',
-          label: isScreenWiderThan(
-            context,
-            InspectorControls.minScreenWidthForTextBeforeTruncating,
-          )
-              ? 'Show Implementation Widgets'
-              : 'Show',
+          label:
+              isScreenWiderThan(
+                    context,
+                    InspectorControls.minScreenWidthForTextBeforeTruncating,
+                  )
+                  ? 'Show Implementation Widgets'
+                  : 'Show',
           onPressed: controller.toggleImplementationWidgetsVisibility,
           icon: Icons.code,
           minScreenWidthForTextBeforeScaling:
diff --git a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
index abfd30b..749f6d0 100644
--- a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_screen.dart
@@ -8,9 +8,9 @@
 import 'package:provider/provider.dart';
 
 import '../../shared/feature_flags.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import '../inspector/inspector_screen_body.dart' as legacy;
 import '../inspector_v2/inspector_screen_body.dart' as v2;
 import 'inspector_screen_controller.dart';
@@ -46,8 +46,10 @@
 class _InspectorScreenSwitcherState extends State<InspectorScreenSwitcher>
     with
         AutoDisposeMixin,
-        ProvidedControllerMixin<InspectorScreenController,
-            InspectorScreenSwitcher> {
+        ProvidedControllerMixin<
+          InspectorScreenController,
+          InspectorScreenSwitcher
+        > {
   bool get shouldShowInspectorV2 =>
       FeatureFlags.inspectorV2 &&
       preferences.inspector.inspectorV2Enabled.value;
@@ -58,10 +60,12 @@
     if (!initController()) return;
 
     addAutoDisposeListener(preferences.inspector.inspectorV2Enabled, () async {
-      controller.legacyInspectorController
-          .setVisibleToUser(!shouldShowInspectorV2);
-      await controller.v2InspectorController
-          .setVisibleToUser(shouldShowInspectorV2);
+      controller.legacyInspectorController.setVisibleToUser(
+        !shouldShowInspectorV2,
+      );
+      await controller.v2InspectorController.setVisibleToUser(
+        shouldShowInspectorV2,
+      );
     });
   }
 
diff --git a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart
index 6df1e49..1d86b14 100644
--- a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart
@@ -10,11 +10,11 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
-import '../../shared/editable_list.dart';
 import '../../shared/globals.dart';
 import '../../shared/preferences/preferences.dart';
 import '../../shared/primitives/simple_items.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/editable_list.dart';
 
 class FlutterInspectorSettingsDialog extends StatelessWidget {
   const FlutterInspectorSettingsDialog({super.key});
@@ -36,20 +36,28 @@
               mainAxisSize: MainAxisSize.min,
               crossAxisAlignment: CrossAxisAlignment.start,
               children: [
-                ...dialogSubHeader(
-                  theme,
-                  'General',
-                ),
+                ...dialogSubHeader(theme, 'General'),
                 CheckboxSetting(
-                  notifier: preferences.inspector.hoverEvalModeEnabled
-                      as ValueNotifier<bool?>,
+                  notifier:
+                      preferences.inspector.hoverEvalModeEnabled
+                          as ValueNotifier<bool?>,
                   title: 'Enable hover inspection',
                   description:
                       'Hovering over any widget displays its properties and values.',
                   gaItem: gac.inspectorHoverEvalMode,
                 ),
-                if (!inspectorV2Enabled) ...[
-                  const SizedBox(height: largeSpacing),
+                const SizedBox(height: largeSpacing),
+                if (inspectorV2Enabled) ...[
+                  CheckboxSetting(
+                    notifier:
+                        preferences.inspector.autoRefreshEnabled
+                            as ValueNotifier<bool?>,
+                    title: 'Enable auto-refreshing of the widget tree',
+                    description:
+                        'The widget tree will automatically be refreshed after a hot-reload.',
+                    gaItem: gac.inspectorAutoRefreshEnabled,
+                  ),
+                ] else ...[
                   const InspectorDefaultDetailsViewOption(),
                 ],
                 const SizedBox(height: largeSpacing),
@@ -76,15 +84,11 @@
                   style: theme.subtleTextStyle,
                 ),
                 const SizedBox(height: denseSpacing),
-                const Expanded(
-                  child: PubRootDirectorySection(),
-                ),
+                const Expanded(child: PubRootDirectorySection()),
               ],
             ),
           ),
-          actions: const [
-            DialogCloseButton(),
-          ],
+          actions: const [DialogCloseButton()],
         );
       },
     );
@@ -137,13 +141,11 @@
   void _onChanged(InspectorDetailsViewType? value) {
     if (value != null) {
       preferences.inspector.setDefaultInspectorDetailsView(value);
-      final item = value.name == InspectorDetailsViewType.layoutExplorer.name
-          ? gac.defaultDetailsViewToLayoutExplorer
-          : gac.defaultDetailsViewToWidgetDetails;
-      ga.select(
-        gac.inspector,
-        item,
-      );
+      final item =
+          value.name == InspectorDetailsViewType.layoutExplorer.name
+              ? gac.defaultDetailsViewToLayoutExplorer
+              : gac.defaultDetailsViewToWidgetDetails;
+      ga.select(gac.inspector, item);
     }
   }
 }
@@ -156,7 +158,7 @@
     return ValueListenableBuilder<IsolateRef?>(
       valueListenable:
           serviceConnection.serviceManager.isolateManager.mainIsolate,
-      builder: (_, __, ___) {
+      builder: (_, _, _) {
         return SizedBox(
           height: 200.0,
           child: EditableList(
@@ -165,16 +167,18 @@
             entries: preferences.inspector.pubRootDirectories,
             textFieldLabel: 'Enter a new package directory',
             isRefreshing: preferences.inspector.isRefreshingPubRootDirectories,
-            onEntryAdded: (p0) => unawaited(
-              preferences.inspector.addPubRootDirectories(
-                [p0],
-                shouldCache: true,
-              ),
-            ),
-            onEntryRemoved: (p0) =>
-                unawaited(preferences.inspector.removePubRootDirectories([p0])),
-            onRefreshTriggered: () =>
-                unawaited(preferences.inspector.loadPubRootDirectories()),
+            onEntryAdded:
+                (p0) => unawaited(
+                  preferences.inspector.addPubRootDirectories([
+                    p0,
+                  ], shouldCache: true),
+                ),
+            onEntryRemoved:
+                (p0) => unawaited(
+                  preferences.inspector.removePubRootDirectories([p0]),
+                ),
+            onRefreshTriggered:
+                () => unawaited(preferences.inspector.loadPubRootDirectories()),
           ),
         );
       },
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart
index bebba01..0add997 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart
@@ -31,8 +31,8 @@
 import '../../shared/diagnostics/inspector_service.dart';
 import '../../shared/diagnostics/primitives/instance_ref.dart';
 import '../../shared/globals.dart';
+import '../../shared/primitives/query_parameters.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/query_parameters.dart';
 import '../inspector_shared/inspector_screen.dart';
 import 'inspector_data_models.dart';
 import 'inspector_tree_controller.dart';
@@ -41,26 +41,24 @@
 
 /// Data pattern containing the properties and render properties for a widget
 /// tree node.
-typedef WidgetTreeNodeProperties = ({
-  /// Properties defined directly on the widget.
-  List<RemoteDiagnosticsNode> widgetProperties,
+typedef WidgetTreeNodeProperties =
+    ({
+      /// Properties defined directly on the widget.
+      List<RemoteDiagnosticsNode> widgetProperties,
 
-  /// Properties defined on the widget's render object.
-  List<RemoteDiagnosticsNode> renderProperties,
+      /// Properties defined on the widget's render object.
+      List<RemoteDiagnosticsNode> renderProperties,
 
-  /// Layout properties for the widget.
-  LayoutProperties? layoutProperties,
-});
+      /// Layout properties for the widget.
+      LayoutProperties? layoutProperties,
+    });
 
 /// This class is based on the InspectorPanel class from the Flutter IntelliJ
 /// plugin with some refactors to make it more of a true controller than a view.
 class InspectorController extends DisposableController
     with AutoDisposeControllerMixin
     implements InspectorServiceClient {
-  InspectorController({
-    required this.inspectorTree,
-    required this.treeType,
-  }) {
+  InspectorController({required this.inspectorTree, required this.treeType}) {
     unawaited(_init());
   }
 
@@ -112,10 +110,10 @@
       if (_supportsToggleSelectWidgetMode.value) {
         serviceConnection.serviceManager.serviceExtensionManager
             .setServiceExtensionState(
-          extensions.enableOnDeviceInspector.extension,
-          enabled: true,
-          value: true,
-        );
+              extensions.enableOnDeviceInspector.extension,
+              enabled: true,
+              value: true,
+            );
       }
     });
 
@@ -132,6 +130,17 @@
     }
 
     serviceConnection.consoleService.ensureServiceInitialized();
+
+    final vmService = serviceConnection.serviceManager.service;
+    if (vmService != null) {
+      autoDisposeStreamSubscription(
+        vmService.onIsolateEvent.listen(_maybeAutoRefreshInspector),
+      );
+
+      autoDisposeStreamSubscription(
+        vmService.onExtensionEvent.listen(_maybeAutoRefreshInspector),
+      );
+    }
   }
 
   void _handleConnectionStart() {
@@ -154,9 +163,10 @@
 
   IsolateRef? _mainIsolate;
 
-  ValueListenable<bool> get _supportsToggleSelectWidgetMode =>
-      serviceConnection.serviceManager.serviceExtensionManager
-          .hasServiceExtension(extensions.toggleSelectWidgetMode.extension);
+  ValueListenable<bool> get _supportsToggleSelectWidgetMode => serviceConnection
+      .serviceManager
+      .serviceExtensionManager
+      .hasServiceExtension(extensions.toggleSelectWidgetMode.extension);
 
   Future<void> _onClientChange(bool added) async {
     if (!added && _clientCount == 0) {
@@ -227,9 +237,11 @@
 
   ValueListenable<WidgetTreeNodeProperties> get selectedNodeProperties =>
       _selectedNodeProperties;
-  final _selectedNodeProperties = ValueNotifier<WidgetTreeNodeProperties>(
-    (widgetProperties: [], renderProperties: [], layoutProperties: null),
-  );
+  final _selectedNodeProperties = ValueNotifier<WidgetTreeNodeProperties>((
+    widgetProperties: [],
+    renderProperties: [],
+    layoutProperties: null,
+  ));
 
   /// Whether the implementation widgets are hidden in the widget tree.
   ValueListenable<bool> get implementationWidgetsHidden =>
@@ -418,9 +430,7 @@
       // We need to start by querying the inspector service to find out the
       // current state of the UI.
       final inspectorRef = DevToolsQueryParams.load().inspectorRef;
-      await updateSelectionFromService(
-        inspectorRef: inspectorRef,
-      );
+      await updateSelectionFromService(inspectorRef: inspectorRef);
     } else {
       if (_disposed) return;
       if (inspectorService is InspectorService) {
@@ -434,11 +444,33 @@
     }
   }
 
+  bool _receivedIsolateReloadEvent = false;
+
+  Future<void> _maybeAutoRefreshInspector(Event event) async {
+    if (!preferences.inspector.autoRefreshEnabled.value) return;
+
+    // It is not sufficent to wait for the isolate reload event, because Flutter
+    // might not have re-painted the app. Instead, we need to wait for the first
+    // frame AFTER the isolate reload event in order to request the new tree.
+    if (event.kind == EventKind.kExtension) {
+      if (!_receivedIsolateReloadEvent) return;
+      if (event.extensionKind == 'Flutter.Frame') {
+        _receivedIsolateReloadEvent = false;
+        await refreshInspector();
+      }
+    }
+
+    if (event.kind == EventKind.kIsolateReload) {
+      _receivedIsolateReloadEvent = true;
+    }
+  }
+
   Future<void> _recomputeTreeRoot(
     RemoteDiagnosticsNode? newSelection, {
-    bool hideImplementationWidgets = false,
+    bool? hideImplementationWidgets,
   }) async {
     assert(!_disposed);
+    hideImplementationWidgets ??= _implementationWidgetsHidden.value;
     final treeGroups = _treeGroups;
     if (_disposed || treeGroups == null) {
       return;
@@ -450,6 +482,7 @@
       final node = await group.getRoot(
         treeType,
         isSummaryTree: hideImplementationWidgets,
+        includeFullDetails: false,
       );
       if (node == null || group.disposed || _disposed) {
         return;
@@ -466,8 +499,10 @@
         expandChildren: true,
       );
       inspectorTree.root = rootNode;
-
-      refreshSelection(newSelection);
+      final selectedNode = _determineNewSelection(
+        newSelection ?? selectedDiagnostic,
+      );
+      refreshSelection(selectedNode);
       _implementationWidgetsHidden.value = hideImplementationWidgets;
     } catch (error, st) {
       _log.shout(error, error, st);
@@ -476,6 +511,79 @@
     }
   }
 
+  RemoteDiagnosticsNode? _determineNewSelection(
+    RemoteDiagnosticsNode? previousSelection,
+  ) {
+    if (previousSelection == null) return null;
+    if (valueToInspectorTreeNode.containsKey(previousSelection.valueRef)) {
+      return previousSelection;
+    }
+
+    // TODO(https://github.com/flutter/devtools/issues/8481): Consider using a
+    // variation of a path-finding algorithm to determine the new selection,
+    // instead of looking for the first matching descendant.
+    final (
+      closestUnchangedAncestor,
+      distanceToAncestor,
+    ) = _findClosestUnchangedAncestor(previousSelection);
+    if (closestUnchangedAncestor == null) return inspectorTree.root?.diagnostic;
+
+    const distanceOffset = 3;
+    final matchingDescendant = _findMatchingDescendant(
+      of: closestUnchangedAncestor,
+      matching: previousSelection,
+      inRange: Range(
+        distanceToAncestor - distanceOffset,
+        distanceToAncestor + distanceOffset,
+      ),
+    );
+
+    return matchingDescendant ?? closestUnchangedAncestor;
+  }
+
+  (RemoteDiagnosticsNode?, int) _findClosestUnchangedAncestor(
+    RemoteDiagnosticsNode node, [
+    int distanceToAncestor = 1,
+  ]) {
+    final inspectorTreeNode = valueToInspectorTreeNode[node.valueRef];
+    if (inspectorTreeNode != null) {
+      return (inspectorTreeNode.diagnostic, distanceToAncestor);
+    }
+
+    final ancestor = node.parent;
+    if (ancestor == null) return (null, distanceToAncestor);
+    return _findClosestUnchangedAncestor(ancestor, distanceToAncestor++);
+  }
+
+  RemoteDiagnosticsNode? _findMatchingDescendant({
+    required RemoteDiagnosticsNode of,
+    required RemoteDiagnosticsNode matching,
+    required Range inRange,
+    int currentDistance = 1,
+  }) {
+    if (currentDistance > inRange.end) return null;
+
+    if (inRange.contains(currentDistance)) {
+      if (of.description == matching.description) {
+        return of;
+      }
+    }
+
+    final children = of.childrenNow;
+    final distance = currentDistance++;
+    for (final child in children) {
+      final matchingDescendant = _findMatchingDescendant(
+        of: child,
+        matching: matching,
+        inRange: inRange,
+        currentDistance: distance,
+      );
+      if (matchingDescendant != null) return matchingDescendant;
+    }
+
+    return null;
+  }
+
   Future<void> toggleImplementationWidgetsVisibility() async {
     final root = inspectorTree.root?.diagnostic;
     if (root != null) {
@@ -486,6 +594,9 @@
       );
       // Persist the selected node after refreshing the widget tree:
       refreshSelection(currentSelectedNode?.diagnostic);
+
+      // If the user is searching the tree, refresh the search matches.
+      inspectorTree.refreshSearchMatches();
     }
   }
 
@@ -527,7 +638,7 @@
     final matchingNode = findMatchingInspectorTreeNode(newSelection);
     if (matchingNode != null) {
       setSelectedNode(matchingNode);
-      syncSelectionHelper(selection: newSelection);
+      syncSelectionHelper(selection: matchingNode.diagnostic);
 
       syncTreeSelection();
     }
@@ -591,9 +702,7 @@
     unawaited(updateSelectionFromService());
   }
 
-  Future<void> updateSelectionFromService({
-    String? inspectorRef,
-  }) async {
+  Future<void> updateSelectionFromService({String? inspectorRef}) async {
     final selectionGroups = _selectionGroups;
     if (selectionGroups == null) {
       // Already disposed. Ignore this requested to update selection.
@@ -624,8 +733,9 @@
       // user selected an implementation widget in the app while implementation
       // widgets are hidden in the tree.
       if (implementationWidgetsHidden.value && newSelection != null) {
-        final isInTree =
-            valueToInspectorTreeNode.containsKey(newSelection.valueRef);
+        final isInTree = valueToInspectorTreeNode.containsKey(
+          newSelection.valueRef,
+        );
         final hasParent = newSelection.parent != null;
         final isImplementationWidget = !isInTree && !hasParent;
         if (isImplementationWidget) {
@@ -775,15 +885,17 @@
   void _updateSelectedErrorFromNode(InspectorTreeNode? node) {
     final inspectorRef = node?.diagnostic?.valueRef.id;
 
-    final errors = serviceConnection.errorBadgeManager
-        .erroredItemsForPage(InspectorScreen.id)
-        .value;
+    final errors =
+        serviceConnection.errorBadgeManager
+            .erroredItemsForPage(InspectorScreen.id)
+            .value;
 
     // Check whether the node that was just selected has any errors associated
     // with it.
-    var errorIndex = inspectorRef != null
-        ? errors.keys.toList().indexOf(inspectorRef)
-        : null;
+    var errorIndex =
+        inspectorRef != null
+            ? errors.keys.toList().indexOf(inspectorRef)
+            : null;
     if (errorIndex == -1) {
       errorIndex = null;
     }
@@ -793,8 +905,10 @@
     if (errorIndex != null) {
       // Mark the error as "seen" as this will render slightly differently
       // so the user can track which errored nodes they've viewed.
-      serviceConnection.errorBadgeManager
-          .markErrorAsRead(InspectorScreen.id, errors[inspectorRef!]!);
+      serviceConnection.errorBadgeManager.markErrorAsRead(
+        InspectorScreen.id,
+        errors[inspectorRef!]!,
+      );
       // Also clear the error badge since new errors may have arrived while
       // the inspector was visible (normally they're cleared when visiting
       // the screen) and visiting an errored node seems an appropriate
@@ -807,14 +921,13 @@
   void selectErrorByIndex(int index) {
     _selectedErrorIndex.value = index;
 
-    final errors = serviceConnection.errorBadgeManager
-        .erroredItemsForPage(InspectorScreen.id)
-        .value;
+    final errors =
+        serviceConnection.errorBadgeManager
+            .erroredItemsForPage(InspectorScreen.id)
+            .value;
 
     unawaited(
-      updateSelectionFromService(
-        inspectorRef: errors.keys.elementAt(index),
-      ),
+      updateSelectionFromService(inspectorRef: errors.keys.elementAt(index)),
     );
   }
 
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_data_models.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_data_models.dart
index 120f566..4e865fb 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_data_models.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_data_models.dart
@@ -72,17 +72,18 @@
   }
 
   List<double> transformToRenderSize(double largestRenderSize) => [
-        for (final s in sizes)
-          (s - smallestSize) *
-                  (largestRenderSize - smallestRenderSize) /
-                  (largestSize - smallestSize) +
-              smallestRenderSize,
-      ];
+    for (final s in sizes)
+      (s - smallestSize) *
+              (largestRenderSize - smallestRenderSize) /
+              (largestSize - smallestSize) +
+          smallestRenderSize,
+  ];
 
   var renderSizes = transformToRenderSize(largestRenderSize);
 
   if (useMaxSizeAvailable && sum(renderSizes) < maxSizeAvailable) {
-    largestRenderSize = (maxSizeAvailable - n * smallestRenderSize) *
+    largestRenderSize =
+        (maxSizeAvailable - n * smallestRenderSize) *
             (largestSize - smallestSize) /
             sum([for (final s in sizes) s - smallestSize]) +
         smallestRenderSize;
@@ -92,46 +93,46 @@
 }
 
 /// Data pattern containing a widget's widths or heights.
-typedef WidgetSizes = ({
-  /// Whether this record represents a widget's widths or heights.
-  SizeType type,
+typedef WidgetSizes =
+    ({
+      /// Whether this record represents a widget's widths or heights.
+      SizeType type,
 
-  /// Either the widget's left (if [type] is [SizeType.widths]) or top (if
-  /// [type] is [SizeType.heights]) padding.
-  double paddingA,
+      /// Either the widget's left (if [type] is [SizeType.widths]) or top (if
+      /// [type] is [SizeType.heights]) padding.
+      double paddingA,
 
-  /// Either the widget's width (if [type] is [SizeType.widths]) or height (if
-  /// [type] is [SizeType.heights]).
-  double widgetSize,
+      /// Either the widget's width (if [type] is [SizeType.widths]) or height (if
+      /// [type] is [SizeType.heights]).
+      double widgetSize,
 
-  /// Either the widget's right (if [type] is [SizeType.widths]) or bottom (if
-  /// [type] is [SizeType.heights]) padding.
-  double paddingB,
-});
+      /// Either the widget's right (if [type] is [SizeType.widths]) or bottom (if
+      /// [type] is [SizeType.heights]) padding.
+      double paddingB,
+    });
 
-enum SizeType {
-  widths,
-  heights,
-}
+enum SizeType { widths, heights }
 
 // TODO(albertusangga): Move this to [RemoteDiagnosticsNode] once dart:html app is removed
 /// Represents parsed layout information for a specific [RemoteDiagnosticsNode].
 class LayoutProperties {
   LayoutProperties(this.node, {int copyLevel = 1})
-      : description = node.description,
-        size = node.size!,
-        constraints = node.constraints,
-        isFlex = node.isFlex,
-        flexFactor = node.flexFactor,
-        flexFit = node.flexFit,
-        children = copyLevel == 0
-            ? []
-            : node.childrenNow
-                .where((child) => child.size != null)
-                .map(
-                  (child) => LayoutProperties(child, copyLevel: copyLevel - 1),
-                )
-                .toList(growable: false) {
+    : description = node.description,
+      size = node.size!,
+      constraints = node.constraints,
+      isFlex = node.isFlex,
+      flexFactor = node.flexFactor,
+      flexFit = node.flexFit,
+      children =
+          copyLevel == 0
+              ? []
+              : node.childrenNow
+                  .where((child) => child.size != null)
+                  .map(
+                    (child) =>
+                        LayoutProperties(child, copyLevel: copyLevel - 1),
+                  )
+                  .toList(growable: false) {
     for (final child in children) {
       child.parent = this;
     }
@@ -194,10 +195,10 @@
     if (constraintsLocal == null) return '';
     return constraintsLocal.hasBoundedWidth
         ? describeAxis(
-            constraintsLocal.minWidth,
-            constraintsLocal.maxWidth,
-            'w',
-          )
+          constraintsLocal.minWidth,
+          constraintsLocal.maxWidth,
+          'w',
+        )
         : 'width is unconstrained';
   }
 
@@ -206,10 +207,10 @@
     if (constraintsLocal == null) return '';
     return constraintsLocal.hasBoundedHeight
         ? describeAxis(
-            constraintsLocal.minHeight,
-            constraintsLocal.maxHeight,
-            'h',
-          )
+          constraintsLocal.minHeight,
+          constraintsLocal.maxHeight,
+          'h',
+        )
         : 'height is unconstrained';
   }
 
@@ -245,8 +246,9 @@
     final parentElement = node.parentRenderElement;
     // Fall back to this node's properties if there is no parent.
     if (parentElement == null) return this;
-    final parentProperties =
-        parentElement.computeLayoutProperties(forFlexLayout: false);
+    final parentProperties = parentElement.computeLayoutProperties(
+      forFlexLayout: false,
+    );
     return parentProperties ?? this;
   }
 
@@ -310,10 +312,7 @@
 ///
 /// See also:
 /// * [OverflowIndicatorPainter]
-enum OverflowSide {
-  right,
-  bottom,
-}
+enum OverflowSide { right, bottom }
 
 // TODO(jacobr): is it possible to overflow on multiple sides?
 // TODO(jacobr): do we need to worry about overflowing on the left side in RTL
@@ -344,10 +343,7 @@
 
 /// Encapsulation of [widths] and [heights] for the layout.
 class LayoutWidthsAndHeights {
-  LayoutWidthsAndHeights({
-    required this.widths,
-    required this.heights,
-  });
+  LayoutWidthsAndHeights({required this.widths, required this.heights});
 
   final WidgetSizes widths;
   final WidgetSizes heights;
@@ -452,8 +448,9 @@
 
   static FlexLayoutProperties _buildNode(RemoteDiagnosticsNode node) {
     final renderObjectJson = node.renderObject!.json;
-    final properties = (renderObjectJson['properties'] as List<Object?>)
-        .cast<Map<String, Object?>>();
+    final properties =
+        (renderObjectJson['properties'] as List<Object?>)
+            .cast<Map<String, Object?>>();
 
     final data = {
       for (final property in properties)
@@ -463,16 +460,19 @@
     return FlexLayoutProperties._fromNode(
       node,
       direction: _directionUtils.enumEntry(data['direction']) ?? Axis.vertical,
-      mainAxisAlignment:
-          _mainAxisAlignmentUtils.enumEntry(data['mainAxisAlignment']),
+      mainAxisAlignment: _mainAxisAlignmentUtils.enumEntry(
+        data['mainAxisAlignment'],
+      ),
       mainAxisSize: _mainAxisSizeUtils.enumEntry(data['mainAxisSize']),
-      crossAxisAlignment:
-          _crossAxisAlignmentUtils.enumEntry(data['crossAxisAlignment']),
-      textDirection: _textDirectionUtils.enumEntry(data['textDirection']) ??
+      crossAxisAlignment: _crossAxisAlignmentUtils.enumEntry(
+        data['crossAxisAlignment'],
+      ),
+      textDirection:
+          _textDirectionUtils.enumEntry(data['textDirection']) ??
           TextDirection.ltr,
       verticalDirection:
           _verticalDirectionUtils.enumEntry(data['verticalDirection']) ??
-              VerticalDirection.down,
+          VerticalDirection.down,
       textBaseline: _textBaselineUtils.enumEntry(data['textBaseline']),
     );
   }
@@ -513,10 +513,11 @@
 
   num get totalFlex {
     if (children.isEmpty) return 0;
-    _totalFlex ??= children
-        .map((child) => child.flexFactor ?? 0)
-        .reduce((value, element) => value + element)
-        .toInt();
+    _totalFlex ??=
+        children
+            .map((child) => child.flexFactor ?? 0)
+            .reduce((value, element) => value + element)
+            .toInt();
     return _totalFlex!;
   }
 
@@ -650,11 +651,12 @@
         );
       } else {
         // uniform cross axis sizes.
-        double size = crossAxisAlignment == CrossAxisAlignment.stretch
-            ? maxSizeAvailable(axis)
-            : largestSize /
-                math.max(dimension(axis), 1.0) *
-                maxSizeAvailable(axis);
+        double size =
+            crossAxisAlignment == CrossAxisAlignment.stretch
+                ? maxSizeAvailable(axis)
+                : largestSize /
+                    math.max(dimension(axis), 1.0) *
+                    maxSizeAvailable(axis);
         size = math.max(size, smallestRenderSize(axis));
         return sizes.map((_) => size).toList();
       }
@@ -663,9 +665,10 @@
     final widths = renderSizes(Axis.horizontal);
     final heights = renderSizes(Axis.vertical);
 
-    final renderFreeSpace = freeSpace > 0.0
-        ? (isMainAxisHorizontal ? widths.last : heights.last)
-        : 0.0;
+    final renderFreeSpace =
+        freeSpace > 0.0
+            ? (isMainAxisHorizontal ? widths.last : heights.last)
+            : 0.0;
 
     final renderLeadingSpace = leadingSpace(renderFreeSpace);
     final renderBetweenSpace = betweenSpace(renderFreeSpace);
@@ -712,11 +715,11 @@
     for (var i = 0; i < children.length; ++i) {
       childrenRenderProps.add(
         RenderProperties(
-          axis: direction,
-          size: Size(widths[i], heights[i]),
-          offset: Offset.zero,
-          realSize: displayChildren[i].size,
-        )
+            axis: direction,
+            size: Size(widths[i], heights[i]),
+            offset: Offset.zero,
+            realSize: displayChildren[i].size,
+          )
           ..mainAxisOffset = calculateMainAxisOffset(i)
           ..crossAxisOffset = calculateCrossAxisOffset(i)
           ..layoutProperties = displayChildren[i],
@@ -757,7 +760,8 @@
         displayMainAxisAlignment != MainAxisAlignment.end) {
       spaces.add(
         renderPropsWithFullCrossAxisDimension.clone()
-          ..mainAxisOffset = childrenRenderProps.last.mainAxisDimension +
+          ..mainAxisOffset =
+              childrenRenderProps.last.mainAxisDimension +
               childrenRenderProps.last.mainAxisOffset
           ..mainAxisDimension = renderLeadingSpace
           ..mainAxisRealDimension = actualLeadingSpace,
@@ -795,13 +799,15 @@
         spaces.add(space.clone()..crossAxisOffset = 0.0);
         spaces.add(
           space.clone()
-            ..crossAxisOffset = renderProperties.crossAxisDimension +
+            ..crossAxisOffset =
+                renderProperties.crossAxisDimension +
                 renderProperties.crossAxisOffset,
         );
       } else {
-        space.crossAxisOffset = crossAxisAlignment == CrossAxisAlignment.end
-            ? 0
-            : renderProperties.crossAxisDimension;
+        space.crossAxisOffset =
+            crossAxisAlignment == CrossAxisAlignment.end
+                ? 0
+                : renderProperties.crossAxisDimension;
         spaces.add(space);
       }
     }
@@ -809,18 +815,24 @@
   }
 
   static final _directionUtils = EnumUtils<Axis>(Axis.values);
-  static final _mainAxisAlignmentUtils =
-      EnumUtils<MainAxisAlignment>(MainAxisAlignment.values);
-  static final _mainAxisSizeUtils =
-      EnumUtils<MainAxisSize>(MainAxisSize.values);
-  static final _crossAxisAlignmentUtils =
-      EnumUtils<CrossAxisAlignment>(CrossAxisAlignment.values);
-  static final _textDirectionUtils =
-      EnumUtils<TextDirection>(TextDirection.values);
-  static final _verticalDirectionUtils =
-      EnumUtils<VerticalDirection>(VerticalDirection.values);
-  static final _textBaselineUtils =
-      EnumUtils<TextBaseline>(TextBaseline.values);
+  static final _mainAxisAlignmentUtils = EnumUtils<MainAxisAlignment>(
+    MainAxisAlignment.values,
+  );
+  static final _mainAxisSizeUtils = EnumUtils<MainAxisSize>(
+    MainAxisSize.values,
+  );
+  static final _crossAxisAlignmentUtils = EnumUtils<CrossAxisAlignment>(
+    CrossAxisAlignment.values,
+  );
+  static final _textDirectionUtils = EnumUtils<TextDirection>(
+    TextDirection.values,
+  );
+  static final _verticalDirectionUtils = EnumUtils<VerticalDirection>(
+    VerticalDirection.values,
+  );
+  static final _textBaselineUtils = EnumUtils<TextBaseline>(
+    TextBaseline.values,
+  );
 }
 
 /// RenderProperties contains information for rendering a [LayoutProperties] node
@@ -832,12 +844,12 @@
     Size? realSize,
     this.layoutProperties,
     this.isFreeSpace = false,
-  })  : width = size?.width ?? 0.0,
-        height = size?.height ?? 0.0,
-        realWidth = realSize?.width ?? 0.0,
-        realHeight = realSize?.height ?? 0.0,
-        dx = offset?.dx ?? 0.0,
-        dy = offset?.dy ?? 0.0;
+  }) : width = size?.width ?? 0.0,
+       height = size?.height ?? 0.0,
+       realWidth = realSize?.width ?? 0.0,
+       realHeight = realSize?.height ?? 0.0,
+       dx = offset?.dx ?? 0.0,
+       dy = offset?.dy ?? 0.0;
 
   final Axis axis;
 
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart
index afcb40a..31c9abc 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart
@@ -11,11 +11,11 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/console/eval/inspector_tree_v2.dart';
-import '../../shared/error_badge_manager.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/error_badge_manager.dart';
 import '../../shared/primitives/blocking_action_mixin.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/search.dart';
 import '../inspector_shared/inspector_controls.dart';
 import '../inspector_shared/inspector_screen.dart';
@@ -122,18 +122,13 @@
     final widgetTrees = SplitPane(
       axis: splitAxis,
       initialFractions: const [0.33, 0.67],
-      children: [
-        inspectorTree,
-        WidgetDetails(controller: controller),
-      ],
+      children: [inspectorTree, WidgetDetails(controller: controller)],
     );
     return Column(
       children: <Widget>[
         InspectorControls(controller: controller),
         const SizedBox(height: intermediateSpacing),
-        Expanded(
-          child: widgetTrees,
-        ),
+        Expanded(child: widgetTrees),
       ],
     );
   }
@@ -149,25 +144,28 @@
                 constraints: constraints,
                 onRefreshInspectorPressed: _refreshInspector,
                 onSearchVisibleToggle: _onSearchVisibleToggle,
-                searchFieldBuilder: () =>
-                    StatelessSearchField<InspectorTreeRow>(
-                  controller: _inspectorTreeController,
-                  searchFieldEnabled: true,
-                  shouldRequestFocus: searchVisible,
-                  supportsNavigation: true,
-                  onClose: _onSearchVisibleToggle,
-                ),
+                searchFieldBuilder:
+                    () => StatelessSearchField<InspectorTreeRow>(
+                      controller: _inspectorTreeController,
+                      searchFieldEnabled: true,
+                      shouldRequestFocus: searchVisible,
+                      supportsNavigation: true,
+                      onClose: _onSearchVisibleToggle,
+                    ),
               ),
               Expanded(
                 child: ValueListenableBuilder(
                   valueListenable: serviceConnection.errorBadgeManager
                       .erroredItemsForPage(InspectorScreen.id),
-                  builder:
-                      (_, LinkedHashMap<String, DevToolsError> errors, __) {
-                    final inspectableErrors = errors.map(
-                      (key, value) =>
-                          MapEntry(key, value as InspectableWidgetError),
-                    ) as LinkedHashMap<String, InspectableWidgetError>;
+                  builder: (_, LinkedHashMap<String, DevToolsError> errors, _) {
+                    final inspectableErrors =
+                        errors.map(
+                              (key, value) => MapEntry(
+                                key,
+                                value as InspectableWidgetError,
+                              ),
+                            )
+                            as LinkedHashMap<String, InspectableWidgetError>;
                     return Stack(
                       children: [
                         InspectorTree(
@@ -180,15 +178,17 @@
                         if (errors.isNotEmpty)
                           ValueListenableBuilder<int?>(
                             valueListenable: controller.selectedErrorIndex,
-                            builder: (_, selectedErrorIndex, __) => Positioned(
-                              top: 0,
-                              right: 0,
-                              child: ErrorNavigator(
-                                errors: inspectableErrors,
-                                errorIndex: selectedErrorIndex,
-                                onSelectError: controller.selectErrorByIndex,
-                              ),
-                            ),
+                            builder:
+                                (_, selectedErrorIndex, _) => Positioned(
+                                  top: 0,
+                                  right: 0,
+                                  child: ErrorNavigator(
+                                    errors: inspectableErrors,
+                                    errorIndex: selectedErrorIndex,
+                                    onSelectError:
+                                        controller.selectErrorByIndex,
+                                  ),
+                                ),
                           ),
                       ],
                     );
@@ -254,18 +254,18 @@
               ),
               ...!isSearchVisible
                   ? [
-                      const Spacer(),
-                      ToolbarAction(
-                        icon: Icons.search,
-                        onPressed: onSearchVisibleToggle,
-                        tooltip: 'Search Tree',
-                      ),
-                    ]
+                    const Spacer(),
+                    ToolbarAction(
+                      icon: Icons.search,
+                      onPressed: onSearchVisibleToggle,
+                      tooltip: 'Search Tree',
+                    ),
+                  ]
                   : [
-                      constraints.maxWidth >= _searchBreakpoint
-                          ? _buildSearchControls()
-                          : const Spacer(),
-                    ],
+                    constraints.maxWidth >= _searchBreakpoint
+                        ? _buildSearchControls()
+                        : const Spacer(),
+                  ],
               ToolbarAction(
                 icon: Icons.refresh,
                 onPressed: onRefreshInspectorPressed,
@@ -275,10 +275,7 @@
           ),
         ),
         if (isSearchVisible && constraints.maxWidth < _searchBreakpoint)
-          _controlsContainer(
-            context,
-            Row(children: [_buildSearchControls()]),
-          ),
+          _controlsContainer(context, Row(children: [_buildSearchControls()])),
       ],
     );
   }
@@ -287,9 +284,7 @@
     return Container(
       height: defaultHeaderHeight,
       decoration: BoxDecoration(
-        border: Border(
-          bottom: defaultBorderSide(Theme.of(context)),
-        ),
+        border: Border(bottom: defaultBorderSide(Theme.of(context))),
       ),
       child: child,
     );
@@ -322,9 +317,10 @@
   @override
   Widget build(BuildContext context) {
     final colorScheme = Theme.of(context).colorScheme;
-    final label = errorIndex != null
-        ? 'Error ${errorIndex! + 1}/${errors.length}'
-        : 'Errors: ${errors.length}';
+    final label =
+        errorIndex != null
+            ? 'Error ${errorIndex! + 1}/${errors.length}'
+            : 'Errors: ${errors.length}';
     return Container(
       color: colorScheme.errorContainer,
       child: Padding(
@@ -338,9 +334,7 @@
               padding: const EdgeInsets.only(right: denseSpacing),
               child: Text(
                 label,
-                style: TextStyle(
-                  color: colorScheme.onErrorContainer,
-                ),
+                style: TextStyle(color: colorScheme.onErrorContainer),
               ),
             ),
             _ErrorNavigatorButton(
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart
index 8768df2..45e3d0e 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart
@@ -16,19 +16,19 @@
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
 import '../../shared/analytics/metrics.dart';
-import '../../shared/collapsible_mixin.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/console/eval/inspector_tree_v2.dart';
 import '../../shared/console/widgets/description.dart';
 import '../../shared/diagnostics/diagnostics_node.dart';
-import '../../shared/diagnostics_text_styles.dart';
-import '../../shared/error_badge_manager.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/error_badge_manager.dart';
+import '../../shared/primitives/collapsible_mixin.dart';
+import '../../shared/primitives/diagnostics_text_styles.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/search.dart';
 import '../../shared/ui/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'inspector_controller.dart';
 
 final _log = Logger('inspector_tree_controller');
@@ -114,11 +114,12 @@
       gac.inspector,
       gac.inspectorTreeControllerInitialized,
       nonInteraction: true,
-      screenMetricsProvider: () => InspectorScreenMetrics.v2(
-        inspectorTreeControllerId: gaId,
-        rootSetCount: _rootSetCount,
-        rowCount: _rowsInTree.value.length,
-      ),
+      screenMetricsProvider:
+          () => InspectorScreenMetrics.v2(
+            inspectorTreeControllerId: gaId,
+            rootSetCount: _rootSetCount,
+            rowCount: _rowsInTree.value.length,
+          ),
     );
   }
 
@@ -161,10 +162,7 @@
 
   set root(InspectorTreeNode? node) {
     if (node != null) {
-      _updateRows(
-        node: node,
-        updateSearchableRows: true,
-      );
+      _updateRows(node: node, updateSearchableRows: true);
     }
     _root = node;
 
@@ -172,11 +170,12 @@
       gac.inspector,
       gac.inspectorTreeControllerRootChange,
       nonInteraction: true,
-      screenMetricsProvider: () => InspectorScreenMetrics.v2(
-        inspectorTreeControllerId: gaId,
-        rootSetCount: ++_rootSetCount,
-        rowCount: _rowsInTree.value.length,
-      ),
+      screenMetricsProvider:
+          () => InspectorScreenMetrics.v2(
+            inspectorTreeControllerId: gaId,
+            rootSetCount: ++_rootSetCount,
+            rowCount: _rowsInTree.value.length,
+          ),
     );
   }
 
@@ -321,8 +320,10 @@
     final selectionLocal = selection;
     final diagnostic = selectionLocal?.diagnostic;
 
-    final toggledHideableGroup =
-        _maybeToggleHideableGroup(diagnostic, showGroup: false);
+    final toggledHideableGroup = _maybeToggleHideableGroup(
+      diagnostic,
+      showGroup: false,
+    );
     if (toggledHideableGroup) return;
 
     // This logic is consistent with how IntelliJ handles tree navigation on
@@ -350,8 +351,10 @@
     final selectionLocal = selection;
     final diagnostic = selectionLocal?.diagnostic;
 
-    final toggledHideableGroup =
-        _maybeToggleHideableGroup(diagnostic, showGroup: true);
+    final toggledHideableGroup = _maybeToggleHideableGroup(
+      diagnostic,
+      showGroup: true,
+    );
     if (toggledHideableGroup) return;
 
     // This logic is consistent with how IntelliJ handles tree navigation on
@@ -371,12 +374,15 @@
 
     refreshTree(
       updateTreeAction: () {
-        final nodeToSelect = selection == null
-            ? root
-            : rowAtIndex(
-                (_rowIndexFromNode(selection!) + indexOffset)
-                    .clamp(0, _numRows - 1),
-              )?.node;
+        final nodeToSelect =
+            selection == null
+                ? root
+                : rowAtIndex(
+                  (_rowIndexFromNode(selection!) + indexOffset).clamp(
+                    0,
+                    _numRows - 1,
+                  ),
+                )?.node;
         setSelectedNode(nodeToSelect);
         return true;
       },
@@ -392,7 +398,8 @@
   }) {
     final isHideableGroupLeader =
         diagnostic != null && diagnostic.isHideableGroupLeader;
-    final shouldToggle = isHideableGroupLeader &&
+    final shouldToggle =
+        isHideableGroupLeader &&
         (showGroup ? diagnostic.groupIsHidden : !diagnostic.groupIsHidden);
 
     if (shouldToggle) {
@@ -478,7 +485,8 @@
             index: currentIdx,
             ticks: ticks,
             depth: depth,
-            lineToParent: !node.isProperty &&
+            lineToParent:
+                !node.isProperty &&
                 currentIdx != 0 &&
                 node.parent!.showLinesToChildren,
             hasSingleChild: node.children.length == 1,
@@ -486,27 +494,21 @@
         );
       }
 
-      final style = node.diagnostic?.style;
-      final indented = style != DiagnosticsTreeStyle.flat &&
-          style != DiagnosticsTreeStyle.error;
-
       if (!node.isExpanded && !includeCollapsedRows) return;
       final children = node.children;
       final parentDepth = depth;
       final childrenDepth = children.length > 1 ? parentDepth + 1 : parentDepth;
       for (final child in children) {
-        final shouldAddTick = children.length > 1 &&
+        final shouldAddTick =
+            children.length > 1 &&
             children.last != child &&
             !children.last.isProperty &&
-            indented;
+            node.diagnostic?.shouldIndent == true;
 
         buildRowsHelper(
           child,
           depth: childrenDepth,
-          ticks: [
-            ...ticks,
-            if (shouldAddTick) parentDepth,
-          ],
+          ticks: [...ticks, if (shouldAddTick) parentDepth],
         );
       }
     }
@@ -546,10 +548,7 @@
 
   void onSelectNode(InspectorTreeNode? node) {
     setSelectedNode(node);
-    ga.select(
-      gac.inspector,
-      gac.treeNodeSelection,
-    );
+    ga.select(gac.inspector, gac.treeNodeSelection);
     final diagnostic = node?.diagnostic;
     if (diagnostic != null && diagnostic.groupIsHidden) {
       diagnostic.hideableGroupLeader?.toggleHiddenGroup();
@@ -662,8 +661,9 @@
     if (diagnosticsNode.hasChildren ||
         diagnosticsNode.inlineProperties.isNotEmpty) {
       if (diagnosticsNode.childrenReady || !diagnosticsNode.hasChildren) {
-        final styleIsMultiline =
-            expandPropertiesByDefault(diagnosticsNode.style);
+        final styleIsMultiline = expandPropertiesByDefault(
+          diagnosticsNode.style,
+        );
         setupChildren(
           diagnosticsNode,
           node,
@@ -734,12 +734,7 @@
       try {
         final children = await diagnostic.children;
         if (treeNode.hasPlaceholderChildren || treeNode.children.isEmpty) {
-          setupChildren(
-            diagnostic,
-            treeNode,
-            children,
-            expandChildren: true,
-          );
+          setupChildren(diagnostic, treeNode, children, expandChildren: true);
           refreshTree(
             updateTreeAction: () {
               nodeChanged(treeNode);
@@ -780,8 +775,9 @@
     if (searchPreviousMatches) {
       final previousMatches = searchMatches.value;
       for (final previousMatch in previousMatches) {
-        if (previousMatch.node.diagnostic!.searchValue
-            .caseInsensitiveContains(search)) {
+        if (previousMatch.node.diagnostic!.searchValue.caseInsensitiveContains(
+          search,
+        )) {
           matches.add(previousMatch);
         }
       }
@@ -796,21 +792,17 @@
     if (search.isEmpty ||
         inspectorService == null ||
         inspectorService.isDisposed) {
-      assert(
-        () {
-          debugPrint('Search completed, no search');
-          return true;
-        }(),
-      );
+      assert(() {
+        debugPrint('Search completed, no search');
+        return true;
+      }());
       return matches;
     }
 
-    assert(
-      () {
-        debugPrint('Search started: $_searchTarget');
-        return true;
-      }(),
-    );
+    assert(() {
+      debugPrint('Search started: $_searchTarget');
+      return true;
+    }());
 
     for (final row in _searchableCachedRows) {
       final diagnostic = row!.node.diagnostic;
@@ -827,14 +819,12 @@
       // Widget search end
     }
 
-    assert(
-      () {
-        debugPrint(
-          'Search completed with $debugStatsWidgets widgets, $debugStatsSearchOps ops',
-        );
-        return true;
-      }(),
-    );
+    assert(() {
+      debugPrint(
+        'Search completed with $debugStatsWidgets widgets, $debugStatsSearchOps ops',
+      );
+      return true;
+    }());
 
     return matches;
   }
@@ -954,21 +944,21 @@
   }
 
   // TODO(devoncarew): Commented out as per flutter/devtools/pull/2001.
-//  void _onScrollYChange() {
-//    if (controller == null) return;
-//
-//    // If the vertical position  is already being animated we should not trigger
-//    // a new animation of the horizontal position as a more direct animation of
-//    // the horizontal position has already been triggered.
-//    if (currentAnimateY != null) return;
-//
-//    final x = _computeTargetX(_scrollControllerY.offset);
-//    _scrollControllerX.animateTo(
-//      x,
-//      duration: defaultDuration,
-//      curve: defaultCurve,
-//    );
-//  }
+  //  void _onScrollYChange() {
+  //    if (controller == null) return;
+  //
+  //    // If the vertical position  is already being animated we should not trigger
+  //    // a new animation of the horizontal position as a more direct animation of
+  //    // the horizontal position has already been triggered.
+  //    if (currentAnimateY != null) return;
+  //
+  //    final x = _computeTargetX(_scrollControllerY.offset);
+  //    _scrollControllerX.animateTo(
+  //      x,
+  //      duration: defaultDuration,
+  //      curve: defaultCurve,
+  //    );
+  //  }
 
   @override
   Future<void> waitForClientsThenScrollToRect(
@@ -992,12 +982,14 @@
 
     final initialX = rect.left;
     final initialY = rect.top;
-    final yOffsetAtViewportTop = _scrollControllerY.hasClients
-        ? _scrollControllerY.offset
-        : _scrollControllerY.initialScrollOffset;
-    final xOffsetAtViewportLeft = _scrollControllerX.hasClients
-        ? _scrollControllerX.offset
-        : _scrollControllerX.initialScrollOffset;
+    final yOffsetAtViewportTop =
+        _scrollControllerY.hasClients
+            ? _scrollControllerY.offset
+            : _scrollControllerY.initialScrollOffset;
+    final xOffsetAtViewportLeft =
+        _scrollControllerX.hasClients
+            ? _scrollControllerX.offset
+            : _scrollControllerX.initialScrollOffset;
 
     final viewPortInScrollControllerSpace = Rect.fromLTWH(
       xOffsetAtViewportLeft,
@@ -1012,8 +1004,9 @@
       (rect.centerLeft.dx + rect.center.dx) / 2,
       rect.center.dy,
     );
-    final isRectInViewPort =
-        viewPortInScrollControllerSpace.contains(centerLeftHalf);
+    final isRectInViewPort = viewPortInScrollControllerSpace.contains(
+      centerLeftHalf,
+    );
     if (isRectInViewPort) {
       // The rect is already in view, don't scroll
       return;
@@ -1083,9 +1076,7 @@
 
   /// Pad [initialY] so that a row would be placed in the vertical center of
   /// the screen.
-  double _padTargetY({
-    required double initialY,
-  }) {
+  double _padTargetY({required double initialY}) {
     return initialY - (safeViewportHeight / 2) + inspectorRowHeight / 2;
   }
 
@@ -1162,7 +1153,8 @@
                 controller: _scrollControllerX,
                 child: ConstrainedBox(
                   constraints: BoxConstraints(
-                    maxWidth: treeControllerLocal.rowWidth +
+                    maxWidth:
+                        treeControllerLocal.rowWidth +
                         treeControllerLocal.maxRowIndent,
                   ),
                   // TODO(kenz): this scrollbar needs to be sticky to the right side of
@@ -1183,29 +1175,29 @@
                         offsetControllerViewportDimension: viewportWidth,
                         child: ListView.custom(
                           itemExtent: inspectorRowHeight,
-                          childrenDelegate: SliverChildBuilderDelegate(
-                            (context, index) {
-                              if (index == rows.length) {
-                                return SizedBox(height: inspectorRowHeight);
-                              }
-                              final row =
-                                  treeControllerLocal.rowAtIndex(index)!;
-                              final inspectorRef =
-                                  row.node.diagnostic?.valueRef.id;
-                              return _InspectorTreeRowWidget(
-                                key: PageStorageKey(row.node),
-                                inspectorTreeState: this,
-                                row: row,
-                                scrollControllerX: _scrollControllerX,
-                                viewportWidth: viewportWidth,
-                                error: widget.widgetErrors != null &&
-                                        inspectorRef != null
-                                    ? widget.widgetErrors![inspectorRef]
-                                    : null,
-                              );
-                            },
-                            childCount: rows.length + 1,
-                          ),
+                          childrenDelegate: SliverChildBuilderDelegate((
+                            context,
+                            index,
+                          ) {
+                            if (index == rows.length) {
+                              return SizedBox(height: inspectorRowHeight);
+                            }
+                            final row = treeControllerLocal.rowAtIndex(index)!;
+                            final inspectorRef =
+                                row.node.diagnostic?.valueRef.id;
+                            return _InspectorTreeRowWidget(
+                              key: PageStorageKey(row.node),
+                              inspectorTreeState: this,
+                              row: row,
+                              scrollControllerX: _scrollControllerX,
+                              viewportWidth: viewportWidth,
+                              error:
+                                  widget.widgetErrors != null &&
+                                          inspectorRef != null
+                                      ? widget.widgetErrors![inspectorRef]
+                                      : null,
+                            );
+                          }, childCount: rows.length + 1),
                           controller: _scrollControllerY,
                         ),
                       ),
@@ -1226,9 +1218,10 @@
   bool get wantKeepAlive => true;
 }
 
-Paint _defaultPaint(ColorScheme colorScheme) => Paint()
-  ..color = colorScheme.treeGuidelineColor
-  ..strokeWidth = chartLineStrokeWidth;
+Paint _defaultPaint(ColorScheme colorScheme) =>
+    Paint()
+      ..color = colorScheme.treeGuidelineColor
+      ..strokeWidth = chartLineStrokeWidth;
 
 /// The distance (on the x-axis) between the expand/collapse and the start of
 /// the row, as determined by a percentage of the [inspectorColumnIndent].
@@ -1273,7 +1266,8 @@
     final distanceFromExpandCollapseToRowStart =
         inspectorColumnIndent * _expandCollapseToRowStartXDistancePercentage;
     for (final tick in row.ticks) {
-      final expandCollapseX = _controller.getDepthIndent(tick) -
+      final expandCollapseX =
+          _controller.getDepthIndent(tick) -
           distanceFromExpandCollapseToRowStart;
       // Draw a vertical line for each tick identifying a connection between
       // an ancestor of this node and some other node in the tree.
@@ -1286,11 +1280,13 @@
     // If this row is itself connected to a parent then draw the L shaped line
     // to make that connection.
     if (row.lineToParent) {
-      final parentExpandCollapseX = _controller.getDepthIndent(row.depth - 1) -
+      final parentExpandCollapseX =
+          _controller.getDepthIndent(row.depth - 1) -
           distanceFromExpandCollapseToRowStart;
-      final width = showExpandCollapse
-          ? inspectorColumnIndent * 0.6
-          : inspectorColumnIndent;
+      final width =
+          showExpandCollapse
+              ? inspectorColumnIndent * 0.6
+              : inspectorColumnIndent;
       canvas.drawLine(
         Offset(parentExpandCollapseX, 0.0),
         Offset(parentExpandCollapseX, inspectorRowHeight * 0.5),
@@ -1314,13 +1310,15 @@
     final subordinates =
         node.diagnostic?.hideableGroupSubordinates ?? <RemoteDiagnosticsNode>[];
     final groupIsHidden = node.diagnostic?.groupIsHidden ?? false;
-    final lastHiddenSubordinateHasNoChildren = groupIsHidden &&
+    final lastHiddenSubordinateHasNoChildren =
+        groupIsHidden &&
         subordinates.isNotEmpty &&
         subordinates.last.childrenNow.isEmpty;
     if (expandedWithSingleChild && !lastHiddenSubordinateHasNoChildren) {
       final distanceFromIconCenterToRowStart =
           inspectorColumnIndent * _iconCenterToRowStartXDistancePercentage;
-      final iconCenterX = _controller.getDepthIndent(row.depth) -
+      final iconCenterX =
+          _controller.getDepthIndent(row.depth) -
           distanceFromIconCenterToRowStart;
       // Draw a line from the bottom of the current row's icon to the top of the
       // child row's icon:
@@ -1393,9 +1391,10 @@
 
     Color? backgroundColor;
     if (row.isSelected) {
-      backgroundColor = hasError
-          ? colorScheme.errorContainer
-          : colorScheme.selectedRowBackgroundColor;
+      backgroundColor =
+          hasError
+              ? colorScheme.errorContainer
+              : colorScheme.selectedRowBackgroundColor;
     }
 
     final node = row.node;
@@ -1414,19 +1413,16 @@
               children: [
                 node.showExpandCollapse
                     ? InkWell(
-                        onTap: onToggle,
-                        child: RotationTransition(
-                          turns: expandArrowAnimation,
-                          child: Icon(
-                            Icons.expand_more,
-                            size: defaultIconSize,
-                          ),
-                        ),
-                      )
-                    : const SizedBox(
-                        width: expandCollapseWidth,
-                        height: defaultSpacing,
+                      onTap: onToggle,
+                      child: RotationTransition(
+                        turns: expandArrowAnimation,
+                        child: Icon(Icons.expand_more, size: defaultIconSize),
                       ),
+                    )
+                    : const SizedBox(
+                      width: expandCollapseWidth,
+                      height: defaultSpacing,
+                    ),
                 Expanded(
                   child: Container(
                     color: backgroundColor,
@@ -1454,30 +1450,32 @@
                           nodeDescriptionHighlightStyle:
                               searchValue.isEmpty || !row.isSearchMatch
                                   ? DiagnosticsTextStyles.regular(
-                                      Theme.of(context).colorScheme,
-                                    )
+                                    Theme.of(context).colorScheme,
+                                  )
                                   : row.isSelected
-                                      ? theme.searchMatchHighlightStyleFocused
-                                      : theme.searchMatchHighlightStyle,
-                          actionLabel: isHideableGroupLeader
-                              ? diagnostic!.groupIsHidden
-                                  ? '(expand)'
-                                  : '(collapse)'
-                              : null,
-                          actionCallback: isHideableGroupLeader
-                              ? () {
-                                  controller.refreshTree(
-                                    updateTreeAction: () {
-                                      controller.toggleHiddenGroup(node);
-                                      return true;
-                                    },
-                                  );
-                                }
-                              : null,
-                          customDescription: isHideableGroupLeader &&
-                                  diagnostic!.groupIsHidden
-                              ? '${diagnostic.hideableGroupSubordinates!.length + 1} more widgets...'
-                              : null,
+                                  ? theme.searchMatchHighlightStyleFocused
+                                  : theme.searchMatchHighlightStyle,
+                          actionLabel:
+                              isHideableGroupLeader
+                                  ? diagnostic!.groupIsHidden
+                                      ? '(expand)'
+                                      : '(collapse)'
+                                  : null,
+                          actionCallback:
+                              isHideableGroupLeader
+                                  ? () {
+                                    controller.refreshTree(
+                                      updateTreeAction: () {
+                                        controller.toggleHiddenGroup(node);
+                                        return true;
+                                      },
+                                    );
+                                  }
+                                  : null,
+                          customDescription:
+                              isHideableGroupLeader && diagnostic!.groupIsHidden
+                                  ? '${diagnostic.hideableGroupSubordinates!.length + 1} more widgets...'
+                                  : null,
                           customIconName:
                               isHideableGroupLeader && diagnostic!.groupIsHidden
                                   ? 'HiddenGroup'
@@ -1496,8 +1494,10 @@
 
     // Wrap with tooltip if there is an error for this node's widget.
     if (hasError) {
-      rowWidget =
-          DevToolsTooltip(message: error!.errorMessage, child: rowWidget);
+      rowWidget = DevToolsTooltip(
+        message: error!.errorMessage,
+        child: rowWidget,
+      );
     }
 
     return CustomPaint(
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/box/box.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/box/box.dart
index d221d7a..21cb5c9 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/box/box.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/box/box.dart
@@ -180,11 +180,7 @@
             Container(
               width: constraints.maxWidth,
               height: constraints.maxHeight,
-              decoration: BoxDecoration(
-                border: Border.all(
-                  color: widgetColor,
-                ),
-              ),
+              decoration: BoxDecoration(border: Border.all(color: widgetColor)),
               child: Stack(
                 children: [
                   ..._paddingWidgets(
@@ -354,9 +350,10 @@
   final paddingASize = sizes.paddingA;
   final paddingBSize = sizes.paddingB;
 
-  final paddingFraction = paddingASize > 0 && paddingBSize > 0
-      ? _narrowPaddingVisualizerPercent
-      : _widePaddingVisualizerPercent;
+  final paddingFraction =
+      paddingASize > 0 && paddingBSize > 0
+          ? _narrowPaddingVisualizerPercent
+          : _widePaddingVisualizerPercent;
 
   final paddingAFraction = paddingASize > 0 ? paddingFraction : 0.0;
   final paddingBFraction = paddingBSize > 0 ? paddingFraction : 0.0;
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/flex.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/flex.dart
index c63b04b..6b8517a 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/flex.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/flex.dart
@@ -8,10 +8,10 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/diagnostics/diagnostics_node.dart';
 import '../../../../shared/diagnostics/inspector_service.dart';
 import '../../../../shared/primitives/math_utils.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../inspector_data_models.dart';
 import '../ui/arrow.dart';
 import '../ui/free_space.dart';
@@ -29,10 +29,7 @@
 double get alignmentDropdownMaxSize => scaleByFontFactor(140.0);
 
 class FlexLayoutExplorerWidget extends LayoutExplorerWidget {
-  const FlexLayoutExplorerWidget(
-    super.inspectorController, {
-    super.key,
-  });
+  const FlexLayoutExplorerWidget(super.inspectorController, {super.key});
 
   static bool shouldDisplay(RemoteDiagnosticsNode node) {
     return (node.isFlex) || (node.parent?.isFlex ?? false);
@@ -43,8 +40,12 @@
       FlexLayoutExplorerWidgetState();
 }
 
-class FlexLayoutExplorerWidgetState extends LayoutExplorerWidgetState<
-    FlexLayoutExplorerWidget, FlexLayoutProperties> {
+class FlexLayoutExplorerWidgetState
+    extends
+        LayoutExplorerWidgetState<
+          FlexLayoutExplorerWidget,
+          FlexLayoutProperties
+        > {
   final scrollController = ScrollController();
 
   Axis get direction => properties!.direction;
@@ -57,9 +58,10 @@
           ? colorScheme.mainAxisColor
           : colorScheme.crossAxisColor;
 
-  Color verticalColor(ColorScheme colorScheme) => properties!.isMainAxisVertical
-      ? colorScheme.mainAxisColor
-      : colorScheme.crossAxisColor;
+  Color verticalColor(ColorScheme colorScheme) =>
+      properties!.isMainAxisVertical
+          ? colorScheme.mainAxisColor
+          : colorScheme.crossAxisColor;
 
   Color horizontalTextColor(ColorScheme colorScheme) =>
       properties!.isMainAxisHorizontal
@@ -120,9 +122,10 @@
 
   Widget _buildAxisAlignmentDropdown(Axis axis, ThemeData theme) {
     final colorScheme = theme.colorScheme;
-    final color = axis == direction
-        ? colorScheme.mainAxisTextColor
-        : colorScheme.crossAxisTextColor;
+    final color =
+        axis == direction
+            ? colorScheme.mainAxisTextColor
+            : colorScheme.crossAxisTextColor;
     List<Enum> alignmentEnumEntries;
     Object? selected;
     final propertiesLocal = properties!;
@@ -150,9 +153,10 @@
           isExpanded: true,
           // Avoid showing an underline for the main axis and cross-axis drop downs.
           underline: const SizedBox(),
-          iconEnabledColor: axis == propertiesLocal.direction
-              ? colorScheme.mainAxisColor
-              : colorScheme.crossAxisColor,
+          iconEnabledColor:
+              axis == propertiesLocal.direction
+                  ? colorScheme.mainAxisColor
+                  : colorScheme.crossAxisColor,
           selectedItemBuilder: (context) {
             return [
               for (final alignment in alignmentEnumEntries)
@@ -173,13 +177,13 @@
                       child: Image.asset(
                         (axis == direction)
                             ? mainAxisAssetImageUrl(
-                                direction,
-                                alignment as MainAxisAlignment,
-                              )
+                              direction,
+                              alignment as MainAxisAlignment,
+                            )
                             : crossAxisAssetImageUrl(
-                                direction,
-                                alignment as CrossAxisAlignment,
-                              ),
+                              direction,
+                              alignment as CrossAxisAlignment,
+                            ),
                         height: axisAlignmentAssetImageHeight,
                         fit: BoxFit.fitHeight,
                         color: color,
@@ -212,13 +216,13 @@
                         child: Image.asset(
                           (axis == direction)
                               ? mainAxisAssetImageUrl(
-                                  direction,
-                                  alignment as MainAxisAlignment,
-                                )
+                                direction,
+                                alignment as MainAxisAlignment,
+                              )
                               : crossAxisAssetImageUrl(
-                                  direction,
-                                  alignment as CrossAxisAlignment,
-                                ),
+                                direction,
+                                alignment as CrossAxisAlignment,
+                              ),
                           fit: BoxFit.fitHeight,
                           color: color,
                         ),
@@ -234,13 +238,14 @@
             // if the axis is the main axis the type should be [MainAxisAlignment]
             // if the axis is the cross axis the type should be [CrossAxisAlignment]
             FlexLayoutProperties changedProperties;
-            changedProperties = axis == direction
-                ? propertiesLocal.copyWith(
-                    mainAxisAlignment: newSelection as MainAxisAlignment?,
-                  )
-                : propertiesLocal.copyWith(
-                    crossAxisAlignment: newSelection as CrossAxisAlignment?,
-                  );
+            changedProperties =
+                axis == direction
+                    ? propertiesLocal.copyWith(
+                      mainAxisAlignment: newSelection as MainAxisAlignment?,
+                    )
+                    : propertiesLocal.copyWith(
+                      crossAxisAlignment: newSelection as CrossAxisAlignment?,
+                    );
             final valueRef = propertiesLocal.node.valueRef;
             markAsDirty();
             await objectGroup!.invokeSetFlexProperties(
@@ -450,9 +455,7 @@
 
     final contents = Container(
       decoration: BoxDecoration(
-        border: Border.all(
-          color: theme.primaryColorLight,
-        ),
+        border: Border.all(color: theme.primaryColorLight),
       ),
       margin: const EdgeInsets.only(top: margin, left: margin),
       child: LayoutBuilder(
@@ -464,21 +467,23 @@
             return axis == Axis.horizontal ? maxWidth : maxHeight;
           }
 
-          final childrenAndMainAxisSpacesRenderProps =
-              widget.properties.childrenRenderProperties(
-            smallestRenderWidth: minRenderWidth,
-            largestRenderWidth: defaultMaxRenderWidth,
-            smallestRenderHeight: minRenderHeight,
-            largestRenderHeight: defaultMaxRenderHeight,
-            maxSizeAvailable: maxSizeAvailable,
-          );
+          final childrenAndMainAxisSpacesRenderProps = widget.properties
+              .childrenRenderProperties(
+                smallestRenderWidth: minRenderWidth,
+                largestRenderWidth: defaultMaxRenderWidth,
+                smallestRenderHeight: minRenderHeight,
+                largestRenderHeight: defaultMaxRenderHeight,
+                maxSizeAvailable: maxSizeAvailable,
+              );
 
-          final renderProperties = childrenAndMainAxisSpacesRenderProps
-              .where((renderProps) => !renderProps.isFreeSpace)
-              .toList();
-          final mainAxisSpaces = childrenAndMainAxisSpacesRenderProps
-              .where((renderProps) => renderProps.isFreeSpace)
-              .toList();
+          final renderProperties =
+              childrenAndMainAxisSpacesRenderProps
+                  .where((renderProps) => !renderProps.isFreeSpace)
+                  .toList();
+          final mainAxisSpaces =
+              childrenAndMainAxisSpacesRenderProps
+                  .where((renderProps) => renderProps.isFreeSpace)
+                  .toList();
           final crossAxisSpaces = widget.properties.crossAxisSpaces(
             childrenRenderProperties: renderProperties,
             maxSizeAvailable: maxSizeAvailable,
@@ -524,22 +529,27 @@
               scrollDirection: widget.properties.direction,
               controller: widget.scrollController,
               child: ConstrainedBox(
-                constraints: BoxConstraints(
-                  minWidth: maxWidth,
-                  minHeight: maxHeight,
-                  maxWidth: widget.direction == Axis.horizontal
-                      ? sum(
-                          childrenAndMainAxisSpacesRenderProps
-                              .map((renderSize) => renderSize.width),
-                        )
-                      : maxWidth,
-                  maxHeight: widget.direction == Axis.vertical
-                      ? sum(
-                          childrenAndMainAxisSpacesRenderProps
-                              .map((renderSize) => renderSize.height),
-                        )
-                      : maxHeight,
-                ).normalize(),
+                constraints:
+                    BoxConstraints(
+                      minWidth: maxWidth,
+                      minHeight: maxHeight,
+                      maxWidth:
+                          widget.direction == Axis.horizontal
+                              ? sum(
+                                childrenAndMainAxisSpacesRenderProps.map(
+                                  (renderSize) => renderSize.width,
+                                ),
+                              )
+                              : maxWidth,
+                      maxHeight:
+                          widget.direction == Axis.vertical
+                              ? sum(
+                                childrenAndMainAxisSpacesRenderProps.map(
+                                  (renderSize) => renderSize.height,
+                                ),
+                              )
+                              : maxHeight,
+                    ).normalize(),
                 child: Stack(
                   children: [
                     LayoutExplorerBackground(colorScheme: colorScheme),
@@ -597,10 +607,7 @@
 
   void onChangeFlexFit(FlexFit? newFlexFit) async {
     state.markAsDirty();
-    await objectGroup!.invokeSetFlexFit(
-      properties.node.valueRef,
-      newFlexFit!,
-    );
+    await objectGroup!.invokeSetFlexFit(properties.node.valueRef, newFlexFit!);
   }
 
   Widget _buildFlexFactorChangerDropdown(
@@ -612,9 +619,10 @@
     Widget buildMenuitemChild(int? flexFactor) {
       return Text(
         'flex: $flexFactor',
-        style: flexFactor == propertiesLocal.flexFactor
-            ? theme.boldTextStyle.copyWith(color: emphasizedTextColor)
-            : theme.regularTextStyleWithColor(emphasizedTextColor),
+        style:
+            flexFactor == propertiesLocal.flexFactor
+                ? theme.boldTextStyle.copyWith(color: emphasizedTextColor)
+                : theme.regularTextStyleWithColor(emphasizedTextColor),
       );
     }
 
@@ -639,9 +647,9 @@
 
   Widget _buildFlexFitChangerDropdown(ThemeData theme) {
     Widget flexFitDescription(FlexFit flexFit) => Text(
-          'fit: ${flexFit.name}',
-          style: theme.regularTextStyleWithColor(emphasizedTextColor),
-        );
+      'fit: ${flexFit.name}',
+      style: theme.regularTextStyleWithColor(emphasizedTextColor),
+    );
 
     final propertiesLocal = properties;
 
@@ -674,14 +682,13 @@
     // TODO(https://github.com/flutter/devtools/issues/4058) allow more dynamic
     // flex factor input
     final currentFlexFactor = properties.flexFactor?.toInt() ?? 0;
-    final currentMaxFlexFactor =
-        math.max(currentFlexFactor, maximumFlexFactorOptions);
+    final currentMaxFlexFactor = math.max(
+      currentFlexFactor,
+      maximumFlexFactorOptions,
+    );
 
     return Container(
-      margin: const EdgeInsets.only(
-        top: margin,
-        left: margin,
-      ),
+      margin: const EdgeInsets.only(top: margin, left: margin),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.end,
         children: [
@@ -719,15 +726,20 @@
       final horizontal = rootLocal.isMainAxisHorizontal;
 
       late Size size;
-      size = propertiesLocal.hasFlexFactor
-          ? SizeTween(
-              begin: Size(
-                horizontal ? minRenderWidth - entranceMargin : renderSize.width,
-                vertical ? minRenderHeight - entranceMargin : renderSize.height,
-              ),
-              end: renderSize,
-            ).evaluate(state.entranceCurve)!
-          : renderSize;
+      size =
+          propertiesLocal.hasFlexFactor
+              ? SizeTween(
+                begin: Size(
+                  horizontal
+                      ? minRenderWidth - entranceMargin
+                      : renderSize.width,
+                  vertical
+                      ? minRenderHeight - entranceMargin
+                      : renderSize.height,
+                ),
+                end: renderSize,
+              ).evaluate(state.entranceCurve)!
+              : renderSize;
       // Not-expanded widgets enter much faster.
       return Opacity(
         opacity: min([state.entranceCurve.value * 5, 1.0]),
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/utils.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/utils.dart
index 26792d0..15f318b 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/utils.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/flex/utils.dart
@@ -23,11 +23,7 @@
 class AnimatedFlexLayoutProperties
     extends AnimatedLayoutProperties<FlexLayoutProperties>
     implements FlexLayoutProperties {
-  AnimatedFlexLayoutProperties(
-    super.begin,
-    super.end,
-    super.animation,
-  );
+  AnimatedFlexLayoutProperties(super.begin, super.end, super.animation);
 
   @override
   CrossAxisAlignment? get crossAxisAlignment => end.crossAxisAlignment;
@@ -85,7 +81,8 @@
   }
 
   @override
-  double get crossAxisDimension => lerpDouble(
+  double get crossAxisDimension =>
+      lerpDouble(
         begin.crossAxisDimension,
         end.crossAxisDimension,
         animation.value,
@@ -119,7 +116,8 @@
   bool get isMainAxisVertical => end.isMainAxisVertical;
 
   @override
-  double get mainAxisDimension => lerpDouble(
+  double get mainAxisDimension =>
+      lerpDouble(
         begin.mainAxisDimension,
         end.mainAxisDimension,
         animation.value,
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/arrow.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/arrow.dart
index 766de8a..8f6c9d9 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/arrow.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/arrow.dart
@@ -11,16 +11,12 @@
 const defaultArrowStrokeWidth = 2.0;
 const defaultDistanceToArrow = 4.0;
 
-enum ArrowType {
-  up,
-  left,
-  right,
-  down,
-}
+enum ArrowType { up, left, right, down }
 
-Axis axis(ArrowType type) => (type == ArrowType.up || type == ArrowType.down)
-    ? Axis.vertical
-    : Axis.horizontal;
+Axis axis(ArrowType type) =>
+    (type == ArrowType.up || type == ArrowType.down)
+        ? Axis.vertical
+        : Axis.horizontal;
 
 /// Widget that draws a bidirectional arrow around another widget.
 ///
@@ -35,12 +31,12 @@
     double? arrowHeadSize,
     this.arrowStrokeWidth = defaultArrowStrokeWidth,
     this.childMarginFromArrow = defaultDistanceToArrow,
-  })  : assert(childMarginFromArrow > 0.0),
-        direction = axis(type),
-        isBidirectional = false,
-        startArrowType = type,
-        endArrowType = type,
-        arrowHeadSize = arrowHeadSize ?? defaultIconSize;
+  }) : assert(childMarginFromArrow > 0.0),
+       direction = axis(type),
+       isBidirectional = false,
+       startArrowType = type,
+       endArrowType = type,
+       arrowHeadSize = arrowHeadSize ?? defaultIconSize;
 
   const ArrowWrapper.bidirectional({
     super.key,
@@ -50,13 +46,13 @@
     required this.arrowHeadSize,
     this.arrowStrokeWidth = defaultArrowStrokeWidth,
     this.childMarginFromArrow = defaultDistanceToArrow,
-  })  : assert(arrowHeadSize >= 0.0),
-        assert(childMarginFromArrow >= 0.0),
-        isBidirectional = true,
-        startArrowType =
-            direction == Axis.horizontal ? ArrowType.left : ArrowType.up,
-        endArrowType =
-            direction == Axis.horizontal ? ArrowType.right : ArrowType.down;
+  }) : assert(arrowHeadSize >= 0.0),
+       assert(childMarginFromArrow >= 0.0),
+       isBidirectional = true,
+       startArrowType =
+           direction == Axis.horizontal ? ArrowType.left : ArrowType.up,
+       endArrowType =
+           direction == Axis.horizontal ? ArrowType.right : ArrowType.down;
 
   final Color arrowColor;
   final double arrowHeadSize;
@@ -96,10 +92,11 @@
               headSize: arrowHeadSize,
               strokeWidth: arrowStrokeWidth,
               type: startArrowType,
-              shouldDrawHead: isBidirectional
-                  ? true
-                  : (startArrowType == ArrowType.left ||
-                      startArrowType == ArrowType.up),
+              shouldDrawHead:
+                  isBidirectional
+                      ? true
+                      : (startArrowType == ArrowType.left ||
+                          startArrowType == ArrowType.up),
             ),
           ),
         ),
@@ -115,10 +112,11 @@
               headSize: arrowHeadSize,
               strokeWidth: arrowStrokeWidth,
               type: endArrowType,
-              shouldDrawHead: isBidirectional
-                  ? true
-                  : (endArrowType == ArrowType.right ||
-                      endArrowType == ArrowType.down),
+              shouldDrawHead:
+                  isBidirectional
+                      ? true
+                      : (endArrowType == ArrowType.right ||
+                          endArrowType == ArrowType.down),
             ),
           ),
         ),
@@ -137,15 +135,15 @@
     this.shouldDrawHead = true,
     this.strokeWidth = defaultArrowStrokeWidth,
     required this.type,
-  })  : assert(headSize > 0.0),
-        assert(strokeWidth > 0.0),
-        _painter = _ArrowPainter(
-          headSize: headSize,
-          color: color,
-          strokeWidth: strokeWidth,
-          type: type,
-          shouldDrawHead: shouldDrawHead,
-        );
+  }) : assert(headSize > 0.0),
+       assert(strokeWidth > 0.0),
+       _painter = _ArrowPainter(
+         headSize: headSize,
+         color: color,
+         strokeWidth: strokeWidth,
+         type: type,
+         shouldDrawHead: shouldDrawHead,
+       );
 
   final Color color;
 
@@ -162,10 +160,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return CustomPaint(
-      painter: _painter,
-      child: Container(),
-    );
+    return CustomPaint(painter: _painter, child: Container());
   }
 }
 
@@ -177,7 +172,7 @@
     this.shouldDrawHead = true,
     required this.type,
   }) : // the height of an equilateral triangle
-        headHeight = 0.5 * sqrt(3) * headSize;
+       headHeight = 0.5 * sqrt(3) * headSize;
 
   final Color color;
   final double headSize;
@@ -204,9 +199,10 @@
 
   @override
   void paint(Canvas canvas, Size size) {
-    final paint = Paint()
-      ..color = color
-      ..strokeWidth = strokeWidth;
+    final paint =
+        Paint()
+          ..color = color
+          ..strokeWidth = strokeWidth;
 
     final originX = size.width / 2, originY = size.height / 2;
     Offset lineStartingPoint = Offset.zero;
@@ -239,11 +235,12 @@
           p3 = Offset(originX + headSizeDividedBy2, startingY);
           break;
       }
-      final path = Path()
-        ..moveTo(p1.dx, p1.dy)
-        ..lineTo(p2.dx, p2.dy)
-        ..lineTo(p3.dx, p3.dy)
-        ..close();
+      final path =
+          Path()
+            ..moveTo(p1.dx, p1.dy)
+            ..lineTo(p2.dx, p2.dy)
+            ..lineTo(p3.dx, p3.dy)
+            ..close();
       canvas.drawPath(path, paint);
 
       switch (type) {
@@ -281,10 +278,6 @@
           break;
       }
     }
-    canvas.drawLine(
-      lineStartingPoint,
-      lineEndingPoint,
-      paint,
-    );
+    canvas.drawLine(lineStartingPoint, lineEndingPoint, paint);
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/dimension.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/dimension.dart
index dbf3e3f..c59af1e 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/dimension.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/dimension.dart
@@ -15,9 +15,10 @@
   final text = Text.rich(
     description,
     textAlign: TextAlign.center,
-    style: overflow
-        ? overflowingDimensionIndicatorTextStyle(colorScheme)
-        : dimensionIndicatorTextStyle,
+    style:
+        overflow
+            ? overflowingDimensionIndicatorTextStyle(colorScheme)
+            : dimensionIndicatorTextStyle,
     overflow: TextOverflow.ellipsis,
   );
   if (overflow) {
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/free_space.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/free_space.dart
index 25b9c20..81482e5 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/free_space.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/free_space.dart
@@ -12,10 +12,7 @@
 import 'theme.dart';
 
 class FreeSpaceVisualizerWidget extends StatelessWidget {
-  const FreeSpaceVisualizerWidget(
-    this.renderProperties, {
-    super.key,
-  });
+  const FreeSpaceVisualizerWidget(this.renderProperties, {super.key});
 
   final RenderProperties renderProperties;
 
@@ -32,9 +29,7 @@
       children: [
         Flexible(
           child: dimensionDescription(
-            TextSpan(
-              text: widthDescription,
-            ),
+            TextSpan(text: widthDescription),
             false,
             colorScheme,
           ),
@@ -108,9 +103,7 @@
       children: [
         Flexible(
           child: dimensionDescription(
-            TextSpan(
-              text: widthDescription,
-            ),
+            TextSpan(text: widthDescription),
             false,
             colorScheme,
           ),
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/layout_explorer_widget.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/layout_explorer_widget.dart
index 17cb72b..c799b40 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/layout_explorer_widget.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/layout_explorer_widget.dart
@@ -20,17 +20,17 @@
 
 /// Base class for layout widgets for all widget types.
 abstract class LayoutExplorerWidget extends StatefulWidget {
-  const LayoutExplorerWidget(
-    this.inspectorController, {
-    super.key,
-  });
+  const LayoutExplorerWidget(this.inspectorController, {super.key});
 
   final InspectorController inspectorController;
 }
 
 /// Base class for state objects for layout widgets for all widget types.
-abstract class LayoutExplorerWidgetState<W extends LayoutExplorerWidget,
-        L extends LayoutProperties> extends State<W>
+abstract class LayoutExplorerWidgetState<
+  W extends LayoutExplorerWidget,
+  L extends LayoutProperties
+>
+    extends State<W>
     with TickerProviderStateMixin
     implements InspectorServiceClient {
   LayoutExplorerWidgetState() {
@@ -128,8 +128,9 @@
   }
 
   void _unregisterInspectorControllerService() {
-    inspectorController.selectedNode
-        .removeListener(_onSelectionChangedCallback);
+    inspectorController.selectedNode.removeListener(
+      _onSelectionChangedCallback,
+    );
     inspectorService?.removeClient(this);
   }
 
@@ -231,9 +232,8 @@
   }
 
   void _initAnimationStates() {
-    entranceController = longAnimationController(
-      this,
-    )..addStatusListener((status) {
+    entranceController = longAnimationController(this)
+      ..addStatusListener((status) {
         if (status == AnimationStatus.dismissed) {
           setState(() {
             _previousProperties = null;
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/overflow_indicator_painter.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/overflow_indicator_painter.dart
index fdc8c40..654ae7d 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/overflow_indicator_painter.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/overflow_indicator_painter.dart
@@ -34,14 +34,15 @@
   /// since all of them are private.
   static const black = Color(0xBF000000);
   static const yellow = Color(0xBFFFFF00);
-  static final indicatorPaint = Paint()
-    ..shader = ui.Gradient.linear(
-      const Offset(0.0, 0.0),
-      const Offset(10.0, 10.0),
-      <Color>[black, yellow, yellow, black],
-      <double>[0.25, 0.25, 0.75, 0.75],
-      TileMode.repeated,
-    );
+  static final indicatorPaint =
+      Paint()
+        ..shader = ui.Gradient.linear(
+          const Offset(0.0, 0.0),
+          const Offset(10.0, 10.0),
+          <Color>[black, yellow, yellow, black],
+          <double>[0.25, 0.25, 0.75, 0.75],
+          TileMode.repeated,
+        );
 
   @override
   void paint(Canvas canvas, Size size) {
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/theme.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/theme.dart
index 24ab3f5..76f44d1 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/theme.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/theme.dart
@@ -133,12 +133,7 @@
   return Container(
     height: 1.0,
     decoration: const BoxDecoration(
-      border: Border(
-        bottom: BorderSide(
-          color: textColor,
-          width: 0.0,
-        ),
-      ),
+      border: Border(bottom: BorderSide(color: textColor, width: 0.0)),
     ),
   );
 }
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/utils.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/utils.dart
index f151ce7..4be0064 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/utils.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/utils.dart
@@ -33,12 +33,12 @@
     this.bottomHeight,
     this.center,
   }) : assert(
-          left != null ||
-              top != null ||
-              right != null ||
-              bottom != null ||
-              center != null,
-        );
+         left != null ||
+             top != null ||
+             right != null ||
+             bottom != null ||
+             center != null,
+       );
 
   final Widget? center;
   final Widget? top;
@@ -161,17 +161,19 @@
                 width:
                     isSelected ? _borderSelectedWidth : _borderUnselectedWidth,
               ),
-              color: isSelected
-                  ? theme.canvasColor.brighten()
-                  : theme.canvasColor.darken(),
-              boxShadow: isSelected
-                  ? [
-                      BoxShadow(
-                        color: Colors.black.withAlpha(255 ~/ 2),
-                        blurRadius: 10,
-                      ),
-                    ]
-                  : null,
+              color:
+                  isSelected
+                      ? theme.canvasColor.brighten()
+                      : theme.canvasColor.darken(),
+              boxShadow:
+                  isSelected
+                      ? [
+                        BoxShadow(
+                          color: Colors.black.withAlpha(255 ~/ 2),
+                          blurRadius: 10,
+                        ),
+                      ]
+                      : null,
             ),
             child: Stack(
               children: [
@@ -186,26 +188,29 @@
                   ),
                 Container(
                   margin: EdgeInsets.only(
-                    right: overflowSide == OverflowSide.right
-                        ? _overflowIndicatorSize
-                        : 0.0,
-                    bottom: overflowSide == OverflowSide.bottom
-                        ? _overflowIndicatorSize
-                        : 0.0,
+                    right:
+                        overflowSide == OverflowSide.right
+                            ? _overflowIndicatorSize
+                            : 0.0,
+                    bottom:
+                        overflowSide == OverflowSide.bottom
+                            ? _overflowIndicatorSize
+                            : 0.0,
                   ),
-                  child: isFlex
-                      ? FlexWidgetVisualizer(
-                          title: title,
-                          largeTitle: largeTitle,
-                          borderColor: borderColor,
-                          hint: hint,
-                          child: child,
-                        )
-                      : BoxWidgetVisualizer(
-                          borderColor: borderColor,
-                          title: title,
-                          properties: properties,
-                        ),
+                  child:
+                      isFlex
+                          ? FlexWidgetVisualizer(
+                            title: title,
+                            largeTitle: largeTitle,
+                            borderColor: borderColor,
+                            hint: hint,
+                            child: child,
+                          )
+                          : BoxWidgetVisualizer(
+                            borderColor: borderColor,
+                            title: title,
+                            properties: properties,
+                          ),
                 ),
               ],
             ),
@@ -251,9 +256,10 @@
               Flexible(
                 child: Container(
                   constraints: BoxConstraints(
-                    maxWidth: largeTitle
-                        ? defaultMaxRenderWidth
-                        : minRenderWidth * widgetTitleMaxWidthPercentage,
+                    maxWidth:
+                        largeTitle
+                            ? defaultMaxRenderWidth
+                            : minRenderWidth * widgetTitleMaxWidthPercentage,
                   ),
                   decoration: BoxDecoration(color: borderColor),
                   padding: const EdgeInsets.all(densePadding),
@@ -298,12 +304,7 @@
     return Column(
       mainAxisSize: MainAxisSize.min,
       children: [
-        Center(
-          child: WidgetLabel(
-            labelColor: borderColor,
-            labelText: title,
-          ),
-        ),
+        Center(child: WidgetLabel(labelColor: borderColor, labelText: title)),
         Expanded(
           child: Column(
             mainAxisAlignment: MainAxisAlignment.center,
@@ -354,9 +355,7 @@
       ),
       child: Text(
         labelText,
-        style: theme.regularTextStyleWithColor(
-          colorScheme.widgetNameColor,
-        ),
+        style: theme.regularTextStyleWithColor(colorScheme.widgetNameColor),
         overflow: TextOverflow.ellipsis,
       ),
     );
@@ -366,15 +365,15 @@
 class AnimatedLayoutProperties<T extends LayoutProperties>
     implements LayoutProperties {
   AnimatedLayoutProperties(this.begin, this.end, this.animation)
-      : assert(begin.children.length == end.children.length),
-        _children = [
-          for (var i = 0; i < begin.children.length; i++)
-            AnimatedLayoutProperties(
-              begin.children[i],
-              end.children[i],
-              animation,
-            ),
-        ];
+    : assert(begin.children.length == end.children.length),
+      _children = [
+        for (var i = 0; i < begin.children.length; i++)
+          AnimatedLayoutProperties(
+            begin.children[i],
+            end.children[i],
+            animation,
+          ),
+      ];
 
   final T begin;
   final T end;
@@ -446,10 +445,10 @@
     final constraintsLocal = constraints!;
     return constraintsLocal.hasBoundedWidth
         ? LayoutProperties.describeAxis(
-            constraintsLocal.minWidth,
-            constraintsLocal.maxWidth,
-            'w',
-          )
+          constraintsLocal.minWidth,
+          constraintsLocal.maxWidth,
+          'w',
+        )
         : 'w=unconstrained';
   }
 
@@ -458,10 +457,10 @@
     final constraintsLocal = constraints!;
     return constraintsLocal.hasBoundedHeight
         ? LayoutProperties.describeAxis(
-            constraintsLocal.minHeight,
-            constraintsLocal.maxHeight,
-            'h',
-          )
+          constraintsLocal.minHeight,
+          constraintsLocal.maxHeight,
+          'h',
+        )
         : 'h=unconstrained';
   }
 
@@ -549,10 +548,7 @@
 }
 
 class LayoutExplorerBackground extends StatelessWidget {
-  const LayoutExplorerBackground({
-    super.key,
-    required this.colorScheme,
-  });
+  const LayoutExplorerBackground({super.key, required this.colorScheme});
 
   final ColorScheme colorScheme;
 
@@ -598,15 +594,17 @@
   Widget build(BuildContext context) {
     return Column(
       // Push to the bottom if there is no padding on the top.
-      mainAxisAlignment: !hasTopPadding && hasBottomPadding
-          ? MainAxisAlignment.end
-          : MainAxisAlignment.start,
+      mainAxisAlignment:
+          !hasTopPadding && hasBottomPadding
+              ? MainAxisAlignment.end
+              : MainAxisAlignment.start,
       children: [
         Row(
           // Push to the right if there is no padding on the left.
-          mainAxisAlignment: (!hasLeftPadding && hasRightPadding)
-              ? MainAxisAlignment.end
-              : MainAxisAlignment.start,
+          mainAxisAlignment:
+              (!hasLeftPadding && hasRightPadding)
+                  ? MainAxisAlignment.end
+                  : MainAxisAlignment.start,
           children: [
             Flexible(
               child: WidgetLabel(
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widget_constraints.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widget_constraints.dart
index f3dc52d..593f9fc 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widget_constraints.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widget_constraints.dart
@@ -30,7 +30,8 @@
   @override
   Widget build(BuildContext context) {
     final propertiesLocal = properties;
-    final showChildrenWidthsSum = propertiesLocal is FlexLayoutProperties &&
+    final showChildrenWidthsSum =
+        propertiesLocal is FlexLayoutProperties &&
         propertiesLocal.isOverflowWidth;
     final bottomHeight = widthAndConstraintIndicatorSize;
     final rightWidth = heightAndConstraintIndicatorSize;
@@ -43,24 +44,22 @@
       child: dimensionDescription(
         TextSpan(
           children: [
-            TextSpan(
-              text: propertiesLocal.describeHeight(),
-            ),
+            TextSpan(text: propertiesLocal.describeHeight()),
             if (propertiesLocal.constraints != null) ...[
               if (!showOverflowHeight) const TextSpan(text: '\n'),
               TextSpan(
                 text: ' (${propertiesLocal.describeHeightConstraints()})',
-                style: propertiesLocal.constraints!.hasBoundedHeight ||
-                        !warnIfUnconstrained
-                    ? null
-                    : TextStyle(
-                        color: colorScheme.unconstrainedColor,
-                      ),
+                style:
+                    propertiesLocal.constraints!.hasBoundedHeight ||
+                            !warnIfUnconstrained
+                        ? null
+                        : TextStyle(color: colorScheme.unconstrainedColor),
               ),
             ],
             if (showOverflowHeight)
               TextSpan(
-                text: '\nchildren take: '
+                text:
+                    '\nchildren take: '
                     '${toStringAsFixed(sum(propertiesLocal.childrenHeights))}',
               ),
           ],
@@ -95,10 +94,7 @@
                   ),
                 ),
               ),
-              if (displayHeightOutsideArrow)
-                Flexible(
-                  child: heightDescription,
-                ),
+              if (displayHeightOutsideArrow) Flexible(child: heightDescription),
             ],
           );
         },
@@ -113,15 +109,17 @@
             if (!showChildrenWidthsSum) const TextSpan(text: '\n'),
             TextSpan(
               text: '(${propertiesLocal.describeWidthConstraints()})',
-              style: propertiesLocal.constraints!.hasBoundedWidth ||
-                      !warnIfUnconstrained
-                  ? null
-                  : TextStyle(color: colorScheme.unconstrainedColor),
+              style:
+                  propertiesLocal.constraints!.hasBoundedWidth ||
+                          !warnIfUnconstrained
+                      ? null
+                      : TextStyle(color: colorScheme.unconstrainedColor),
             ),
           ],
           if (showChildrenWidthsSum)
             TextSpan(
-              text: '\nchildren take '
+              text:
+                  '\nchildren take '
                   '${toStringAsFixed(sum(propertiesLocal.childrenWidths))}',
             ),
         ],
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widgets_theme.dart b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widgets_theme.dart
index 4f56040..1b62054 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widgets_theme.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/layout_explorer/ui/widgets_theme.dart
@@ -5,10 +5,7 @@
 import 'package:flutter/material.dart';
 
 class WidgetTheme {
-  const WidgetTheme({
-    this.iconAsset,
-    this.color = otherWidgetColor,
-  });
+  const WidgetTheme({this.iconAsset, this.color = otherWidgetColor});
 
   final String? iconAsset;
   final Color color;
@@ -175,15 +172,17 @@
     'Tab': tabTheme,
     'TabBar': tabTheme,
     'TabBarView': tabTheme,
-    'BottomNavigationBar':
-        WidgetTheme(iconAsset: WidgetIcons.bottomNavigationBar),
+    'BottomNavigationBar': WidgetTheme(
+      iconAsset: WidgetIcons.bottomNavigationBar,
+    ),
     'CupertinoTabScaffold': tabTheme,
     'CupertinoTabView': tabTheme,
 
     // Other
     'Scaffold': WidgetTheme(iconAsset: WidgetIcons.scaffold),
-    'CircularProgressIndicator':
-        WidgetTheme(iconAsset: WidgetIcons.circularProgress),
+    'CircularProgressIndicator': WidgetTheme(
+      iconAsset: WidgetIcons.circularProgress,
+    ),
     'Card': WidgetTheme(iconAsset: WidgetIcons.card),
     'Divider': WidgetTheme(iconAsset: WidgetIcons.divider),
     'AlertDialog': WidgetTheme(iconAsset: WidgetIcons.alertDialog),
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/widget_details.dart b/packages/devtools_app/lib/src/screens/inspector_v2/widget_details.dart
index 899ffe3..966785d 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/widget_details.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/widget_details.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/widgets.dart';
 
@@ -35,22 +36,21 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder<InspectorTreeNode?>(
       valueListenable: controller.selectedNode,
-      builder: (context, _, __) {
+      builder: (context, _, _) {
         final node = selectedNode;
         if (node == null) {
-          return const Center(
-            child: Text(
-              'Select a widget to view its layout and properties.',
-              textAlign: TextAlign.center,
-              overflow: TextOverflow.clip,
+          return const RoundedOutlinedBorder(
+            child: Center(
+              child: Text(
+                'Select a widget to view its layout and properties.',
+                textAlign: TextAlign.center,
+                overflow: TextOverflow.clip,
+              ),
             ),
           );
         }
 
-        return DetailsTable(
-          controller: controller,
-          node: node,
-        );
+        return DetailsTable(controller: controller, node: node);
       },
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/widget_properties/properties_view.dart b/packages/devtools_app/lib/src/screens/inspector_v2/widget_properties/properties_view.dart
index d1e9282..501a5eb 100644
--- a/packages/devtools_app/lib/src/screens/inspector_v2/widget_properties/properties_view.dart
+++ b/packages/devtools_app/lib/src/screens/inspector_v2/widget_properties/properties_view.dart
@@ -163,12 +163,11 @@
   List<DevToolsTab> _getTabsInOrder({
     required bool renderTabExists,
     required bool flexExplorerTabExists,
-  }) =>
-      [
-        _widgetPropertiesTab,
-        if (renderTabExists) _renderObjectTab,
-        if (flexExplorerTabExists) _flexExplorerTab,
-      ];
+  }) => [
+    _widgetPropertiesTab,
+    if (renderTabExists) _renderObjectTab,
+    if (flexExplorerTabExists) _flexExplorerTab,
+  ];
 }
 
 /// Displays a widget's properties, including the layout properties and a
@@ -243,7 +242,8 @@
 
     return LayoutBuilder(
       builder: (context, constraints) {
-        final horizontalLayout = constraints.maxWidth >
+        final horizontalLayout =
+            constraints.maxWidth >
             (PropertiesView.layoutExplorerWidth *
                 PropertiesView.scaleFactorForVerticalLayout);
 
@@ -275,9 +275,10 @@
                       ),
                       if (layoutPropertiesList != null)
                         Padding(
-                          padding: horizontalLayout
-                              ? const EdgeInsets.only(left: largeSpacing)
-                              : const EdgeInsets.only(bottom: largeSpacing),
+                          padding:
+                              horizontalLayout
+                                  ? const EdgeInsets.only(left: largeSpacing)
+                                  : const EdgeInsets.only(bottom: largeSpacing),
                           child: layoutPropertiesList,
                         ),
                     ],
@@ -345,9 +346,9 @@
   LayoutWidthsAndHeights? get widthsAndHeights =>
       widgetHeights != null && widgetWidths != null
           ? LayoutWidthsAndHeights(
-              widths: widgetWidths!,
-              heights: widgetHeights!,
-            )
+            widths: widgetWidths!,
+            heights: widgetHeights!,
+          )
           : null;
 
   @override
@@ -370,34 +371,16 @@
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
-        LayoutPropertyItem(
-          name: 'height',
-          value: widgetHeight,
-        ),
-        LayoutPropertyItem(
-          name: 'width',
-          value: widgetWidth,
-        ),
+        LayoutPropertyItem(name: 'height', value: widgetHeight),
+        LayoutPropertyItem(name: 'width', value: widgetWidth),
         if (hasTopPadding)
-          LayoutPropertyItem(
-            name: 'top padding',
-            value: topPadding,
-          ),
+          LayoutPropertyItem(name: 'top padding', value: topPadding),
         if (hasBottomPadding)
-          LayoutPropertyItem(
-            name: 'bottom padding',
-            value: bottomPadding,
-          ),
+          LayoutPropertyItem(name: 'bottom padding', value: bottomPadding),
         if (hasLeftPadding)
-          LayoutPropertyItem(
-            name: 'left padding',
-            value: leftPadding,
-          ),
+          LayoutPropertyItem(name: 'left padding', value: leftPadding),
         if (hasRightPadding)
-          LayoutPropertyItem(
-            name: 'right padding',
-            value: rightPadding,
-          ),
+          LayoutPropertyItem(name: 'right padding', value: rightPadding),
       ],
     );
   }
@@ -424,10 +407,7 @@
           text: '$name: ',
           style: theme.subtleTextStyle,
           children: [
-            TextSpan(
-              text: toStringAsFixed(value),
-              style: theme.fixedFontStyle,
-            ),
+            TextSpan(text: toStringAsFixed(value), style: theme.fixedFontStyle),
           ],
         ),
       ),
@@ -455,10 +435,7 @@
         controller: scrollController,
         itemCount: properties.length,
         itemBuilder: (context, index) {
-          return PropertyItem(
-            index: index,
-            properties: properties,
-          );
+          return PropertyItem(index: index, properties: properties);
         },
       ),
     );
@@ -519,10 +496,7 @@
 
 /// A widget property's name.
 class PropertyName extends StatelessWidget {
-  const PropertyName({
-    super.key,
-    required this.property,
-  });
+  const PropertyName({super.key, required this.property});
 
   final RemoteDiagnosticsNode property;
 
@@ -539,10 +513,7 @@
 
 /// A widget property's value.
 class PropertyValue extends StatelessWidget {
-  const PropertyValue({
-    super.key,
-    required this.property,
-  });
+  const PropertyValue({super.key, required this.property});
 
   final RemoteDiagnosticsNode property;
 
@@ -561,10 +532,7 @@
 
 /// Wraps a text widget with the correct amount of padding for the table.
 class PaddedText extends StatelessWidget {
-  const PaddedText({
-    super.key,
-    required this.child,
-  });
+  const PaddedText({super.key, required this.child});
 
   final Widget child;
 
diff --git a/packages/devtools_app/lib/src/screens/logging/_kind_column.dart b/packages/devtools_app/lib/src/screens/logging/_kind_column.dart
deleted file mode 100644
index aa832ca..0000000
--- a/packages/devtools_app/lib/src/screens/logging/_kind_column.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app_shared/service.dart' show FlutterEvent;
-import 'package:devtools_app_shared/ui.dart';
-import 'package:flutter/material.dart';
-
-import '../../shared/primitives/utils.dart';
-import '../../shared/table/table.dart';
-import '../../shared/table/table_data.dart';
-import 'logging_controller.dart';
-
-class KindColumn extends ColumnData<LogData>
-    implements ColumnRenderer<LogData> {
-  KindColumn()
-      : super(
-          'Kind',
-          fixedWidthPx: scaleByFontFactor(155),
-        );
-
-  @override
-  bool get supportsSorting => false;
-
-  @override
-  String getValue(LogData dataObject) => dataObject.kind;
-
-  @override
-  Widget build(
-    BuildContext context,
-    LogData item, {
-    bool isRowSelected = false,
-    bool isRowHovered = false,
-    VoidCallback? onPressed,
-  }) {
-    final kind = item.kind;
-
-    Color color = const Color.fromARGB(0xff, 0x61, 0x61, 0x61);
-
-    if (kind == 'stderr' ||
-        item.isError ||
-        kind.caseInsensitiveEquals(FlutterEvent.error)) {
-      color = const Color.fromARGB(0xff, 0xF4, 0x43, 0x36);
-    } else if (kind == 'stdout') {
-      color = const Color.fromARGB(0xff, 0x78, 0x90, 0x9C);
-    } else if (kind.startsWith('flutter')) {
-      color = const Color.fromARGB(0xff, 0x00, 0x91, 0xea);
-    } else if (kind == 'gc') {
-      color = const Color.fromARGB(0xff, 0x42, 0x42, 0x42);
-    }
-
-    // Use a font color that contrasts with the colored backgrounds.
-    final textStyle = Theme.of(context).regularTextStyleWithColor(Colors.white);
-
-    return Container(
-      padding: const EdgeInsets.symmetric(horizontal: 3.0),
-      decoration: BoxDecoration(
-        color: color,
-        borderRadius: BorderRadius.circular(3.0),
-      ),
-      child: Text(
-        kind,
-        overflow: TextOverflow.ellipsis,
-        style: textStyle,
-      ),
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/_log_details.dart b/packages/devtools_app/lib/src/screens/logging/_log_details.dart
index b803a8f..f265651 100644
--- a/packages/devtools_app/lib/src/screens/logging/_log_details.dart
+++ b/packages/devtools_app/lib/src/screens/logging/_log_details.dart
@@ -5,10 +5,12 @@
 import 'dart:async';
 
 import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/common_widgets.dart';
-import '../../shared/console/console.dart';
+import '../../shared/globals.dart';
+import '../../shared/preferences/preferences.dart';
+import '../../shared/ui/common_widgets.dart';
 import 'logging_controller.dart';
 
 class LogDetails extends StatefulWidget {
@@ -19,12 +21,13 @@
   @override
   State<LogDetails> createState() => _LogDetailsState();
 
-  static const copyToClipboardButtonKey =
-      Key('log_details_copy_to_clipboard_button');
+  static const copyToClipboardButtonKey = Key(
+    'log_details_copy_to_clipboard_button',
+  );
 }
 
 class _LogDetailsState extends State<LogDetails>
-    with SingleTickerProviderStateMixin {
+    with AutoDisposeMixin<LogDetails>, SingleTickerProviderStateMixin {
   String? _lastDetails;
   late final ScrollController scrollController;
 
@@ -32,6 +35,7 @@
   void initState() {
     super.initState();
     scrollController = ScrollController();
+    addAutoDisposeListener(preferences.logging.detailsFormat);
     unawaited(_computeLogDetails());
   }
 
@@ -73,21 +77,27 @@
       _lastDetails = details;
     }
 
-    return RoundedOutlinedBorder(
-      clip: true,
-      child: ConsoleFrame(
-        title: _LogDetailsHeader(log: log),
-        child: Padding(
-          padding: const EdgeInsets.all(denseSpacing),
-          child: Scrollbar(
-            child: SingleChildScrollView(
-              controller: scrollController,
-              child: SelectableText(
-                log?.prettyPrinted() ?? '',
-                textAlign: TextAlign.left,
-              ),
-            ),
-          ),
+    return DevToolsAreaPane(
+      header: _LogDetailsHeader(
+        log: log,
+        format: preferences.logging.detailsFormat.value,
+      ),
+      child: Scrollbar(
+        controller: scrollController,
+        child: SingleChildScrollView(
+          controller: scrollController,
+          child:
+              preferences.logging.detailsFormat.value ==
+                          LoggingDetailsFormat.text ||
+                      (log?.encodedDetails ?? '').isEmpty
+                  ? Padding(
+                    padding: const EdgeInsets.all(denseSpacing),
+                    child: SelectableText(
+                      log?.prettyPrinted() ?? '',
+                      textAlign: TextAlign.left,
+                    ),
+                  )
+                  : JsonViewer(encodedJson: log!.encodedDetails),
         ),
       ),
     );
@@ -95,9 +105,10 @@
 }
 
 class _LogDetailsHeader extends StatelessWidget {
-  const _LogDetailsHeader({required this.log});
+  const _LogDetailsHeader({required this.log, required this.format});
 
   final LogData? log;
+  final LoggingDetailsFormat format;
 
   @override
   Widget build(BuildContext context) {
@@ -110,6 +121,8 @@
       includeTopBorder: false,
       roundedTopBorder: false,
       actions: [
+        LogDetailsFormatButton(format: format),
+        const SizedBox(width: densePadding),
         CopyToClipboardControl(
           dataProvider: dataProvider,
           buttonKey: LogDetails.copyToClipboardButtonKey,
@@ -118,3 +131,39 @@
     );
   }
 }
+
+@visibleForTesting
+class LogDetailsFormatButton extends StatelessWidget {
+  const LogDetailsFormatButton({super.key, required this.format});
+
+  final LoggingDetailsFormat format;
+
+  static const viewAsJsonTooltip = 'View as JSON';
+  static const viewAsRawTextTooltip = 'View as raw text';
+
+  @override
+  Widget build(BuildContext context) {
+    final currentlyUsingTextFormat = format == LoggingDetailsFormat.text;
+    final tooltip =
+        currentlyUsingTextFormat ? viewAsJsonTooltip : viewAsRawTextTooltip;
+    void togglePreference() =>
+        preferences.logging.detailsFormat.value = format.opposite();
+
+    return currentlyUsingTextFormat
+        ? Padding(
+          // This padding aligns this button with the copy button.
+          padding: const EdgeInsets.only(bottom: borderPadding),
+          child: SmallAction(
+            tooltip: tooltip,
+            onPressed: togglePreference,
+            child: Text(' { } ', style: Theme.of(context).regularTextStyle),
+          ),
+        )
+        : ToolbarAction(
+          icon: Icons.text_fields,
+          tooltip: tooltip,
+          onPressed: togglePreference,
+          size: defaultIconSize,
+        );
+  }
+}
diff --git a/packages/devtools_app/lib/src/screens/logging/_logs_table.dart b/packages/devtools_app/lib/src/screens/logging/_logs_table.dart
index 76a43e2..de9ddc3 100644
--- a/packages/devtools_app/lib/src/screens/logging/_logs_table.dart
+++ b/packages/devtools_app/lib/src/screens/logging/_logs_table.dart
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
 import '../../shared/primitives/utils.dart';
 import '../../shared/table/table.dart';
-import '_kind_column.dart';
 import '_message_column.dart';
 import '_when_column.dart';
 import 'logging_controller.dart';
@@ -15,37 +15,40 @@
 class LogsTable extends StatelessWidget {
   const LogsTable({
     super.key,
+    required this.controller,
     required this.data,
     required this.selectionNotifier,
     required this.searchMatchesNotifier,
     required this.activeSearchMatchNotifier,
   });
 
+  static final _logRowHeight = scaleByFontFactor(45.0);
+
+  final LoggingController controller;
   final List<LogData> data;
   final ValueNotifier<LogData?> selectionNotifier;
   final ValueListenable<List<LogData>> searchMatchesNotifier;
   final ValueListenable<LogData?> activeSearchMatchNotifier;
 
-  static final when = WhenColumn();
-  static final kind = KindColumn();
-  static final message = MessageColumn();
-  static final columns = [when, kind, message];
+  static final whenColumn = WhenColumn();
+  static final messageColumn = MessageColumn();
+  static final columns = [whenColumn, messageColumn];
 
   @override
   Widget build(BuildContext context) {
-    // TODO(kenz): use SearchableFlatTable instead.
-    return FlatTable<LogData>(
+    return SearchableFlatTable<LogData>(
+      searchController: controller,
       keyFactory: (LogData data) => ValueKey<LogData>(data),
       data: data,
       dataKey: 'logs',
       autoScrollContent: true,
-      searchMatchesNotifier: searchMatchesNotifier,
-      activeSearchMatchNotifier: activeSearchMatchNotifier,
+      startScrolledAtBottom: true,
       columns: columns,
       selectionNotifier: selectionNotifier,
-      defaultSortColumn: when,
+      defaultSortColumn: whenColumn,
       defaultSortDirection: SortDirection.ascending,
-      secondarySortColumn: message,
+      secondarySortColumn: messageColumn,
+      rowHeight: _logRowHeight,
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/logging/_message_column.dart b/packages/devtools_app/lib/src/screens/logging/_message_column.dart
index c6716b5..0c12b9e 100644
--- a/packages/devtools_app/lib/src/screens/logging/_message_column.dart
+++ b/packages/devtools_app/lib/src/screens/logging/_message_column.dart
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:convert';
-
-import 'package:devtools_app_shared/service.dart' show FlutterEvent;
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
@@ -12,11 +9,11 @@
 import '../../shared/table/table.dart';
 import '../../shared/table/table_data.dart';
 import 'logging_controller.dart';
+import 'metadata.dart';
 
-@visibleForTesting
 class MessageColumn extends ColumnData<LogData>
     implements ColumnRenderer<LogData> {
-  MessageColumn() : super.wide('Message');
+  MessageColumn() : super.wide('Log');
 
   @override
   bool get supportsSorting => false;
@@ -53,46 +50,58 @@
     bool isRowHovered = false,
     VoidCallback? onPressed,
   }) {
-    if (data.kind.caseInsensitiveEquals(FlutterEvent.frame)) {
-      const color = Color.fromARGB(0xff, 0x00, 0x91, 0xea);
-      final text = Text(
-        getDisplayValue(data),
-        overflow: TextOverflow.ellipsis,
-      );
+    final theme = Theme.of(context);
+    final hasSummary = !data.summary.isNullOrEmpty;
+    // This needs to be a function because the details may be computed after the
+    // initial build.
+    bool hasDetails() => !data.details.isNullOrEmpty;
 
-      double frameLength = 0.0;
-      try {
-        final int micros = (jsonDecode(data.details!) as Map)['elapsed'];
-        frameLength = micros * 3.0 / 1000.0;
-      } catch (e) {
-        // ignore
-      }
-
-      return Row(
-        children: <Widget>[
-          text,
-          Flexible(
-            child: Container(
-              height: 12.0,
-              width: frameLength,
-              decoration: const BoxDecoration(color: color),
+    return ValueListenableBuilder<bool>(
+      valueListenable: data.detailsComputed,
+      builder: (context, detailsComputed, _) {
+        return Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Padding(
+              padding: const EdgeInsets.symmetric(vertical: borderPadding),
+              child: RichText(
+                text: TextSpan(
+                  children: [
+                    if (hasSummary)
+                      ...processAnsiTerminalCodes(
+                        // TODO(helin24): Recompute summary length considering ansi codes.
+                        //  The current summary is generally the first 200 chars of details.
+                        data.summary!,
+                        theme.regularTextStyle,
+                      ),
+                    if (hasSummary && hasDetails())
+                      TextSpan(text: '  •  ', style: theme.subtleTextStyle),
+                    if (hasDetails())
+                      ...processAnsiTerminalCodes(
+                        detailsComputed ? data.details! : '<fetching>',
+                        theme.subtleTextStyle,
+                      ),
+                  ],
+                ),
+                overflow: TextOverflow.ellipsis,
+                maxLines: 1,
+              ),
             ),
-          ),
-        ],
-      );
-    } else {
-      return RichText(
-        text: TextSpan(
-          children: processAnsiTerminalCodes(
-            // TODO(helin24): Recompute summary length considering ansi codes.
-            //  The current summary is generally the first 200 chars of details.
-            getDisplayValue(data),
-            Theme.of(context).regularTextStyle,
-          ),
-        ),
-        overflow: TextOverflow.ellipsis,
-        maxLines: 1,
-      );
-    }
+            Padding(
+              padding: const EdgeInsets.symmetric(vertical: densePadding),
+              child: LayoutBuilder(
+                builder: (context, constraints) {
+                  return MetadataChips(
+                    data: data,
+                    maxWidth: constraints.maxWidth,
+                  );
+                },
+              ),
+            ),
+          ],
+        );
+      },
+    );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/logging/_when_column.dart b/packages/devtools_app/lib/src/screens/logging/_when_column.dart
index 0a46614..c9a872b 100644
--- a/packages/devtools_app/lib/src/screens/logging/_when_column.dart
+++ b/packages/devtools_app/lib/src/screens/logging/_when_column.dart
@@ -8,18 +8,30 @@
 import 'logging_controller.dart';
 
 class WhenColumn extends ColumnData<LogData> {
-  WhenColumn()
-      : super(
-          'When',
-          fixedWidthPx: scaleByFontFactor(100),
-        );
+  WhenColumn() : super('When', fixedWidthPx: scaleByFontFactor(80));
 
   @override
   bool get supportsSorting => false;
 
   @override
-  String getValue(LogData dataObject) => dataObject.timestamp == null
-      ? ''
-      : timeFormat
-          .format(DateTime.fromMillisecondsSinceEpoch(dataObject.timestamp!));
+  bool get numeric => true;
+
+  @override
+  int getValue(LogData dataObject) => dataObject.timestamp ?? -1;
+
+  @override
+  String getDisplayValue(LogData dataObject) =>
+      dataObject.timestamp == null
+          ? ''
+          : timeFormat.format(
+            DateTime.fromMillisecondsSinceEpoch(dataObject.timestamp!),
+          );
+
+  @override
+  String getTooltip(LogData dataObject) =>
+      dataObject.timestamp == null
+          ? ''
+          : dateTimeFormat.format(
+            DateTime.fromMillisecondsSinceEpoch(dataObject.timestamp!),
+          );
 }
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_controller.dart b/packages/devtools_app/lib/src/screens/logging/logging_controller.dart
index d2564f3..77b01fe 100644
--- a/packages/devtools_app/lib/src/screens/logging/logging_controller.dart
+++ b/packages/devtools_app/lib/src/screens/logging/logging_controller.dart
@@ -18,6 +18,7 @@
 import '../../service/vm_service_wrapper.dart';
 import '../../shared/diagnostics/diagnostics_node.dart';
 import '../../shared/diagnostics/inspector_service.dart';
+import '../../shared/framework/app_error_handling.dart' as error_handling;
 import '../../shared/globals.dart';
 import '../../shared/primitives/byte_utils.dart';
 import '../../shared/primitives/message_bus.dart';
@@ -26,25 +27,23 @@
 import '../../shared/ui/search.dart';
 import '../inspector/inspector_tree_controller.dart';
 import 'logging_screen.dart';
+import 'metadata.dart';
 
 final _log = Logger('logging_controller');
 
-// For performance reasons, we drop old logs in batches, so the log will grow
-// to kMaxLogItemsUpperBound then truncate to kMaxLogItemsLowerBound.
-const kMaxLogItemsLowerBound = 5000;
-const kMaxLogItemsUpperBound = 5500;
+const defaultLogBufferReductionSize = 500;
 final timeFormat = DateFormat('HH:mm:ss.SSS');
+final dateTimeFormat = DateFormat('HH:mm:ss.SSS (MM/dd/yy)');
 
 bool _verboseDebugging = false;
 
-typedef OnShowDetails = void Function({
-  String? text,
-  InspectorTreeController? tree,
-});
+typedef OnShowDetails =
+    void Function({String? text, InspectorTreeController? tree});
 
-typedef CreateLoggingTree = InspectorTreeController Function({
-  VoidCallback? onSelectionChange,
-});
+typedef CreateLoggingTree =
+    InspectorTreeController Function({VoidCallback? onSelectionChange});
+
+typedef ZoneDescription = ({String? name, int? identityHashCode});
 
 Future<String> _retrieveFullStringValue(
   VmServiceWrapper? service,
@@ -94,14 +93,10 @@
         _handleConnectionStart(serviceConnection.serviceManager.service!);
 
         autoDisposeStreamSubscription(
-          serviceConnection.serviceManager.service!.onIsolateEvent
-              .listen((event) {
-            messageBus.addEvent(
-              BusEvent(
-                'debugger',
-                data: event,
-              ),
-            );
+          serviceConnection.serviceManager.service!.onIsolateEvent.listen((
+            event,
+          ) {
+            messageBus.addEvent(BusEvent('debugger', data: event));
           }),
         );
       }
@@ -110,54 +105,117 @@
       _handleConnectionStart(serviceConnection.serviceManager.service!);
     }
     _handleBusEvents();
-    subscribeToFilterChanges();
+    initFilterController();
+
+    addAutoDisposeListener(
+      preferences.logging.retentionLimit,
+      // When the retention limit setting changes, trim the logs to the exact
+      // length of the limit.
+      () => _updateForRetentionLimit(trimWithBuffer: false),
+    );
   }
 
-  /// The toggle filters available for the Logging screen.
-  @override
-  List<ToggleFilter<LogData>> createToggleFilters() => [
-        if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ??
-            true) ...[
-          ToggleFilter<LogData>(
-            name: 'Hide verbose Flutter framework logs (initialization, frame '
-                'times, image sizes)',
-            includeCallback: (log) => !_verboseFlutterFrameworkLogKinds
-                .any((kind) => kind.caseInsensitiveEquals(log.kind)),
-            enabledByDefault: true,
-          ),
-          ToggleFilter<LogData>(
-            name: 'Hide verbose Flutter service logs (service extension state '
-                'changes)',
-            includeCallback: (log) => !_verboseFlutterServiceLogKinds
-                .any((kind) => kind.caseInsensitiveEquals(log.kind)),
-            enabledByDefault: true,
-          ),
-        ],
-        ToggleFilter<LogData>(
-          name: 'Hide garbage collection logs',
-          includeCallback: (log) => !log.kind.caseInsensitiveEquals(_gcLogKind),
-          enabledByDefault: true,
-        ),
-      ];
+  static const _minLogLevelFilterId = 'min-log-level';
+  static const _verboseFlutterFrameworkFilterId = 'verbose-flutter-framework';
+  static const _verboseFlutterServiceFilterId = 'verbose-flutter-service';
+  static const _gcFilterId = 'gc';
 
-  static const kindFilterId = 'logging-kind-filter';
+  /// The setting filters available for the Logging screen.
+  @override
+  SettingFilters<LogData> createSettingFilters() => loggingSettingFilters;
+
+  @visibleForTesting
+  static final loggingSettingFilters = <SettingFilter<LogData, Object>>[
+    SettingFilter<LogData, int>(
+      id: _minLogLevelFilterId,
+      name: 'Hide logs below the minimum log level',
+      includeCallback:
+          (LogData element, int currentFilterValue) =>
+              element.level >= currentFilterValue,
+      enabledCallback: (int filterValue) => filterValue > Level.ALL.value,
+      possibleValues: _possibleLogLevels.map((l) => l.value).toList(),
+      possibleValueDisplays: _possibleLogLevels.map((l) => l.name).toList(),
+      defaultValue: Level.ALL.value,
+    ),
+    if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ??
+        true) ...[
+      ToggleFilter<LogData>(
+        id: _verboseFlutterFrameworkFilterId,
+        name:
+            'Hide verbose Flutter framework logs (initialization, frame '
+            'times, image sizes)',
+        includeCallback:
+            (log) =>
+                !_verboseFlutterFrameworkLogKinds.any(
+                  (kind) => kind.caseInsensitiveEquals(log.kind),
+                ),
+        defaultValue: true,
+      ),
+      ToggleFilter<LogData>(
+        id: _verboseFlutterServiceFilterId,
+        name:
+            'Hide verbose Flutter service logs (service extension state '
+            'changes)',
+        includeCallback:
+            (log) =>
+                !_verboseFlutterServiceLogKinds.any(
+                  (kind) => kind.caseInsensitiveEquals(log.kind),
+                ),
+        defaultValue: true,
+      ),
+    ],
+    ToggleFilter<LogData>(
+      id: _gcFilterId,
+      name: 'Hide garbage collection logs',
+      includeCallback: (log) => !log.kind.caseInsensitiveEquals(_gcLogKind),
+      defaultValue: true,
+    ),
+  ];
+
+  static final _possibleLogLevels = Level.LEVELS
+  // Omit Level.OFF from the possible minimum levels.
+  .where((level) => level != Level.OFF);
+
+  static const _kindFilterId = 'logging-kind-filter';
+  static const _isolateFilterId = 'logging-isolate-filter';
+  static const _zoneFilterId = 'logging-zone-filter';
 
   @override
-  Map<String, QueryFilterArgument<LogData>> createQueryFilterArgs() => {
-        kindFilterId: QueryFilterArgument<LogData>(
-          keys: ['kind', 'k'],
-          dataValueProvider: (log) => log.kind,
-          substringMatch: true,
-        ),
-      };
+  Map<String, QueryFilterArgument<LogData>> createQueryFilterArgs() =>
+      loggingQueryFilterArgs;
 
-  final _logStatusController = StreamController<String>.broadcast();
+  @visibleForTesting
+  static final loggingQueryFilterArgs = <String, QueryFilterArgument<LogData>>{
+    _kindFilterId: QueryFilterArgument<LogData>(
+      keys: ['kind', 'k'],
+      exampleUsages: ['k:stderr', '-k:stdout,gc'],
+      dataValueProvider: (log) => log.kind,
+      substringMatch: true,
+    ),
+    _isolateFilterId: QueryFilterArgument<LogData>(
+      keys: ['isolate', 'i'],
+      exampleUsages: ['i:main', '-i:worker'],
+      dataValueProvider: (log) => log.isolateRef?.name,
+      substringMatch: true,
+    ),
+    _zoneFilterId: QueryFilterArgument<LogData>(
+      keys: ['zone', 'z'],
+      exampleUsages: ['z:custom', '-z:root'],
+      dataValueProvider: (log) => log.zone?.name,
+      substringMatch: true,
+    ),
+  };
+
+  @override
+  ValueNotifier<String>? get filterTagNotifier => preferences.logging.filterTag;
 
   /// A stream of events for the textual description of the log contents.
   ///
   /// See also [statusText].
   Stream<String> get onLogStatusChanged => _logStatusController.stream;
 
+  final _logStatusController = StreamController<String>.broadcast();
+
   List<LogData> data = <LogData>[];
 
   final selectedLog = ValueNotifier<LogData?>(null);
@@ -189,9 +247,11 @@
 
     String label;
 
-    label = totalCount == showingCount
-        ? nf.format(totalCount)
-        : 'showing ${nf.format(showingCount)} of ' '${nf.format(totalCount)}';
+    label =
+        totalCount == showingCount
+            ? nf.format(totalCount)
+            : 'showing ${nf.format(showingCount)} of '
+                '${nf.format(totalCount)}';
 
     label = '$label ${pluralize('event', totalCount)}';
 
@@ -236,6 +296,10 @@
   }
 
   void _handleExtensionEvent(Event e) {
+    final kind = e.extensionKind!.toLowerCase();
+    final timestamp = e.timestamp;
+    final isolateRef = e.isolate;
+
     if (e.extensionKind == FlutterEvent.frame) {
       final frame = FrameInfo(e.extensionData!.data);
 
@@ -245,10 +309,11 @@
 
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.extensionData!.data),
-          e.timestamp,
+          timestamp,
           summary: frameInfoText,
+          isolateRef: isolateRef,
         ),
       );
     } else if (e.extensionKind == FlutterEvent.imageSizesForFrame) {
@@ -257,10 +322,11 @@
       for (final image in images) {
         log(
           LogData(
-            e.extensionKind!.toLowerCase(),
+            kind,
             jsonEncode(image.json),
-            e.timestamp,
+            timestamp,
             summary: image.summary,
+            isolateRef: isolateRef,
           ),
         );
       }
@@ -269,31 +335,35 @@
 
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.json),
-          e.timestamp,
+          timestamp,
           summary: navInfo.routeDescription,
+          isolateRef: isolateRef,
         ),
       );
     } else if (_hideSummaryLogKinds.contains(e.extensionKind)) {
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.json),
-          e.timestamp,
+          timestamp,
           summary: '',
+          isolateRef: isolateRef,
         ),
       );
     } else if (e.extensionKind == FlutterEvent.serviceExtensionStateChanged) {
-      final changedInfo =
-          ServiceExtensionStateChangedInfo.from(e.extensionData!.data);
+      final changedInfo = ServiceExtensionStateChangedInfo.from(
+        e.extensionData!.data,
+      );
 
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.json),
-          e.timestamp,
+          timestamp,
           summary: '${changedInfo.extension}: ${changedInfo.value}',
+          isolateRef: isolateRef,
         ),
       );
     } else if (e.extensionKind == FlutterEvent.error) {
@@ -315,19 +385,23 @@
       final summary = _findFirstSummary(node) ?? node;
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.extensionData!.data),
-          e.timestamp,
+          timestamp,
           summary: summary.toDiagnosticsNode().toString(),
+          level: Level.SEVERE.value,
+          isError: true,
+          isolateRef: isolateRef,
         ),
       );
     } else {
       log(
         LogData(
-          e.extensionKind!.toLowerCase(),
+          kind,
           jsonEncode(e.json),
-          e.timestamp,
+          timestamp,
           summary: e.json.toString(),
+          isolateRef: isolateRef,
         ),
       );
     }
@@ -343,7 +417,8 @@
 
     final time = ((newSpace.time! + oldSpace.time!) * 1000).round();
 
-    final summary = '${isolateRef['name']} • '
+    final summary =
+        '${isolateRef['name']} • '
         '${e.json!['reason']} collection in $time ms • '
         '${printBytes(usedBytes, unit: ByteUnit.mb, includeUnit: true)} used of '
         '${printBytes(capacityBytes, unit: ByteUnit.mb, includeUnit: true)}';
@@ -356,20 +431,38 @@
     };
 
     final message = jsonEncode(event);
-    log(LogData('gc', message, e.timestamp, summary: summary));
+    log(
+      LogData(
+        _gcFilterId,
+        message,
+        e.timestamp,
+        summary: summary,
+        isolateRef: e.isolateRef,
+      ),
+    );
   }
 
   void _handleDeveloperLogEvent(Event e) {
+    final eventJson = e.json!;
     final service = serviceConnection.serviceManager.service;
 
-    final logRecord = _LogRecord(e.json!['logRecord']);
+    final logRecord = _LogRecord(eventJson['logRecord']);
 
-    String? loggerName =
-        _valueAsString(InstanceRef.parse(logRecord.loggerName));
+    String? loggerName = _valueAsString(
+      InstanceRef.parse(logRecord.loggerName),
+    );
     if (loggerName == null || loggerName.isEmpty) {
       loggerName = 'log';
     }
+
     final level = logRecord.level;
+
+    final zoneInstanceRef = InstanceRef.parse(logRecord.zone);
+    final zone = (
+      name: zoneInstanceRef?.classRef?.name,
+      identityHashCode: zoneInstanceRef?.identityHashCode,
+    );
+
     final messageRef = InstanceRef.parse(logRecord.message)!;
     String? summary = _valueAsString(messageRef);
     if (messageRef.valueAsStringIsTruncated == true) {
@@ -378,7 +471,8 @@
     final error = InstanceRef.parse(logRecord.error);
     final stackTrace = InstanceRef.parse(logRecord.stackTrace);
 
-    final details = summary;
+    // TODO(kenz): we may want to narrow down the details of dart developer logs
+    final details = jsonEncode(e.json);
     Future<String> Function()? detailsComputer;
 
     // If the message string was truncated by the VM, or the error object or
@@ -390,16 +484,22 @@
         _isNotNull(stackTrace)) {
       detailsComputer = () async {
         // Get the full string value of the message.
-        String result =
-            await _retrieveFullStringValue(service, e.isolate!, messageRef);
+        String result = await _retrieveFullStringValue(
+          service,
+          e.isolate!,
+          messageRef,
+        );
 
         // Get information about the error object. Some users of the
         // dart:developer log call may pass a data payload in the `error`
         // field, encoded as a json encoded string, so handle that case.
         if (_isNotNull(error)) {
           if (error!.valueAsString != null) {
-            final errorString =
-                await _retrieveFullStringValue(service, e.isolate!, error);
+            final errorString = await _retrieveFullStringValue(
+              service,
+              e.isolate!,
+              error,
+            );
             result += '\n\n$errorString';
           } else {
             // Call `toString()` on the error object and display that.
@@ -442,33 +542,64 @@
         loggerName,
         details,
         e.timestamp,
+        level: level,
         isError: isError,
         summary: summary,
         detailsComputer: detailsComputer,
+        isolateRef: e.isolateRef,
+        zone: zone,
       ),
     );
   }
 
   void log(LogData log) {
-    List<LogData> currentLogs = List.of(data);
+    data.add(log);
+    if (includeLogForFilter(log, filter: activeFilter.value)) {
+      // This will notify since [filteredData] is a [ListValueNotifier].
+      filteredData.add(log);
+    }
+    // TODO(kenz): we don't need to re-filter the data from this method if we
+    // trim the logs for the retention limit. This would require some
+    // refactoring to the _updateData method to support updating the notifiers
+    // without re-filtering all the data.
+    // If we need to trim logs to meet the retention limit, this will call
+    // [_updateData] and perform a re-filter of all the logs.
+    _updateForRetentionLimit();
 
-    // Insert the new item and clamped the list to kMaxLogItemsLength.
-    currentLogs.add(log);
+    // TODO(kenz): this will traverse all the logs to refresh search matches.
+    // We don't need to do this. We could optimize further by updating the
+    // search match status for the individual log and for the controller without
+    // traversing the entire data set. This is a no-op when the search value is
+    // empty, but this cost is O(N*N) when a search value is present.
+    refreshSearchMatches();
 
-    // Note: We need to drop rows from the start because we want to drop old
-    // rows but because that's expensive, we only do it periodically (eg. when
-    // the list is 500 rows more).
-    if (currentLogs.length > kMaxLogItemsUpperBound) {
-      int itemsToRemove = currentLogs.length - kMaxLogItemsLowerBound;
+    _updateStatus();
+  }
+
+  void _updateForRetentionLimit({
+    bool trimWithBuffer = true,
+    bool updateData = true,
+  }) {
+    // For performance reasons, we drop old logs in batches. Because it is
+    // expensive to drop from the beginning of a list, we only do it
+    // periodically (e.g. drop [_defaultBufferReductionSize] logs when the log
+    // retention limit has been reached.
+    final retentionLimit = preferences.logging.retentionLimit.value;
+    if (data.length > retentionLimit) {
+      final reduceToSize = math.max(
+        retentionLimit - (trimWithBuffer ? defaultLogBufferReductionSize : 0),
+        0,
+      );
+      int dropUntilIndex = data.length - reduceToSize;
       // Ensure we remove an even number of rows to keep the alternating
       // background in-sync.
-      if (itemsToRemove % 2 == 1) {
-        itemsToRemove--;
+      if (dropUntilIndex % 2 == 1) {
+        dropUntilIndex--;
       }
-      currentLogs = currentLogs.sublist(itemsToRemove);
+      if (updateData) {
+        _updateData(data.sublist(math.max(dropUntilIndex, 0)));
+      }
     }
-
-    _updateData(currentLogs);
   }
 
   static RemoteDiagnosticsNode? _findFirstSummary(RemoteDiagnosticsNode node) {
@@ -576,59 +707,80 @@
   @override
   Iterable<LogData> get currentDataToSearchThrough => filteredData.value;
 
+  bool includeLogForFilter(LogData log, {required Filter filter}) {
+    final filteredOutBySettingFilters = filter.settingFilters.any(
+      (settingFilter) => !settingFilter.includeData(log),
+    );
+    if (filteredOutBySettingFilters) return false;
+
+    final queryFilter = filter.queryFilter;
+    if (!queryFilter.isEmpty) {
+      final filteredOutByQueryFilterArgument = queryFilter
+          .filterArguments
+          .values
+          .any((argument) => !argument.matchesValue(log));
+      if (filteredOutByQueryFilterArgument) return false;
+
+      if (filter.queryFilter.substringExpressions.isNotEmpty) {
+        for (final substring in filter.queryFilter.substringExpressions) {
+          final matchesKind = log.kind.caseInsensitiveContains(substring);
+          if (matchesKind) return true;
+
+          final matchesLevel = log.levelName.caseInsensitiveContains(substring);
+          if (matchesLevel) return true;
+
+          final matchesIsolateName =
+              log.isolateRef?.name?.caseInsensitiveContains(substring) ?? false;
+          if (matchesIsolateName) return true;
+
+          final zone = log.zone;
+          final matchesZoneName =
+              zone?.name?.caseInsensitiveContains(substring) ?? false;
+          final matchesZoneIdentity =
+              zone?.identityHashCode?.toString().caseInsensitiveContains(
+                substring,
+              ) ??
+              false;
+          if (matchesZoneName || matchesZoneIdentity) return true;
+
+          final matchesSummary =
+              log.summary != null &&
+              log.summary!.caseInsensitiveContains(substring);
+          if (matchesSummary) return true;
+
+          final matchesDetails =
+              log.details != null &&
+              log.details!.caseInsensitiveContains(substring);
+          if (matchesDetails) return true;
+        }
+        return false;
+      }
+    }
+    return true;
+  }
+
   @override
   void filterData(Filter<LogData> filter) {
     super.filterData(filter);
-
-    bool filterCallback(LogData log) {
-      final filteredOutByToggleFilters = filter.toggleFilters.any(
-        (toggleFilter) =>
-            toggleFilter.enabled.value && !toggleFilter.includeCallback(log),
-      );
-      if (filteredOutByToggleFilters) return false;
-
-      final queryFilter = filter.queryFilter;
-      if (!queryFilter.isEmpty) {
-        final filteredOutByQueryFilterArgument = queryFilter
-            .filterArguments.values
-            .any((argument) => !argument.matchesValue(log));
-        if (filteredOutByQueryFilterArgument) return false;
-
-        if (filter.queryFilter.substringExpressions.isNotEmpty) {
-          for (final substring in filter.queryFilter.substringExpressions) {
-            final matchesKind = log.kind.caseInsensitiveContains(substring);
-            if (matchesKind) return true;
-
-            final matchesSummary = log.summary != null &&
-                log.summary!.caseInsensitiveContains(substring);
-            if (matchesSummary) return true;
-
-            final matchesDetails = log.details != null &&
-                log.details!.caseInsensitiveContains(substring);
-            if (matchesDetails) return true;
-          }
-          return false;
-        }
-      }
-
-      return true;
-    }
-
     filteredData
       ..clear()
       ..addAll(
-        data.where(filterCallback).toList(),
+        data.where((log) => includeLogForFilter(log, filter: filter)).toList(),
       );
   }
 }
 
 extension type _LogRecord(Map<String, dynamic> json) {
+  int? get sequenceNumber => json['sequenceNumber'];
+
   int? get level => json['level'];
 
   Map<String, Object?> get loggerName => json['loggerName'];
 
   Map<String, Object?> get message => json['message'];
 
+  Map<String, Object?> get zone => json['zone'];
+
   Map<String, Object?> get error => json['error'];
 
   Map<String, Object?> get stackTrace => json['stackTrace'];
@@ -668,6 +820,7 @@
             buffer!.timestamp,
             summary: buffer!.summary! + message,
             isError: buffer!.isError,
+            isolateRef: e.isolateRef,
           ),
         );
         buffer = null;
@@ -691,6 +844,7 @@
       e.timestamp,
       summary: summary,
       isError: isError,
+      isolateRef: e.isolateRef,
     );
 
     if (message == '\n') {
@@ -737,15 +891,39 @@
     this._details,
     this.timestamp, {
     this.summary,
+    int? level,
     this.isError = false,
     this.detailsComputer,
     this.node,
-  });
+    this.isolateRef,
+    this.zone,
+  }) : level = level ?? (isError ? Level.SEVERE.value : Level.INFO.value) {
+    final originalDetails = _details;
+    // Fetch details immediately on creation.
+    unawaited(
+      compute().catchError((Object? error) {
+        // On error, set the value of details to its original value.
+        _details = originalDetails;
+        _detailsComputed.value = true;
+        error_handling.reportError(
+          'Error fetching details for $kind log'
+          '${error != null ? ': $error' : ''}.',
+        );
+      }),
+    );
+  }
 
   final String kind;
+  final int level;
   final int? timestamp;
   final bool isError;
   final String? summary;
+  final IsolateRef? isolateRef;
+  final ZoneDescription? zone;
+
+  String get levelName =>
+      _levelName ??= LogLevelMetadataChip.generateLogLevel(level).name;
+  String? _levelName;
 
   final RemoteDiagnosticsNode? node;
   String? _details;
@@ -755,30 +933,65 @@
 
   String? get details => _details;
 
-  bool get needsComputing => detailsComputer != null;
+  bool get needsComputing => !_detailsComputed.value;
+
+  ValueListenable<bool> get detailsComputed => _detailsComputed;
+  final _detailsComputed = ValueNotifier<bool>(false);
 
   Future<void> compute() async {
-    _details = await detailsComputer!();
-    detailsComputer = null;
+    if (!detailsComputed.value) {
+      if (detailsComputer != null) {
+        _details = await detailsComputer!();
+      }
+      detailsComputer = null;
+      _detailsComputed.value = true;
+    }
   }
 
   String? prettyPrinted() {
-    if (needsComputing) {
-      return details;
+    if (!detailsComputed.value) {
+      return details?.trim();
     }
 
     try {
       return prettyPrinter
           .convert(jsonDecode(details!))
-          .replaceAll(r'\n', '\n');
+          .replaceAll(r'\n', '\n')
+          .trim();
     } catch (_) {
-      return details;
+      return details?.trim();
     }
   }
 
+  String get encodedDetails {
+    if (_encodedDetails != null) return _encodedDetails!;
+    if (details == null) return '';
+
+    // TODO(kenz): ensure this doesn't cause performance issues.
+    String encoded;
+    try {
+      // Attempt to decode the input string as JSON
+      jsonDecode(details!);
+      // If decoding is successful, it's already JSON encoded
+      encoded = details!;
+    } catch (e) {
+      // If decoding fails, it's not JSON encoded, so encode it
+      encoded = jsonEncode(details!);
+    }
+
+    // Only cache the value if details have already been computed.
+    if (detailsComputed.value) _encodedDetails = encoded;
+    return encoded;
+  }
+
+  String? _encodedDetails;
+
   @override
   bool matchesSearchToken(RegExp regExpSearch) {
     return kind.caseInsensitiveContains(regExpSearch) ||
+        levelName.caseInsensitiveContains(regExpSearch) ||
+        isolateRef?.name?.caseInsensitiveContains(regExpSearch) == true ||
+        zone?.name?.caseInsensitiveContains(regExpSearch) == true ||
         (summary?.caseInsensitiveContains(regExpSearch) == true) ||
         (details?.caseInsensitiveContains(regExpSearch) == true);
   }
@@ -870,3 +1083,7 @@
   final String? extension;
   final Object value;
 }
+
+extension on Event {
+  IsolateRef? get isolateRef => IsolateRef.parse(this.json?['isolate']);
+}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_controls.dart b/packages/devtools_app/lib/src/screens/logging/logging_controls.dart
new file mode 100644
index 0000000..f1652b3
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/logging/logging_controls.dart
@@ -0,0 +1,139 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import '../../service/service_extension_widgets.dart';
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/globals.dart';
+import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/filter.dart';
+import '../../shared/ui/search.dart';
+import 'logging_controller.dart';
+
+const _loggingMinVerboseWidth = 650.0;
+
+class LoggingControls extends StatelessWidget {
+  const LoggingControls({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final controller = Provider.of<LoggingController>(context);
+    return Row(
+      children: [
+        ClearButton(
+          onPressed: controller.clear,
+          gaScreen: gac.logging,
+          gaSelection: gac.clear,
+          minScreenWidthForTextBeforeScaling: _loggingMinVerboseWidth,
+        ),
+        const SizedBox(width: denseSpacing),
+        Expanded(
+          // TODO(kenz): fix focus issue when state is refreshed
+          child: ValueListenableBuilder(
+            valueListenable: controller.filteredData,
+            builder:
+                (context, _, _) => SearchField<LoggingController>(
+                  searchFieldWidth:
+                      isScreenWiderThan(context, _loggingMinVerboseWidth)
+                          ? wideSearchFieldWidth
+                          : defaultSearchFieldWidth,
+                  searchController: controller,
+                  searchFieldEnabled: controller.filteredData.value.isNotEmpty,
+                ),
+          ),
+        ),
+        const SizedBox(width: denseSpacing),
+        Expanded(
+          child: StandaloneFilterField<LogData>(
+            controller: controller,
+            filteredItem: 'log',
+          ),
+        ),
+        const SizedBox(width: denseSpacing),
+        CopyToClipboardControl(
+          dataProvider:
+              () => controller.filteredData.value
+                  .map((e) => '${e.timestamp} [${e.kind}] ${e.prettyPrinted()}')
+                  .joinWithTrailing('\n'),
+          tooltip: 'Copy filtered logs',
+        ),
+        const SizedBox(width: denseSpacing),
+        SettingsOutlinedButton(
+          gaScreen: gac.logging,
+          gaSelection: gac.loggingSettings,
+          tooltip: 'Logging Settings',
+          onPressed: () {
+            unawaited(
+              showDialog(
+                context: context,
+                builder: (context) => const LoggingSettingsDialog(),
+              ),
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
+
+class LoggingSettingsDialog extends StatefulWidget {
+  const LoggingSettingsDialog({super.key});
+
+  @override
+  State<LoggingSettingsDialog> createState() => _LoggingSettingsDialogState();
+}
+
+class _LoggingSettingsDialogState extends State<LoggingSettingsDialog> {
+  static const _retentionLimitHeight = 48.0;
+
+  final temporaryRetentionLimit = ValueNotifier<int>(
+    preferences.logging.retentionLimit.value,
+  );
+
+  @override
+  void dispose() {
+    temporaryRetentionLimit.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return DevToolsDialog(
+      title: const DialogTitleText('Logging Settings'),
+      content: Column(
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          SizedBox(
+            height: _retentionLimitHeight,
+            child: PositiveIntegerSetting(
+              title: 'Limit for the number of logs retained.',
+              subTitle:
+                  'Once the limit is reached, the first '
+                  '$defaultLogBufferReductionSize logs will be dropped.',
+              notifier: temporaryRetentionLimit,
+              minimumValue: defaultLogBufferReductionSize,
+            ),
+          ),
+          const StructuredErrorsToggle(),
+        ],
+      ),
+      actions: [
+        DialogApplyButton(
+          onPressed: () {
+            preferences.logging.retentionLimit.value =
+                temporaryRetentionLimit.value;
+          },
+        ),
+        const DialogCloseButton(),
+      ],
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen.dart
index 40aaae3..d077b75 100644
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen.dart
+++ b/packages/devtools_app/lib/src/screens/logging/logging_screen.dart
@@ -2,26 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:async';
-
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../../service/service_extension_widgets.dart';
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
-import '../../shared/primitives/utils.dart';
-import '../../shared/screen.dart';
-import '../../shared/ui/filter.dart';
-import '../../shared/ui/search.dart';
-import '../../shared/utils.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/ui/utils.dart';
+import '../../shared/utils/utils.dart';
 import '_log_details.dart';
 import '_logs_table.dart';
 import 'logging_controller.dart';
-import 'shared/constants.dart';
+import 'logging_controls.dart';
 
 /// Presents logs from the connected app.
 class LoggingScreen extends Screen {
@@ -52,19 +46,6 @@
 class LoggingScreenBody extends StatefulWidget {
   const LoggingScreenBody({super.key});
 
-  static const filterQueryInstructions = '''
-Type a filter query to show or hide specific logs.
-
-Any text that is not paired with an available filter key below will be queried against all categories (kind, message).
-
-Available filters:
-    'kind', 'k'       (e.g. 'k:flutter.frame', '-k:gc,stdout')
-
-Example queries:
-    'my log message k:stdout,stdin'
-    'flutter -k:gc'
-''';
-
   @override
   State<LoggingScreenBody> createState() => _LoggingScreenState();
 }
@@ -73,8 +54,6 @@
     with
         AutoDisposeMixin,
         ProvidedControllerMixin<LoggingController, LoggingScreenBody> {
-  late List<LogData> filteredLogs;
-
   @override
   void initState() {
     super.initState();
@@ -87,142 +66,54 @@
     if (!initController()) return;
 
     cancelListeners();
-
-    filteredLogs = controller.filteredData.value;
-    addAutoDisposeListener(controller.filteredData, () {
-      setState(() {
-        filteredLogs = controller.filteredData.value;
-      });
-    });
+    addAutoDisposeListener(controller.filteredData);
   }
 
   @override
   Widget build(BuildContext context) {
+    final splitAxis = _splitAxisFor(context);
     return Column(
       children: [
-        _buildLoggingControls(),
+        const LoggingControls(),
         const SizedBox(height: intermediateSpacing),
         Expanded(
-          child: _buildLoggingBody(),
-        ),
-      ],
-    );
-  }
-
-  // TODO(kenz): replace with helper widget
-  Widget _buildLoggingControls() {
-    final hasData = controller.filteredData.value.isNotEmpty;
-    return Row(
-      children: [
-        ClearButton(
-          onPressed: controller.clear,
-          gaScreen: gac.logging,
-          gaSelection: gac.clear,
-          minScreenWidthForTextBeforeScaling: loggingMinVerboseWidth,
-        ),
-        const Spacer(),
-        const SizedBox(width: denseSpacing),
-        // TODO(kenz): fix focus issue when state is refreshed
-        SearchField<LoggingController>(
-          searchFieldWidth: isScreenWiderThan(context, loggingMinVerboseWidth)
-              ? wideSearchFieldWidth
-              : defaultSearchFieldWidth,
-          searchController: controller,
-          searchFieldEnabled: hasData,
-        ),
-        const SizedBox(width: denseSpacing),
-        DevToolsFilterButton(
-          onPressed: _showFilterDialog,
-          isFilterActive: controller.isFilterActive,
-        ),
-        const SizedBox(width: denseSpacing),
-        CopyToClipboardControl(
-          dataProvider: () => controller.filteredData.value
-              .map((e) => '${e.timestamp} [${e.kind}] ${e.prettyPrinted()}')
-              .joinWithTrailing('\n'),
-          tooltip: 'Copy filtered logs',
-        ),
-        const SizedBox(width: denseSpacing),
-        SettingsOutlinedButton(
-          gaScreen: gac.logging,
-          gaSelection: gac.loggingSettings,
-          tooltip: 'Logging Settings',
-          onPressed: () {
-            unawaited(
-              showDialog(
-                context: context,
-                builder: (context) => const LoggingSettingsDialog(),
+          child: SplitPane(
+            axis: splitAxis,
+            initialFractions:
+                splitAxis == Axis.vertical
+                    ? const [0.8, 0.2]
+                    : const [0.7, 0.3],
+            children: [
+              RoundedOutlinedBorder(
+                clip: true,
+                child: LogsTable(
+                  controller: controller,
+                  data: controller.filteredData.value,
+                  selectionNotifier: controller.selectedLog,
+                  searchMatchesNotifier: controller.searchMatches,
+                  activeSearchMatchNotifier: controller.activeSearchMatch,
+                ),
               ),
-            );
-          },
-        ),
-      ],
-    );
-  }
-
-  // TODO(kenz): replace with helper widget.
-  Widget _buildLoggingBody() {
-    return SplitPane(
-      axis: Axis.vertical,
-      initialFractions: const [0.72, 0.28],
-      // TODO(kenz): refactor so that the LogDetails header can be the splitter.
-      // This would be more consistent with other screens that use the console
-      // header as the splitter.
-      children: [
-        RoundedOutlinedBorder(
-          clip: true,
-          child: LogsTable(
-            data: filteredLogs,
-            selectionNotifier: controller.selectedLog,
-            searchMatchesNotifier: controller.searchMatches,
-            activeSearchMatchNotifier: controller.activeSearchMatch,
+              ValueListenableBuilder<LogData?>(
+                valueListenable: controller.selectedLog,
+                builder: (context, selected, _) {
+                  return LogDetails(log: selected);
+                },
+              ),
+            ],
           ),
         ),
-        ValueListenableBuilder<LogData?>(
-          valueListenable: controller.selectedLog,
-          builder: (context, selected, _) {
-            return LogDetails(log: selected);
-          },
-        ),
       ],
     );
   }
 
-  void _showFilterDialog() {
-    unawaited(
-      showDialog(
-        context: context,
-        builder: (context) => FilterDialog<LogData>(
-          controller: controller,
-          queryInstructions: LoggingScreenBody.filterQueryInstructions,
-        ),
-      ),
-    );
-  }
-}
-
-class LoggingSettingsDialog extends StatelessWidget {
-  const LoggingSettingsDialog({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    return DevToolsDialog(
-      title: const DialogTitleText('Logging Settings'),
-      content: Column(
-        mainAxisSize: MainAxisSize.min,
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: [
-          ...dialogSubHeader(
-            theme,
-            'General',
-          ),
-          const StructuredErrorsToggle(),
-        ],
-      ),
-      actions: const [
-        DialogCloseButton(),
-      ],
-    );
+  Axis _splitAxisFor(BuildContext context) {
+    final screenSize = MediaQuery.of(context).size;
+    final aspectRatio = screenSize.width / screenSize.height;
+    if (screenSize.height <= MediaSize.s.heightThreshold ||
+        aspectRatio >= 1.2) {
+      return Axis.horizontal;
+    }
+    return Axis.vertical;
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_controller_v2.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_controller_v2.dart
deleted file mode 100644
index b0f3b26..0000000
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_controller_v2.dart
+++ /dev/null
@@ -1,664 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:devtools_app_shared/service.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_shared.dart';
-import 'package:flutter/foundation.dart';
-import 'package:logging/logging.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../../service/vm_service_wrapper.dart';
-import '../../../shared/diagnostics/diagnostics_node.dart';
-import '../../../shared/diagnostics/inspector_service.dart';
-import '../../../shared/globals.dart';
-import '../../../shared/primitives/byte_utils.dart';
-import '../../../shared/primitives/message_bus.dart';
-import '../../../shared/primitives/utils.dart';
-import '../../../shared/ui/search.dart';
-import '../logging_controller.dart'
-    show
-        FrameInfo,
-        ImageSizesForFrame,
-        NavigationInfo,
-        ServiceExtensionStateChangedInfo;
-import '../logging_screen.dart';
-import 'logging_model.dart';
-
-final _log = Logger('logging_controller');
-
-// For performance reasons, we drop old logs in batches, so the log will grow
-// to kMaxLogItemsUpperBound then truncate to kMaxLogItemsLowerBound.
-
-bool _verboseDebugging = false;
-
-Future<String> _retrieveFullStringValue(
-  VmServiceWrapper? service,
-  IsolateRef isolateRef,
-  InstanceRef stringRef,
-) {
-  final fallback = '${stringRef.valueAsString}...';
-  // TODO(kenz): why is service null?
-
-  return service
-          ?.retrieveFullStringValue(
-            isolateRef.id!,
-            stringRef,
-            onUnavailable: (truncatedValue) => fallback,
-          )
-          .then((value) => value ?? fallback) ??
-      Future.value(fallback);
-}
-
-/// Logs kinds to show without a summary in the table.
-final _hideSummaryLogKinds = <String>{
-  FlutterEvent.firstFrame,
-  FlutterEvent.frameworkInitialization,
-};
-
-class LoggingControllerV2 extends DisposableController
-    with AutoDisposeControllerMixin {
-  LoggingControllerV2() {
-    addAutoDisposeListener(serviceConnection.serviceManager.connectedState, () {
-      if (serviceConnection.serviceManager.connectedState.value.connected) {
-        _handleConnectionStart(serviceConnection.serviceManager.service!);
-
-        autoDisposeStreamSubscription(
-          serviceConnection.serviceManager.service!.onIsolateEvent
-              .listen((event) {
-            messageBus.addEvent(
-              BusEvent(
-                'debugger',
-                data: event,
-              ),
-            );
-          }),
-        );
-      }
-    });
-    if (serviceConnection.serviceManager.connectedAppInitialized) {
-      _handleConnectionStart(serviceConnection.serviceManager.service!);
-    }
-    _handleBusEvents();
-  }
-
-  static const kindFilterId = 'logging-kind-filter';
-
-  final _logStatusController = StreamController<String>.broadcast();
-
-  /// A stream of events for the textual description of the log contents.
-  ///
-  /// See also [statusText].
-  Stream<String> get onLogStatusChanged => _logStatusController.stream;
-
-  final selectedLog = ValueNotifier<LogDataV2?>(null);
-  final loggingModel = LoggingTableModel();
-
-  void _updateSelection() {
-    final selected = selectedLog.value;
-    if (selected != null) {
-      final logs = loggingModel.filteredData.value;
-      if (!logs.contains(selected)) {
-        selectedLog.value = null;
-      }
-    }
-  }
-
-  ObjectGroup get objectGroup =>
-      serviceConnection.consoleService.objectGroup as ObjectGroup;
-
-  String get statusText {
-    final totalCount = loggingModel.logCount;
-    final showingCount = loggingModel.filteredData.value.length;
-
-    String label;
-
-    label = totalCount == showingCount
-        ? nf.format(totalCount)
-        : 'showing ${nf.format(showingCount)} of ' '${nf.format(totalCount)}';
-
-    label = '$label ${pluralize('event', totalCount)}';
-
-    return label;
-  }
-
-  void _updateStatus() {
-    final label = statusText;
-    _logStatusController.add(label);
-  }
-
-  void clear() {
-    loggingModel.clear();
-    _updateSelection();
-    _updateStatus();
-    serviceConnection.errorBadgeManager.clearErrors(LoggingScreen.id);
-  }
-
-  void _handleConnectionStart(VmServiceWrapper service) {
-    // Log stdout events.
-    final stdoutHandler = _StdoutEventHandler(this, 'stdout');
-    autoDisposeStreamSubscription(
-      service.onStdoutEventWithHistorySafe.listen(stdoutHandler.handle),
-    );
-
-    // Log stderr events.
-    final stderrHandler = _StdoutEventHandler(this, 'stderr', isError: true);
-    autoDisposeStreamSubscription(
-      service.onStderrEventWithHistorySafe.listen(stderrHandler.handle),
-    );
-
-    // Log GC events.
-    autoDisposeStreamSubscription(service.onGCEvent.listen(_handleGCEvent));
-
-    // Log `dart:developer` `log` events.
-    autoDisposeStreamSubscription(
-      service.onLoggingEventWithHistorySafe.listen(_handleDeveloperLogEvent),
-    );
-
-    // Log Flutter extension events.
-    autoDisposeStreamSubscription(
-      service.onExtensionEventWithHistorySafe.listen(_handleExtensionEvent),
-    );
-  }
-
-  void _handleExtensionEvent(Event e) {
-    if (e.extensionKind == FlutterEvent.frame) {
-      final frame = FrameInfo(e.extensionData!.data);
-
-      final frameId = '#${frame.number}';
-      final frameInfoText =
-          '$frameId ${frame.elapsedMs.toStringAsFixed(1).padLeft(4)}ms ';
-
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.extensionData!.data),
-          e.timestamp,
-          summary: frameInfoText,
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.imageSizesForFrame) {
-      final images = ImageSizesForFrame.from(e.extensionData!.data);
-      for (final image in images) {
-        log(
-          LogDataV2(
-            e.extensionKind!.toLowerCase(),
-            jsonEncode(image.json),
-            e.timestamp,
-            summary: image.summary,
-          ),
-        );
-      }
-    } else if (e.extensionKind == FlutterEvent.navigation) {
-      final navInfo = NavigationInfo.from(e.extensionData!.data);
-
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: navInfo.routeDescription,
-        ),
-      );
-    } else if (_hideSummaryLogKinds.contains(e.extensionKind)) {
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: '',
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.serviceExtensionStateChanged) {
-      final changedInfo =
-          ServiceExtensionStateChangedInfo.from(e.extensionData!.data);
-
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: '${changedInfo.extension}: ${changedInfo.value}',
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.error) {
-      // TODO(pq): add tests for error extension handling once framework changes
-      // are landed.
-      final node = RemoteDiagnosticsNode(
-        e.extensionData!.data,
-        objectGroup,
-        false,
-        null,
-      );
-      // Workaround the fact that the error objects from the server don't have
-      // style error.
-      node.style = DiagnosticsTreeStyle.error;
-      if (_verboseDebugging) {
-        _log.info('node toStringDeep:######\n${node.toStringDeep()}\n###');
-      }
-
-      final summary = _findFirstSummary(node) ?? node;
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.extensionData!.data),
-          e.timestamp,
-          summary: summary.toDiagnosticsNode().toString(),
-        ),
-      );
-    } else {
-      log(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: e.json.toString(),
-        ),
-      );
-    }
-  }
-
-  void _handleGCEvent(Event e) {
-    final newSpace = HeapSpace.parse(e.json!['new'])!;
-    final oldSpace = HeapSpace.parse(e.json!['old'])!;
-    final isolateRef = (e.json!['isolate'] as Map).cast<String, Object?>();
-
-    final usedBytes = newSpace.used! + oldSpace.used!;
-    final capacityBytes = newSpace.capacity! + oldSpace.capacity!;
-
-    final time = ((newSpace.time! + oldSpace.time!) * 1000).round();
-
-    final summary = '${isolateRef['name']} • '
-        '${e.json!['reason']} collection in $time ms • '
-        '${printBytes(usedBytes, unit: ByteUnit.mb, includeUnit: true)} used of '
-        '${printBytes(capacityBytes, unit: ByteUnit.mb, includeUnit: true)}';
-
-    final event = <String, Object>{
-      'reason': e.json!['reason'],
-      'new': newSpace.json,
-      'old': oldSpace.json,
-      'isolate': isolateRef,
-    };
-
-    final message = jsonEncode(event);
-    log(LogDataV2('gc', message, e.timestamp, summary: summary));
-  }
-
-  void _handleDeveloperLogEvent(Event e) {
-    final service = serviceConnection.serviceManager.service;
-
-    final logRecord = _LogRecord(e.json!['logRecord']);
-
-    String? loggerName =
-        _valueAsString(InstanceRef.parse(logRecord.loggerName));
-    if (loggerName == null || loggerName.isEmpty) {
-      loggerName = 'log';
-    }
-    final level = logRecord.level;
-    final messageRef = InstanceRef.parse(logRecord.message)!;
-    String? summary = _valueAsString(messageRef);
-    if (messageRef.valueAsStringIsTruncated == true) {
-      summary = '${summary!}...';
-    }
-    final error = InstanceRef.parse(logRecord.error);
-    final stackTrace = InstanceRef.parse(logRecord.stackTrace);
-
-    final details = summary;
-    Future<String> Function()? detailsComputer;
-
-    // If the message string was truncated by the VM, or the error object or
-    // stackTrace objects were non-null, we need to ask the VM for more
-    // information in order to render the log entry. We do this asynchronously
-    // on-demand using the `detailsComputer` Future.
-    if (messageRef.valueAsStringIsTruncated == true ||
-        _isNotNull(error) ||
-        _isNotNull(stackTrace)) {
-      detailsComputer = () async {
-        // Get the full string value of the message.
-        String result =
-            await _retrieveFullStringValue(service, e.isolate!, messageRef);
-
-        // Get information about the error object. Some users of the
-        // dart:developer log call may pass a data payload in the `error`
-        // field, encoded as a json encoded string, so handle that case.
-        if (_isNotNull(error)) {
-          if (error!.valueAsString != null) {
-            final errorString =
-                await _retrieveFullStringValue(service, e.isolate!, error);
-            result += '\n\n$errorString';
-          } else {
-            // Call `toString()` on the error object and display that.
-            final toStringResult = await service!.invoke(
-              e.isolate!.id!,
-              error.id!,
-              'toString',
-              <String>[],
-              disableBreakpoints: true,
-            );
-
-            if (toStringResult is ErrorRef) {
-              final errorString = _valueAsString(error);
-              result += '\n\n$errorString';
-            } else if (toStringResult is InstanceRef) {
-              final str = await _retrieveFullStringValue(
-                service,
-                e.isolate!,
-                toStringResult,
-              );
-              result += '\n\n$str';
-            }
-          }
-        }
-
-        // Get info about the stackTrace object.
-        if (_isNotNull(stackTrace)) {
-          result += '\n\n${_valueAsString(stackTrace)}';
-        }
-
-        return result;
-      };
-    }
-
-    const severeIssue = 1000;
-    final isError = level != null && level >= severeIssue ? true : false;
-
-    log(
-      LogDataV2(
-        loggerName,
-        details,
-        e.timestamp,
-        isError: isError,
-        summary: summary,
-        detailsComputer: detailsComputer,
-      ),
-    );
-  }
-
-  void log(LogDataV2 log) {
-    loggingModel.add(log);
-  }
-
-  static RemoteDiagnosticsNode? _findFirstSummary(RemoteDiagnosticsNode node) {
-    if (node.level == DiagnosticLevel.summary) {
-      return node;
-    }
-    RemoteDiagnosticsNode? summary;
-    for (final property in node.inlineProperties) {
-      summary = _findFirstSummary(property);
-      if (summary != null) return summary;
-    }
-
-    for (final child in node.childrenNow) {
-      summary = _findFirstSummary(child);
-      if (summary != null) return summary;
-    }
-
-    return null;
-  }
-
-  void _handleBusEvents() {
-    // TODO(jacobr): expose the messageBus for use by vm tests.
-    autoDisposeStreamSubscription(
-      messageBus.onEvent(type: 'reload.end').listen((BusEvent event) {
-        log(
-          LogDataV2(
-            'hot.reload',
-            event.data as String?,
-            DateTime.now().millisecondsSinceEpoch,
-          ),
-        );
-      }),
-    );
-
-    autoDisposeStreamSubscription(
-      messageBus.onEvent(type: 'restart.end').listen((BusEvent event) {
-        log(
-          LogDataV2(
-            'hot.restart',
-            event.data as String?,
-            DateTime.now().millisecondsSinceEpoch,
-          ),
-        );
-      }),
-    );
-
-    // Listen for debugger events.
-    autoDisposeStreamSubscription(
-      messageBus
-          .onEvent()
-          .where(
-            (event) =>
-                event.type == 'debugger' || event.type.startsWith('debugger.'),
-          )
-          .listen(_handleDebuggerEvent),
-    );
-
-    // Listen for DevTools internal events.
-    autoDisposeStreamSubscription(
-      messageBus
-          .onEvent()
-          .where((event) => event.type.startsWith('devtools.'))
-          .listen(_handleDevToolsEvent),
-    );
-  }
-
-  void _handleDebuggerEvent(BusEvent event) {
-    final debuggerEvent = event.data as Event;
-
-    // Filter ServiceExtensionAdded events as they're pretty noisy.
-    if (debuggerEvent.kind == EventKind.kServiceExtensionAdded) {
-      return;
-    }
-
-    log(
-      LogDataV2(
-        event.type,
-        jsonEncode(debuggerEvent.json),
-        debuggerEvent.timestamp,
-        summary: '${debuggerEvent.kind} ${debuggerEvent.isolate!.id}',
-      ),
-    );
-  }
-
-  void _handleDevToolsEvent(BusEvent event) {
-    var details = event.data.toString();
-    String? summary;
-
-    if (details.contains('\n')) {
-      final lines = details.split('\n');
-      summary = lines.first;
-      details = lines.sublist(1).join('\n');
-    }
-
-    log(
-      LogDataV2(
-        event.type,
-        details,
-        DateTime.now().millisecondsSinceEpoch,
-        summary: summary,
-      ),
-    );
-  }
-}
-
-extension type _LogRecord(Map<String, dynamic> json) {
-  int? get level => json['level'];
-
-  Map<String, Object?> get loggerName => json['loggerName'];
-
-  Map<String, Object?> get message => json['message'];
-
-  Map<String, Object?> get error => json['error'];
-
-  Map<String, Object?> get stackTrace => json['stackTrace'];
-}
-
-/// Receive and log stdout / stderr events from the VM.
-///
-/// This class buffers the events for up to 1ms. This is in order to combine a
-/// stdout message and its newline. Currently, `foo\n` is sent as two VM events;
-/// we wait for up to 1ms when we get the `foo` event, to see if the next event
-/// is a single newline. If so, we add the newline to the previous log message.
-class _StdoutEventHandler {
-  _StdoutEventHandler(
-    this.loggingController,
-    this.name, {
-    this.isError = false,
-  });
-
-  final LoggingControllerV2 loggingController;
-  final String name;
-  final bool isError;
-
-  LogDataV2? buffer;
-  Timer? timer;
-
-  void handle(Event e) {
-    final message = decodeBase64(e.bytes!);
-
-    if (buffer != null) {
-      timer?.cancel();
-
-      if (message == '\n') {
-        loggingController.log(
-          LogDataV2(
-            buffer!.kind,
-            buffer!.details! + message,
-            buffer!.timestamp,
-            summary: buffer!.summary! + message,
-            isError: buffer!.isError,
-          ),
-        );
-        buffer = null;
-        return;
-      }
-
-      loggingController.log(buffer!);
-      buffer = null;
-    }
-
-    const maxLength = 200;
-
-    String summary = message;
-    if (message.length > maxLength) {
-      summary = message.substring(0, maxLength);
-    }
-
-    final data = LogDataV2(
-      name,
-      message,
-      e.timestamp,
-      summary: summary,
-      isError: isError,
-    );
-
-    if (message == '\n') {
-      loggingController.log(data);
-    } else {
-      buffer = data;
-      timer = Timer(const Duration(milliseconds: 1), () {
-        loggingController.log(buffer!);
-        buffer = null;
-      });
-    }
-  }
-}
-
-bool _isNotNull(InstanceRef? serviceRef) {
-  return serviceRef != null && serviceRef.kind != 'Null';
-}
-
-String? _valueAsString(InstanceRef? ref) {
-  if (ref == null) {
-    return null;
-  }
-
-  if (ref.valueAsString == null) {
-    return ref.valueAsString;
-  }
-
-  return ref.valueAsStringIsTruncated == true
-      ? '${ref.valueAsString}...'
-      : ref.valueAsString;
-}
-
-/// A log data object that includes optional summary information about whether
-/// the log entry represents an error entry, the log entry kind, and more
-/// detailed data for the entry.
-///
-/// The details can optionally be loaded lazily on first use. If this is the
-/// case, this log entry will have a non-null `detailsComputer` field. After the
-/// data is calculated, the log entry will be modified to contain the calculated
-/// `details` data.
-class LogDataV2 with SearchableDataMixin {
-  LogDataV2(
-    this.kind,
-    this._details,
-    this.timestamp, {
-    this.summary,
-    this.isError = false,
-    this.detailsComputer,
-    this.node,
-  }) {
-    // Fetch details immediately on creation.
-    unawaited(compute());
-  }
-
-  final String kind;
-  final int? timestamp;
-  final bool isError;
-  final String? summary;
-
-  final RemoteDiagnosticsNode? node;
-  String? _details;
-  Future<String> Function()? detailsComputer;
-
-  static const prettyPrinter = JsonEncoder.withIndent('  ');
-
-  String? get details => _details;
-
-  ValueListenable<bool> get detailsComputed => _detailsComputed;
-  final _detailsComputed = ValueNotifier<bool>(false);
-
-  Future<void> compute() async {
-    if (!detailsComputed.value) {
-      if (detailsComputer != null) {
-        _details = await detailsComputer!();
-      }
-      detailsComputer = null;
-      _detailsComputed.value = true;
-    }
-  }
-
-  String? prettyPrinted() {
-    if (!detailsComputed.value) {
-      return details;
-    }
-
-    try {
-      return prettyPrinter
-          .convert(jsonDecode(details!))
-          .replaceAll(r'\n', '\n');
-    } catch (_) {
-      return details;
-    }
-  }
-
-  String asLogDetails() {
-    return !detailsComputed.value ? '<fetching>' : prettyPrinted() ?? '';
-  }
-
-  @override
-  bool matchesSearchToken(RegExp regExpSearch) {
-    return kind.caseInsensitiveContains(regExpSearch) ||
-        (summary?.caseInsensitiveContains(regExpSearch) == true) ||
-        (details?.caseInsensitiveContains(regExpSearch) == true);
-  }
-
-  @override
-  String toString() => 'LogData($kind, $timestamp)';
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_model.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_model.dart
deleted file mode 100644
index 4a3f740..0000000
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_model.dart
+++ /dev/null
@@ -1,872 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:collection';
-import 'dart:convert';
-
-import 'package:devtools_app_shared/service.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_shared.dart';
-import 'package:flutter/foundation.dart';
-import 'package:logging/logging.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../../service/vm_service_wrapper.dart';
-import '../../../shared/diagnostics/diagnostics_node.dart';
-import '../../../shared/diagnostics/inspector_service.dart';
-import '../../../shared/globals.dart';
-import '../../../shared/primitives/byte_utils.dart';
-import '../../../shared/primitives/message_bus.dart';
-import '../../../shared/primitives/utils.dart';
-import '../../../shared/ui/filter.dart';
-import '../../../shared/utils.dart';
-import '../logging_controller.dart'
-    show
-        FrameInfo,
-        ImageSizesForFrame,
-        NavigationInfo,
-        ServiceExtensionStateChangedInfo;
-import 'logging_controller_v2.dart';
-import 'logging_table_row.dart';
-import 'logging_table_v2.dart';
-
-final _log = Logger('logging_model');
-
-bool _verboseDebugging = false;
-
-Future<String> _retrieveFullStringValue(
-  VmServiceWrapper? service,
-  IsolateRef isolateRef,
-  InstanceRef stringRef,
-) {
-  final fallback = '${stringRef.valueAsString}...';
-  // TODO(kenz): why is service null?
-
-  return service
-          ?.retrieveFullStringValue(
-            isolateRef.id!,
-            stringRef,
-            onUnavailable: (truncatedValue) => fallback,
-          )
-          .then((value) => value ?? fallback) ??
-      Future.value(fallback);
-}
-
-const _gcLogKind = 'gc';
-
-final _verboseFlutterFrameworkLogKinds = <String>{
-  FlutterEvent.firstFrame,
-  FlutterEvent.frameworkInitialization,
-  FlutterEvent.frame,
-  FlutterEvent.imageSizesForFrame,
-};
-
-final _verboseFlutterServiceLogKinds = <String>{
-  FlutterEvent.serviceExtensionStateChanged,
-};
-
-/// Log kinds to show without a summary in the table.
-final _hideSummaryLogKinds = <String>{
-  FlutterEvent.firstFrame,
-  FlutterEvent.frameworkInitialization,
-};
-
-/// A class for holding state and state changes relevant to [LoggingControllerV2]
-/// and [LoggingTableV2].
-///
-/// The [LoggingTableV2] table uses variable height rows. This model caches the
-/// relevant heights and offsets so that the row heights only need to be calculated
-/// once per parent width.
-class LoggingTableModel extends DisposableController
-    with ChangeNotifier, DisposerMixin, FilterControllerMixin<LogDataV2> {
-  LoggingTableModel() {
-    _worker = InterruptableChunkWorker(
-      callback: (index) => getLogHeight(
-        index,
-      ),
-      progressCallback: (progress) => _cacheLoadProgress.value = progress,
-    );
-
-    _retentionLimit = preferences.logging.retentionLimit.value;
-
-    addAutoDisposeListener(
-      preferences.logging.retentionLimit,
-      _onRetentionLimitUpdate,
-    );
-
-    subscribeToFilterChanges();
-
-    _retentionLimit = preferences.logging.retentionLimit.value;
-    addAutoDisposeListener(serviceConnection.serviceManager.connectedState, () {
-      if (serviceConnection.serviceManager.connectedState.value.connected) {
-        _handleConnectionStart(serviceConnection.serviceManager.service!);
-
-        autoDisposeStreamSubscription(
-          serviceConnection.serviceManager.service!.onIsolateEvent
-              .listen((event) {
-            messageBus.addEvent(
-              BusEvent(
-                'debugger',
-                data: event,
-              ),
-            );
-          }),
-        );
-      }
-    });
-    if (serviceConnection.serviceManager.connectedAppInitialized) {
-      _handleConnectionStart(serviceConnection.serviceManager.service!);
-    }
-    _handleBusEvents();
-  }
-
-  /// The toggle filters available for the Logging screen.
-  @override
-  List<ToggleFilter<LogDataV2>> createToggleFilters() => [
-        if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ??
-            true) ...[
-          ToggleFilter<LogDataV2>(
-            name: 'Hide verbose Flutter framework logs (initialization, frame '
-                'times, image sizes)',
-            includeCallback: (log) => !_verboseFlutterFrameworkLogKinds
-                .any((kind) => kind.caseInsensitiveEquals(log.kind)),
-            enabledByDefault: true,
-          ),
-          ToggleFilter<LogDataV2>(
-            name: 'Hide verbose Flutter service logs (service extension state '
-                'changes)',
-            includeCallback: (log) => !_verboseFlutterServiceLogKinds
-                .any((kind) => kind.caseInsensitiveEquals(log.kind)),
-            enabledByDefault: true,
-          ),
-        ],
-        ToggleFilter<LogDataV2>(
-          name: 'Hide garbage collection logs',
-          includeCallback: (log) => !log.kind.caseInsensitiveEquals(_gcLogKind),
-          enabledByDefault: true,
-        ),
-      ];
-
-  static const kindFilterId = 'logging-kind-filter';
-
-  @override
-  Map<String, QueryFilterArgument<LogDataV2>> createQueryFilterArgs() => {
-        kindFilterId: QueryFilterArgument<LogDataV2>(
-          keys: ['kind', 'k'],
-          dataValueProvider: (log) => log.kind,
-          substringMatch: true,
-        ),
-      };
-
-  final _logStatusController = StreamController<String>.broadcast();
-
-  /// A stream of events for the textual description of the log contents.
-  ///
-  /// See also [statusText].
-  Stream<String> get onLogStatusChanged => _logStatusController.stream;
-
-  ObjectGroup get objectGroup =>
-      serviceConnection.consoleService.objectGroup as ObjectGroup;
-
-  final _logs = ListQueue<_LogEntry>();
-
-  /// [FilterControllerMixin] uses [ListValueNotifier] which isn't well optimized to the
-  /// retention limit behavior that [LoggingTableModel] uses. So we use
-  /// [ListQueue] here to facilitate those actions. Then instead of
-  /// using [FilterControllerMixin.filteredLogs] in [FilterControllerMixin.filterData],
-  /// we use [_filteredLogs]. After any changes are done to [_filteredLogs], [notifyListeners]
-  /// must be manually triggered, since the listener behaviour is accomplished by the
-  /// [LoggingTableModel] being a [ChangeNotifier].
-  final _filteredLogs = ListQueue<_FilteredLogEntry>();
-
-  final _selectedLogs = ListQueue<LogDataV2>();
-  late int _retentionLimit;
-
-  late final InterruptableChunkWorker _worker;
-
-  /// Represents the state of reloading the height caches.
-  ///
-  /// When null, then the cache is not loading.
-  /// When double, then the value is represents how much progress has been made.
-  ValueListenable<double?> get cacheLoadProgress => _cacheLoadProgress;
-  final _cacheLoadProgress = ValueNotifier<double?>(null);
-
-  String get statusText {
-    final totalCount = _logs.length;
-    final showingCount = _filteredLogs.length;
-
-    String label;
-
-    label = totalCount == showingCount
-        ? nf.format(totalCount)
-        : 'showing ${nf.format(showingCount)} of ' '${nf.format(totalCount)}';
-
-    label = '$label ${pluralize('event', totalCount)}';
-
-    return label;
-  }
-
-  void _updateStatus() {
-    final label = statusText;
-    _logStatusController.add(label);
-  }
-
-  void _onRetentionLimitUpdate() {
-    _retentionLimit = preferences.logging.retentionLimit.value;
-    while (_logs.length > _retentionLimit) {
-      _trimOneOutOfRetentionLog();
-    }
-    _recalculateOffsets();
-    notifyListeners();
-  }
-
-  void _handleConnectionStart(VmServiceWrapper service) {
-    // Log stdout events.
-    final stdoutHandler = _StdoutEventHandler(this, 'stdout');
-    autoDisposeStreamSubscription(
-      service.onStdoutEventWithHistorySafe.listen(stdoutHandler.handle),
-    );
-
-    // Log stderr events.
-    final stderrHandler = _StdoutEventHandler(this, 'stderr', isError: true);
-    autoDisposeStreamSubscription(
-      service.onStderrEventWithHistorySafe.listen(stderrHandler.handle),
-    );
-
-    // Log GC events.
-    autoDisposeStreamSubscription(service.onGCEvent.listen(_handleGCEvent));
-
-    // Log `dart:developer` `log` events.
-    autoDisposeStreamSubscription(
-      service.onLoggingEventWithHistorySafe.listen(_handleDeveloperLogEvent),
-    );
-
-    // Log Flutter extension events.
-    autoDisposeStreamSubscription(
-      service.onExtensionEventWithHistorySafe.listen(_handleExtensionEvent),
-    );
-  }
-
-  void _handleExtensionEvent(Event e) {
-    if (e.extensionKind == FlutterEvent.frame) {
-      final frame = FrameInfo(e.extensionData!.data);
-
-      final frameId = '#${frame.number}';
-      final frameInfoText =
-          '$frameId ${frame.elapsedMs.toStringAsFixed(1).padLeft(4)}ms ';
-
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.extensionData!.data),
-          e.timestamp,
-          summary: frameInfoText,
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.imageSizesForFrame) {
-      final images = ImageSizesForFrame.from(e.extensionData!.data);
-
-      for (final image in images) {
-        add(
-          LogDataV2(
-            e.extensionKind!.toLowerCase(),
-            jsonEncode(image.json),
-            e.timestamp,
-            summary: image.summary,
-          ),
-        );
-      }
-    } else if (e.extensionKind == FlutterEvent.navigation) {
-      final navInfo = NavigationInfo.from(e.extensionData!.data);
-
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: navInfo.routeDescription,
-        ),
-      );
-    } else if (_hideSummaryLogKinds.contains(e.extensionKind)) {
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: '',
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.serviceExtensionStateChanged) {
-      final changedInfo =
-          ServiceExtensionStateChangedInfo.from(e.extensionData!.data);
-
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: '${changedInfo.extension}: ${changedInfo.value}',
-        ),
-      );
-    } else if (e.extensionKind == FlutterEvent.error) {
-      // TODO(pq): add tests for error extension handling once framework changes
-      // are landed.
-      final node = RemoteDiagnosticsNode(
-        e.extensionData!.data,
-        objectGroup,
-        false,
-        null,
-      );
-      // Workaround the fact that the error objects from the server don't have
-      // style error.
-      node.style = DiagnosticsTreeStyle.error;
-      if (_verboseDebugging) {
-        _log.info('node toStringDeep:######\n${node.toStringDeep()}\n###');
-      }
-
-      final summary = _findFirstSummary(node) ?? node;
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.extensionData!.data),
-          e.timestamp,
-          summary: summary.toDiagnosticsNode().toString(),
-        ),
-      );
-    } else {
-      add(
-        LogDataV2(
-          e.extensionKind!.toLowerCase(),
-          jsonEncode(e.json),
-          e.timestamp,
-          summary: e.json.toString(),
-        ),
-      );
-    }
-  }
-
-  void _handleGCEvent(Event e) {
-    final newSpace = HeapSpace.parse(e.json!['new'])!;
-    final oldSpace = HeapSpace.parse(e.json!['old'])!;
-    final isolateRef = (e.json!['isolate'] as Map).cast<String, Object?>();
-
-    final usedBytes = newSpace.used! + oldSpace.used!;
-    final capacityBytes = newSpace.capacity! + oldSpace.capacity!;
-
-    final time = ((newSpace.time! + oldSpace.time!) * 1000).round();
-
-    final summary = '${isolateRef['name']} • '
-        '${e.json!['reason']} collection in $time ms • '
-        '${printBytes(usedBytes, unit: ByteUnit.mb, includeUnit: true)} used of '
-        '${printBytes(capacityBytes, unit: ByteUnit.mb, includeUnit: true)}';
-
-    final event = <String, Object>{
-      'reason': e.json!['reason'],
-      'new': newSpace.json,
-      'old': oldSpace.json,
-      'isolate': isolateRef,
-    };
-
-    final message = jsonEncode(event);
-    add(LogDataV2('gc', message, e.timestamp, summary: summary));
-  }
-
-  void _handleDeveloperLogEvent(Event e) {
-    final service = serviceConnection.serviceManager.service;
-
-    final logRecord = _LogRecord(e.json!['logRecord']);
-
-    String? loggerName =
-        _valueAsString(InstanceRef.parse(logRecord.loggerName));
-    if (loggerName == null || loggerName.isEmpty) {
-      loggerName = 'log';
-    }
-    final level = logRecord.level;
-    final messageRef = InstanceRef.parse(logRecord.message)!;
-    String? summary = _valueAsString(messageRef);
-    if (messageRef.valueAsStringIsTruncated == true) {
-      summary = '${summary!}...';
-    }
-    final error = InstanceRef.parse(logRecord.error);
-    final stackTrace = InstanceRef.parse(logRecord.stackTrace);
-
-    final details = summary;
-    Future<String> Function()? detailsComputer;
-
-    // If the message string was truncated by the VM, or the error object or
-    // stackTrace objects were non-null, we need to ask the VM for more
-    // information in order to render the log entry. We do this asynchronously
-    // on-demand using the `detailsComputer` Future.
-    if (messageRef.valueAsStringIsTruncated == true ||
-        _isNotNull(error) ||
-        _isNotNull(stackTrace)) {
-      detailsComputer = () async {
-        // Get the full string value of the message.
-        String result =
-            await _retrieveFullStringValue(service, e.isolate!, messageRef);
-
-        // Get information about the error object. Some users of the
-        // dart:developer log call may pass a data payload in the `error`
-        // field, encoded as a json encoded string, so handle that case.
-        if (_isNotNull(error)) {
-          if (error!.valueAsString != null) {
-            final errorString =
-                await _retrieveFullStringValue(service, e.isolate!, error);
-            result += '\n\n$errorString';
-          } else {
-            // Call `toString()` on the error object and display that.
-            final toStringResult = await service!.invoke(
-              e.isolate!.id!,
-              error.id!,
-              'toString',
-              <String>[],
-              disableBreakpoints: true,
-            );
-
-            if (toStringResult is ErrorRef) {
-              final errorString = _valueAsString(error);
-              result += '\n\n$errorString';
-            } else if (toStringResult is InstanceRef) {
-              final str = await _retrieveFullStringValue(
-                service,
-                e.isolate!,
-                toStringResult,
-              );
-              result += '\n\n$str';
-            }
-          }
-        }
-
-        // Get info about the stackTrace object.
-        if (_isNotNull(stackTrace)) {
-          result += '\n\n${_valueAsString(stackTrace)}';
-        }
-
-        return result;
-      };
-    }
-
-    const severeIssue = 1000;
-    final isError = level != null && level >= severeIssue ? true : false;
-
-    add(
-      LogDataV2(
-        loggerName,
-        details,
-        e.timestamp,
-        isError: isError,
-        summary: summary,
-        detailsComputer: detailsComputer,
-      ),
-    );
-  }
-
-  void add(LogDataV2 log) {
-    final newEntry = _LogEntry(log);
-    _logs.add(newEntry);
-    getLogHeight(_logs.length - 1);
-    _trimOneOutOfRetentionLog();
-
-    if (!_filterCallback(newEntry)) {
-      // Only add the log to filtered logs if it matches the filter.
-      return;
-    }
-    _filteredLogs.add(_FilteredLogEntry(newEntry));
-
-    // TODO(danchevalier): Calculate the new offset here
-
-    _updateStatus();
-    notifyListeners();
-  }
-
-  static RemoteDiagnosticsNode? _findFirstSummary(RemoteDiagnosticsNode node) {
-    if (node.level == DiagnosticLevel.summary) {
-      return node;
-    }
-    RemoteDiagnosticsNode? summary;
-    for (final property in node.inlineProperties) {
-      summary = _findFirstSummary(property);
-      if (summary != null) return summary;
-    }
-
-    for (final child in node.childrenNow) {
-      summary = _findFirstSummary(child);
-      if (summary != null) return summary;
-    }
-
-    return null;
-  }
-
-  void _handleBusEvents() {
-    // TODO(jacobr): expose the messageBus for use by vm tests.
-    autoDisposeStreamSubscription(
-      messageBus.onEvent(type: 'reload.end').listen((BusEvent event) {
-        add(
-          LogDataV2(
-            'hot.reload',
-            event.data as String?,
-            DateTime.now().millisecondsSinceEpoch,
-          ),
-        );
-      }),
-    );
-
-    autoDisposeStreamSubscription(
-      messageBus.onEvent(type: 'restart.end').listen((BusEvent event) {
-        add(
-          LogDataV2(
-            'hot.restart',
-            event.data as String?,
-            DateTime.now().millisecondsSinceEpoch,
-          ),
-        );
-      }),
-    );
-
-    // Listen for debugger events.
-    autoDisposeStreamSubscription(
-      messageBus
-          .onEvent()
-          .where(
-            (event) =>
-                event.type == 'debugger' || event.type.startsWith('debugger.'),
-          )
-          .listen(_handleDebuggerEvent),
-    );
-
-    // Listen for DevTools internal events.
-    autoDisposeStreamSubscription(
-      messageBus
-          .onEvent()
-          .where((event) => event.type.startsWith('devtools.'))
-          .listen(_handleDevToolsEvent),
-    );
-  }
-
-  void _handleDebuggerEvent(BusEvent event) {
-    final debuggerEvent = event.data as Event;
-
-    // Filter ServiceExtensionAdded events as they're pretty noisy.
-    if (debuggerEvent.kind == EventKind.kServiceExtensionAdded) {
-      return;
-    }
-
-    add(
-      LogDataV2(
-        event.type,
-        jsonEncode(debuggerEvent.json),
-        debuggerEvent.timestamp,
-        summary: '${debuggerEvent.kind} ${debuggerEvent.isolate!.id}',
-      ),
-    );
-  }
-
-  void _handleDevToolsEvent(BusEvent event) {
-    var details = event.data.toString();
-    String? summary;
-
-    if (details.contains('\n')) {
-      final lines = details.split('\n');
-      summary = lines.first;
-      details = lines.sublist(1).join('\n');
-    }
-
-    add(
-      LogDataV2(
-        event.type,
-        details,
-        DateTime.now().millisecondsSinceEpoch,
-        summary: summary,
-      ),
-    );
-  }
-
-  void _recalculateOffsets() {
-    double runningOffset = 0.0;
-    for (var i = 0; i < _filteredLogs.length; i++) {
-      _filteredLogs.elementAt(i).offset = runningOffset;
-      runningOffset += getFilteredLogHeight(i);
-    }
-  }
-
-  @override
-  void dispose() {
-    _cacheLoadProgress.dispose();
-    _worker.dispose();
-    super.dispose();
-  }
-
-  @override
-  void filterData(Filter<LogDataV2> filter) {
-    super.filterData(filter);
-
-    _filteredLogs
-      ..clear()
-      ..addAll(
-        _logs.where(_filterCallback).map((e) => _FilteredLogEntry(e)).toList(),
-      );
-
-    _recalculateOffsets();
-
-    _updateStatus();
-
-    notifyListeners();
-  }
-
-  bool _filterCallback(_LogEntry entry) {
-    final filter = activeFilter.value;
-
-    final log = entry.log;
-    final filteredOutByToggleFilters = filter.toggleFilters.any(
-      (toggleFilter) =>
-          toggleFilter.enabled.value && !toggleFilter.includeCallback(log),
-    );
-    if (filteredOutByToggleFilters) return false;
-
-    final queryFilter = filter.queryFilter;
-    if (!queryFilter.isEmpty) {
-      final filteredOutByQueryFilterArgument = queryFilter
-          .filterArguments.values
-          .any((argument) => !argument.matchesValue(log));
-      if (filteredOutByQueryFilterArgument) return false;
-
-      if (filter.queryFilter.substringExpressions.isNotEmpty) {
-        for (final substring in filter.queryFilter.substringExpressions) {
-          final matchesKind = log.kind.caseInsensitiveContains(substring);
-          if (matchesKind) return true;
-
-          final matchesSummary = log.summary != null &&
-              log.summary!.caseInsensitiveContains(substring);
-          if (matchesSummary) return true;
-
-          final matchesDetails = log.details != null &&
-              log.details!.caseInsensitiveContains(substring);
-          if (matchesDetails) return true;
-        }
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  double get tableWidth => _tableWidth;
-
-  /// Update the width of the table.
-  ///
-  /// If different from the last width, this will flush all of the calculated heights, and recalculate their heights
-  /// in the background.
-  set tableWidth(double width) {
-    if (width != _tableWidth) {
-      _tableWidth = width;
-      for (final log in _logs) {
-        log.height = null;
-      }
-      for (final log in _filteredLogs) {
-        log.offset = null;
-      }
-      unawaited(_preFetchRowHeights());
-    }
-  }
-
-  /// Get the filtered log at [index].
-  LogDataV2 filteredLogAt(int index) =>
-      _filteredLogs.elementAt(index).logEntry.log;
-
-  double _tableWidth = 0.0;
-
-  /// The total number of logs being held by the [LoggingTableModel].
-  int get logCount => _logs.length;
-
-  /// The number of filtered logs.
-  int get filteredLogCount => _filteredLogs.length;
-
-  /// The number of selected logs.
-  int get selectedLogCount => _selectedLogs.length;
-
-  /// Add a log to the list of tracked logs.
-
-  void _trimOneOutOfRetentionLog() {
-    if (_logs.length > _retentionLimit) {
-      if (identical(_logs.first.log, _filteredLogs.first.logEntry.log)) {
-        // Remove a filtered log if it is about to go out of retention.
-        _filteredLogs.removeFirst();
-      }
-
-      // Remove the log that has just gone out of retention.
-      _logs.removeFirst();
-    }
-  }
-
-  /// Clears all of the logs from the model.
-  void clear() {
-    _logs.clear();
-    _filteredLogs.clear();
-    notifyListeners();
-  }
-
-  /// Get the offset of a filtered log, at [index], from the top of the list of filtered logs.
-  double filteredLogOffsetAt(int _) {
-    throw Exception('Implement this when needed');
-  }
-
-  double getLogHeight(int index) {
-    final entry = _logs.elementAt(index);
-    final cachedHeight = entry.height;
-    if (cachedHeight != null) return cachedHeight;
-    return entry.height ??= LoggingTableRow.estimateRowHeight(
-      entry.log,
-      _tableWidth,
-    );
-  }
-
-  /// Get the height of a filtered Log at [index].
-  double getFilteredLogHeight(int index) {
-    final filteredLog = _filteredLogs.elementAt(index);
-    final cachedHeight = filteredLog.logEntry.height;
-    if (cachedHeight != null) return cachedHeight;
-
-    return filteredLog.logEntry.height ??= LoggingTableRow.estimateRowHeight(
-      filteredLog.logEntry.log,
-      _tableWidth,
-    );
-  }
-
-  Future<bool> _preFetchRowHeights() async {
-    final didComplete = await _worker.doWork(_logs.length);
-    if (didComplete) {
-      _cacheLoadProgress.value = null;
-    }
-    _recalculateOffsets();
-    return didComplete;
-  }
-}
-
-extension type _LogRecord(Map<String, dynamic> json) {
-  int? get level => json['level'];
-
-  Map<String, Object?> get loggerName => json['loggerName'];
-
-  Map<String, Object?> get message => json['message'];
-
-  Map<String, Object?> get error => json['error'];
-
-  Map<String, Object?> get stackTrace => json['stackTrace'];
-}
-
-/// Receive and log stdout / stderr events from the VM.
-///
-/// This class buffers the events for up to 1ms. This is in order to combine a
-/// stdout message and its newline. Currently, `foo\n` is sent as two VM events;
-/// we wait for up to 1ms when we get the `foo` event, to see if the next event
-/// is a single newline. If so, we add the newline to the previous log message.
-class _StdoutEventHandler {
-  _StdoutEventHandler(
-    this.loggingModel,
-    this.name, {
-    this.isError = false,
-  });
-
-  final LoggingTableModel loggingModel;
-  final String name;
-  final bool isError;
-
-  LogDataV2? buffer;
-  Timer? timer;
-
-  void handle(Event e) {
-    final message = decodeBase64(e.bytes!);
-
-    if (buffer != null) {
-      timer?.cancel();
-
-      if (message == '\n') {
-        loggingModel.add(
-          LogDataV2(
-            buffer!.kind,
-            buffer!.details! + message,
-            buffer!.timestamp,
-            summary: buffer!.summary! + message,
-            isError: buffer!.isError,
-          ),
-        );
-        buffer = null;
-        return;
-      }
-
-      loggingModel.add(buffer!);
-      buffer = null;
-    }
-
-    const maxLength = 200;
-
-    String summary = message;
-    if (message.length > maxLength) {
-      summary = message.substring(0, maxLength);
-    }
-
-    final data = LogDataV2(
-      name,
-      message,
-      e.timestamp,
-      summary: summary,
-      isError: isError,
-    );
-
-    if (message == '\n') {
-      loggingModel.add(data);
-    } else {
-      buffer = data;
-      timer = Timer(const Duration(milliseconds: 1), () {
-        loggingModel.add(buffer!);
-        buffer = null;
-      });
-    }
-  }
-}
-
-bool _isNotNull(InstanceRef? serviceRef) {
-  return serviceRef != null && serviceRef.kind != 'Null';
-}
-
-String? _valueAsString(InstanceRef? ref) {
-  if (ref == null) {
-    return null;
-  }
-
-  if (ref.valueAsString == null) {
-    return ref.valueAsString;
-  }
-
-  return ref.valueAsStringIsTruncated == true
-      ? '${ref.valueAsString}...'
-      : ref.valueAsString;
-}
-
-/// A class for holding a [LogDataV2] and its current estimated [height].
-///
-/// The [log] and its [height] have similar lifecycles, so it is helpful to keep
-/// them tied together.
-class _LogEntry {
-  _LogEntry(this.log);
-  final LogDataV2 log;
-
-  /// The current calculated height [log].
-  double? height;
-}
-
-/// A class for holding a [logEntry] and its [offset] from the top of a list of
-/// filtered entries.
-///
-/// The [logEntry] and its [offset] have similar lifecycles, so it is helpful to keep
-/// them tied together.
-class _FilteredLogEntry {
-  _FilteredLogEntry(this.logEntry);
-
-  final _LogEntry logEntry;
-
-  /// The offset of this log entry in a view.
-  double? offset;
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_screen_v2.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_screen_v2.dart
deleted file mode 100644
index 7572a97..0000000
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_screen_v2.dart
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
-
-import '../../../service/service_extension_widgets.dart';
-import '../../../shared/analytics/analytics.dart' as ga;
-import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/screen.dart';
-import '../../../shared/utils.dart';
-import 'logging_controller_v2.dart';
-import 'logging_table_v2.dart';
-
-/// Presents logs from the connected app.
-class LoggingScreenV2 extends Screen {
-  LoggingScreenV2() : super.fromMetaData(ScreenMetaData.logging);
-
-  static final id = ScreenMetaData.logging.id;
-
-  @override
-  String get docPageId => screenId;
-
-  @override
-  Widget buildScreenBody(BuildContext context) => const LoggingScreenBodyV2();
-
-  @override
-  Widget buildStatus(BuildContext context) {
-    final controller = Provider.of<LoggingControllerV2>(context);
-
-    return StreamBuilder<String>(
-      initialData: controller.statusText,
-      stream: controller.onLogStatusChanged,
-      builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
-        return Text(snapshot.data ?? '');
-      },
-    );
-  }
-}
-
-class LoggingScreenBodyV2 extends StatefulWidget {
-  const LoggingScreenBodyV2({super.key});
-
-  @override
-  State<LoggingScreenBodyV2> createState() => _LoggingScreenBodyV2State();
-}
-
-class _LoggingScreenBodyV2State extends State<LoggingScreenBodyV2>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<LoggingControllerV2, LoggingScreenBodyV2> {
-  List<String> items = [];
-  late List<LogDataV2> filteredLogs;
-  @override
-  void initState() {
-    super.initState();
-    ga.screen(gac.logging);
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    if (!initController()) return;
-
-    cancelListeners();
-
-    filteredLogs = controller.loggingModel.filteredData.value;
-    addAutoDisposeListener(controller.loggingModel.filteredData, () {
-      setState(() {
-        filteredLogs = controller.loggingModel.filteredData.value;
-      });
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return LoggingTableV2(
-      model: controller.loggingModel,
-    );
-  }
-}
-
-class LoggingSettingsDialogV2 extends StatelessWidget {
-  const LoggingSettingsDialogV2({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    return DevToolsDialog(
-      title: const DialogTitleText('Logging Settings'),
-      content: Column(
-        mainAxisSize: MainAxisSize.min,
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: [
-          ...dialogSubHeader(
-            theme,
-            'General',
-          ),
-          const StructuredErrorsToggle(),
-        ],
-      ),
-      actions: const [
-        DialogCloseButton(),
-      ],
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_row.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_row.dart
deleted file mode 100644
index 03b0f43..0000000
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_row.dart
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:convert';
-import 'dart:math';
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:flutter/material.dart';
-import 'package:intl/intl.dart';
-
-import '../../../shared/globals.dart';
-import '../../../shared/ui/utils.dart';
-import 'logging_controller_v2.dart';
-
-final loggingTableTimeFormat = DateFormat('HH:mm:ss.SSS');
-
-class LoggingTableRow extends StatefulWidget {
-  const LoggingTableRow({
-    super.key,
-    required this.index,
-    required this.data,
-    required this.isSelected,
-  });
-
-  final int index;
-
-  final LogDataV2 data;
-
-  final bool isSelected;
-
-  static TextStyle get metadataStyle {
-    final currentContext = navigatorKey.currentContext;
-    assert(
-      currentContext != null,
-      'LoggingTableRow.metadataStyle requires a valid navigatorKey to be set. If this assertion is hit in tests then make sure to `wrap()` the widget being pumped.',
-    );
-    return Theme.of(currentContext!).subtleTextStyle;
-  }
-
-  static TextStyle get detailsStyle =>
-      Theme.of(navigatorKey.currentContext!).regularTextStyle;
-
-  /// All of the metatadata chips that can be visible for this [data] entry.
-  @visibleForTesting
-  static List<MetadataChip> metadataChips(LogDataV2 data, double maxWidth) {
-    String? elapsedFrameTimeAsString;
-    try {
-      final int micros = (jsonDecode(data.details!) as Map)['elapsed'];
-      elapsedFrameTimeAsString = (micros * 3.0 / 1000.0).toString();
-    } catch (e) {
-      // Ignore exception; [elapsedFrameTimeAsString] will be null.
-    }
-
-    return [
-      if (data.timestamp != null)
-        WhenMetaDataChip(
-          data: data,
-          maxWidth: maxWidth,
-        ),
-      KindMetaDataChip(
-        data: data,
-        maxWidth: maxWidth,
-      ),
-      if (elapsedFrameTimeAsString != null)
-        FrameElapsedMetaDataChip(
-          data: data,
-          maxWidth: maxWidth,
-          elapsedTimeDisplay: elapsedFrameTimeAsString,
-        ),
-    ];
-  }
-
-  @override
-  State<LoggingTableRow> createState() => _LoggingTableRowState();
-
-  static final _padding = scaleByFontFactor(8.0);
-
-  /// Estimates the height of the row, including the details section and all of the metadata chips.
-  static double estimateRowHeight(
-    LogDataV2 log,
-    double width,
-  ) {
-    final text = log.asLogDetails();
-    final maxWidth = max(0.0, width - _padding * 2);
-
-    final row1Height = calculateTextSpanHeight(
-      TextSpan(text: text, style: detailsStyle),
-      maxWidth: maxWidth,
-    );
-
-    final row2Height = estimateMetaDataWrapHeight(log, maxWidth);
-
-    return row1Height + row2Height + _padding * 2;
-  }
-
-  /// Estimates the height that the [metadataChips] will occupy if they are
-  /// the children of a [Wrap] widget with a parent of [maxWidth] width.
-  @visibleForTesting
-  static double estimateMetaDataWrapHeight(LogDataV2 data, double maxWidth) {
-    var totalHeight = 0.0;
-    var rowHeight = 0.0;
-    var remainingWidth = maxWidth;
-
-    for (final presentChip in LoggingTableRow.metadataChips(data, maxWidth)) {
-      final chipSize = presentChip.estimateSize();
-      if (chipSize.width > remainingWidth) {
-        // The chip does not fit so add it to a new row
-        totalHeight += rowHeight;
-        rowHeight = 0.0;
-        remainingWidth = maxWidth;
-      }
-
-      // The Chip fits so it will stay in this row.
-      rowHeight = max(rowHeight, chipSize.height);
-      remainingWidth -= chipSize.width;
-    }
-
-    totalHeight += rowHeight;
-    return totalHeight;
-  }
-}
-
-class _LoggingTableRowState extends State<LoggingTableRow> {
-  @override
-  Widget build(BuildContext context) {
-    final colorScheme = Theme.of(context).colorScheme;
-    final color = widget.isSelected
-        ? colorScheme.selectedRowBackgroundColor
-        : alternatingColorForIndex(
-            widget.index,
-            colorScheme,
-          );
-
-    return Container(
-      color: color,
-      child: ValueListenableBuilder<bool>(
-        valueListenable: widget.data.detailsComputed,
-        builder: (context, _, __) {
-          return Padding(
-            padding: EdgeInsets.all(LoggingTableRow._padding),
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              mainAxisSize: MainAxisSize.min,
-              children: [
-                RichText(
-                  text: TextSpan(
-                    text: widget.data.asLogDetails(),
-                    style: LoggingTableRow.detailsStyle,
-                  ),
-                ),
-                LayoutBuilder(
-                  builder: (context, constraints) {
-                    return Wrap(
-                      children: LoggingTableRow.metadataChips(
-                        widget.data,
-                        constraints.maxWidth,
-                      ),
-                    );
-                  },
-                ),
-              ],
-            ),
-          );
-        },
-      ),
-    );
-  }
-}
-
-@visibleForTesting
-abstract class MetadataChip extends StatelessWidget {
-  const MetadataChip({
-    super.key,
-    required this.data,
-    required this.maxWidth,
-    required this.icon,
-    required this.text,
-  });
-
-  final LogDataV2 data;
-  final double maxWidth;
-  final IconData icon;
-  final String text;
-
-  static const padding = defaultSpacing;
-
-  /// Estimates the size of this single metadata chip.
-  ///
-  /// If the [build] method is changed then this may need to be updated
-  Size estimateSize() {
-    final maxWidthInsidePadding = maxWidth - padding * 2;
-    final iconSize = Size.square(tooltipIconSize);
-    final textSize = calculateTextSpanSize(
-      _buildValueText(),
-      maxWidth: maxWidthInsidePadding,
-    );
-    return Size(
-      iconSize.width + defaultSpacing + textSize.width + padding * 2,
-      max(iconSize.height, textSize.height) + padding * 2,
-    );
-  }
-
-  /// If this build method is changed then you may need to modify [estimateSize()]
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      constraints: BoxConstraints(maxWidth: maxWidth),
-      padding: const EdgeInsets.all(padding),
-      child: Row(
-        mainAxisSize: MainAxisSize.min,
-        children: [
-          Icon(
-            icon,
-            size: tooltipIconSize,
-          ),
-          const SizedBox(width: defaultSpacing),
-          RichText(
-            text: _buildValueText(),
-          ),
-        ],
-      ),
-    );
-  }
-
-  TextSpan _buildValueText() {
-    return TextSpan(
-      text: text,
-      style: LoggingTableRow.metadataStyle,
-    );
-  }
-}
-
-@visibleForTesting
-class WhenMetaDataChip extends MetadataChip {
-  WhenMetaDataChip({
-    super.key,
-    required super.data,
-    required super.maxWidth,
-  }) : super(
-          icon: Icons.punch_clock,
-          text: data.timestamp == null
-              ? ''
-              : loggingTableTimeFormat
-                  .format(DateTime.fromMillisecondsSinceEpoch(data.timestamp!)),
-        );
-}
-
-@visibleForTesting
-class KindMetaDataChip extends MetadataChip {
-  KindMetaDataChip({
-    super.key,
-    required super.data,
-    required super.maxWidth,
-  }) : super(icon: Icons.type_specimen, text: data.kind);
-}
-
-@visibleForTesting
-class FrameElapsedMetaDataChip extends MetadataChip {
-  const FrameElapsedMetaDataChip({
-    super.key,
-    required super.data,
-    required super.maxWidth,
-    required String elapsedTimeDisplay,
-  }) : super(icon: Icons.timer, text: elapsedTimeDisplay);
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_v2.dart b/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_v2.dart
deleted file mode 100644
index e7e7524..0000000
--- a/packages/devtools_app/lib/src/screens/logging/logging_screen_v2/logging_table_v2.dart
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:flutter/material.dart';
-
-import '../../../service/service_extension_widgets.dart';
-import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/common_widgets.dart';
-import '../../../shared/globals.dart';
-import '../../../shared/ui/filter.dart';
-import '../../../shared/utils.dart';
-import 'logging_controller_v2.dart';
-import 'logging_model.dart';
-import 'logging_table_row.dart';
-
-/// A Widget for displaying logs with line wrapping, along with log metadata.
-class LoggingTableV2 extends StatefulWidget {
-  // TODO(danchevalier): Use SearchControllerMixin and FilterControllerMixin.
-  const LoggingTableV2({super.key, required this.model});
-
-  final LoggingTableModel model;
-
-  @override
-  State<LoggingTableV2> createState() => _LoggingTableV2State();
-}
-
-class _LoggingTableV2State extends State<LoggingTableV2>
-    with ProvidedControllerMixin<LoggingControllerV2, LoggingTableV2> {
-  final selections = <int>{};
-  final cachedOffets = <int, double>{};
-  String lastSearch = '';
-  double maxWidth = 0.0;
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    if (!initController()) return;
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      children: [
-        Row(
-          children: [
-            Expanded(
-              child: DevToolsClearableTextField(
-                labelText: 'Search', // TODO(danchevalier): use SearchField
-              ),
-            ),
-            const SizedBox(width: defaultSpacing),
-            Expanded(
-              child: StandaloneFilterField<LogDataV2>(
-                controller: widget.model,
-              ),
-            ), // TODO for some reason the controller isn't hooking up correctly to the one over in the model. It is not getting notified of the changes?
-            const SizedBox(width: defaultSpacing),
-            SettingsOutlinedButton(
-              gaScreen: gac.logging,
-              gaSelection: gac.loggingSettings,
-              tooltip: 'Logging Settings',
-              onPressed: () {
-                unawaited(
-                  showDialog(
-                    context: context,
-                    builder: (context) => const LoggingSettingsDialogV2(),
-                  ),
-                );
-              },
-            ),
-          ],
-        ),
-        const SizedBox(height: denseSpacing),
-        Expanded(
-          child: _LoggingTableProgress(
-            model: widget.model,
-          ),
-        ),
-      ],
-    );
-  }
-}
-
-class _LoggingTableProgress extends StatefulWidget {
-  const _LoggingTableProgress({
-    required this.model,
-  });
-
-  final LoggingTableModel model;
-
-  @override
-  State<_LoggingTableProgress> createState() => _LoggingTableProgressState();
-}
-
-class _LoggingTableProgressState extends State<_LoggingTableProgress> {
-  static const _millisecondsUntilCacheProgressShows = 500;
-  static const _millisecondsUntilCacheProgressHelperShows = 2000;
-
-  final _progressStopwatch = Stopwatch();
-
-  @override
-  Widget build(BuildContext context) {
-    return LayoutBuilder(
-      builder: (context, constraints) {
-        widget.model.tableWidth = constraints.maxWidth;
-        _progressStopwatch.reset();
-        return ValueListenableBuilder(
-          valueListenable: widget.model.cacheLoadProgress,
-          builder: (context, cacheLoadProgress, _) {
-            if (cacheLoadProgress != null) {
-              double progress = cacheLoadProgress;
-              if (!_progressStopwatch.isRunning) {
-                _progressStopwatch.start();
-              }
-
-              if (_progressStopwatch.elapsedMilliseconds <
-                  _millisecondsUntilCacheProgressShows) {
-                progress = 0.0;
-              }
-
-              return Center(
-                child: Column(
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  children: [
-                    const Text(
-                      'Resizing... this will only take a moment.',
-                    ),
-                    const SizedBox(height: defaultSpacing),
-                    SizedBox(
-                      width: defaultLinearProgressIndicatorWidth,
-                      height: defaultLinearProgressIndicatorHeight,
-                      child: LinearProgressIndicator(
-                        value: progress,
-                      ),
-                    ),
-                    const SizedBox(height: defaultSpacing),
-                    if (_progressStopwatch.elapsedMilliseconds >
-                        _millisecondsUntilCacheProgressHelperShows)
-                      const Text(
-                        'To make this process faster, reduce the "Log Retention" setting.',
-                      ),
-                  ],
-                ),
-              );
-            } else {
-              _progressStopwatch
-                ..stop()
-                ..reset();
-              return _LoggingTableRows(model: widget.model);
-            }
-          },
-        );
-      },
-    );
-  }
-
-  @override
-  void dispose() {
-    _progressStopwatch.stop();
-    super.dispose();
-  }
-}
-
-class _LoggingTableRows extends StatefulWidget {
-  const _LoggingTableRows({required this.model});
-
-  final LoggingTableModel model;
-
-  @override
-  State<_LoggingTableRows> createState() => _LoggingTableRowsState();
-}
-
-class _LoggingTableRowsState extends State<_LoggingTableRows>
-    with AutoDisposeMixin {
-  late final _verticalController = ScrollController();
-
-  @override
-  void initState() {
-    super.initState();
-    addAutoDisposeListener(widget.model);
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Scrollbar(
-      thumbVisibility: true,
-      controller: _verticalController,
-      child: CustomScrollView(
-        controller: _verticalController,
-        slivers: <Widget>[
-          SliverVariedExtentList.builder(
-            itemCount: widget.model.filteredLogCount,
-            itemBuilder: (context, index) {
-              return LoggingTableRow(
-                index: index,
-                data: widget.model.filteredLogAt(index),
-                isSelected: false,
-              );
-            },
-            itemExtentBuilder: (index, _) =>
-                widget.model.getFilteredLogHeight(index),
-          ),
-        ],
-      ),
-    );
-  }
-}
-
-class LoggingSettingsDialogV2 extends StatefulWidget {
-  const LoggingSettingsDialogV2({super.key});
-
-  @override
-  State<LoggingSettingsDialogV2> createState() =>
-      _LoggingSettingsDialogV2State();
-}
-
-class _LoggingSettingsDialogV2State extends State<LoggingSettingsDialogV2> {
-  late final ValueNotifier<int> newRetentionLimit;
-
-  @override
-  void initState() {
-    super.initState();
-    newRetentionLimit =
-        ValueNotifier<int>(preferences.logging.retentionLimit.value);
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    return DevToolsDialog(
-      title: const DialogTitleText('Logging Settings'),
-      content: Column(
-        mainAxisSize: MainAxisSize.min,
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: [
-          ...dialogSubHeader(
-            theme,
-            'General',
-          ),
-          const StructuredErrorsToggle(),
-          const SizedBox(height: defaultSpacing),
-          PositiveIntegerSetting(
-            title: preferences.logging.retentionLimitTitle,
-            subTitle: 'Used to limit the number of log messages retained.',
-            notifier: newRetentionLimit,
-          ),
-        ],
-      ),
-      actions: [
-        DialogApplyButton(
-          onPressed: () {
-            preferences.logging.retentionLimit.value = newRetentionLimit.value;
-          },
-        ),
-        const DialogCloseButton(),
-      ],
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/logging/metadata.dart b/packages/devtools_app/lib/src/screens/logging/metadata.dart
new file mode 100644
index 0000000..925b57a
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/logging/metadata.dart
@@ -0,0 +1,334 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+
+import '../../shared/primitives/utils.dart';
+import '../../shared/ui/icons.dart';
+import 'logging_controller.dart';
+
+class MetadataChips extends StatelessWidget {
+  const MetadataChips({
+    super.key,
+    required this.data,
+    // TODO(kenz): remove maxWidth from these metadata chips once the Logging
+    // V2 code is removed.
+    required this.maxWidth,
+  });
+
+  final LogData data;
+  final double maxWidth;
+
+  @override
+  Widget build(BuildContext context) {
+    final colorScheme = Theme.of(context).colorScheme;
+
+    // Prepare kind chip.
+    final kindIcon = KindMetaDataChip.generateIcon(data.kind);
+    final kindColors = KindMetaDataChip.generateColors(data.kind, colorScheme);
+
+    // Prepare log level chip.
+    final logLevel = LogLevelMetadataChip.generateLogLevel(data.level);
+    final logLevelColors = LogLevelMetadataChip.generateColors(
+      logLevel,
+      colorScheme,
+    );
+    final logLevelChip = LogLevelMetadataChip(
+      level: logLevel,
+      rawLevel: data.level,
+      maxWidth: maxWidth,
+      backgroundColor: logLevelColors.background,
+      foregroundColor: logLevelColors.foreground,
+    );
+
+    // Prepare the isolate chip.
+    Widget? isolateChip;
+    final isolateName = data.isolateRef?.name;
+    if (isolateName != null) {
+      isolateChip = IsolateChip(
+        name: isolateName,
+        id: data.isolateRef?.id,
+        maxWidth: maxWidth,
+        backgroundColor: colorScheme.surface,
+        foregroundColor: colorScheme.onSurface,
+        outlined: true,
+      );
+    }
+
+    // Prepare the zone chip.
+    Widget? zoneChip;
+    final zone = data.zone;
+    final zoneName = zone?.name;
+    if (zoneName != null && !zoneName.caseInsensitiveEquals('null')) {
+      zoneChip = ZoneChip(
+        name: zoneName,
+        identityHashCode: zone!.identityHashCode,
+        maxWidth: maxWidth,
+        backgroundColor: colorScheme.surface,
+        foregroundColor: colorScheme.onSurface,
+        outlined: true,
+      );
+    }
+
+    // Prepare frame time chip.
+    String? elapsedFrameTimeAsString;
+    try {
+      if (data.details != null) {
+        final int? micros = (jsonDecode(data.details!) as Map)['elapsed'];
+        if (micros != null) {
+          elapsedFrameTimeAsString = durationText(
+            Duration(microseconds: micros),
+            unit: DurationDisplayUnit.milliseconds,
+            fractionDigits: 2,
+          );
+        }
+      }
+    } catch (e) {
+      // Ignore exception; [elapsedFrameTimeAsString] will be null.
+    }
+
+    return Wrap(
+      children: [
+        KindMetaDataChip(
+          kind: data.kind,
+          maxWidth: maxWidth,
+          icon: kindIcon.icon,
+          iconAsset: kindIcon.iconAsset,
+          backgroundColor: kindColors.background,
+          foregroundColor: kindColors.foreground,
+        ),
+        logLevelChip,
+        if (elapsedFrameTimeAsString != null)
+          FrameElapsedMetaDataChip(
+            maxWidth: maxWidth,
+            elapsedTimeDisplay: elapsedFrameTimeAsString,
+          ),
+        if (isolateChip != null) isolateChip,
+        if (zoneChip != null) zoneChip,
+      ],
+    );
+  }
+}
+
+abstract class MetadataChip extends StatelessWidget {
+  const MetadataChip({
+    super.key,
+    required this.maxWidth,
+    required this.text,
+    this.tooltip,
+    this.icon,
+    this.iconAsset,
+    this.backgroundColor,
+    this.foregroundColor,
+    this.outlined = false,
+    this.includeLeadingMargin = true,
+  });
+
+  final double maxWidth;
+  final IconData? icon;
+  final String? iconAsset;
+  final String text;
+  final String? tooltip;
+  final Color? backgroundColor;
+  final Color? foregroundColor;
+  final bool outlined;
+  final bool includeLeadingMargin;
+
+  static const horizontalPadding = densePadding;
+  static const verticalPadding = borderPadding;
+  static const iconPadding = densePadding;
+  static const _borderRadius = 4.0;
+  static final _metadataIconSize = scaleByFontFactor(12.0);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+    final backgroundColor =
+        this.backgroundColor ?? theme.colorScheme.secondaryContainer;
+    final foregroundColor =
+        this.foregroundColor ?? theme.colorScheme.onSecondaryContainer;
+
+    return maybeWrapWithTooltip(
+      tooltip: tooltip,
+      child: Container(
+        constraints: BoxConstraints(maxWidth: maxWidth),
+        decoration: BoxDecoration(
+          color: backgroundColor,
+          borderRadius: BorderRadius.circular(_borderRadius),
+          border:
+              outlined
+                  ? Border.all(color: theme.colorScheme.subtleTextColor)
+                  : null,
+        ),
+        margin:
+            includeLeadingMargin
+                ? const EdgeInsets.only(left: denseSpacing)
+                : null,
+        padding: const EdgeInsets.symmetric(
+          horizontal: horizontalPadding,
+          vertical: verticalPadding,
+        ),
+        child: Row(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            if (icon != null || iconAsset != null) ...[
+              DevToolsIcon(
+                icon: icon,
+                iconAsset: iconAsset,
+                size: _metadataIconSize,
+                color: foregroundColor,
+              ),
+              const SizedBox(width: iconPadding),
+            ] else
+              // Include an empty SizedBox to ensure a consistent height for the
+              // chips, regardless of whether the chip includes an icon.
+              SizedBox(height: _metadataIconSize),
+            RichText(
+              text: TextSpan(
+                text: text,
+                style: theme
+                    .regularTextStyleWithColor(foregroundColor)
+                    .copyWith(fontSize: smallFontSize),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+class KindMetaDataChip extends MetadataChip {
+  const KindMetaDataChip({
+    super.key,
+    required String kind,
+    required super.maxWidth,
+    super.icon,
+    super.iconAsset,
+    super.backgroundColor,
+    super.foregroundColor,
+  }) : super(text: kind, includeLeadingMargin: false);
+
+  static ({IconData? icon, String? iconAsset}) generateIcon(String kind) {
+    IconData? kindIcon = Icons.list_rounded;
+    String? kindIconAsset;
+    if (kind == 'stdout' || kind == 'stderr') {
+      kindIcon = Icons.terminal_rounded;
+    } else if (RegExp(r'^flutter\..*$').hasMatch(kind)) {
+      kindIconAsset = 'icons/flutter.png';
+      kindIcon = null;
+    }
+    return (icon: kindIcon, iconAsset: kindIconAsset);
+  }
+
+  static ({Color background, Color foreground}) generateColors(
+    String kind,
+    ColorScheme colorScheme,
+  ) {
+    Color background, foreground;
+    if (kind == 'stderr' || kind.caseInsensitiveEquals(FlutterEvent.error)) {
+      background = colorScheme.errorContainer;
+      foreground = colorScheme.onErrorContainer;
+    } else if (kind == 'stdout') {
+      background = colorScheme.surfaceContainerHighest;
+      foreground = colorScheme.onSurfaceVariant;
+    } else if (kind.startsWith('flutter')) {
+      background = colorScheme.primaryContainer;
+      foreground = colorScheme.onPrimaryContainer;
+    } else {
+      background = colorScheme.secondaryContainer;
+      foreground = colorScheme.onSecondaryContainer;
+    }
+    return (background: background, foreground: foreground);
+  }
+}
+
+class FrameElapsedMetaDataChip extends MetadataChip {
+  const FrameElapsedMetaDataChip({
+    super.key,
+    required super.maxWidth,
+    required String elapsedTimeDisplay,
+  }) : super(icon: Icons.timer, text: elapsedTimeDisplay);
+}
+
+class LogLevelMetadataChip extends MetadataChip {
+  LogLevelMetadataChip({
+    super.key,
+    required Level level,
+    required int rawLevel,
+    required super.maxWidth,
+    super.backgroundColor,
+    super.foregroundColor,
+  }) : super(text: 'Level.${level.name} ($rawLevel)');
+
+  static Level generateLogLevel(int rawLevel) {
+    var level = Level.FINEST;
+    for (final l in Level.LEVELS) {
+      if (rawLevel >= l.value) {
+        level = l;
+      }
+    }
+    return level;
+  }
+
+  static ({Color background, Color foreground}) generateColors(
+    Level level,
+    ColorScheme colorScheme,
+  ) {
+    Color background, foreground;
+    if (level >= Level.SHOUT) {
+      background = colorScheme.errorContainer.darken(0.2);
+      foreground = colorScheme.onErrorContainer;
+    } else if (level >= Level.SEVERE) {
+      background = colorScheme.errorContainer;
+      foreground = colorScheme.onErrorContainer;
+    } else if (level >= Level.WARNING) {
+      background = colorScheme.warningContainer;
+      foreground = colorScheme.onWarningContainer;
+    } else if (level >= Level.INFO) {
+      background = colorScheme.secondaryContainer;
+      foreground = colorScheme.onSecondaryContainer;
+    } else {
+      // This includes Level.CONFIG, Level.FINE, Level.FINER, Level.FINEST.
+      background = colorScheme.surfaceContainerHighest;
+      foreground = colorScheme.onSurfaceVariant;
+    }
+    return (background: background, foreground: foreground);
+  }
+}
+
+class IsolateChip extends MetadataChip {
+  const IsolateChip({
+    super.key,
+    required String name,
+    required String? id,
+    required super.maxWidth,
+    super.backgroundColor,
+    super.foregroundColor,
+    super.outlined = false,
+  }) : super(text: 'isolate: $name', tooltip: id);
+}
+
+class ZoneChip extends MetadataChip {
+  const ZoneChip({
+    super.key,
+    required String name,
+    required int? identityHashCode,
+    required super.maxWidth,
+    super.backgroundColor,
+    super.foregroundColor,
+    super.outlined = false,
+  }) : super(
+         text: name,
+         tooltip:
+             identityHashCode != null
+                 ? 'Zone identityHashCode: $identityHashCode'
+                 : null,
+       );
+}
diff --git a/packages/devtools_app/lib/src/screens/logging/shared/constants.dart b/packages/devtools_app/lib/src/screens/logging/shared/constants.dart
deleted file mode 100644
index 20c0280..0000000
--- a/packages/devtools_app/lib/src/screens/logging/shared/constants.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const loggingMinVerboseWidth = 650.0;
diff --git a/packages/devtools_app/lib/src/screens/memory/framework/memory_controller.dart b/packages/devtools_app/lib/src/screens/memory/framework/memory_controller.dart
index 79457d1..fea2a1f 100644
--- a/packages/devtools_app/lib/src/screens/memory/framework/memory_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/framework/memory_controller.dart
@@ -7,11 +7,11 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/foundation.dart';
 
+import '../../../shared/framework/screen.dart';
 import '../../../shared/globals.dart';
 import '../../../shared/memory/class_name.dart';
 import '../../../shared/memory/heap_graph_loader.dart';
-import '../../../shared/offline_data.dart';
-import '../../../shared/screen.dart';
+import '../../../shared/offline/offline_data.dart';
 import '../panes/chart/controller/chart_data.dart';
 import '../panes/chart/controller/chart_pane_controller.dart';
 import '../panes/diff/controller/diff_pane_controller.dart';
@@ -34,10 +34,7 @@
     @visibleForTesting ProfilePaneController? connectedProfile,
   }) {
     unawaited(
-      _init(
-        connectedDiff: connectedDiff,
-        connectedProfile: connectedProfile,
-      ),
+      _init(connectedDiff: connectedDiff, connectedProfile: connectedProfile),
     );
   }
 
@@ -115,11 +112,13 @@
 
     chart = MemoryChartPaneController(data: offlineData?.chart ?? ChartData());
 
-    final rootPackage = isConnected
-        ? serviceConnection.serviceManager.rootInfoNow().package!
-        : null;
+    final rootPackage =
+        isConnected
+            ? serviceConnection.serviceManager.rootInfoNow().package!
+            : null;
 
-    diff = diffPaneController ??
+    diff =
+        diffPaneController ??
         offlineData?.diff ??
         DiffPaneController(
           loader:
@@ -127,7 +126,8 @@
           rootPackage: rootPackage,
         );
 
-    profile = profilePaneController ??
+    profile =
+        profilePaneController ??
         offlineData?.profile ??
         ProfilePaneController(rootPackage: rootPackage!);
 
@@ -172,9 +172,7 @@
     });
 
     diff.core.classFilter.addListener(() {
-      theProfile.setFilter(
-        diff.core.classFilter.value,
-      );
+      theProfile.setFilter(diff.core.classFilter.value);
     });
   }
 
@@ -186,7 +184,11 @@
     try {
       await serviceConnection.serviceManager.service!.getAllocationProfile(
         (serviceConnection
-            .serviceManager.isolateManager.selectedIsolate.value?.id)!,
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value
+            ?.id)!,
         gc: true,
       );
       chart.data.timeline.addGCEvent();
diff --git a/packages/devtools_app/lib/src/screens/memory/framework/memory_screen.dart b/packages/devtools_app/lib/src/screens/memory/framework/memory_screen.dart
index 399d155..ced6107 100644
--- a/packages/devtools_app/lib/src/screens/memory/framework/memory_screen.dart
+++ b/packages/devtools_app/lib/src/screens/memory/framework/memory_screen.dart
@@ -8,8 +8,8 @@
 import 'package:flutter/material.dart';
 
 import '../../../shared/analytics/analytics.dart' as ga;
+import '../../../shared/framework/screen.dart';
 import '../../../shared/primitives/listenable.dart';
-import '../../../shared/screen.dart';
 import '../panes/diff/controller/diff_pane_controller.dart';
 import '../panes/diff/diff_pane.dart';
 import 'screen_body.dart';
@@ -97,9 +97,7 @@
             roundedTopBorder: false,
             includeTopBorder: false,
           ),
-          Expanded(
-            child: DiffPane(diffController: diffController),
-          ),
+          Expanded(child: DiffPane(diffController: diffController)),
         ],
       ),
     );
diff --git a/packages/devtools_app/lib/src/screens/memory/framework/memory_tabs.dart b/packages/devtools_app/lib/src/screens/memory/framework/memory_tabs.dart
index 9456612..f11fb78 100644
--- a/packages/devtools_app/lib/src/screens/memory/framework/memory_tabs.dart
+++ b/packages/devtools_app/lib/src/screens/memory/framework/memory_tabs.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/common_widgets.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../../../shared/ui/tab.dart';
 import '../panes/diff/diff_pane.dart';
 import '../panes/profile/profile_view.dart';
@@ -39,39 +39,33 @@
   }
 
   TabAndView _diff() => (
-        tab: DevToolsTab.create(
-          key: MemoryScreenKeys.diffTab,
-          gaPrefix: _gaPrefix,
-          tabName: 'Diff Snapshots',
-        ),
-        tabView: KeepAliveWrapper(
-          child: DiffPane(
-            diffController: controller.diff,
-          ),
-        ),
-      );
+    tab: DevToolsTab.create(
+      key: MemoryScreenKeys.diffTab,
+      gaPrefix: _gaPrefix,
+      tabName: 'Diff Snapshots',
+    ),
+    tabView: KeepAliveWrapper(child: DiffPane(diffController: controller.diff)),
+  );
 
   TabAndView _profile() => (
-        tab: DevToolsTab.create(
-          key: MemoryScreenKeys.profileTab,
-          tabName: 'Profile Memory',
-          gaPrefix: _gaPrefix,
-        ),
-        tabView: KeepAliveWrapper(
-          child: AllocationProfileTableView(
-            controller: controller.profile!,
-          ),
-        ),
-      );
+    tab: DevToolsTab.create(
+      key: MemoryScreenKeys.profileTab,
+      tabName: 'Profile Memory',
+      gaPrefix: _gaPrefix,
+    ),
+    tabView: KeepAliveWrapper(
+      child: AllocationProfileTableView(controller: controller.profile!),
+    ),
+  );
 
   TabAndView _trace() => (
-        tab: DevToolsTab.create(
-          key: MemoryScreenKeys.traceTab,
-          tabName: 'Trace Instances',
-          gaPrefix: _gaPrefix,
-        ),
-        tabView: KeepAliveWrapper(
-          child: TracingPane(controller: controller.trace!),
-        ),
-      );
+    tab: DevToolsTab.create(
+      key: MemoryScreenKeys.traceTab,
+      tabName: 'Trace Instances',
+      gaPrefix: _gaPrefix,
+    ),
+    tabView: KeepAliveWrapper(
+      child: TracingPane(controller: controller.trace!),
+    ),
+  );
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/framework/offline_data/offline_data.dart b/packages/devtools_app/lib/src/screens/memory/framework/offline_data/offline_data.dart
index 9016ba0..2fd997f 100644
--- a/packages/devtools_app/lib/src/screens/memory/framework/offline_data/offline_data.dart
+++ b/packages/devtools_app/lib/src/screens/memory/framework/offline_data/offline_data.dart
@@ -12,14 +12,7 @@
 import '../../shared/heap/class_filter.dart';
 
 @visibleForTesting
-enum Json {
-  selectedTab,
-  classFilter,
-  diffData,
-  profileData,
-  chartData,
-  trace;
-}
+enum Json { selectedTab, classFilter, diffData, profileData, chartData, trace }
 
 class OfflineMemoryData with Serializable {
   OfflineMemoryData(
diff --git a/packages/devtools_app/lib/src/screens/memory/framework/screen_body.dart b/packages/devtools_app/lib/src/screens/memory/framework/screen_body.dart
index 2581327..7639bc5 100644
--- a/packages/devtools_app/lib/src/screens/memory/framework/screen_body.dart
+++ b/packages/devtools_app/lib/src/screens/memory/framework/screen_body.dart
@@ -6,12 +6,12 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
-import '../../../shared/banner_messages.dart';
-import '../../../shared/common_widgets.dart';
+import '../../../shared/framework/screen.dart';
 import '../../../shared/globals.dart';
 import '../../../shared/http/http_service.dart' as http_service;
-import '../../../shared/screen.dart';
-import '../../../shared/utils.dart';
+import '../../../shared/managers/banner_messages.dart';
+import '../../../shared/ui/common_widgets.dart';
+import '../../../shared/utils/utils.dart';
 import '../panes/chart/widgets/chart_pane.dart';
 import '../panes/control/widgets/control_pane.dart';
 import 'memory_controller.dart';
@@ -74,13 +74,8 @@
               onSave: controller.exportData,
             ),
             const SizedBox(height: intermediateSpacing),
-            MemoryChartPane(
-              chart: controller.chart,
-              keyFocusNode: _focusNode,
-            ),
-            Expanded(
-              child: MemoryTabView(controller),
-            ),
+            MemoryChartPane(chart: controller.chart, keyFocusNode: _focusNode),
+            Expanded(child: MemoryTabView(controller)),
           ],
         );
       },
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_connection.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_connection.dart
index db8794e..3022daf 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_connection.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_connection.dart
@@ -6,7 +6,7 @@
 import 'package:flutter/foundation.dart';
 
 import '../../../../../shared/globals.dart';
-import '../../../../../shared/utils.dart';
+import '../../../../../shared/utils/utils.dart';
 import '../../../shared/primitives/memory_timeline.dart';
 import '../data/primitives.dart';
 import 'memory_tracker.dart';
@@ -61,41 +61,42 @@
     });
 
     autoDisposeStreamSubscription(
-      serviceConnection.serviceManager.service!.onExtensionEvent
-          .listen(_memoryTracker.onMemoryData),
+      serviceConnection.serviceManager.service!.onExtensionEvent.listen(
+        _memoryTracker.onMemoryData,
+      ),
     );
 
     autoDisposeStreamSubscription(
-      serviceConnection.serviceManager.service!.onGCEvent
-          .listen(_memoryTracker.onGCEvent),
+      serviceConnection.serviceManager.service!.onGCEvent.listen(
+        _memoryTracker.onGCEvent,
+      ),
     );
 
     autoDisposeStreamSubscription(
-      serviceConnection.serviceManager.service!.onIsolateEvent
-          .listen(_memoryTracker.onIsolateEvent),
+      serviceConnection.serviceManager.service!.onIsolateEvent.listen(
+        _memoryTracker.onIsolateEvent,
+      ),
     );
 
-    _polling = DebounceTimer.periodic(
-      chartUpdateDelay,
-      () async {
-        if (!_isConnected) {
-          _polling?.cancel();
-          return;
-        }
-        try {
-          await _memoryTracker.pollMemory();
-        } catch (e, trace) {
-          // TODO (polina-c): remove after fixing https://github.com/flutter/devtools/issues/7808
-          // and https://github.com/flutter/devtools/issues/7722
-          final isDisconnectionError = e.toString().contains('connection') ||
-              trace.toString().contains('isFlutterApp');
+    _polling = DebounceTimer.periodic(chartUpdateDelay, () async {
+      if (!_isConnected) {
+        _polling?.cancel();
+        return;
+      }
+      try {
+        await _memoryTracker.pollMemory();
+      } catch (e, trace) {
+        // TODO (polina-c): remove after fixing https://github.com/flutter/devtools/issues/7808
+        // and https://github.com/flutter/devtools/issues/7722
+        final isDisconnectionError =
+            e.toString().contains('connection') ||
+            trace.toString().contains('isFlutterApp');
 
-          if (_isConnected && !isDisconnectionError) {
-            rethrow;
-          }
+        if (_isConnected && !isDisconnectionError) {
+          rethrow;
         }
-      },
-    );
+      }
+    });
 
     initialized = true;
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_data.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_data.dart
index 7c79521..a7bbe36 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_data.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_data.dart
@@ -9,12 +9,7 @@
 import '../data/primitives.dart';
 
 @visibleForTesting
-enum Json {
-  isDeviceAndroid,
-  timeline,
-  interval,
-  isLegendVisible;
-}
+enum Json { isDeviceAndroid, timeline, interval, isLegendVisible }
 
 /// Chart data, that should be saved when transferred to offline data mode.
 class ChartData with Serializable {
@@ -23,8 +18,8 @@
     MemoryTimeline? timeline,
     ChartInterval interval = ChartInterval.theDefault,
     bool isLegendVisible = false,
-  })  : _displayInterval = ValueNotifier<ChartInterval>(interval),
-        _isLegendVisible = ValueNotifier<bool>(isLegendVisible) {
+  }) : _displayInterval = ValueNotifier<ChartInterval>(interval),
+       _isLegendVisible = ValueNotifier<bool>(isLegendVisible) {
     this.timeline = timeline ?? MemoryTimeline();
   }
 
@@ -35,7 +30,8 @@
         json[Json.timeline.name],
         MemoryTimeline.fromJson,
       ),
-      interval: ChartInterval.byName(json[Json.interval.name]) ??
+      interval:
+          ChartInterval.byName(json[Json.interval.name]) ??
           ChartInterval.theDefault,
       isLegendVisible: json[Json.isLegendVisible.name] as bool? ?? false,
     );
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_pane_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_pane_controller.dart
index def2bbb..4daab0b 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_pane_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/chart_pane_controller.dart
@@ -72,7 +72,8 @@
     if (!isChartVisible.value) return;
     assert(data.isDeviceAndroid != null || _chartConnection!.initialized);
     data.isDeviceAndroid ??= _chartConnection!.isDeviceAndroid;
-    isAndroidChartVisible.value = data.isDeviceAndroid! &&
+    isAndroidChartVisible.value =
+        data.isDeviceAndroid! &&
         preferences.memory.androidCollectionEnabled.value;
   }
 
@@ -82,10 +83,11 @@
     if (!isChartVisible.value) return;
     if (!offlineDataController.showingOfflineData.value) {
       if (_chartConnection == null) {
-        _chartConnection ??= _chartConnection = ChartVmConnection(
-          data.timeline,
-          isAndroidChartVisible: isAndroidChartVisible,
-        );
+        _chartConnection ??=
+            _chartConnection = ChartVmConnection(
+              data.timeline,
+              isAndroidChartVisible: isAndroidChartVisible,
+            );
         if (serviceConnection.serviceManager.connectedState.value.connected) {
           _chartConnection!.init();
           resume();
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/android_chart_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/android_chart_controller.dart
index cf74319..c30ac02 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/android_chart_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/android_chart_controller.dart
@@ -30,10 +30,7 @@
     this.memoryTimeline, {
     required this.paused,
     List<int> sharedLabels = const <int>[],
-  }) : super(
-          name: 'Android',
-          sharedLabelTimestamps: sharedLabels,
-        ) {
+  }) : super(name: 'Android', sharedLabelTimestamps: sharedLabels) {
     setupData();
 
     addAutoDisposeListener(memoryTimeline.sampleAdded, () {
@@ -276,19 +273,13 @@
     final graphicValue = adb.graphics.toDouble();
     addDataToTrace(
       AndroidTraceName.graphics.index,
-      chart_trace.Data(
-        timestamp,
-        graphicValue,
-      ),
+      chart_trace.Data(timestamp, graphicValue),
     );
 
     final nativeHeapValue = adb.nativeHeap.toDouble();
     addDataToTrace(
       AndroidTraceName.nativeHeap.index,
-      chart_trace.Data(
-        timestamp,
-        nativeHeapValue,
-      ),
+      chart_trace.Data(timestamp, nativeHeapValue),
     );
 
     final javaHeapValue = adb.javaHeap.toDouble();
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/event_chart_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/event_chart_controller.dart
index 768ac39..23e2922 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/event_chart_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/event_chart_controller.dart
@@ -53,12 +53,12 @@
 /// Their y-position is such that the symbols won't overlap.
 class EventChartController extends ChartController {
   EventChartController(this.memoryTimeline, {required this.paused})
-      : super(
-          displayYLabels: false,
-          displayXAxis: false,
-          displayXLabels: false,
-          name: 'Event Pane',
-        ) {
+    : super(
+        displayYLabels: false,
+        displayXAxis: false,
+        displayXLabels: false,
+        name: 'Event Pane',
+      ) {
     setFixedYRange(_Sizes.visibleVm, _Sizes.extensions);
     setupData();
 
@@ -78,10 +78,7 @@
     final chartDataLength = timestampsLength;
     final dataLength = memoryTimeline.data.length;
 
-    final dataRange = memoryTimeline.data.getRange(
-      chartDataLength,
-      dataLength,
-    );
+    final dataRange = memoryTimeline.data.getRange(chartDataLength, dataLength);
 
     dataRange.forEach(addSample);
   }
@@ -258,9 +255,7 @@
       name: _EventsTraceName.gc.toString(),
     );
     assert(gcIndex == _EventsTraceName.gc.index);
-    assert(
-      trace(gcIndex).name == _EventsTraceName.values[gcIndex].toString(),
-    );
+    assert(trace(gcIndex).name == _EventsTraceName.values[gcIndex].toString());
 
     assert(traces.length == _EventsTraceName.values.length);
   }
@@ -290,10 +285,7 @@
 
     // User events snapshot, auto-snapshot, manual GC, are plotted on the top-line
     // of the event pane (visible Events).
-    final data = chart_trace.Data(
-      sample.timestamp,
-      _Sizes.visible,
-    );
+    final data = chart_trace.Data(sample.timestamp, _Sizes.visible);
 
     if (events.isEventGC) {
       // Plot manual requested GC on the visibleEvent Y coordinate.
@@ -312,10 +304,7 @@
 
     if (sample.memoryEventInfo.isEventAllocationAccumulator) {
       final allocationEvent = events.allocationAccumulator!;
-      final data = chart_trace.Data(
-        sample.timestamp,
-        _Sizes.visibleMonitor,
-      );
+      final data = chart_trace.Data(sample.timestamp, _Sizes.visibleMonitor);
       if (allocationEvent.isReset) {
         addDataToTrace(_EventsTraceName.monitorReset.index, data);
       } else if (allocationEvent.isStart) {
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/vm_chart_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/vm_chart_controller.dart
index dd2a3fd..c1ca585 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/vm_chart_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/charts/vm_chart_controller.dart
@@ -26,7 +26,7 @@
 
 class VMChartController extends ChartController {
   VMChartController(this.memoryTimeline, {required this.paused})
-      : super(name: 'VM Memory') {
+    : super(name: 'VM Memory') {
     setupData();
 
     addAutoDisposeListener(memoryTimeline.sampleAdded, () {
@@ -45,10 +45,7 @@
     final chartDataLength = timestampsLength;
     final dataLength = memoryTimeline.data.length;
 
-    final dataRange = memoryTimeline.data.getRange(
-      chartDataLength,
-      dataLength,
-    );
+    final dataRange = memoryTimeline.data.getRange(chartDataLength, dataLength);
 
     dataRange.forEach(addSample);
   }
@@ -109,9 +106,7 @@
       );
 
       final usedIndex = VmTraceName.used.index;
-      assert(
-        trace(usedIndex).name == VmTraceName.values[usedIndex].toString(),
-      );
+      assert(trace(usedIndex).name == VmTraceName.values[usedIndex].toString());
 
       final capacityIndex = VmTraceName.capacity.index;
       assert(
@@ -120,9 +115,7 @@
       );
 
       final rSSIndex = VmTraceName.rSS.index;
-      assert(
-        trace(rSSIndex).name == VmTraceName.values[rSSIndex].toString(),
-      );
+      assert(trace(rSSIndex).name == VmTraceName.values[rSSIndex].toString());
 
       final rasterLayerIndex = VmTraceName.rasterLayer.index;
       assert(
@@ -166,9 +159,7 @@
       name: VmTraceName.used.toString(),
     );
     assert(usedIndex == VmTraceName.used.index);
-    assert(
-      trace(usedIndex).name == VmTraceName.values[usedIndex].toString(),
-    );
+    assert(trace(usedIndex).name == VmTraceName.values[usedIndex].toString());
 
     // Heap Capacity
     final capacityIndex = createTrace(
@@ -196,9 +187,7 @@
       name: VmTraceName.rSS.toString(),
     );
     assert(rSSIndex == VmTraceName.rSS.index);
-    assert(
-      trace(rSSIndex).name == VmTraceName.values[rSSIndex].toString(),
-    );
+    assert(trace(rSSIndex).name == VmTraceName.values[rSSIndex].toString());
 
     final rasterLayerIndex = createTrace(
       chart_trace.ChartType.line,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/memory_tracker.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/memory_tracker.dart
index 83dc227..b7e3a07 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/memory_tracker.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/controller/memory_tracker.dart
@@ -12,23 +12,16 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../../../../shared/globals.dart';
-import '../../../../../shared/utils.dart';
+import '../../../../../shared/utils/utils.dart';
 import '../../../shared/primitives/memory_timeline.dart';
 import '../data/primitives.dart';
 
 final _log = Logger('memory_protocol');
 
-enum _ContinuesState {
-  none,
-  stop,
-  next,
-}
+enum _ContinuesState { none, stop, next }
 
 class MemoryTracker {
-  MemoryTracker(
-    this.timeline, {
-    required this.isAndroidChartVisible,
-  });
+  MemoryTracker(this.timeline, {required this.isAndroidChartVisible});
 
   final MemoryTimeline timeline;
 
@@ -101,7 +94,8 @@
         in serviceConnection.serviceManager.isolateManager.isolates.value) {
       try {
         isolateMemory[isolateRef] = await serviceConnection
-            .serviceManager.service!
+            .serviceManager
+            .service!
             .getMemoryUsage(isolateRef.id!);
       } on SentinelException {
         // Isolates can disappear during polling, so just swallow this exception.
@@ -110,12 +104,13 @@
 
     // Polls for current Android meminfo using:
     //    > adb shell dumpsys meminfo -d <package_name>
-    _adbMemoryInfo = serviceConnection
-                .serviceManager.connectedState.value.connected &&
-            serviceConnection.serviceManager.vm!.operatingSystem == 'android' &&
-            isAndroidChartVisible.value
-        ? await _fetchAdbInfo()
-        : AdbMemoryInfo.empty();
+    _adbMemoryInfo =
+        serviceConnection.serviceManager.connectedState.value.connected &&
+                serviceConnection.serviceManager.vm!.operatingSystem ==
+                    'android' &&
+                isAndroidChartVisible.value
+            ? await _fetchAdbInfo()
+            : AdbMemoryInfo.empty();
 
     // Query the engine's rasterCache estimate.
     rasterCache = await _fetchRasterCacheInfo();
@@ -153,9 +148,8 @@
   }
 
   /// Fetch ADB meminfo, ADB returns values in KB convert to total bytes.
-  Future<AdbMemoryInfo> _fetchAdbInfo() async => AdbMemoryInfo.fromJsonInKB(
-        (await serviceConnection.adbMemoryInfo).json!,
-      );
+  Future<AdbMemoryInfo> _fetchAdbInfo() async =>
+      AdbMemoryInfo.fromJsonInKB((await serviceConnection.adbMemoryInfo).json!);
 
   void _recalculate({bool fromGC = false}) {
     int used = 0;
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/data/charts.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/data/charts.dart
index 8945ab6..0876bc2 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/data/charts.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/data/charts.dart
@@ -11,7 +11,6 @@
 import '../../../../../shared/primitives/byte_utils.dart';
 import '../../../../../shared/primitives/utils.dart';
 import '../../../shared/primitives/memory_timeline.dart';
-//import '../../../framework/connected/memory_controller.dart';
 
 /// Name of each trace being charted, index order is the trace index
 /// too (order of trace creation top-down order).
@@ -107,14 +106,7 @@
 
 /// Name of each trace being charted, index order is the trace index
 /// too (order of trace creation top-down order).
-enum VmTraceName {
-  external,
-  used,
-  capacity,
-  rSS,
-  rasterLayer,
-  rasterPicture,
-}
+enum VmTraceName { external, used, capacity, rSS, rasterLayer, rasterPicture }
 
 Map<String, Object?> traceRender({
   String? image,
@@ -238,15 +230,13 @@
       for (final event
           in eventInfo.extensionEvents?.events ?? <ExtensionEvent>[]) {
         if (event.customEventName != null) {
-          events.add(
-            {
-              eventName: event.eventKind!,
-              customEvent: {
-                customEventName: event.customEventName,
-                customEventData: event.data,
-              },
+          events.add({
+            eventName: event.eventKind!,
+            customEvent: {
+              customEventName: event.customEventName,
+              customEventData: event.data,
             },
-          );
+          });
         } else {
           events.add({
             eventName: event.eventKind!,
@@ -318,8 +308,9 @@
       dashed: true,
     );
 
-    final capacityValueDisplay =
-        formatNumeric(vmData[capacityJsonName] as num?);
+    final capacityValueDisplay = formatNumeric(
+      vmData[capacityJsonName] as num?,
+    );
     vmDataDisplayed['$allocatedDisplay $capacityValueDisplay'] = traceRender(
       color: traces[VmTraceName.capacity.index].characteristics.color,
       dashed: true,
@@ -330,21 +321,24 @@
       color: traces[VmTraceName.used.index].characteristics.color,
     );
 
-    final externalValueDisplay =
-        formatNumeric(vmData[externalJsonName] as num?);
+    final externalValueDisplay = formatNumeric(
+      vmData[externalJsonName] as num?,
+    );
     vmDataDisplayed['$externalDisplay $externalValueDisplay'] = traceRender(
       color: traces[VmTraceName.external.index].characteristics.color,
     );
 
-    final layerValueDisplay =
-        formatNumeric(vmData[rasterLayerJsonName] as num?);
+    final layerValueDisplay = formatNumeric(
+      vmData[rasterLayerJsonName] as num?,
+    );
     vmDataDisplayed['$layerDisplay $layerValueDisplay'] = traceRender(
       color: traces[VmTraceName.rasterLayer.index].characteristics.color,
       dashed: true,
     );
 
-    final pictureValueDisplay =
-        formatNumeric(vmData[rasterPictureJsonName] as num?);
+    final pictureValueDisplay = formatNumeric(
+      vmData[rasterPictureJsonName] as num?,
+    );
     vmDataDisplayed['$pictureDisplay $pictureValueDisplay'] = traceRender(
       color: traces[VmTraceName.rasterPicture.index].characteristics.color,
       dashed: true,
@@ -363,62 +357,67 @@
       final totalValueDisplay = formatNumeric(data[adbTotalJsonName] as num?);
       androidDataDisplayed['$androidTotalDisplay $totalValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.total.index].characteristics.color,
-        dashed: true,
-      );
+            color: traces[AndroidTraceName.total.index].characteristics.color,
+            dashed: true,
+          );
 
       // Other trace
       final otherValueDisplay = formatNumeric(data[adbOtherJsonName] as num?);
       androidDataDisplayed['$androidOtherDisplay $otherValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.other.index].characteristics.color,
-      );
+            color: traces[AndroidTraceName.other.index].characteristics.color,
+          );
 
       // Native heap trace
-      final nativeValueDisplay =
-          formatNumeric(data[adbNativeHeapJsonName] as num?);
+      final nativeValueDisplay = formatNumeric(
+        data[adbNativeHeapJsonName] as num?,
+      );
       androidDataDisplayed['$androidNativeDisplay $nativeValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.nativeHeap.index].characteristics.color,
-      );
+            color:
+                traces[AndroidTraceName.nativeHeap.index].characteristics.color,
+          );
 
       // Graphics trace
-      final graphicsValueDisplay =
-          formatNumeric(data[adbGraphicsJsonName] as num?);
+      final graphicsValueDisplay = formatNumeric(
+        data[adbGraphicsJsonName] as num?,
+      );
       androidDataDisplayed['$androidGraphicsDisplay $graphicsValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.graphics.index].characteristics.color,
-      );
+            color:
+                traces[AndroidTraceName.graphics.index].characteristics.color,
+          );
 
       // Code trace
       final codeValueDisplay = formatNumeric(data[adbCodeJsonName] as num?);
       androidDataDisplayed['$androidCodeDisplay $codeValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.code.index].characteristics.color,
-      );
+            color: traces[AndroidTraceName.code.index].characteristics.color,
+          );
 
       // Java heap trace
       final javaValueDisplay = formatNumeric(data[adbJavaHeapJsonName] as num?);
       androidDataDisplayed['$androidJavaDisplay $javaValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.javaHeap.index].characteristics.color,
-      );
+            color:
+                traces[AndroidTraceName.javaHeap.index].characteristics.color,
+          );
 
       // Stack trace
       final stackValueDisplay = formatNumeric(data[adbStackJsonName] as num?);
       androidDataDisplayed['$androidStackDisplay $stackValueDisplay'] =
           traceRender(
-        color: traces[AndroidTraceName.stack.index].characteristics.color,
-      );
+            color: traces[AndroidTraceName.stack.index].characteristics.color,
+          );
     }
 
     return androidDataDisplayed;
   }
 
   String? formatNumeric(num? number) => prettyPrintBytes(
-        number,
-        mbFractionDigits: 2,
-        includeUnit: true,
-        roundingPoint: 0.7,
-      );
+    number,
+    mbFractionDigits: 2,
+    includeUnit: true,
+    roundingPoint: 0.7,
+  );
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_control_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_control_pane.dart
index 363944d..35b1379 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_control_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_control_pane.dart
@@ -8,9 +8,9 @@
 
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/globals.dart';
 import '../../../../../shared/primitives/simple_items.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '../../../shared/primitives/simple_elements.dart';
 import '../controller/chart_pane_controller.dart';
 import 'interval_dropdown.dart';
@@ -87,11 +87,7 @@
           ),
         ],
         const SizedBox(height: denseSpacing),
-        Row(
-          children: [
-            _LegendButton(chartController: widget.chart),
-          ],
-        ),
+        Row(children: [_LegendButton(chartController: widget.chart)]),
         const SizedBox(height: denseSpacing),
         IntervalDropdown(chartController: widget.chart),
         const SizedBox(height: denseSpacing),
@@ -110,17 +106,19 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder<bool>(
       valueListenable: chartController.data.isLegendVisible,
-      builder: (_, legendVisible, __) => GaDevToolsButton(
-        onPressed: chartController.data.toggleLegendVisibility,
-        gaScreen: gac.memory,
-        gaSelection: legendVisible
-            ? gac.MemoryEvents.hideChartLegend.name
-            : gac.MemoryEvents.showChartLegend.name,
-        icon: legendVisible ? Icons.close : Icons.storage,
-        label: 'Legend',
-        tooltip: 'Toggle visibility of the chart legend',
-        minScreenWidthForTextBeforeScaling: memoryControlsMinVerboseWidth,
-      ),
+      builder:
+          (_, legendVisible, _) => GaDevToolsButton(
+            onPressed: chartController.data.toggleLegendVisibility,
+            gaScreen: gac.memory,
+            gaSelection:
+                legendVisible
+                    ? gac.MemoryEvents.hideChartLegend.name
+                    : gac.MemoryEvents.showChartLegend.name,
+            icon: legendVisible ? Icons.close : Icons.storage,
+            label: 'Legend',
+            tooltip: 'Toggle visibility of the chart legend',
+            minScreenWidthForTextBeforeScaling: memoryControlsMinVerboseWidth,
+          ),
     );
   }
 }
@@ -140,8 +138,9 @@
         MoreInfoLink(
           url: DocLinks.chart.value,
           gaScreenName: '',
-          gaSelectedItemDescription:
-              gac.topicDocumentationLink(_documentationTopic),
+          gaSelectedItemDescription: gac.topicDocumentationLink(
+            _documentationTopic,
+          ),
         ),
       ],
       child: const Column(
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_pane.dart
index 9a3cacf..d5e24ca 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/chart_pane.dart
@@ -8,11 +8,11 @@
 import 'package:flutter/services.dart';
 
 import '../../../../../shared/charts/chart_controller.dart';
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/globals.dart';
 import '../../../../../shared/primitives/utils.dart';
 import '../../../../../shared/ui/colors.dart';
-import '../../../../../shared/utils.dart';
+import '../../../../../shared/ui/common_widgets.dart';
+import '../../../../../shared/utils/utils.dart';
 import '../../../shared/primitives/painting.dart';
 import '../controller/chart_pane_controller.dart';
 import '../data/charts.dart';
@@ -102,11 +102,7 @@
         timestamp: value.timestamp ?? _timestamp,
       );
 
-      _showHover(
-        context,
-        allValues,
-        details.globalPosition,
-      );
+      _showHover(context, allValues, details.globalPosition);
     });
   }
 
@@ -135,9 +131,7 @@
     }
 
     // There is no listener passed, so SetState will be invoked.
-    addAutoDisposeListener(
-      widget.chart.isAndroidChartVisible,
-    );
+    addAutoDisposeListener(widget.chart.isAndroidChartVisible);
   }
 
   @override
@@ -146,7 +140,7 @@
     const memoryEventsPainHeight = 70.0;
     return ValueListenableBuilder<bool>(
       valueListenable: preferences.memory.showChart,
-      builder: (_, showChart, __) {
+      builder: (_, showChart, _) {
         if (!showChart) return const SizedBox.shrink();
 
         return KeyboardListener(
@@ -184,7 +178,7 @@
                   widget.chart.data.isLegendVisible,
                   widget.chart.isAndroidChartVisible,
                 ],
-                builder: (_, values, __) {
+                builder: (_, values, _) {
                   final isLegendVisible = values.first as bool;
                   final isAndroidChartVisible = values.second as bool;
                   if (!isLegendVisible) return const SizedBox.shrink();
@@ -201,9 +195,7 @@
                 },
               ),
               // Chart control pane.
-              ChartControlPane(
-                chart: widget.chart,
-              ),
+              ChartControlPane(chart: widget.chart),
             ],
           ),
         );
@@ -231,13 +223,15 @@
       return [];
     }
 
-    final androidDataDisplayed =
-        chartsValues.androidDataToDisplay(widget.chart.android.traces);
+    final androidDataDisplayed = chartsValues.androidDataToDisplay(
+      widget.chart.android.traces,
+    );
 
     // Separator between Android data.
     // TODO(terry): Why Center widget doesn't work (parent width is bigger/centered too far right).
     //              Is it centering on a too wide Overlay?
-    final width = _hoverWidth -
+    final width =
+        _hoverWidth -
         totalDividerLineHorizontalSpace -
         DashedLine.defaultDashWidth;
     final dashedColor = Colors.grey.shade600;
@@ -266,8 +260,9 @@
     final focusColor = theme.focusColor;
     final colorScheme = theme.colorScheme;
 
-    final box = MemoryChartPane.hoverKey.currentContext!.findRenderObject()
-        as RenderBox;
+    final box =
+        MemoryChartPane.hoverKey.currentContext!.findRenderObject()
+            as RenderBox;
     final renderBoxWidth = box.size.width;
 
     // Display hover to left of right side of position.
@@ -278,11 +273,12 @@
 
     double totalHoverHeight;
     int totalTraces;
-    totalTraces = widget.chart.isAndroidChartVisible.value
-        ? chartsValues.vmData.entries.length -
-            1 +
-            chartsValues.androidData.entries.length
-        : chartsValues.vmData.entries.length - 1;
+    totalTraces =
+        widget.chart.isAndroidChartVisible.value
+            ? chartsValues.vmData.entries.length -
+                1 +
+                chartsValues.androidData.entries.length
+            : chartsValues.vmData.entries.length - 1;
 
     totalHoverHeight = _computeHoverHeight(
       chartsValues.eventCount,
@@ -294,40 +290,41 @@
 
     final overlayState = Overlay.of(context);
     _hoverOverlayEntry ??= OverlayEntry(
-      builder: (context) => Positioned(
-        top: position.dy + _hoverYOffset,
-        left: xPosition,
-        height: totalHoverHeight,
-        child: Container(
-          padding: const EdgeInsets.only(top: 5, bottom: 8),
-          decoration: BoxDecoration(
-            color: colorScheme.defaultBackgroundColor,
-            border: Border.all(
-              color: focusColor,
-              width: _hoverCardBorderWidth,
-            ),
-            borderRadius: defaultBorderRadius,
-          ),
-          width: _hoverWidth,
-          child: ListView(
-            children: [
-              Container(
-                width: _hoverWidth,
-                padding: const EdgeInsets.only(bottom: 4),
-                child: Text(
-                  'Time $displayTimestamp',
-                  style: theme.legendTextStyle,
-                  textAlign: TextAlign.center,
+      builder:
+          (context) => Positioned(
+            top: position.dy + _hoverYOffset,
+            left: xPosition,
+            height: totalHoverHeight,
+            child: Container(
+              padding: const EdgeInsets.only(top: 5, bottom: 8),
+              decoration: BoxDecoration(
+                color: colorScheme.defaultBackgroundColor,
+                border: Border.all(
+                  color: focusColor,
+                  width: _hoverCardBorderWidth,
                 ),
+                borderRadius: defaultBorderRadius,
               ),
-              ..._displayEventsInHover(chartsValues),
-              ..._displayVmDataInHover(chartsValues),
-              ..._displayAndroidDataInHover(chartsValues),
-              ..._displayExtensionEventsInHover(chartsValues),
-            ],
+              width: _hoverWidth,
+              child: ListView(
+                children: [
+                  Container(
+                    width: _hoverWidth,
+                    padding: const EdgeInsets.only(bottom: 4),
+                    child: Text(
+                      'Time $displayTimestamp',
+                      style: theme.legendTextStyle,
+                      textAlign: TextAlign.center,
+                    ),
+                  ),
+                  ..._displayEventsInHover(chartsValues),
+                  ..._displayVmDataInHover(chartsValues),
+                  ..._displayAndroidDataInHover(chartsValues),
+                  ..._displayExtensionEventsInHover(chartsValues),
+                ],
+              ),
+            ),
           ),
-        ),
-      ),
     );
 
     overlayState.insert(_hoverOverlayEntry!);
@@ -343,9 +340,10 @@
 
     for (final entry in dataToDisplay.entries) {
       final keys = entry.value.keys;
-      final image = keys.contains(renderImage)
-          ? entry.value[renderImage] as String?
-          : null;
+      final image =
+          keys.contains(renderImage)
+              ? entry.value[renderImage] as String?
+              : null;
       final color =
           keys.contains(renderLine) ? entry.value[renderLine] as Color? : null;
       final dashedLine =
@@ -404,24 +402,17 @@
         traceColor =
             dashed ? createDashWidget(colorPatch) : createSolidLine(colorPatch);
       } else {
-        traceColor = image == null
-            ? const SizedBox()
-            : scaleImage
-                ? Image(
-                    image: AssetImage(image),
-                    width: 20,
-                    height: 10,
-                  )
-                : Image(
-                    image: AssetImage(image),
-                  );
+        traceColor =
+            image == null
+                ? const SizedBox()
+                : scaleImage
+                ? Image(image: AssetImage(image), width: 20, height: 10)
+                : Image(image: AssetImage(image));
       }
 
       return [
         traceColor,
-        const PaddedDivider(
-          padding: EdgeInsets.only(left: denseRowSpacing),
-        ),
+        const PaddedDivider(padding: EdgeInsets.only(left: denseRowSpacing)),
         Text(displayName, style: theme.legendTextStyle),
         Text(displayValue, style: theme.legendTextStyle),
       ];
@@ -439,9 +430,7 @@
     );
     return Container(
       margin: const EdgeInsets.only(left: 5, bottom: 2),
-      child: Row(
-        children: rowChildren,
-      ),
+      child: Row(children: rowChildren),
     );
   }
 
@@ -459,9 +448,7 @@
   List<Widget> _displayExtensionEventsInHover(ChartsValues chartsValues) {
     return [
       if (chartsValues.hasExtensionEvents)
-        ..._extensionEvents(
-          allEvents: chartsValues.extensionEvents,
-        ),
+        ..._extensionEvents(allEvents: chartsValues.extensionEvents),
     ];
   }
 
@@ -528,9 +515,7 @@
       children: [
         Container(
           padding: const EdgeInsets.only(left: 6.0),
-          child: Image(
-            image: AssetImage(eventLegendAsset(eventsLength)),
-          ),
+          child: Image(image: AssetImage(eventLegendAsset(eventsLength))),
         ),
         const SizedBox(width: denseSpacing),
         Text(
@@ -539,10 +524,7 @@
         ),
       ],
     );
-    return [
-      title,
-      ...widgets,
-    ];
+    return [title, ...widgets];
   }
 
   String _decodeEventValues(Map<String, Object> event) {
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/interval_dropdown.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/interval_dropdown.dart
index 04b44ff..d6cbf21 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/interval_dropdown.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/interval_dropdown.dart
@@ -23,16 +23,14 @@
   @override
   Widget build(BuildContext context) {
     final displayTypes =
-        ChartInterval.values.map<DropdownMenuItem<ChartInterval>>(
-      (
-        ChartInterval value,
-      ) {
-        return DropdownMenuItem<ChartInterval>(
-          value: value,
-          child: Text(value.displayName),
-        );
-      },
-    ).toList();
+        ChartInterval.values.map<DropdownMenuItem<ChartInterval>>((
+          ChartInterval value,
+        ) {
+          return DropdownMenuItem<ChartInterval>(
+            value: value,
+            child: Text(value.displayName),
+          );
+        }).toList();
 
     return RoundedDropDownButton<ChartInterval>(
       isDense: true,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/legend.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/legend.dart
index b578d16..738633d 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/legend.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/legend.dart
@@ -49,12 +49,7 @@
     while (iterator.moveNext()) {
       final leftEntry = iterator.current;
       final rightEntry = iterator.moveNext() ? iterator.current : null;
-      legendRows.add(
-        _LegendRow(
-          entry1: leftEntry,
-          entry2: rightEntry,
-        ),
-      );
+      legendRows.add(_LegendRow(entry1: leftEntry, entry2: rightEntry));
     }
 
     final vms = vmLegendContent(chartController.vm);
@@ -66,11 +61,7 @@
     );
 
     for (final entry in vms.entries) {
-      legendRows.add(
-        _LegendRow(
-          entry1: entry,
-        ),
-      );
+      legendRows.add(_LegendRow(entry1: entry));
     }
 
     if (isAndroidVisible) {
@@ -83,11 +74,7 @@
       );
 
       for (final entry in androids.entries) {
-        legendRows.add(
-          _LegendRow(
-            entry1: entry,
-          ),
-        );
+        legendRows.add(_LegendRow(entry1: entry));
       }
     }
 
@@ -111,10 +98,7 @@
 }
 
 class _LegendRow extends StatelessWidget {
-  const _LegendRow({
-    required this.entry1,
-    this.entry2,
-  });
+  const _LegendRow({required this.entry1, this.entry2});
 
   final MapEntry<String, Map<String, Object?>> entry1;
   final MapEntry<String, Map<String, Object?>>? entry2;
@@ -138,9 +122,7 @@
             child: Text(name, style: legendEntry),
           ),
         ),
-        const PaddedDivider(
-          padding: EdgeInsets.only(left: denseRowSpacing),
-        ),
+        const PaddedDivider(padding: EdgeInsets.only(left: denseRowSpacing)),
         widget,
       ]);
 
@@ -148,15 +130,18 @@
     }
 
     Widget legendSymbol(Map<String, Object?> dataToDisplay) {
-      final image = dataToDisplay.containsKey(renderImage)
-          ? dataToDisplay[renderImage] as String?
-          : null;
-      final color = dataToDisplay.containsKey(renderLine)
-          ? dataToDisplay[renderLine] as Color?
-          : null;
-      final dashedLine = dataToDisplay.containsKey(renderDashed)
-          ? dataToDisplay[renderDashed]
-          : false;
+      final image =
+          dataToDisplay.containsKey(renderImage)
+              ? dataToDisplay[renderImage] as String?
+              : null;
+      final color =
+          dataToDisplay.containsKey(renderLine)
+              ? dataToDisplay[renderLine] as Color?
+              : null;
+      final dashedLine =
+          dataToDisplay.containsKey(renderDashed)
+              ? dataToDisplay[renderDashed]
+              : false;
 
       Widget traceColor;
       if (color != null) {
@@ -205,8 +190,7 @@
   vmGC('GC VM'),
   manualGC('Manual'),
   event('Event'),
-  events('Events'),
-  ;
+  events('Events');
 
   const _LegendCategory(this.displayName);
 
@@ -214,32 +198,22 @@
 }
 
 Map<String, Map<String, Object?>> _eventLegendContent(bool isLight) => {
-      _LegendCategory.manualSnapshot.displayName: traceRender(
-        image: snapshotManualLegend,
-      ),
-      _LegendCategory.autoSnapshot.displayName: traceRender(
-        image: snapshotAutoLegend,
-      ),
-      _LegendCategory.monitor.displayName: traceRender(
-        image: monitorLegend,
-      ),
-      _LegendCategory.reset.displayName: traceRender(
-        image: isLight ? resetLightLegend : resetDarkLegend,
-      ),
-      _LegendCategory.vmGC.displayName: traceRender(
-        image: gcVMLegend,
-      ),
-      _LegendCategory.manualGC.displayName: traceRender(
-        image: gcManualLegend,
-      ),
-      // TODO: why do we need both a singular and plural legend entry for event?
-      _LegendCategory.event.displayName: traceRender(
-        image: eventLegendAsset(1),
-      ),
-      _LegendCategory.events.displayName: traceRender(
-        image: eventLegendAsset(2),
-      ),
-    };
+  _LegendCategory.manualSnapshot.displayName: traceRender(
+    image: snapshotManualLegend,
+  ),
+  _LegendCategory.autoSnapshot.displayName: traceRender(
+    image: snapshotAutoLegend,
+  ),
+  _LegendCategory.monitor.displayName: traceRender(image: monitorLegend),
+  _LegendCategory.reset.displayName: traceRender(
+    image: isLight ? resetLightLegend : resetDarkLegend,
+  ),
+  _LegendCategory.vmGC.displayName: traceRender(image: gcVMLegend),
+  _LegendCategory.manualGC.displayName: traceRender(image: gcManualLegend),
+  // TODO: why do we need both a singular and plural legend entry for event?
+  _LegendCategory.event.displayName: traceRender(image: eventLegendAsset(1)),
+  _LegendCategory.events.displayName: traceRender(image: eventLegendAsset(2)),
+};
 
 Map<String, Map<String, Object?>> vmLegendContent(
   VMChartController vmChartController,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/memory_chart.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/memory_chart.dart
index df82209..bb8d286 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/memory_chart.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/widgets/memory_chart.dart
@@ -42,9 +42,6 @@
 
   @override
   Widget build(BuildContext context) {
-    return SizedBox(
-      height: defaultChartHeight,
-      child: Chart(widget.chart),
-    );
+    return SizedBox(height: defaultChartHeight, child: Chart(widget.chart));
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/control_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/control_pane.dart
index a3938fe..d81d48d 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/control_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/control_pane.dart
@@ -5,8 +5,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../../shared/common_widgets.dart';
-import '../../../../../shared/screen.dart';
+import '../../../../../shared/framework/screen.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import 'primary_controls.dart';
 import 'secondary_controls.dart';
 
@@ -26,18 +26,15 @@
   Widget build(BuildContext context) {
     // OfflineAwareControls are here to enable button to exit offline mode.
     return OfflineAwareControls(
-      controlsBuilder: (_) => Row(
-        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-        children: [
-          const PrimaryControls(),
-          const Spacer(),
-          SecondaryControls(
-            isGcing: isGcing,
-            onGc: onGc,
-            onSave: onSave,
+      controlsBuilder:
+          (_) => Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              const PrimaryControls(),
+              const Spacer(),
+              SecondaryControls(isGcing: isGcing, onGc: onGc, onSave: onSave),
+            ],
           ),
-        ],
-      ),
       gaScreen: ScreenMetaData.memory.id,
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/primary_controls.dart b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/primary_controls.dart
index eaf8f68..be5261c 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/primary_controls.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/primary_controls.dart
@@ -5,8 +5,8 @@
 import 'package:flutter/material.dart';
 
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/globals.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '../../../shared/primitives/simple_elements.dart';
 
 class PrimaryControls extends StatelessWidget {
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/secondary_controls.dart b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/secondary_controls.dart
index abe7607..745c133 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/secondary_controls.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/secondary_controls.dart
@@ -9,11 +9,11 @@
 import 'package:flutter/material.dart';
 
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/feature_flags.dart';
-import '../../../../../shared/file_import.dart';
+import '../../../../../shared/framework/screen.dart';
 import '../../../../../shared/globals.dart';
-import '../../../../../shared/screen.dart';
+import '../../../../../shared/ui/common_widgets.dart';
+import '../../../../../shared/ui/file_import.dart';
 import '../../../shared/primitives/simple_elements.dart';
 import 'settings_dialog.dart';
 
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/settings_dialog.dart b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/settings_dialog.dart
index d67ad42..6b2bf64 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/settings_dialog.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/control/widgets/settings_dialog.dart
@@ -5,8 +5,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/globals.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 
 /// The dialog keys for testing purposes.
 @visibleForTesting
@@ -43,9 +43,7 @@
           ],
         ),
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/diff_pane_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/diff_pane_controller.dart
index b194ca1..2f76ffe 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/diff_pane_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/diff_pane_controller.dart
@@ -13,12 +13,12 @@
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
 import '../../../../../shared/config_specific/import_export/import_export.dart';
-import '../../../../../shared/file_import.dart';
 import '../../../../../shared/memory/class_name.dart';
 import '../../../../../shared/memory/classes.dart';
 import '../../../../../shared/memory/heap_graph_loader.dart';
 import '../../../../../shared/memory/retaining_path.dart';
 import '../../../../../shared/memory/simple_items.dart';
+import '../../../../../shared/ui/file_import.dart';
 import '../../../shared/heap/class_filter.dart';
 import '../../../shared/primitives/memory_utils.dart';
 import '../data/classes_diff.dart';
@@ -29,11 +29,7 @@
 import 'snapshot_item.dart';
 
 @visibleForTesting
-enum Json {
-  snapshots,
-  diffWith,
-  rootPackage;
-}
+enum Json { snapshots, diffWith, rootPackage }
 
 class DiffPaneController extends DisposableController with Serializable {
   DiffPaneController({
@@ -48,9 +44,13 @@
   }
 
   factory DiffPaneController.fromJson(Map<String, dynamic> json) {
-    final snapshots = (json[Json.snapshots.name] as List)
-        .map((e) => deserialize<SnapshotDataItem>(e, SnapshotDataItem.fromJson))
-        .toList();
+    final snapshots =
+        (json[Json.snapshots.name] as List)
+            .map(
+              (e) =>
+                  deserialize<SnapshotDataItem>(e, SnapshotDataItem.fromJson),
+            )
+            .toList();
 
     final diffWith =
         (json[Json.diffWith.name] as List).map((e) => e as int?).toList();
@@ -73,18 +73,21 @@
 
   @override
   Map<String, dynamic> toJson() {
-    final snapshots = core.snapshots.value
-        .whereType<SnapshotDataItem>()
-        .where((s) => s.heap != null)
-        .toList();
+    final snapshots =
+        core.snapshots.value
+            .whereType<SnapshotDataItem>()
+            .where((s) => s.heap != null)
+            .toList();
 
-    final snapshotToIndex =
-        snapshots.asMap().map((index, item) => MapEntry(item, index));
+    final snapshotToIndex = snapshots.asMap().map(
+      (index, item) => MapEntry(item, index),
+    );
 
-    final diffWithIndices = snapshots.map((item) {
-      final diffWith = item.diffWith.value;
-      return diffWith == null ? null : snapshotToIndex[diffWith];
-    }).toList();
+    final diffWithIndices =
+        snapshots.map((item) {
+          final diffWith = item.diffWith.value;
+          return diffWith == null ? null : snapshotToIndex[diffWith];
+        }).toList();
 
     return {
       Json.snapshots.name: snapshots,
@@ -105,10 +108,7 @@
   bool get hasSnapshots => core.snapshots.value.length > 1;
 
   Future<void> takeSnapshot() async {
-    ga.select(
-      gac.memory,
-      gac.MemoryEvents.diffTakeSnapshotControlPane.name,
-    );
+    ga.select(gac.memory, gac.MemoryEvents.diffTakeSnapshotControlPane.name);
     final item = SnapshotDataItem(
       displayNumber: _nextDisplayNumber(),
       defaultName: selectedIsolateName ?? '<isolate-not-detected>',
@@ -121,10 +121,7 @@
   ///
   /// Opens file selector and loads snapshots from the selected files.
   Future<void> importSnapshots() async {
-    ga.select(
-      gac.memory,
-      gac.importFile,
-    );
+    ga.select(gac.memory, gac.importFile);
     final files = await importRawFilesFromPicker();
     if (files.isEmpty) return;
 
@@ -189,10 +186,7 @@
     derived._updateValues();
   }
 
-  void setDiffing(
-    SnapshotDataItem diffItem,
-    SnapshotDataItem? withItem,
-  ) {
+  void setDiffing(SnapshotDataItem diffItem, SnapshotDataItem? withItem) {
     diffItem.diffWith.value = withItem;
     derived._updateValues();
   }
@@ -249,9 +243,10 @@
   SnapshotItem get selectedItem =>
       _snapshots.value[_selectedSnapshotIndex.value];
 
-  SnapshotDataItem? get selectedDataItem => selectedItem is SnapshotDataItem
-      ? selectedItem as SnapshotDataItem
-      : null;
+  SnapshotDataItem? get selectedDataItem =>
+      selectedItem is SnapshotDataItem
+          ? selectedItem as SnapshotDataItem
+          : null;
 
   /// Full name for the selected class.
   ///
@@ -324,8 +319,9 @@
   /// Classes to show for currently selected item, if the item is not diffed.
   ValueListenable<ClassDataList<SingleClassData>?> get singleClassesToShow =>
       _singleClassesToShow;
-  final _singleClassesToShow =
-      ValueNotifier<ClassDataList<SingleClassData>?>(null);
+  final _singleClassesToShow = ValueNotifier<ClassDataList<SingleClassData>?>(
+    null,
+  );
 
   /// Classes to show for currently selected item, if the item is diffed.
   ValueListenable<ClassDataList<DiffClassData>?> get diffClassesToShow =>
@@ -425,16 +421,18 @@
     if (classes is ClassDataList<SingleClassData>) {
       _singleClassesToShow.value = classes;
       _diffClassesToShow.value = null;
-      classesTableSingle.selection.value = classes.list
-          .singleWhereOrNull((d) => d.className == selectedClassName);
+      classesTableSingle.selection.value = classes.list.singleWhereOrNull(
+        (d) => d.className == selectedClassName,
+      );
       classesTableDiff.selection.value = null;
       classData.value = classesTableSingle.selection.value;
     } else if (classes is ClassDataList<DiffClassData>) {
       _singleClassesToShow.value = null;
       _diffClassesToShow.value = classes;
       classesTableSingle.selection.value = null;
-      classesTableDiff.selection.value = classes.list
-          .singleWhereOrNull((d) => d.className == selectedClassName);
+      classesTableDiff.selection.value = classes.list.singleWhereOrNull(
+        (d) => d.className == selectedClassName,
+      );
       classData.value = classesTableDiff.selection.value;
     } else if (classes == null) {
       _singleClassesToShow.value = null;
@@ -483,10 +481,7 @@
     // Make sure the method does not trigger itself recursively.
     assert(!_updatingValues);
 
-    ga.timeStart(
-      gac.memory,
-      gac.MemoryTime.updateValues.name,
-    );
+    ga.timeStart(gac.memory, gac.MemoryTime.updateValues.name);
 
     _updatingValues = true;
   }
@@ -494,10 +489,7 @@
   void _endUpdatingValues() {
     _updatingValues = false;
 
-    ga.timeEnd(
-      gac.memory,
-      gac.MemoryTime.updateValues.name,
-    );
+    ga.timeEnd(gac.memory, gac.MemoryTime.updateValues.name);
 
     _assertIntegrity();
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/snapshot_item.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/snapshot_item.dart
index a429c41..bfefe23 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/snapshot_item.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/controller/snapshot_item.dart
@@ -23,13 +23,7 @@
 }
 
 @visibleForTesting
-enum Json {
-  defaultName,
-  displayNumber,
-  graph,
-  created,
-  nameOverride;
-}
+enum Json { defaultName, displayNumber, graph, created, nameOverride }
 
 class SnapshotDataItem extends SnapshotItem
     with Serializable
@@ -47,14 +41,17 @@
       nameOverride: json[Json.nameOverride.name] as String?,
     );
 
-    final graph = HeapSnapshotGraphEncodeDecode.instance
-        .decodeNullable(json[Json.graph.name]);
+    final graph = HeapSnapshotGraphEncodeDecode.instance.decodeNullable(
+      json[Json.graph.name],
+    );
     if (graph == null) return result;
 
     final loader = HeapGraphLoaderDirect(
       graph: graph,
-      created: DateTimeEncodeDecode.instance
-              .decodeNullable(json[Json.created.name]) ??
+      created:
+          DateTimeEncodeDecode.instance.decodeNullable(
+            json[Json.created.name],
+          ) ??
           DateTime.now(),
     );
 
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/classes_diff.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/classes_diff.dart
index 6710e70..94a509f 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/classes_diff.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/classes_diff.dart
@@ -56,8 +56,9 @@
     HeapData data,
     ObjectSet originalSet,
   ) {
-    final excludeFromRetained =
-        originalSet.excludedFromRetainedSize.contains(index);
+    final excludeFromRetained = originalSet.excludedFromRetainedSize.contains(
+      index,
+    );
     setToAlter.countInstance(
       data.graph,
       index,
@@ -72,8 +73,9 @@
     HeapData data,
     ObjectSet originalSet,
   ) {
-    final excludeFromRetained =
-        originalSet.excludedFromRetainedSize.contains(index);
+    final excludeFromRetained = originalSet.excludedFromRetainedSize.contains(
+      index,
+    );
     setToAlter.uncountInstance(
       data.graph,
       index,
@@ -82,10 +84,7 @@
     );
   }
 
-  static Map<int, int> _hashCodeToIndexMap(
-    ObjectSet? ids,
-    HeapData? data,
-  ) {
+  static Map<int, int> _hashCodeToIndexMap(ObjectSet? ids, HeapData? data) {
     if (ids == null || data == null) return const {};
     return {
       for (final id in ids.indexes) data.graph.objects[id].identityHashCode: id,
@@ -133,12 +132,17 @@
         dataAfter: dataAfter,
       ),
       // PathFromRoot, ObjectSetStats
-      byPath: subtractMaps<PathFromRoot, ObjectSetStats, ObjectSetStats,
-          ObjectSetStats>(
+      byPath: subtractMaps<
+        PathFromRoot,
+        ObjectSetStats,
+        ObjectSetStats,
+        ObjectSetStats
+      >(
         from: after?.byPath,
         subtract: before?.byPath,
-        subtractor: ({subtract, from}) =>
-            ObjectSetStats.subtract(subtract: subtract, from: from),
+        subtractor:
+            ({subtract, from}) =>
+                ObjectSetStats.subtract(subtract: subtract, from: from),
       ),
     );
 
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_data.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_data.dart
index 252b6ac..24ed9b1 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_data.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_data.dart
@@ -28,16 +28,21 @@
   required HeapData before,
   required HeapData after,
 }) {
-  final classesByName = subtractMaps<HeapClassName, SingleClassData,
-      SingleClassData, DiffClassData>(
+  final classesByName = subtractMaps<
+    HeapClassName,
+    SingleClassData,
+    SingleClassData,
+    DiffClassData
+  >(
     from: after.classes!.asMap(),
     subtract: before.classes!.asMap(),
-    subtractor: ({subtract, from}) => DiffClassData.compare(
-      before: subtract,
-      after: from,
-      dataBefore: before,
-      dataAfter: after,
-    ),
+    subtractor:
+        ({subtract, from}) => DiffClassData.compare(
+          before: subtract,
+          after: from,
+          dataBefore: before,
+          dataAfter: after,
+        ),
   );
 
   return HeapDiffData._(
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_store.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_store.dart
index edc167e..5d0e9c1 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_store.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/data/heap_diff_store.dart
@@ -28,12 +28,17 @@
   ga.timeSync(
     gac.memory,
     gac.MemoryTime.calculateDiff.name,
-    syncOperation: () => result =
-        calculateHeapDiffData(before: couple.before, after: couple.after),
-    screenMetricsProvider: () => MemoryScreenMetrics(
-      heapDiffObjectsBefore: couple.before.graph.objects.length,
-      heapDiffObjectsAfter: couple.after.graph.objects.length,
-    ),
+    syncOperation:
+        () =>
+            result = calculateHeapDiffData(
+              before: couple.before,
+              after: couple.after,
+            ),
+    screenMetricsProvider:
+        () => MemoryScreenMetrics(
+          heapDiffObjectsBefore: couple.before.graph.objects.length,
+          heapDiffObjectsAfter: couple.after.graph.objects.length,
+        ),
   );
   return result;
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/diff_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/diff_pane.dart
index f9451c7..c132def 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/diff_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/diff_pane.dart
@@ -10,9 +10,9 @@
 import 'package:flutter_markdown/flutter_markdown.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/primitives/simple_items.dart';
-import '../../../../shared/utils.dart';
+import '../../../../shared/ui/common_widgets.dart';
+import '../../../../shared/utils/utils.dart';
 import '../../shared/widgets/shared_memory_widgets.dart';
 import 'controller/diff_pane_controller.dart';
 import 'controller/snapshot_item.dart';
@@ -36,9 +36,7 @@
           child: SnapshotList(controller: diffController),
         ),
         OutlineDecoration.onlyLeft(
-          child: _SnapshotItemContent(
-            controller: diffController,
-          ),
+          child: _SnapshotItemContent(controller: diffController),
         ),
       ],
     );
@@ -56,7 +54,7 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder<SnapshotItem>(
       valueListenable: controller.derived.selectedItem,
-      builder: (_, item, __) {
+      builder: (_, item, _) {
         if (item is SnapshotDocItem) {
           return Padding(
             padding: const EdgeInsets.all(denseSpacing),
@@ -74,8 +72,9 @@
                           styleSheet: MarkdownStyleSheet(
                             p: Theme.of(context).regularTextStyle,
                           ),
-                          onTapLink: (text, url, title) =>
-                              unawaited(launchUrlWithErrorHandling(url!)),
+                          onTapLink:
+                              (text, url, title) =>
+                                  unawaited(launchUrlWithErrorHandling(url!)),
                         ),
                       ),
                       const SizedBox(width: densePadding),
@@ -93,8 +92,8 @@
                           MoreInfoLink(
                             url: DocLinks.diff.value,
                             gaScreenName: gac.memory,
-                            gaSelectedItemDescription:
-                                gac.topicDocumentationLink(_documentationTopic),
+                            gaSelectedItemDescription: gac
+                                .topicDocumentationLink(_documentationTopic),
                           ),
                         ],
                       ),
@@ -128,11 +127,7 @@
             child: SnapshotControlPane(controller: controller),
           ),
         ),
-        Expanded(
-          child: SnapshotView(
-            controller: controller,
-          ),
-        ),
+        Expanded(child: SnapshotView(controller: controller)),
       ],
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/class_details.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/class_details.dart
index 450e737..08dea9c 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/class_details.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/class_details.dart
@@ -5,8 +5,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/widgets.dart';
 
-import '../../../../../../shared/common_widgets.dart';
 import '../../../../../../shared/memory/classes.dart';
+import '../../../../../../shared/ui/common_widgets.dart';
 import '../../controller/class_data.dart';
 import 'path.dart';
 import 'paths.dart';
@@ -42,17 +42,14 @@
 
     final selectedPathView = ValueListenableBuilder<PathData?>(
       valueListenable: pathSelection,
-      builder: (_, pathData, __) {
+      builder: (_, pathData, _) {
         if (pathData == null) {
           return const CenteredMessage(
             message: 'Click a table row to see the detailed path.',
           );
         }
 
-        return RetainingPathView(
-          data: pathData,
-          controller: pathController,
-        );
+        return RetainingPathView(data: pathData, controller: pathController);
       },
     );
 
@@ -60,12 +57,8 @@
       axis: Axis.horizontal,
       initialFractions: const [0.7, 0.3],
       children: [
-        OutlineDecoration.onlyRight(
-          child: retainingPathsTable,
-        ),
-        OutlineDecoration.onlyLeft(
-          child: selectedPathView,
-        ),
+        OutlineDecoration.onlyRight(child: retainingPathsTable),
+        OutlineDecoration.onlyLeft(child: selectedPathView),
       ],
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/path.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/path.dart
index 5c0811c..2319d43 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/path.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/path.dart
@@ -7,9 +7,9 @@
 
 import '../../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../../shared/common_widgets.dart';
 import '../../../../../../shared/memory/retaining_path.dart';
 import '../../../../../../shared/primitives/utils.dart';
+import '../../../../../../shared/ui/common_widgets.dart';
 import '../../controller/class_data.dart';
 
 class RetainingPathView extends StatelessWidget {
@@ -30,10 +30,7 @@
         crossAxisAlignment: CrossAxisAlignment.stretch,
         children: [
           const SizedBox(height: densePadding),
-          _PathControlPane(
-            controller: controller,
-            data: data,
-          ),
+          _PathControlPane(controller: controller, data: data),
           Expanded(
             child: Padding(
               padding: const EdgeInsets.only(
@@ -50,10 +47,7 @@
 }
 
 class _PathControlPane extends StatelessWidget {
-  const _PathControlPane({
-    required this.controller,
-    required this.data,
-  });
+  const _PathControlPane({required this.controller, required this.data});
 
   final PathData data;
   final RetainingPathController controller;
@@ -87,33 +81,36 @@
         const SizedBox(width: denseSpacing),
         ValueListenableBuilder<bool>(
           valueListenable: controller.hideStandard,
-          builder: (_, hideStandard, __) => DevToolsFilterButton(
-            onPressed: () {
-              ga.select(
-                gac.memory,
-                '${gac.MemoryEvents.diffPathFilter.name}-$hideStandard',
-              );
-              controller.hideStandard.value = !controller.hideStandard.value;
-            },
-            isFilterActive: hideStandard,
-            message: 'Hide standard libraries',
-          ),
+          builder:
+              (_, hideStandard, _) => DevToolsFilterButton(
+                onPressed: () {
+                  ga.select(
+                    gac.memory,
+                    '${gac.MemoryEvents.diffPathFilter.name}-$hideStandard',
+                  );
+                  controller.hideStandard.value =
+                      !controller.hideStandard.value;
+                },
+                isFilterActive: hideStandard,
+                message: 'Hide standard libraries',
+              ),
         ),
         const SizedBox(width: denseSpacing),
         ValueListenableBuilder<bool>(
           valueListenable: controller.invert,
-          builder: (_, invert, __) => DevToolsToggleButton(
-            onPressed: () {
-              ga.select(
-                gac.memory,
-                '${gac.MemoryEvents.diffPathInvert.name}-$invert',
-              );
-              controller.invert.value = !controller.invert.value;
-            },
-            isSelected: invert,
-            message: 'Invert the path',
-            icon: Icons.swap_horiz,
-          ),
+          builder:
+              (_, invert, _) => DevToolsToggleButton(
+                onPressed: () {
+                  ga.select(
+                    gac.memory,
+                    '${gac.MemoryEvents.diffPathInvert.name}-$invert',
+                  );
+                  controller.invert.value = !controller.invert.value;
+                },
+                isSelected: invert,
+                message: 'Invert the path',
+                icon: Icons.swap_horiz,
+              ),
         ),
       ],
     );
@@ -129,11 +126,8 @@
   @override
   Widget build(BuildContext context) {
     return MultiValueListenableBuilder(
-      listenables: [
-        controller.hideStandard,
-        controller.invert,
-      ],
-      builder: (_, values, __) {
+      listenables: [controller.hideStandard, controller.invert],
+      builder: (_, values, _) {
         final hideStandard = values.first as bool;
         final invert = values.second as bool;
         return SingleChildScrollView(
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/paths.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/paths.dart
index edc5ba8..b34a367 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/paths.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/class_details/paths.dart
@@ -17,12 +17,13 @@
 
 class _RetainingPathColumn extends ColumnData<PathData> {
   _RetainingPathColumn(String className)
-      : super.wide(
-          'Shortest Retaining Path for Instances of $className',
-          titleTooltip: 'The shortest sequence of objects\n'
-              'retaining $className instances from garbage collection.',
-          alignment: ColumnAlignment.left,
-        );
+    : super.wide(
+        'Shortest Retaining Path for Instances of $className',
+        titleTooltip:
+            'The shortest sequence of objects\n'
+            'retaining $className instances from garbage collection.',
+        alignment: ColumnAlignment.left,
+      );
 
   @override
   String? getValue(PathData record) =>
@@ -34,13 +35,14 @@
 
 class _InstanceColumn extends ColumnData<PathData> {
   _InstanceColumn(bool isDiff)
-      : super(
-          isDiff ? 'Instance\nDelta' : 'Instances',
-          titleTooltip: 'Number of instances of the class\n'
-              'retained by the path.',
-          fixedWidthPx: scaleByFontFactor(80.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        isDiff ? 'Instance\nDelta' : 'Instances',
+        titleTooltip:
+            'Number of instances of the class\n'
+            'retained by the path.',
+        fixedWidthPx: scaleByFontFactor(80.0),
+        alignment: ColumnAlignment.right,
+      );
 
   @override
   int getValue(PathData record) => record.objects.instanceCount;
@@ -51,12 +53,12 @@
 
 class _ShallowSizeColumn extends ColumnData<PathData> {
   _ShallowSizeColumn(bool isDiff)
-      : super(
-          isDiff ? 'Shallow\nSize Delta' : 'Shallow\nDart Size',
-          titleTooltip: SizeType.shallow.description,
-          fixedWidthPx: scaleByFontFactor(80.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        isDiff ? 'Shallow\nSize Delta' : 'Shallow\nDart Size',
+        titleTooltip: SizeType.shallow.description,
+        fixedWidthPx: scaleByFontFactor(80.0),
+        alignment: ColumnAlignment.right,
+      );
 
   @override
   int getValue(PathData record) => record.objects.shallowSize;
@@ -65,20 +67,18 @@
   bool get numeric => true;
 
   @override
-  String getDisplayValue(PathData record) => prettyPrintBytes(
-        getValue(record),
-        includeUnit: true,
-      )!;
+  String getDisplayValue(PathData record) =>
+      prettyPrintBytes(getValue(record), includeUnit: true)!;
 }
 
 class _RetainedSizeColumn extends ColumnData<PathData> {
   _RetainedSizeColumn(bool isDiff)
-      : super(
-          isDiff ? 'Retained\nSize Delta' : 'Retained\nDart Size',
-          titleTooltip: SizeType.retained.description,
-          fixedWidthPx: scaleByFontFactor(80.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        isDiff ? 'Retained\nSize Delta' : 'Retained\nDart Size',
+        titleTooltip: SizeType.retained.description,
+        fixedWidthPx: scaleByFontFactor(80.0),
+        alignment: ColumnAlignment.right,
+      );
 
   @override
   int getValue(PathData record) => record.objects.retainedSize;
@@ -87,10 +87,8 @@
   bool get numeric => true;
 
   @override
-  String getDisplayValue(PathData record) => prettyPrintBytes(
-        getValue(record),
-        includeUnit: true,
-      )!;
+  String getDisplayValue(PathData record) =>
+      prettyPrintBytes(getValue(record), includeUnit: true)!;
 }
 
 class _RetainingPathTableColumns {
@@ -133,11 +131,10 @@
     String dataKey,
     bool isDiff,
     String className,
-  ) =>
-      _columnStore.putIfAbsent(
-        dataKey,
-        () => _RetainingPathTableColumns(isDiff, className),
-      );
+  ) => _columnStore.putIfAbsent(
+    dataKey,
+    () => _RetainingPathTableColumns(isDiff, className),
+  );
 
   @override
   Widget build(BuildContext context) {
@@ -150,10 +147,11 @@
       data: _data,
       keyFactory: (e) => ValueKey(e.path),
       selectionNotifier: selection,
-      onItemSelected: (_) => ga.select(
-        gac.memory,
-        '${gac.MemoryEvents.diffPathSelect.name}-${isDiff ? "diff" : "single"}',
-      ),
+      onItemSelected:
+          (_) => ga.select(
+            gac.memory,
+            '${gac.MemoryEvents.diffPathSelect.name}-${isDiff ? "diff" : "single"}',
+          ),
       defaultSortColumn: columns.retainedSizeColumn,
       defaultSortDirection: SortDirection.descending,
       tallHeaders: true,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
index d5f7baa..cf52e10 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_diff.dart
@@ -19,24 +19,19 @@
 import '../data/classes_diff.dart';
 import 'instances.dart';
 
-enum _DataPart {
-  created,
-  deleted,
-  delta,
-  persisted,
-}
+enum _DataPart { created, deleted, delta, persisted }
 
 class _ClassNameColumn extends ColumnData<DiffClassData>
     implements
         ColumnRenderer<DiffClassData>,
         ColumnHeaderRenderer<DiffClassData> {
   _ClassNameColumn(this.diffData)
-      : super(
-          'Class',
-          titleTooltip: 'Class name',
-          fixedWidthPx: scaleByFontFactor(200.0),
-          alignment: ColumnAlignment.left,
-        );
+    : super(
+        'Class',
+        titleTooltip: 'Class name',
+        fixedWidthPx: scaleByFontFactor(200.0),
+        alignment: ColumnAlignment.left,
+      );
 
   final ClassesTableDiffData diffData;
 
@@ -81,11 +76,11 @@
 class _InstanceColumn extends ColumnData<DiffClassData>
     implements ColumnRenderer<DiffClassData> {
   _InstanceColumn(this.dataPart, this.diffData)
-      : super(
-          columnTitle(dataPart),
-          fixedWidthPx: scaleByFontFactor(110.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        columnTitle(dataPart),
+        fixedWidthPx: scaleByFontFactor(110.0),
+        alignment: ColumnAlignment.right,
+      );
 
   final _DataPart dataPart;
 
@@ -146,9 +141,10 @@
       return null;
     }
 
-    final heapCallback = dataPart == _DataPart.deleted
-        ? diffData.heapBefore
-        : diffData.heapAfter;
+    final heapCallback =
+        dataPart == _DataPart.deleted
+            ? diffData.heapBefore
+            : diffData.heapAfter;
 
     if (objects is! ObjectSet) {
       throw StateError(
@@ -168,11 +164,11 @@
 
 class _SizeColumn extends ColumnData<DiffClassData> {
   _SizeColumn(this.dataPart, this.sizeType)
-      : super(
-          columnTitle(dataPart),
-          fixedWidthPx: scaleByFontFactor(80.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        columnTitle(dataPart),
+        fixedWidthPx: scaleByFontFactor(80.0),
+        alignment: ColumnAlignment.right,
+      );
 
   final _DataPart dataPart;
   final SizeType sizeType;
@@ -270,16 +266,17 @@
             RoundedDropDownButton<SizeType>(
               isDense: true,
               value: sizeType,
-              onChanged: (SizeType? value) =>
-                  diffData.selectedSizeType.value = value!,
-              items: SizeType.values
-                  .map(
-                    (sizeType) => DropdownMenuItem<SizeType>(
-                      value: sizeType,
-                      child: Text(sizeType.displayName),
-                    ),
-                  )
-                  .toList(),
+              onChanged:
+                  (SizeType? value) => diffData.selectedSizeType.value = value!,
+              items:
+                  SizeType.values
+                      .map(
+                        (sizeType) => DropdownMenuItem<SizeType>(
+                          value: sizeType,
+                          child: Text(sizeType.displayName),
+                        ),
+                      )
+                      .toList(),
             ),
             const SizedBox(width: denseSpacing),
             const Text('Size'),
@@ -292,11 +289,7 @@
 }
 
 class ClassesTableDiff extends StatelessWidget {
-  ClassesTableDiff({
-    super.key,
-    required this.classes,
-    required this.diffData,
-  }) {
+  ClassesTableDiff({super.key, required this.classes, required this.diffData}) {
     _columns = {
       for (final sizeType in SizeType.values)
         sizeType: ClassesTableDiffColumns(sizeType, diffData),
@@ -308,19 +301,13 @@
 
   List<ColumnGroup> _columnGroups() {
     return [
-      ColumnGroup.fromText(
-        title: '',
-        range: const Range(0, 1),
-      ),
+      ColumnGroup.fromText(title: '', range: const Range(0, 1)),
       ColumnGroup.fromText(
         title: 'Instances',
         range: const Range(1, 5),
         tooltip: nonGcableInstancesColumnTooltip,
       ),
-      ColumnGroup(
-        title: _SizeGroupTitle(diffData),
-        range: const Range(5, 9),
-      ),
+      ColumnGroup(title: _SizeGroupTitle(diffData), range: const Range(5, 9)),
     ];
   }
 
@@ -344,10 +331,11 @@
           dataKey: dataKey,
           keyFactory: (e) => Key(e.className.fullName),
           selectionNotifier: diffData.selection,
-          onItemSelected: (_) => ga.select(
-            gac.memory,
-            gac.MemoryEvents.diffClassDiffSelect.name,
-          ),
+          onItemSelected:
+              (_) => ga.select(
+                gac.memory,
+                gac.MemoryEvents.diffClassDiffSelect.name,
+              ),
           defaultSortColumn: columns.sizeDeltaColumn,
           defaultSortDirection: SortDirection.descending,
         );
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
index 8e2dfdb..cbe13e4 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/classes_table_single.dart
@@ -24,12 +24,12 @@
         ColumnRenderer<SingleClassData>,
         ColumnHeaderRenderer<SingleClassData> {
   _ClassNameColumn(this.data)
-      : super(
-          'Class',
-          titleTooltip: 'Class name',
-          fixedWidthPx: scaleByFontFactor(200.0),
-          alignment: ColumnAlignment.left,
-        );
+    : super(
+        'Class',
+        titleTooltip: 'Class name',
+        fixedWidthPx: scaleByFontFactor(200.0),
+        alignment: ColumnAlignment.left,
+      );
 
   final ClassesTableSingleData data;
 
@@ -74,12 +74,12 @@
 class _InstanceColumn extends ColumnData<SingleClassData>
     implements ColumnRenderer<SingleClassData> {
   _InstanceColumn(this.classData)
-      : super(
-          'Instances',
-          titleTooltip: nonGcableInstancesColumnTooltip,
-          fixedWidthPx: scaleByFontFactor(80.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        'Instances',
+        titleTooltip: nonGcableInstancesColumnTooltip,
+        fixedWidthPx: scaleByFontFactor(80.0),
+        alignment: ColumnAlignment.right,
+      );
 
   final ClassesTableSingleData classData;
 
@@ -108,12 +108,12 @@
 
 class _ShallowSizeColumn extends ColumnData<SingleClassData> {
   _ShallowSizeColumn()
-      : super(
-          'Shallow Dart Size',
-          titleTooltip: SizeType.shallow.description,
-          fixedWidthPx: scaleByFontFactor(120.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        'Shallow Dart Size',
+        titleTooltip: SizeType.shallow.description,
+        fixedWidthPx: scaleByFontFactor(120.0),
+        alignment: ColumnAlignment.right,
+      );
 
   @override
   int getValue(SingleClassData data) => data.objects.shallowSize;
@@ -122,20 +122,18 @@
   bool get numeric => true;
 
   @override
-  String getDisplayValue(SingleClassData data) => prettyPrintBytes(
-        getValue(data),
-        includeUnit: true,
-      )!;
+  String getDisplayValue(SingleClassData data) =>
+      prettyPrintBytes(getValue(data), includeUnit: true)!;
 }
 
 class _RetainedSizeColumn extends ColumnData<SingleClassData> {
   _RetainedSizeColumn(this.classData)
-      : super(
-          'Retained Dart Size',
-          titleTooltip: SizeType.retained.description,
-          fixedWidthPx: scaleByFontFactor(130.0),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        'Retained Dart Size',
+        titleTooltip: SizeType.retained.description,
+        fixedWidthPx: scaleByFontFactor(130.0),
+        alignment: ColumnAlignment.right,
+      );
 
   final ClassesTableSingleData classData;
 
@@ -151,8 +149,10 @@
 
     final bytes = prettyPrintRetainedSize(value)!;
 
-    final percents =
-        percent(value / classData.totalHeapSize(), fractionDigits: 0);
+    final percents = percent(
+      value / classData.totalHeapSize(),
+      fractionDigits: 0,
+    );
 
     return '$bytes ($percents)';
   }
@@ -198,10 +198,11 @@
       dataKey: dataKey,
       keyFactory: (e) => Key(e.className.fullName),
       selectionNotifier: classesData.selection,
-      onItemSelected: (_) => ga.select(
-        gac.memory,
-        gac.MemoryEvents.diffClassSingleSelect.name,
-      ),
+      onItemSelected:
+          (_) => ga.select(
+            gac.memory,
+            gac.MemoryEvents.diffClassSingleSelect.name,
+          ),
       defaultSortColumn: _columns.retainedSizeColumn,
       defaultSortDirection: SortDirection.descending,
     );
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart
index 2caf3fc..083cd1f 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/instances.dart
@@ -25,10 +25,11 @@
     super.key,
     required bool isSelected,
     this.liveItemsEnabled = true,
-  })  : _sampleObtainer = _shouldShowMenu(isSelected, objects)
-            ? SnapshotClassSampler(heapClass, objects, heap())
-            : null,
-        _count = objects.instanceCount;
+  }) : _sampleObtainer =
+           _shouldShowMenu(isSelected, objects)
+               ? SnapshotClassSampler(heapClass, objects, heap())
+               : null,
+       _count = objects.instanceCount;
 
   static bool _shouldShowMenu(bool isSelected, ObjectSet objects) =>
       isSelected && objects.instanceCount > 0;
@@ -42,10 +43,11 @@
   Widget build(BuildContext context) {
     return InstanceViewWithContextMenu(
       count: _count,
-      menuBuilder: () => _buildHeapInstancesMenu(
-        sampler: _sampleObtainer,
-        liveItemsEnabled: liveItemsEnabled,
-      ),
+      menuBuilder:
+          () => _buildHeapInstancesMenu(
+            sampler: _sampleObtainer,
+            liveItemsEnabled: liveItemsEnabled,
+          ),
     );
   }
 }
@@ -62,10 +64,7 @@
 }
 
 class _StoreAllAsVariableMenu extends StatelessWidget {
-  const _StoreAllAsVariableMenu(
-    this.sampler, {
-    required this.liveItemsEnabled,
-  });
+  const _StoreAllAsVariableMenu(this.sampler, {required this.liveItemsEnabled});
 
   final SnapshotClassSampler sampler;
 
@@ -86,14 +85,14 @@
       String title, {
       required bool subclasses,
       required bool implementers,
-    }) =>
-        MenuItemButton(
-          onPressed: () async => await sampler.allLiveToConsole(
+    }) => MenuItemButton(
+      onPressed:
+          () async => await sampler.allLiveToConsole(
             includeImplementers: implementers,
             includeSubclasses: subclasses,
           ),
-          child: Text(title),
-        );
+      child: Text(title),
+    );
 
     return SubmenuButton(
       menuChildren: <Widget>[
@@ -112,10 +111,7 @@
 }
 
 class _StoreAsOneVariableMenu extends StatelessWidget {
-  const _StoreAsOneVariableMenu(
-    this.sampler, {
-    required this.liveItemsEnabled,
-  });
+  const _StoreAsOneVariableMenu(this.sampler, {required this.liveItemsEnabled});
 
   final SnapshotClassSampler sampler;
   final bool liveItemsEnabled;
@@ -132,21 +128,23 @@
     return SubmenuButton(
       menuChildren: <Widget>[
         MenuItemButton(
-          onPressed: () => unawaited(
-            sampler.oneStaticToConsole(
-              sourceFeature: MemoryAreas.snapshotDiff.name,
-            ),
-          ),
+          onPressed:
+              () => unawaited(
+                sampler.oneStaticToConsole(
+                  sourceFeature: MemoryAreas.snapshotDiff.name,
+                ),
+              ),
           child: const Text('Any'),
         ),
         MenuItemButton(
-          onPressed: liveItemsEnabled
-              ? () => unawaited(
+          onPressed:
+              liveItemsEnabled
+                  ? () => unawaited(
                     sampler.oneLiveStaticToConsole(
                       sourceFeature: MemoryAreas.snapshotDiff.name,
                     ),
                   )
-              : null,
+                  : null,
           child: const Text('Any, not garbage collected'),
         ),
       ],
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_control_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_control_pane.dart
index 61d5b2c..e21f7f5 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_control_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_control_pane.dart
@@ -7,9 +7,9 @@
 
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/memory/simple_items.dart';
 import '../../../../../shared/primitives/byte_utils.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '../../../shared/primitives/simple_elements.dart';
 import '../controller/diff_pane_controller.dart';
 import '../controller/snapshot_item.dart';
@@ -29,10 +29,7 @@
         children: [
           Row(
             children: [
-              _DiffDropdown(
-                current: current,
-                controller: controller,
-              ),
+              _DiffDropdown(current: current, controller: controller),
               const SizedBox(width: defaultSpacing),
               DownloadButton(
                 tooltip: 'Download data in CSV format',
@@ -47,9 +44,7 @@
           ),
           if (current.heap!.footprint != null)
             Expanded(
-              child: _SnapshotSizeView(
-                footprint: current.heap!.footprint!,
-              ),
+              child: _SnapshotSizeView(footprint: current.heap!.footprint!),
             ),
         ],
       );
@@ -59,10 +54,7 @@
 }
 
 class _DiffDropdown extends StatelessWidget {
-  _DiffDropdown({
-    required this.current,
-    required this.controller,
-  }) {
+  _DiffDropdown({required this.current, required this.controller}) {
     final list = controller.core.snapshots.value;
     final diffWith = current.diffWith.value;
     // Check if diffWith was deleted from list.
@@ -78,47 +70,47 @@
       controller.core.snapshots.value
           .whereType<SnapshotDataItem>()
           .where((item) => item.isProcessed)
-          .map(
-        (item) {
-          return DropdownMenuItem<SnapshotDataItem>(
-            value: item,
-            child: Text(item == current ? '-' : item.name),
-          );
-        },
-      ).toList();
+          .map((item) {
+            return DropdownMenuItem<SnapshotDataItem>(
+              value: item,
+              child: Text(item == current ? '-' : item.name),
+            );
+          })
+          .toList();
 
   @override
   Widget build(BuildContext context) {
     return ValueListenableBuilder<SnapshotDataItem?>(
       valueListenable: current.diffWith,
-      builder: (_, diffWith, __) => Row(
-        children: [
-          const Text('Diff with:'),
-          const SizedBox(width: defaultSpacing),
-          RoundedDropDownButton<SnapshotDataItem>(
-            isDense: true,
-            value: current.diffWith.value ?? current,
-            onChanged: (SnapshotDataItem? value) {
-              late SnapshotDataItem? newDiffWith;
-              if ((value ?? current) == current) {
-                ga.select(
-                  gac.memory,
-                  gac.MemoryEvents.diffSnapshotDiffSelectOff.name,
-                );
-                newDiffWith = null;
-              } else {
-                ga.select(
-                  gac.memory,
-                  gac.MemoryEvents.diffSnapshotDiffSelect.name,
-                );
-                newDiffWith = value;
-              }
-              controller.setDiffing(current, newDiffWith);
-            },
-            items: items(),
+      builder:
+          (_, diffWith, _) => Row(
+            children: [
+              const Text('Diff with:'),
+              const SizedBox(width: defaultSpacing),
+              RoundedDropDownButton<SnapshotDataItem>(
+                isDense: true,
+                value: current.diffWith.value ?? current,
+                onChanged: (SnapshotDataItem? value) {
+                  late SnapshotDataItem? newDiffWith;
+                  if ((value ?? current) == current) {
+                    ga.select(
+                      gac.memory,
+                      gac.MemoryEvents.diffSnapshotDiffSelectOff.name,
+                    );
+                    newDiffWith = null;
+                  } else {
+                    ga.select(
+                      gac.memory,
+                      gac.MemoryEvents.diffSnapshotDiffSelect.name,
+                    );
+                    newDiffWith = value;
+                  }
+                  controller.setDiffing(current, newDiffWith);
+                },
+                items: items(),
+              ),
+            ],
           ),
-        ],
-      ),
     );
   }
 }
@@ -137,7 +129,8 @@
     return Text(
       items.entries
           .map<String>(
-            (e) => '${e.key}: '
+            (e) =>
+                '${e.key}: '
                 '${prettyPrintBytes(e.value, includeUnit: true, kbFractionDigits: 0)}',
           )
           // TODO(polina-c): consider using vertical divider instead of text.
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_list.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_list.dart
index edcdde9..6703691 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_list.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_list.dart
@@ -12,11 +12,11 @@
 
 import '../../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
-import '../../../../../shared/dialogs.dart';
 import '../../../../../shared/globals.dart';
 import '../../../../../shared/primitives/byte_utils.dart';
 import '../../../../../shared/primitives/utils.dart';
+import '../../../../../shared/ui/common_widgets.dart';
+import '../../../../../shared/ui/dialogs.dart';
 import '../controller/diff_pane_controller.dart';
 import '../controller/snapshot_item.dart';
 
@@ -39,9 +39,7 @@
             child: _ListControlPane(controller: controller),
           ),
         ),
-        Expanded(
-          child: _SnapshotListItems(controller: controller),
-        ),
+        Expanded(child: _SnapshotListItems(controller: controller)),
       ],
     );
   }
@@ -63,17 +61,19 @@
       if (!context.mounted) return;
       await showDialog(
         context: context,
-        builder: (context) => UnexpectedErrorDialog(
-          additionalInfo:
-              'Encountered an error while taking a heap snapshot:\n${e.runtimeType}\n$e\n$trace',
-        ),
+        builder:
+            (context) => UnexpectedErrorDialog(
+              additionalInfo:
+                  'Encountered an error while taking a heap snapshot:\n${e.runtimeType}\n$e\n$trace',
+            ),
       );
     }
   }
 
   @override
   Widget build(BuildContext context) {
-    final showTakeSnapshotButton = controller.loader != null &&
+    final showTakeSnapshotButton =
+        controller.loader != null &&
         !offlineDataController.showingOfflineData.value;
     return Row(
       children: [
@@ -93,15 +93,16 @@
               icon: Icons.delete,
               size: defaultIconSize,
               tooltip: 'Delete all snapshots',
-              onPressed: controller.hasSnapshots
-                  ? () {
-                      ga.select(
-                        gac.memory,
-                        gac.MemoryEvents.diffClearSnapshots.name,
-                      );
-                      controller.clearSnapshots();
-                    }
-                  : null,
+              onPressed:
+                  controller.hasSnapshots
+                      ? () {
+                        ga.select(
+                          gac.memory,
+                          gac.MemoryEvents.diffClearSnapshots.name,
+                        );
+                        controller.clearSnapshots();
+                      }
+                      : null,
             );
           },
         ),
@@ -182,29 +183,28 @@
           ContextMenuButton.defaultWidth + ContextMenuButton.densePadding;
       trailing.addAll([
         if (theItem.totalSize != null)
-          Text(
-            prettyPrintBytes(theItem.totalSize, includeUnit: true)!,
-          ),
+          Text(prettyPrintBytes(theItem.totalSize, includeUnit: true)!),
         Padding(
           padding: const EdgeInsets.only(left: ContextMenuButton.densePadding),
-          child: selected
-              ? ContextMenuButton(
-                  menuChildren: <Widget>[
-                    MenuItemButton(
-                      onPressed: onEdit,
-                      child: const Text('Rename'),
-                    ),
-                    MenuItemButton(
-                      onPressed: onDelete,
-                      child: const Text('Delete'),
-                    ),
-                    MenuItemButton(
-                      onPressed: onExport,
-                      child: const Text('Export'),
-                    ),
-                  ],
-                )
-              : const SizedBox(width: menuButtonWidth),
+          child:
+              selected
+                  ? ContextMenuButton(
+                    menuChildren: <Widget>[
+                      MenuItemButton(
+                        onPressed: onEdit,
+                        child: const Text('Rename'),
+                      ),
+                      MenuItemButton(
+                        onPressed: onDelete,
+                        child: const Text('Delete'),
+                      ),
+                      MenuItemButton(
+                        onPressed: onExport,
+                        child: const Text('Export'),
+                      ),
+                    ],
+                  )
+                  : const SizedBox(width: menuButtonWidth),
         ),
       ]);
     } else {
@@ -213,7 +213,7 @@
 
     return FutureBuilder(
       future: theItem is SnapshotDataItem ? theItem.process : null,
-      builder: (_, __) {
+      builder: (_, _) {
         final isProcessing =
             theItem is SnapshotDataItem ? !theItem.isProcessed : false;
 
@@ -384,11 +384,8 @@
     final core = widget.controller.core;
 
     return MultiValueListenableBuilder(
-      listenables: [
-        core.snapshots,
-        core.selectedSnapshotIndex,
-      ],
-      builder: (_, values, __) {
+      listenables: [core.snapshots, core.selectedSnapshotIndex],
+      builder: (_, values, _) {
         final snapshots = values.first as List<SnapshotItem>;
         final selectedIndex = values.second as int;
         return ListView.builder(
@@ -399,9 +396,10 @@
             final selected = selectedIndex == index;
             return Container(
               height: _headerHeight,
-              color: selected
-                  ? Theme.of(context).colorScheme.selectedRowBackgroundColor
-                  : null,
+              color:
+                  selected
+                      ? Theme.of(context).colorScheme.selectedRowBackgroundColor
+                      : null,
               child: InkWell(
                 canRequestFocus: false,
                 onTap: () {
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
index fe9698f..e9e277d 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/diff/widgets/snapshot_view.dart
@@ -5,9 +5,9 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/memory/classes.dart';
 import '../../../../../shared/primitives/utils.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '../controller/diff_pane_controller.dart';
 import '../data/classes_diff.dart';
 import 'class_details/class_details.dart';
@@ -26,7 +26,7 @@
         controller.derived.singleClassesToShow,
         controller.derived.diffClassesToShow,
       ],
-      builder: (_, values, __) {
+      builder: (_, values, _) {
         final singleClasses = values.first as ClassDataList<SingleClassData>?;
         final diffClasses = values.second as ClassDataList<DiffClassData>?;
         if (controller.derived.updatingValues) {
@@ -58,12 +58,13 @@
 
         final pathTable = ValueListenableBuilder<ClassData?>(
           valueListenable: controller.derived.classData,
-          builder: (_, classData, __) => HeapClassDetails(
-            classData: classData,
-            pathSelection: controller.derived.selectedPath,
-            isDiff: classes is ClassDataList<DiffClassData>,
-            pathController: controller.retainingPathController,
-          ),
+          builder:
+              (_, classData, _) => HeapClassDetails(
+                classData: classData,
+                pathSelection: controller.derived.selectedPath,
+                isDiff: classes is ClassDataList<DiffClassData>,
+                pathController: controller.retainingPathController,
+              ),
         );
 
         return SplitPane(
@@ -71,12 +72,8 @@
           initialFractions: const [0.4, 0.6],
           minSizes: const [80, 80],
           children: [
-            OutlineDecoration.onlyBottom(
-              child: classTable,
-            ),
-            OutlineDecoration.onlyTop(
-              child: pathTable,
-            ),
+            OutlineDecoration.onlyBottom(child: classTable),
+            OutlineDecoration.onlyTop(child: pathTable),
           ],
         );
       },
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/profile/instances.dart b/packages/devtools_app/lib/src/screens/memory/panes/profile/instances.dart
index 4c0338f..11418ab 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/profile/instances.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/profile/instances.dart
@@ -62,14 +62,14 @@
       String title, {
       required bool subclasses,
       required bool implementers,
-    }) =>
-        MenuItemButton(
-          onPressed: () async => await sampler.allLiveToConsole(
+    }) => MenuItemButton(
+      onPressed:
+          () async => await sampler.allLiveToConsole(
             includeImplementers: implementers,
             includeSubclasses: subclasses,
           ),
-          child: Text(title),
-        );
+      child: Text(title),
+    );
 
     return SubmenuButton(
       menuChildren: <Widget>[
@@ -95,9 +95,10 @@
   @override
   Widget build(BuildContext context) {
     return MenuItemButton(
-      onPressed: () => unawaited(
-        sampler.oneLiveToConsole(sourceFeature: MemoryAreas.profile.name),
-      ),
+      onPressed:
+          () => unawaited(
+            sampler.oneLiveToConsole(sourceFeature: MemoryAreas.profile.name),
+          ),
       child: const Text('Store one instance as a console variable'),
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/profile/model.dart b/packages/devtools_app/lib/src/screens/memory/panes/profile/model.dart
index 9689a29..681ec6a 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/profile/model.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/profile/model.dart
@@ -26,10 +26,10 @@
     required this.newSpaceGCStats,
     required this.oldSpaceGCStats,
     required this.totalGCStats,
-  })  : filter = ClassFilter.empty(),
-        _total = total,
-        _items = items,
-        _itemsFiltered = items;
+  }) : filter = ClassFilter.empty(),
+       _total = total,
+       _items = items,
+       _itemsFiltered = items;
 
   factory AdaptedProfile.fromAllocationProfile(
     AllocationProfile profile,
@@ -38,10 +38,11 @@
   ) {
     final adaptedProfile = AdaptedProfile._(
       total: ProfileRecord.total(profile),
-      items: (profile.members ?? [])
-          .where((e) => (e.instancesCurrent ?? 0) > 0)
-          .map((e) => ProfileRecord.fromClassHeapStats(e))
-          .toList(),
+      items:
+          (profile.members ?? [])
+              .where((e) => (e.instancesCurrent ?? 0) > 0)
+              .map((e) => ProfileRecord.fromClassHeapStats(e))
+              .toList(),
       newSpaceGCStats: profile.newSpaceGCStats,
       oldSpaceGCStats: profile.oldSpaceGCStats,
       totalGCStats: profile.totalGCStats,
@@ -54,9 +55,9 @@
     AdaptedProfile profile,
     this.filter,
     String? rootPackage,
-  )   : newSpaceGCStats = profile.newSpaceGCStats,
-        oldSpaceGCStats = profile.oldSpaceGCStats,
-        totalGCStats = profile.totalGCStats {
+  ) : newSpaceGCStats = profile.newSpaceGCStats,
+      oldSpaceGCStats = profile.oldSpaceGCStats,
+      totalGCStats = profile.totalGCStats {
     _items = profile._items;
     _total = profile._total;
 
@@ -73,9 +74,10 @@
   factory AdaptedProfile.fromJson(Map<String, dynamic> json) {
     return AdaptedProfile._(
       total: ProfileRecord.fromJson(json[_ProfileJson.total]),
-      items: (json[_ProfileJson.items] as List)
-          .map((e) => ProfileRecord.fromJson(e))
-          .toList(),
+      items:
+          (json[_ProfileJson.items] as List)
+              .map((e) => ProfileRecord.fromJson(e))
+              .toList(),
       newSpaceGCStats: GCStats.fromJson(json[_ProfileJson.newGC]),
       oldSpaceGCStats: GCStats.fromJson(json[_ProfileJson.oldGC]),
       totalGCStats: GCStats.fromJson(json[_ProfileJson.totalGC]),
@@ -160,7 +162,8 @@
       isTotal: false,
       heapClass: HeapClassName.fromClassRef(stats.classRef),
       totalInstances: stats.instancesCurrent ?? 0,
-      totalSize: stats.bytesCurrent! +
+      totalSize:
+          stats.bytesCurrent! +
           stats.oldSpace.externalSize +
           stats.newSpace.externalSize,
       totalDartHeapSize: stats.bytesCurrent!,
@@ -178,22 +181,22 @@
   }
 
   ProfileRecord.total(AllocationProfile profile)
-      : isTotal = true,
-        heapClass =
-            HeapClassName.fromPath(className: 'All Classes', library: ''),
-        totalInstances = null,
-        totalSize = (profile.memoryUsage?.externalUsage ?? 0) +
-            (profile.memoryUsage?.heapUsage ?? 0),
-        totalDartHeapSize = profile.memoryUsage?.heapUsage ?? 0,
-        totalExternalSize = profile.memoryUsage?.externalUsage ?? 0,
-        newSpaceInstances = null,
-        newSpaceSize = null,
-        newSpaceDartHeapSize = null,
-        newSpaceExternalSize = null,
-        oldSpaceInstances = null,
-        oldSpaceSize = null,
-        oldSpaceDartHeapSize = null,
-        oldSpaceExternalSize = null {
+    : isTotal = true,
+      heapClass = HeapClassName.fromPath(className: 'All Classes', library: ''),
+      totalInstances = null,
+      totalSize =
+          (profile.memoryUsage?.externalUsage ?? 0) +
+          (profile.memoryUsage?.heapUsage ?? 0),
+      totalDartHeapSize = profile.memoryUsage?.heapUsage ?? 0,
+      totalExternalSize = profile.memoryUsage?.externalUsage ?? 0,
+      newSpaceInstances = null,
+      newSpaceSize = null,
+      newSpaceDartHeapSize = null,
+      newSpaceExternalSize = null,
+      oldSpaceInstances = null,
+      oldSpaceSize = null,
+      oldSpaceDartHeapSize = null,
+      oldSpaceExternalSize = null {
     _verifyIntegrity();
   }
 
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart
index 5c1b6cc..d5775e5 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart
@@ -15,17 +15,11 @@
 import 'model.dart';
 
 @visibleForTesting
-enum Json {
-  profile,
-  rootPackage;
-}
+enum Json { profile, rootPackage }
 
 class ProfilePaneController extends DisposableController
     with AutoDisposeControllerMixin, Serializable {
-  ProfilePaneController({
-    required this.rootPackage,
-    AdaptedProfile? profile,
-  }) {
+  ProfilePaneController({required this.rootPackage, AdaptedProfile? profile}) {
     // [profile] should only be non-null when loading offline data.
     if (profile != null) {
       _currentAllocationProfile.value = AdaptedProfile.withNewFilter(
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart
index 715aaf3..9e4a1e2 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/profile/profile_view.dart
@@ -8,7 +8,6 @@
 
 import '../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/memory/gc_stats.dart';
 import '../../../../shared/primitives/byte_utils.dart';
@@ -17,6 +16,7 @@
 import '../../../../shared/table/table.dart';
 import '../../../../shared/table/table_controller.dart';
 import '../../../../shared/table/table_data.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../shared/heap/class_filter.dart';
 import '../../shared/primitives/simple_elements.dart';
 import '../../shared/widgets/class_filter.dart';
@@ -37,10 +37,7 @@
         ColumnRenderer<ProfileRecord>,
         ColumnHeaderRenderer<ProfileRecord> {
   _FieldClassNameColumn(this.classFilterData)
-      : super(
-          'Class',
-          fixedWidthPx: scaleByFontFactor(200),
-        );
+    : super('Class', fixedWidthPx: scaleByFontFactor(200));
 
   @override
   String? getValue(ProfileRecord dataObject) => dataObject.heapClass.className;
@@ -107,12 +104,12 @@
 class _FieldInstanceCountColumn extends ColumnData<ProfileRecord>
     implements ColumnRenderer<ProfileRecord> {
   _FieldInstanceCountColumn({required this.heap})
-      : super(
-          'Instances',
-          titleTooltip: 'The number of instances of the class in the heap',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Instances',
+        titleTooltip: 'The number of instances of the class in the heap',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   final HeapGeneration heap;
 
@@ -150,11 +147,11 @@
 
 class _FieldExternalSizeColumn extends _FieldSizeColumn {
   _FieldExternalSizeColumn({required super.heap})
-      : super._(
-          title: 'External',
-          titleTooltip:
-              'Non-Dart heap allocated memory associated with a Dart object',
-        );
+    : super._(
+        title: 'External',
+        titleTooltip:
+            'Non-Dart heap allocated memory associated with a Dart object',
+      );
 
   @override
   int? getValue(ProfileRecord dataObject) {
@@ -171,10 +168,7 @@
 
 class _FieldDartHeapSizeColumn extends _FieldSizeColumn {
   _FieldDartHeapSizeColumn({required super.heap})
-      : super._(
-          title: 'Dart Heap',
-          titleTooltip: SizeType.shallow.description,
-        );
+    : super._(title: 'Dart Heap', titleTooltip: SizeType.shallow.description);
 
   @override
   int? getValue(ProfileRecord dataObject) {
@@ -193,7 +187,8 @@
   factory _FieldSizeColumn({required HeapGeneration heap}) =>
       _FieldSizeColumn._(
         title: 'Total Size',
-        titleTooltip: "The sum of the type's total shallow memory "
+        titleTooltip:
+            "The sum of the type's total shallow memory "
             'consumption in the Dart heap and associated external (e.g., '
             'non-Dart heap) allocations',
         heap: heap,
@@ -204,11 +199,11 @@
     required String titleTooltip,
     required this.heap,
   }) : super(
-          title,
-          titleTooltip: titleTooltip,
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+         title,
+         titleTooltip: titleTooltip,
+         alignment: ColumnAlignment.right,
+         fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+       );
 
   final HeapGeneration heap;
 
@@ -259,11 +254,7 @@
 }
 
 class _GCHeapNameColumn extends ColumnData<AdaptedProfile> {
-  _GCHeapNameColumn()
-      : super(
-          '',
-          fixedWidthPx: scaleByFontFactor(200),
-        );
+  _GCHeapNameColumn() : super('', fixedWidthPx: scaleByFontFactor(200));
 
   @override
   String? getValue(AdaptedProfile dataObject) {
@@ -276,12 +267,12 @@
 
 class _GCHeapUsageColumn extends _GCHeapStatsColumn {
   _GCHeapUsageColumn({required super.generation})
-      : super(
-          'Usage',
-          titleTooltip: 'The current amount of memory allocated from the heap',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Usage',
+        titleTooltip: 'The current amount of memory allocated from the heap',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   @override
   int? getValue(AdaptedProfile dataObject) {
@@ -299,13 +290,13 @@
 
 class _GCHeapCapacityColumn extends _GCHeapStatsColumn {
   _GCHeapCapacityColumn({required super.generation})
-      : super(
-          'Capacity',
-          titleTooltip:
-              'The current size of the heap, including unallocated memory',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Capacity',
+        titleTooltip:
+            'The current size of the heap, including unallocated memory',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   @override
   int? getValue(AdaptedProfile dataObject) {
@@ -323,12 +314,12 @@
 
 class _GCCountColumn extends _GCHeapStatsColumn {
   _GCCountColumn({required super.generation})
-      : super(
-          'Collections',
-          titleTooltip: 'The number of garbage collections run on the heap',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Collections',
+        titleTooltip: 'The number of garbage collections run on the heap',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   @override
   int? getValue(AdaptedProfile dataObject) {
@@ -341,13 +332,13 @@
 
 class _GCLatencyColumn extends _GCHeapStatsColumn {
   _GCLatencyColumn({required super.generation})
-      : super(
-          'Latency',
-          titleTooltip:
-              'The average time taken to perform a garbage collection on the heap (ms)',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Latency',
+        titleTooltip:
+            'The average time taken to perform a garbage collection on the heap (ms)',
+        alignment: ColumnAlignment.right,
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   @override
   double? getValue(AdaptedProfile dataObject) {
@@ -361,9 +352,7 @@
   @override
   String getDisplayValue(AdaptedProfile dataObject) {
     return durationText(
-      Duration(
-        milliseconds: getValue(dataObject)!.toInt(),
-      ),
+      Duration(milliseconds: getValue(dataObject)!.toInt()),
       fractionDigits: 2,
     );
   }
@@ -373,15 +362,10 @@
 }
 
 class _GCStatsTable extends StatelessWidget {
-  const _GCStatsTable({
-    required this.controller,
-  });
+  const _GCStatsTable({required this.controller});
 
   static final _columnGroup = [
-    ColumnGroup.fromText(
-      title: '',
-      range: const Range(0, 1),
-    ),
+    ColumnGroup.fromText(title: '', range: const Range(0, 1)),
     ColumnGroup.fromText(
       title: HeapGeneration.total.toString(),
       range: const Range(1, 5),
@@ -405,12 +389,8 @@
     ]) ...[
       _GCHeapUsageColumn(generation: generation),
       _GCHeapCapacityColumn(generation: generation),
-      _GCCountColumn(
-        generation: generation,
-      ),
-      _GCLatencyColumn(
-        generation: generation,
-      ),
+      _GCCountColumn(generation: generation),
+      _GCLatencyColumn(generation: generation),
     ],
   ];
 
@@ -475,9 +455,7 @@
       children: [
         Padding(
           padding: const EdgeInsets.all(denseSpacing),
-          child: _AllocationProfileTableControls(
-            controller: widget.controller,
-          ),
+          child: _AllocationProfileTableControls(controller: widget.controller),
         ),
         ValueListenableBuilder<bool>(
           valueListenable: preferences.vmDeveloperModeEnabled,
@@ -491,36 +469,25 @@
                   // ensure the underlying scrollable area has enough space to not
                   // display a scroll bar.
                   height: defaultRowHeight + defaultHeaderHeight * 2 + 1,
-                  child: _GCStatsTable(
-                    controller: widget.controller,
-                  ),
+                  child: _GCStatsTable(controller: widget.controller),
                 ),
                 const ThickDivider(),
               ],
             );
           },
         ),
-        Expanded(
-          child: _AllocationProfileTable(
-            controller: widget.controller,
-          ),
-        ),
+        Expanded(child: _AllocationProfileTable(controller: widget.controller)),
       ],
     );
   }
 }
 
 class _AllocationProfileTable extends StatelessWidget {
-  _AllocationProfileTable({
-    required this.controller,
-  });
+  _AllocationProfileTable({required this.controller});
 
   /// List of columns displayed in VM developer mode state.
   static final _vmModeColumnGroups = [
-    ColumnGroup.fromText(
-      title: '',
-      range: const Range(0, 1),
-    ),
+    ColumnGroup.fromText(title: '', range: const Range(0, 1)),
     ColumnGroup.fromText(
       title: HeapGeneration.total.toString(),
       range: const Range(1, 5),
@@ -535,9 +502,7 @@
     ),
   ];
 
-  static final _fieldSizeColumn = _FieldSizeColumn(
-    heap: HeapGeneration.total,
-  );
+  static final _fieldSizeColumn = _FieldSizeColumn(heap: HeapGeneration.total);
 
   late final _columns = <ColumnData<ProfileRecord>>[
     _FieldClassNameColumn(
@@ -584,9 +549,10 @@
               keyFactory: (element) => Key(element.heapClass.fullName),
               data: profile.records,
               dataKey: 'allocation-profile',
-              columnGroups: vmDeveloperModeEnabled
-                  ? _AllocationProfileTable._vmModeColumnGroups
-                  : null,
+              columnGroups:
+                  vmDeveloperModeEnabled
+                      ? _AllocationProfileTable._vmModeColumnGroups
+                      : null,
               columns: [
                 ..._columns,
                 if (vmDeveloperModeEnabled) ..._vmDeveloperModeColumns,
@@ -605,9 +571,7 @@
 }
 
 class _AllocationProfileTableControls extends StatelessWidget {
-  const _AllocationProfileTableControls({
-    required this.controller,
-  });
+  const _AllocationProfileTableControls({required this.controller});
 
   final ProfilePaneController controller;
 
@@ -615,9 +579,7 @@
   Widget build(BuildContext context) {
     return Row(
       children: [
-        _ExportAllocationProfileButton(
-          allocationProfileController: controller,
-        ),
+        _ExportAllocationProfileButton(allocationProfileController: controller),
         if (!offlineDataController.showingOfflineData.value) ...[
           const SizedBox(width: denseSpacing),
           RefreshButton(
@@ -626,9 +588,7 @@
             onPressed: controller.refresh,
           ),
           const SizedBox(width: denseSpacing),
-          _RefreshOnGCToggleButton(
-            allocationProfileController: controller,
-          ),
+          _RefreshOnGCToggleButton(allocationProfileController: controller),
         ],
         const SizedBox(width: denseSpacing),
         const _ProfileHelpLink(),
@@ -655,10 +615,12 @@
           minScreenWidthForTextBeforeScaling: memoryControlsMinVerboseWidth,
           tooltip: 'Download allocation profile data in CSV format',
           label: 'CSV',
-          onPressed: currentAllocationProfile == null
-              ? null
-              : () => allocationProfileController
-                  .downloadMemoryTableCsv(currentAllocationProfile),
+          onPressed:
+              currentAllocationProfile == null
+                  ? null
+                  : () => allocationProfileController.downloadMemoryTableCsv(
+                    currentAllocationProfile,
+                  ),
         );
       },
     );
@@ -666,9 +628,7 @@
 }
 
 class _RefreshOnGCToggleButton extends StatelessWidget {
-  const _RefreshOnGCToggleButton({
-    required this.allocationProfileController,
-  });
+  const _RefreshOnGCToggleButton({required this.allocationProfileController});
 
   final ProfilePaneController allocationProfileController;
 
@@ -702,15 +662,17 @@
   Widget build(BuildContext context) {
     return HelpButtonWithDialog(
       gaScreen: gac.memory,
-      gaSelection:
-          gac.topicDocumentationButton(gac.MemoryEvents.profileHelp.name),
+      gaSelection: gac.topicDocumentationButton(
+        gac.MemoryEvents.profileHelp.name,
+      ),
       dialogTitle: 'Memory Allocation Profile Help',
       actions: [
         MoreInfoLink(
           url: DocLinks.profile.value,
           gaScreenName: '',
-          gaSelectedItemDescription:
-              gac.topicDocumentationLink(gac.MemoryEvents.profileHelp.name),
+          gaSelectedItemDescription: gac.topicDocumentationLink(
+            gac.MemoryEvents.profileHelp.name,
+          ),
         ),
       ],
       child: const Column(
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/tracing/class_table.dart b/packages/devtools_app/lib/src/screens/memory/panes/tracing/class_table.dart
index 7135fc5..3586fc3 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/tracing/class_table.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/tracing/class_table.dart
@@ -7,11 +7,11 @@
 
 import '../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/primitives/utils.dart';
 import '../../../../shared/table/table.dart';
 import '../../../../shared/table/table_controller.dart';
 import '../../../../shared/table/table_data.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../shared/widgets/shared_memory_widgets.dart';
 import 'tracing_data.dart';
 import 'tracing_pane_controller.dart';
@@ -23,13 +23,13 @@
 class _TraceCheckBoxColumn extends ColumnData<TracedClass>
     implements ColumnRenderer<TracedClass> {
   _TraceCheckBoxColumn({required this.controller})
-      : super(
-          'Trace',
-          titleTooltip:
-              'Enable or disable allocation tracing for a specific type',
-          fixedWidthPx: scaleByFontFactor(40.0),
-          alignment: ColumnAlignment.left,
-        );
+    : super(
+        'Trace',
+        titleTooltip:
+            'Enable or disable allocation tracing for a specific type',
+        fixedWidthPx: scaleByFontFactor(40.0),
+        alignment: ColumnAlignment.left,
+      );
 
   final TracePaneController controller;
 
@@ -99,12 +99,12 @@
 
 class _InstancesColumn extends ColumnData<TracedClass> {
   _InstancesColumn()
-      : super(
-          'Delta',
-          titleTooltip:
-              'Number of instances, allocated after the class was selected for tracing.',
-          fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
-        );
+    : super(
+        'Delta',
+        titleTooltip:
+            'Number of instances, allocated after the class was selected for tracing.',
+        fixedWidthPx: scaleByFontFactor(_defaultNumberFieldWidth),
+      );
 
   @override
   int getValue(TracedClass dataObject) {
@@ -135,8 +135,9 @@
   void initState() {
     super.initState();
 
-    _classNameColumn =
-        _ClassNameColumn(rootPackage: widget.controller.rootPackage);
+    _classNameColumn = _ClassNameColumn(
+      rootPackage: widget.controller.rootPackage,
+    );
 
     _checkboxColumn = _TraceCheckBoxColumn(controller: widget.controller);
     columns = <ColumnData<TracedClass>>[
@@ -152,10 +153,7 @@
   void _sendFilterEditGaEvent() {
     final now = DateTime.now();
     if (now.difference(_editFilterGaSent) < _editFilterGaThrottling) return;
-    ga.select(
-      gac.memory,
-      gac.MemoryEvents.tracingClassFilter.name,
-    );
+    ga.select(gac.memory, gac.MemoryEvents.tracingClassFilter.name);
     _editFilterGaSent = now;
   }
 
@@ -181,7 +179,7 @@
               widget.controller.refreshing,
               widget.controller.selection,
             ],
-            builder: (context, values, __) {
+            builder: (context, values, _) {
               final state = values.second as TracingIsolateState;
               return ValueListenableBuilder<List<TracedClass>>(
                 valueListenable: state.filteredClassList,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_data.dart b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_data.dart
index 438e5bf..31495ba 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_data.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_data.dart
@@ -16,19 +16,14 @@
 import '../../../profiler/cpu_profile_transformer.dart';
 
 @visibleForTesting
-enum TracedClassJson {
-  clazz,
-  instances,
-  allocations,
-}
+enum TracedClassJson { clazz, instances, allocations }
 
 /// A representation of a class and it's allocation tracing state.
 class TracedClass with PinnableListEntry, Serializable {
-  TracedClass({
-    required this.clazz,
-  })  : traceAllocations = false,
-        instances = 0,
-        name = HeapClassName.fromClassRef(clazz);
+  TracedClass({required this.clazz})
+    : traceAllocations = false,
+      instances = 0,
+      name = HeapClassName.fromClassRef(clazz);
 
   TracedClass._({
     required this.clazz,
@@ -39,8 +34,9 @@
   factory TracedClass.fromJson(Map<String, dynamic> json) {
     return TracedClass._(
       instances: json[TracedClassJson.instances.name] as int,
-      clazz: ClassRefEncodeDecode.instance
-          .decode(json[TracedClassJson.clazz.name]),
+      clazz: ClassRefEncodeDecode.instance.decode(
+        json[TracedClassJson.clazz.name],
+      ),
       traceAllocations: json[TracedClassJson.allocations.name] as bool,
     );
   }
@@ -92,11 +88,7 @@
 
 // TODO(kenz): include the selected class in the toJson and fromJson methods.
 @visibleForTesting
-enum TracingIsolateStateJson {
-  isolate,
-  classes,
-  profiles;
-}
+enum TracingIsolateStateJson { isolate, classes, profiles }
 
 /// Contains allocation tracing state for a single isolate.
 ///
@@ -118,17 +110,19 @@
 
   factory TracingIsolateState.fromJson(Map<String, dynamic> json) {
     return TracingIsolateState(
-      isolate: IsolateRefEncodeDecode.instance
-          .decode(json[TracingIsolateStateJson.isolate.name]),
+      isolate: IsolateRefEncodeDecode.instance.decode(
+        json[TracingIsolateStateJson.isolate.name],
+      ),
       profiles: (json[TracingIsolateStateJson.profiles.name] as Map).map(
         (key, value) => MapEntry(
           key,
           deserialize<CpuProfileData>(value, CpuProfileData.fromJson),
         ),
       ),
-      classes: (json[TracingIsolateStateJson.classes.name] as List)
-          .map((e) => deserialize<TracedClass>(e, TracedClass.fromJson))
-          .toList(),
+      classes:
+          (json[TracingIsolateStateJson.classes.name] as List)
+              .map((e) => deserialize<TracedClass>(e, TracedClass.fromJson))
+              .toList(),
     );
   }
 
@@ -207,9 +201,7 @@
         (newFilter.caseInsensitiveContains(currentFilter) && !force
                 ? _filteredClassList.value
                 : classes)
-            .where(
-              (e) => e.clazz.name!.caseInsensitiveContains(newFilter),
-            )
+            .where((e) => e.clazz.name!.caseInsensitiveContains(newFilter))
             .map((e) => classesById[e.clazz.id!]!)
             .toList();
 
@@ -254,9 +246,7 @@
     // Only update if the tracing state has changed for `clazz`.
     if (tracedClass.traceAllocations != enabled) {
       await service.setTraceClassAllocation(isolate.id!, clazz.id!, enabled);
-      final update = tracedClass.copyWith(
-        traceAllocations: enabled,
-      );
+      final update = tracedClass.copyWith(traceAllocations: enabled);
       _updateClassState(tracedClass, update);
     }
   }
@@ -280,8 +270,13 @@
       );
     }
     final service = serviceConnection.serviceManager.service!;
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
     final clazz = tracedClass.clazz;
 
     // Note: we need to provide `timeExtentMicros` to `getAllocationTraces`,
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_pane_controller.dart b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_pane_controller.dart
index 50e1d72..8ab2162 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_pane_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_pane_controller.dart
@@ -15,11 +15,7 @@
 import 'tracing_data.dart';
 
 @visibleForTesting
-enum Json {
-  stateForIsolate,
-  selection,
-  rootPackage;
-}
+enum Json { stateForIsolate, selection, rootPackage }
 
 class TracePaneController extends DisposableController
     with AutoDisposeControllerMixin, Serializable {
@@ -29,10 +25,9 @@
     required this.rootPackage,
   }) {
     this.stateForIsolate = stateForIsolate ?? {};
-    final isolate = this
-        .stateForIsolate
-        .values
-        .firstWhereOrNull((i) => i.isolate.id == selectedIsolateId);
+    final isolate = this.stateForIsolate.values.firstWhereOrNull(
+      (i) => i.isolate.id == selectedIsolateId,
+    );
     if (selectedIsolateId != null && isolate == null) {
       throw ArgumentError(
         '$selectedIsolateId must be a key in stateForIsolate',
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_tree.dart b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_tree.dart
index 8111719..7a1e037 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_tree.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_tree.dart
@@ -7,10 +7,10 @@
 import 'package:flutter_markdown/flutter_markdown.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/primitives/utils.dart';
 import '../../../../shared/table/table.dart';
 import '../../../../shared/table/table_data.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../../../shared/ui/tab.dart';
 import '../../../profiler/cpu_profile_model.dart';
 import '../../../profiler/panes/cpu_profile_columns.dart';
@@ -28,10 +28,7 @@
 
   static final _bottomUpTab = _buildTab(tabName: 'Bottom Up');
   static final _callTreeTab = _buildTab(tabName: 'Call Tree');
-  static final tabs = [
-    _bottomUpTab,
-    _callTreeTab,
-  ];
+  static final tabs = [_bottomUpTab, _callTreeTab];
 
   static DevToolsTab _buildTab({Key? key, required String tabName}) {
     return DevToolsTab.create(
@@ -78,7 +75,8 @@
               return const _TracingInstructions();
             } else if (!selection.traceAllocations) {
               return _TracingInstructions(
-                prefix: 'Allocation tracing is not enabled for class '
+                prefix:
+                    'Allocation tracing is not enabled for class '
                     '${selection.clazz.name}.',
               );
             } else if (selection.traceAllocations &&
@@ -124,9 +122,7 @@
 }
 
 class _TracingInstructions extends StatelessWidget {
-  const _TracingInstructions({
-    this.prefix,
-  });
+  const _TracingInstructions({this.prefix});
 
   final String? prefix;
 
@@ -138,9 +134,7 @@
     }
     return Markdown(
       data: data,
-      styleSheet: MarkdownStyleSheet(
-        p: Theme.of(context).regularTextStyle,
-      ),
+      styleSheet: MarkdownStyleSheet(p: Theme.of(context).regularTextStyle),
     );
   }
 }
@@ -180,9 +174,7 @@
       title: Text.rich(
         TextSpan(
           children: [
-            const TextSpan(
-              text: 'Traced allocations for: ',
-            ),
+            const TextSpan(text: 'Traced allocations for: '),
             TextSpan(
               style: theme.fixedFontStyle,
               text: controller.selection.value.selectedClass.value?.clazz.name!,
@@ -204,25 +196,23 @@
         ExpandAllButton(
           gaScreen: gac.memory,
           gaSelection: gac.MemoryEvents.tracingTreeExpandAll.name,
-          onPressed: () => updateTreeStateCallback(
-            () {
-              for (final root in _currentDataRoots) {
-                root.expandCascading();
-              }
-            },
-          ),
+          onPressed:
+              () => updateTreeStateCallback(() {
+                for (final root in _currentDataRoots) {
+                  root.expandCascading();
+                }
+              }),
         ),
         const SizedBox(width: denseSpacing),
         CollapseAllButton(
           gaScreen: gac.memory,
           gaSelection: gac.MemoryEvents.tracingTreeCollapseAll.name,
-          onPressed: () => updateTreeStateCallback(
-            () {
-              for (final root in _currentDataRoots) {
-                root.collapseCascading();
-              }
-            },
-          ),
+          onPressed:
+              () => updateTreeStateCallback(() {
+                for (final root in _currentDataRoots) {
+                  root.collapseCascading();
+                }
+              }),
         ),
       ],
     );
@@ -238,11 +228,11 @@
 
 class _InclusiveCountColumn extends ColumnData<CpuStackFrame> {
   _InclusiveCountColumn()
-      : super(
-          'Inclusive',
-          titleTooltip: _tooltip,
-          fixedWidthPx: scaleByFontFactor(_countColumnWidth),
-        );
+    : super(
+        'Inclusive',
+        titleTooltip: _tooltip,
+        fixedWidthPx: scaleByFontFactor(_countColumnWidth),
+      );
 
   static const _tooltip =
       'The number of instances allocated by calls made from a stack frame.';
@@ -271,11 +261,11 @@
 
 class _ExclusiveCountColumn extends ColumnData<CpuStackFrame> {
   _ExclusiveCountColumn()
-      : super(
-          'Exclusive',
-          titleTooltip: _tooltip,
-          fixedWidthPx: scaleByFontFactor(_countColumnWidth),
-        );
+    : super(
+        'Exclusive',
+        titleTooltip: _tooltip,
+        fixedWidthPx: scaleByFontFactor(_countColumnWidth),
+      );
 
   static const _tooltip =
       'The number of instances allocated directly by a stack frame.';
@@ -304,10 +294,7 @@
 
 /// A table of an allocation profile tree.
 class TracingTable extends StatelessWidget {
-  const TracingTable({
-    super.key,
-    required this.dataRoots,
-  });
+  const TracingTable({super.key, required this.dataRoots});
 
   static final treeColumn = MethodAndSourceColumn();
   static final startingSortColumn = _InclusiveCountColumn();
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_view.dart b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_view.dart
index bcfac97..1887649 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_view.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/tracing/tracing_view.dart
@@ -8,19 +8,16 @@
 import 'package:flutter/material.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/primitives/simple_items.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../shared/widgets/shared_memory_widgets.dart';
 import 'class_table.dart';
 import 'tracing_pane_controller.dart';
 import 'tracing_tree.dart';
 
 class TracingPane extends StatefulWidget {
-  const TracingPane({
-    super.key,
-    required this.controller,
-  });
+  const TracingPane({super.key, required this.controller});
 
   final TracePaneController controller;
 
@@ -49,7 +46,7 @@
   Widget build(BuildContext context) {
     final isProfileMode =
         serviceConnection.serviceManager.connectedApp?.isProfileBuildNow ??
-            false;
+        false;
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
@@ -64,14 +61,10 @@
               initialFractions: const [0.25, 0.75],
               children: [
                 OutlineDecoration.onlyRight(
-                  child: AllocationTracingTable(
-                    controller: widget.controller,
-                  ),
+                  child: AllocationTracingTable(controller: widget.controller),
                 ),
                 OutlineDecoration.onlyLeft(
-                  child: AllocationTracingTree(
-                    controller: widget.controller,
-                  ),
+                  child: AllocationTracingTree(controller: widget.controller),
                 ),
               ],
             ),
@@ -136,8 +129,9 @@
         MoreInfoLink(
           url: DocLinks.trace.value,
           gaScreenName: gac.memory,
-          gaSelectedItemDescription:
-              gac.topicDocumentationLink(_documentationTopic),
+          gaSelectedItemDescription: gac.topicDocumentationLink(
+            _documentationTopic,
+          ),
         ),
       ],
       child: const Column(
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/heap/class_filter.dart b/packages/devtools_app/lib/src/screens/memory/shared/heap/class_filter.dart
index 58fb314..fb67ae1 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/heap/class_filter.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/heap/class_filter.dart
@@ -8,11 +8,7 @@
 
 import '../../../../shared/memory/class_name.dart';
 
-enum ClassFilterType {
-  showAll,
-  except,
-  only,
-}
+enum ClassFilterType { showAll, except, only }
 
 class ClassFilterData {
   ClassFilterData({
@@ -57,29 +53,29 @@
     required this.filterType,
     required String except,
     required String? only,
-  })  : except = _trimByLine(except),
-        only = only == null ? null : _trimByLine(only);
+  }) : except = _trimByLine(except),
+       only = only == null ? null : _trimByLine(only);
 
   ClassFilter.theDefault()
-      : this(
-          filterType: _defaultFilterType,
-          except: defaultExceptString,
-          only: null,
-        );
+    : this(
+        filterType: _defaultFilterType,
+        except: defaultExceptString,
+        only: null,
+      );
 
   ClassFilter.empty()
-      : this(
-          filterType: ClassFilterType.showAll,
-          except: defaultExceptString,
-          only: null,
-        );
+    : this(
+        filterType: ClassFilterType.showAll,
+        except: defaultExceptString,
+        only: null,
+      );
 
   factory ClassFilter.fromJson(Map<String, dynamic> json) {
     final type = json[_Json.type] as String?;
     return ClassFilter(
       filterType:
           ClassFilterType.values.lastWhereOrNull((t) => t.name == type) ??
-              _defaultFilterType,
+          _defaultFilterType,
       except: json[_Json.except] as String? ?? defaultExceptString,
       only: json[_Json.only] as String?,
     );
@@ -132,13 +128,14 @@
   int get hashCode => Object.hash(filterType, except, only);
 
   Set<String> _filtersAsSet() {
-    Set<String> stringToSet(String? s) => s == null
-        ? {}
-        : s
-            .split(RegExp(',|\n'))
-            .map((e) => e.trim())
-            .where((e) => e.isNotEmpty)
-            .toSet();
+    Set<String> stringToSet(String? s) =>
+        s == null
+            ? {}
+            : s
+                .split(RegExp(',|\n'))
+                .map((e) => e.trim())
+                .where((e) => e.isNotEmpty)
+                .toSet();
 
     switch (filterType) {
       case ClassFilterType.showAll:
@@ -240,9 +237,10 @@
       throw StateError('Unexpected task: $task.');
     }
 
-    final result = dataToFilter
-        .where((e) => newFilter.apply(extractClass(e), rootPackage))
-        .toList();
+    final result =
+        dataToFilter
+            .where((e) => newFilter.apply(extractClass(e), rootPackage))
+            .toList();
 
     return result;
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/heap/sampler.dart b/packages/devtools_app/lib/src/screens/memory/shared/heap/sampler.dart
index d8d6229..7f9b42b 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/heap/sampler.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/heap/sampler.dart
@@ -12,7 +12,7 @@
 import '../../../../shared/memory/classes.dart';
 import '../../../../shared/memory/heap_data.dart';
 import '../../../../shared/memory/heap_object.dart';
-import '../../../../shared/vm_utils.dart';
+import '../../../../shared/utils/vm_utils.dart';
 
 class _HeapObjects {
   _HeapObjects(this.objects, this.heap);
@@ -32,13 +32,10 @@
 }
 
 class LiveClassSampler {
-  LiveClassSampler(
-    this.heapClass, {
-    ObjectSet? objects,
-    HeapData? heap,
-  })  : assert(objects?.indexes.isNotEmpty ?? true),
-        assert((objects == null) == (heap == null)),
-        _objects = objects == null ? null : _HeapObjects(objects, heap!);
+  LiveClassSampler(this.heapClass, {ObjectSet? objects, HeapData? heap})
+    : assert(objects?.indexes.isNotEmpty ?? true),
+      assert((objects == null) == (heap == null)),
+      _objects = objects == null ? null : _HeapObjects(objects, heap!);
 
   final HeapClassName heapClass;
   final _HeapObjects? _objects;
@@ -55,11 +52,10 @@
 
       final object =
           (await serviceConnection.serviceManager.service!.getInstances(
-        isolateId,
-        theClass.id!,
-        1,
-      ))
-              .instances?[0];
+            isolateId,
+            theClass.id!,
+            1,
+          )).instances?[0];
 
       if (object is InstanceRef) return object;
       return null;
@@ -109,8 +105,9 @@
   }
 
   bool get isEvalEnabled =>
-      heapClass
-          .classType(serviceConnection.serviceManager.rootInfoNow().package) !=
+      heapClass.classType(
+        serviceConnection.serviceManager.rootInfoNow().package,
+      ) !=
       ClassType.runtime;
 
   Future<void> allLiveToConsole({
@@ -161,11 +158,8 @@
 }
 
 class SnapshotClassSampler extends LiveClassSampler {
-  SnapshotClassSampler(
-    super.heapClass,
-    ObjectSet objects,
-    HeapData heap,
-  ) : super(heap: heap, objects: objects);
+  SnapshotClassSampler(super.heapClass, ObjectSet objects, HeapData heap)
+    : super(heap: heap, objects: objects);
 
   Future<void> oneLiveStaticToConsole({required String sourceFeature}) async {
     ga.select(
@@ -176,11 +170,13 @@
     final heapObjects = _objects!;
     final instances = (await _liveInstances())?.instances;
 
-    final instanceRef = instances?.firstWhereOrNull(
-      (objRef) =>
-          objRef is InstanceRef &&
-          heapObjects.codes.contains(objRef.identityHashCode),
-    ) as InstanceRef?;
+    final instanceRef =
+        instances?.firstWhereOrNull(
+              (objRef) =>
+                  objRef is InstanceRef &&
+                  heapObjects.codes.contains(objRef.identityHashCode),
+            )
+            as InstanceRef?;
 
     if (instanceRef == null) {
       serviceConnection.consoleService.appendStdio(
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_context_menu.dart b/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_context_menu.dart
index 3ff1b03..fa37269 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_context_menu.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/primitives/instance_context_menu.dart
@@ -4,8 +4,8 @@
 
 import 'package:flutter/material.dart';
 
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/primitives/utils.dart';
+import '../../../../shared/ui/common_widgets.dart';
 
 typedef MenuBuilder = List<Widget> Function();
 
@@ -30,12 +30,7 @@
       mainAxisSize: MainAxisSize.min,
       mainAxisAlignment: MainAxisAlignment.end,
       children: [
-        Expanded(
-          child: Text(
-            nf.format(count),
-            textAlign: TextAlign.end,
-          ),
-        ),
+        Expanded(child: Text(nf.format(count), textAlign: TextAlign.end)),
         if (shouldShowMenu)
           ContextMenuButton(
             // ignore: avoid_redundant_argument_values, ensures consistency with [SizedBox] below.
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/primitives/memory_timeline.dart b/packages/devtools_app/lib/src/screens/memory/shared/primitives/memory_timeline.dart
index 3b7573e..0831100 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/primitives/memory_timeline.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/primitives/memory_timeline.dart
@@ -16,17 +16,16 @@
 
   factory MemoryTimeline.fromJson(Map<String, dynamic> json) {
     return MemoryTimeline(
-      data: (json[_jsonData] as List)
-          .map((e) => HeapSample.fromJson(e as Map<String, dynamic>))
-          .toList(),
+      data:
+          (json[_jsonData] as List)
+              .map((e) => HeapSample.fromJson(e as Map<String, dynamic>))
+              .toList(),
     );
   }
 
   @override
   Map<String, dynamic> toJson() {
-    return {
-      _jsonData: data,
-    };
+    return {_jsonData: data};
   }
 
   static const _jsonData = 'data';
@@ -79,10 +78,8 @@
   static bool isCustomEvent(String extensionEvent) =>
       extensionEvent.startsWith(devToolsExtensionEvent);
 
-  static String customEventName(String extensionEventKind) =>
-      extensionEventKind.substring(
-        MemoryTimeline.devToolsExtensionEvent.length,
-      );
+  static String customEventName(String extensionEventKind) => extensionEventKind
+      .substring(MemoryTimeline.devToolsExtensionEvent.length);
 
   void addSample(HeapSample sample) {
     data.add(sample);
@@ -95,9 +92,15 @@
     Map<String, Object> json, {
     String? customEventName,
   }) {
-    final extensionEvent = customEventName == null
-        ? ExtensionEvent(timestamp, eventKind, json)
-        : ExtensionEvent.custom(timestamp, eventKind, customEventName, json);
+    final extensionEvent =
+        customEventName == null
+            ? ExtensionEvent(timestamp, eventKind, json)
+            : ExtensionEvent.custom(
+              timestamp,
+              eventKind,
+              customEventName,
+              json,
+            );
 
     _extensionEvents.add(extensionEvent);
   }
@@ -119,11 +122,6 @@
 
   void addGCEvent() {
     final timestamp = DateTime.now().millisecondsSinceEpoch;
-    _eventSamples.add(
-      EventSample.gcEvent(
-        timestamp,
-        events: extensionEvents,
-      ),
-    );
+    _eventSamples.add(EventSample.gcEvent(timestamp, events: extensionEvents));
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/primitives/painting.dart b/packages/devtools_app/lib/src/screens/memory/shared/primitives/painting.dart
index 55181d4..842f1e4 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/primitives/painting.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/primitives/painting.dart
@@ -10,18 +10,9 @@
 const _spaceBetweenDash = 3.0;
 
 Widget createDashWidget(Color color) {
-  final dash = Container(
-    width: _dashWidth,
-    height: _dashHeight,
-    color: color,
-  );
-  const space = SizedBox(
-    width: _spaceBetweenDash,
-    height: _dashHeight,
-  );
-  return Row(
-    children: [dash, space, dash, space, dash],
-  );
+  final dash = Container(width: _dashWidth, height: _dashHeight, color: color);
+  const space = SizedBox(width: _spaceBetweenDash, height: _dashHeight);
+  return Row(children: [dash, space, dash, space, dash]);
 }
 
 Widget createSolidLine(Color color) {
@@ -59,9 +50,10 @@
   @override
   void paint(Canvas canvas, Size size) {
     double startX = 0;
-    final paint = Paint()
-      ..color = _color
-      ..strokeWidth = _dashHeight;
+    final paint =
+        Paint()
+          ..color = _color
+          ..strokeWidth = _dashHeight;
 
     while (_totalWidth >= 0) {
       canvas.drawLine(Offset(startX, 0), Offset(startX + _dashWidth, 0), paint);
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/primitives/simple_elements.dart b/packages/devtools_app/lib/src/screens/memory/shared/primitives/simple_elements.dart
index 685288f..a91e701 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/primitives/simple_elements.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/primitives/simple_elements.dart
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const nonGcableInstancesColumnTooltip = 'Number of instances of the class,\n'
+const nonGcableInstancesColumnTooltip =
+    'Number of instances of the class,\n'
     'that are reachable, i.e. have a retaining path from the root\n'
     "and therefore can't be garbage collected.";
 
@@ -12,7 +13,8 @@
 enum SizeType {
   shallow(
     displayName: 'Shallow',
-    description: 'The total shallow size of all of the instances.\n'
+    description:
+        'The total shallow size of all of the instances.\n'
         'The shallow size of an object is the size of the object\n'
         'plus the references it holds to other Dart objects\n'
         "in its fields (this doesn't include the size of\n"
@@ -23,8 +25,7 @@
     description:
         'Total shallow Dart size of objects plus shallow Dart size of objects they retain,\n'
         'taking into account only the shortest retaining path for the referenced objects.',
-  ),
-  ;
+  );
 
   const SizeType({required this.displayName, required this.description});
 
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart b/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
index 9d4325f..b6f487a 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
@@ -9,8 +9,8 @@
 
 import '../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/memory/class_name.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../heap/class_filter.dart';
 
 String _adaptRootPackageForFilter(String? rootPackage) {
@@ -20,7 +20,7 @@
 
 class ClassFilterButton extends StatelessWidget {
   ClassFilterButton(this.data, {super.key})
-      : _rootPackage = _adaptRootPackageForFilter(data.rootPackage);
+    : _rootPackage = _adaptRootPackageForFilter(data.rootPackage);
 
   final ClassFilterData data;
   final String _rootPackage;
@@ -32,19 +32,17 @@
       builder: (context, filter, _) {
         return DevToolsFilterButton(
           onPressed: () {
-            ga.select(
-              gac.memory,
-              gac.MemoryEvents.diffSnapshotFilter.name,
-            );
+            ga.select(gac.memory, gac.MemoryEvents.diffSnapshotFilter.name);
 
             unawaited(
               showDialog(
                 context: context,
-                builder: (context) => ClassFilterDialog(
-                  filter,
-                  onChanged: data.onChanged,
-                  rootPackage: _rootPackage,
-                ),
+                builder:
+                    (context) => ClassFilterDialog(
+                      filter,
+                      onChanged: data.onChanged,
+                      rootPackage: _rootPackage,
+                    ),
               ),
             );
           },
@@ -114,27 +112,24 @@
         );
 
     Widget textField(TextEditingController controller) => Padding(
-          padding: EdgeInsets.only(left: textFieldLeftPadding),
-          child: TextField(
-            style: Theme.of(context).regularTextStyle,
-            decoration: const InputDecoration(
-              contentPadding: EdgeInsets.all(denseSpacing),
-              border: OutlineInputBorder(),
-            ),
-            keyboardType: TextInputType.multiline,
-            maxLines: null,
-            controller: controller,
-          ),
-        );
+      padding: EdgeInsets.only(left: textFieldLeftPadding),
+      child: TextField(
+        style: Theme.of(context).regularTextStyle,
+        decoration: const InputDecoration(
+          contentPadding: EdgeInsets.all(denseSpacing),
+          border: OutlineInputBorder(),
+        ),
+        keyboardType: TextInputType.multiline,
+        maxLines: null,
+        controller: controller,
+      ),
+    );
 
     return StateUpdateDialog(
       title: 'Filter Classes and Packages',
       helpBuilder: _helpBuilder,
       onResetDefaults: () {
-        ga.select(
-          gac.memory,
-          gac.MemoryEvents.diffSnapshotFilterReset.name,
-        );
+        ga.select(gac.memory, gac.MemoryEvents.diffSnapshotFilterReset.name);
         setState(() => _loadStateFromFilter(ClassFilter.theDefault()));
       },
       onApply: () {
@@ -176,10 +171,7 @@
         (t) => Row(
           children: [
             t.icon,
-            Text(
-              ' ${t.alias} - for ${t.aliasDescription}',
-              style: textStyle,
-            ),
+            Text(' ${t.alias} - for ${t.aliasDescription}', style: textStyle),
             CopyToClipboardControl(
               dataProvider: () => t.alias,
               size: tableIconSize,
@@ -191,7 +183,8 @@
   );
 }
 
-const _helpText = 'Choose and customize the filter.\n'
+const _helpText =
+    'Choose and customize the filter.\n'
     'List full or partial class names separated by new lines. For example:\n\n'
     '  package:myPackage/src/myFolder/myLibrary.dart/MyClass\n'
     '  MyClass\n'
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/widgets/shared_memory_widgets.dart b/packages/devtools_app/lib/src/screens/memory/shared/widgets/shared_memory_widgets.dart
index 5f7549d..199af62 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/widgets/shared_memory_widgets.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/widgets/shared_memory_widgets.dart
@@ -6,8 +6,8 @@
 import 'package:flutter/widgets.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/memory/class_name.dart';
+import '../../../../shared/ui/common_widgets.dart';
 
 class HeapClassView extends StatelessWidget {
   const HeapClassView({
@@ -73,12 +73,7 @@
       children: [
         const Text('Class type legend:'),
         ...ClassType.values.map(
-          (t) => Row(
-            children: [
-              t.icon,
-              Text(' ${t.aliasDescription}'),
-            ],
-          ),
+          (t) => Row(children: [t.icon, Text(' ${t.aliasDescription}')]),
         ),
       ],
     );
diff --git a/packages/devtools_app/lib/src/screens/network/constants.dart b/packages/devtools_app/lib/src/screens/network/constants.dart
index b6e016e..d09b0e7 100644
--- a/packages/devtools_app/lib/src/screens/network/constants.dart
+++ b/packages/devtools_app/lib/src/screens/network/constants.dart
@@ -80,9 +80,4 @@
   static const events = '_events';
 }
 
-enum NetworkEventCustomFieldRemappedKeys {
-  isolateId,
-  id,
-  startTime,
-  events,
-}
+enum NetworkEventCustomFieldRemappedKeys { isolateId, id, startTime, events }
diff --git a/packages/devtools_app/lib/src/screens/network/har_builder.dart b/packages/devtools_app/lib/src/screens/network/har_builder.dart
index 6e37ca9..b715fc8 100644
--- a/packages/devtools_app/lib/src/screens/network/har_builder.dart
+++ b/packages/devtools_app/lib/src/screens/network/har_builder.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import '../../shared/http/http_request_data.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'constants.dart';
 import 'har_data_entry.dart';
 
diff --git a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
index be71e04..4be56ec 100644
--- a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
+++ b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
@@ -28,16 +28,18 @@
     final modifiedRequestData = _remapCustomFieldKeys(json);
 
     // Retrieving url, method from requestData
-    final requestData = modifiedRequestData[NetworkEventKeys.request.name]
-        as Map<String, Object?>;
+    final requestData =
+        modifiedRequestData[NetworkEventKeys.request.name]
+            as Map<String, Object?>;
     modifiedRequestData[NetworkEventKeys.uri.name] =
         requestData[NetworkEventKeys.url.name];
     modifiedRequestData[NetworkEventKeys.method.name] =
         requestData[NetworkEventKeys.method.name];
 
     // Adding missing keys which are mandatory for parsing
-    final responseData = modifiedRequestData[NetworkEventKeys.response.name]
-        as Map<String, Object?>;
+    final responseData =
+        modifiedRequestData[NetworkEventKeys.response.name]
+            as Map<String, Object?>;
     responseData[NetworkEventKeys.redirects.name] = <Map<String, Object?>>[];
     final requestPostData = responseData[NetworkEventKeys.postData.name];
     final responseContent = responseData[NetworkEventKeys.content.name];
@@ -59,60 +61,65 @@
   /// serialization.
   static Map<String, Object?> toJson(DartIOHttpRequestData e) {
     // Implement the logic to convert DartIOHttpRequestData to HAR entry format
-    final requestCookies = e.requestCookies.map((cookie) {
-      return <String, Object?>{
-        NetworkEventKeys.name.name: cookie.name,
-        NetworkEventKeys.value.name: cookie.value,
-        NetworkEventKeys.path.name: cookie.path,
-        NetworkEventKeys.domain.name: cookie.domain,
-        NetworkEventKeys.expires.name:
-            cookie.expires?.toUtc().toIso8601String(),
-        NetworkEventKeys.httpOnly.name: cookie.httpOnly,
-        NetworkEventKeys.secure.name: cookie.secure,
-      };
-    }).toList();
+    final requestCookies =
+        e.requestCookies.map((cookie) {
+          return <String, Object?>{
+            NetworkEventKeys.name.name: cookie.name,
+            NetworkEventKeys.value.name: cookie.value,
+            NetworkEventKeys.path.name: cookie.path,
+            NetworkEventKeys.domain.name: cookie.domain,
+            NetworkEventKeys.expires.name:
+                cookie.expires?.toUtc().toIso8601String(),
+            NetworkEventKeys.httpOnly.name: cookie.httpOnly,
+            NetworkEventKeys.secure.name: cookie.secure,
+          };
+        }).toList();
 
-    final requestHeaders = e.requestHeaders?.entries.map((header) {
-      var value = header.value;
-      if (value is List) {
-        value = value.first;
-      }
-      return <String, Object?>{
-        NetworkEventKeys.name.name: header.key,
-        NetworkEventKeys.value.name: value,
-      };
-    }).toList();
+    final requestHeaders =
+        e.requestHeaders?.entries.map((header) {
+          var value = header.value;
+          if (value is List) {
+            value = value.first;
+          }
+          return <String, Object?>{
+            NetworkEventKeys.name.name: header.key,
+            NetworkEventKeys.value.name: value,
+          };
+        }).toList();
 
-    final queryString = Uri.parse(e.uri).queryParameters.entries.map((param) {
-      return <String, Object?>{
-        NetworkEventKeys.name.name: param.key,
-        NetworkEventKeys.value.name: param.value,
-      };
-    }).toList();
+    final queryString =
+        Uri.parse(e.uri).queryParameters.entries.map((param) {
+          return <String, Object?>{
+            NetworkEventKeys.name.name: param.key,
+            NetworkEventKeys.value.name: param.value,
+          };
+        }).toList();
 
-    final responseCookies = e.responseCookies.map((cookie) {
-      return <String, Object?>{
-        NetworkEventKeys.name.name: cookie.name,
-        NetworkEventKeys.value.name: cookie.value,
-        NetworkEventKeys.path.name: cookie.path,
-        NetworkEventKeys.domain.name: cookie.domain,
-        NetworkEventKeys.expires.name:
-            cookie.expires?.toUtc().toIso8601String(),
-        NetworkEventKeys.httpOnly.name: cookie.httpOnly,
-        NetworkEventKeys.secure.name: cookie.secure,
-      };
-    }).toList();
+    final responseCookies =
+        e.responseCookies.map((cookie) {
+          return <String, Object?>{
+            NetworkEventKeys.name.name: cookie.name,
+            NetworkEventKeys.value.name: cookie.value,
+            NetworkEventKeys.path.name: cookie.path,
+            NetworkEventKeys.domain.name: cookie.domain,
+            NetworkEventKeys.expires.name:
+                cookie.expires?.toUtc().toIso8601String(),
+            NetworkEventKeys.httpOnly.name: cookie.httpOnly,
+            NetworkEventKeys.secure.name: cookie.secure,
+          };
+        }).toList();
 
-    final responseHeaders = e.responseHeaders?.entries.map((header) {
-      var value = header.value;
-      if (value is List) {
-        value = value.first;
-      }
-      return <String, Object?>{
-        NetworkEventKeys.name.name: header.key,
-        NetworkEventKeys.value.name: value,
-      };
-    }).toList();
+    final responseHeaders =
+        e.responseHeaders?.entries.map((header) {
+          var value = header.value;
+          if (value is List) {
+            value = value.first;
+          }
+          return <String, Object?>{
+            NetworkEventKeys.name.name: header.key,
+            NetworkEventKeys.value.name: value,
+          };
+        }).toList();
 
     return <String, Object?>{
       NetworkEventKeys.startedDateTime.name:
@@ -130,8 +137,9 @@
           NetworkEventKeys.mimeType.name: e.contentType,
           NetworkEventKeys.text.name: e.requestBody,
         },
-        NetworkEventKeys.headersSize.name:
-            _calculateHeadersSize(e.requestHeaders),
+        NetworkEventKeys.headersSize.name: _calculateHeadersSize(
+          e.requestHeaders,
+        ),
         NetworkEventKeys.bodySize.name: _calculateBodySize(e.requestBody),
       },
       // Response
@@ -149,8 +157,9 @@
           NetworkEventKeys.text.name: e.responseBody,
         },
         NetworkEventKeys.redirectURL.name: '',
-        NetworkEventKeys.headersSize.name:
-            _calculateHeadersSize(e.responseHeaders),
+        NetworkEventKeys.headersSize.name: _calculateHeadersSize(
+          e.responseHeaders,
+        ),
         NetworkEventKeys.bodySize.name: _calculateBodySize(e.responseBody),
       },
       // Cache
@@ -267,15 +276,16 @@
   if (headers == null) return -1;
 
   // Combine headers into a single string with CRLF endings
-  String headersString = headers.entries.map((entry) {
-    final key = entry.key;
-    var value = entry.value;
-    // If the value is a List, join it with a comma
-    if (value is List<String>) {
-      value = value.join(', ');
-    }
-    return '$key: $value\r\n';
-  }).join();
+  String headersString =
+      headers.entries.map((entry) {
+        final key = entry.key;
+        var value = entry.value;
+        // If the value is a List, join it with a comma
+        if (value is List<String>) {
+          value = value.join(', ');
+        }
+        return '$key: $value\r\n';
+      }).join();
 
   // Add final CRLF to indicate end of headers
   headersString += '\r\n';
diff --git a/packages/devtools_app/lib/src/screens/network/har_network_data.dart b/packages/devtools_app/lib/src/screens/network/har_network_data.dart
index 60b02ac..5b81bb0 100644
--- a/packages/devtools_app/lib/src/screens/network/har_network_data.dart
+++ b/packages/devtools_app/lib/src/screens/network/har_network_data.dart
@@ -29,15 +29,17 @@
   /// final harData = HarNetworkData.fromJson(json);
   /// ```
   factory HarNetworkData.fromJson(Map<String, Object?> json) {
-    final entries = ((json[NetworkEventKeys.log.name]
-                as Map<String, Object?>)[NetworkEventKeys.entries.name]
-            as List<Object?>)
-        .map(
-          (entryJson) =>
-              HarDataEntry.fromJson(entryJson as Map<String, Object?>)
-                  .toDartIOHttpRequest(),
-        )
-        .toList();
+    final entries =
+        ((json[NetworkEventKeys.log.name]
+                    as Map<String, Object?>)[NetworkEventKeys.entries.name]
+                as List<Object?>)
+            .map(
+              (entryJson) =>
+                  HarDataEntry.fromJson(
+                    entryJson as Map<String, Object?>,
+                  ).toDartIOHttpRequest(),
+            )
+            .toList();
 
     return HarNetworkData(entries);
   }
diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart
index 6fb9067..bc61b25 100644
--- a/packages/devtools_app/lib/src/screens/network/network_controller.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart
@@ -17,7 +17,7 @@
 import '../../shared/primitives/utils.dart';
 import '../../shared/ui/filter.dart';
 import '../../shared/ui/search.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'har_network_data.dart';
 import 'network_model.dart';
 import 'network_screen.dart';
@@ -40,10 +40,7 @@
   }
 }
 
-enum _NetworkTrafficType {
-  http,
-  socket,
-}
+enum _NetworkTrafficType { http, socket }
 
 class NetworkController extends DisposableController
     with
@@ -57,7 +54,9 @@
       _currentNetworkRequests,
       _filterAndRefreshSearchMatches,
     );
-    subscribeToFilterChanges();
+    // TODO(https://github.com/flutter/devtools/issues/7727): add support for
+    // persisting network filter.
+    initFilterController();
   }
   List<DartIOHttpRequestData>? _httpRequests;
 
@@ -92,22 +91,28 @@
 
   @override
   Map<String, QueryFilterArgument<NetworkRequest>> createQueryFilterArgs() => {
-        methodFilterId: QueryFilterArgument<NetworkRequest>(
-          keys: ['method', 'm'],
-          dataValueProvider: (request) => request.method,
-          substringMatch: false,
-        ),
-        statusFilterId: QueryFilterArgument<NetworkRequest>(
-          keys: ['status', 's'],
-          dataValueProvider: (request) => request.status,
-          substringMatch: false,
-        ),
-        typeFilterId: QueryFilterArgument<NetworkRequest>(
-          keys: ['type', 't'],
-          dataValueProvider: (request) => request.type,
-          substringMatch: false,
-        ),
-      };
+    methodFilterId: QueryFilterArgument<NetworkRequest>(
+      keys: ['method', 'm'],
+      exampleUsages: ['m:get', '-m:put,patch'],
+      dataValueProvider: (request) => request.method,
+      substringMatch: false,
+    ),
+    statusFilterId: QueryFilterArgument<NetworkRequest>(
+      keys: ['status', 's'],
+      exampleUsages: ['s:200', '-s:404'],
+      dataValueProvider: (request) => request.status,
+      substringMatch: false,
+    ),
+    typeFilterId: QueryFilterArgument<NetworkRequest>(
+      keys: ['type', 't'],
+      exampleUsages: ['t:json', '-t:text'],
+      dataValueProvider: (request) => request.type,
+      substringMatch: false,
+    ),
+  };
+
+  @override
+  ValueNotifier<String>? get filterTagNotifier => preferences.network.filterTag;
 
   /// Notifies that new Network requests have been processed.
   ValueListenable<List<NetworkRequest>> get requests => _currentNetworkRequests;
@@ -116,8 +121,9 @@
   ValueListenable<NetworkResponseViewType> get currentResponseViewType =>
       _currentResponseViewType;
 
-  final _currentResponseViewType =
-      ValueNotifier<NetworkResponseViewType>(NetworkResponseViewType.auto);
+  final _currentResponseViewType = ValueNotifier<NetworkResponseViewType>(
+    NetworkResponseViewType.auto,
+  );
 
   /// Change current response type
   set setResponseViewType(NetworkResponseViewType type) =>
@@ -177,8 +183,9 @@
     // the value.
     final currentSelectedRequestId = selectedRequest.value?.id;
     if (currentSelectedRequestId != null) {
-      selectedRequest.value =
-          currentRequests.getRequest(currentSelectedRequestId);
+      selectedRequest.value = currentRequests.getRequest(
+        currentSelectedRequestId,
+      );
     }
   }
 
@@ -213,10 +220,12 @@
 
   Future<void> startRecording() async {
     await _startRecording(
-      alreadyRecordingHttp:
-          await _recordingNetworkTraffic(type: _NetworkTrafficType.http),
-      alreadyRecordingSocketData:
-          await _recordingNetworkTraffic(type: _NetworkTrafficType.socket),
+      alreadyRecordingHttp: await _recordingNetworkTraffic(
+        type: _NetworkTrafficType.http,
+      ),
+      alreadyRecordingSocketData: await _recordingNetworkTraffic(
+        type: _NetworkTrafficType.socket,
+      ),
     );
   }
 
@@ -248,8 +257,11 @@
     // fewer flags risks breaking functionality on the timeline view that
     // assumes that all flags are set.
     await allowedError(
-      serviceConnection.serviceManager.service!
-          .setVMTimelineFlags(['GC', 'Dart', 'Embedder']),
+      serviceConnection.serviceManager.service!.setVMTimelineFlags([
+        'GC',
+        'Dart',
+        'Embedder',
+      ]),
     );
 
     // TODO(kenz): only call these if http logging and socket profiling are not
@@ -291,22 +303,22 @@
   }) async {
     bool enabled = true;
     final service = serviceConnection.serviceManager.service!;
-    await service.forEachIsolate(
-      (isolate) async {
-        final future = switch (type) {
-          _NetworkTrafficType.http =>
-            service.httpEnableTimelineLoggingWrapper(isolate.id!),
-          _NetworkTrafficType.socket =>
-            service.socketProfilingEnabledWrapper(isolate.id!),
-        };
-        // The above call won't complete immediately if the isolate is paused,
-        // so give up waiting after 500ms.
-        final state = await timeout(future, 500);
-        if (state?.enabled != true) {
-          enabled = false;
-        }
-      },
-    );
+    await service.forEachIsolate((isolate) async {
+      final future = switch (type) {
+        _NetworkTrafficType.http => service.httpEnableTimelineLoggingWrapper(
+          isolate.id!,
+        ),
+        _NetworkTrafficType.socket => service.socketProfilingEnabledWrapper(
+          isolate.id!,
+        ),
+      };
+      // The above call won't complete immediately if the isolate is paused,
+      // so give up waiting after 500ms.
+      final state = await timeout(future, 500);
+      if (state?.enabled != true) {
+        enabled = false;
+      }
+    });
     return enabled;
   }
 
@@ -354,7 +366,8 @@
       ..addAll(
         _currentNetworkRequests.value.where((NetworkRequest r) {
           final filteredOutByQueryFilterArgument = queryFilter
-              .filterArguments.values
+              .filterArguments
+              .values
               .any((argument) => !argument.matchesValue(r));
           if (filteredOutByQueryFilterArgument) return false;
 
@@ -388,10 +401,10 @@
   }
 
   Future<void> _fetchFullDataBeforeExport() => Future.wait(
-        filteredData.value
-            .whereType<DartIOHttpRequestData>()
-            .map((item) => item.getFullRequestData()),
-      );
+    filteredData.value.whereType<DartIOHttpRequestData>().map(
+      (item) => item.getFullRequestData(),
+    ),
+  );
 }
 
 /// Class for managing the set of all current sockets, and
diff --git a/packages/devtools_app/lib/src/screens/network/network_model.dart b/packages/devtools_app/lib/src/screens/network/network_model.dart
index 64e7ba1..44a12b1 100644
--- a/packages/devtools_app/lib/src/screens/network/network_model.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_model.dart
@@ -36,12 +36,10 @@
 
   String get durationDisplay {
     final duration = this.duration;
-    final text = duration != null
-        ? durationText(
-            duration,
-            unit: DurationDisplayUnit.milliseconds,
-          )
-        : 'Pending';
+    final text =
+        duration != null
+            ? durationText(duration, unit: DurationDisplayUnit.milliseconds)
+            : 'Pending';
     return 'Duration: $text';
   }
 
@@ -72,14 +70,8 @@
   }
 
   @override
-  int get hashCode => Object.hash(
-        method,
-        uri,
-        contentType,
-        type,
-        port,
-        startTimestamp,
-      );
+  int get hashCode =>
+      Object.hash(method, uri, contentType, type, port, startTimestamp);
 }
 
 class Socket extends NetworkRequest {
@@ -113,16 +105,16 @@
 
   @override
   DateTime get startTimestamp => DateTime.fromMicrosecondsSinceEpoch(
-        timelineMicrosecondsSinceEpoch(_socket.startTime),
-      );
+    timelineMicrosecondsSinceEpoch(_socket.startTime),
+  );
 
   @override
   DateTime? get endTimestamp {
     final endTime = _socket.endTime;
     return endTime != null
         ? DateTime.fromMicrosecondsSinceEpoch(
-            timelineMicrosecondsSinceEpoch(endTime),
-          )
+          timelineMicrosecondsSinceEpoch(endTime),
+        )
         : null;
   }
 
@@ -130,8 +122,8 @@
     final lastReadTime = _socket.lastReadTime;
     return lastReadTime != null
         ? DateTime.fromMicrosecondsSinceEpoch(
-            timelineMicrosecondsSinceEpoch(lastReadTime),
-          )
+          timelineMicrosecondsSinceEpoch(lastReadTime),
+        )
         : null;
   }
 
@@ -139,8 +131,8 @@
     final lastWriteTime = _socket.lastWriteTime;
     return lastWriteTime != null
         ? DateTime.fromMicrosecondsSinceEpoch(
-            timelineMicrosecondsSinceEpoch(lastWriteTime),
-          )
+          timelineMicrosecondsSinceEpoch(lastWriteTime),
+        )
         : null;
   }
 
diff --git a/packages/devtools_app/lib/src/screens/network/network_request_inspector.dart b/packages/devtools_app/lib/src/screens/network/network_request_inspector.dart
index 0e8fb91..68d3e93 100644
--- a/packages/devtools_app/lib/src/screens/network/network_request_inspector.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_request_inspector.dart
@@ -37,25 +37,25 @@
     return ValueListenableBuilder<NetworkRequest?>(
       valueListenable: controller.selectedRequest,
       builder: (context, data, _) {
-        return RoundedOutlinedBorder(
-          child: (data == null)
-              ? Center(
-                  child: Text(
-                    'No request selected',
-                    style: Theme.of(context).regularTextStyle,
-                  ),
-                )
-              : ListenableBuilder(
-                  listenable: data,
-                  builder: (context, _) {
-                    return AnalyticsTabbedView(
-                      analyticsSessionIdentifier: data.id,
-                      tabs: _generateTabs(data),
-                      gaScreen: gac.network,
-                    );
-                  },
+        return data == null
+            ? RoundedOutlinedBorder(
+              child: Center(
+                child: Text(
+                  'No request selected',
+                  style: Theme.of(context).regularTextStyle,
                 ),
-        );
+              ),
+            )
+            : ListenableBuilder(
+              listenable: data,
+              builder: (context, _) {
+                return AnalyticsTabbedView(
+                  analyticsSessionIdentifier: data.id,
+                  tabs: _generateTabs(data),
+                  gaScreen: gac.network,
+                );
+              },
+            );
       },
     );
   }
@@ -64,58 +64,64 @@
     NetworkRequest data,
   ) =>
       [
-        (
-          tab: _buildTab(tabName: NetworkRequestInspector._overviewTabTitle),
-          tabView: NetworkRequestOverviewView(data),
-        ),
-        if (data is DartIOHttpRequestData) ...[
-          (
-            tab: _buildTab(tabName: NetworkRequestInspector._headersTabTitle),
-            tabView: HttpRequestHeadersView(data),
-          ),
-          if (data.requestBody != null)
             (
               tab: _buildTab(
-                tabName: NetworkRequestInspector._requestTabTitle,
-                trailing: HttpViewTrailingCopyButton(
-                  data,
-                  (data) => data.requestBody,
+                tabName: NetworkRequestInspector._overviewTabTitle,
+              ),
+              tabView: NetworkRequestOverviewView(data),
+            ),
+            if (data is DartIOHttpRequestData) ...[
+              (
+                tab: _buildTab(
+                  tabName: NetworkRequestInspector._headersTabTitle,
                 ),
+                tabView: HttpRequestHeadersView(data),
               ),
-              tabView: HttpRequestView(data),
-            ),
-          if (data.responseBody != null)
-            (
-              tab: _buildTab(
-                tabName: NetworkRequestInspector._responseTabTitle,
-                trailing: Row(
-                  children: [
-                    HttpResponseTrailingDropDown(
+              if (data.requestBody != null)
+                (
+                  tab: _buildTab(
+                    tabName: NetworkRequestInspector._requestTabTitle,
+                    trailing: HttpViewTrailingCopyButton(
                       data,
-                      currentResponseViewType:
-                          controller.currentResponseViewType,
-                      onChanged: (value) =>
-                          controller.setResponseViewType = value,
+                      (data) => data.requestBody,
                     ),
-                    HttpViewTrailingCopyButton(
-                      data,
-                      (data) => data.responseBody,
-                    ),
-                  ],
+                  ),
+                  tabView: HttpRequestView(data),
                 ),
-              ),
-              tabView: HttpResponseView(
-                data,
-                currentResponseViewType: controller.currentResponseViewType,
-              ),
-            ),
-          if (data.hasCookies)
-            (
-              tab: _buildTab(tabName: NetworkRequestInspector._cookiesTabTitle),
-              tabView: HttpRequestCookiesView(data),
-            ),
-        ],
-      ]
+              if (data.responseBody != null)
+                (
+                  tab: _buildTab(
+                    tabName: NetworkRequestInspector._responseTabTitle,
+                    trailing: Row(
+                      children: [
+                        HttpResponseTrailingDropDown(
+                          data,
+                          currentResponseViewType:
+                              controller.currentResponseViewType,
+                          onChanged:
+                              (value) => controller.setResponseViewType = value,
+                        ),
+                        HttpViewTrailingCopyButton(
+                          data,
+                          (data) => data.responseBody,
+                        ),
+                      ],
+                    ),
+                  ),
+                  tabView: HttpResponseView(
+                    data,
+                    currentResponseViewType: controller.currentResponseViewType,
+                  ),
+                ),
+              if (data.hasCookies)
+                (
+                  tab: _buildTab(
+                    tabName: NetworkRequestInspector._cookiesTabTitle,
+                  ),
+                  tabView: HttpRequestCookiesView(data),
+                ),
+            ],
+          ]
           .map(
             (t) => (
               tab: t.tab,
diff --git a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart
index 3ff6f01..1bf9602 100644
--- a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart
@@ -9,12 +9,12 @@
 import 'package:flutter/material.dart';
 import 'package:image/image.dart' as image;
 
-import '../../shared/common_widgets.dart';
 import '../../shared/http/http.dart';
 import '../../shared/http/http_request_data.dart';
 import '../../shared/primitives/byte_utils.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
 import 'network_controller.dart';
 import 'network_model.dart';
 
@@ -24,20 +24,16 @@
 // No padding between the last element and the divider of a expandable tile.
 const _rowSpacingPadding = 15.0;
 
-const _rowPadding =
-    EdgeInsets.only(left: _rowIndentPadding, bottom: _rowSpacingPadding);
+const _rowPadding = EdgeInsets.only(
+  left: _rowIndentPadding,
+  bottom: _rowSpacingPadding,
+);
 
 /// Helper to build ExpansionTile widgets for inspector views.
-ExpansionTile _buildTile(
-  String title,
-  List<Widget> children, {
-  Key? key,
-}) {
+ExpansionTile _buildTile(String title, List<Widget> children, {Key? key}) {
   return ExpansionTile(
     key: key,
-    title: Text(
-      title,
-    ),
+    title: Text(title),
     initiallyExpanded: true,
     children: children,
   );
@@ -67,36 +63,24 @@
         return SelectionArea(
           child: ListView(
             children: [
-              _buildTile(
-                'General',
-                [
-                  for (final entry in general.entries)
-                    _Row(
-                      entry: entry,
-                      constraints: constraints,
-                      isErrorValue: data.didFail && entry.key == 'statusCode',
-                    ),
-                ],
-                key: generalKey,
-              ),
-              _buildTile(
-                'Response Headers',
-                [
-                  if (responseHeaders != null)
-                    for (final entry in responseHeaders.entries)
-                      _Row(entry: entry, constraints: constraints),
-                ],
-                key: responseHeadersKey,
-              ),
-              _buildTile(
-                'Request Headers',
-                [
-                  if (requestHeaders != null)
-                    for (final entry in requestHeaders.entries)
-                      _Row(entry: entry, constraints: constraints),
-                ],
-                key: requestHeadersKey,
-              ),
+              _buildTile('General', [
+                for (final entry in general.entries)
+                  _Row(
+                    entry: entry,
+                    constraints: constraints,
+                    isErrorValue: data.didFail && entry.key == 'statusCode',
+                  ),
+              ], key: generalKey),
+              _buildTile('Response Headers', [
+                if (responseHeaders != null)
+                  for (final entry in responseHeaders.entries)
+                    _Row(entry: entry, constraints: constraints),
+              ], key: responseHeadersKey),
+              _buildTile('Request Headers', [
+                if (requestHeaders != null)
+                  for (final entry in requestHeaders.entries)
+                    _Row(entry: entry, constraints: constraints),
+              ], key: requestHeadersKey),
             ],
           ),
         );
@@ -125,10 +109,7 @@
       child: Row(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          Text(
-            '${entry.key}: ',
-            style: theme.textTheme.titleMedium,
-          ),
+          Text('${entry.key}: ', style: theme.textTheme.titleMedium),
           Expanded(
             child: Text(
               style: isErrorValue ? theme.errorTextStyle : null,
@@ -150,39 +131,38 @@
   Widget build(BuildContext context) {
     return ListenableBuilder(
       listenable: data,
-      builder: (context, __) {
+      builder: (context, _) {
         final theme = Theme.of(context);
         final requestHeaders = data.requestHeaders;
         final requestContentType = requestHeaders?['content-type'] ?? '';
         final isLoading = data.isFetchingFullData;
         if (isLoading) {
-          return CenteredCircularProgressIndicator(
-            size: mediumProgressSize,
-          );
+          return CenteredCircularProgressIndicator(size: mediumProgressSize);
         }
 
         final isJson = switch (requestContentType) {
-          List() =>
-            requestContentType.any((e) => (e as String).contains('json')),
+          List() => requestContentType.any(
+            (e) => (e as String).contains('json'),
+          ),
           String() => requestContentType.contains('json'),
-          _ => throw StateError(
+          _ =>
+            throw StateError(
               "Expected 'content-type' to be a List or String, but got: "
               '$requestContentType',
             ),
         };
 
         Widget child;
-        child = isJson
-            ? JsonViewer(encodedJson: data.requestBody!)
-            : TextViewer(
-                text: data.requestBody!,
-                style: theme.fixedFontStyle,
-              );
+        child =
+            isJson
+                ? JsonViewer(encodedJson: data.requestBody!)
+                : TextViewer(
+                  text: data.requestBody!,
+                  style: theme.fixedFontStyle,
+                );
         return Padding(
           padding: const EdgeInsets.all(denseSpacing),
-          child: SingleChildScrollView(
-            child: child,
-          ),
+          child: SingleChildScrollView(child: child),
         );
       },
     );
@@ -202,7 +182,7 @@
   Widget build(BuildContext context) {
     return ListenableBuilder(
       listenable: data,
-      builder: (context, __) {
+      builder: (context, _) {
         final dataToCopy = dataSelector(data);
         final isLoading = data.isFetchingFullData;
         if (dataToCopy == null || dataToCopy.isEmpty || isLoading) {
@@ -211,9 +191,7 @@
 
         return Align(
           alignment: Alignment.centerRight,
-          child: CopyToClipboardControl(
-            dataProvider: () => dataToCopy,
-          ),
+          child: CopyToClipboardControl(dataProvider: () => dataToCopy),
         );
       },
     );
@@ -249,8 +227,9 @@
   Widget build(BuildContext context) {
     return ListenableBuilder(
       listenable: data,
-      builder: (_, __) {
-        final visible = (data.contentType != null &&
+      builder: (_, _) {
+        final visible =
+            (data.contentType != null &&
                 !data.contentType!.contains('image')) &&
             data.responseBody!.isNotEmpty;
 
@@ -265,17 +244,18 @@
           replacement: const SizedBox(),
           child: ValueListenableBuilder<NetworkResponseViewType>(
             valueListenable: currentResponseViewType,
-            builder: (_, currentType, __) {
+            builder: (_, currentType, _) {
               return RoundedDropDownButton<NetworkResponseViewType>(
                 value: currentType,
-                items: availableResponseTypes
-                    .map(
-                      (e) => DropdownMenuItem(
-                        value: e,
-                        child: Text(e.toString()),
-                      ),
-                    )
-                    .toList(),
+                items:
+                    availableResponseTypes
+                        .map(
+                          (e) => DropdownMenuItem(
+                            value: e,
+                            child: Text(e.toString()),
+                          ),
+                        )
+                        .toList(),
                 onChanged: (value) {
                   if (value == null) {
                     return;
@@ -305,7 +285,7 @@
   Widget build(BuildContext context) {
     return ListenableBuilder(
       listenable: data,
-      builder: (context, __) {
+      builder: (context, _) {
         Widget child;
         final theme = Theme.of(context);
         // We shouldn't try and display an image response view when using the
@@ -315,9 +295,7 @@
         final responseBody = data.responseBody!;
         final isLoading = data.isFetchingFullData;
         if (isLoading) {
-          return CenteredCircularProgressIndicator(
-            size: mediumProgressSize,
-          );
+          return CenteredCircularProgressIndicator(size: mediumProgressSize);
         }
         if (contentType != null && contentType.contains('image')) {
           child = ImageResponseView(data);
@@ -356,7 +334,7 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder(
       valueListenable: currentResponseNotifier,
-      builder: (_, currentResponseType, __) {
+      builder: (_, currentResponseType, _) {
         NetworkResponseViewType currentLocalResponseType = currentResponseType;
 
         if (currentResponseType == NetworkResponseViewType.auto) {
@@ -372,9 +350,9 @@
         return switch (currentLocalResponseType) {
           NetworkResponseViewType.json => JsonViewer(encodedJson: responseBody),
           NetworkResponseViewType.text => TextViewer(
-              text: responseBody,
-              style: textStyle,
-            ),
+            text: responseBody,
+            style: textStyle,
+          ),
           _ => const SizedBox(),
         };
       },
@@ -394,67 +372,34 @@
     return Column(
       crossAxisAlignment: CrossAxisAlignment.stretch,
       children: [
-        _buildTile(
-          'Image Preview',
-          [
-            Padding(
-              padding: const EdgeInsets.all(
-                denseSpacing,
-              ),
-              child: Image.memory(
-                encodedResponse,
-              ),
-            ),
-          ],
-        ),
-        _buildTile(
-          'Metadata',
-          [
-            _buildRow(
-              context,
-              'Format',
-              data.type,
-            ),
-            _buildRow(
-              context,
-              'Size',
-              prettyPrintBytes(
-                encodedResponse.lengthInBytes,
-                includeUnit: true,
-              )!,
-            ),
-            _buildRow(
-              context,
-              'Dimensions',
-              '${img.width} x ${img.height}',
-            ),
-          ],
-        ),
+        _buildTile('Image Preview', [
+          Padding(
+            padding: const EdgeInsets.all(denseSpacing),
+            child: Image.memory(encodedResponse),
+          ),
+        ]),
+        _buildTile('Metadata', [
+          _buildRow(context, 'Format', data.type),
+          _buildRow(
+            context,
+            'Size',
+            prettyPrintBytes(encodedResponse.lengthInBytes, includeUnit: true)!,
+          ),
+          _buildRow(context, 'Dimensions', '${img.width} x ${img.height}'),
+        ]),
       ],
     );
   }
 
-  Widget _buildRow(
-    BuildContext context,
-    String key,
-    String value,
-  ) {
+  Widget _buildRow(BuildContext context, String key, String value) {
     return Padding(
       padding: _rowPadding,
       child: Row(
         mainAxisSize: MainAxisSize.min,
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          Text(
-            '$key: ',
-            style: Theme.of(context).textTheme.titleMedium,
-          ),
-          Expanded(
-            child: Text(
-              value,
-              overflow: TextOverflow.ellipsis,
-            ),
-          ),
+          Text('$key: ', style: Theme.of(context).textTheme.titleMedium),
+          Expanded(child: Text(value, overflow: TextOverflow.ellipsis)),
         ],
       ),
     );
@@ -506,10 +451,7 @@
     bool requestCookies = false,
   }) {
     final theme = Theme.of(context);
-    DataColumn buildColumn(
-      String title, {
-      bool numeric = false,
-    }) {
+    DataColumn buildColumn(String title, {bool numeric = false}) {
       return DataColumn(
         label: Expanded(
           child: Text(
@@ -528,58 +470,50 @@
       );
     }
 
-    return _buildTile(
-      title,
-      [
-        // Add a divider between the tile's title and the cookie table headers for
-        // symmetry.
-        const Divider(
-          // Remove extra padding at the top of the divider; the tile's title
-          // already has bottom padding.
-          height: 0,
-        ),
-        Align(
-          alignment: Alignment.centerLeft,
-          child: SingleChildScrollView(
-            scrollDirection: Axis.horizontal,
-            child: ConstrainedBox(
-              constraints: requestCookies
-                  ? const BoxConstraints()
-                  : BoxConstraints(
-                      minWidth: constraints.minWidth,
-                    ),
-              child: DataTable(
-                key: key,
-                dataRowMinHeight: defaultRowHeight,
-                dataRowMaxHeight: defaultRowHeight,
-                // NOTE: if this list of columns change, _buildRow will need
-                // to be updated to match.
-                columns: [
-                  buildColumn('Name'),
-                  buildColumn('Value'),
-                  if (!requestCookies) ...[
-                    buildColumn('Domain'),
-                    buildColumn('Path'),
-                    buildColumn('Expires / Max Age'),
-                    buildColumn('Size', numeric: true),
-                    buildColumn('HttpOnly'),
-                    buildColumn('Secure'),
-                  ],
+    return _buildTile(title, [
+      // Add a divider between the tile's title and the cookie table headers for
+      // symmetry.
+      const Divider(
+        // Remove extra padding at the top of the divider; the tile's title
+        // already has bottom padding.
+        height: 0,
+      ),
+      Align(
+        alignment: Alignment.centerLeft,
+        child: SingleChildScrollView(
+          scrollDirection: Axis.horizontal,
+          child: ConstrainedBox(
+            constraints:
+                requestCookies
+                    ? const BoxConstraints()
+                    : BoxConstraints(minWidth: constraints.minWidth),
+            child: DataTable(
+              key: key,
+              dataRowMinHeight: defaultRowHeight,
+              dataRowMaxHeight: defaultRowHeight,
+              // NOTE: if this list of columns change, _buildRow will need
+              // to be updated to match.
+              columns: [
+                buildColumn('Name'),
+                buildColumn('Value'),
+                if (!requestCookies) ...[
+                  buildColumn('Domain'),
+                  buildColumn('Path'),
+                  buildColumn('Expires / Max Age'),
+                  buildColumn('Size', numeric: true),
+                  buildColumn('HttpOnly'),
+                  buildColumn('Secure'),
                 ],
-                rows: [
-                  for (int i = 0; i < cookies.length; ++i)
-                    _buildRow(
-                      i,
-                      cookies[i],
-                      requestCookies: requestCookies,
-                    ),
-                ],
-              ),
+              ],
+              rows: [
+                for (int i = 0; i < cookies.length; ++i)
+                  _buildRow(i, cookies[i], requestCookies: requestCookies),
+              ],
             ),
           ),
         ),
-      ],
-    );
+      ),
+    ]);
   }
 
   @override
@@ -601,9 +535,7 @@
             // Add padding between the cookie tables if displaying both
             // response and request cookies.
             if (responseCookies.isNotEmpty && requestCookies.isNotEmpty)
-              const Padding(
-                padding: EdgeInsets.only(bottom: 24.0),
-              ),
+              const Padding(padding: EdgeInsets.only(bottom: 24.0)),
             if (requestCookies.isNotEmpty)
               _buildCookiesTable(
                 context,
@@ -699,9 +631,10 @@
       _buildRow(
         context: context,
         title: 'Timing',
-        child: data is Socket
-            ? _buildSocketTimeGraph(context)
-            : _buildHttpTimeGraph(),
+        child:
+            data is Socket
+                ? _buildSocketTimeGraph(context)
+                : _buildHttpTimeGraph(),
       ),
       const SizedBox(height: denseSpacing),
       _buildRow(
@@ -732,21 +665,14 @@
     ];
   }
 
-  Widget _buildTimingRow(
-    Color color,
-    String label,
-    Duration duration,
-  ) {
+  Widget _buildTimingRow(Color color, String label, Duration duration) {
     final flex =
         (duration.inMicroseconds / data.duration!.inMicroseconds * 100).round();
     return Flexible(
       flex: flex,
       child: DevToolsTooltip(
         message: '$label - ${durationText(duration)}',
-        child: Container(
-          height: _timingGraphHeight,
-          color: color,
-        ),
+        child: Container(height: _timingGraphHeight, color: color),
       ),
     );
   }
@@ -761,11 +687,7 @@
       );
     }
 
-    const colors = [
-      searchMatchColor,
-      mainRasterColor,
-      mainAsyncColor,
-    ];
+    const colors = [searchMatchColor, mainRasterColor, mainAsyncColor];
 
     var colorIndex = 0;
     Color nextColor() {
@@ -779,21 +701,15 @@
     final timingWidgets = <Widget>[];
     for (final instant in data.instantEvents) {
       final duration = instant.timeRange!.duration;
-      timingWidgets.add(
-        _buildTimingRow(nextColor(), instant.name, duration),
-      );
+      timingWidgets.add(_buildTimingRow(nextColor(), instant.name, duration));
     }
     final duration = Duration(
-      microseconds: data.endTimestamp!.microsecondsSinceEpoch -
+      microseconds:
+          data.endTimestamp!.microsecondsSinceEpoch -
           data.instantEvents.last.timestamp.microsecondsSinceEpoch,
     );
-    timingWidgets.add(
-      _buildTimingRow(nextColor(), 'Response', duration),
-    );
-    return Row(
-      key: httpTimingGraphKey,
-      children: timingWidgets,
-    );
+    timingWidgets.add(_buildTimingRow(nextColor(), 'Response', duration));
+    return Row(key: httpTimingGraphKey, children: timingWidgets);
   }
 
   // TODO(kenz): add a "waterfall" like visualization with the same colors that
@@ -877,17 +793,19 @@
       _buildRow(
         context: context,
         title: 'Last read time',
-        child: lastReadTimestamp != null
-            ? _valueText(formatDateTime(lastReadTimestamp))
-            : _valueText('--'),
+        child:
+            lastReadTimestamp != null
+                ? _valueText(formatDateTime(lastReadTimestamp))
+                : _valueText('--'),
       ),
       const SizedBox(height: defaultSpacing),
       _buildRow(
         context: context,
         title: 'Last write time',
-        child: lastWriteTimestamp != null
-            ? _valueText(formatDateTime(lastWriteTimestamp))
-            : _valueText('--'),
+        child:
+            lastWriteTimestamp != null
+                ? _valueText(formatDateTime(lastWriteTimestamp))
+                : _valueText('--'),
       ),
     ];
   }
@@ -907,17 +825,12 @@
             style: Theme.of(context).textTheme.titleMedium,
           ),
         ),
-        Expanded(
-          child: child,
-        ),
+        Expanded(child: child),
       ],
     );
   }
 
   Widget _valueText(String value, [TextStyle? style]) {
-    return Text(
-      style: style,
-      value,
-    );
+    return Text(style: style, value);
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart
index 8b2b378..ce7e247 100644
--- a/packages/devtools_app/lib/src/screens/network/network_screen.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart
@@ -12,19 +12,19 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
 import '../../shared/http/curl_command.dart';
 import '../../shared/http/http_request_data.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/screen.dart';
 import '../../shared/table/table.dart';
 import '../../shared/table/table_data.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/filter.dart';
 import '../../shared/ui/search.dart';
 import '../../shared/ui/utils.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'network_controller.dart';
 import 'network_model.dart';
 import 'network_request_inspector.dart';
@@ -45,10 +45,7 @@
     final networkController = Provider.of<NetworkController>(context);
     final color = Theme.of(context).colorScheme.onPrimary;
     return MultiValueListenableBuilder(
-      listenables: [
-        networkController.requests,
-        networkController.filteredData,
-      ],
+      listenables: [networkController.requests, networkController.filteredData],
       builder: (context, values, child) {
         final networkRequests = values.first as List<NetworkRequest>;
         final filteredRequests = values.second as List<NetworkRequest>;
@@ -73,11 +70,12 @@
           return SizedBox(
             width: smallProgressSize,
             height: smallProgressSize,
-            child: recording
-                ? SmallCircularProgressIndicator(
-                    valueColor: AlwaysStoppedAnimation<Color>(color),
-                  )
-                : const SizedBox(),
+            child:
+                recording
+                    ? SmallCircularProgressIndicator(
+                      valueColor: AlwaysStoppedAnimation<Color>(color),
+                    )
+                    : const SizedBox(),
           );
         },
       ),
@@ -88,23 +86,6 @@
 class NetworkScreenBody extends StatefulWidget {
   const NetworkScreenBody({super.key});
 
-  static const filterQueryInstructions = '''
-Type a filter query to show or hide specific requests.
-
-Any text that is not paired with an available filter key below will be queried against all categories (method, uri, status, type).
-
-Available filters:
-    'method', 'm'       (e.g. 'm:get', '-m:put,patch')
-    'status', 's'           (e.g. 's:200', '-s:404')
-    'type', 't'               (e.g. 't:json', '-t:ws')
-
-Example queries:
-    'my-endpoint method:put,post -status:404 type:json'
-    'example.com -m:get s:200,201 t:htm,html,json'
-    'http s:404'
-    'POST'
-''';
-
   @override
   State<StatefulWidget> createState() => _NetworkScreenBodyState();
 }
@@ -152,9 +133,7 @@
       children: [
         _NetworkProfilerControls(controller: controller),
         const SizedBox(height: intermediateSpacing),
-        Expanded(
-          child: _NetworkProfilerBody(controller: controller),
-        ),
+        Expanded(child: _NetworkProfilerBody(controller: controller)),
       ],
     );
   }
@@ -163,9 +142,7 @@
 /// The row of controls that control the Network profiler (e.g., record, pause,
 /// clear, search, filter, etc.).
 class _NetworkProfilerControls extends StatefulWidget {
-  const _NetworkProfilerControls({
-    required this.controller,
-  });
+  const _NetworkProfilerControls({required this.controller});
 
   static const _includeTextWidth = 810.0;
 
@@ -178,10 +155,6 @@
 
 class _NetworkProfilerControlsState extends State<_NetworkProfilerControls>
     with AutoDisposeMixin {
-  late List<NetworkRequest> _requests;
-
-  late List<NetworkRequest> _filteredRequests;
-
   bool _recording = false;
 
   @override
@@ -194,33 +167,24 @@
         _recording = widget.controller.recordingNotifier.value;
       });
     });
-    _requests = widget.controller.requests.value;
-    addAutoDisposeListener(widget.controller.requests, () {
-      setState(() {
-        _requests = widget.controller.requests.value;
-      });
-    });
-    _filteredRequests = widget.controller.filteredData.value;
-    addAutoDisposeListener(widget.controller.filteredData, () {
-      setState(() {
-        _filteredRequests = widget.controller.filteredData.value;
-      });
-    });
+
+    addAutoDisposeListener(widget.controller.filteredData);
   }
 
   @override
   Widget build(BuildContext context) {
     final screenWidth = ScreenSize(context).width;
-    final hasRequests = _filteredRequests.isNotEmpty;
+    final hasRequests = widget.controller.filteredData.value.isNotEmpty;
     return Row(
       children: [
         StartStopRecordingButton(
           recording: _recording,
-          onPressed: () async =>
-              await widget.controller.togglePolling(!_recording),
-          tooltipOverride: _recording
-              ? 'Stop recording network traffic'
-              : 'Resume recording network traffic',
+          onPressed:
+              () async => await widget.controller.togglePolling(!_recording),
+          tooltipOverride:
+              _recording
+                  ? 'Stop recording network traffic'
+                  : 'Resume recording network traffic',
           minScreenWidthForTextBeforeScaling: double.infinity,
           gaScreen: gac.network,
           gaSelection: _recording ? gac.pause : gac.resume,
@@ -235,6 +199,7 @@
         ),
         const SizedBox(width: defaultSpacing),
         DownloadButton(
+          tooltip: 'Download as .har file',
           minScreenWidthForTextBeforeScaling:
               _NetworkProfilerControls._includeTextWidth,
           onPressed: widget.controller.exportAsHarFile,
@@ -242,35 +207,27 @@
           gaSelection: gac.NetworkEvent.downloadAsHar.name,
         ),
         const SizedBox(width: defaultSpacing),
-        const Expanded(child: SizedBox()),
         // TODO(kenz): fix focus issue when state is refreshed
-        SearchField<NetworkController>(
-          searchController: widget.controller,
-          searchFieldEnabled: hasRequests,
-          searchFieldWidth: screenWidth <= MediaSize.xs
-              ? defaultSearchFieldWidth
-              : wideSearchFieldWidth,
+        Expanded(
+          child: SearchField<NetworkController>(
+            searchController: widget.controller,
+            searchFieldEnabled: hasRequests,
+            searchFieldWidth:
+                screenWidth <= MediaSize.xs
+                    ? defaultSearchFieldWidth
+                    : wideSearchFieldWidth,
+          ),
         ),
         const SizedBox(width: denseSpacing),
-        DevToolsFilterButton(
-          onPressed: _showFilterDialog,
-          isFilterActive: _filteredRequests.length != _requests.length,
+        Expanded(
+          child: StandaloneFilterField<NetworkRequest>(
+            controller: widget.controller,
+            filteredItem: 'request',
+          ),
         ),
       ],
     );
   }
-
-  void _showFilterDialog() {
-    unawaited(
-      showDialog(
-        context: context,
-        builder: (context) => FilterDialog<NetworkRequest>(
-          controller: widget.controller,
-          queryInstructions: NetworkScreenBody.filterQueryInstructions,
-        ),
-      ),
-    );
-  }
 }
 
 class _NetworkProfilerBody extends StatelessWidget {
@@ -364,11 +321,11 @@
 class AddressColumn extends ColumnData<NetworkRequest>
     implements ColumnRenderer<NetworkRequest> {
   AddressColumn()
-      : super.wide(
-          'Address',
-          minWidthPx: scaleByFontFactor(isEmbedded() ? 100 : 150.0),
-          showTooltip: true,
-        );
+    : super.wide(
+        'Address',
+        minWidthPx: scaleByFontFactor(isEmbedded() ? 100 : 150.0),
+        showTooltip: true,
+      );
 
   @override
   String getValue(NetworkRequest dataObject) {
@@ -409,11 +366,11 @@
 class ActionsColumn extends ColumnData<NetworkRequest>
     implements ColumnRenderer<NetworkRequest> {
   ActionsColumn()
-      : super(
-          '',
-          fixedWidthPx: scaleByFontFactor(32),
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        '',
+        fixedWidthPx: scaleByFontFactor(32),
+        alignment: ColumnAlignment.right,
+      );
 
   static const _actionSplashRadius = 16.0;
 
@@ -484,12 +441,12 @@
 class StatusColumn extends ColumnData<NetworkRequest>
     implements ColumnRenderer<NetworkRequest> {
   StatusColumn()
-      : super(
-          'Status',
-          alignment: ColumnAlignment.right,
-          headerAlignment: TextAlign.right,
-          fixedWidthPx: scaleByFontFactor(50),
-        );
+    : super(
+        'Status',
+        alignment: ColumnAlignment.right,
+        headerAlignment: TextAlign.right,
+        fixedWidthPx: scaleByFontFactor(50),
+      );
 
   @override
   String? getValue(NetworkRequest dataObject) {
@@ -512,21 +469,22 @@
     final theme = Theme.of(context);
     return Text(
       getDisplayValue(data),
-      style: data.didFail
-          ? TextStyle(color: theme.colorScheme.error)
-          : theme.regularTextStyle,
+      style:
+          data.didFail
+              ? TextStyle(color: theme.colorScheme.error)
+              : theme.regularTextStyle,
     );
   }
 }
 
 class TypeColumn extends ColumnData<NetworkRequest> {
   TypeColumn()
-      : super(
-          'Type',
-          alignment: ColumnAlignment.right,
-          headerAlignment: TextAlign.right,
-          fixedWidthPx: scaleByFontFactor(50),
-        );
+    : super(
+        'Type',
+        alignment: ColumnAlignment.right,
+        headerAlignment: TextAlign.right,
+        fixedWidthPx: scaleByFontFactor(50),
+      );
 
   @override
   String getValue(NetworkRequest dataObject) {
@@ -541,12 +499,12 @@
 
 class DurationColumn extends ColumnData<NetworkRequest> {
   DurationColumn()
-      : super(
-          'Duration',
-          alignment: ColumnAlignment.right,
-          headerAlignment: TextAlign.right,
-          fixedWidthPx: scaleByFontFactor(75),
-        );
+    : super(
+        'Duration',
+        alignment: ColumnAlignment.right,
+        headerAlignment: TextAlign.right,
+        fixedWidthPx: scaleByFontFactor(75),
+      );
 
   @override
   int? getValue(NetworkRequest dataObject) {
@@ -558,21 +516,18 @@
     final ms = getValue(dataObject);
     return ms == null
         ? 'Pending'
-        : durationText(
-            Duration(milliseconds: ms),
-            fractionDigits: 0,
-          );
+        : durationText(Duration(milliseconds: ms), fractionDigits: 0);
   }
 }
 
 class TimestampColumn extends ColumnData<NetworkRequest> {
   TimestampColumn()
-      : super(
-          'Timestamp',
-          alignment: ColumnAlignment.right,
-          headerAlignment: TextAlign.right,
-          fixedWidthPx: scaleByFontFactor(115),
-        );
+    : super(
+        'Timestamp',
+        alignment: ColumnAlignment.right,
+        headerAlignment: TextAlign.right,
+        fixedWidthPx: scaleByFontFactor(115),
+      );
 
   @override
   DateTime? getValue(NetworkRequest dataObject) {
diff --git a/packages/devtools_app/lib/src/screens/network/network_service.dart b/packages/devtools_app/lib/src/screens/network/network_service.dart
index 5e78a4d..ae21304 100644
--- a/packages/devtools_app/lib/src/screens/network/network_service.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_service.dart
@@ -42,9 +42,7 @@
   /// would have occurred, so the refresh time is not updated. Otherwise,
   /// [NetworkController.lastHttpDataRefreshTime] is updated to the current
   /// time.
-  void updateLastHttpDataRefreshTime({
-    bool alreadyRecordingHttp = false,
-  }) {
+  void updateLastHttpDataRefreshTime({bool alreadyRecordingHttp = false}) {
     if (!alreadyRecordingHttp) {
       networkController.lastHttpDataRefreshTime = DateTime.now();
     }
@@ -128,8 +126,8 @@
     if (service == null) return;
     await service.forEachIsolate((isolate) async {
       final isolateId = isolate.id!;
-      final socketProfilingAvailable =
-          await service.isSocketProfilingAvailableWrapper(isolateId);
+      final socketProfilingAvailable = await service
+          .isSocketProfilingAvailableWrapper(isolateId);
       if (socketProfilingAvailable) {
         final future = service.clearSocketProfileWrapper(isolateId);
         // The above call won't complete immediately if the isolate is paused, so
@@ -148,8 +146,8 @@
     if (service == null) return;
     await service.forEachIsolate((isolate) async {
       final isolateId = isolate.id!;
-      final socketProfilingAvailable =
-          await service.isSocketProfilingAvailableWrapper(isolateId);
+      final socketProfilingAvailable = await service
+          .isSocketProfilingAvailableWrapper(isolateId);
       if (socketProfilingAvailable) {
         final future = service.socketProfilingEnabledWrapper(isolateId, state);
         // The above call won't complete immediately if the isolate is paused, so
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing.dart b/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing.dart
index 83478a5..b08fbe7 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing.dart
@@ -11,17 +11,14 @@
 
 import '../../../../../service/service_extension_widgets.dart';
 import '../../../../../service/service_extensions.dart' as extensions;
-import '../../../../../shared/common_widgets.dart';
 import '../../../../../shared/globals.dart';
 import '../../../../../shared/primitives/utils.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '../performance_controls.dart';
 import 'enhance_tracing_controller.dart';
 
 class EnhanceTracingButton extends StatelessWidget {
-  const EnhanceTracingButton(
-    this.enhanceTracingController, {
-    super.key,
-  });
+  const EnhanceTracingButton(this.enhanceTracingController, {super.key});
 
   static const title = 'Enhance Tracing';
 
@@ -49,7 +46,8 @@
       },
       overlayDescription: RichText(
         text: TextSpan(
-          text: 'These options can be used to add more detail to the '
+          text:
+              'These options can be used to add more detail to the '
               'timeline, but be aware that ',
           style: textStyle,
           children: [
@@ -57,12 +55,10 @@
               text: 'frame times may be negatively affected',
               style: textStyle.copyWith(color: theme.colorScheme.error),
             ),
+            TextSpan(text: '.\n\n', style: textStyle),
             TextSpan(
-              text: '.\n\n',
-              style: textStyle,
-            ),
-            TextSpan(
-              text: 'When toggling on/off a tracing option, you will need '
+              text:
+                  'When toggling on/off a tracing option, you will need '
                   'to reproduce activity in your app to see the enhanced '
                   'tracing in the timeline.',
               style: textStyle,
@@ -74,10 +70,7 @@
   }
 }
 
-enum TraceWidgetBuildsScope {
-  all,
-  userCreated,
-}
+enum TraceWidgetBuildsScope { all, userCreated }
 
 extension TraceWidgetBuildsScopeExtension on TraceWidgetBuildsScope {
   String get radioDisplay {
@@ -153,19 +146,20 @@
         serviceConnection.serviceManager.serviceExtensionManager
             .waitForServiceExtensionAvailable(extension.extension)
             .then((isServiceAvailable) {
-          if (isServiceAvailable) {
-            _tracingAvailable.value = true;
+              if (isServiceAvailable) {
+                _tracingAvailable.value = true;
 
-            final state = serviceConnection
-                .serviceManager.serviceExtensionManager
-                .getServiceExtensionState(extension.extension);
+                final state = serviceConnection
+                    .serviceManager
+                    .serviceExtensionManager
+                    .getServiceExtensionState(extension.extension);
 
-            _updateForServiceExtensionState(state.value, type);
-            addAutoDisposeListener(state, () {
-              _updateForServiceExtensionState(state.value, type);
-            });
-          }
-        }),
+                _updateForServiceExtensionState(state.value, type);
+                addAutoDisposeListener(state, () {
+                  _updateForServiceExtensionState(state.value, type);
+                });
+              }
+            }),
       );
     }
   }
@@ -174,15 +168,17 @@
     ServiceExtensionState newState,
     TraceWidgetBuildsScope type,
   ) async {
-    final otherState = serviceConnection.serviceManager.serviceExtensionManager
-        .getServiceExtensionState(type.opposite.extensionForScope.extension)
-        .value
-        .enabled;
+    final otherState =
+        serviceConnection.serviceManager.serviceExtensionManager
+            .getServiceExtensionState(type.opposite.extensionForScope.extension)
+            .value
+            .enabled;
     final traceAllWidgets =
         type == TraceWidgetBuildsScope.all ? newState.enabled : otherState;
-    final traceUserWidgets = type == TraceWidgetBuildsScope.userCreated
-        ? newState.enabled
-        : otherState;
+    final traceUserWidgets =
+        type == TraceWidgetBuildsScope.userCreated
+            ? newState.enabled
+            : otherState;
     await _updateTracing(
       traceAllWidgets: traceAllWidgets,
       traceUserWidgets: traceUserWidgets,
@@ -199,10 +195,10 @@
       // widgets. Disable the service extension for tracing all widgets.
       await serviceConnection.serviceManager.serviceExtensionManager
           .setServiceExtensionState(
-        extensions.profileWidgetBuilds.extension,
-        enabled: false,
-        value: extensions.profileWidgetBuilds.disabledValue,
-      );
+            extensions.profileWidgetBuilds.extension,
+            enabled: false,
+            value: extensions.profileWidgetBuilds.disabledValue,
+          );
       traceAllWidgets = false;
     }
 
@@ -211,9 +207,10 @@
     // Double nested conditinoal expressions are hard to read.
     // ignore: prefer-conditional-expression
     if (_tracingEnabled.value) {
-      _selectedScope.value = traceUserWidgets
-          ? TraceWidgetBuildsScope.userCreated
-          : TraceWidgetBuildsScope.all;
+      _selectedScope.value =
+          traceUserWidgets
+              ? TraceWidgetBuildsScope.userCreated
+              : TraceWidgetBuildsScope.all;
     } else {
       _selectedScope.value = null;
     }
@@ -234,10 +231,7 @@
           },
         ),
         MultiValueListenableBuilder(
-          listenables: [
-            _tracingEnabled,
-            _selectedScope,
-          ],
+          listenables: [_tracingEnabled, _selectedScope],
           builder: (context, values, _) {
             final tracingEnabled = values.first as bool;
             final selectedScope = values.second as TraceWidgetBuildsScope?;
@@ -300,26 +294,27 @@
 
   void _checkboxChanged(bool? value) async {
     final enabled = value == true;
-    final tracingExtensions =
-        TraceWidgetBuildsScope.values.map((scope) => scope.extensionForScope);
+    final tracingExtensions = TraceWidgetBuildsScope.values.map(
+      (scope) => scope.extensionForScope,
+    );
     if (enabled) {
       // Default to tracing only user-created widgets.
       final extension = extensions.profileUserWidgetBuilds;
       await serviceConnection.serviceManager.serviceExtensionManager
           .setServiceExtensionState(
-        extension.extension,
-        enabled: true,
-        value: extension.enabledValue,
-      );
+            extension.extension,
+            enabled: true,
+            value: extension.enabledValue,
+          );
     } else {
       await Future.wait([
         for (final extension in tracingExtensions)
           serviceConnection.serviceManager.serviceExtensionManager
               .setServiceExtensionState(
-            extension.extension,
-            enabled: false,
-            value: extension.disabledValue,
-          ),
+                extension.extension,
+                enabled: false,
+                value: extension.disabledValue,
+              ),
       ]);
     }
   }
@@ -347,10 +342,7 @@
           textStyle: textStyle,
         ),
         const SizedBox(width: defaultSpacing),
-        ..._scopeSetting(
-          TraceWidgetBuildsScope.all,
-          textStyle: textStyle,
-        ),
+        ..._scopeSetting(TraceWidgetBuildsScope.all, textStyle: textStyle),
       ],
     );
   }
@@ -366,10 +358,7 @@
         materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
         onChanged: enabled ? _changeScope : null,
       ),
-      Text(
-        type.radioDisplay,
-        style: textStyle,
-      ),
+      Text(type.radioDisplay, style: textStyle),
     ];
   }
 
@@ -377,21 +366,19 @@
     assert(enabled);
     final extension = type!.extensionForScope;
     final opposite = type.opposite.extensionForScope;
-    await Future.wait(
-      [
-        serviceConnection.serviceManager.serviceExtensionManager
-            .setServiceExtensionState(
-          opposite.extension,
-          enabled: false,
-          value: opposite.disabledValue,
-        ),
-        serviceConnection.serviceManager.serviceExtensionManager
-            .setServiceExtensionState(
-          extension.extension,
-          enabled: true,
-          value: extension.enabledValue,
-        ),
-      ],
-    );
+    await Future.wait([
+      serviceConnection.serviceManager.serviceExtensionManager
+          .setServiceExtensionState(
+            opposite.extension,
+            enabled: false,
+            value: opposite.disabledValue,
+          ),
+      serviceConnection.serviceManager.serviceExtensionManager
+          .setServiceExtensionState(
+            extension.extension,
+            enabled: true,
+            value: extension.enabledValue,
+          ),
+    ]);
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing_controller.dart
index 6ab917b..e5d0e57 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing_controller.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/controls/enhance_tracing/enhance_tracing_controller.dart
@@ -53,20 +53,21 @@
   /// This method assigns [_firstLiveFrameId] when the first 'Flutter.Frame'
   /// event is received, and then cancels the stream subscription.
   void _listenForFirstLiveFrame() {
-    _firstFrameEventSubscription =
-        serviceConnection.serviceManager.service!.onExtensionEvent.listen(
-      (event) {
-        if (event.extensionKind == 'Flutter.Frame' &&
-            _firstLiveFrameId == null) {
-          _firstLiveFrameId =
-              FlutterFrame.fromJson(event.extensionData!.data).id;
-          // See https://github.com/dart-lang/linter/issues/3801
-          // ignore: discarded_futures
-          unawaited(_firstFrameEventSubscription!.cancel());
-          _firstFrameEventSubscription = null;
-        }
-      },
-    );
+    _firstFrameEventSubscription = serviceConnection
+        .serviceManager
+        .service!
+        .onExtensionEvent
+        .listen((event) {
+          if (event.extensionKind == 'Flutter.Frame' &&
+              _firstLiveFrameId == null) {
+            _firstLiveFrameId =
+                FlutterFrame.fromJson(event.extensionData!.data).id;
+            // See https://github.com/dart-lang/linter/issues/3801
+            // ignore: discarded_futures
+            unawaited(_firstFrameEventSubscription!.cancel());
+            _firstFrameEventSubscription = null;
+          }
+        });
   }
 
   void init() {
@@ -92,7 +93,8 @@
   }
 
   void _updateTracingState() {
-    final builds = _extensionStates[extensions.profileWidgetBuilds]! ||
+    final builds =
+        _extensionStates[extensions.profileWidgetBuilds]! ||
         _extensionStates[extensions.profileUserWidgetBuilds]!;
     final layouts = _extensionStates[extensions.profileRenderObjectLayouts]!;
     final paints = _extensionStates[extensions.profileRenderObjectPaints]!;
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/controls/more_debugging_options.dart b/packages/devtools_app/lib/src/screens/performance/panes/controls/more_debugging_options.dart
index c4df180..630bc00 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/controls/more_debugging_options.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/controls/more_debugging_options.dart
@@ -42,15 +42,18 @@
             style: theme.subtleTextStyle,
           ),
           if (serviceConnection
-              .serviceManager.connectedApp!.isProfileBuildNow!) ...[
+              .serviceManager
+              .connectedApp!
+              .isProfileBuildNow!) ...[
             const SizedBox(height: denseSpacing),
             RichText(
               text: TextSpan(
                 text:
                     "These debugging options aren't available in profile mode. "
                     'To use them, run your app in debug mode.',
-                style: theme.subtleTextStyle
-                    .copyWith(color: theme.colorScheme.error),
+                style: theme.subtleTextStyle.copyWith(
+                  color: theme.colorScheme.error,
+                ),
               ),
             ),
           ],
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_controls.dart b/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_controls.dart
index ef31f16..b592f82 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_controls.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_controls.dart
@@ -11,10 +11,10 @@
 import '../../../../service/service_extensions.dart' as extensions;
 import '../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
-import '../../../../shared/file_import.dart';
+import '../../../../shared/framework/screen.dart';
 import '../../../../shared/globals.dart';
-import '../../../../shared/screen.dart';
+import '../../../../shared/ui/common_widgets.dart';
+import '../../../../shared/ui/file_import.dart';
 import '../../../../shared/ui/utils.dart';
 import '../../panes/timeline_events/timeline_events_controller.dart';
 import '../../performance_controller.dart';
@@ -89,7 +89,9 @@
     return Row(
       children: [
         if (serviceConnection
-            .serviceManager.connectedApp!.isFlutterAppNow!) ...[
+            .serviceManager
+            .connectedApp!
+            .isFlutterAppNow!) ...[
           VisibilityButton(
             show: preferences.performance.showFlutterFramesChart,
             gaScreen: gac.performance,
@@ -123,9 +125,7 @@
 }
 
 class _SecondaryPerformanceControls extends StatelessWidget {
-  const _SecondaryPerformanceControls({
-    required this.controller,
-  });
+  const _SecondaryPerformanceControls({required this.controller});
 
   final PerformanceController controller;
 
@@ -140,9 +140,7 @@
           ServiceExtensionButtonGroup(
             minScreenWidthForTextBeforeScaling:
                 PerformanceControls.minScreenWidthForTextBeforeScaling,
-            extensions: [
-              extensions.performanceOverlay,
-            ],
+            extensions: [extensions.performanceOverlay],
           ),
           const SizedBox(width: denseSpacing),
           EnhanceTracingButton(controller.enhanceTracingController),
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_settings.dart b/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_settings.dart
index 2ec00c3..b85844d 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_settings.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/controls/performance_settings.dart
@@ -5,8 +5,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/globals.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../performance_controller.dart';
 import '../flutter_frames/flutter_frames_controller.dart';
 
@@ -32,9 +32,7 @@
           ),
         ],
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
@@ -50,8 +48,9 @@
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         CheckboxSetting(
-          notifier: flutterFramesController.badgeTabForJankyFrames
-              as ValueNotifier<bool?>,
+          notifier:
+              flutterFramesController.badgeTabForJankyFrames
+                  as ValueNotifier<bool?>,
           title: 'Badge Performance tab when Flutter UI jank is detected',
         ),
       ],
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart
index bc4443b..896735b 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frame_model.dart
@@ -27,9 +27,10 @@
     final timeStart = Duration(microseconds: json[startTimeKey]! as int);
     final timeEnd =
         timeStart + Duration(microseconds: json[elapsedKey]! as int);
-    final frameTime = TimeRange()
-      ..start = timeStart
-      ..end = timeEnd;
+    final frameTime =
+        TimeRange()
+          ..start = timeStart
+          ..end = timeEnd;
     return FlutterFrame._(
       id: json[numberKey]! as int,
       timeFromFrameTiming: frameTime,
@@ -107,8 +108,10 @@
     if (timelineEventData.rasterEvent == null) return Duration.zero;
     final shaderEvents = timelineEventData.rasterEvent!
         .shallowNodesWithCondition((event) => event.isShaderEvent);
-    final duration =
-        shaderEvents.fold<Duration>(Duration.zero, (previous, event) {
+    final duration = shaderEvents.fold<Duration>(Duration.zero, (
+      previous,
+      event,
+    ) {
       return previous + event.time.duration;
     });
     return _shaderTime = duration;
@@ -147,13 +150,13 @@
   }
 
   Map<String, Object?> get json => {
-        numberKey: id,
-        startTimeKey: timeFromFrameTiming.start!.inMicroseconds,
-        elapsedKey: timeFromFrameTiming.duration.inMicroseconds,
-        buildKey: buildTime.inMicroseconds,
-        rasterKey: rasterTime.inMicroseconds,
-        vsyncOverheadKey: vsyncOverheadTime.inMicroseconds,
-      };
+    numberKey: id,
+    startTimeKey: timeFromFrameTiming.start!.inMicroseconds,
+    elapsedKey: timeFromFrameTiming.duration.inMicroseconds,
+    buildKey: buildTime.inMicroseconds,
+    rasterKey: rasterTime.inMicroseconds,
+    vsyncOverheadKey: vsyncOverheadTime.inMicroseconds,
+  };
 
   @override
   String toString() {
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_chart.dart b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_chart.dart
index cb3b573..814964c 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_chart.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_chart.dart
@@ -9,18 +9,17 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
-import '../../../../framework/scaffold.dart';
 import '../../../../shared/analytics/analytics.dart' as ga;
 import '../../../../shared/analytics/constants.dart' as gac;
 import '../../../../shared/analytics/metrics.dart';
-import '../../../../shared/banner_messages.dart';
-import '../../../../shared/common_widgets.dart';
+import '../../../../shared/framework/screen.dart';
 import '../../../../shared/globals.dart';
+import '../../../../shared/managers/banner_messages.dart';
 import '../../../../shared/primitives/utils.dart';
 import '../../../../shared/ui/colors.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../../../shared/ui/hover.dart';
 import '../../../../shared/ui/utils.dart';
-import '../../performance_screen.dart';
 import '../../performance_utils.dart';
 import 'flutter_frame_model.dart';
 import 'flutter_frames_controller.dart';
@@ -113,9 +112,10 @@
   }
 
   void _maybeShowShaderJankMessage() {
-    final shaderJankFrames = widget.frames
-        .where((frame) => frame.hasShaderJank(widget.displayRefreshRate))
-        .toList();
+    final shaderJankFrames =
+        widget.frames
+            .where((frame) => frame.hasShaderJank(widget.displayRefreshRate))
+            .toList();
     if (shaderJankFrames.isNotEmpty) {
       final Duration shaderJankDuration = shaderJankFrames.fold(
         Duration.zero,
@@ -124,8 +124,8 @@
       bannerMessages.addMessage(
         ShaderJankMessage(
           offlineDataController.showingOfflineData.value
-              ? SimpleScreen.id
-              : PerformanceScreen.id,
+              ? ScreenMetaData.simple.id
+              : ScreenMetaData.performance.id,
           jankyFramesCount: shaderJankFrames.length,
           jankDuration: shaderJankDuration,
         ).build(context),
@@ -145,7 +145,8 @@
         right: denseSpacing,
         bottom: denseSpacing,
       ),
-      height: defaultChartHeight +
+      height:
+          defaultChartHeight +
           _FlutterFramesChart.frameNumberSectionHeight +
           _FlutterFramesChart.frameChartScrollbarOffset,
       child: Row(
@@ -299,17 +300,18 @@
             scrollDirection: Axis.horizontal,
             itemCount: widget.frames.length,
             itemExtent: _defaultFrameWidthWithPadding,
-            itemBuilder: (context, index) => FlutterFramesChartItem(
-              framesController: widget.framesController,
-              index: index,
-              frame: widget.frames[index],
-              selected: widget.frames[index] == _selectedFrame,
-              msPerPx: _msPerPx,
-              availableChartHeight:
-                  defaultChartHeight - 2 * _outlineBorderWidth,
-              displayRefreshRate: widget.displayRefreshRate,
-              onSelected: (index) => _selectedFrameIndex = index,
-            ),
+            itemBuilder:
+                (context, index) => FlutterFramesChartItem(
+                  framesController: widget.framesController,
+                  index: index,
+                  frame: widget.frames[index],
+                  selected: widget.frames[index] == _selectedFrame,
+                  msPerPx: _msPerPx,
+                  availableChartHeight:
+                      defaultChartHeight - 2 * _outlineBorderWidth,
+                  displayRefreshRate: widget.displayRefreshRate,
+                  onSelected: (index) => _selectedFrameIndex = index,
+                ),
           ),
         ),
       ),
@@ -321,7 +323,8 @@
         displayRefreshRate: widget.displayRefreshRate,
         msPerPx: _msPerPx,
         themeData: themeData,
-        bottomMargin: _FlutterFramesChart.frameChartScrollbarOffset +
+        bottomMargin:
+            _FlutterFramesChart.frameChartScrollbarOffset +
             _FlutterFramesChart.frameNumberSectionHeight,
       ),
     );
@@ -332,17 +335,15 @@
         displayRefreshRate: widget.displayRefreshRate,
         msPerPx: _msPerPx,
         themeData: themeData,
-        bottomMargin: _FlutterFramesChart.frameChartScrollbarOffset +
+        bottomMargin:
+            _FlutterFramesChart.frameChartScrollbarOffset +
             _FlutterFramesChart.frameNumberSectionHeight,
       ),
     );
     return Stack(
       children: [
         chartAxisPainter,
-        Padding(
-          padding: EdgeInsets.only(left: _yAxisUnitsSpace),
-          child: chart,
-        ),
+        Padding(padding: EdgeInsets.only(left: _yAxisUnitsSpace), child: chart),
         fpsLinePainter,
         Positioned(
           right: denseSpacing,
@@ -409,18 +410,12 @@
         Legend(
           dense: true,
           entries: [
-            LegendEntry(
-              terse ? 'UI' : 'Frame Time (UI)',
-              mainUiColor,
-            ),
+            LegendEntry(terse ? 'UI' : 'Frame Time (UI)', mainUiColor),
             LegendEntry(
               terse ? 'Raster' : 'Frame Time (Raster)',
               mainRasterColor,
             ),
-            LegendEntry(
-              terse ? 'Jank' : 'Jank (slow frame)',
-              uiJankColor,
-            ),
+            LegendEntry(terse ? 'Jank' : 'Jank (slow frame)', uiJankColor),
             if (!impellerEnabled)
               LegendEntry(
                 'Shader Compilation',
@@ -465,8 +460,9 @@
 
   static const selectedIndicatorHeight = 8.0;
 
-  static const selectedFrameIndicatorKey =
-      Key('flutter frames chart - selected frame indicator');
+  static const selectedFrameIndicatorKey = Key(
+    'flutter frames chart - selected frame indicator',
+  );
 
   final FlutterFramesController framesController;
 
@@ -512,8 +508,10 @@
     final ui = Container(
       key: Key('frame ${frame.id} - ui'),
       width: defaultFrameWidth / 2,
-      height: (frame.buildTime.inMilliseconds / msPerPx)
-          .clamp(0.0, availableChartHeight),
+      height: (frame.buildTime.inMilliseconds / msPerPx).clamp(
+        0.0,
+        availableChartHeight,
+      ),
       color: uiColor,
     );
 
@@ -534,8 +532,10 @@
           Container(
             key: Key('frame ${frame.id} - shaders'),
             width: defaultFrameWidth / 2,
-            height: (shaderDuration / msPerPx)
-                .clamp(0.0, availableChartHeight * shaderToRasterRatio),
+            height: (shaderDuration / msPerPx).clamp(
+              0.0,
+              availableChartHeight * shaderToRasterRatio,
+            ),
             color: shaderColor,
           ),
       ],
@@ -563,10 +563,7 @@
                     const Expanded(child: SizedBox()),
                     Row(
                       crossAxisAlignment: CrossAxisAlignment.end,
-                      children: [
-                        ui,
-                        raster,
-                      ],
+                      children: [ui, raster],
                     ),
                   ],
                 ),
@@ -591,19 +588,16 @@
     );
     return index % 2 == 0
         ? Stack(
-            children: [
-              content,
-              Container(
-                margin: EdgeInsets.only(top: defaultChartHeight),
-                height: _FlutterFramesChart.frameNumberSectionHeight,
-                alignment: AlignmentDirectional.center,
-                child: Text(
-                  '${frame.id}',
-                  style: themeData.subtleChartTextStyle,
-                ),
-              ),
-            ],
-          )
+          children: [
+            content,
+            Container(
+              margin: EdgeInsets.only(top: defaultChartHeight),
+              height: _FlutterFramesChart.frameNumberSectionHeight,
+              alignment: AlignmentDirectional.center,
+              child: Text('${frame.id}', style: themeData.subtleChartTextStyle),
+            ),
+          ],
+        )
         : content;
   }
 
@@ -616,11 +610,12 @@
       ga.select(
         gac.performance,
         gac.PerformanceEvents.selectFlutterFrame.name,
-        screenMetricsProvider: () => PerformanceScreenMetrics(
-          uiDuration: frame.buildTime,
-          rasterDuration: frame.rasterTime,
-          shaderCompilationDuration: frame.shaderDuration,
-        ),
+        screenMetricsProvider:
+            () => PerformanceScreenMetrics(
+              uiDuration: frame.buildTime,
+              rasterDuration: frame.rasterTime,
+              shaderCompilationDuration: frame.shaderDuration,
+            ),
       );
     }
     framesController.handleSelectedFrame(frame);
@@ -657,29 +652,21 @@
   }
 
   HoverCardData _buildCardData(TextStyle textStyle) {
-    final uiText = 'UI: ${durationText(
-      frame.buildTime,
-      unit: DurationDisplayUnit.milliseconds,
-      allowRoundingToZero: false,
-    )}';
-    final rasterText = 'Raster: ${durationText(
-      frame.rasterTime,
-      unit: DurationDisplayUnit.milliseconds,
-      allowRoundingToZero: false,
-    )}';
-    final shaderText = hasShaderJank
-        ? 'Shader Compilation: ${durationText(
-            frame.shaderDuration,
-            unit: DurationDisplayUnit.milliseconds,
-            allowRoundingToZero: false,
-          )}  -'
-        : '';
+    final uiText =
+        'UI: ${durationText(frame.buildTime, unit: DurationDisplayUnit.milliseconds, allowRoundingToZero: false)}';
+    final rasterText =
+        'Raster: ${durationText(frame.rasterTime, unit: DurationDisplayUnit.milliseconds, allowRoundingToZero: false)}';
+    final shaderText =
+        hasShaderJank
+            ? 'Shader Compilation: ${durationText(frame.shaderDuration, unit: DurationDisplayUnit.milliseconds, allowRoundingToZero: false)}  -'
+            : '';
     return HoverCardData(
       position: HoverCardPosition.element,
-      width: _calculateTooltipWidth(
-        [uiText, rasterText, shaderText],
-        textStyle,
-      ),
+      width: _calculateTooltipWidth([
+        uiText,
+        rasterText,
+        shaderText,
+      ], textStyle),
       contents: Material(
         child: DefaultTextStyle(
           style: textStyle,
@@ -701,8 +688,11 @@
                     MoreInfoLink(
                       url: preCompileShadersDocsUrl,
                       gaScreenName: gac.performance,
-                      gaSelectedItemDescription: gac.PerformanceDocs
-                          .shaderCompilationDocsTooltipLink.name,
+                      gaSelectedItemDescription:
+                          gac
+                              .PerformanceDocs
+                              .shaderCompilationDocsTooltipLink
+                              .name,
                     ),
                   ],
                 ),
@@ -716,8 +706,9 @@
   double _calculateTooltipWidth(List<String> lines, TextStyle style) {
     var maxWidth = 0.0;
     for (final line in lines) {
-      final lineWidth =
-          calculateTextSpanWidth(TextSpan(text: line, style: style));
+      final lineWidth = calculateTextSpanWidth(
+        TextSpan(text: line, style: style),
+      );
       maxWidth = math.max(maxWidth, lineWidth);
     }
     // Add (2 * denseSpacing) for the card padding, and add
@@ -830,10 +821,7 @@
     _paintYAxisLabels(canvas, chartArea);
   }
 
-  void _paintYAxisLabels(
-    Canvas canvas,
-    Rect chartArea,
-  ) {
+  void _paintYAxisLabels(Canvas canvas, Rect chartArea) {
     const yAxisLabelCount = 5;
     final totalMs = msPerPx * constraints.maxHeight;
 
@@ -849,14 +837,18 @@
     // Y axis time units centered around [targetMsPerFrameRounded].
     final yAxisTimes = [
       0,
-      for (int timeMs = targetMsPerFrameRounded - timeUnitMs;
-          timeMs > 0;
-          timeMs -= timeUnitMs)
+      for (
+        int timeMs = targetMsPerFrameRounded - timeUnitMs;
+        timeMs > 0;
+        timeMs -= timeUnitMs
+      )
         timeMs,
       targetMsPerFrameRounded,
-      for (int timeMs = targetMsPerFrameRounded + timeUnitMs;
-          timeMs < totalMs;
-          timeMs += timeUnitMs)
+      for (
+        int timeMs = targetMsPerFrameRounded + timeUnitMs;
+        timeMs < totalMs;
+        timeMs += timeUnitMs
+      )
         timeMs,
     ];
 
@@ -865,11 +857,7 @@
     }
   }
 
-  void _paintYAxisLabel(
-    Canvas canvas,
-    Rect chartArea, {
-    required int timeMs,
-  }) {
+  void _paintYAxisLabel(Canvas canvas, Rect chartArea, {required int timeMs}) {
     final labelText = durationText(
       Duration(milliseconds: timeMs),
       unit: DurationDisplayUnit.milliseconds,
@@ -893,10 +881,7 @@
 
     // Paint the axis label.
     final textPainter = TextPainter(
-      text: TextSpan(
-        text: labelText,
-        style: themeData.subtleChartTextStyle,
-      ),
+      text: TextSpan(text: labelText, style: themeData.subtleChartTextStyle),
       textAlign: TextAlign.end,
       textDirection: TextDirection.ltr,
     )..layout();
@@ -979,10 +964,7 @@
 
     textPainter.paint(
       canvas,
-      Offset(
-        chartArea.right - fpsTextSpace,
-        targetLineY + borderPadding,
-      ),
+      Offset(chartArea.right - fpsTextSpace, targetLineY + borderPadding),
     );
   }
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart
index 24da9f3..ae59453 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/flutter_frames/flutter_frames_controller.dart
@@ -75,9 +75,10 @@
         // Default to true for profile builds only.
         _badgeTabForJankyFrames.value = await connectedApp.isProfileBuild;
 
-        final refreshRate = connectedApp.isFlutterAppNow!
-            ? await serviceConnection.queryDisplayRefreshRate
-            : defaultRefreshRate;
+        final refreshRate =
+            connectedApp.isFlutterAppNow!
+                ? await serviceConnection.queryDisplayRefreshRate
+                : defaultRefreshRate;
 
         _displayRefreshRate.value = refreshRate ?? defaultRefreshRate;
       }
@@ -143,8 +144,9 @@
   void _maybeBadgeTabForJankyFrame(FlutterFrame frame) {
     if (_badgeTabForJankyFrames.value) {
       if (frame.isJanky(_displayRefreshRate.value)) {
-        serviceConnection.errorBadgeManager
-            .incrementBadgeCount(PerformanceScreen.id);
+        serviceConnection.errorBadgeManager.incrementBadgeCount(
+          PerformanceScreen.id,
+        );
       }
     }
   }
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis.dart b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis.dart
index 9a03afe..45648c4 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis.dart
@@ -55,8 +55,9 @@
                 ),
                 TextSpan(
                   text: '${frame.id}',
-                  style: theme.fixedFontStyle
-                      .copyWith(color: theme.colorScheme.primary),
+                  style: theme.fixedFontStyle.copyWith(
+                    color: theme.colorScheme.primary,
+                  ),
                 ),
               ],
             ),
@@ -94,10 +95,11 @@
               const PaddedDivider.noPadding(),
               ValueListenableBuilder<ServiceExtensionState>(
                 valueListenable: serviceConnection
-                    .serviceManager.serviceExtensionManager
+                    .serviceManager
+                    .serviceExtensionManager
                     .getServiceExtensionState(
-                  extensions.countWidgetBuilds.extension,
-                ),
+                      extensions.countWidgetBuilds.extension,
+                    ),
                 builder: (context, extensionState, _) {
                   if (!extensionState.enabled) {
                     return Row(
@@ -119,9 +121,7 @@
               ),
             ],
             if (rebuilds == null)
-              const Text(
-                'Rebuild information not available for this frame.',
-              )
+              const Text('Rebuild information not available for this frame.')
             else if (rebuilds.isEmpty)
               const Text(
                 'No widget rebuilds occurred for widgets that were directly '
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis_model.dart b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis_model.dart
index c3bc842..4b7fe9e 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis_model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_analysis_model.dart
@@ -63,12 +63,12 @@
         final buildChildren = layoutEvent.shallowNodesWithCondition(
           (event) => FramePhaseType.build.isMatchForEventName(event.name),
         );
-        final buildDuration = buildChildren.fold<Duration>(
-          Duration.zero,
-          (previous, FlutterTimelineEvent event) {
-            return previous + event.time.duration;
-          },
-        );
+        final buildDuration = buildChildren.fold<Duration>(Duration.zero, (
+          previous,
+          FlutterTimelineEvent event,
+        ) {
+          return previous + event.time.duration;
+        });
 
         return FramePhase.layout(
           events: <FlutterTimelineEvent>[layoutEvent],
@@ -109,11 +109,13 @@
 
   late FramePhase longestUiPhase = _calculateLongestFramePhase();
 
-  bool get hasUiData => _hasUiData ??= [
-        ...buildPhase.events,
-        ...layoutPhase.events,
-        ...paintPhase.events,
-      ].isNotEmpty;
+  bool get hasUiData =>
+      _hasUiData ??=
+          [
+            ...buildPhase.events,
+            ...layoutPhase.events,
+            ...paintPhase.events,
+          ].isNotEmpty;
 
   bool? _hasUiData;
 
@@ -272,15 +274,13 @@
 }
 
 class FramePhase {
-  FramePhase._({
-    required this.type,
-    required this.events,
-    Duration? duration,
-  })  : title = type.display,
-        duration = duration ??
-            events.fold<Duration>(Duration.zero, (previous, event) {
-              return previous + event.time.duration;
-            });
+  FramePhase._({required this.type, required this.events, Duration? duration})
+    : title = type.display,
+      duration =
+          duration ??
+          events.fold<Duration>(Duration.zero, (previous, event) {
+            return previous + event.time.duration;
+          });
 
   factory FramePhase.build({
     required List<FlutterTimelineEvent> events,
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_hints.dart b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_hints.dart
index 5be0064..24d2b46 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_hints.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_hints.dart
@@ -6,12 +6,12 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
+import '../../../../service/connected_app/connected_app.dart';
 import '../../../../service/service_extensions.dart' as extensions;
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
-import '../../../../shared/connected_app.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/primitives/utils.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../../performance_utils.dart';
 import '../controls/enhance_tracing/enhance_tracing.dart';
 import '../controls/enhance_tracing/enhance_tracing_controller.dart';
@@ -44,32 +44,34 @@
     final theme = Theme.of(context);
     final saveLayerCount = frameAnalysis.saveLayerCount;
     final intrinsicOperationsCount = frameAnalysis.intrinsicOperationsCount;
-    final uiHints = showUiJankHints
-        ? [
-            Text('UI Jank Detected', style: theme.errorTextStyle),
-            const SizedBox(height: denseSpacing),
-            EnhanceTracingHint(
-              longestPhase: frameAnalysis.longestUiPhase,
-              enhanceTracingState: frameAnalysis.frame.enhanceTracingState,
-              enhanceTracingController: enhanceTracingController,
-            ),
-            const SizedBox(height: densePadding),
-            if (intrinsicOperationsCount > 0)
-              IntrinsicOperationsHint(intrinsicOperationsCount),
-          ]
-        : <Widget>[];
-    final rasterHints = showRasterJankHints
-        ? [
-            Text('Raster Jank Detected', style: theme.errorTextStyle),
-            const SizedBox(height: denseSpacing),
-            if (saveLayerCount > 0) CanvasSaveLayerHint(saveLayerCount),
-            const SizedBox(height: denseSpacing),
-            if (frame.hasShaderTime)
-              ShaderCompilationHint(shaderTime: frame.shaderDuration),
-            const SizedBox(height: denseSpacing),
-            const GeneralRasterJankHint(),
-          ]
-        : <Widget>[];
+    final uiHints =
+        showUiJankHints
+            ? [
+              Text('UI Jank Detected', style: theme.errorTextStyle),
+              const SizedBox(height: denseSpacing),
+              EnhanceTracingHint(
+                longestPhase: frameAnalysis.longestUiPhase,
+                enhanceTracingState: frameAnalysis.frame.enhanceTracingState,
+                enhanceTracingController: enhanceTracingController,
+              ),
+              const SizedBox(height: densePadding),
+              if (intrinsicOperationsCount > 0)
+                IntrinsicOperationsHint(intrinsicOperationsCount),
+            ]
+            : <Widget>[];
+    final rasterHints =
+        showRasterJankHints
+            ? [
+              Text('Raster Jank Detected', style: theme.errorTextStyle),
+              const SizedBox(height: denseSpacing),
+              if (saveLayerCount > 0) CanvasSaveLayerHint(saveLayerCount),
+              const SizedBox(height: denseSpacing),
+              if (frame.hasShaderTime)
+                ShaderCompilationHint(shaderTime: frame.shaderDuration),
+              const SizedBox(height: denseSpacing),
+              const GeneralRasterJankHint(),
+            ]
+            : <Widget>[];
 
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
@@ -92,10 +94,7 @@
   Widget build(BuildContext context) {
     return Row(
       children: [
-        Icon(
-          Icons.lightbulb_outline,
-          size: defaultIconSize,
-        ),
+        Icon(Icons.lightbulb_outline, size: defaultIconSize),
         const SizedBox(width: denseSpacing),
         Expanded(child: message),
       ],
@@ -130,10 +129,7 @@
         text: TextSpan(
           text: '',
           children: [
-            TextSpan(
-              text: longestPhase.title,
-              style: theme.fixedFontStyle,
-            ),
+            TextSpan(text: longestPhase.title, style: theme.fixedFontStyle),
             TextSpan(
               text: ' was the longest UI phase in this frame. ',
               style: theme.regularTextStyle,
@@ -145,10 +141,7 @@
     );
   }
 
-  List<InlineSpan> _hintForPhase(
-    FramePhase phase,
-    ThemeData theme,
-  ) {
+  List<InlineSpan> _hintForPhase(FramePhase phase, ThemeData theme) {
     final phaseType = phase.type;
     // TODO(kenz): when [enhanceTracingState] is not available, use heuristics
     // to detect whether tracing was enhanced for a frame (e.g. the depth or
@@ -191,7 +184,8 @@
     if (tracingEnhanced) {
       return [
         TextSpan(
-          text: 'Since "$settingTitle" was enabled while this frame was drawn, '
+          text:
+              'Since "$settingTitle" was enabled while this frame was drawn, '
               'you should be able to see timeline events for each '
               '$eventDescription.',
           style: theme.regularTextStyle,
@@ -244,10 +238,7 @@
 
 @visibleForTesting
 class IntrinsicOperationsHint extends StatelessWidget {
-  const IntrinsicOperationsHint(
-    this.intrinsicOperationsCount, {
-    super.key,
-  });
+  const IntrinsicOperationsHint(this.intrinsicOperationsCount, {super.key});
 
   static const _intrinsicOperationsDocs =
       'https://flutter.dev/to/minimize-layout-passes';
@@ -265,12 +256,10 @@
             gac.PerformanceDocs.intrinsicOperationsDocs.name,
         message: TextSpan(
           children: [
+            TextSpan(text: 'Intrinsic', style: theme.fixedFontStyle),
             TextSpan(
-              text: 'Intrinsic',
-              style: theme.fixedFontStyle,
-            ),
-            TextSpan(
-              text: ' passes were performed $intrinsicOperationsCount '
+              text:
+                  ' passes were performed $intrinsicOperationsCount '
                   '${pluralize('time', intrinsicOperationsCount)} during this '
                   'frame.',
               style: theme.regularTextStyle,
@@ -288,10 +277,7 @@
 // args, display it in the hint.
 @visibleForTesting
 class CanvasSaveLayerHint extends StatelessWidget {
-  const CanvasSaveLayerHint(
-    this.saveLayerCount, {
-    super.key,
-  });
+  const CanvasSaveLayerHint(this.saveLayerCount, {super.key});
 
   static const _saveLayerDocs = 'https://flutter.dev/to/save-layer-perf';
 
@@ -307,12 +293,10 @@
         gaSelectedItemDescription: gac.PerformanceDocs.canvasSaveLayerDocs.name,
         message: TextSpan(
           children: [
+            TextSpan(text: 'Canvas.saveLayer()', style: theme.fixedFontStyle),
             TextSpan(
-              text: 'Canvas.saveLayer()',
-              style: theme.fixedFontStyle,
-            ),
-            TextSpan(
-              text: ' was called $saveLayerCount '
+              text:
+                  ' was called $saveLayerCount '
                   '${pluralize('time', saveLayerCount)} during this frame.',
               style: theme.regularTextStyle,
             ),
@@ -325,10 +309,7 @@
 
 @visibleForTesting
 class ShaderCompilationHint extends StatelessWidget {
-  const ShaderCompilationHint({
-    super.key,
-    required this.shaderTime,
-  });
+  const ShaderCompilationHint({super.key, required this.shaderTime});
 
   final Duration shaderTime;
 
@@ -356,30 +337,28 @@
             ),
           ],
         ),
-        childrenSpans: serviceConnection.serviceManager.connectedApp!.isIosApp
-            ? [
-                TextSpan(
-                  text:
-                      ' Note: pre-compiling shaders is a legacy solution with many '
-                      'pitfalls. Try ',
-                  style: theme.regularTextStyle,
-                ),
-                GaLinkTextSpan(
-                  link: GaLink(
-                    display: 'Impeller',
-                    url: impellerDocsUrl,
-                    gaScreenName: gac.performance,
-                    gaSelectedItemDescription:
-                        gac.PerformanceDocs.impellerDocsLink.name,
+        childrenSpans:
+            serviceConnection.serviceManager.connectedApp!.isIosApp
+                ? [
+                  TextSpan(
+                    text:
+                        ' Note: pre-compiling shaders is a legacy solution with many '
+                        'pitfalls. Try ',
+                    style: theme.regularTextStyle,
                   ),
-                  context: context,
-                ),
-                TextSpan(
-                  text: ' instead!',
-                  style: theme.regularTextStyle,
-                ),
-              ]
-            : [],
+                  GaLinkTextSpan(
+                    link: GaLink(
+                      display: 'Impeller',
+                      url: impellerDocsUrl,
+                      gaScreenName: gac.performance,
+                      gaSelectedItemDescription:
+                          gac.PerformanceDocs.impellerDocsLink.name,
+                    ),
+                    context: context,
+                  ),
+                  TextSpan(text: ' instead!', style: theme.regularTextStyle),
+                ]
+                : [],
       ),
     );
   }
@@ -397,7 +376,8 @@
         text: TextSpan(
           children: [
             TextSpan(
-              text: 'To learn about rendering performance in Flutter, check '
+              text:
+                  'To learn about rendering performance in Flutter, check '
                   'out the Flutter documentation on ',
               style: theme.regularTextStyle,
             ),
@@ -411,10 +391,7 @@
               ),
               context: context,
             ),
-            TextSpan(
-              text: '.',
-              style: theme.regularTextStyle,
-            ),
+            TextSpan(text: '.', style: theme.regularTextStyle),
           ],
         ),
       ),
@@ -444,10 +421,7 @@
       text: TextSpan(
         children: [
           message,
-          TextSpan(
-            text: ' This may ',
-            style: theme.regularTextStyle,
-          ),
+          TextSpan(text: ' This may ', style: theme.regularTextStyle),
           GaLinkTextSpan(
             context: context,
             link: GaLink(
@@ -458,10 +432,7 @@
                   'frameAnalysis_$gaSelectedItemDescription',
             ),
           ),
-          TextSpan(
-            text: '.',
-            style: theme.regularTextStyle,
-          ),
+          TextSpan(text: '.', style: theme.regularTextStyle),
           ...childrenSpans,
         ],
       ),
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_time_visualizer.dart b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_time_visualizer.dart
index 4515f57..a47b814 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_time_visualizer.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/frame_analysis/frame_time_visualizer.dart
@@ -13,10 +13,7 @@
 import 'frame_analysis_model.dart';
 
 class FrameTimeVisualizer extends StatefulWidget {
-  const FrameTimeVisualizer({
-    super.key,
-    required this.frameAnalysis,
-  });
+  const FrameTimeVisualizer({super.key, required this.frameAnalysis});
 
   final FrameAnalysis frameAnalysis;
 
@@ -154,12 +151,16 @@
   Widget build(BuildContext context) {
     Widget content;
     if (hasData) {
-      final totalFlex =
-          data.fold<int>(0, (current, block) => current + block.flex);
+      final totalFlex = data.fold<int>(
+        0,
+        (current, block) => current + block.flex,
+      );
       content = LayoutBuilder(
         builder: (context, constraints) {
-          final adjustedBlockWidths =
-              adjustedWidthsForBlocks(constraints, totalFlex);
+          final adjustedBlockWidths = adjustedWidthsForBlocks(
+            constraints,
+            totalFlex,
+          );
           return Row(
             children: [
               for (var i = 0; i < data.length; i++)
@@ -177,11 +178,7 @@
 
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        Text(title),
-        const SizedBox(height: denseSpacing),
-        content,
-      ],
+      children: [Text(title), const SizedBox(height: denseSpacing), content],
     );
   }
 
@@ -194,11 +191,12 @@
     BoxConstraints constraints,
     int totalFlex,
   ) {
-    final unadjustedBlockWidths = data
-        .map(
-          (blockData) => constraints.maxWidth * blockData.flex / totalFlex,
-        )
-        .toList();
+    final unadjustedBlockWidths =
+        data
+            .map(
+              (blockData) => constraints.maxWidth * blockData.flex / totalFlex,
+            )
+            .toList();
 
     var adjustment = 0.0;
     var widestBlockIndex = 0;
@@ -213,11 +211,13 @@
       }
     }
 
-    final adjustedBlockWidths = unadjustedBlockWidths
-        .map(
-          (blockWidth) => math.max(blockWidth, _FramePhaseBlock.minBlockWidth),
-        )
-        .toList();
+    final adjustedBlockWidths =
+        unadjustedBlockWidths
+            .map(
+              (blockWidth) =>
+                  math.max(blockWidth, _FramePhaseBlock.minBlockWidth),
+            )
+            .toList();
     final widest = adjustedBlockWidths[widestBlockIndex];
     adjustedBlockWidths[widestBlockIndex] = math.max(
       widest - adjustment,
@@ -229,10 +229,7 @@
 }
 
 class _FramePhaseBlock extends StatelessWidget {
-  const _FramePhaseBlock({
-    required this.blockData,
-    required this.width,
-  });
+  const _FramePhaseBlock({required this.blockData, required this.width});
 
   static const _height = 24.0;
 
@@ -263,7 +260,8 @@
         padding: const EdgeInsets.symmetric(horizontal: densePadding),
         child: LayoutBuilder(
           builder: (context, constraints) {
-            final minWidthForText = defaultIconSize +
+            final minWidthForText =
+                defaultIconSize +
                 densePadding * 2 +
                 denseSpacing +
                 calculateTextSpanWidth(TextSpan(text: blockData.display));
@@ -274,16 +272,10 @@
             return Row(
               mainAxisAlignment: MainAxisAlignment.center,
               children: [
-                Icon(
-                  blockData.icon,
-                  size: defaultIconSize,
-                ),
+                Icon(blockData.icon, size: defaultIconSize),
                 if (includeText) ...[
                   const SizedBox(width: denseSpacing),
-                  Text(
-                    blockData.display,
-                    overflow: TextOverflow.ellipsis,
-                  ),
+                  Text(blockData.display, overflow: TextOverflow.ellipsis),
                 ],
               ],
             );
@@ -311,13 +303,14 @@
   final IconData icon;
 
   String get display {
-    final text = duration != Duration.zero
-        ? durationText(
-            duration,
-            unit: DurationDisplayUnit.milliseconds,
-            allowRoundingToZero: false,
-          )
-        : '--';
+    final text =
+        duration != Duration.zero
+            ? durationText(
+              duration,
+              unit: DurationDisplayUnit.milliseconds,
+              allowRoundingToZero: false,
+            )
+            : '--';
     return '$title - $text';
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart
index 146f326..52a4d08 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats.dart
@@ -12,11 +12,11 @@
 import '../../../../service/service_extensions.dart' as extensions;
 import '../../../../service/vm_service_wrapper.dart';
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/primitives/utils.dart';
 import '../../../../shared/table/table.dart';
 import '../../../../shared/table/table_data.dart';
+import '../../../../shared/ui/common_widgets.dart';
 import '../flutter_frames/flutter_frame_model.dart';
 import 'rebuild_stats_model.dart';
 
@@ -107,8 +107,9 @@
                 ),
                 Expanded(
                   child: Padding(
-                    padding:
-                        const EdgeInsets.symmetric(horizontal: denseSpacing),
+                    padding: const EdgeInsets.symmetric(
+                      horizontal: denseSpacing,
+                    ),
                     child: ServiceExtensionCheckbox(
                       serviceExtension: extensions.countWidgetBuilds,
                     ),
@@ -121,10 +122,11 @@
         Expanded(
           child: ValueListenableBuilder<ServiceExtensionState>(
             valueListenable: serviceConnection
-                .serviceManager.serviceExtensionManager
+                .serviceManager
+                .serviceExtensionManager
                 .getServiceExtensionState(
-              extensions.countWidgetBuilds.extension,
-            ),
+                  extensions.countWidgetBuilds.extension,
+                ),
             builder: (context, state, _) {
               if (metrics.isEmpty && !state.enabled) {
                 return const Center(
@@ -135,9 +137,7 @@
               }
               if (metrics.isEmpty) {
                 return const Center(
-                  child: Text(
-                    'Interact with the app to trigger rebuilds.',
-                  ),
+                  child: Text('Interact with the app to trigger rebuilds.'),
                 ); // No data to display but there should be data soon.
               }
               return RebuildTable(
@@ -196,8 +196,11 @@
   static final _widgetColumn = _WidgetColumn();
   static final _locationColumn = _LocationColumn();
 
-  List<ColumnData<RebuildLocationStats>> get _columns =>
-      [_widgetColumn, ..._metricsColumns, _locationColumn];
+  List<ColumnData<RebuildLocationStats>> get _columns => [
+    _widgetColumn,
+    ..._metricsColumns,
+    _locationColumn,
+  ];
 
   @override
   Widget build(BuildContext context) {
@@ -205,8 +208,9 @@
       dataKey: 'RebuildMetricsTable',
       columns: _columns,
       data: widget.metrics,
-      keyFactory: (RebuildLocationStats location) =>
-          ValueKey<String?>('${location.location.id}'),
+      keyFactory:
+          (RebuildLocationStats location) =>
+              ValueKey<String?>('${location.location.id}'),
       defaultSortColumn: _metricsColumns.first,
       defaultSortDirection: sortDirection,
       onItemSelected: (item) async {
@@ -226,11 +230,7 @@
 }
 
 class _WidgetColumn extends ColumnData<RebuildLocationStats> {
-  _WidgetColumn()
-      : super(
-          'Widget',
-          fixedWidthPx: scaleByFontFactor(200),
-        );
+  _WidgetColumn() : super('Widget', fixedWidthPx: scaleByFontFactor(200));
 
   @override
   String getValue(RebuildLocationStats dataObject) {
@@ -263,9 +263,7 @@
 
 class _RebuildCountColumn extends ColumnData<RebuildLocationStats> {
   _RebuildCountColumn(super.name, this.metricIndex)
-      : super(
-          fixedWidthPx: scaleByFontFactor(130),
-        );
+    : super(fixedWidthPx: scaleByFontFactor(130));
 
   final int metricIndex;
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart
index 74c1f23..66cecad 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/rebuild_stats/rebuild_stats_model.dart
@@ -307,7 +307,7 @@
 /// the last route change.
 class RebuildLocationStats {
   RebuildLocationStats(this.location, {required int numStats})
-      : buildCounts = List<int>.filled(numStats, 0);
+    : buildCounts = List<int>.filled(numStats, 0);
   final Location location;
   final List<int> buildCounts;
 }
@@ -321,14 +321,12 @@
     final statsForIndex = rebuildStats[i];
     for (final entry in statsForIndex) {
       output
-          .putIfAbsent(
-            entry.location,
-            () => RebuildLocationStats(
-              entry.location,
-              numStats: numStats,
-            ),
-          )
-          .buildCounts[i] = entry.buildCount;
+              .putIfAbsent(
+                entry.location,
+                () => RebuildLocationStats(entry.location, numStats: numStats),
+              )
+              .buildCounts[i] =
+          entry.buildCount;
     }
   }
   return output.values.toList(growable: false);
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
index 52bed2d..a857e2a 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
@@ -123,8 +123,9 @@
       origin: window.location.origin,
       path: window.location.pathname,
     );
-    final indexFilePath = ui_web.assetManager
-        .getAssetUrl(devToolsEnvironmentParameters.perfettoIndexLocation);
+    final indexFilePath = ui_web.assetManager.getAssetUrl(
+      devToolsEnvironmentParameters.perfettoIndexLocation,
+    );
     final baseUrl = '$basePath/$indexFilePath';
     return '$baseUrl$_embeddedModeQuery';
   }
@@ -164,12 +165,12 @@
     );
     _initialized = true;
 
-    _perfettoIFrame = HTMLIFrameElement()
-      // This url is safe because we built it ourselves and it does not include
-      // any user input.
-      // ignore: unsafe_html
-      ..src = perfettoUrl
-      ..allow = 'usb';
+    _perfettoIFrame =
+        HTMLIFrameElement()
+          // This url is safe because we built it ourselves and it does not include
+          // any user input.
+          ..src = perfettoUrl
+          ..allow = 'usb';
     _perfettoIFrame.style
       ..border = 'none'
       ..height = '100%'
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_desktop.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_desktop.dart
index 175990a..41deb69 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_desktop.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_desktop.dart
@@ -7,10 +7,7 @@
 import 'perfetto_controller.dart';
 
 class Perfetto extends StatelessWidget {
-  const Perfetto({
-    super.key,
-    required this.perfettoController,
-  });
+  const Perfetto({super.key, required this.perfettoController});
 
   final PerfettoController perfettoController;
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
index 9d60603..984cfde 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
@@ -18,7 +18,7 @@
 import '../../../../../shared/analytics/constants.dart' as gac;
 import '../../../../../shared/globals.dart';
 import '../../../../../shared/primitives/utils.dart';
-import '../../../../../shared/utils.dart';
+import '../../../../../shared/utils/utils.dart';
 import '../../../performance_utils.dart';
 import '_perfetto_controller_web.dart';
 import 'perfetto_controller.dart';
@@ -26,10 +26,7 @@
 final _log = Logger('PerfettoWeb');
 
 class Perfetto extends StatefulWidget {
-  const Perfetto({
-    super.key,
-    required this.perfettoController,
-  });
+  const Perfetto({super.key, required this.perfettoController});
 
   final PerfettoController perfettoController;
 
@@ -83,9 +80,7 @@
   Widget build(BuildContext context) {
     return Container(
       color: Theme.of(context).scaffoldBackgroundColor,
-      child: HtmlElementView(
-        viewType: _perfettoController.viewId,
-      ),
+      child: HtmlElementView(viewType: _perfettoController.viewId),
     );
   }
 
@@ -237,9 +232,7 @@
     _postMessageWithId(
       EmbeddedPerfettoEvent.devtoolsThemeChange.event,
       perfettoIgnore: true,
-      args: {
-        'theme': darkMode ? 'dark' : 'light',
-      },
+      args: {'theme': darkMode ? 'dark' : 'light'},
     );
   }
 
@@ -309,12 +302,14 @@
 
   Future<void> _pingPerfettoUntilReady() async {
     if (!_perfettoHandlerReady.isCompleted) {
-      _pollForPerfettoHandlerReady =
-          Timer.periodic(const Duration(milliseconds: 200), (_) {
-        // Once the Perfetto UI is ready, Perfetto will receive this 'PING'
-        // message and return a 'PONG' message, handled in [_handleMessage].
-        _postMessage(EmbeddedPerfettoEvent.ping.event);
-      });
+      _pollForPerfettoHandlerReady = Timer.periodic(
+        const Duration(milliseconds: 200),
+        (_) {
+          // Once the Perfetto UI is ready, Perfetto will receive this 'PING'
+          // message and return a 'PONG' message, handled in [_handleMessage].
+          _postMessage(EmbeddedPerfettoEvent.ping.event);
+        },
+      );
 
       await _perfettoHandlerReady.future.timeout(
         _pollUntilReadyTimeout,
@@ -326,16 +321,18 @@
 
   Future<void> _pingDevToolsThemeHandlerUntilReady() async {
     if (!_devtoolsThemeHandlerReady.isCompleted) {
-      _pollForThemeHandlerReady =
-          Timer.periodic(const Duration(milliseconds: 200), (_) {
-        // Once [devtools_theme_handler.js] is ready, it will receive this
-        // 'PING-DEVTOOLS-THEME' message and return a 'PONG-DEVTOOLS-THEME'
-        // message, handled in [_handleMessage].
-        _postMessageWithId(
-          EmbeddedPerfettoEvent.devtoolsThemePing.event,
-          perfettoIgnore: true,
-        );
-      });
+      _pollForThemeHandlerReady = Timer.periodic(
+        const Duration(milliseconds: 200),
+        (_) {
+          // Once [devtools_theme_handler.js] is ready, it will receive this
+          // 'PING-DEVTOOLS-THEME' message and return a 'PONG-DEVTOOLS-THEME'
+          // message, handled in [_handleMessage].
+          _postMessageWithId(
+            EmbeddedPerfettoEvent.devtoolsThemePing.event,
+            perfettoIgnore: true,
+          );
+        },
+      );
 
       await _devtoolsThemeHandlerReady.future.timeout(
         _pollUntilReadyTimeout,
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/perfetto.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/perfetto.dart
index c178981..1ae376f 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/perfetto.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/perfetto.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 
 import '../../../../../shared/analytics/constants.dart' as gac;
-import '../../../../../shared/common_widgets.dart';
+import '../../../../../shared/ui/common_widgets.dart';
 import '_perfetto_desktop.dart'
     if (dart.library.js_interop) '_perfetto_web.dart';
 import 'perfetto_controller.dart';
@@ -17,9 +17,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return Perfetto(
-      perfettoController: perfettoController,
-    );
+    return Perfetto(perfettoController: perfettoController);
   }
 }
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/tracing/model.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/tracing/model.dart
index 60ae5ee..8b71bda 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/tracing/model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/tracing/model.dart
@@ -38,9 +38,10 @@
 
   final TrackDescriptor trackDescriptor;
 
-  String get name => trackDescriptor.name.isNotEmpty
-      ? trackDescriptor.name
-      : trackDescriptor.thread.threadName;
+  String get name =>
+      trackDescriptor.name.isNotEmpty
+          ? trackDescriptor.name
+          : trackDescriptor.thread.threadName;
 
   Int64 get id => trackDescriptor.uuid;
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_event_processor.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_event_processor.dart
index 786dad6..c53e438 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_event_processor.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_event_processor.dart
@@ -133,7 +133,8 @@
   TimelineEventType _inferTrackType(PerfettoTrackEvent event) {
     // Whether the UI and Raster events are expected to come on a single track.
     // This is expected when DevTools is connected to a flutter-tester device.
-    final singleTrackType = uiTrackId != null &&
+    final singleTrackType =
+        uiTrackId != null &&
         rasterTrackId != null &&
         uiTrackId == rasterTrackId;
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart
index 0638b8b..ac7c480 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart
@@ -17,10 +17,10 @@
 import '../../../../shared/analytics/constants.dart' as gac;
 import '../../../../shared/analytics/metrics.dart';
 import '../../../../shared/development_helpers.dart';
-import '../../../../shared/future_work_tracker.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/primitives/byte_utils.dart';
 import '../../../../shared/primitives/utils.dart';
+import '../../../../shared/utils/future_work_tracker.dart';
 import '../../performance_controller.dart';
 import '../../performance_model.dart';
 import '../../performance_utils.dart';
@@ -30,11 +30,7 @@
 
 final _log = Logger('timeline_events_controller');
 
-enum EventsControllerStatus {
-  empty,
-  refreshing,
-  ready,
-}
+enum EventsControllerStatus { empty, refreshing, ready }
 
 class TimelineEventsController extends PerformanceFeatureController
     with AutoDisposeControllerMixin {
@@ -110,8 +106,9 @@
 
   /// Whether the recorded timeline data is currently being processed.
   ValueListenable<EventsControllerStatus> get status => _status;
-  final _status =
-      ValueNotifier<EventsControllerStatus>(EventsControllerStatus.empty);
+  final _status = ValueNotifier<EventsControllerStatus>(
+    EventsControllerStatus.empty,
+  );
 
   final _refreshWorkTracker = FutureWorkTracker();
 
@@ -184,40 +181,33 @@
 
     late PerfettoTimeline rawPerfettoTimeline;
     if (preferences.performance.includeCpuSamplesInTimeline.value) {
-      await debugTimeAsync(
-        () async {
-          await ga.timeAsync(
-            gac.performance,
-            gac.PerformanceEvents.getPerfettoVMTimelineWithCpuSamplesTime.name,
-            asyncOperation: () async {
-              rawPerfettoTimeline =
-                  await service.getPerfettoVMTimelineWithCpuSamplesWrapper(
-                timeOriginMicros: _nextPollStartMicros,
-                timeExtentMicros:
-                    currentVmTime.timestamp! - _nextPollStartMicros,
-              );
-            },
-          );
-        },
-        debugName: 'VmService.getPerfettoVMTimelineWithCpuSamples',
-      );
+      await debugTimeAsync(() async {
+        await ga.timeAsync(
+          gac.performance,
+          gac.PerformanceEvents.getPerfettoVMTimelineWithCpuSamplesTime.name,
+          asyncOperation: () async {
+            rawPerfettoTimeline = await service
+                .getPerfettoVMTimelineWithCpuSamplesWrapper(
+                  timeOriginMicros: _nextPollStartMicros,
+                  timeExtentMicros:
+                      currentVmTime.timestamp! - _nextPollStartMicros,
+                );
+          },
+        );
+      }, debugName: 'VmService.getPerfettoVMTimelineWithCpuSamples');
     } else {
-      await debugTimeAsync(
-        () async {
-          await ga.timeAsync(
-            gac.performance,
-            gac.PerformanceEvents.getPerfettoVMTimelineTime.name,
-            asyncOperation: () async {
-              rawPerfettoTimeline = await service.getPerfettoVMTimeline(
-                timeOriginMicros: _nextPollStartMicros,
-                timeExtentMicros:
-                    currentVmTime.timestamp! - _nextPollStartMicros,
-              );
-            },
-          );
-        },
-        debugName: 'VmService.getPerfettoVMTimeline',
-      );
+      await debugTimeAsync(() async {
+        await ga.timeAsync(
+          gac.performance,
+          gac.PerformanceEvents.getPerfettoVMTimelineTime.name,
+          asyncOperation: () async {
+            rawPerfettoTimeline = await service.getPerfettoVMTimeline(
+              timeOriginMicros: _nextPollStartMicros,
+              timeExtentMicros: currentVmTime.timestamp! - _nextPollStartMicros,
+            );
+          },
+        );
+      }, debugName: 'VmService.getPerfettoVMTimeline');
     }
     _nextPollStartMicros = currentVmTime.timestamp! + 1;
 
@@ -241,8 +231,9 @@
   }) {
     if (!_isFlutterAppHelper()) {
       debugTraceCallback(
-        () => _log
-            .info('[_prepareTraceForProcessing] not a flutter app, returning.'),
+        () => _log.info(
+          '[_prepareTraceForProcessing] not a flutter app, returning.',
+        ),
       );
       return;
     }
@@ -251,8 +242,9 @@
     final newTrackDescriptors = <PerfettoTrackDescriptorEvent>[];
     for (final packet in trace.packet) {
       if (packet.hasTrackDescriptor()) {
-        final trackDescriptor =
-            PerfettoTrackDescriptorEvent(packet.trackDescriptor);
+        final trackDescriptor = PerfettoTrackDescriptorEvent(
+          packet.trackDescriptor,
+        );
         final added = _trackDescriptors.add(trackDescriptor);
         if (added) {
           newTrackDescriptors.add(trackDescriptor);
@@ -367,10 +359,12 @@
     ga.timeSync(
       gac.performance,
       gac.PerformanceEvents.perfettoModeTraceEventProcessingTime.nameOverride!,
-      syncOperation: () => perfettoController.processor
-          .processTrackEvents(_unprocessedTrackEvents),
-      screenMetricsProvider: () =>
-          PerformanceScreenMetrics(traceEventCount: eventCount),
+      syncOperation:
+          () => perfettoController.processor.processTrackEvents(
+            _unprocessedTrackEvents,
+          ),
+      screenMetricsProvider:
+          () => PerformanceScreenMetrics(traceEventCount: eventCount),
     );
     _unprocessedTrackEvents.clear();
   }
@@ -388,11 +382,11 @@
     void processMoreEventsOrExitHelper({
       required FutureOr<void> Function() onProcessMore,
     }) async {
-      final hasProcessedTimelineEventsForFrame =
-          perfettoController.processor.hasProcessedEventsForFrame(frame.id);
+      final hasProcessedTimelineEventsForFrame = perfettoController.processor
+          .hasProcessedEventsForFrame(frame.id);
       if (!hasProcessedTimelineEventsForFrame) {
-        final timelineEventsUnavailable =
-            perfettoController.processor.frameIsBeforeTimelineData(frame.id);
+        final timelineEventsUnavailable = perfettoController.processor
+            .frameIsBeforeTimelineData(frame.id);
         if (timelineEventsUnavailable) {
           _maybePushNoTimelineEventsWarning();
           return;
@@ -429,8 +423,9 @@
           );
           await forceRefresh();
 
-          final hasProcessedTimelineEventsForFrame =
-              perfettoController.processor.hasProcessedEventsForFrame(frame.id);
+          final hasProcessedTimelineEventsForFrame = perfettoController
+              .processor
+              .hasProcessedEventsForFrame(frame.id);
           if (!hasProcessedTimelineEventsForFrame) {
             // At this point, we still have not processed any timeline events
             // for this Flutter frame, which means we will never have access to
@@ -465,11 +460,8 @@
           event,
         );
       } else {
-        final unassignedEventsForFrame =
-            _unassignedFlutterTimelineEvents.putIfAbsent(
-          frameNumber,
-          () => FrameTimelineEventData(),
-        );
+        final unassignedEventsForFrame = _unassignedFlutterTimelineEvents
+            .putIfAbsent(frameNumber, () => FrameTimelineEventData());
         unassignedEventsForFrame.setEventFlow(event: event, setTimeData: false);
       }
     }
@@ -516,8 +508,9 @@
     await loadPerfettoTrace();
 
     if (offlineData.selectedFrame != null) {
-      perfettoController
-          .scrollToTimeRange(offlineData.selectedFrame!.timeFromFrameTiming);
+      perfettoController.scrollToTimeRange(
+        offlineData.selectedFrame!.timeFromFrameTiming,
+      );
     }
   }
 
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_view.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_view.dart
index 8a79bef..3901837 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_view.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_view.dart
@@ -10,9 +10,9 @@
 import 'package:flutter/material.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
 import '../../../../shared/globals.dart';
 import '../../../../shared/http/http_service.dart' as http_service;
+import '../../../../shared/ui/common_widgets.dart';
 import 'perfetto/perfetto.dart';
 import 'timeline_events_controller.dart';
 
@@ -191,9 +191,10 @@
         return RefreshButton(
           iconOnly: true,
           outlined: false,
-          onPressed: status == EventsControllerStatus.refreshing
-              ? null
-              : controller.forceRefresh,
+          onPressed:
+              status == EventsControllerStatus.refreshing
+                  ? null
+                  : controller.forceRefresh,
           tooltip: 'Refresh timeline events',
           gaScreen: gac.performance,
           gaSelection: gac.PerformanceEvents.refreshTimelineEvents.name,
@@ -251,9 +252,7 @@
           ],
         ),
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 
@@ -267,12 +266,7 @@
       ),
       const SizedBox(height: defaultSpacing),
       ...dialogSubHeader(theme, 'Trace categories'),
-      RichText(
-        text: TextSpan(
-          text: 'Default',
-          style: theme.subtleTextStyle,
-        ),
-      ),
+      RichText(text: TextSpan(text: 'Default', style: theme.subtleTextStyle)),
       ..._timelineStreams(advanced: false),
       // Special case "Network Traffic" because it is not implemented as a
       // Timeline recorded stream in the VM. The user does not need to be aware of
@@ -281,46 +275,41 @@
         title: 'Network',
         description: 'Http traffic',
         notifier: _httpLogging,
-        onChanged: (value) => unawaited(
-          http_service.toggleHttpRequestLogging(value ?? false),
-        ),
+        onChanged:
+            (value) => unawaited(
+              http_service.toggleHttpRequestLogging(value ?? false),
+            ),
       ),
     ];
   }
 
   List<Widget> _advancedStreams(ThemeData theme) {
     return [
-      RichText(
-        text: TextSpan(
-          text: 'Advanced',
-          style: theme.subtleTextStyle,
-        ),
-      ),
+      RichText(text: TextSpan(text: 'Advanced', style: theme.subtleTextStyle)),
       ..._timelineStreams(advanced: true),
     ];
   }
 
-  List<Widget> _timelineStreams({
-    required bool advanced,
-  }) {
-    final streams = advanced
-        ? serviceConnection.timelineStreamManager.advancedStreams
-        : serviceConnection.timelineStreamManager.basicStreams;
-    final settings = streams
-        .map(
-          (stream) => CheckboxSetting(
-            title: stream.name,
-            description: stream.description,
-            notifier: stream.recorded as ValueNotifier<bool?>,
-            onChanged: (newValue) => unawaited(
-              serviceConnection.timelineStreamManager.updateTimelineStream(
-                stream,
-                newValue ?? false,
+  List<Widget> _timelineStreams({required bool advanced}) {
+    final streams =
+        advanced
+            ? serviceConnection.timelineStreamManager.advancedStreams
+            : serviceConnection.timelineStreamManager.basicStreams;
+    final settings =
+        streams
+            .map(
+              (stream) => CheckboxSetting(
+                title: stream.name,
+                description: stream.description,
+                notifier: stream.recorded as ValueNotifier<bool?>,
+                onChanged:
+                    (newValue) => unawaited(
+                      serviceConnection.timelineStreamManager
+                          .updateTimelineStream(stream, newValue ?? false),
+                    ),
               ),
-            ),
-          ),
-        )
-        .toList();
+            )
+            .toList();
     return settings;
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/performance/performance_controller.dart b/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
index c2935f5..c51887e 100644
--- a/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
+++ b/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
@@ -12,7 +12,7 @@
 import '../../shared/diagnostics/inspector_service.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/globals.dart';
-import '../../shared/offline_data.dart';
+import '../../shared/offline/offline_data.dart';
 import 'panes/controls/enhance_tracing/enhance_tracing_controller.dart';
 import 'panes/flutter_frames/flutter_frame_model.dart';
 import 'panes/flutter_frames/flutter_frames_controller.dart';
@@ -106,9 +106,7 @@
       if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ??
           false) {
         final impellerEnabledResponse = await serviceConnection.serviceManager
-            .callServiceExtensionOnMainIsolate(
-          registrations.isImpellerEnabled,
-        );
+            .callServiceExtensionOnMainIsolate(registrations.isImpellerEnabled);
         _impellerEnabled = impellerEnabledResponse.json?['enabled'] == true;
       } else {
         _impellerEnabled = false;
@@ -120,27 +118,31 @@
       // Listen for Flutter.RebuiltWidgets events.
       autoDisposeStreamSubscription(
         serviceConnection
-            .serviceManager.service!.onExtensionEventWithHistorySafe
+            .serviceManager
+            .service!
+            .onExtensionEventWithHistorySafe
             .listen((event) {
-          if (event.extensionKind == FlutterEvent.frame) {
-            final frame = FlutterFrame.fromJson(event.extensionData!.data);
-            enhanceTracingController.assignStateForFrame(frame);
-            flutterFramesController.addFrame(frame);
-          } else if (event.extensionKind == FlutterEvent.rebuiltWidgets &&
-              FeatureFlags.widgetRebuildStats) {
-            if (_currentRebuildWidgetsIsolate != event.isolate) {
-              rebuildCountModel.clearFromRestart();
-            }
-            _currentRebuildWidgetsIsolate = event.isolate;
-            // TODO(jacobr): need to make sure we don't get events from before
-            // the last hot restart. Their data would be bogus.
-            rebuildCountModel.processRebuildEvent(event.extensionData!.data);
-            if (!rebuildCountModel.locationMap.locationsResolved.value &&
-                !_fetchMissingLocationsStarted) {
-              _fetchMissingRebuildLocations();
-            }
-          }
-        }),
+              if (event.extensionKind == FlutterEvent.frame) {
+                final frame = FlutterFrame.fromJson(event.extensionData!.data);
+                enhanceTracingController.assignStateForFrame(frame);
+                flutterFramesController.addFrame(frame);
+              } else if (event.extensionKind == FlutterEvent.rebuiltWidgets &&
+                  FeatureFlags.widgetRebuildStats) {
+                if (_currentRebuildWidgetsIsolate != event.isolate) {
+                  rebuildCountModel.clearFromRestart();
+                }
+                _currentRebuildWidgetsIsolate = event.isolate;
+                // TODO(jacobr): need to make sure we don't get events from before
+                // the last hot restart. Their data would be bogus.
+                rebuildCountModel.processRebuildEvent(
+                  event.extensionData!.data,
+                );
+                if (!rebuildCountModel.locationMap.locationsResolved.value &&
+                    !_fetchMissingLocationsStarted) {
+                  _fetchMissingRebuildLocations();
+                }
+              }
+            }),
       );
     } else {
       await maybeLoadOfflineData(
@@ -245,15 +247,16 @@
 
   @override
   OfflineScreenData prepareOfflineScreenData() => OfflineScreenData(
-        screenId: PerformanceScreen.id,
-        data: OfflinePerformanceData(
+    screenId: PerformanceScreen.id,
+    data:
+        OfflinePerformanceData(
           perfettoTraceBinary: timelineEventsController.fullPerfettoTrace,
           frames: flutterFramesController.flutterFrames.value,
           selectedFrame: flutterFramesController.selectedFrame.value,
           rebuildCountModel: rebuildCountModel,
           displayRefreshRate: flutterFramesController.displayRefreshRate.value,
         ).toJson(),
-      );
+  );
 }
 
 abstract class PerformanceFeatureController extends DisposableController {
diff --git a/packages/devtools_app/lib/src/screens/performance/performance_model.dart b/packages/devtools_app/lib/src/screens/performance/performance_model.dart
index 80747e0..8747db6 100644
--- a/packages/devtools_app/lib/src/screens/performance/performance_model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/performance_model.dart
@@ -26,8 +26,9 @@
 
     final selectedFrameId = json.selectedFrameId;
     final frames = json.frames;
-    final selectedFrame =
-        frames.firstWhereOrNull((frame) => frame.id == selectedFrameId);
+    final selectedFrame = frames.firstWhereOrNull(
+      (frame) => frame.id == selectedFrameId,
+    );
 
     return OfflinePerformanceData(
       perfettoTraceBinary: json.traceBinary,
@@ -58,12 +59,12 @@
   bool get isEmpty => perfettoTraceBinary == null;
 
   Map<String, Object?> toJson() => {
-        traceBinaryKey: perfettoTraceBinary,
-        flutterFramesKey: frames.map((frame) => frame.json).toList(),
-        selectedFrameIdKey: selectedFrame?.id,
-        displayRefreshRateKey: displayRefreshRate,
-        rebuildCountModelKey: rebuildCountModel?.toJson(),
-      };
+    traceBinaryKey: perfettoTraceBinary,
+    flutterFramesKey: frames.map((frame) => frame.json).toList(),
+    selectedFrameIdKey: selectedFrame?.id,
+    displayRefreshRateKey: displayRefreshRate,
+    rebuildCountModelKey: rebuildCountModel?.toJson(),
+  };
 }
 
 extension type _PerformanceDataJson(Map<String, Object?> json) {
@@ -98,8 +99,8 @@
 
 class FlutterTimelineEvent extends TreeNode<FlutterTimelineEvent> {
   FlutterTimelineEvent(PerfettoTrackEvent firstTrackEvent)
-      : trackEvents = [firstTrackEvent],
-        type = firstTrackEvent.timelineEventType {
+    : trackEvents = [firstTrackEvent],
+      type = firstTrackEvent.timelineEventType {
     time.start = Duration(microseconds: firstTrackEvent.timestampMicros);
   }
 
@@ -137,9 +138,10 @@
     }
     copy
       ..type = type
-      ..time = (TimeRange()
-        ..start = time.start
-        ..end = time.end);
+      ..time =
+          (TimeRange()
+            ..start = time.start
+            ..end = time.end);
     return copy;
   }
 
@@ -180,8 +182,4 @@
   }
 }
 
-enum TimelineEventType {
-  ui,
-  raster,
-  other,
-}
+enum TimelineEventType { ui, raster, other }
diff --git a/packages/devtools_app/lib/src/screens/performance/performance_screen.dart b/packages/devtools_app/lib/src/screens/performance/performance_screen.dart
index 98a119b..871bce7 100644
--- a/packages/devtools_app/lib/src/screens/performance/performance_screen.dart
+++ b/packages/devtools_app/lib/src/screens/performance/performance_screen.dart
@@ -12,13 +12,13 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/banner_messages.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/config_specific/import_export/import_export.dart';
-import '../../shared/file_import.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/managers/banner_messages.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/file_import.dart';
+import '../../shared/utils/utils.dart';
 import 'panes/controls/performance_controls.dart';
 import 'panes/flutter_frames/flutter_frames_chart.dart';
 import 'performance_controller.dart';
@@ -94,7 +94,8 @@
 
         final showingOfflineData =
             offlineDataController.showingOfflineData.value;
-        final isOfflineFlutterApp = showingOfflineData &&
+        final isOfflineFlutterApp =
+            showingOfflineData &&
             controller.offlinePerformanceData != null &&
             controller.offlinePerformanceData!.frames.isNotEmpty;
         return Column(
@@ -107,7 +108,9 @@
             if (isOfflineFlutterApp ||
                 (!showingOfflineData &&
                     serviceConnection
-                        .serviceManager.connectedApp!.isFlutterAppNow!))
+                        .serviceManager
+                        .connectedApp!
+                        .isFlutterAppNow!))
               FlutterFramesChart(
                 controller.flutterFramesController,
                 showingOfflineData: showingOfflineData,
@@ -128,10 +131,10 @@
   Widget build(BuildContext context) {
     final isFlutterWebApp =
         serviceConnection.serviceManager.connectedApp?.isFlutterWebAppNow ??
-            false;
+        false;
     return Markdown(
       data: isFlutterWebApp ? flutterWebInstructionsMd : dartWebInstructionsMd,
-      onTapLink: (_, url, __) {
+      onTapLink: (_, url, _) {
         if (url != null) {
           unawaited(launchUrlWithErrorHandling(url));
         }
@@ -155,8 +158,10 @@
       gaSelectionImport: gac.PerformanceEvents.openDataFile.name,
       gaSelectionAction: gac.PerformanceEvents.loadDataFromFile.name,
       onAction: (jsonFile) {
-        Provider.of<ImportController>(context, listen: false)
-            .importData(jsonFile, expectedScreenId: PerformanceScreen.id);
+        Provider.of<ImportController>(
+          context,
+          listen: false,
+        ).importData(jsonFile, expectedScreenId: PerformanceScreen.id);
       },
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart b/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
index 6adb48a..531527f 100644
--- a/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
+++ b/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
@@ -9,11 +9,11 @@
 import 'package:flutter/material.dart';
 
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/feature_flags.dart';
 import '../../shared/globals.dart';
+import '../../shared/ui/common_widgets.dart';
 import '../../shared/ui/tab.dart';
-import '../../shared/utils.dart';
+import '../../shared/utils/utils.dart';
 import 'panes/flutter_frames/flutter_frame_model.dart';
 import 'panes/flutter_frames/flutter_frames_controller.dart';
 import 'panes/frame_analysis/frame_analysis.dart';
@@ -68,7 +68,8 @@
       final hasOfflineData = offlineData != null;
       showFrameAnalysis =
           showFrameAnalysis && hasOfflineData && offlineData.frames.isNotEmpty;
-      showRebuildStats = showRebuildStats &&
+      showRebuildStats =
+          showRebuildStats &&
           hasOfflineData &&
           offlineData.rebuildCountModel != null;
     }
@@ -77,9 +78,10 @@
       showFrameAnalysis: showFrameAnalysis,
       showRebuildStats: showRebuildStats,
     );
-    final tabs = tabsAndControllers
-        .map((t) => (tab: t.tab, tabView: t.tabView))
-        .toList();
+    final tabs =
+        tabsAndControllers
+            .map((t) => (tab: t.tab, tabView: t.tabView))
+            .toList();
     final featureControllers =
         tabsAndControllers.map((t) => t.featureController).toList();
 
@@ -110,11 +112,13 @@
   }
 
   List<
-      ({
-        DevToolsTab tab,
-        Widget tabView,
-        PerformanceFeatureController? featureController,
-      })> _generateTabs({
+    ({
+      DevToolsTab tab,
+      Widget tabView,
+      PerformanceFeatureController? featureController,
+    })
+  >
+  _generateTabs({
     required bool showFrameAnalysis,
     required bool showRebuildStats,
   }) {
@@ -126,18 +130,19 @@
         (
           tab: _buildTab(tabName: 'Frame Analysis'),
           tabView: KeepAliveWrapper(
-            child: _selectedFlutterFrame != null
-                ? FlutterFrameAnalysisView(
-                    frame: _selectedFlutterFrame!,
-                    enhanceTracingController:
-                        controller.enhanceTracingController,
-                    rebuildCountModel: controller.rebuildCountModel,
-                    displayRefreshRateNotifier:
-                        controller.flutterFramesController.displayRefreshRate,
-                  )
-                : const CenteredMessage(
-                    message: 'Select a frame above to view analysis data.',
-                  ),
+            child:
+                _selectedFlutterFrame != null
+                    ? FlutterFrameAnalysisView(
+                      frame: _selectedFlutterFrame!,
+                      enhanceTracingController:
+                          controller.enhanceTracingController,
+                      rebuildCountModel: controller.rebuildCountModel,
+                      displayRefreshRateNotifier:
+                          controller.flutterFramesController.displayRefreshRate,
+                    )
+                    : const CenteredMessage(
+                      message: 'Select a frame above to view analysis data.',
+                    ),
           ),
           featureController: null,
         ),
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
index 7c4b3b5..93ca122 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
@@ -13,8 +13,8 @@
 import '../../shared/primitives/trace_event.dart';
 import '../../shared/primitives/trees.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/profiler_utils.dart';
 import '../../shared/ui/search.dart';
+import '../../shared/utils/profiler_utils.dart';
 import '../vm_developer/vm_service_private_extensions.dart';
 import 'cpu_profile_transformer.dart';
 import 'cpu_profiler_controller.dart';
@@ -53,8 +53,10 @@
     CpuProfilePair original,
     bool Function(CpuStackFrame) callback,
   ) {
-    final function =
-        CpuProfileData.filterFrom(original.functionProfile, callback);
+    final function = CpuProfileData.filterFrom(
+      original.functionProfile,
+      callback,
+    );
     CpuProfileData? code;
     if (original.codeProfile != null) {
       code = CpuProfileData.filterFrom(original.codeProfile!, callback);
@@ -70,8 +72,10 @@
     CpuProfilePair original,
     TimeRange subTimeRange,
   ) {
-    final function =
-        CpuProfileData.subProfile(original.functionProfile, subTimeRange);
+    final function = CpuProfileData.subProfile(
+      original.functionProfile,
+      subTimeRange,
+    );
     CpuProfileData? code;
     if (original.codeProfile != null) {
       code = CpuProfileData.subProfile(original.codeProfile!, subTimeRange);
@@ -90,10 +94,7 @@
     final codeProfile = original.codeProfile;
     CpuProfileData? code;
     if (codeProfile != null) {
-      code = CpuProfileData.withTagRoots(
-        codeProfile,
-        tagType,
-      );
+      code = CpuProfileData.withTagRoots(codeProfile, tagType);
     }
     return CpuProfilePair(functionProfile: function, codeProfile: code);
   }
@@ -173,13 +174,14 @@
       sampleCount: json.sampleCount ?? 0,
       samplePeriod: json.samplePeriod ?? 0,
       stackDepth: json.stackDepth ?? 0,
-      time: (json.timeOriginMicros != null && json.timeExtentMicros != null)
-          ? (TimeRange()
-            ..start = Duration(microseconds: json.timeOriginMicros!)
-            ..end = Duration(
-              microseconds: json.timeOriginMicros! + json.timeExtentMicros!,
-            ))
-          : null,
+      time:
+          (json.timeOriginMicros != null && json.timeExtentMicros != null)
+              ? (TimeRange()
+                ..start = Duration(microseconds: json.timeOriginMicros!)
+                ..end = Duration(
+                  microseconds: json.timeOriginMicros! + json.timeExtentMicros!,
+                ))
+              : null,
     );
 
     // Initialize all stack frames.
@@ -226,12 +228,14 @@
   ) {
     // Each sample in [subSamples] will have the leaf stack
     // frame id for a cpu sample within [subTimeRange].
-    final subSamples = superProfile.cpuSamples
-        .where(
-          (sample) => subTimeRange
-              .contains(Duration(microseconds: sample.timestampMicros!)),
-        )
-        .toList();
+    final subSamples =
+        superProfile.cpuSamples
+            .where(
+              (sample) => subTimeRange.contains(
+                Duration(microseconds: sample.timestampMicros!),
+              ),
+            )
+            .toList();
 
     final subStackFrames = <String, CpuStackFrame>{};
     for (final sample in subSamples) {
@@ -306,9 +310,7 @@
         profileMetaData: metaData,
         isTag: true,
       );
-      final idMapping = <String, String>{
-        rootId: tagId,
-      };
+      final idMapping = <String, String>{rootId: tagId};
 
       tagProfile.stackFrames.forEach((k, v) {
         idMapping.putIfAbsent(k, () => '$isolateId-${nextId++}');
@@ -333,9 +335,10 @@
             profileMetaData: metaData,
             parentId: parentId,
           );
-          final parentStackFrameJson = parentId != null
-              ? originalData.stackFrames[currentStackFrame.parentId]
-              : null;
+          final parentStackFrameJson =
+              parentId != null
+                  ? originalData.stackFrames[currentStackFrame.parentId]
+                  : null;
           updatedId = parentId;
           currentStackFrame = parentStackFrameJson;
         }
@@ -368,9 +371,12 @@
       return CpuProfileData.empty();
     }
 
-    final samplesWithTag = originalData.cpuSamples
-        .where((sample) => (useUserTag ? sample.userTag : sample.vmTag) == tag)
-        .toList();
+    final samplesWithTag =
+        originalData.cpuSamples
+            .where(
+              (sample) => (useUserTag ? sample.userTag : sample.vmTag) == tag,
+            )
+            .toList();
     assert(samplesWithTag.isNotEmpty);
 
     final originalTime = originalData.profileMetaData.time!.duration;
@@ -383,13 +389,15 @@
       // for this profile data, and the samples included in this data could be
       // sparse over the original profile's time range, so true start and end
       // times wouldn't be helpful.
-      time: TimeRange()
-        ..start = const Duration()
-        ..end = Duration(
-          microseconds: microsPerSample.isInfinite
-              ? 0
-              : (newSampleCount * microsPerSample).round(),
-        ),
+      time:
+          TimeRange()
+            ..start = const Duration()
+            ..end = Duration(
+              microseconds:
+                  microsPerSample.isInfinite
+                      ? 0
+                      : (newSampleCount * microsPerSample).round(),
+            ),
     );
 
     final stackFramesWithTag = <String, CpuStackFrame>{};
@@ -487,13 +495,15 @@
       // for this profile data, and the samples included in this data could be
       // sparse over the original profile's time range, so true start and end
       // times wouldn't be helpful.
-      time: TimeRange()
-        ..start = const Duration()
-        ..end = Duration(
-          microseconds: microsPerSample.isInfinite || microsPerSample.isNaN
-              ? 0
-              : (filteredCpuSamples.length * microsPerSample).round(),
-        ),
+      time:
+          TimeRange()
+            ..start = const Duration()
+            ..end = Duration(
+              microseconds:
+                  microsPerSample.isInfinite || microsPerSample.isNaN
+                      ? 0
+                      : (filteredCpuSamples.length * microsPerSample).round(),
+            ),
     );
 
     void walkAndFilter(CpuStackFrame stackFrame) {
@@ -734,17 +744,17 @@
 
   @override
   Map<String, Object?> toJson() => {
-        'type': '_CpuProfileTimeline',
-        _samplePeriodKey: profileMetaData.samplePeriod,
-        _sampleCountKey: profileMetaData.sampleCount,
-        _stackDepthKey: profileMetaData.stackDepth,
-        if (profileMetaData.time?.start != null)
-          _timeOriginKey: profileMetaData.time!.start!.inMicroseconds,
-        if (profileMetaData.time?.duration != null)
-          _timeExtentKey: profileMetaData.time!.duration.inMicroseconds,
-        _stackFramesKey: stackFramesJson,
-        _traceEventsKey: cpuSamples.map((sample) => sample.toJson).toList(),
-      };
+    'type': '_CpuProfileTimeline',
+    _samplePeriodKey: profileMetaData.samplePeriod,
+    _sampleCountKey: profileMetaData.sampleCount,
+    _stackDepthKey: profileMetaData.stackDepth,
+    if (profileMetaData.time?.start != null)
+      _timeOriginKey: profileMetaData.time!.start!.inMicroseconds,
+    if (profileMetaData.time?.duration != null)
+      _timeExtentKey: profileMetaData.time!.duration.inMicroseconds,
+    _stackFramesKey: stackFramesJson,
+    _traceEventsKey: cpuSamples.map((sample) => sample.toJson).toList(),
+  };
 
   bool get isEmpty => profileMetaData.sampleCount == 0;
 
@@ -939,21 +949,25 @@
   /// samples are being grouped by tag.
   final bool isTag;
 
-  bool get isNative => _isNative ??= id != CpuProfileData.rootId &&
-      packageUri.isEmpty &&
-      !name.startsWith(PackagePrefixes.flutterEngine) &&
-      !isTag;
+  bool get isNative =>
+      _isNative ??=
+          id != CpuProfileData.rootId &&
+          packageUri.isEmpty &&
+          !name.startsWith(PackagePrefixes.flutterEngine) &&
+          !isTag;
 
   bool? _isNative;
 
   bool get isDartCore =>
-      _isDartCore ??= packageUri.startsWith(PackagePrefixes.dart) &&
+      _isDartCore ??=
+          packageUri.startsWith(PackagePrefixes.dart) &&
           !packageUri.startsWith(PackagePrefixes.dartUi);
 
   bool? _isDartCore;
 
-  bool get isFlutterCore => _isFlutterCore ??=
-      packageUri.startsWith(PackagePrefixes.flutterPackage) ||
+  bool get isFlutterCore =>
+      _isFlutterCore ??=
+          packageUri.startsWith(PackagePrefixes.flutterPackage) ||
           name.startsWith(PackagePrefixes.flutterEngine) ||
           packageUri.startsWith(PackagePrefixes.dartUi);
 
@@ -1052,15 +1066,15 @@
   }
 
   Map<String, Object?> get toJson => {
-        id: {
-          CpuProfileData.nameKey: verboseName,
-          CpuProfileData.categoryKey: category,
-          CpuProfileData.resolvedUrlKey: rawUrl,
-          CpuProfileData.resolvedPackageUriKey: packageUri,
-          CpuProfileData.sourceLineKey: sourceLine,
-          if (parentId != null) CpuProfileData.parentIdKey: parentId,
-        },
-      };
+    id: {
+      CpuProfileData.nameKey: verboseName,
+      CpuProfileData.categoryKey: category,
+      CpuProfileData.resolvedUrlKey: rawUrl,
+      CpuProfileData.resolvedPackageUriKey: packageUri,
+      CpuProfileData.sourceLineKey: sourceLine,
+      if (parentId != null) CpuProfileData.parentIdKey: parentId,
+    },
+  };
 
   @override
   String toString() {
@@ -1105,10 +1119,7 @@
   /// generated, cached in [_profilesByTime] and then returned. This method will
   /// return null if no profiles are cached for [time] or if a sub profile
   /// cannot be generated for [time].
-  CpuProfilePair? lookupProfile({
-    String? label,
-    TimeRange? time,
-  }) {
+  CpuProfilePair? lookupProfile({String? label, TimeRange? time}) {
     assert((label == null) != (time == null));
 
     if (label != null) {
@@ -1124,11 +1135,7 @@
     return _profilesByTime[time];
   }
 
-  void storeProfile(
-    CpuProfilePair profile, {
-    String? label,
-    TimeRange? time,
-  }) {
+  void storeProfile(CpuProfilePair profile, {String? label, TimeRange? time}) {
     assert((label == null) != (time == null));
     if (label != null) {
       _profilesByLabel[label] = profile;
@@ -1244,13 +1251,17 @@
     return null;
   }
 
-  String? get resolvedUrl => isCodeTree && _function is vm_service.FuncRef?
-      ?
-      // TODO(bkonyi): not sure if this is a resolved URL or not, but it's not
-      // critical since this is only displayed when VM developer mode is
-      // enabled.
-      (_function as vm_service.FuncRef?)?.location?.script?.uri
-      : samples.functions![index].resolvedUrl;
+  String? get resolvedUrl =>
+      isCodeTree && _function is vm_service.FuncRef?
+          ?
+              // TODO(bkonyi): not sure if this is a resolved URL or not, but it's not
+              // critical since this is only displayed when VM developer mode is
+              // enabled.
+              (_function as vm_service.FuncRef?)
+              ?.location
+              ?.script
+              ?.uri
+          : samples.functions![index].resolvedUrl;
 
   int? get sourceLine {
     final function = _function;
@@ -1269,8 +1280,7 @@
 
   static _CpuProfileTimelineTree? getTreeFromSample(
     vm_service.CpuSample sample,
-  ) =>
-      _timelineTreeExpando[sample];
+  ) => _timelineTreeExpando[sample];
 
   _CpuProfileTimelineTree _getChild(int index) {
     final length = children.length;
@@ -1285,8 +1295,11 @@
         break;
       }
     }
-    final child =
-        _CpuProfileTimelineTree._fromIndex(samples, index, isCodeTree);
+    final child = _CpuProfileTimelineTree._fromIndex(
+      samples,
+      index,
+      isCodeTree,
+    );
     if (i < length) {
       children.insert(i, child);
     } else {
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
index 994323d..bbfed58 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
@@ -18,14 +18,15 @@
     // Grab the value of this flag before doing asynchronous work.
     final vmDeveloperModeEnabled = preferences.vmDeveloperModeEnabled.value;
 
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
-    final cpuSamples =
-        await serviceConnection.serviceManager.service!.getCpuSamples(
-      isolateId,
-      startMicros,
-      extentMicros,
-    );
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
+    final cpuSamples = await serviceConnection.serviceManager.service!
+        .getCpuSamples(isolateId, startMicros, extentMicros);
 
     // If VM developer mode is enabled, getCpuSamples will also include code
     // profile details automatically (e.g., code stacks and a list of code
@@ -75,17 +76,25 @@
   Future clearSamples() {
     return serviceConnection.serviceManager.service!.clearCpuSamples(
       serviceConnection
-          .serviceManager.isolateManager.selectedIsolate.value!.id!,
+          .serviceManager
+          .isolateManager
+          .selectedIsolate
+          .value!
+          .id!,
     );
   }
 
   Future<Response> setProfilePeriod(String value) {
-    return serviceConnection.serviceManager.service!
-        .setFlag(vm_flags.profilePeriod, value);
+    return serviceConnection.serviceManager.service!.setFlag(
+      vm_flags.profilePeriod,
+      value,
+    );
   }
 
   Future<Response> enableCpuProfiler() async {
-    return await serviceConnection.serviceManager.service!
-        .setFlag(vm_flags.profiler, 'true');
+    return await serviceConnection.serviceManager.service!.setFlag(
+      vm_flags.profiler,
+      'true',
+    );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_transformer.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_transformer.dart
index 9e4fe2b..466f70f 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_transformer.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_transformer.dart
@@ -64,9 +64,11 @@
       // Check to see if there are any empty tag roots as a result of filtering
       // and remove them.
       final nodeIndicesToRemove = <int>[];
-      for (int i = cpuProfileData.cpuProfileRoot.children.length - 1;
-          i >= 0;
-          --i) {
+      for (
+        int i = cpuProfileData.cpuProfileRoot.children.length - 1;
+        i >= 0;
+        --i
+      ) {
         final root = cpuProfileData.cpuProfileRoot.children[i];
         if (root.isTag && root.children.isEmpty) {
           nodeIndicesToRemove.add(i);
@@ -80,15 +82,13 @@
     _setExclusiveSampleCountsAndTags(cpuProfileData);
     cpuProfileData.processed = true;
 
-    // TODO(kenz): investigate why this assert is firing again.
-    // https://github.com/flutter/devtools/issues/1529.
-//    assert(
-//      cpuProfileData.profileMetaData.sampleCount ==
-//          cpuProfileData.cpuProfileRoot.inclusiveSampleCount,
-//      'SampleCount from response (${cpuProfileData.profileMetaData.sampleCount})'
-//      ' != sample count from root '
-//      '(${cpuProfileData.cpuProfileRoot.inclusiveSampleCount})',
-//    );
+    assert(
+      cpuProfileData.profileMetaData.sampleCount ==
+          cpuProfileData.cpuProfileRoot.inclusiveSampleCount,
+      'SampleCount from response (${cpuProfileData.profileMetaData.sampleCount})'
+      ' != sample count from root '
+      '(${cpuProfileData.cpuProfileRoot.inclusiveSampleCount})',
+    );
 
     // Reset the transformer after processing.
     reset();
@@ -99,8 +99,10 @@
     CpuProfileData cpuProfileData, {
     required String processId,
   }) {
-    final batchEnd =
-        math.min(_stackFramesProcessed + batchSize, _stackFramesCount);
+    final batchEnd = math.min(
+      _stackFramesProcessed + batchSize,
+      _stackFramesCount,
+    );
     for (int i = _stackFramesProcessed; i < batchEnd; i++) {
       if (processId != _activeProcessId) {
         throw ProcessCancelledException();
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profiler.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profiler.dart
index 563467f..af0e86b 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profiler.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profiler.dart
@@ -10,10 +10,11 @@
 
 import '../../shared/analytics/constants.dart' as gac;
 import '../../shared/charts/flame_chart.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/utils.dart';
 import '../../shared/ui/colors.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/filter.dart';
 import '../../shared/ui/search.dart';
 import '../../shared/ui/tab.dart';
 import 'common.dart';
@@ -34,14 +35,14 @@
     required this.data,
     required this.controller,
     List<Key>? searchableTabKeys,
-  })  : callTreeRoots = data.callTreeRoots,
-        bottomUpRoots = data.bottomUpRoots,
-        tabs = [
-          _buildTab(ProfilerTab.bottomUp),
-          _buildTab(ProfilerTab.callTree),
-          _buildTab(ProfilerTab.methodTable),
-          _buildTab(ProfilerTab.cpuFlameChart),
-        ];
+  }) : callTreeRoots = data.callTreeRoots,
+       bottomUpRoots = data.bottomUpRoots,
+       tabs = [
+         _buildTab(ProfilerTab.bottomUp),
+         _buildTab(ProfilerTab.callTree),
+         _buildTab(ProfilerTab.methodTable),
+         _buildTab(ProfilerTab.cpuFlameChart),
+       ];
 
   static DevToolsTab _buildTab(ProfilerTab profilerTab) {
     return DevToolsTab.create(
@@ -112,10 +113,7 @@
       _tabController.removeListener(_onTabChanged);
       _tabController.dispose();
     }
-    _tabController = TabController(
-      length: widget.tabs.length,
-      vsync: this,
-    );
+    _tabController = TabController(length: widget.tabs.length, vsync: this);
     _tabControllerInitialized = true;
 
     if (widget.controller.selectedProfilerTabIndex >= _tabController.length) {
@@ -188,12 +186,10 @@
               if (currentTab.key == ProfilerTab.methodTable.key)
                 SearchField<MethodTableController>(
                   searchController: widget.controller.methodTableController,
-                  containerPadding: EdgeInsets.zero,
                 )
               else
                 SearchField<CpuProfilerController>(
                   searchController: widget.controller,
-                  containerPadding: EdgeInsets.zero,
                 ),
             ],
             if (currentTab.key == ProfilerTab.cpuFlameChart.key) ...[
@@ -287,23 +283,21 @@
     unawaited(
       showDialog(
         context: context,
-        builder: (context) => CpuProfileFilterDialog(
-          controller: widget.controller,
-        ),
+        builder:
+            (context) => FilterDialog<CpuStackFrame>(
+              controller: widget.controller,
+              filteredItem: 'stack frame',
+            ),
       ),
     );
   }
 
   List<Widget> _buildProfilerViews() {
     final bottomUp = KeepAliveWrapper(
-      child: CpuBottomUpTable(
-        bottomUpRoots: widget.bottomUpRoots,
-      ),
+      child: CpuBottomUpTable(bottomUpRoots: widget.bottomUpRoots),
     );
     final callTree = KeepAliveWrapper(
-      child: CpuCallTreeTable(
-        dataRoots: widget.callTreeRoots,
-      ),
+      child: CpuCallTreeTable(dataRoots: widget.callTreeRoots),
     );
     final methodTable = KeepAliveWrapper(
       child: CpuMethodTable(
@@ -325,21 +319,17 @@
         },
       ),
     );
-    return [
-      bottomUp,
-      callTree,
-      methodTable,
-      cpuFlameChart,
-    ];
+    return [bottomUp, callTree, methodTable, cpuFlameChart];
   }
 
   void _performOnDataRoots(
     void Function(CpuStackFrame root) callback,
     Tab currentTab,
   ) {
-    final roots = currentTab.key == ProfilerTab.callTree.key
-        ? widget.callTreeRoots
-        : widget.bottomUpRoots;
+    final roots =
+        currentTab.key == ProfilerTab.callTree.key
+            ? widget.callTreeRoots
+            : widget.bottomUpRoots;
     setState(() {
       roots.forEach(callback);
     });
@@ -360,15 +350,14 @@
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
     final samplePeriodValid = metadata.samplePeriod > 0;
-    final samplingPeriodDisplay = samplePeriodValid
-        ? const Duration(seconds: 1).inMicroseconds ~/ metadata.samplePeriod
-        : '--';
+    final samplingPeriodDisplay =
+        samplePeriodValid
+            ? const Duration(seconds: 1).inMicroseconds ~/ metadata.samplePeriod
+            : '--';
     return RoundedOutlinedBorder.onlyBottom(
       child: Container(
         height: _statsRowHeight,
-        padding: const EdgeInsets.symmetric(
-          horizontal: defaultSpacing,
-        ),
+        padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
         child: Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           children: [
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profiler_controller.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profiler_controller.dart
index 87fd3c9..f214a53 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profiler_controller.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profiler_controller.dart
@@ -32,10 +32,7 @@
   }
 }
 
-enum CpuProfilerTagType {
-  user,
-  vm,
-}
+enum CpuProfilerTagType { user, vm }
 
 enum CpuProfilerBusyStatus {
   fetching,
@@ -51,7 +48,9 @@
         FilterControllerMixin<CpuStackFrame>,
         AutoDisposeControllerMixin {
   CpuProfilerController() {
-    subscribeToFilterChanges();
+    // TODO(https://github.com/flutter/devtools/issues/7727): add support for
+    // persisting cpu profiler filter.
+    initFilterController();
   }
 
   /// Tag to represent when no user tag filters are applied.
@@ -92,7 +91,11 @@
   CpuProfileStore get cpuProfileStore =>
       _cpuProfileStoreByIsolateId.putIfAbsent(
         serviceConnection
-                .serviceManager.isolateManager.selectedIsolate.value?.id ??
+                .serviceManager
+                .isolateManager
+                .selectedIsolate
+                .value
+                ?.id ??
             '',
         () => CpuProfileStore(),
       );
@@ -100,8 +103,9 @@
   /// Store of cached CPU profiles for each isolate.
   final _cpuProfileStoreByIsolateId = <String, CpuProfileStore>{};
 
-  late final methodTableController =
-      MethodTableController(dataNotifier: dataNotifier);
+  late final methodTableController = MethodTableController(
+    dataNotifier: dataNotifier,
+  );
 
   /// Notifies that new cpu profile data is available.
   ValueListenable<CpuProfileData?> get dataNotifier => _dataNotifier;
@@ -110,8 +114,9 @@
   /// The current busy state of the cpu profiler.
   ValueListenable<CpuProfilerBusyStatus> get profilerBusyStatus =>
       _profilerBusyStatus;
-  final _profilerBusyStatus =
-      ValueNotifier<CpuProfilerBusyStatus>(CpuProfilerBusyStatus.none);
+  final _profilerBusyStatus = ValueNotifier<CpuProfilerBusyStatus>(
+    CpuProfilerBusyStatus.none,
+  );
 
   /// Notifies that a cpu stack frame was selected.
   ValueListenable<CpuStackFrame?> get selectedCpuStackFrameNotifier =>
@@ -124,47 +129,61 @@
 
   Iterable<String> get userTags =>
       cpuProfileStore
-          .lookupProfile(
-            label: userTagNone,
-          )
+          .lookupProfile(label: userTagNone)
           ?.functionProfile
           .userTags ??
       const <String>[];
 
   ValueListenable<CpuProfilerViewType> get viewType => _viewType;
-  final _viewType =
-      ValueNotifier<CpuProfilerViewType>(CpuProfilerViewType.function);
+  final _viewType = ValueNotifier<CpuProfilerViewType>(
+    CpuProfilerViewType.function,
+  );
+
+  static const _nativeFilterId = 'native';
+  static const _coreDartFilterId = 'core-dart';
+  static const _coreFlutterFilterId = 'core-flutter';
+  static const _uriQueryFilterId = 'cpu-profiler-uri-filter';
 
   /// The toggle filters available for the CPU profiler.
   @override
-  List<ToggleFilter<CpuStackFrame>> createToggleFilters() => [
-        ToggleFilter<CpuStackFrame>(
-          name: 'Hide Native code',
-          includeCallback: (stackFrame) => !stackFrame.isNative,
-          enabledByDefault: true,
-        ),
-        ToggleFilter<CpuStackFrame>(
-          name: 'Hide core Dart libraries',
-          includeCallback: (stackFrame) => !stackFrame.isDartCore,
-        ),
-        if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ??
-            true)
-          ToggleFilter<CpuStackFrame>(
-            name: 'Hide core Flutter libraries',
-            includeCallback: (stackFrame) => !stackFrame.isFlutterCore,
-          ),
-      ];
-
-  static const uriFilterId = 'cpu-profiler-uri-filter';
+  SettingFilters<CpuStackFrame> createSettingFilters() => [
+    ToggleFilter<CpuStackFrame>(
+      id: _nativeFilterId,
+      name: 'Hide Native code',
+      includeCallback: (stackFrame) => !stackFrame.isNative,
+      defaultValue: true,
+    ),
+    ToggleFilter<CpuStackFrame>(
+      id: _coreDartFilterId,
+      name: 'Hide core Dart libraries',
+      includeCallback: (stackFrame) => !stackFrame.isDartCore,
+      defaultValue: false,
+    ),
+    if (serviceConnection.serviceManager.connectedApp?.isFlutterAppNow ?? true)
+      ToggleFilter<CpuStackFrame>(
+        id: _coreFlutterFilterId,
+        name: 'Hide core Flutter libraries',
+        includeCallback: (stackFrame) => !stackFrame.isFlutterCore,
+        defaultValue: false,
+      ),
+  ];
 
   @override
-  Map<String, QueryFilterArgument> createQueryFilterArgs() => {
-        uriFilterId: QueryFilterArgument<CpuStackFrame>(
-          keys: ['uri', 'u'],
-          dataValueProvider: (stackFrame) => stackFrame.packageUri,
-          substringMatch: true,
-        ),
-      };
+  Map<String, QueryFilterArgument<CpuStackFrame>> createQueryFilterArgs() => {
+    _uriQueryFilterId: QueryFilterArgument<CpuStackFrame>(
+      keys: ['uri', 'u'],
+      exampleUsages: [
+        'uri:my_dart_package/some_lib.dart',
+        '-u:some_lib_to_hide',
+      ],
+      dataValueProvider: (stackFrame) => stackFrame.packageUri,
+      substringMatch: true,
+    ),
+  };
+
+  @override
+  ValueNotifier<String>? get filterTagNotifier =>
+      preferences.cpuProfiler.filterTag;
 
   int selectedProfilerTabIndex = 0;
 
@@ -208,9 +227,10 @@
   /// Clients interested in the current value of [profilerFlagNotifier] should
   /// use this getter. Otherwise, clients subscribing to change notifications,
   /// should listen to [profilerFlagNotifier].
-  bool get profilerEnabled => offlineDataController.showingOfflineData.value
-      ? true
-      : profilerFlagNotifier?.value.valueAsString == 'true';
+  bool get profilerEnabled =>
+      offlineDataController.showingOfflineData.value
+          ? true
+          : profilerFlagNotifier?.value.valueAsString == 'true';
 
   Future<Response> enableCpuProfiler() {
     return serviceConnection.serviceManager.service!.enableCpuProfiler();
@@ -236,11 +256,8 @@
       // TODO(kenz): add a cancel button to the processing UI in case pulling a
       // large payload from the vm service takes a long time.
       _profilerBusyStatus.value = CpuProfilerBusyStatus.fetching;
-      cpuProfiles =
-          await serviceConnection.serviceManager.service!.getCpuProfile(
-        startMicros: startMicros,
-        extentMicros: extentMicros,
-      );
+      cpuProfiles = await serviceConnection.serviceManager.service!
+          .getCpuProfile(startMicros: startMicros, extentMicros: extentMicros);
       await processAndSetData(
         cpuProfiles,
         processId: processId,
@@ -257,10 +274,11 @@
         gac.cpuProfiler,
         gac.CpuProfilerEvents.cpuProfileProcessingTime.name,
         asyncOperation: pullAndProcessHelper,
-        screenMetricsProvider: () => ProfilerScreenMetrics(
-          cpuSampleCount: cpuProfiles.profileMetaData.sampleCount,
-          cpuStackDepth: cpuProfiles.profileMetaData.stackDepth,
-        ),
+        screenMetricsProvider:
+            () => ProfilerScreenMetrics(
+              cpuSampleCount: cpuProfiles.profileMetaData.sampleCount,
+              cpuStackDepth: cpuProfiles.profileMetaData.stackDepth,
+            ),
       );
     } on ProcessCancelledException catch (_) {
       // Do nothing for instances of [ProcessCancelledException].
@@ -275,10 +293,7 @@
   }) async {
     assert(_profilerBusyStatus.value == CpuProfilerBusyStatus.processing);
     if (storeAsUserTagNone) {
-      cpuProfileStore.storeProfile(
-        cpuProfiles,
-        label: userTagNone,
-      );
+      cpuProfileStore.storeProfile(cpuProfiles, label: userTagNone);
       cpuProfileStore.storeProfile(
         cpuProfiles,
         time: cpuProfiles.profileMetaData.time,
@@ -289,10 +304,7 @@
       cpuProfiles = _filterData(cpuProfiles);
     }
 
-    await cpuProfiles.process(
-      transformer: transformer,
-      processId: processId,
-    );
+    await cpuProfiles.process(transformer: transformer, processId: processId);
     if (storeAsUserTagNone && shouldApplyFilters) {
       cpuProfileStore.storeProfile(
         cpuProfiles,
@@ -421,28 +433,26 @@
       _dataNotifier.value = null;
 
       if (appStartUpProfileWithFilters != null) {
-        _dataNotifier.value =
-            appStartUpProfileWithFilters.getActive(viewType.value);
+        _dataNotifier.value = appStartUpProfileWithFilters.getActive(
+          viewType.value,
+        );
         refreshSearchMatches();
         return;
       }
 
       if (appStartUpProfile == null) {
-        final cpuProfile =
-            await serviceConnection.serviceManager.service!.getCpuProfile(
-          startMicros: 0,
-          // Using [maxJsInt] as [extentMicros] for the getCpuProfile requests will
-          // give us all cpu samples we have available.
-          extentMicros: maxJsInt,
-        );
+        final cpuProfile = await serviceConnection.serviceManager.service!
+            .getCpuProfile(
+              startMicros: 0,
+              // Using [maxJsInt] as [extentMicros] for the getCpuProfile requests will
+              // give us all cpu samples we have available.
+              extentMicros: maxJsInt,
+            );
         appStartUpProfile = CpuProfilePair.fromUserTag(
           cpuProfile,
           appStartUpUserTag,
         );
-        cpuProfileStore.storeProfile(
-          appStartUpProfile,
-          label: userTagNone,
-        );
+        cpuProfileStore.storeProfile(appStartUpProfile, label: userTagNone);
         cpuProfileStore.storeProfile(
           appStartUpProfile,
           label: appStartUpUserTag,
@@ -496,10 +506,7 @@
     assert(data.processed);
     _dataNotifier.value = data.getActive(viewType.value);
     if (storeAsUserTagNone) {
-      cpuProfileStore.storeProfile(
-        data,
-        label: userTagNone,
-      );
+      cpuProfileStore.storeProfile(data, label: userTagNone);
     }
   }
 
@@ -510,9 +517,9 @@
     _profilerBusyStatus.value = CpuProfilerBusyStatus.processing;
 
     try {
-      _dataNotifier.value = await processDataForTag(tag).then(
-        (e) => e.getActive(viewType.value),
-      );
+      _dataNotifier.value = await processDataForTag(
+        tag,
+      ).then((e) => e.getActive(viewType.value));
     } catch (e, stackTrace) {
       // In the event of an error, reset the data to the original CPU profile.
       final filteredOriginalData = await lookupProfileAndProcess(
@@ -539,15 +546,14 @@
       label: tag,
       createIfAbsent: () {
         final fullData = cpuProfileStore.lookupProfile(label: userTagNone)!;
-        final tagType = tag == groupByUserTag
-            ? CpuProfilerTagType.user
-            : CpuProfilerTagType.vm;
-        final data = tag == groupByUserTag || tag == groupByVmTag
-            ? CpuProfilePair.withTagRoots(
-                fullData,
-                tagType,
-              )
-            : CpuProfilePair.fromUserTag(fullData, tag);
+        final tagType =
+            tag == groupByUserTag
+                ? CpuProfilerTagType.user
+                : CpuProfilerTagType.vm;
+        final data =
+            tag == groupByUserTag || tag == groupByVmTag
+                ? CpuProfilePair.withTagRoots(fullData, tagType)
+                : CpuProfilePair.fromUserTag(fullData, tag);
         cpuProfileStore.storeProfile(data, label: tag);
         return data;
       },
@@ -559,9 +565,7 @@
   void updateViewForType(CpuProfilerViewType type) {
     _viewType.value = type;
     _dataNotifier.value = cpuProfileStore
-        .lookupProfile(
-          label: _wrapWithFilterTag(_userTagFilter.value),
-        )
+        .lookupProfile(label: _wrapWithFilterTag(_userTagFilter.value))
         ?.getActive(type);
   }
 
@@ -624,9 +628,7 @@
     } else {
       // Lookup the cpu profile from the cached [cpuProfileStore] if present.
       final dataLabel = _wrapWithFilterTag(_userTagFilter.value);
-      filteredData = cpuProfileStore.lookupProfile(
-        label: dataLabel,
-      );
+      filteredData = cpuProfileStore.lookupProfile(label: dataLabel);
 
       if (filteredData == null) {
         // TODO(https://github.com/flutter/devtools/issues/5203): optimize
@@ -650,10 +652,7 @@
 
         final filterFrom = dataForCurrentTag;
         filteredData = _filterData(filterFrom, filter: filter);
-        cpuProfileStore.storeProfile(
-          filteredData,
-          label: dataLabel,
-        );
+        cpuProfileStore.storeProfile(filteredData, label: dataLabel);
       }
     }
     unawaited(
@@ -675,17 +674,16 @@
   }) {
     filter ??= activeFilter.value;
     bool filterCallback(CpuStackFrame stackFrame) {
-      for (final toggleFilter in filter!.toggleFilters) {
-        if (toggleFilter.enabled.value &&
-            !toggleFilter.includeCallback(stackFrame)) {
-          return false;
-        }
-      }
+      final filteredOutBySettingFilters = filter!.settingFilters.any(
+        (settingFilter) => !settingFilter.includeData(stackFrame),
+      );
+      if (filteredOutBySettingFilters) return false;
 
       final queryFilter = filter.queryFilter;
       if (!queryFilter.isEmpty) {
         final filteredOutByQueryFilterArgument = queryFilter
-            .filterArguments.values
+            .filterArguments
+            .values
             .any((argument) => !argument.matchesValue(stackFrame));
         if (filteredOutByQueryFilterArgument) return false;
 
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/bottom_up.dart b/packages/devtools_app/lib/src/screens/profiler/panes/bottom_up.dart
index 52989b3..4ae941b 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/bottom_up.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/bottom_up.dart
@@ -13,21 +13,20 @@
 
 /// A table of the bottom up tree for a CPU profile.
 class CpuBottomUpTable extends StatelessWidget {
-  const CpuBottomUpTable({
-    required this.bottomUpRoots,
-    super.key,
-  });
+  const CpuBottomUpTable({required this.bottomUpRoots, super.key});
 
   static final methodColumn = MethodAndSourceColumn();
   static final selfTimeColumn = SelfTimeColumn(
     titleTooltip: selfTimeTooltip,
-    dataTooltipProvider: (stackFrame, context) =>
-        _bottomUpTimeTooltipBuilder(_TimeType.self, stackFrame, context),
+    dataTooltipProvider:
+        (stackFrame, context) =>
+            _bottomUpTimeTooltipBuilder(_TimeType.self, stackFrame, context),
   );
   static final totalTimeColumn = TotalTimeColumn(
     titleTooltip: totalTimeTooltip,
-    dataTooltipProvider: (stackFrame, context) =>
-        _bottomUpTimeTooltipBuilder(_TimeType.total, stackFrame, context),
+    dataTooltipProvider:
+        (stackFrame, context) =>
+            _bottomUpTimeTooltipBuilder(_TimeType.total, stackFrame, context),
   );
   static final columns = List<ColumnData<CpuStackFrame>>.unmodifiable([
     totalTimeColumn,
@@ -64,12 +63,10 @@
           return TextSpan(
             children: [
               const TextSpan(text: 'Time that '),
-              TextSpan(
-                text: '[${stackFrame.name}]',
-                style: fixedStyle,
-              ),
+              TextSpan(text: '[${stackFrame.name}]', style: fixedStyle),
               const TextSpan(
-                text: ' spent executing its own code,\nas well as the code for'
+                text:
+                    ' spent executing its own code,\nas well as the code for'
                     ' any methods that it called.',
               ),
             ],
@@ -78,10 +75,7 @@
           return TextSpan(
             children: [
               const TextSpan(text: 'Time that '),
-              TextSpan(
-                text: '[${stackFrame.name}]',
-                style: fixedStyle,
-              ),
+              TextSpan(text: '[${stackFrame.name}]', style: fixedStyle),
               const TextSpan(text: ' spent executing its own code.'),
             ],
           );
@@ -90,15 +84,9 @@
     return TextSpan(
       children: [
         TextSpan(text: '$type time for root '),
-        TextSpan(
-          text: '[${stackFrame.root.name}]',
-          style: fixedStyle,
-        ),
+        TextSpan(text: '[${stackFrame.root.name}]', style: fixedStyle),
         const TextSpan(text: '\nwhen called through '),
-        TextSpan(
-          text: '[${stackFrame.name}]',
-          style: fixedStyle,
-        ),
+        TextSpan(text: '[${stackFrame.name}]', style: fixedStyle),
       ],
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/call_tree.dart b/packages/devtools_app/lib/src/screens/profiler/panes/call_tree.dart
index 28c3f37..91949d5 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/call_tree.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/call_tree.dart
@@ -12,17 +12,15 @@
 
 /// A table of the CPU's top-down call tree.
 class CpuCallTreeTable extends StatelessWidget {
-  const CpuCallTreeTable({
-    required this.dataRoots,
-    super.key,
-  });
+  const CpuCallTreeTable({required this.dataRoots, super.key});
 
   static final methodColumn = MethodAndSourceColumn();
 
   static final selfTimeColumn = SelfTimeColumn(titleTooltip: selfTimeTooltip);
 
-  static final totalTimeColumn =
-      TotalTimeColumn(titleTooltip: totalTimeTooltip);
+  static final totalTimeColumn = TotalTimeColumn(
+    titleTooltip: totalTimeTooltip,
+  );
 
   static final columns = List<ColumnData<CpuStackFrame>>.unmodifiable([
     totalTimeColumn,
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/controls/cpu_profiler_controls.dart b/packages/devtools_app/lib/src/screens/profiler/panes/controls/cpu_profiler_controls.dart
index 4eb3d42..5d0c63a 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/controls/cpu_profiler_controls.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/controls/cpu_profiler_controls.dart
@@ -8,38 +8,9 @@
 import 'package:flutter/material.dart';
 
 import '../../../../shared/globals.dart';
-import '../../../../shared/ui/filter.dart';
-import '../../cpu_profile_model.dart';
 import '../../cpu_profiler_controller.dart';
 import '../../profiler_screen_controller.dart';
 
-class CpuProfileFilterDialog extends StatelessWidget {
-  const CpuProfileFilterDialog({required this.controller, super.key});
-
-  static const filterQueryInstructions = '''
-Type a filter query to show or hide specific stack frames.
-
-Any text that is not paired with an available filter key below will be queried against all categories (method, uri).
-
-Available filters:
-    'uri', 'u'       (e.g. 'uri:my_dart_package/some_lib.dart', '-u:some_lib_to_hide')
-
-Example queries:
-    'someMethodName uri:my_dart_package,b_dart_package'
-    '.toString -uri:flutter'
-''';
-
-  final CpuProfilerController controller;
-
-  @override
-  Widget build(BuildContext context) {
-    return FilterDialog<CpuStackFrame>(
-      controller: controller,
-      queryInstructions: filterQueryInstructions,
-    );
-  }
-}
-
 /// DropdownButton that controls the value of
 /// [ProfilerScreenController.userTagFilter].
 class UserTagDropdown extends StatelessWidget {
@@ -54,9 +25,10 @@
       valueListenable: controller.userTagFilter,
       builder: (context, userTag, _) {
         final userTags = controller.userTags;
-        final tooltip = userTags.isNotEmpty
-            ? 'Filter the CPU profile by the given UserTag'
-            : 'No UserTags found for this CPU profile';
+        final tooltip =
+            userTags.isNotEmpty
+                ? 'Filter the CPU profile by the given UserTag'
+                : 'No UserTags found for this CPU profile';
         return SizedBox(
           height: defaultButtonHeight,
           child: DevToolsTooltip(
@@ -94,11 +66,12 @@
                         value: CpuProfilerController.groupByVmTag,
                       ),
                   ],
-                  onChanged: userTags.isEmpty ||
-                          (userTags.length == 1 &&
-                              userTags.first == UserTag.defaultTag.label)
-                      ? null
-                      : (String? tag) => _onUserTagChanged(tag!),
+                  onChanged:
+                      userTags.isEmpty ||
+                              (userTags.length == 1 &&
+                                  userTags.first == UserTag.defaultTag.label)
+                          ? null
+                          : (String? tag) => _onUserTagChanged(tag!),
                 );
               },
             ),
@@ -112,10 +85,7 @@
     required String display,
     required String value,
   }) {
-    return DropdownMenuItem<String>(
-      value: value,
-      child: Text(display),
-    );
+    return DropdownMenuItem<String>(value: value, child: Text(display));
   }
 
   void _onUserTagChanged(String newTag) async {
@@ -140,12 +110,13 @@
     return ValueListenableBuilder<CpuProfilerViewType>(
       valueListenable: controller.viewType,
       builder: (context, viewType, _) {
-        final tooltip = viewType == CpuProfilerViewType.function
-            ? 'Display the profile in terms of the Dart call stack '
-                '(i.e., inlined frames are expanded)'
-            : 'Display the profile in terms of native stack frames '
-                '(i.e., inlined frames are not expanded, display code objects '
-                'rather than individual functions)';
+        final tooltip =
+            viewType == CpuProfilerViewType.function
+                ? 'Display the profile in terms of the Dart call stack '
+                    '(i.e., inlined frames are expanded)'
+                : 'Display the profile in terms of native stack frames '
+                    '(i.e., inlined frames are not expanded, display code objects '
+                    'rather than individual functions)';
         return SizedBox(
           height: defaultButtonHeight,
           child: DevToolsTooltip(
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart b/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart
index 6a2d3de..8cba8f8 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart
@@ -6,10 +6,10 @@
 import 'package:flutter/material.dart';
 
 import '../../../../shared/analytics/constants.dart' as gac;
-import '../../../../shared/common_widgets.dart';
-import '../../../../shared/file_import.dart';
+import '../../../../shared/framework/screen.dart';
 import '../../../../shared/globals.dart';
-import '../../../../shared/screen.dart';
+import '../../../../shared/ui/common_widgets.dart';
+import '../../../../shared/ui/file_import.dart';
 import '../../../../shared/ui/vm_flag_widgets.dart';
 import '../../profiler_screen_controller.dart';
 
@@ -42,10 +42,7 @@
             child: ExitOfflineButton(gaScreen: gac.cpuProfiler),
           )
         else ...[
-          _PrimaryControls(
-            controller: controller,
-            recording: recording,
-          ),
+          _PrimaryControls(controller: controller, recording: recording),
           const SizedBox(width: defaultSpacing),
           _SecondaryControls(
             controller: controller,
@@ -58,10 +55,7 @@
 }
 
 class _PrimaryControls extends StatelessWidget {
-  const _PrimaryControls({
-    required this.controller,
-    required this.recording,
-  });
+  const _PrimaryControls({required this.controller, required this.recording});
 
   static const _primaryControlsMinIncludeTextWidth = 1170.0;
 
@@ -113,20 +107,24 @@
       mainAxisAlignment: MainAxisAlignment.end,
       children: [
         if (serviceConnection
-            .serviceManager.connectedApp!.isFlutterNativeAppNow)
+            .serviceManager
+            .connectedApp!
+            .isFlutterNativeAppNow)
           GaDevToolsButton(
             icon: Icons.timer,
             label: 'Profile app start up',
-            tooltip: 'Load all Dart CPU samples that occurred before \n'
+            tooltip:
+                'Load all Dart CPU samples that occurred before \n'
                 'the first Flutter frame was drawn (if available)',
             tooltipPadding: const EdgeInsets.all(denseSpacing),
             gaScreen: gac.cpuProfiler,
             gaSelection: gac.CpuProfilerEvents.profileAppStartUp.name,
             minScreenWidthForTextBeforeScaling:
                 _profilingControlsMinScreenWidthForText,
-            onPressed: !profilerBusy
-                ? controller.cpuProfilerController.loadAppStartUpProfile
-                : null,
+            onPressed:
+                !profilerBusy
+                    ? controller.cpuProfilerController.loadAppStartUpProfile
+                    : null,
           ),
         const SizedBox(width: denseSpacing),
         RefreshButton(
@@ -136,9 +134,10 @@
           gaSelection: gac.CpuProfilerEvents.loadAllCpuSamples.name,
           minScreenWidthForTextBeforeScaling:
               _profilingControlsMinScreenWidthForText,
-          onPressed: !profilerBusy
-              ? controller.cpuProfilerController.loadAllSamples
-              : null,
+          onPressed:
+              !profilerBusy
+                  ? controller.cpuProfilerController.loadAllSamples
+                  : null,
         ),
         const SizedBox(width: denseSpacing),
         CpuSamplingRateDropdown(
@@ -149,11 +148,12 @@
         const SizedBox(width: denseSpacing),
         OpenSaveButtonGroup(
           screenId: ScreenMetaData.cpuProfiler.id,
-          onSave: !profilerBusy &&
-                  controller.cpuProfileData != null &&
-                  controller.cpuProfileData?.isEmpty == false
-              ? _exportPerformance
-              : null,
+          onSave:
+              !profilerBusy &&
+                      controller.cpuProfileData != null &&
+                      controller.cpuProfileData?.isEmpty == false
+                  ? _exportPerformance
+                  : null,
         ),
       ],
     );
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/cpu_flame_chart.dart b/packages/devtools_app/lib/src/screens/profiler/panes/cpu_flame_chart.dart
index cff8fbf..1f459ff 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/cpu_flame_chart.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/cpu_flame_chart.dart
@@ -21,17 +21,17 @@
     required ValueListenable<CpuStackFrame?> activeSearchMatchNotifier,
     required void Function(CpuStackFrame? stackFrame) onDataSelected,
   }) : super(
-          data,
-          time: data.profileMetaData.time!,
-          containerWidth: width,
-          containerHeight: height,
-          startInset: sideInsetSmall,
-          endInset: sideInsetSmall,
-          selectionNotifier: selectionNotifier,
-          searchMatchesNotifier: searchMatchesNotifier,
-          activeSearchMatchNotifier: activeSearchMatchNotifier,
-          onDataSelected: onDataSelected,
-        );
+         data,
+         time: data.profileMetaData.time!,
+         containerWidth: width,
+         containerHeight: height,
+         startInset: sideInsetSmall,
+         endInset: sideInsetSmall,
+         selectionNotifier: selectionNotifier,
+         searchMatchesNotifier: searchMatchesNotifier,
+         activeSearchMatchNotifier: activeSearchMatchNotifier,
+         onDataSelected: onDataSelected,
+       );
 
   @override
   State<CpuProfileFlameChart> createState() => _CpuProfileFlameChartState();
@@ -53,7 +53,8 @@
     );
 
     void createChartNodes(CpuStackFrame stackFrame, int row) {
-      final width = widget.startingContentWidth * stackFrame.totalTimeRatio -
+      final width =
+          widget.startingContentWidth * stackFrame.totalTimeRatio -
           stackFramePadding;
       final left = startingLeftForStackFrame(stackFrame);
       final colorPair = _colorPairForStackFrame(stackFrame);
@@ -144,7 +145,8 @@
         // [stackFrame] is not the first child of its parent. [left] should
         // equal the right value of its previous sibling.
         final previous = parent.children[stackFrameIndex - 1];
-        left = stackFrameLefts[previous.id]! +
+        left =
+            stackFrameLefts[previous.id]! +
             (widget.startingContentWidth * previous.totalTimeRatio);
       }
     }
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/cpu_profile_columns.dart b/packages/devtools_app/lib/src/screens/profiler/panes/cpu_profile_columns.dart
index 9044058..fedf980 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/cpu_profile_columns.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/cpu_profile_columns.dart
@@ -4,9 +4,9 @@
 
 import 'package:flutter/material.dart';
 
-import '../../../shared/profiler_utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
+import '../../../shared/utils/profiler_utils.dart';
 import '../cpu_profile_model.dart';
 
 class SelfTimeColumn extends TimeAndPercentageColumn<CpuStackFrame> {
@@ -14,12 +14,12 @@
     super.titleTooltip,
     RichTooltipBuilder<CpuStackFrame>? dataTooltipProvider,
   }) : super(
-          title: 'Self Time',
-          timeProvider: (stackFrame) => stackFrame.selfTime,
-          percentAsDoubleProvider: (stackFrame) => stackFrame.selfTimeRatio,
-          richTooltipProvider: dataTooltipProvider,
-          secondaryCompare: (stackFrame) => stackFrame.name,
-        );
+         title: 'Self Time',
+         timeProvider: (stackFrame) => stackFrame.selfTime,
+         percentAsDoubleProvider: (stackFrame) => stackFrame.selfTimeRatio,
+         richTooltipProvider: dataTooltipProvider,
+         secondaryCompare: (stackFrame) => stackFrame.name,
+       );
 }
 
 class TotalTimeColumn extends TimeAndPercentageColumn<CpuStackFrame> {
@@ -27,12 +27,12 @@
     super.titleTooltip,
     RichTooltipBuilder<CpuStackFrame>? dataTooltipProvider,
   }) : super(
-          title: 'Total Time',
-          timeProvider: (stackFrame) => stackFrame.totalTime,
-          percentAsDoubleProvider: (stackFrame) => stackFrame.totalTimeRatio,
-          richTooltipProvider: dataTooltipProvider,
-          secondaryCompare: (stackFrame) => stackFrame.name,
-        );
+         title: 'Total Time',
+         timeProvider: (stackFrame) => stackFrame.totalTime,
+         percentAsDoubleProvider: (stackFrame) => stackFrame.totalTimeRatio,
+         richTooltipProvider: dataTooltipProvider,
+         secondaryCompare: (stackFrame) => stackFrame.name,
+       );
 }
 
 class MethodAndSourceColumn extends TreeColumnData<CpuStackFrame>
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table.dart b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table.dart
index 6b938b1..06f34c0 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table.dart
@@ -7,9 +7,9 @@
 import 'package:flutter/material.dart';
 
 import '../../../../shared/primitives/utils.dart';
-import '../../../../shared/profiler_utils.dart';
 import '../../../../shared/table/table.dart';
 import '../../../../shared/table/table_data.dart';
+import '../../../../shared/utils/profiler_utils.dart';
 import 'method_table_controller.dart';
 import 'method_table_model.dart';
 
@@ -114,9 +114,10 @@
       _callers = <MethodTableGraphNode>[];
       _callees = <MethodTableGraphNode>[];
     } else {
-      _callers = _selectedGraphNode!.predecessors
-          .cast<MethodTableGraphNode>()
-          .toList();
+      _callers =
+          _selectedGraphNode!.predecessors
+              .cast<MethodTableGraphNode>()
+              .toList();
       _callees =
           _selectedGraphNode!.successors.cast<MethodTableGraphNode>().toList();
     }
@@ -139,10 +140,7 @@
         children: [
           Flexible(
             child: OutlineDecoration.onlyBottom(
-              child: _CallersTable(
-                widget.methodTableController,
-                _callers,
-              ),
+              child: _CallersTable(widget.methodTableController, _callers),
             ),
           ),
           DevToolsTooltip(
@@ -162,10 +160,7 @@
           ),
           Flexible(
             child: OutlineDecoration.onlyTop(
-              child: _CalleesTable(
-                widget.methodTableController,
-                _callees,
-              ),
+              child: _CalleesTable(widget.methodTableController, _callees),
             ),
           ),
         ],
@@ -177,8 +172,9 @@
 /// A table of predecessors (callers) for a single method in a method table.
 class _CallersTable extends StatelessWidget {
   _CallersTable(this._methodTableController, this._callers) {
-    _callerTimeColumn =
-        _CallerTimeColumn(methodTableController: _methodTableController);
+    _callerTimeColumn = _CallerTimeColumn(
+      methodTableController: _methodTableController,
+    );
     columns = List<ColumnData<MethodTableGraphNode>>.unmodifiable([
       _callerTimeColumn,
       methodColumn,
@@ -213,8 +209,9 @@
 /// A table of successors (callees) for a single method in a method table.
 class _CalleesTable extends StatelessWidget {
   _CalleesTable(this._methodTableController, this._callees) {
-    _calleeTimeColumn =
-        _CalleeTimeColumn(methodTableController: _methodTableController);
+    _calleeTimeColumn = _CalleeTimeColumn(
+      methodTableController: _methodTableController,
+    );
     _columns = List<ColumnData<MethodTableGraphNode>>.unmodifiable([
       _calleeTimeColumn,
       _methodColumn,
@@ -248,11 +245,7 @@
 
 class _MethodColumn extends ColumnData<MethodTableGraphNode>
     implements ColumnRenderer<MethodTableGraphNode> {
-  _MethodColumn()
-      : super.wide(
-          'Method',
-          minWidthPx: _methodColumnMinWidth,
-        );
+  _MethodColumn() : super.wide('Method', minWidthPx: _methodColumnMinWidth);
 
   @override
   String getValue(MethodTableGraphNode dataObject) => dataObject.name;
@@ -284,50 +277,48 @@
 
 class _SelfTimeColumn extends TimeAndPercentageColumn<MethodTableGraphNode> {
   _SelfTimeColumn()
-      : super(
-          title: 'Self %',
-          percentageOnly: true,
-          timeProvider: (node) => node.selfTime,
-          percentAsDoubleProvider: (node) => node.selfTimeRatio,
-          secondaryCompare: (node) => node.name,
-          columnWidth: _totalAndSelfColumnWidth,
-        );
+    : super(
+        title: 'Self %',
+        percentageOnly: true,
+        timeProvider: (node) => node.selfTime,
+        percentAsDoubleProvider: (node) => node.selfTimeRatio,
+        secondaryCompare: (node) => node.name,
+        columnWidth: _totalAndSelfColumnWidth,
+      );
 }
 
 class _TotalTimeColumn extends TimeAndPercentageColumn<MethodTableGraphNode> {
   _TotalTimeColumn()
-      : super(
-          title: 'Total %',
-          percentageOnly: true,
-          timeProvider: (node) => node.totalTime,
-          percentAsDoubleProvider: (node) => node.totalTimeRatio,
-          secondaryCompare: (node) => node.name,
-          columnWidth: _totalAndSelfColumnWidth,
-        );
+    : super(
+        title: 'Total %',
+        percentageOnly: true,
+        timeProvider: (node) => node.totalTime,
+        percentAsDoubleProvider: (node) => node.totalTimeRatio,
+        secondaryCompare: (node) => node.name,
+        columnWidth: _totalAndSelfColumnWidth,
+      );
 }
 
 class _CallerTimeColumn extends TimeAndPercentageColumn<MethodTableGraphNode> {
-  _CallerTimeColumn({
-    required MethodTableController methodTableController,
-  }) : super(
-          title: 'Caller %',
-          percentageOnly: true,
-          percentAsDoubleProvider: (node) =>
-              methodTableController.callerPercentageFor(node),
-          secondaryCompare: (node) => node.name,
-          columnWidth: _callGraphColumnWidth,
-        );
+  _CallerTimeColumn({required MethodTableController methodTableController})
+    : super(
+        title: 'Caller %',
+        percentageOnly: true,
+        percentAsDoubleProvider:
+            (node) => methodTableController.callerPercentageFor(node),
+        secondaryCompare: (node) => node.name,
+        columnWidth: _callGraphColumnWidth,
+      );
 }
 
 class _CalleeTimeColumn extends TimeAndPercentageColumn<MethodTableGraphNode> {
-  _CalleeTimeColumn({
-    required MethodTableController methodTableController,
-  }) : super(
-          title: 'Callee %',
-          percentageOnly: true,
-          percentAsDoubleProvider: (node) =>
-              methodTableController.calleePercentageFor(node),
-          secondaryCompare: (node) => node.name,
-          columnWidth: _callGraphColumnWidth,
-        );
+  _CalleeTimeColumn({required MethodTableController methodTableController})
+    : super(
+        title: 'Callee %',
+        percentageOnly: true,
+        percentAsDoubleProvider:
+            (node) => methodTableController.calleePercentageFor(node),
+        secondaryCompare: (node) => node.name,
+        columnWidth: _callGraphColumnWidth,
+      );
 }
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_controller.dart b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_controller.dart
index 5d0df63..90f77de 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_controller.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_controller.dart
@@ -65,13 +65,14 @@
       depthFirstTraversal(
         root,
         action: (CpuStackFrame frame) {
-          final parentNode = frame.parent != null &&
-                  frame.parentId != CpuProfileData.rootId &&
-                  !frame.parent!.isTag
-              // Since we are performing a DFS, the parent should always be in
-              // the map.
-              ? methodMap[frame.parent!.methodTableId]!
-              : null;
+          final parentNode =
+              frame.parent != null &&
+                      frame.parentId != CpuProfileData.rootId &&
+                      !frame.parent!.isTag
+                  // Since we are performing a DFS, the parent should always be in
+                  // the map.
+                  ? methodMap[frame.parent!.methodTableId]!
+                  : null;
 
           var graphNode = MethodTableGraphNode.fromStackFrame(frame);
           final existingNode = methodMap[frame.methodTableId];
diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_model.dart b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_model.dart
index 484c4ea..92cf67a 100644
--- a/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_model.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/panes/method_table/method_table_model.dart
@@ -6,8 +6,8 @@
 
 import '../../../../shared/primitives/graph.dart';
 import '../../../../shared/primitives/utils.dart';
-import '../../../../shared/profiler_utils.dart';
 import '../../../../shared/ui/search.dart';
+import '../../../../shared/utils/profiler_utils.dart';
 import '../../cpu_profile_model.dart';
 
 /// Represents a graph node for a method in a CPU profile method table.
@@ -20,9 +20,9 @@
     required int selfCount,
     required this.profileMetaData,
     required this.stackFrameIds,
-  })  : _totalCount = totalCount,
-        _selfCount = selfCount,
-        _sourceUri = uriWithSourceLine(packageUri, sourceLine);
+  }) : _totalCount = totalCount,
+       _selfCount = selfCount,
+       _sourceUri = uriWithSourceLine(packageUri, sourceLine);
 
   factory MethodTableGraphNode.fromStackFrame(CpuStackFrame frame) {
     return MethodTableGraphNode(
@@ -67,19 +67,18 @@
       safeDivide(selfCount, profileMetaData.sampleCount);
 
   Duration get selfTime => Duration(
-        microseconds:
-            (selfTimeRatio * profileMetaData.time!.duration.inMicroseconds)
-                .round(),
-      );
+    microseconds:
+        (selfTimeRatio * profileMetaData.time!.duration.inMicroseconds).round(),
+  );
 
   double get totalTimeRatio =>
       safeDivide(totalCount, profileMetaData.sampleCount);
 
   Duration get totalTime => Duration(
-        microseconds:
-            (totalTimeRatio * profileMetaData.time!.duration.inMicroseconds)
-                .round(),
-      );
+    microseconds:
+        (totalTimeRatio * profileMetaData.time!.duration.inMicroseconds)
+            .round(),
+  );
 
   void merge(MethodTableGraphNode other, {required bool mergeTotalTime}) {
     if (!shallowEquals(other)) return;
@@ -113,9 +112,7 @@
           .cast<MethodTableGraphNode>()
           // Sort in descending order.
           .sorted((a, b) => percentCallback(b).compareTo(percentCallback(a)))
-          .map(
-            (node) => '${node.display} - ${percent(percentCallback(node))}',
-          )
+          .map((node) => '${node.display} - ${percent(percentCallback(node))}')
           .join(newLineAndIndent);
     }
 
diff --git a/packages/devtools_app/lib/src/screens/profiler/profiler_screen.dart b/packages/devtools_app/lib/src/screens/profiler/profiler_screen.dart
index 3383430..2e84c37 100644
--- a/packages/devtools_app/lib/src/screens/profiler/profiler_screen.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/profiler_screen.dart
@@ -11,14 +11,14 @@
 
 import '../../shared/analytics/analytics.dart' as ga;
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/banner_messages.dart';
-import '../../shared/common_widgets.dart';
 import '../../shared/config_specific/import_export/import_export.dart';
-import '../../shared/file_import.dart';
+import '../../shared/framework/screen.dart';
 import '../../shared/globals.dart';
+import '../../shared/managers/banner_messages.dart';
 import '../../shared/primitives/listenable.dart';
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/file_import.dart';
+import '../../shared/utils/utils.dart';
 import 'cpu_profile_model.dart';
 import 'cpu_profiler.dart';
 import 'cpu_profiler_controller.dart';
@@ -129,11 +129,12 @@
             child: const CenteredCircularProgressIndicator(),
           );
         }
-        final status = recording || profilerBusy
-            ? (recording
-                ? const RecordingStatus()
-                : ProfilerBusyStatus(status: profilerBusyStatus))
-            : null;
+        final status =
+            recording || profilerBusy
+                ? (recording
+                    ? const RecordingStatus()
+                    : ProfilerBusyStatus(status: profilerBusyStatus))
+                : null;
         return Column(
           children: [
             ProfilerScreenControls(
@@ -144,7 +145,8 @@
             ),
             const SizedBox(height: intermediateSpacing),
             Expanded(
-              child: status ??
+              child:
+                  status ??
                   ValueListenableBuilder<CpuProfileData?>(
                     valueListenable:
                         controller.cpuProfilerController.dataNotifier,
@@ -191,8 +193,10 @@
       gaSelectionImport: gac.CpuProfilerEvents.openDataFile.name,
       gaSelectionAction: gac.CpuProfilerEvents.loadDataFromFile.name,
       onAction: (jsonFile) {
-        Provider.of<ImportController>(context, listen: false)
-            .importData(jsonFile, expectedScreenId: ProfilerScreen.id);
+        Provider.of<ImportController>(
+          context,
+          listen: false,
+        ).importData(jsonFile, expectedScreenId: ProfilerScreen.id);
       },
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/profiler/profiler_screen_controller.dart b/packages/devtools_app/lib/src/screens/profiler/profiler_screen_controller.dart
index 625287b..49a18f1 100644
--- a/packages/devtools_app/lib/src/screens/profiler/profiler_screen_controller.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/profiler_screen_controller.dart
@@ -10,7 +10,7 @@
 
 import '../../shared/config_specific/logger/allowed_error.dart';
 import '../../shared/globals.dart';
-import '../../shared/offline_data.dart';
+import '../../shared/offline/offline_data.dart';
 import '../../shared/primitives/utils.dart';
 import 'cpu_profile_model.dart';
 import 'cpu_profile_service.dart';
@@ -38,8 +38,9 @@
   Future<void> _initHelper() async {
     if (!offlineDataController.showingOfflineData.value) {
       await allowedError(
-        serviceConnection.serviceManager.service!
-            .setProfilePeriod(mediumProfilePeriod),
+        serviceConnection.serviceManager.service!.setProfilePeriod(
+          mediumProfilePeriod,
+        ),
         logError: false,
       );
 
@@ -48,8 +49,12 @@
       addAutoDisposeListener(
         serviceConnection.serviceManager.isolateManager.selectedIsolate,
         () {
-          final selectedIsolate = serviceConnection
-              .serviceManager.isolateManager.selectedIsolate.value;
+          final selectedIsolate =
+              serviceConnection
+                  .serviceManager
+                  .isolateManager
+                  .selectedIsolate
+                  .value;
           if (selectedIsolate != null) {
             switchToIsolate(selectedIsolate);
           }
@@ -68,8 +73,9 @@
           // need to default to the basic view of the profile.
           final userTagFilter = cpuProfilerController.userTagFilter.value;
           if (userTagFilter == CpuProfilerController.groupByVmTag) {
-            await cpuProfilerController
-                .loadDataWithTag(CpuProfilerController.userTagNone);
+            await cpuProfilerController.loadDataWithTag(
+              CpuProfilerController.userTagNone,
+            );
           }
         }
         // Always reset to the function view when the VM developer mode state
@@ -93,10 +99,7 @@
       processId: 'offline data processing',
     );
     cpuProfilerController.loadProcessedData(
-      CpuProfilePair(
-        functionProfile: data,
-        codeProfile: null,
-      ),
+      CpuProfilePair(functionProfile: data, codeProfile: null),
       storeAsUserTagNone: true,
     );
   }
@@ -151,9 +154,9 @@
 
   @override
   OfflineScreenData prepareOfflineScreenData() => OfflineScreenData(
-        screenId: ProfilerScreen.id,
-        data: cpuProfileData!.toJson(),
-      );
+    screenId: ProfilerScreen.id,
+    data: cpuProfileData!.toJson(),
+  );
 
   Future<void> clear() async {
     await cpuProfilerController.clear();
diff --git a/packages/devtools_app/lib/src/screens/profiler/profiler_status.dart b/packages/devtools_app/lib/src/screens/profiler/profiler_status.dart
index beb8ee8..41ca81e 100644
--- a/packages/devtools_app/lib/src/screens/profiler/profiler_status.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/profiler_status.dart
@@ -5,7 +5,7 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/common_widgets.dart';
+import '../../shared/ui/common_widgets.dart';
 import 'cpu_profiler_controller.dart';
 
 class CpuProfilerDisabled extends StatelessWidget {
@@ -109,8 +109,8 @@
 
 class ProfilerBusyStatus extends _Status {
   ProfilerBusyStatus({required CpuProfilerBusyStatus status})
-      : assert(status != CpuProfilerBusyStatus.none),
-        super(statusVerb: status.display);
+    : assert(status != CpuProfilerBusyStatus.none),
+      super(statusVerb: status.display);
 }
 
 class RecordingStatus extends _Status {
diff --git a/packages/devtools_app/lib/src/screens/provider/provider_screen.dart b/packages/devtools_app/lib/src/screens/provider/provider_screen.dart
index 30162a6..2d956d9 100644
--- a/packages/devtools_app/lib/src/screens/provider/provider_screen.dart
+++ b/packages/devtools_app/lib/src/screens/provider/provider_screen.dart
@@ -5,8 +5,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/common_widgets.dart';
-import '../../shared/screen.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/ui/common_widgets.dart';
 
 class ProviderScreen extends Screen {
   ProviderScreen() : super.fromMetaData(ScreenMetaData.provider);
@@ -18,7 +18,8 @@
     return CenteredMessage(
       richMessage: [
         const TextSpan(
-          text: 'The Provider screen is now shipped as a DevTools extension.\n'
+          text:
+              'The Provider screen is now shipped as a DevTools extension.\n'
               'If you want to use this tool, please upgrade your ',
         ),
         TextSpan(
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
index 430e3fb..7bdf306 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
@@ -9,11 +9,11 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/common_widgets.dart';
 import '../../../shared/primitives/byte_utils.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../../profiler/profiler_status.dart';
 import '../vm_developer_common_widgets.dart';
 import '../vm_developer_tools_screen.dart';
@@ -24,10 +24,7 @@
 /// isolate.
 class IsolateStatisticsView extends VMDeveloperView {
   const IsolateStatisticsView()
-      : super(
-          title: 'Isolates',
-          icon: Icons.bar_chart,
-        );
+    : super(title: 'Isolates', icon: Icons.bar_chart);
 
   @override
   bool get showIsolateSelector => true;
@@ -58,14 +55,8 @@
             Flexible(
               child: Column(
                 children: [
-                  Flexible(
-                    child: _buildTopRow(),
-                  ),
-                  Flexible(
-                    child: IsolatePortsWidget(
-                      controller: controller,
-                    ),
-                  ),
+                  Flexible(child: _buildTopRow()),
+                  Flexible(child: IsolatePortsWidget(controller: controller)),
                 ],
               ),
             ),
@@ -82,28 +73,16 @@
           child: Column(
             children: [
               Expanded(
-                child: GeneralIsolateStatisticsWidget(
-                  controller: controller,
-                ),
+                child: GeneralIsolateStatisticsWidget(controller: controller),
               ),
               Expanded(
-                child: IsolateMemoryStatisticsWidget(
-                  controller: controller,
-                ),
+                child: IsolateMemoryStatisticsWidget(controller: controller),
               ),
             ],
           ),
         ),
-        Flexible(
-          child: TagStatisticsWidget(
-            controller: controller,
-          ),
-        ),
-        Flexible(
-          child: ServiceExtensionsWidget(
-            controller: controller,
-          ),
-        ),
+        Flexible(child: TagStatisticsWidget(controller: controller)),
+        Flexible(child: ServiceExtensionsWidget(controller: controller)),
       ],
     );
   }
@@ -125,9 +104,7 @@
     }
     final startedAtFormatter = DateFormat.yMMMMd().add_jms();
     return startedAtFormatter.format(
-      DateTime.fromMillisecondsSinceEpoch(
-        isolate.startTime!,
-      ).toLocal(),
+      DateTime.fromMillisecondsSinceEpoch(isolate.startTime!).toLocal(),
     );
   }
 
@@ -138,9 +115,7 @@
     final uptimeFormatter = DateFormat.Hms();
     return uptimeFormatter.format(
       DateTime.now()
-          .subtract(
-            Duration(milliseconds: isolate.startTime!),
-          )
+          .subtract(Duration(milliseconds: isolate.startTime!))
           .toUtc(),
     );
   }
@@ -201,10 +176,7 @@
           ),
           selectableTextBuilderMapEntry(
             'New Space',
-            _buildMemoryString(
-              isolate?.newSpaceUsage,
-              isolate?.newSpaceUsage,
-            ),
+            _buildMemoryString(isolate?.newSpaceUsage, isolate?.newSpaceUsage),
           ),
           selectableTextBuilderMapEntry(
             'Old Space',
@@ -238,18 +210,17 @@
         title: 'Execution Time',
         roundedTopBorder: false,
         table: Flexible(
-          child: controller.cpuProfilerController.profilerEnabled
-              ? FlatTable<VMTag>(
-                  keyFactory: (VMTag tag) => ValueKey<String>(tag.name),
-                  data: controller.tags,
-                  dataKey: 'tag-statistics',
-                  columns: _columns,
-                  defaultSortColumn: _percentage,
-                  defaultSortDirection: SortDirection.descending,
-                )
-              : CpuProfilerDisabled(
-                  controller.cpuProfilerController,
-                ),
+          child:
+              controller.cpuProfilerController.profilerEnabled
+                  ? FlatTable<VMTag>(
+                    keyFactory: (VMTag tag) => ValueKey<String>(tag.name),
+                    data: controller.tags,
+                    dataKey: 'tag-statistics',
+                    columns: _columns,
+                    defaultSortColumn: _percentage,
+                    defaultSortDirection: SortDirection.descending,
+                  )
+                  : CpuProfilerDisabled(controller.cpuProfilerController),
         ),
       ),
     );
@@ -265,7 +236,7 @@
 
 class _PercentageColumn extends ColumnData<VMTag> {
   _PercentageColumn()
-      : super.wide('Percentage', alignment: ColumnAlignment.right);
+    : super.wide('Percentage', alignment: ColumnAlignment.right);
 
   @override
   double getValue(VMTag dataObject) => dataObject.percentage;
@@ -313,10 +284,7 @@
 // TODO(bkonyi): merge with debugger stack trace viewer.
 /// A simple table to display a stack trace, sorted by frame number.
 class StackTraceViewerWidget extends StatelessWidget {
-  const StackTraceViewerWidget({
-    super.key,
-    required this.stackTrace,
-  });
+  const StackTraceViewerWidget({super.key, required this.stackTrace});
 
   static final frame = _StackTraceViewerFrameColumn();
 
@@ -327,29 +295,29 @@
     return ValueListenableBuilder<InstanceRef?>(
       valueListenable: stackTrace,
       builder: (context, stackTrace, _) {
-        final lines = stackTrace?.allocationLocation?.valueAsString
-            ?.split('\n')
-            .where((e) => e.isNotEmpty)
-            .toList();
+        final lines =
+            stackTrace?.allocationLocation?.valueAsString
+                ?.split('\n')
+                .where((e) => e.isNotEmpty)
+                .toList();
         return VMInfoList(
           title: 'Allocation Location',
           roundedTopBorder: false,
-          table: lines == null
-              ? const Expanded(
-                  child: Center(
-                    child: Text('No port selected'),
+          table:
+              lines == null
+                  ? const Expanded(
+                    child: Center(child: Text('No port selected')),
+                  )
+                  : Flexible(
+                    child: FlatTable<String>(
+                      keyFactory: (String s) => ValueKey<String>(s),
+                      data: lines,
+                      dataKey: 'stack-trace-viewer',
+                      columns: [frame],
+                      defaultSortColumn: frame,
+                      defaultSortDirection: SortDirection.ascending,
+                    ),
                   ),
-                )
-              : Flexible(
-                  child: FlatTable<String>(
-                    keyFactory: (String s) => ValueKey<String>(s),
-                    data: lines,
-                    dataKey: 'stack-trace-viewer',
-                    columns: [frame],
-                    defaultSortColumn: frame,
-                    defaultSortDirection: SortDirection.ascending,
-                  ),
-                ),
         );
       },
     );
@@ -383,10 +351,7 @@
     return OutlineDecoration(
       child: SplitPane(
         axis: Axis.horizontal,
-        initialFractions: const [
-          0.3,
-          0.7,
-        ],
+        initialFractions: const [0.3, 0.7],
         children: [
           OutlineDecoration.onlyRight(
             child: Column(
@@ -394,14 +359,13 @@
                 AreaPaneHeader(
                   includeTopBorder: false,
                   roundedTopBorder: false,
-                  title: Text(
-                    'Open Ports (${ports.length})',
-                  ),
+                  title: Text('Open Ports (${ports.length})'),
                 ),
                 Flexible(
                   child: FlatTable<InstanceRef?>(
-                    keyFactory: (InstanceRef? port) =>
-                        ValueKey<String>(port!.debugName!),
+                    keyFactory:
+                        (InstanceRef? port) =>
+                            ValueKey<String>(port!.debugName!),
                     data: ports,
                     dataKey: 'isolate-ports',
                     columns: _columns,
@@ -414,9 +378,7 @@
             ),
           ),
           OutlineDecoration.onlyLeft(
-            child: StackTraceViewerWidget(
-              stackTrace: selectedPort,
-            ),
+            child: StackTraceViewerWidget(stackTrace: selectedPort),
           ),
         ],
       ),
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer.dart
index 2e2b4bd..85e8511 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer.dart
@@ -4,8 +4,8 @@
 
 import 'package:flutter/material.dart';
 
-import '../../../shared/common_widgets.dart';
-import '../../../shared/tree.dart';
+import '../../../shared/ui/common_widgets.dart';
+import '../../../shared/ui/tree_view.dart';
 import '../vm_developer_common_widgets.dart';
 import 'class_hierarchy_explorer_controller.dart';
 import 'object_inspector_view_controller.dart';
@@ -32,10 +32,11 @@
     return TreeView<ClassHierarchyNode>(
       dataRootsListenable:
           controller.classHierarchyController.selectedIsolateClassHierarchy,
-      dataDisplayProvider: (node, onPressed) => VmServiceObjectLink(
-        object: node.cls,
-        onTap: controller.findAndSelectNodeForObject,
-      ),
+      dataDisplayProvider:
+          (node, onPressed) => VmServiceObjectLink(
+            object: node.cls,
+            onTap: controller.findAndSelectNodeForObject,
+          ),
       emptyTreeViewBuilder: () => const CenteredCircularProgressIndicator(),
     );
   }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer_controller.dart
index 627d41f..782f4ef 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/class_hierarchy_explorer_controller.dart
@@ -37,10 +37,7 @@
   @visibleForTesting
   void buildHierarchy(List<Class> classes) {
     final nodes = <String?, ClassHierarchyNode>{
-      for (final cls in classes)
-        cls.id: ClassHierarchyNode(
-          cls: cls,
-        ),
+      for (final cls in classes) cls.id: ClassHierarchyNode(cls: cls),
     };
 
     late final ClassHierarchyNode objectNode;
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/inbound_references_tree.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/inbound_references_tree.dart
index 0e58697..cedffd0 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/inbound_references_tree.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/inbound_references_tree.dart
@@ -35,9 +35,7 @@
     }
 
     return switch (objectRef) {
-      ClassRef(:final name) ||
-      FuncRef(:final name) ||
-      FieldRef(:final name) =>
+      ClassRef(:final name) || FuncRef(:final name) || FieldRef(:final name) =>
         name,
       LibraryRef(:final name, :final uri) => name.isNullOrEmpty ? uri : name,
       ScriptRef(:final uri) => fileNameFromUri(uri),
@@ -65,18 +63,13 @@
   String _inboundRefDescription(InboundReference inboundRef, int? offset) {
     final parentListIndex = inboundRef.parentListIndex;
     if (parentListIndex != null) {
-      return 'Referenced by ${_parentListElementDescription(
-        parentListIndex,
-        inboundRef.source,
-      )}';
+      return 'Referenced by ${_parentListElementDescription(parentListIndex, inboundRef.source)}';
     }
 
     final description = StringBuffer('Referenced by ');
 
     if (offset != null) {
-      description.write(
-        'offset $offset of ',
-      );
+      description.write('offset $offset of ');
     }
 
     if (inboundRef.parentField is int) {
@@ -86,14 +79,10 @@
       assert((inboundRef.source as InstanceRef).kind == InstanceKind.kRecord);
       description.write('${inboundRef.parentField} of ');
     } else if (inboundRef.parentField is FieldRef) {
-      description.write(
-        '${_objectName(inboundRef.parentField)} of ',
-      );
+      description.write('${_objectName(inboundRef.parentField)} of ');
     }
 
-    description.write(
-      _objectDescription(inboundRef.source) ?? '<object>',
-    );
+    description.write(_objectDescription(inboundRef.source) ?? '<object>');
 
     return description.toString();
   }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
index 07f9c77..0863068 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
@@ -23,10 +23,7 @@
 /// information about objects in the Dart VM.
 class ObjectInspectorView extends VMDeveloperView {
   ObjectInspectorView()
-      : super(
-          title: 'Objects',
-          icon: Icons.data_object_outlined,
-        );
+    : super(title: 'Objects', icon: Icons.data_object_outlined);
 
   @override
   bool get showIsolateSelector => true;
@@ -47,8 +44,9 @@
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
-    final vmDeveloperToolsController =
-        Provider.of<VMDeveloperToolsController>(context);
+    final vmDeveloperToolsController = Provider.of<VMDeveloperToolsController>(
+      context,
+    );
     controller = vmDeveloperToolsController.objectInspectorViewController;
     unawaited(controller.init());
   }
@@ -60,20 +58,14 @@
       initialFractions: const [0.2, 0.8],
       children: [
         const ObjectInspectorSelector(),
-        SelectionArea(
-          child: ObjectViewport(
-            controller: controller,
-          ),
-        ),
+        SelectionArea(child: ObjectViewport(controller: controller)),
       ],
     );
   }
 }
 
 class ObjectInspectorSelector extends StatefulWidget {
-  const ObjectInspectorSelector({
-    super.key,
-  });
+  const ObjectInspectorSelector({super.key});
 
   static const kProgramExplorer = 'Program Explorer';
   static const kObjectStore = 'Object Store';
@@ -91,8 +83,9 @@
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
-    final vmDeveloperToolsController =
-        Provider.of<VMDeveloperToolsController>(context);
+    final vmDeveloperToolsController = Provider.of<VMDeveloperToolsController>(
+      context,
+    );
     controller = vmDeveloperToolsController.objectInspectorViewController;
     unawaited(controller.init());
   }
@@ -125,9 +118,10 @@
               gac.classHierarchy,
             ),
           ],
-          onChanged: (newValue) => setState(() {
-            value = newValue!;
-          }),
+          onChanged:
+              (newValue) => setState(() {
+                value = newValue!;
+              }),
         ),
         Expanded(
           child: RoundedOutlinedBorder(
@@ -145,10 +139,7 @@
     String gaId,
   ) {
     return (
-      item: DropdownMenuItem<String>(
-        value: text,
-        child: Text(text),
-      ),
+      item: DropdownMenuItem<String>(value: text, child: Text(text)),
       gaId: gaId,
     );
   }
@@ -167,9 +158,7 @@
           onLinkTapped: controller.findAndSelectNodeForObject,
         );
       case ObjectInspectorSelector.kClassHierarchy:
-        return ClassHierarchyExplorer(
-          controller: controller,
-        );
+        return ClassHierarchyExplorer(controller: controller);
       default:
         throw StateError('Unexpected value: $value');
     }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
index cd5fa64..76f7baf 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
@@ -25,20 +25,18 @@
   ObjectInspectorViewController({
     ClassHierarchyExplorerController? classHierarchyController,
   }) : classHierarchyController =
-            classHierarchyController ?? ClassHierarchyExplorerController() {
+           classHierarchyController ?? ClassHierarchyExplorerController() {
     addAutoDisposeListener(
       scriptManager.sortedScripts,
       _initializeForCurrentIsolate,
     );
 
-    addAutoDisposeListener(
-      objectHistory.current,
-      _onCurrentObjectChanged,
-    );
+    addAutoDisposeListener(objectHistory.current, _onCurrentObjectChanged);
   }
 
-  final programExplorerController =
-      ProgramExplorerController(showCodeNodes: true);
+  final programExplorerController = ProgramExplorerController(
+    showCodeNodes: true,
+  );
 
   final ClassHierarchyExplorerController classHierarchyController;
   final codeViewController = CodeViewController();
@@ -65,10 +63,11 @@
 
     if (currentObjectValue != null) {
       try {
-        final scriptRef = currentObjectValue.scriptRef ??
-            (await programExplorerController
-                    .searchFileExplorer(currentObjectValue.obj))
-                .script;
+        final scriptRef =
+            currentObjectValue.scriptRef ??
+            (await programExplorerController.searchFileExplorer(
+              currentObjectValue.obj,
+            )).script;
 
         if (scriptRef != null) {
           await programExplorerController.selectScriptNode(scriptRef);
@@ -133,58 +132,29 @@
   }) async {
     VmObject object;
     if (objRef is ClassRef) {
-      object = ClassObject(
-        ref: objRef,
-        scriptRef: scriptRef,
-      );
+      object = ClassObject(ref: objRef, scriptRef: scriptRef);
     } else if (objRef is FuncRef) {
-      object = FuncObject(
-        ref: objRef,
-        scriptRef: scriptRef,
-      );
+      object = FuncObject(ref: objRef, scriptRef: scriptRef);
     } else if (objRef is FieldRef) {
-      object = FieldObject(
-        ref: objRef,
-        scriptRef: scriptRef,
-      );
+      object = FieldObject(ref: objRef, scriptRef: scriptRef);
     } else if (objRef is LibraryRef) {
-      object = LibraryObject(
-        ref: objRef,
-        scriptRef: scriptRef,
-      );
+      object = LibraryObject(ref: objRef, scriptRef: scriptRef);
     } else if (objRef is ScriptRef) {
-      object = ScriptObject(
-        ref: objRef,
-        scriptRef: scriptRef,
-      );
+      object = ScriptObject(ref: objRef, scriptRef: scriptRef);
     } else if (objRef is InstanceRef) {
-      object = InstanceObject(
-        ref: objRef,
-      );
+      object = InstanceObject(ref: objRef);
     } else if (objRef is CodeRef) {
-      object = CodeObject(
-        ref: objRef,
-      );
+      object = CodeObject(ref: objRef);
     } else if (objRef.isObjectPool) {
-      object = ObjectPoolObject(
-        ref: objRef,
-      );
+      object = ObjectPoolObject(ref: objRef);
     } else if (objRef.isICData) {
-      object = ICDataObject(
-        ref: objRef,
-      );
+      object = ICDataObject(ref: objRef);
     } else if (objRef.isSubtypeTestCache) {
-      object = SubtypeTestCacheObject(
-        ref: objRef,
-      );
+      object = SubtypeTestCacheObject(ref: objRef);
     } else if (objRef.isWeakArray) {
-      object = WeakArrayObject(
-        ref: objRef,
-      );
+      object = WeakArrayObject(ref: objRef);
     } else {
-      object = UnknownObject(
-        ref: objRef,
-      );
+      object = UnknownObject(ref: objRef);
     }
 
     await object.initialize();
@@ -203,7 +173,11 @@
     final service = serviceConnection.serviceManager.service!;
     final isolate = await service.getIsolate(
       serviceConnection
-          .serviceManager.isolateManager.selectedIsolate.value!.id!,
+          .serviceManager
+          .isolateManager
+          .selectedIsolate
+          .value!
+          .id!,
     );
 
     final mainScriptRef = scriptRefs.firstWhereOrNull((ref) {
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
index 64a7fcf..e42c0b1 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
@@ -5,10 +5,10 @@
 import 'package:flutter/widgets.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import '../vm_service_private_extensions.dart';
 import 'object_store_controller.dart';
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store_controller.dart
index 88f2f0d..a515370 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store_controller.dart
@@ -22,7 +22,8 @@
       return;
     }
     _selectedIsolateObjectStore.value = null;
-    _selectedIsolateObjectStore.value =
-        await service.getObjectStore(isolate.id!);
+    _selectedIsolateObjectStore.value = await service.getObjectStore(
+      isolate.id!,
+    );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
index 8c8462b..84e5ca3 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
@@ -5,9 +5,9 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
-import '../../../shared/history_viewport.dart';
 import '../../../shared/primitives/history_manager.dart';
+import '../../../shared/ui/common_widgets.dart';
+import '../../../shared/ui/history_viewport.dart';
 import 'object_inspector_view_controller.dart';
 import 'vm_class_display.dart';
 import 'vm_code_display.dart';
@@ -25,10 +25,7 @@
 /// Displays the VM information for the currently selected object in the
 /// program explorer.
 class ObjectViewport extends StatelessWidget {
-  const ObjectViewport({
-    super.key,
-    required this.controller,
-  });
+  const ObjectViewport({super.key, required this.controller});
 
   final ObjectInspectorViewController controller;
 
@@ -36,9 +33,7 @@
   Widget build(BuildContext context) {
     return HistoryViewport<VmObject>(
       history: controller.objectHistory,
-      controls: [
-        ToolbarRefresh(onPressed: controller.refreshObject),
-      ],
+      controls: [ToolbarRefresh(onPressed: controller.refreshObject)],
       generateTitle: viewportTitle,
       contentBuilder: (context, _) {
         return ValueListenableBuilder<bool>(
@@ -50,9 +45,10 @@
               child = const CenteredCircularProgressIndicator();
             } else {
               final currentObject = controller.objectHistory.current.value;
-              child = currentObject == null
-                  ? const SizedBox.shrink()
-                  : buildObjectDisplay(currentObject);
+              child =
+                  currentObject == null
+                      ? const SizedBox.shrink()
+                      : buildObjectDisplay(currentObject);
             }
 
             return Expanded(child: child);
@@ -84,70 +80,37 @@
   @visibleForTesting
   Widget buildObjectDisplay(VmObject obj) {
     if (obj is ClassObject) {
-      return VmClassDisplay(
-        controller: controller,
-        clazz: obj,
-      );
+      return VmClassDisplay(controller: controller, clazz: obj);
     }
     if (obj is FuncObject) {
-      return VmFuncDisplay(
-        controller: controller,
-        function: obj,
-      );
+      return VmFuncDisplay(controller: controller, function: obj);
     }
     if (obj is FieldObject) {
-      return VmFieldDisplay(
-        controller: controller,
-        field: obj,
-      );
+      return VmFieldDisplay(controller: controller, field: obj);
     }
     if (obj is LibraryObject) {
-      return VmLibraryDisplay(
-        controller: controller,
-        library: obj,
-      );
+      return VmLibraryDisplay(controller: controller, library: obj);
     }
     if (obj is ScriptObject) {
-      return VmScriptDisplay(
-        controller: controller,
-        script: obj,
-      );
+      return VmScriptDisplay(controller: controller, script: obj);
     }
     if (obj is InstanceObject) {
-      return VmInstanceDisplay(
-        controller: controller,
-        instance: obj,
-      );
+      return VmInstanceDisplay(controller: controller, instance: obj);
     }
     if (obj is CodeObject) {
-      return VmCodeDisplay(
-        controller: controller,
-        code: obj,
-      );
+      return VmCodeDisplay(controller: controller, code: obj);
     }
     if (obj is ObjectPoolObject) {
-      return VmObjectPoolDisplay(
-        controller: controller,
-        objectPool: obj,
-      );
+      return VmObjectPoolDisplay(controller: controller, objectPool: obj);
     }
     if (obj is ICDataObject) {
-      return VmICDataDisplay(
-        controller: controller,
-        icData: obj,
-      );
+      return VmICDataDisplay(controller: controller, icData: obj);
     }
     if (obj is VmListObject) {
-      return VmSimpleListDisplay(
-        controller: controller,
-        vmObject: obj,
-      );
+      return VmSimpleListDisplay(controller: controller, vmObject: obj);
     }
     if (obj is UnknownObject) {
-      return VmUnknownObjectDisplay(
-        controller: controller,
-        object: obj,
-      );
+      return VmUnknownObjectDisplay(controller: controller, object: obj);
     }
     return const SizedBox.shrink();
   }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_class_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_class_display.dart
index 541cd71..9dae336 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_class_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_class_display.dart
@@ -38,11 +38,7 @@
           ),
         ),
         if (displayClassInstances)
-          Flexible(
-            child: ClassInstancesWidget(
-              instances: clazz.instances,
-            ),
-          ),
+          Flexible(child: ClassInstancesWidget(instances: clazz.instances)),
       ],
     );
     if (clazz.scriptRef != null) {
@@ -60,15 +56,10 @@
   // ClassInstancesWidget implementation is completed.
   /// Generates a list of key-value pairs (map entries) containing the general
   /// information of the class object [clazz].
-  List<MapEntry<String, WidgetBuilder>> _classDataRows(
-    ClassObject clazz,
-  ) {
+  List<MapEntry<String, WidgetBuilder>> _classDataRows(ClassObject clazz) {
     final superClass = clazz.obj.superClass;
     return [
-      ...vmObjectGeneralDataRows(
-        controller,
-        clazz,
-      ),
+      ...vmObjectGeneralDataRows(controller, clazz),
       if (superClass != null)
         serviceObjectLinkBuilderMapEntry(
           controller: controller,
@@ -88,10 +79,7 @@
 // all class instances. When done, remove the last row of the ClassInfoWidget.
 /// Displays information on the instances of the Class object.
 class ClassInstancesWidget extends StatelessWidget {
-  const ClassInstancesWidget({
-    super.key,
-    required this.instances,
-  });
+  const ClassInstancesWidget({super.key, required this.instances});
 
   final InstanceSet? instances;
 
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_code_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_code_display.dart
index 22d6288..ea22fe2 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_code_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_code_display.dart
@@ -7,11 +7,11 @@
 import 'package:string_scanner/string_scanner.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
 import '../../../shared/ui/colors.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import '../vm_service_private_extensions.dart';
 import 'object_inspector_view_controller.dart';
@@ -26,11 +26,7 @@
 }
 
 class _AddressColumn extends _CodeColumnData<Instruction> {
-  _AddressColumn()
-      : super(
-          'Address',
-          fixedWidthPx: 160,
-        );
+  _AddressColumn() : super('Address', fixedWidthPx: 160);
 
   @override
   int getValue(Instruction dataObject) {
@@ -45,11 +41,7 @@
 }
 
 class _AddressRangeColumn extends _CodeColumnData<InliningEntry> {
-  _AddressRangeColumn()
-      : super(
-          'Address Range',
-          fixedWidthPx: 300,
-        );
+  _AddressRangeColumn() : super('Address Range', fixedWidthPx: 300);
 
   @override
   String getValue(InliningEntry dataObject) {
@@ -79,9 +71,7 @@
             object: function,
             onTap: controller.findAndSelectNodeForObject,
           ),
-          const SizedBox(
-            width: denseSpacing,
-          ),
+          const SizedBox(width: denseSpacing),
         ],
       ],
     );
@@ -162,11 +152,7 @@
 
 class _InstructionColumn extends _CodeColumnData<Instruction>
     implements ColumnRenderer<Instruction> {
-  _InstructionColumn()
-      : super(
-          'Disassembly',
-          fixedWidthPx: 240,
-        );
+  _InstructionColumn() : super('Disassembly', fixedWidthPx: 240);
 
   @override
   Object? getValue(Instruction dataObject) {
@@ -184,10 +170,7 @@
     final theme = Theme.of(context);
     return Text.rich(
       style: theme.fixedFontStyle,
-      _highlightAssemblyCode(
-        context,
-        data.instruction,
-      ),
+      _highlightAssemblyCode(context, data.instruction),
     );
   }
 
@@ -202,18 +185,14 @@
   ) {
     return TextSpan(
       text: _getLastMatch(scanner),
-      style: TextStyle(
-        color: colorScheme.controlFlowSyntaxColor,
-      ),
+      style: TextStyle(color: colorScheme.controlFlowSyntaxColor),
     );
   }
 
   TextSpan _buildRegisterSpan(ColorScheme colorScheme, StringScanner scanner) {
     return TextSpan(
       text: _getLastMatch(scanner),
-      style: TextStyle(
-        color: colorScheme.variableSyntaxColor,
-      ),
+      style: TextStyle(color: colorScheme.variableSyntaxColor),
     );
   }
 
@@ -225,9 +204,7 @@
     final match = _getLastMatch(scanner);
     return TextSpan(
       text: isHex ? '0x${match.substring(2).toUpperCase()}' : match,
-      style: TextStyle(
-        color: colorScheme.numericConstantSyntaxColor,
-      ),
+      style: TextStyle(color: colorScheme.numericConstantSyntaxColor),
     );
   }
 
@@ -254,11 +231,7 @@
       } else if (scanner.scan(registerRegExp)) {
         spans.add(_buildRegisterSpan(colorScheme, scanner));
       } else {
-        spans.add(
-          TextSpan(
-            text: String.fromCharCode(scanner.readChar()),
-          ),
-        );
+        spans.add(TextSpan(text: String.fromCharCode(scanner.readChar())));
       }
     }
     return TextSpan(children: spans);
@@ -350,10 +323,7 @@
     CodeObject code,
   ) {
     return [
-      selectableTextBuilderMapEntry(
-        'Kind',
-        code.obj.kind,
-      ),
+      selectableTextBuilderMapEntry('Kind', code.obj.kind),
       serviceObjectLinkBuilderMapEntry(
         controller: controller,
         key: 'Function',
@@ -435,16 +405,8 @@
     _InstructionColumn(),
     _DartObjectColumn(controller: controller),
     if (ticks != null) ...[
-      _ProfileTicksColumn(
-        'Total %',
-        ticks: code.ticksTable,
-        inclusive: true,
-      ),
-      _ProfileTicksColumn(
-        'Self %',
-        ticks: code.ticksTable,
-        inclusive: false,
-      ),
+      _ProfileTicksColumn('Total %', ticks: code.ticksTable, inclusive: true),
+      _ProfileTicksColumn('Self %', ticks: code.ticksTable, inclusive: false),
     ],
   ];
 
@@ -519,15 +481,12 @@
 /// Tracks inclusive and exclusive CPU profiler ticks for a single
 /// [Instruction].
 class CodeTicks {
-  const CodeTicks({
-    required this.inclusiveTicks,
-    required this.exclusiveTicks,
-  });
+  const CodeTicks({required this.inclusiveTicks, required this.exclusiveTicks});
 
   CodeTicks operator +(CodeTicks other) => CodeTicks(
-        inclusiveTicks: inclusiveTicks + other.inclusiveTicks,
-        exclusiveTicks: exclusiveTicks + other.exclusiveTicks,
-      );
+    inclusiveTicks: inclusiveTicks + other.inclusiveTicks,
+    exclusiveTicks: exclusiveTicks + other.exclusiveTicks,
+  );
 
   final int exclusiveTicks;
   final int inclusiveTicks;
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_field_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_field_display.dart
index dbb5c6f..02d1d62 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_field_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_field_display.dart
@@ -38,15 +38,10 @@
 
   /// Generates a list of key-value pairs (map entries) containing the general
   /// information of the field object [field].
-  List<MapEntry<String, WidgetBuilder>> _fieldDataRows(
-    FieldObject field,
-  ) {
+  List<MapEntry<String, WidgetBuilder>> _fieldDataRows(FieldObject field) {
     final staticValue = field.obj.staticValue;
     return [
-      ...vmObjectGeneralDataRows(
-        controller,
-        field,
-      ),
+      ...vmObjectGeneralDataRows(controller, field),
       selectableTextBuilderMapEntry(
         'Observed types',
         _fieldObservedTypes(field),
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_function_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_function_display.dart
index 55c30af..2239426 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_function_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_function_display.dart
@@ -61,10 +61,7 @@
     }
 
     return [
-      selectableTextBuilderMapEntry(
-        'Kind',
-        _kindDescription(function.kind),
-      ),
+      selectableTextBuilderMapEntry('Kind', _kindDescription(function.kind)),
       selectableTextBuilderMapEntry(
         'Deoptimizations',
         function.deoptimizations?.toString(),
@@ -85,10 +82,7 @@
         'Recognized',
         boolYesOrNo(function.isRecognized),
       ),
-      selectableTextBuilderMapEntry(
-        'Native',
-        boolYesOrNo(function.isNative),
-      ),
+      selectableTextBuilderMapEntry('Native', boolYesOrNo(function.isNative)),
       selectableTextBuilderMapEntry('VM Name', function.vmName),
     ];
   }
@@ -113,9 +107,7 @@
 
     addSpace();
 
-    kind.write(
-      funcKind.kindDescription().toLowerCase(),
-    );
+    kind.write(funcKind.kindDescription().toLowerCase());
 
     return kind.toString();
   }
@@ -135,20 +127,17 @@
   Widget build(BuildContext context) {
     return VmExpansionTile(
       title: 'Call Site Data (${callSiteDataArray.length})',
-      children: prettyRows(
-        context,
-        [
-          for (final entry in callSiteDataArray.elements!)
-            Row(
-              children: [
-                VmServiceObjectLink(
-                  object: entry as ObjRef,
-                  onTap: controller.findAndSelectNodeForObject,
-                ),
-              ],
-            ),
-        ],
-      ),
+      children: prettyRows(context, [
+        for (final entry in callSiteDataArray.elements!)
+          Row(
+            children: [
+              VmServiceObjectLink(
+                object: entry as ObjRef,
+                onTap: controller.findAndSelectNodeForObject,
+              ),
+            ],
+          ),
+      ]),
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_ic_data_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_ic_data_display.dart
index 5762650..e8dba62 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_ic_data_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_ic_data_display.dart
@@ -7,8 +7,8 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
 import '../../../shared/globals.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import 'object_inspector_view_controller.dart';
 import 'vm_object_model.dart';
@@ -68,8 +68,13 @@
       return;
     }
 
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
     final service = serviceConnection.serviceManager.service!;
     final argumentsDescriptorFuture = service
         .getObject(isolateId, icData.argumentsDescriptor.id!)
@@ -80,9 +85,7 @@
     _initialized = Future.wait([
       argumentsDescriptorFuture,
       entriesFuture,
-    ]).then(
-      (result) => populateLists(result[0], result[1]),
-    );
+    ]).then((result) => populateLists(result[0], result[1]));
   }
 
   @override
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
index 59e9661..2b45272 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
@@ -8,13 +8,13 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
 import '../../../shared/console/widgets/expandable_variable.dart';
 import '../../../shared/diagnostics/dart_object_node.dart';
 import '../../../shared/diagnostics/tree_builder.dart';
 import '../../../shared/globals.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/ui/colors.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import 'object_inspector_view_controller.dart';
 import 'vm_object_model.dart';
@@ -64,9 +64,7 @@
 
     unawaited(
       _initialized = buildVariablesTree(_root)
-          .then(
-            (_) => _root.expand(),
-          )
+          .then((_) => _root.expand())
           .then(
             (_) => unawaited(
               Future.wait([
@@ -125,10 +123,7 @@
 }
 
 class _InstanceViewer extends StatelessWidget {
-  const _InstanceViewer({
-    required this.controller,
-    required this.instance,
-  });
+  const _InstanceViewer({required this.controller, required this.instance});
 
   final ObjectInspectorViewController controller;
   final InstanceObject instance;
@@ -185,17 +180,14 @@
         SelectableText.rich(
           TextSpan(
             text: hasName ? variable.name : null,
-            style: variable.artificialName
-                ? theme.subtleFixedFontStyle
-                : theme.fixedFontStyle.apply(
-                    color: theme.colorScheme.controlFlowSyntaxColor,
-                  ),
+            style:
+                variable.artificialName
+                    ? theme.subtleFixedFontStyle
+                    : theme.fixedFontStyle.apply(
+                      color: theme.colorScheme.controlFlowSyntaxColor,
+                    ),
             children: [
-              if (hasName)
-                TextSpan(
-                  text: ': ',
-                  style: theme.fixedFontStyle,
-                ),
+              if (hasName) TextSpan(text: ': ', style: theme.fixedFontStyle),
               if (variable.ref!.value is Sentinel)
                 TextSpan(
                   text: 'Sentinel ${variable.displayValue.toString()}',
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_library_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_library_display.dart
index 60c154a..1605ece 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_library_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_library_display.dart
@@ -47,10 +47,7 @@
     LibraryObject library,
   ) {
     return [
-      ...vmObjectGeneralDataRows(
-        controller,
-        library,
-      ),
+      ...vmObjectGeneralDataRows(controller, library),
       serviceObjectLinkBuilderMapEntry(
         controller: controller,
         key: 'URI',
@@ -58,20 +55,14 @@
         object:
             (library.obj.uri?.isEmpty ?? false) ? library.script! : library.obj,
       ),
-      selectableTextBuilderMapEntry(
-        'VM Name',
-        library.vmName,
-      ),
+      selectableTextBuilderMapEntry('VM Name', library.vmName),
     ];
   }
 }
 
 /// An expandable tile displaying a list of library dependencies
 class LibraryDependencies extends StatelessWidget {
-  const LibraryDependencies({
-    super.key,
-    required this.dependencies,
-  });
+  const LibraryDependencies({super.key, required this.dependencies});
 
   final List<LibraryDependency> dependencies;
 
@@ -82,12 +73,7 @@
       for (final dep in dependencies)
         Row(
           children: [
-            Flexible(
-              child: SelectableText(
-                dep.description,
-                style: textStyle,
-              ),
-            ),
+            Flexible(child: SelectableText(dep.description, style: textStyle)),
           ],
         ),
     ];
@@ -97,10 +83,7 @@
   Widget build(BuildContext context) {
     return VmExpansionTile(
       title: 'Dependencies (${dependencies.length})',
-      children: prettyRows(
-        context,
-        dependencyRows(context),
-      ),
+      children: prettyRows(context, dependencyRows(context)),
     );
   }
 }
@@ -117,9 +100,7 @@
 
     addSpace();
 
-    description.write(
-      target?.name ?? target?.uri ?? '<Library name>',
-    );
+    description.write(target?.name ?? target?.uri ?? '<Library name>');
 
     final libPrefix = prefix;
 
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
index 5803fa8..cc8edc2 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
@@ -60,30 +60,31 @@
 
   ValueListenable<List<InboundReferencesTreeNode>> get inboundReferencesTree =>
       _inboundReferencesTree;
-  final _inboundReferencesTree =
-      ListValueNotifier<InboundReferencesTreeNode>([]);
+  final _inboundReferencesTree = ListValueNotifier<InboundReferencesTreeNode>(
+    [],
+  );
 
   @mustCallSuper
   Future<void> initialize() async {
     _isolate =
         serviceConnection.serviceManager.isolateManager.selectedIsolate.value!;
 
-    _obj = ref is Obj
-        ? ref as Obj
-        : await _service.getObject(_isolate!.id!, ref.id!);
+    _obj =
+        ref is Obj
+            ? ref as Obj
+            : await _service.getObject(_isolate!.id!, ref.id!);
 
     if (_sourceLocation != null) {
-      _sourceScript = await _service.getObject(
-        _isolate!.id!,
-        scriptRef?.id ?? _sourceLocation!.script!.id!,
-      ) as Script;
+      _sourceScript =
+          await _service.getObject(
+                _isolate!.id!,
+                scriptRef?.id ?? _sourceLocation!.script!.id!,
+              )
+              as Script;
 
       final token = _sourceLocation!.tokenPos!;
 
-      _pos = SourcePosition.calculatePosition(
-        _sourceScript!,
-        token,
-      );
+      _pos = SourcePosition.calculatePosition(_sourceScript!, token);
     }
   }
 
@@ -100,8 +101,11 @@
   }
 
   Future<void> requestRetainingPath() async {
-    _retainingPath.value =
-        await _service.getRetainingPath(_isolate!.id!, ref.id!, 100);
+    _retainingPath.value = await _service.getRetainingPath(
+      _isolate!.id!,
+      ref.id!,
+      100,
+    );
   }
 
   /// Retrieves the root set of inbound references to the current object.
@@ -127,9 +131,7 @@
       node.ref.source!.id!,
       100,
     );
-    node.addAllChildren(
-      InboundReferencesTreeNode.buildTreeRoots(inboundRefs),
-    );
+    node.addAllChildren(InboundReferencesTreeNode.buildTreeRoots(inboundRefs));
     _inboundReferencesTree.notifyListeners();
   }
 }
@@ -192,8 +194,9 @@
     final funcKind = obj.kind;
     return funcKind == null
         ? null
-        : FunctionKind.values
-            .firstWhereOrNull((element) => element.kind() == funcKind);
+        : FunctionKind.values.firstWhereOrNull(
+          (element) => element.kind() == funcKind,
+        );
   }
 
   int? get deoptimizations => obj.deoptimizations;
@@ -321,8 +324,13 @@
     await super.initialize();
 
     final service = serviceConnection.serviceManager.service!;
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value!.id!;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!;
 
     // Attempt to retrieve the CPU profile data for this code object.
     try {
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_pool_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_pool_display.dart
index f4a9c17..18ceb0d 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_pool_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_pool_display.dart
@@ -23,11 +23,7 @@
 }
 
 class _AddressColumn extends _ObjectPoolColumnData {
-  _AddressColumn()
-      : super(
-          'Offset',
-          fixedWidthPx: 160,
-        );
+  _AddressColumn() : super('Offset', fixedWidthPx: 160);
 
   @override
   int getValue(ObjectPoolEntry dataObject) {
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_script_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_script_display.dart
index e4d365c..ffb6d34 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_script_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_script_display.dart
@@ -37,23 +37,15 @@
 
   /// Generates a list of key-value pairs (map entries) containing the general
   /// VM information of the Script object [widget.script].
-  List<MapEntry<String, WidgetBuilder>> _scriptDataRows(
-    ScriptObject field,
-  ) {
+  List<MapEntry<String, WidgetBuilder>> _scriptDataRows(ScriptObject field) {
     return [
-      ...vmObjectGeneralDataRows(
-        controller,
-        field,
-      ),
+      ...vmObjectGeneralDataRows(controller, field),
       serviceObjectLinkBuilderMapEntry(
         controller: controller,
         key: 'URI',
         object: script.obj,
       ),
-      selectableTextBuilderMapEntry(
-        'Load time',
-        script.loadTime.toString(),
-      ),
+      selectableTextBuilderMapEntry('Load time', script.loadTime.toString()),
     ];
   }
 }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart
index 2a7952e..e2d8213 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart
@@ -7,8 +7,8 @@
 import 'package:flutter/material.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../../shared/common_widgets.dart';
 import '../../../shared/globals.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import 'object_inspector_view_controller.dart';
 import 'vm_object_model.dart';
@@ -59,10 +59,17 @@
         return;
       }
 
-      final isolateId = serviceConnection
-          .serviceManager.isolateManager.selectedIsolate.value!.id!;
+      final isolateId =
+          serviceConnection
+              .serviceManager
+              .isolateManager
+              .selectedIsolate
+              .value!
+              .id!;
       final service = serviceConnection.serviceManager.service!;
-      _initialized = service.getObject(isolateId, elementsInstance.id!).then(
+      _initialized = service
+          .getObject(isolateId, elementsInstance.id!)
+          .then(
             (e) => entries.addAll((e as Instance).elements!.cast<Response?>()),
           );
       return;
@@ -88,10 +95,7 @@
           controller: widget.controller,
           object: widget.vmObject,
           generalDataRows: [
-            ...vmObjectGeneralDataRows(
-              widget.controller,
-              widget.vmObject,
-            ),
+            ...vmObjectGeneralDataRows(widget.controller, widget.vmObject),
           ],
           expandableWidgets: [
             ExpansionTileInstanceList(
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_tree_columns.dart b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_tree_columns.dart
index f5ad49e..b074539 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_tree_columns.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_tree_columns.dart
@@ -25,10 +25,10 @@
 
 class MemoryColumn extends SizeAndPercentageColumn<TreemapNode> {
   MemoryColumn({required VMProcessMemoryViewController controller})
-      : super(
-          title: 'Memory Usage',
-          sizeProvider: (node) => node.byteSize,
-          percentAsDoubleProvider: (node) =>
-              node.byteSize / controller.treeRoot.value!.byteSize,
-        );
+    : super(
+        title: 'Memory Usage',
+        sizeProvider: (node) => node.byteSize,
+        percentAsDoubleProvider:
+            (node) => node.byteSize / controller.treeRoot.value!.byteSize,
+      );
 }
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
index ff5d980..8d5c955 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
@@ -8,10 +8,10 @@
 
 import '../../../shared/analytics/constants.dart' as gac;
 import '../../../shared/charts/treemap.dart';
-import '../../../shared/common_widgets.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../../../shared/ui/tab.dart';
 import '../vm_developer_tools_screen.dart';
 import 'process_memory_tree_columns.dart';
@@ -31,10 +31,7 @@
 /// process's memory footprint.
 class VMProcessMemoryView extends VMDeveloperView {
   const VMProcessMemoryView()
-      : super(
-          title: 'Process Memory',
-          icon: Icons.memory,
-        );
+    : super(title: 'Process Memory', icon: Icons.memory);
 
   @override
   Widget build(BuildContext context) => VMProcessMemoryViewBody();
@@ -55,10 +52,10 @@
 
 class VMProcessMemoryViewBody extends StatefulWidget {
   VMProcessMemoryViewBody({super.key})
-      : tabs = [
-          _buildTab(ProcessMemoryTab.tree),
-          _buildTab(ProcessMemoryTab.treeMap),
-        ];
+    : tabs = [
+        _buildTab(ProcessMemoryTab.tree),
+        _buildTab(ProcessMemoryTab.treeMap),
+      ];
 
   static DevToolsTab _buildTab(ProcessMemoryTab processMemoryTab) {
     return DevToolsTab.create(
@@ -105,10 +102,8 @@
         ..dispose();
     }
 
-    _tabController = TabController(
-      length: widget.tabs.length,
-      vsync: this,
-    )..addListener(_onTabChanged);
+    _tabController = TabController(length: widget.tabs.length, vsync: this)
+      ..addListener(_onTabChanged);
     _tabControllerInitialized = true;
   }
 
@@ -178,9 +173,7 @@
 }
 
 class _ProcessMemoryTree extends StatelessWidget {
-  _ProcessMemoryTree({
-    required this.controller,
-  });
+  _ProcessMemoryTree({required this.controller});
 
   final VMProcessMemoryViewController controller;
 
@@ -200,12 +193,11 @@
       valueListenable: controller.treeRoot,
       builder: (context, root, _) {
         return TreeTable<TreemapNode>(
-          keyFactory: (e) =>
-              PageStorageKey<String>('${e.name}+${e.depth}+${e.byteSize}'),
+          keyFactory:
+              (e) =>
+                  PageStorageKey<String>('${e.name}+${e.depth}+${e.byteSize}'),
           displayTreeGuidelines: true,
-          dataRoots: [
-            if (root != null) root,
-          ],
+          dataRoots: [if (root != null) root],
           dataKey: 'process-memory-tree',
           columns: columns,
           treeColumn: categoryColumn,
@@ -218,9 +210,7 @@
 }
 
 class _ProcessMemoryTreeMap extends StatelessWidget {
-  const _ProcessMemoryTreeMap({
-    required this.controller,
-  });
+  const _ProcessMemoryTreeMap({required this.controller});
 
   final VMProcessMemoryViewController controller;
 
@@ -230,7 +220,7 @@
       builder: (context, constraints) {
         return ValueListenableBuilder<TreemapNode?>(
           valueListenable: controller.treeMapRoot,
-          builder: (context, root, __) {
+          builder: (context, root, _) {
             return Treemap.fromRoot(
               rootNode: root!,
               levelsVisible: 2,
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view_controller.dart
index b1d5766..c5b0067 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view_controller.dart
@@ -42,11 +42,9 @@
     currentRoot.addChild(
       TreemapNode(
         name: 'Other',
-        byteSize: currentRoot.byteSize -
-            currentRoot.children.fold<int>(
-              0,
-              (sum, e) => sum + e.byteSize,
-            ),
+        byteSize:
+            currentRoot.byteSize -
+            currentRoot.children.fold<int>(0, (sum, e) => sum + e.byteSize),
       ),
     );
 
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
index 749c67a..aedee03 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
@@ -13,11 +13,11 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
 import '../../shared/globals.dart';
 import '../../shared/primitives/byte_utils.dart';
 import '../../shared/primitives/utils.dart';
-import '../../shared/tree.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/tree_view.dart';
 import '../debugger/codeview.dart';
 import '../debugger/codeview_controller.dart';
 import '../debugger/debugger_model.dart';
@@ -81,10 +81,7 @@
 ) {
   return MapEntry(
     key,
-    (context) => Text(
-      value ?? '--',
-      style: Theme.of(context).fixedFontStyle,
-    ),
+    (context) => Text(value ?? '--', style: Theme.of(context).fixedFontStyle),
   );
 }
 
@@ -141,25 +138,20 @@
               controller: listScrollController,
               child: ListView(
                 controller: listScrollController,
-                children: prettyRows(
-                  context,
-                  [
-                    for (final row in rowKeyValues)
-                      Row(
-                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                        children: [
-                          Text(
-                            '${row.key.toString()}:',
-                            style: theme.fixedFontStyle,
-                          ),
-                          const SizedBox(width: denseSpacing),
-                          Flexible(
-                            child: Builder(builder: row.value),
-                          ),
-                        ],
-                      ),
-                  ],
-                ),
+                children: prettyRows(context, [
+                  for (final row in rowKeyValues)
+                    Row(
+                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                      children: [
+                        Text(
+                          '${row.key.toString()}:',
+                          style: theme.fixedFontStyle,
+                        ),
+                        const SizedBox(width: denseSpacing),
+                        Flexible(child: Builder(builder: row.value)),
+                      ],
+                    ),
+                ]),
               ),
             ),
           ),
@@ -180,9 +172,7 @@
   return Container(
     color: alternatingColorForIndex(index, Theme.of(context).colorScheme),
     height: defaultRowHeight,
-    padding: const EdgeInsets.symmetric(
-      horizontal: defaultSpacing,
-    ),
+    padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
     child: row,
   );
 }
@@ -222,28 +212,28 @@
           final size = sizeProvider();
           return size == null
               ? GaDevToolsButton(
-                  icon: Icons.call_made,
-                  label: 'Request',
-                  outlined: false,
-                  gaScreen: gac.vmTools,
-                  gaSelection: gac.requestSize,
-                  onPressed: requestFunction,
-                )
+                icon: Icons.call_made,
+                label: 'Request',
+                outlined: false,
+                gaScreen: gac.vmTools,
+                gaSelection: gac.requestSize,
+                onPressed: requestFunction,
+              )
               : Row(
-                  mainAxisAlignment: MainAxisAlignment.end,
-                  children: [
-                    Text(
-                      size.valueAsString == null
-                          ? '--'
-                          : prettyPrintBytes(
-                              int.parse(size.valueAsString!),
-                              includeUnit: true,
-                              maxBytes: 512,
-                            )!,
-                    ),
-                    ToolbarRefresh(onPressed: requestFunction),
-                  ],
-                );
+                mainAxisAlignment: MainAxisAlignment.end,
+                children: [
+                  Text(
+                    size.valueAsString == null
+                        ? '--'
+                        : prettyPrintBytes(
+                          int.parse(size.valueAsString!),
+                          includeUnit: true,
+                          maxBytes: 512,
+                        )!,
+                  ),
+                  ToolbarRefresh(onPressed: requestFunction),
+                ],
+              );
         }
       },
     );
@@ -292,9 +282,7 @@
     final theme = Theme.of(context);
     return Card(
       child: ListTileTheme(
-        data: ListTileTheme.of(context).copyWith(
-          dense: true,
-        ),
+        data: ListTileTheme.of(context).copyWith(dense: true),
         child: Theme(
           // Prevents divider lines appearing at the top and bottom of the
           // expanded ExpansionTile.
@@ -347,10 +335,7 @@
       for (int i = 0; i < elements.length; ++i)
         Row(
           children: [
-            Text(
-              '[$i]: ',
-              style: theme.subtleFixedFontStyle,
-            ),
+            Text('[$i]: ', style: theme.subtleFixedFontStyle),
             VmServiceObjectLink(
               object: elements[i],
               onTap: controller.findAndSelectNodeForObject,
@@ -383,12 +368,10 @@
     return ValueListenableBuilder<RetainingPath?>(
       valueListenable: retainingPath,
       builder: (context, retainingPath, _) {
-        final retainingObjects = retainingPath == null
-            ? const <Widget>[]
-            : _retainingPathList(
-                context,
-                retainingPath,
-              );
+        final retainingObjects =
+            retainingPath == null
+                ? const <Widget>[]
+                : _retainingPathList(context, retainingPath);
         return VmExpansionTile(
           title: 'Retaining Path',
           onExpanded: onExpanded,
@@ -396,11 +379,11 @@
             retainingPath == null
                 ? const SizedCircularProgressIndicator()
                 : SizedBox.fromSize(
-                    size: Size.fromHeight(
-                      retainingObjects.length * defaultRowHeight + densePadding,
-                    ),
-                    child: Column(children: retainingObjects),
+                  size: Size.fromHeight(
+                    retainingObjects.length * defaultRowHeight + densePadding,
                   ),
+                  child: Column(children: retainingObjects),
+                ),
           ],
         );
       },
@@ -419,25 +402,19 @@
     }
 
     final theme = Theme.of(context);
-    final emptyList = Text(
-      'No retaining objects',
-      style: theme.fixedFontStyle,
-    );
+    final emptyList = Text('No retaining objects', style: theme.fixedFontStyle);
     if (retainingPath.elements == null) return [emptyList];
 
-    final firstRetainingObject = retainingPath.elements!.isNotEmpty
-        ? VmServiceObjectLink(
-            object: retainingPath.elements!.first.value,
-            onTap: onTap,
-          )
-        : emptyList;
+    final firstRetainingObject =
+        retainingPath.elements!.isNotEmpty
+            ? VmServiceObjectLink(
+              object: retainingPath.elements!.first.value,
+              onTap: onTap,
+            )
+            : emptyList;
 
     final retainingObjects = [
-      Row(
-        children: [
-          firstRetainingObject,
-        ],
-      ),
+      Row(children: [firstRetainingObject]),
       if (retainingPath.elements!.length > 1)
         for (final object in retainingPath.elements!.sublist(1))
           Row(
@@ -498,19 +475,21 @@
         text: TextSpan(
           children: [
             const TextSpan(text: 'Retained by element at ['),
-            VmServiceObjectLink(object: object.parentMapKey, onTap: onTap)
-                .buildTextSpan(context),
+            VmServiceObjectLink(
+              object: object.parentMapKey,
+              onTap: onTap,
+            ).buildTextSpan(context),
             const TextSpan(text: '] of '),
-            VmServiceObjectLink(object: object.value, onTap: onTap)
-                .buildTextSpan(context),
+            VmServiceObjectLink(
+              object: object.value,
+              onTap: onTap,
+            ).buildTextSpan(context),
           ],
         ),
       );
     }
 
-    final entries = <TextSpan>[
-      const TextSpan(text: 'Retained by '),
-    ];
+    final entries = <TextSpan>[const TextSpan(text: 'Retained by ')];
 
     if (object.parentField is int) {
       assert((object.value as InstanceRef).kind == InstanceKind.kRecord);
@@ -521,31 +500,23 @@
 
     if (object.value is FieldRef) {
       final field = object.value as FieldRef;
-      entries.addAll(
-        [
-          VmServiceObjectLink(
-            object: field.declaredType,
-            onTap: onTap,
-          ).buildTextSpan(context),
-          const TextSpan(text: ' '),
-          VmServiceObjectLink(
-            object: field,
-            onTap: onTap,
-          ).buildTextSpan(context),
-          const TextSpan(text: ' of '),
-          VmServiceObjectLink(
-            object: field.owner,
-            onTap: onTap,
-          ).buildTextSpan(context),
-        ],
-      );
+      entries.addAll([
+        VmServiceObjectLink(
+          object: field.declaredType,
+          onTap: onTap,
+        ).buildTextSpan(context),
+        const TextSpan(text: ' '),
+        VmServiceObjectLink(object: field, onTap: onTap).buildTextSpan(context),
+        const TextSpan(text: ' of '),
+        VmServiceObjectLink(
+          object: field.owner,
+          onTap: onTap,
+        ).buildTextSpan(context),
+      ]);
     } else if (object.value is FuncRef) {
       final func = object.value as FuncRef;
       entries.add(
-        VmServiceObjectLink(
-          object: func,
-          onTap: onTap,
-        ).buildTextSpan(context),
+        VmServiceObjectLink(object: func, onTap: onTap).buildTextSpan(context),
       );
     } else {
       entries.add(
@@ -555,9 +526,7 @@
         ).buildTextSpan(context),
       );
     }
-    return RichText(
-      text: TextSpan(children: entries),
-    );
+    return RichText(text: TextSpan(children: entries));
   }
 }
 
@@ -588,10 +557,11 @@
             builder: (context, references, _) {
               return TreeView<InboundReferencesTreeNode>(
                 dataRootsListenable: object.inboundReferencesTree,
-                dataDisplayProvider: (node, _) => InboundReferenceWidget(
-                  controller: controller,
-                  node: node,
-                ),
+                dataDisplayProvider:
+                    (node, _) => InboundReferenceWidget(
+                      controller: controller,
+                      node: node,
+                    ),
                 emptyTreeViewBuilder: () {
                   return Padding(
                     padding: EdgeInsets.all(defaultRowHeight / 2),
@@ -625,9 +595,7 @@
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
-    final rowContent = <Widget>[
-      const Text('Referenced by '),
-    ];
+    final rowContent = <Widget>[const Text('Referenced by ')];
 
     final parentField = node.ref.parentField;
     if (parentField != null) {
@@ -647,9 +615,7 @@
     );
     return DefaultTextStyle(
       style: theme.regularTextStyle,
-      child: Row(
-        children: rowContent,
-      ),
+      child: Row(children: rowContent),
     );
   }
 }
@@ -669,10 +635,7 @@
   final FutureOr<void> Function(ObjRef) onTap;
 
   @visibleForTesting
-  static String? defaultTextBuilder(
-    Object? object, {
-    bool preferUri = false,
-  }) {
+  static String? defaultTextBuilder(Object? object, {bool preferUri = false}) {
     if (object == null) return null;
     return switch (object) {
       FieldRef(:final name) ||
@@ -725,7 +688,8 @@
   TextSpan buildTextSpan(BuildContext context) {
     final theme = Theme.of(context);
 
-    String? text = textBuilder?.call(object) ??
+    String? text =
+        textBuilder?.call(object) ??
         defaultTextBuilder(object, preferUri: preferUri);
 
     // Sentinels aren't objects that can be inspected.
@@ -741,15 +705,16 @@
     return TextSpan(
       text: text,
       style: style.apply(overflow: TextOverflow.ellipsis),
-      recognizer: isServiceObject
-          ? (TapGestureRecognizer()
-            ..onTap = () async {
-              final obj = object;
-              if (obj is ObjRef) {
-                await onTap(obj);
-              }
-            })
-          : null,
+      recognizer:
+          isServiceObject
+              ? (TapGestureRecognizer()
+                ..onTap = () async {
+                  final obj = object;
+                  if (obj is ObjRef) {
+                    await onTap(obj);
+                  }
+                })
+              : null,
     );
   }
 
@@ -757,9 +722,7 @@
   Widget build(BuildContext context) {
     return RichText(
       maxLines: 1,
-      text: TextSpan(
-        children: [buildTextSpan(context)],
-      ),
+      text: TextSpan(children: [buildTextSpan(context)]),
     );
   }
 }
@@ -849,11 +812,7 @@
 MapEntry<String, WidgetBuilder> shallowSizeRowBuilder(VmObject object) {
   return selectableTextBuilderMapEntry(
     'Shallow Size',
-    prettyPrintBytes(
-      object.obj.size ?? 0,
-      includeUnit: true,
-      maxBytes: 512,
-    ),
+    prettyPrintBytes(object.obj.size ?? 0, includeUnit: true, maxBytes: 512),
   );
 }
 
@@ -1011,9 +970,8 @@
               location.endTokenPos != null) {
             final script = currentParsedScript.script;
             final startLine = location.line!;
-            final endLine = script.getLineNumberFromTokenPos(
-              location.endTokenPos!,
-            )!;
+            final endLine =
+                script.getLineNumberFromTokenPos(location.endTokenPos!)!;
             lineRange = LineRange(startLine, endLine);
           }
         }
@@ -1022,9 +980,7 @@
           axis: Axis.vertical,
           initialFractions: const [0.5, 0.5],
           children: [
-            OutlineDecoration.onlyBottom(
-              child: widget.child,
-            ),
+            OutlineDecoration.onlyBottom(child: widget.child),
             Column(
               children: [
                 const AreaPaneHeader(
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_controller.dart
index a2e77c9..af05699 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_controller.dart
@@ -11,7 +11,7 @@
   VMDeveloperToolsController({
     ObjectInspectorViewController? objectInspectorViewController,
   }) : objectInspectorViewController =
-            objectInspectorViewController ?? ObjectInspectorViewController();
+           objectInspectorViewController ?? ObjectInspectorViewController();
 
   ValueListenable<int> get selectedIndex => _selectedIndex;
   final _selectedIndex = ValueNotifier<int>(0);
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_screen.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_screen.dart
index 69370c3..4e57c9c 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_screen.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_tools_screen.dart
@@ -7,8 +7,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
-import '../../shared/screen.dart';
-import '../../shared/utils.dart';
+import '../../shared/framework/screen.dart';
+import '../../shared/utils/utils.dart';
 import 'isolate_statistics/isolate_statistics_view.dart';
 import 'object_inspector/object_inspector_view.dart';
 import 'process_memory/process_memory_view.dart';
@@ -16,10 +16,7 @@
 import 'vm_statistics/vm_statistics_view.dart';
 
 abstract class VMDeveloperView {
-  const VMDeveloperView({
-    required this.title,
-    required this.icon,
-  });
+  const VMDeveloperView({required this.title, required this.icon});
 
   /// The user-facing name of the page.
   final String title;
@@ -68,8 +65,10 @@
 class _VMDeveloperToolsScreenState extends State<VMDeveloperToolsScreenBody>
     with
         AutoDisposeMixin,
-        ProvidedControllerMixin<VMDeveloperToolsController,
-            VMDeveloperToolsScreenBody> {
+        ProvidedControllerMixin<
+          VMDeveloperToolsController,
+          VMDeveloperToolsScreenBody
+        > {
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
@@ -98,9 +97,7 @@
               ),
             Expanded(
               child: Padding(
-                padding: const EdgeInsets.only(
-                  left: defaultSpacing,
-                ),
+                padding: const EdgeInsets.only(left: defaultSpacing),
                 child: IndexedStack(
                   index: selectedIndex,
                   children: [
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
index 7802964..50f42dc 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
@@ -79,15 +79,12 @@
     required this.externalSize,
   });
 
-  const HeapStats.empty()
-      : count = 0,
-        size = 0,
-        externalSize = 0;
+  const HeapStats.empty() : count = 0, size = 0, externalSize = 0;
 
   HeapStats.parse(List<int> stats)
-      : count = stats[0],
-        size = stats[1],
-        externalSize = stats[2];
+    : count = stats[0],
+      size = stats[1],
+      externalSize = stats[2];
 
   final int count;
   final int size;
@@ -100,12 +97,14 @@
   static const _newSpaceKey = '_new';
   static const _oldSpaceKey = '_old';
 
-  HeapStats get newSpace => json!.containsKey(_newSpaceKey)
-      ? HeapStats.parse((json![_newSpaceKey] as List).cast<int>())
-      : const HeapStats.empty();
-  HeapStats get oldSpace => json!.containsKey(_oldSpaceKey)
-      ? HeapStats.parse((json![_oldSpaceKey] as List).cast<int>())
-      : const HeapStats.empty();
+  HeapStats get newSpace =>
+      json!.containsKey(_newSpaceKey)
+          ? HeapStats.parse((json![_newSpaceKey] as List).cast<int>())
+          : const HeapStats.empty();
+  HeapStats get oldSpace =>
+      json!.containsKey(_oldSpaceKey)
+          ? HeapStats.parse((json![_oldSpaceKey] as List).cast<int>())
+          : const HeapStats.empty();
 }
 
 extension AllocationProfilePrivateViewExtension on AllocationProfile {
@@ -114,14 +113,14 @@
   static const oldSpaceKey = 'old';
 
   GCStats get newSpaceGCStats => GCStats.parse(
-        heap: HeapGeneration.newSpace.toString(),
-        json: (_heaps[newSpaceKey] as Map).cast<String, Object?>(),
-      );
+    heap: HeapGeneration.newSpace.toString(),
+    json: (_heaps[newSpaceKey] as Map).cast<String, Object?>(),
+  );
 
   GCStats get oldSpaceGCStats => GCStats.parse(
-        heap: HeapGeneration.oldSpace.toString(),
-        json: (_heaps[oldSpaceKey] as Map).cast<String, Object?>(),
-      );
+    heap: HeapGeneration.oldSpace.toString(),
+    json: (_heaps[oldSpaceKey] as Map).cast<String, Object?>(),
+  );
 
   Map<String, Object?> get _heaps =>
       (json![heapsKey] as Map).cast<String, Object?>();
@@ -132,8 +131,8 @@
     final collections = newSpace.collections + oldSpace.collections;
     final averageCollectionTime =
         ((newSpace.collections * newSpace.averageCollectionTime) +
-                (oldSpace.collections * oldSpace.averageCollectionTime)) /
-            collections;
+            (oldSpace.collections * oldSpace.averageCollectionTime)) /
+        collections;
     return GCStats(
       heap: HeapGeneration.total.toString(),
       usage: newSpace.usage + oldSpace.usage,
@@ -205,17 +204,10 @@
 /// not prevent referenced objects from being collected when all other
 /// references to the object disappear.
 class WeakArrayRef implements ObjRef {
-  WeakArrayRef({
-    required this.id,
-    required this.json,
-    required this.length,
-  });
+  WeakArrayRef({required this.id, required this.json, required this.length});
 
-  factory WeakArrayRef.fromJson(Map<String, dynamic> json) => WeakArrayRef(
-        id: json['id'],
-        json: json,
-        length: json['length'],
-      );
+  factory WeakArrayRef.fromJson(Map<String, dynamic> json) =>
+      WeakArrayRef(id: json['id'], json: json, length: json['length']);
 
   @override
   bool? fixedId;
@@ -252,14 +244,14 @@
   });
 
   factory WeakArray.fromJson(Map<String, dynamic> json) => WeakArray(
-        id: json['id'],
-        json: json,
-        length: json['length'],
-        size: json['size'],
-        elements: (createServiceObject(json['elements'], []) as List)
-            .cast<Response?>(),
-        classRef: createServiceObject(json['class'], [])! as ClassRef,
-      );
+    id: json['id'],
+    json: json,
+    length: json['length'],
+    size: json['size'],
+    elements:
+        (createServiceObject(json['elements'], []) as List).cast<Response?>(),
+    classRef: createServiceObject(json['class'], [])! as ClassRef,
+  );
 
   final List<Response?> elements;
 
@@ -278,16 +270,10 @@
 
 /// A partially-populated representation of the Dart VM's subtype test cache.
 class SubtypeTestCacheRef implements ObjRef {
-  SubtypeTestCacheRef({
-    required this.id,
-    required this.json,
-  });
+  SubtypeTestCacheRef({required this.id, required this.json});
 
   factory SubtypeTestCacheRef.fromJson(Map<String, dynamic> json) =>
-      SubtypeTestCacheRef(
-        id: json['id'],
-        json: json,
-      );
+      SubtypeTestCacheRef(id: json['id'], json: json);
 
   @override
   bool? fixedId;
@@ -348,11 +334,11 @@
   });
 
   factory ICDataRef.fromJson(Map<String, dynamic> json) => ICDataRef(
-        id: json['id'],
-        owner: createServiceObject(json['_owner'], []) as ObjRef,
-        selector: json['_selector'],
-        json: json,
-      );
+    id: json['id'],
+    owner: createServiceObject(json['_owner'], []) as ObjRef,
+    selector: json['_selector'],
+    json: json,
+  );
 
   final ObjRef owner;
   final String selector;
@@ -391,17 +377,16 @@
   }) : super();
 
   factory ICData.fromJson(Map<String, dynamic> json) => ICData(
-        id: json['id'],
-        owner: createServiceObject(json['_owner'], []) as ObjRef,
-        selector: json['_selector'],
-        classRef: createServiceObject(json['class'], []) as ClassRef,
-        size: json['size'],
-        argumentsDescriptor:
-            createServiceObject(json['_argumentsDescriptor'], [])!
-                as InstanceRef,
-        entries: createServiceObject(json['_entries'], [])! as InstanceRef,
-        json: json,
-      );
+    id: json['id'],
+    owner: createServiceObject(json['_owner'], []) as ObjRef,
+    selector: json['_selector'],
+    classRef: createServiceObject(json['class'], []) as ClassRef,
+    size: json['size'],
+    argumentsDescriptor:
+        createServiceObject(json['_argumentsDescriptor'], [])! as InstanceRef,
+    entries: createServiceObject(json['_entries'], [])! as InstanceRef,
+    json: json,
+  );
 
   @override
   ClassRef? classRef;
@@ -416,17 +401,18 @@
 /// A single assembly instruction from a [Func]'s generated code disassembly.
 class Instruction {
   Instruction.parse(List data)
-      : address = data[0],
-        unknown = data[1],
-        instruction = data[2] {
+    : address = data[0],
+      unknown = data[1],
+      instruction = data[2] {
     if (data[3] == null) {
       object = null;
       return;
     }
     final rawObject = data[3] as Map<String, dynamic>;
-    object = (rawObject['type'] as List).contains('Instance')
-        ? InstanceRef.parse(rawObject)
-        : createServiceObject(data[3], const <String>[]) as Response?;
+    object =
+        (rawObject['type'] as List).contains('Instance')
+            ? InstanceRef.parse(rawObject)
+            : createServiceObject(data[3], const <String>[]) as Response?;
   }
 
   /// The instruction's address in memory.
@@ -445,12 +431,7 @@
   /// The Dart object this instruction is acting upon directly.
   late final Response? object;
 
-  List toJson() => [
-        address,
-        unknown,
-        instruction,
-        object?.json,
-      ];
+  List toJson() => [address, unknown, instruction, object?.json];
 }
 
 /// The full disassembly of generated [Code] for a function.
@@ -466,9 +447,7 @@
   /// The list of [Instructions] that make up the generated code.
   final instructions = <Instruction>[];
 
-  List toJson() => [
-        for (final i in instructions) ...i.toJson(),
-      ];
+  List toJson() => [for (final i in instructions) ...i.toJson()];
 }
 
 /// An extension on [Func] which allows for access to VM internal fields.
@@ -551,10 +530,7 @@
     final camelCase = RegExp(r'(?<=[a-z])[A-Z]');
 
     description.write(
-      kind().replaceAllMapped(
-        camelCase,
-        (Match m) => ' ${m.group(0)!}',
-      ),
+      kind().replaceAllMapped(camelCase, (Match m) => ' ${m.group(0)!}'),
     );
 
     return description.toString();
@@ -597,10 +573,11 @@
   factory InliningData.fromJson(Map<String, dynamic> json) {
     final startAddress = int.parse(json[kStartAddressKey], radix: 16);
     final intervals = (json[kInlinedIntervals] as List).cast<List>();
-    final functions = (json[kInlinedFunctions] as List)
-        .cast<Map<String, dynamic>>()
-        .map<FuncRef>((e) => FuncRef.parse(e)!)
-        .toList();
+    final functions =
+        (json[kInlinedFunctions] as List)
+            .cast<Map<String, dynamic>>()
+            .map<FuncRef>((e) => FuncRef.parse(e)!)
+            .toList();
 
     final entries = <InliningEntry>[];
 
@@ -618,10 +595,7 @@
         for (int i = 3; i < interval.length; ++i) functions[interval[i]],
       ];
       entries.add(
-        InliningEntry(
-          addressRange: range,
-          functions: inlinedFunctions,
-        ),
+        InliningEntry(addressRange: range, functions: inlinedFunctions),
       );
     }
 
@@ -639,10 +613,7 @@
 }
 
 class InliningEntry {
-  const InliningEntry({
-    required this.addressRange,
-    required this.functions,
-  });
+  const InliningEntry({required this.addressRange, required this.functions});
 
   final Range addressRange;
   final List<FuncRef> functions;
@@ -660,11 +631,8 @@
   static const _idKey = 'id';
   static const _lengthKey = 'length';
 
-  static ObjectPoolRef parse(Map<String, dynamic> json) => ObjectPoolRef(
-        id: json[_idKey],
-        length: json[_lengthKey],
-        json: json,
-      );
+  static ObjectPoolRef parse(Map<String, dynamic> json) =>
+      ObjectPoolRef(id: json[_idKey], length: json[_lengthKey], json: json);
 
   final int length;
 }
@@ -683,9 +651,10 @@
     return ObjectPool(
       json: json,
       id: json[ObjectPoolRef._idKey],
-      entries: (json[_entriesKey] as List)
-          .map((e) => ObjectPoolEntry.parse(e))
-          .toList(),
+      entries:
+          (json[_entriesKey] as List)
+              .map((e) => ObjectPoolEntry.parse(e))
+              .toList(),
       length: json[ObjectPoolRef._lengthKey],
     );
   }
@@ -749,10 +718,10 @@
   static const _valueKey = 'value';
 
   static ObjectPoolEntry parse(Map<String, dynamic> json) => ObjectPoolEntry(
-        offset: json[_offsetKey],
-        kind: ObjectPoolEntryKind.fromString(json[_kindKey]),
-        value: createServiceObject(json[_valueKey], [])!,
-      );
+    offset: json[_offsetKey],
+    kind: ObjectPoolEntryKind.fromString(json[_kindKey]),
+    value: createServiceObject(json[_valueKey], [])!,
+  );
 
   final int offset;
 
@@ -774,9 +743,10 @@
           serviceConnection.serviceManager.isolateManager.selectedIsolate.value;
 
       return await service.getObject(
-        isolate!.id!,
-        guardClassData['id'] as String,
-      ) as Class;
+            isolate!.id!,
+            guardClassData['id'] as String,
+          )
+          as Class;
     }
 
     return null;
@@ -857,9 +827,7 @@
 /// collector, meaning that objects in the store will be considered live, even
 /// if they're not referenced anywhere else in the program.
 class ObjectStore {
-  const ObjectStore({
-    required this.fields,
-  });
+  const ObjectStore({required this.fields});
 
   static ObjectStore? parse(Map<String, dynamic>? json) {
     if (json?['type'] != '_ObjectStore') {
@@ -930,7 +898,8 @@
   }
 
   @override
-  String toString() => '[ProfileCode ' //
+  String toString() =>
+      '[ProfileCode ' //
       'kind: $kind, inclusiveTicks: $inclusiveTicks, exclusiveTicks: $exclusiveTicks, ' //
       'code: $code]';
 }
@@ -953,10 +922,11 @@
   }
 
   List<ProfileCode> get codes {
-    return _expando[this] ??= (json![_kCodesKey] as List)
-        .cast<Map<String, dynamic>>()
-        .map<ProfileCode>((e) => ProfileCode.parse(e)!)
-        .toList();
+    return _expando[this] ??=
+        (json![_kCodesKey] as List)
+            .cast<Map<String, dynamic>>()
+            .map<ProfileCode>((e) => ProfileCode.parse(e)!)
+            .toList();
   }
 }
 
@@ -988,17 +958,20 @@
   ProfileReportRange._fromJson(Script script, _ProfileReportRangeJson json) {
     final inclusiveTicks = json.inclusiveTicks;
     final exclusiveTicks = json.exclusiveTicks;
-    final lines = json.positions
-        .map<int>(
-          // It's possible to get a synthetic token position which will either
-          // be a negative value or a String (e.g., 'ParallelMove' or
-          // 'NoSource'). We'll just use -1 as a placeholder since we won't
-          // display anything for these tokens anyway.
-          (e) => e is int
-              ? script.getLineNumberFromTokenPos(e) ?? _kNoSourcePosition
-              : _kNoSourcePosition,
-        )
-        .toList();
+    final lines =
+        json.positions
+            .map<int>(
+              // It's possible to get a synthetic token position which will either
+              // be a negative value or a String (e.g., 'ParallelMove' or
+              // 'NoSource'). We'll just use -1 as a placeholder since we won't
+              // display anything for these tokens anyway.
+              (e) =>
+                  e is int
+                      ? script.getLineNumberFromTokenPos(e) ??
+                          _kNoSourcePosition
+                      : _kNoSourcePosition,
+            )
+            .toList();
     for (int i = 0; i < lines.length; ++i) {
       final line = lines[i];
       entries[line] = ProfileReportEntry(
@@ -1040,16 +1013,17 @@
 /// information for a given [Script].
 class ProfileReport {
   ProfileReport._fromJson(Script script, Map<String, dynamic> json)
-      : _profileRanges = (json['ranges'] as List)
-            .cast<Map<String, dynamic>>()
-            .where((e) => e.containsKey('profile'))
-            .map<ProfileReportRange>(
-              (e) => ProfileReportRange._fromJson(
-                script,
-                _ProfileReportRangeJson(e),
-              ),
-            )
-            .toList();
+    : _profileRanges =
+          (json['ranges'] as List)
+              .cast<Map<String, dynamic>>()
+              .where((e) => e.containsKey('profile'))
+              .map<ProfileReportRange>(
+                (e) => ProfileReportRange._fromJson(
+                  script,
+                  _ProfileReportRangeJson(e),
+                ),
+              )
+              .toList();
 
   List<ProfileReportRange> get profileRanges => _profileRanges;
   final List<ProfileReportRange> _profileRanges;
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view.dart
index ea21338..ef539a9 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view.dart
@@ -7,22 +7,18 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/common_widgets.dart';
 import '../../../shared/primitives/byte_utils.dart';
 import '../../../shared/primitives/utils.dart';
 import '../../../shared/table/table.dart';
 import '../../../shared/table/table_data.dart';
+import '../../../shared/ui/common_widgets.dart';
 import '../vm_developer_common_widgets.dart';
 import '../vm_developer_tools_screen.dart';
 import '../vm_service_private_extensions.dart';
 import 'vm_statistics_view_controller.dart';
 
 class VMStatisticsView extends VMDeveloperView {
-  const VMStatisticsView()
-      : super(
-          title: 'VM',
-          icon: Icons.devices,
-        );
+  const VMStatisticsView() : super(title: 'VM', icon: Icons.devices);
 
   @override
   Widget build(BuildContext context) => VMStatisticsViewBody();
@@ -47,10 +43,8 @@
         Expanded(
           child: ValueListenableBuilder(
             valueListenable: controller.refreshing,
-            builder: (context, _, __) {
-              return VMStatisticsWidget(
-                controller: controller,
-              );
+            builder: (context, _, _) {
+              return VMStatisticsWidget(controller: controller);
             },
           ),
         ),
@@ -73,15 +67,9 @@
           child: Column(
             children: [
               Expanded(
-                child: GeneralVMStatisticsWidget(
-                  controller: controller,
-                ),
+                child: GeneralVMStatisticsWidget(controller: controller),
               ),
-              Expanded(
-                child: ProcessStatisticsWidget(
-                  controller: controller,
-                ),
-              ),
+              Expanded(child: ProcessStatisticsWidget(controller: controller)),
             ],
           ),
         ),
@@ -89,11 +77,7 @@
           flex: 4,
           child: Column(
             children: [
-              Flexible(
-                child: IsolatesPreviewWidget(
-                  controller: controller,
-                ),
-              ),
+              Flexible(child: IsolatesPreviewWidget(controller: controller)),
               Flexible(
                 child: IsolatesPreviewWidget(
                   controller: controller,
@@ -136,16 +120,13 @@
             vm == null
                 ? null
                 : formatDateTime(
-                    DateTime.fromMillisecondsSinceEpoch(vm.startTime!),
-                  ),
+                  DateTime.fromMillisecondsSinceEpoch(vm.startTime!),
+                ),
           ),
           selectableTextBuilderMapEntry('Profiler Mode', vm?.profilerMode),
           selectableTextBuilderMapEntry(
             'Current Memory',
-            prettyPrintBytes(
-              vm?.currentMemory,
-              includeUnit: true,
-            ),
+            prettyPrintBytes(vm?.currentMemory, includeUnit: true),
           ),
         ],
       ),
@@ -186,24 +167,15 @@
           ),
           selectableTextBuilderMapEntry(
             'Max Memory (RSS)',
-            prettyPrintBytes(
-              vm?.maxRSS,
-              includeUnit: true,
-            ),
+            prettyPrintBytes(vm?.maxRSS, includeUnit: true),
           ),
           selectableTextBuilderMapEntry(
             'Current Memory (RSS)',
-            prettyPrintBytes(
-              vm?.currentRSS,
-              includeUnit: true,
-            ),
+            prettyPrintBytes(vm?.currentRSS, includeUnit: true),
           ),
           selectableTextBuilderMapEntry(
             'Zone Memory',
-            prettyPrintBytes(
-              vm?.nativeZoneMemoryUsage,
-              includeUnit: true,
-            ),
+            prettyPrintBytes(vm?.nativeZoneMemoryUsage, includeUnit: true),
           ),
         ],
       ),
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view_controller.dart
index 0c709f2..789b9f3 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics/vm_statistics_view_controller.dart
@@ -22,14 +22,10 @@
     final vm = await _service.getVM();
     _vm = vm;
     _isolates = await Future.wait<Isolate>(
-      vm.isolates!.map(
-        (i) => _service.getIsolate(i.id!),
-      ),
+      vm.isolates!.map((i) => _service.getIsolate(i.id!)),
     );
     _systemIsolates = await Future.wait<Isolate>(
-      vm.systemIsolates!.map(
-        (i) => _service.getIsolate(i.id!),
-      ),
+      vm.systemIsolates!.map((i) => _service.getIsolate(i.id!)),
     );
     _refreshing.value = false;
   }
diff --git a/packages/devtools_app/lib/src/shared/connected_app.dart b/packages/devtools_app/lib/src/service/connected_app/connected_app.dart
similarity index 94%
rename from packages/devtools_app/lib/src/shared/connected_app.dart
rename to packages/devtools_app/lib/src/service/connected_app/connected_app.dart
index 730a3b2..0034e4a 100644
--- a/packages/devtools_app/lib/src/shared/connected_app.dart
+++ b/packages/devtools_app/lib/src/service/connected_app/connected_app.dart
@@ -9,9 +9,9 @@
 import 'package:flutter/foundation.dart';
 import 'package:vm_service/vm_service.dart';
 
-import 'console/primitives/eval_history.dart';
-import 'diagnostics/dap_object_node.dart';
-import 'diagnostics/dart_object_node.dart';
+import '../../shared/console/primitives/eval_history.dart';
+import '../../shared/diagnostics/dap_object_node.dart';
+import '../../shared/diagnostics/dart_object_node.dart';
 
 /// Extension methods for the [ConnectedApp] class.
 ///
diff --git a/packages/devtools_app/lib/src/shared/connection_info.dart b/packages/devtools_app/lib/src/service/connected_app/connection_info.dart
similarity index 87%
rename from packages/devtools_app/lib/src/shared/connection_info.dart
rename to packages/devtools_app/lib/src/service/connected_app/connection_info.dart
index bd1a348..b7bc665 100644
--- a/packages/devtools_app/lib/src/shared/connection_info.dart
+++ b/packages/devtools_app/lib/src/service/connected_app/connection_info.dart
@@ -5,13 +5,13 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import 'analytics/constants.dart' as gac;
-import 'common_widgets.dart';
-import 'globals.dart';
-import 'primitives/utils.dart';
-import 'routing.dart';
-import 'ui/utils.dart';
-import 'utils.dart';
+import '../../shared/analytics/constants.dart' as gac;
+import '../../shared/framework/routing.dart';
+import '../../shared/globals.dart';
+import '../../shared/primitives/utils.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../../shared/ui/utils.dart';
+import '../../shared/utils/utils.dart';
 
 class ConnectedAppSummary extends StatelessWidget {
   const ConnectedAppSummary({super.key, this.narrowView = true});
@@ -29,8 +29,10 @@
       return const SizedBox();
     }
 
-    final connectionDescriptionEntries =
-        generateDeviceDescription(vm, connectedApp);
+    final connectionDescriptionEntries = generateDeviceDescription(
+      vm,
+      connectedApp,
+    );
 
     // Ensure the screen is large enough to render two columns, even if
     // [narrowView] is false.
@@ -78,10 +80,7 @@
               mainAxisSize: MainAxisSize.min,
               children: [
                 Text('${entry.title}: ', style: boldText),
-                SelectableText(
-                  entry.description,
-                  style: theme.subtleTextStyle,
-                ),
+                SelectableText(entry.description, style: theme.subtleTextStyle),
                 if (entry.actions.isNotEmpty) ...entry.actions,
               ],
             ),
diff --git a/packages/devtools_app/lib/src/service/editor/api_classes.dart b/packages/devtools_app/lib/src/service/editor/api_classes.dart
index 0ed7188..7a7835c 100644
--- a/packages/devtools_app/lib/src/service/editor/api_classes.dart
+++ b/packages/devtools_app/lib/src/service/editor/api_classes.dart
@@ -52,7 +52,7 @@
   debugSessionStopped,
 
   /// The kind for a [ThemeChangedEvent].
-  themeChanged
+  themeChanged,
 }
 
 /// Constants for all fields used in JSON maps to avoid literal strings that
@@ -105,10 +105,11 @@
   DeviceAddedEvent({required this.device});
 
   DeviceAddedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          device:
-              EditorDevice.fromJson(map[Field.device] as Map<String, Object?>),
-        );
+    : this(
+        device: EditorDevice.fromJson(
+          map[Field.device] as Map<String, Object?>,
+        ),
+      );
 
   final EditorDevice device;
 
@@ -116,9 +117,7 @@
   EditorEventKind get kind => EditorEventKind.deviceAdded;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.device: device,
-      };
+  Map<String, Object?> toJson() => {Field.device: device};
 }
 
 /// An event sent by an editor when an existing device is updated.
@@ -129,10 +128,11 @@
   DeviceChangedEvent({required this.device});
 
   DeviceChangedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          device:
-              EditorDevice.fromJson(map[Field.device] as Map<String, Object?>),
-        );
+    : this(
+        device: EditorDevice.fromJson(
+          map[Field.device] as Map<String, Object?>,
+        ),
+      );
 
   final EditorDevice device;
 
@@ -140,9 +140,7 @@
   EditorEventKind get kind => EditorEventKind.deviceChanged;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.device: device,
-      };
+  Map<String, Object?> toJson() => {Field.device: device};
 }
 
 /// An event sent by an editor when a device is no longer available.
@@ -150,9 +148,7 @@
   DeviceRemovedEvent({required this.deviceId});
 
   DeviceRemovedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          deviceId: map[Field.deviceId] as String,
-        );
+    : this(deviceId: map[Field.deviceId] as String);
 
   final String deviceId;
 
@@ -160,9 +156,7 @@
   EditorEventKind get kind => EditorEventKind.deviceRemoved;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.deviceId: deviceId,
-      };
+  Map<String, Object?> toJson() => {Field.deviceId: deviceId};
 }
 
 /// An event sent by an editor when the current selected device was changed.
@@ -175,9 +169,7 @@
   DeviceSelectedEvent({required this.deviceId});
 
   DeviceSelectedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          deviceId: map[Field.deviceId] as String?,
-        );
+    : this(deviceId: map[Field.deviceId] as String?);
 
   /// The ID of the device being selected, or `null` if the current device is
   /// being unselected without a new device being selected.
@@ -187,9 +179,7 @@
   EditorEventKind get kind => EditorEventKind.deviceSelected;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.deviceId: deviceId,
-      };
+  Map<String, Object?> toJson() => {Field.deviceId: deviceId};
 }
 
 /// An event sent by an editor when a new debug session is started.
@@ -197,11 +187,11 @@
   DebugSessionStartedEvent({required this.debugSession});
 
   DebugSessionStartedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          debugSession: EditorDebugSession.fromJson(
-            map[Field.debugSession] as Map<String, Object?>,
-          ),
-        );
+    : this(
+        debugSession: EditorDebugSession.fromJson(
+          map[Field.debugSession] as Map<String, Object?>,
+        ),
+      );
 
   final EditorDebugSession debugSession;
 
@@ -209,9 +199,7 @@
   EditorEventKind get kind => EditorEventKind.debugSessionStarted;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.debugSession: debugSession,
-      };
+  Map<String, Object?> toJson() => {Field.debugSession: debugSession};
 }
 
 /// An event sent by an editor when a debug session is changed (for example the
@@ -220,11 +208,11 @@
   DebugSessionChangedEvent({required this.debugSession});
 
   DebugSessionChangedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          debugSession: EditorDebugSession.fromJson(
-            map[Field.debugSession] as Map<String, Object?>,
-          ),
-        );
+    : this(
+        debugSession: EditorDebugSession.fromJson(
+          map[Field.debugSession] as Map<String, Object?>,
+        ),
+      );
 
   final EditorDebugSession debugSession;
 
@@ -232,9 +220,7 @@
   EditorEventKind get kind => EditorEventKind.debugSessionChanged;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.debugSession: debugSession,
-      };
+  Map<String, Object?> toJson() => {Field.debugSession: debugSession};
 }
 
 /// An event sent by an editor when a debug session ends.
@@ -242,9 +228,7 @@
   DebugSessionStoppedEvent({required this.debugSessionId});
 
   DebugSessionStoppedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          debugSessionId: map[Field.debugSessionId] as String,
-        );
+    : this(debugSessionId: map[Field.debugSessionId] as String);
 
   final String debugSessionId;
 
@@ -252,20 +236,16 @@
   EditorEventKind get kind => EditorEventKind.debugSessionStopped;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.debugSessionId: debugSessionId,
-      };
+  Map<String, Object?> toJson() => {Field.debugSessionId: debugSessionId};
 }
 
 class ThemeChangedEvent extends EditorEvent {
   ThemeChangedEvent({required this.theme});
 
   ThemeChangedEvent.fromJson(Map<String, Object?> map)
-      : this(
-          theme: EditorTheme.fromJson(
-            map[Field.theme] as Map<String, Object?>,
-          ),
-        );
+    : this(
+        theme: EditorTheme.fromJson(map[Field.theme] as Map<String, Object?>),
+      );
 
   final EditorTheme theme;
 
@@ -273,57 +253,50 @@
   EditorEventKind get kind => EditorEventKind.themeChanged;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.theme: theme,
-      };
+  Map<String, Object?> toJson() => {Field.theme: theme};
 }
 
 /// The result of a `GetDevices` request.
 class GetDevicesResult with Serializable {
-  GetDevicesResult({
-    required this.devices,
-    required this.selectedDeviceId,
-  });
+  GetDevicesResult({required this.devices, required this.selectedDeviceId});
 
   GetDevicesResult.fromJson(Map<String, Object?> map)
-      : this(
-          devices: (map[Field.devices] as List<Object?>)
-              .cast<Map<String, Object?>>()
-              .map(EditorDevice.fromJson)
-              .toList(),
-          selectedDeviceId: map[Field.selectedDeviceId] as String?,
-        );
+    : this(
+        devices:
+            (map[Field.devices] as List<Object?>)
+                .cast<Map<String, Object?>>()
+                .map(EditorDevice.fromJson)
+                .toList(),
+        selectedDeviceId: map[Field.selectedDeviceId] as String?,
+      );
 
   final List<EditorDevice> devices;
   final String? selectedDeviceId;
 
   @override
   Map<String, Object?> toJson() => {
-        Field.devices: devices,
-        Field.selectedDeviceId: selectedDeviceId,
-      };
+    Field.devices: devices,
+    Field.selectedDeviceId: selectedDeviceId,
+  };
 }
 
 /// The result of a `GetDebugSessions` request.
 class GetDebugSessionsResult with Serializable {
-  GetDebugSessionsResult({
-    required this.debugSessions,
-  });
+  GetDebugSessionsResult({required this.debugSessions});
 
   GetDebugSessionsResult.fromJson(Map<String, Object?> map)
-      : this(
-          debugSessions: (map[Field.debugSessions] as List<Object?>)
-              .cast<Map<String, Object?>>()
-              .map(EditorDebugSession.fromJson)
-              .toList(),
-        );
+    : this(
+        debugSessions:
+            (map[Field.debugSessions] as List<Object?>)
+                .cast<Map<String, Object?>>()
+                .map(EditorDebugSession.fromJson)
+                .toList(),
+      );
 
   final List<EditorDebugSession> debugSessions;
 
   @override
-  Map<String, Object?> toJson() => {
-        Field.debugSessions: debugSessions,
-      };
+  Map<String, Object?> toJson() => {Field.debugSessions: debugSessions};
 }
 
 /// A debug session running in the editor.
@@ -339,15 +312,15 @@
   });
 
   EditorDebugSession.fromJson(Map<String, Object?> map)
-      : this(
-          id: map[Field.id] as String,
-          name: map[Field.name] as String,
-          vmServiceUri: map[Field.vmServiceUri] as String?,
-          flutterMode: map[Field.flutterMode] as String?,
-          flutterDeviceId: map[Field.flutterDeviceId] as String?,
-          debuggerType: map[Field.debuggerType] as String?,
-          projectRootPath: map[Field.projectRootPath] as String?,
-        );
+    : this(
+        id: map[Field.id] as String,
+        name: map[Field.name] as String,
+        vmServiceUri: map[Field.vmServiceUri] as String?,
+        flutterMode: map[Field.flutterMode] as String?,
+        flutterDeviceId: map[Field.flutterDeviceId] as String?,
+        debuggerType: map[Field.debuggerType] as String?,
+        projectRootPath: map[Field.projectRootPath] as String?,
+      );
 
   final String id;
   final String name;
@@ -359,14 +332,14 @@
 
   @override
   Map<String, Object?> toJson() => {
-        Field.id: id,
-        Field.name: name,
-        Field.vmServiceUri: vmServiceUri,
-        Field.flutterMode: flutterMode,
-        Field.flutterDeviceId: flutterDeviceId,
-        Field.debuggerType: debuggerType,
-        Field.projectRootPath: projectRootPath,
-      };
+    Field.id: id,
+    Field.name: name,
+    Field.vmServiceUri: vmServiceUri,
+    Field.flutterMode: flutterMode,
+    Field.flutterDeviceId: flutterDeviceId,
+    Field.debuggerType: debuggerType,
+    Field.projectRootPath: projectRootPath,
+  };
 }
 
 /// A device that is available in the editor.
@@ -384,17 +357,17 @@
   });
 
   EditorDevice.fromJson(Map<String, Object?> map)
-      : this(
-          id: map[Field.id] as String,
-          name: map[Field.name] as String,
-          category: map[Field.category] as String?,
-          emulator: map[Field.emulator] as bool,
-          emulatorId: map[Field.emulatorId] as String?,
-          ephemeral: map[Field.ephemeral] as bool,
-          platform: map[Field.platform] as String,
-          platformType: map[Field.platformType] as String?,
-          supported: map[Field.supported] as bool,
-        );
+    : this(
+        id: map[Field.id] as String,
+        name: map[Field.name] as String,
+        category: map[Field.category] as String?,
+        emulator: map[Field.emulator] as bool,
+        emulatorId: map[Field.emulatorId] as String?,
+        ephemeral: map[Field.ephemeral] as bool,
+        platform: map[Field.platform] as String,
+        platformType: map[Field.platformType] as String?,
+        supported: map[Field.supported] as bool,
+      );
 
   final String id;
   final String name;
@@ -414,16 +387,16 @@
 
   @override
   Map<String, Object?> toJson() => {
-        Field.id: id,
-        Field.name: name,
-        Field.category: category,
-        Field.emulator: emulator,
-        Field.emulatorId: emulatorId,
-        Field.ephemeral: ephemeral,
-        Field.platform: platform,
-        Field.platformType: platformType,
-        Field.supported: supported,
-      };
+    Field.id: id,
+    Field.name: name,
+    Field.category: category,
+    Field.emulator: emulator,
+    Field.emulatorId: emulatorId,
+    Field.ephemeral: ephemeral,
+    Field.platform: platform,
+    Field.platformType: platformType,
+    Field.supported: supported,
+  };
 }
 
 /// UI settings for an editor's theme.
@@ -436,12 +409,12 @@
   });
 
   EditorTheme.fromJson(Map<String, Object?> map)
-      : this(
-          isDarkMode: map[Field.isDarkMode] as bool,
-          backgroundColor: map[Field.backgroundColor] as String?,
-          foregroundColor: map[Field.foregroundColor] as String?,
-          fontSize: map[Field.fontSize] as int?,
-        );
+    : this(
+        isDarkMode: map[Field.isDarkMode] as bool,
+        backgroundColor: map[Field.backgroundColor] as String?,
+        foregroundColor: map[Field.foregroundColor] as String?,
+        fontSize: map[Field.fontSize] as int?,
+      );
 
   final bool isDarkMode;
   final String? backgroundColor;
@@ -450,9 +423,9 @@
 
   @override
   Map<String, Object?> toJson() => {
-        Field.isDarkMode: isDarkMode,
-        Field.backgroundColor: backgroundColor,
-        Field.foregroundColor: foregroundColor,
-        Field.fontSize: fontSize,
-      };
+    Field.isDarkMode: isDarkMode,
+    Field.backgroundColor: backgroundColor,
+    Field.foregroundColor: foregroundColor,
+    Field.fontSize: fontSize,
+  };
 }
diff --git a/packages/devtools_app/lib/src/service/editor/editor_client.dart b/packages/devtools_app/lib/src/service/editor/editor_client.dart
index 48e0d3c..ffd2699 100644
--- a/packages/devtools_app/lib/src/service/editor/editor_client.dart
+++ b/packages/devtools_app/lib/src/service/editor/editor_client.dart
@@ -10,96 +10,19 @@
 import '../../shared/analytics/constants.dart';
 import 'api_classes.dart';
 
-/// An interface to services provided by an editor.
+/// A client wrapper that connects to an editor over DTD.
 ///
 /// Changes made to the editor services/events should be considered carefully to
 /// ensure they are not breaking changes to already-shipped editors.
-abstract class EditorClient extends DisposableController {
-  Future<void> close();
-
-  /// The ID to use for analytics events.
-  String get gaId;
-
-  /// Whether the connected editor supports the `getDevices` method.
-  bool get supportsGetDevices;
-
-  /// Whether the connected editor supports the `getDebugSessions` method.
-  bool get supportsGetDebugSessions;
-
-  /// Whether the connected editor supports the `selectDevice` method.
-  bool get supportsSelectDevice;
-
-  /// Whether the connected editor supports the `hotReload` method.
-  bool get supportsHotReload;
-
-  /// Whether the connected editor supports the `hotRestart` method.
-  bool get supportsHotRestart;
-
-  /// Whether the connected editor supports the `openDevToolsPage` method.
-  bool get supportsOpenDevToolsPage;
-
-  /// Whether the connected editor supports the `forceExternal` flag in the
-  /// params for `openDevToolsPage`.
-  bool get supportsOpenDevToolsForceExternal;
-
-  /// A stream of [EditorEvent]s from the editor.
-  Stream<EditorEvent> get event;
-
-  /// A stream of events of when editor service methods/capabilities change.
-  Stream<ServiceRegistrationChange> get editorServiceChanged;
-
-  /// Gets the set of currently available devices from the editor.
-  Future<GetDevicesResult> getDevices();
-
-  /// Gets the set of currently active debug sessions from the editor.
-  Future<GetDebugSessionsResult> getDebugSessions();
-
-  /// Requests the editor selects a specific device.
-  ///
-  /// It should not be assumed that calling this method succeeds (if it does, a
-  /// `deviceSelected` event will provide the appropriate update).
-  Future<void> selectDevice(EditorDevice? device);
-
-  /// Requests the editor Hot Reloads the given debug session.
-  Future<void> hotReload(String debugSessionId);
-
-  /// Requests the editor Hot Restarts the given debug session.
-  Future<void> hotRestart(String debugSessionId);
-
-  /// Requests the editor opens a DevTools page for the given debug session.
-  Future<void> openDevToolsPage(
-    String? debugSessionId, {
-    String? page,
-    bool? forceExternal,
-    bool? requiresDebugSession,
-    bool? prefersDebugSession,
-  });
-
-  /// Requests the editor enables a new platform (for example by running
-  /// `flutter create` to add the native project files).
-  ///
-  /// This action may prompt the user so it should not be assumed that calling
-  /// this method succeeds (if it does, a `deviceChanged` event will provide
-  /// the appropriate updates).
-  Future<void> enablePlatformType(String platformType);
-}
-
-/// An implementation of [EditorClient] that connects to an editor over DTD.
-///
-/// Changes made to the editor services/events should be considered carefully to
-/// ensure they are not breaking changes to already-shipped editors.
-class DtdEditorClient extends EditorClient with AutoDisposeControllerMixin {
-  // TODO(dantup): Merge this into EditorClient once the postMessage version
-  //  is removed.
-
-  DtdEditorClient(this._dtd) {
+class EditorClient extends DisposableController
+    with AutoDisposeControllerMixin {
+  EditorClient(this._dtd) {
     unawaited(initialized); // Trigger async initialization.
   }
 
   final DartToolingDaemon _dtd;
   late final initialized = _initialize();
 
-  @override
   String get gaId => EditorSidebar.id;
 
   Future<void> _initialize() async {
@@ -137,16 +60,14 @@
           return;
         }
 
-        final info = isRegistered
-            ? ServiceRegistered(
-                service: service,
-                method: method,
-                capabilities: capabilities,
-              )
-            : ServiceUnregistered(
-                service: service,
-                method: method,
-              );
+        final info =
+            isRegistered
+                ? ServiceRegistered(
+                  service: service,
+                  method: method,
+                  capabilities: capabilities,
+                )
+                : ServiceUnregistered(service: service, method: method);
         _editorServiceChangedController.add(info);
       }),
     );
@@ -160,12 +81,15 @@
           // the rest.
           null => null,
           EditorEventKind.deviceAdded => DeviceAddedEvent.fromJson(data.data),
-          EditorEventKind.deviceRemoved =>
-            DeviceRemovedEvent.fromJson(data.data),
-          EditorEventKind.deviceChanged =>
-            DeviceChangedEvent.fromJson(data.data),
-          EditorEventKind.deviceSelected =>
-            DeviceSelectedEvent.fromJson(data.data),
+          EditorEventKind.deviceRemoved => DeviceRemovedEvent.fromJson(
+            data.data,
+          ),
+          EditorEventKind.deviceChanged => DeviceChangedEvent.fromJson(
+            data.data,
+          ),
+          EditorEventKind.deviceSelected => DeviceSelectedEvent.fromJson(
+            data.data,
+          ),
           EditorEventKind.debugSessionStarted =>
             DebugSessionStartedEvent.fromJson(data.data),
           EditorEventKind.debugSessionChanged =>
@@ -191,59 +115,47 @@
   }
 
   /// Close the connection to DTD.
-  @override
   Future<void> close() => _dtd.close();
 
-  @override
   bool get supportsGetDevices => _supportsGetDevices;
   var _supportsGetDevices = false;
 
-  @override
   bool get supportsGetDebugSessions => _supportsGetDebugSessions;
   var _supportsGetDebugSessions = false;
 
-  @override
   bool get supportsSelectDevice => _supportsSelectDevice;
   var _supportsSelectDevice = false;
 
-  @override
   bool get supportsHotReload => _supportsHotReload;
   var _supportsHotReload = false;
 
-  @override
   bool get supportsHotRestart => _supportsHotRestart;
   var _supportsHotRestart = false;
 
-  @override
   bool get supportsOpenDevToolsPage => _supportsOpenDevToolsPage;
   var _supportsOpenDevToolsPage = false;
 
-  @override
   bool get supportsOpenDevToolsForceExternal =>
       _supportsOpenDevToolsForceExternal;
   var _supportsOpenDevToolsForceExternal = false;
 
   /// A stream of [EditorEvent]s from the editor.
-  @override
   Stream<EditorEvent> get event => _eventController.stream;
   final _eventController = StreamController<EditorEvent>();
 
   /// A stream of events of when editor services are registrered or
   /// unregistered.
-  @override
   Stream<ServiceRegistrationChange> get editorServiceChanged =>
       _editorServiceChangedController.stream;
   final _editorServiceChangedController =
       StreamController<ServiceRegistrationChange>();
 
-  @override
   Future<GetDevicesResult> getDevices() async {
     final response = await _call(EditorMethod.getDevices);
     return GetDevicesResult.fromJson(response.result);
   }
 
   /// Gets the set of currently active debug sessions from the editor.
-  @override
   Future<GetDebugSessionsResult> getDebugSessions() async {
     final response = await _call(EditorMethod.getDebugSessions);
     return GetDebugSessionsResult.fromJson(response.result);
@@ -253,7 +165,6 @@
   ///
   /// It should not be assumed that calling this method succeeds (if it does, a
   /// `deviceSelected` event will provide the appropriate update).
-  @override
   Future<void> selectDevice(EditorDevice? device) async {
     await _call(
       EditorMethod.selectDevice,
@@ -261,7 +172,6 @@
     );
   }
 
-  @override
   Future<void> hotReload(String debugSessionId) async {
     await _call(
       EditorMethod.hotReload,
@@ -269,7 +179,6 @@
     );
   }
 
-  @override
   Future<void> hotRestart(String debugSessionId) async {
     await _call(
       EditorMethod.hotRestart,
@@ -277,7 +186,6 @@
     );
   }
 
-  @override
   Future<void> openDevToolsPage(
     String? debugSessionId, {
     String? page,
@@ -297,7 +205,6 @@
     );
   }
 
-  @override
   Future<void> enablePlatformType(String platformType) async {
     await _call(
       EditorMethod.enablePlatformType,
@@ -309,11 +216,7 @@
     EditorMethod method, {
     Map<String, Object?>? params,
   }) {
-    return _dtd.call(
-      editorServiceName,
-      method.name,
-      params: params,
-    );
+    return _dtd.call(editorServiceName, method.name, params: params);
   }
 }
 
@@ -341,8 +244,5 @@
 
 /// Represents a service method that was unregistered.
 class ServiceUnregistered extends ServiceRegistrationChange {
-  ServiceUnregistered({
-    required super.service,
-    required super.method,
-  });
+  ServiceUnregistered({required super.service, required super.method});
 }
diff --git a/packages/devtools_app/lib/src/service/editor/readme.md b/packages/devtools_app/lib/src/service/editor/readme.md
deleted file mode 100644
index d8b1fe1..0000000
--- a/packages/devtools_app/lib/src/service/editor/readme.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# DTD Editor Service
-
-An editor (such as VS Code or IntelliJ) can provide services to be used by other
-tools (such as the DevTools sidebar).
-
-The original version of the sidebar in VS Code used `postMessage` but the goal
-is to move to DTD and deprecate the `postMessage` support. For the transition
-the DevTools sidebar will support both.
-
-```mermaid
-flowchart LR
-    subgraph DevTools["`DevTools / Client Tool`"]
-        EditorClient
-        PostMessageEditorClient
-    end
-    subgraph Editor["`Editor / Mock Editor / Tests`"]
-        SimulatedEditorBase
-        PostMessageSimulatedEditor
-    end
-    EditorClient <--> DTD <--> SimulatedEditorBase
-    PostMessageEditorClient <--> postMessage <--> PostMessageSimulatedEditor
-```
-
-- `EditorClient` classes are used by the client tool (DevTools sidebar) to
-  interact with the services provided by an editor in a strongly-typed way.
-- `SimulatedEditor` classes are used by the editor to provide those services in
-  a strongly-typed way.
-- `SimulatedEditorMixin` is a helper mixin for `SimulatedEditorBase` classes
-  that provide some mock editor functionality (such as the ability to connect
-  and disconnect mock devices and start debug sessions). This is used by the
-  "mock editor" Stager scene to allow testing/developing the sidebar without a
-  real editor.
-- `_VsCodeConnectedPanel` is the sidebar implementation that works with the
-  generic `EditorClient` class so it can work against either transport channel.
-
-
-## postMessage Implementation
-
-- `PostMessageEditorClient` is an implementation of `EditorClient` that
-  communicates over `postMessage`.
-- `PostMessageSimulatedEditor` is an implementation of `SimulatedEditorBase`
-  (that mixes in `SimulatedEditorMixin`) that communicates over `postMessage` streams.
-- `VsCodePostMessageSidebarPanel` is a widget that wraps `_VsCodeConnectedPanel`
-  and provides it with a `postMessage` implementation of `EditorClient`.
-
-
-## DTD Implementation
-
-(work in progress, will be the same as above but using DTD-based implementations
-of classes)
diff --git a/packages/devtools_app/lib/src/service/json_to_service_cache.dart b/packages/devtools_app/lib/src/service/json_to_service_cache.dart
index 83eccc2..017af80 100644
--- a/packages/devtools_app/lib/src/service/json_to_service_cache.dart
+++ b/packages/devtools_app/lib/src/service/json_to_service_cache.dart
@@ -20,10 +20,7 @@
   static final _kTrue = Instance(
     kind: InstanceKind.kBool,
     identityHashCode: -1,
-    classRef: ClassRef(
-      name: 'bool',
-      id: 'json-cache-bool',
-    ),
+    classRef: ClassRef(name: 'bool', id: 'json-cache-bool'),
     valueAsString: 'true',
     id: 'json-cache-true',
   );
@@ -31,10 +28,7 @@
   static final _kFalse = Instance(
     kind: InstanceKind.kBool,
     identityHashCode: -1,
-    classRef: ClassRef(
-      name: 'bool',
-      id: 'json-cache-bool',
-    ),
+    classRef: ClassRef(name: 'bool', id: 'json-cache-bool'),
     valueAsString: 'false',
     id: 'json-cache-false',
   );
@@ -42,10 +36,7 @@
   static final _kNull = Instance(
     kind: InstanceKind.kNull,
     identityHashCode: -1,
-    classRef: ClassRef(
-      name: 'Null',
-      id: 'json-cache-null-cls',
-    ),
+    classRef: ClassRef(name: 'Null', id: 'json-cache-null-cls'),
     id: 'json-cache-null',
   );
 
@@ -54,10 +45,7 @@
     id: 'json-cache-list-class',
   );
 
-  static final _kMapClass = ClassRef(
-    name: 'Map',
-    id: 'json-cache-map-class',
-  );
+  static final _kMapClass = ClassRef(name: 'Map', id: 'json-cache-map-class');
 
   /// The current number of non-constant elements in the cache.
   @visibleForTesting
@@ -71,11 +59,7 @@
   /// should be returned. If `offset` is provided, `count` must also be provided.
   /// The result will be an `Instance` containing `count` objects, starting from
   /// `offset`.
-  Instance? getObject({
-    required String objectId,
-    int? offset,
-    int? count,
-  }) {
+  Instance? getObject({required String objectId, int? offset, int? count}) {
     final obj = _cache[objectId];
     if (obj == null) return null;
     if (offset != null && count != null) {
@@ -126,8 +110,9 @@
       case InstanceKind.kMap:
         final map = <String, Object?>{};
         for (final association in instance.associations ?? <MapAssociation>[]) {
-          map[(association.key as Instance).valueAsString!] =
-              instanceToJson(association.value);
+          map[(association.key as Instance).valueAsString!] = instanceToJson(
+            association.value,
+          );
         }
         return map;
       case InstanceKind.kList:
@@ -209,9 +194,7 @@
       classRef: _kListClass,
       id: _nextId(),
     );
-    list.elements = <Instance>[
-      for (final e in json) insertJsonObject(e),
-    ];
+    list.elements = <Instance>[for (final e in json) insertJsonObject(e)];
     list.length = json.length;
     _cache[list.id!] = list;
     return list;
@@ -232,10 +215,7 @@
       instance = Instance(
         kind: InstanceKind.kString,
         identityHashCode: -1,
-        classRef: ClassRef(
-          name: 'String',
-          id: 'json-cache-string',
-        ),
+        classRef: ClassRef(name: 'String', id: 'json-cache-string'),
         id: _nextId(),
         valueAsString: json,
       );
@@ -243,10 +223,7 @@
       instance = Instance(
         kind: InstanceKind.kInt,
         identityHashCode: json,
-        classRef: ClassRef(
-          name: 'int',
-          id: 'json-cache-int',
-        ),
+        classRef: ClassRef(name: 'int', id: 'json-cache-int'),
         valueAsString: json.toString(),
         id: _nextId(),
       );
@@ -254,10 +231,7 @@
       instance = Instance(
         kind: InstanceKind.kDouble,
         identityHashCode: -1,
-        classRef: ClassRef(
-          name: 'double',
-          id: 'json-cache-double',
-        ),
+        classRef: ClassRef(name: 'double', id: 'json-cache-double'),
         valueAsString: json.toString(),
         id: _nextId(),
       );
diff --git a/packages/devtools_app/lib/src/service/service_extension_widgets.dart b/packages/devtools_app/lib/src/service/service_extension_widgets.dart
index 3394f16..1c4b0af 100644
--- a/packages/devtools_app/lib/src/service/service_extension_widgets.dart
+++ b/packages/devtools_app/lib/src/service/service_extension_widgets.dart
@@ -16,12 +16,12 @@
 
 import '../shared/analytics/analytics.dart' as ga;
 import '../shared/analytics/constants.dart' as gac;
-import '../shared/common_widgets.dart';
 import '../shared/constants.dart';
 import '../shared/globals.dart';
 import '../shared/primitives/message_bus.dart';
 import '../shared/primitives/utils.dart';
 import '../shared/ui/colors.dart';
+import '../shared/ui/common_widgets.dart';
 import '../shared/ui/hover.dart';
 import 'service_extensions.dart';
 import 'service_registrations.dart';
@@ -101,17 +101,15 @@
 
       // Track whether the extension is actually exposed by the VM.
       final listenable = serviceConnection
-          .serviceManager.serviceExtensionManager
+          .serviceManager
+          .serviceExtensionManager
           .hasServiceExtension(extensionName);
       extension.isAvailable = listenable.value;
-      addAutoDisposeListener(
-        listenable,
-        () {
-          setState(() {
-            extension.isAvailable = listenable.value;
-          });
-        },
-      );
+      addAutoDisposeListener(listenable, () {
+        setState(() {
+          extension.isAvailable = listenable.value;
+        });
+      });
     }
   }
 
@@ -164,12 +162,13 @@
         unawaited(
           serviceConnection.serviceManager.serviceExtensionManager
               .setServiceExtensionState(
-            extensionState.description.extension,
-            enabled: !wasSelected,
-            value: wasSelected
-                ? extensionState.description.disabledValue
-                : extensionState.description.enabledValue,
-          ),
+                extensionState.description.extension,
+                enabled: !wasSelected,
+                value:
+                    wasSelected
+                        ? extensionState.description.disabledValue
+                        : extensionState.description.enabledValue,
+              ),
         );
       });
     } else {
@@ -238,27 +237,27 @@
         // reload directly on the VM service (e.g. for Dart CLI apps).
         ? _HotReloadScaffoldAction()
         : DevToolsTooltip(
-            message: _hotReloadTooltip,
-            child: _RegisteredServiceExtensionButton._(
-              serviceDescription: hotReload,
-              action: _callHotReload,
-              completedText: 'Hot reload completed.',
-              describeError: (error) => 'Unable to hot reload the app: $error',
-            ),
-          );
+          message: _hotReloadTooltip,
+          child: _RegisteredServiceExtensionButton._(
+            serviceDescription: hotReload,
+            action: _callHotReload,
+            completedText: 'Hot reload completed.',
+            describeError: (error) => 'Unable to hot reload the app: $error',
+          ),
+        );
   }
 }
 
 class _HotReloadScaffoldAction extends ScaffoldAction {
   _HotReloadScaffoldAction()
-      : super(
-          icon: hotReloadIcon,
-          tooltip: HotReloadButton._hotReloadTooltip,
-          onPressed: (context) {
-            ga.select(gac.devToolsMain, gac.hotReload);
-            _callHotReload();
-          },
-        );
+    : super(
+        icon: hotReloadIcon,
+        tooltip: HotReloadButton._hotReloadTooltip,
+        onPressed: (context) {
+          ga.select(gac.devToolsMain, gac.hotReload);
+          _callHotReload();
+        },
+      );
 }
 
 Future<void> _callHotReload() {
@@ -374,16 +373,17 @@
     if (_hidden) return const SizedBox.shrink();
 
     return InkWell(
-      onTap: () => unawaited(
-        invokeAndCatchErrors(() async {
-          final gaScreenName = widget.serviceDescription.gaScreenName;
-          final gaItem = widget.serviceDescription.gaItem;
-          if (gaScreenName != null && gaItem != null) {
-            ga.select(gaScreenName, gaItem);
-          }
-          await widget.action();
-        }),
-      ),
+      onTap:
+          () => unawaited(
+            invokeAndCatchErrors(() async {
+              final gaScreenName = widget.serviceDescription.gaScreenName;
+              final gaItem = widget.serviceDescription.gaItem;
+              if (gaScreenName != null && gaItem != null) {
+                ga.select(gaScreenName, gaItem);
+              }
+              await widget.action();
+            }),
+          ),
       child: Container(
         constraints: BoxConstraints.tightFor(
           width: actionWidgetSize,
@@ -407,8 +407,8 @@
   Widget build(BuildContext context) {
     return _ServiceExtensionToggle(
       service: structuredErrors,
-      describeError: (error) =>
-          'Failed to update structuredError settings: $error',
+      describeError:
+          (error) => 'Failed to update structuredError settings: $error',
     );
   }
 }
@@ -420,9 +420,7 @@
   const _ServiceExtensionToggle({
     required this.service,
     required super.describeError,
-  }) : super(
-          completedText: null,
-        );
+  }) : super(completedText: null);
   final ToggleableServiceExtensionDescription service;
 
   @override
@@ -487,12 +485,13 @@
       invokeAndCatchErrors(() async {
         await serviceConnection.serviceManager.serviceExtensionManager
             .setServiceExtensionState(
-          widget.service.extension,
-          enabled: value,
-          value: value
-              ? widget.service.enabledValue
-              : widget.service.disabledValue,
-        );
+              widget.service.extension,
+              enabled: value,
+              value:
+                  value
+                      ? widget.service.enabledValue
+                      : widget.service.disabledValue,
+            );
       }),
     );
   }
@@ -507,12 +506,10 @@
     required this.serviceExtension,
     this.showDescription = true,
   }) : super(
-          completedText: null,
-          describeError: (error) => _errorMessage(
-            serviceExtension.extension,
-            error,
-          ),
-        );
+         completedText: null,
+         describeError:
+             (error) => _errorMessage(serviceExtension.extension, error),
+       );
 
   static String _errorMessage(String extensionName, Object? error) {
     return 'Failed to update $extensionName setting: $error';
@@ -562,16 +559,18 @@
       serviceConnection.serviceManager.serviceExtensionManager
           .waitForServiceExtensionAvailable(widget.serviceExtension.extension)
           .then((isServiceAvailable) {
-        if (isServiceAvailable) {
-          extensionAvailable.value = true;
-          final state = serviceConnection.serviceManager.serviceExtensionManager
-              .getServiceExtensionState(widget.serviceExtension.extension);
-          _setValueFromState(state.value);
-          addAutoDisposeListener(state, () {
-            _setValueFromState(state.value);
-          });
-        }
-      }),
+            if (isServiceAvailable) {
+              extensionAvailable.value = true;
+              final state = serviceConnection
+                  .serviceManager
+                  .serviceExtensionManager
+                  .getServiceExtensionState(widget.serviceExtension.extension);
+              _setValueFromState(state.value);
+              addAutoDisposeListener(state, () {
+                _setValueFromState(state.value);
+              });
+            }
+          }),
     );
   }
 
@@ -593,9 +592,10 @@
               child: CheckboxSetting(
                 notifier: value,
                 title: widget.serviceExtension.title,
-                description: widget.showDescription
-                    ? widget.serviceExtension.description
-                    : null,
+                description:
+                    widget.showDescription
+                        ? widget.serviceExtension.description
+                        : null,
                 tooltip: widget.serviceExtension.tooltip,
                 onChanged: _onChanged,
                 enabled: available,
@@ -627,12 +627,13 @@
         if (widget.serviceExtension.inverted) enabled = !enabled;
         await serviceConnection.serviceManager.serviceExtensionManager
             .setServiceExtensionState(
-          widget.serviceExtension.extension,
-          enabled: enabled,
-          value: enabled
-              ? widget.serviceExtension.enabledValue
-              : widget.serviceExtension.disabledValue,
-        );
+              widget.serviceExtension.extension,
+              enabled: enabled,
+              value:
+                  enabled
+                      ? widget.serviceExtension.enabledValue
+                      : widget.serviceExtension.disabledValue,
+            );
       }),
     );
   }
@@ -1015,10 +1016,7 @@
       preferBelow: true,
       decoration: BoxDecoration(
         color: colorScheme.surface,
-        border: Border.all(
-          color: focusColor,
-          width: hoverCardBorderSize,
-        ),
+        border: Border.all(color: focusColor, width: hoverCardBorderSize),
         borderRadius: defaultBorderRadius,
       ),
       textStyle: theme.regularTextStyle.copyWith(color: colorScheme.onSurface),
@@ -1058,9 +1056,7 @@
           mainAxisSize: MainAxisSize.min,
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
-            Text(
-              description.tooltip,
-            ),
+            Text(description.tooltip),
             if (description.documentationUrl != null &&
                 description.gaScreenName != null)
               Align(
@@ -1086,15 +1082,13 @@
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
-    final color = extensionState.isSelected
-        ? theme.colorScheme.primary
-        : theme.colorScheme.onSurface;
+    final color =
+        extensionState.isSelected
+            ? theme.colorScheme.primary
+            : theme.colorScheme.onSurface;
     final description = extensionState.description;
     if (description.iconData != null) {
-      return Icon(
-        description.iconData,
-        color: color,
-      );
+      return Icon(description.iconData, color: color);
     }
     return Image(
       image: AssetImage(extensionState.description.iconAsset!),
diff --git a/packages/devtools_app/lib/src/service/service_extensions.dart b/packages/devtools_app/lib/src/service/service_extensions.dart
index 4a32575..c0f3275 100644
--- a/packages/devtools_app/lib/src/service/service_extensions.dart
+++ b/packages/devtools_app/lib/src/service/service_extensions.dart
@@ -38,8 +38,9 @@
 /// A subclass of [extensions.ToggleableServiceExtension] that includes metadata
 /// for displaying and interacting with a toggleable service extension in the
 /// DevTools UI.
-class ToggleableServiceExtensionDescription<T extends Object> extends extensions
-    .ToggleableServiceExtension implements ServiceExtensionInterface {
+class ToggleableServiceExtensionDescription<T extends Object>
+    extends extensions.ToggleableServiceExtension
+    implements ServiceExtensionInterface {
   ToggleableServiceExtensionDescription._({
     required super.extension,
     required super.enabledValue,
@@ -56,10 +57,10 @@
     this.iconAsset,
     this.iconData,
     this.shortTitle,
-  })  : displayValues =
-            [enabledValue, disabledValue].map((v) => v.toString()).toList(),
-        assert((iconAsset == null) != (iconData == null)),
-        assert((documentationUrl == null) == (gaDocsItem == null));
+  }) : displayValues =
+           [enabledValue, disabledValue].map((v) => v.toString()).toList(),
+       assert((iconAsset == null) != (iconData == null)),
+       assert((documentationUrl == null) == (gaDocsItem == null));
 
   factory ToggleableServiceExtensionDescription.from(
     extensions.ToggleableServiceExtension<T> extension, {
@@ -149,10 +150,10 @@
     this.documentationUrl,
     this.shortTitle,
     this.gaDocsItem,
-  })  : displayValues =
-            displayValues ?? values.map((v) => v.toString()).toList(),
-        assert((iconAsset == null) != (iconData == null)),
-        assert((documentationUrl == null) == (gaDocsItem == null));
+  }) : displayValues =
+           displayValues ?? values.map((v) => v.toString()).toList(),
+       assert((iconAsset == null) != (iconData == null)),
+       assert((documentationUrl == null) == (gaDocsItem == null));
 
   factory ServiceExtensionDescription.from(
     extensions.ServiceExtension<T> extension, {
@@ -294,8 +295,9 @@
   gaDocsItem: gac.PerformanceDocs.trackWidgetBuildsDocs.name,
 );
 
-final profileUserWidgetBuilds =
-    ToggleableServiceExtensionDescription<bool>.from(
+final profileUserWidgetBuilds = ToggleableServiceExtensionDescription<
+  bool
+>.from(
   extensions.profileUserWidgetBuilds,
   title: 'Trace user-created widget builds',
   iconAsset: 'icons/trackwidget-white.png',
@@ -308,31 +310,33 @@
 
 final profileRenderObjectPaints =
     ToggleableServiceExtensionDescription<bool>.from(
-  extensions.profileRenderObjectPaints,
-  title: 'Trace paints',
-  iconData: Icons.format_paint,
-  gaScreenName: gac.performance,
-  gaItem: gac.PerformanceEvents.trackPaints.name,
-  description: 'Adds an event to the timeline for every RenderObject painted.',
-  tooltip: '',
-  documentationUrl:
-      'https://docs.flutter.dev/tools/devtools/performance#track-paints',
-  gaDocsItem: gac.PerformanceDocs.trackPaintsDocs.name,
-);
+      extensions.profileRenderObjectPaints,
+      title: 'Trace paints',
+      iconData: Icons.format_paint,
+      gaScreenName: gac.performance,
+      gaItem: gac.PerformanceEvents.trackPaints.name,
+      description:
+          'Adds an event to the timeline for every RenderObject painted.',
+      tooltip: '',
+      documentationUrl:
+          'https://docs.flutter.dev/tools/devtools/performance#track-paints',
+      gaDocsItem: gac.PerformanceDocs.trackPaintsDocs.name,
+    );
 
 final profileRenderObjectLayouts =
     ToggleableServiceExtensionDescription<bool>.from(
-  extensions.profileRenderObjectLayouts,
-  title: 'Trace layouts',
-  iconData: Icons.auto_awesome_mosaic,
-  gaScreenName: gac.performance,
-  gaItem: gac.PerformanceEvents.trackLayouts.name,
-  description: 'Adds an event to the timeline for every RenderObject layout.',
-  tooltip: '',
-  documentationUrl:
-      'https://docs.flutter.dev/tools/devtools/performance#track-layouts',
-  gaDocsItem: gac.PerformanceDocs.trackLayoutsDocs.name,
-);
+      extensions.profileRenderObjectLayouts,
+      title: 'Trace layouts',
+      iconData: Icons.auto_awesome_mosaic,
+      gaScreenName: gac.performance,
+      gaItem: gac.PerformanceEvents.trackLayouts.name,
+      description:
+          'Adds an event to the timeline for every RenderObject layout.',
+      tooltip: '',
+      documentationUrl:
+          'https://docs.flutter.dev/tools/devtools/performance#track-layouts',
+      gaDocsItem: gac.PerformanceDocs.trackLayoutsDocs.name,
+    );
 
 final repaintRainbow = ToggleableServiceExtensionDescription<bool>.from(
   extensions.repaintRainbow,
@@ -405,8 +409,9 @@
   gaDocsItem: gac.PerformanceDocs.disableOpacityLayersDocs.name,
 );
 
-final disablePhysicalShapeLayers =
-    ToggleableServiceExtensionDescription<bool>.from(
+final disablePhysicalShapeLayers = ToggleableServiceExtensionDescription<
+  bool
+>.from(
   extensions.disablePhysicalShapeLayers,
   title: 'Render Physical Shape layers',
   iconData: Icons.format_shapes,
@@ -424,17 +429,18 @@
 
 final httpEnableTimelineLogging =
     ToggleableServiceExtensionDescription<bool>.from(
-  extensions.httpEnableTimelineLogging,
-  title: 'Whether HTTP timeline logging is enabled',
-  iconData: Icons.http,
-  gaScreenName: null,
-  gaItem: null,
-  tooltip: 'Toggle HTTP timeline logging',
-);
+      extensions.httpEnableTimelineLogging,
+      title: 'Whether HTTP timeline logging is enabled',
+      iconData: Icons.http,
+      gaScreenName: null,
+      gaItem: null,
+      tooltip: 'Toggle HTTP timeline logging',
+    );
 
 // Legacy extension to show the inspector and enable inspector select mode.
-final toggleOnDeviceWidgetInspector =
-    ToggleableServiceExtensionDescription<bool>.from(
+final toggleOnDeviceWidgetInspector = ToggleableServiceExtensionDescription<
+  bool
+>.from(
   extensions.toggleOnDeviceWidgetInspector,
   // Technically this enables the on-device widget inspector but for older
   // versions of package:flutter it makes sense to describe this extension as
@@ -473,13 +479,13 @@
 /// mode is triggered.
 final enableOnDeviceInspector =
     ToggleableServiceExtensionDescription<bool>.from(
-  extensions.enableOnDeviceInspector,
-  title: 'Enable on-device inspector',
-  iconAsset: 'icons/general/locate@2x.png',
-  gaScreenName: gac.inspector,
-  gaItem: gac.enableOnDeviceInspector,
-  tooltip: 'Toggle on-device inspector',
-);
+      extensions.enableOnDeviceInspector,
+      title: 'Enable on-device inspector',
+      iconAsset: 'icons/general/locate@2x.png',
+      gaScreenName: gac.inspector,
+      gaItem: gac.enableOnDeviceInspector,
+      tooltip: 'Toggle on-device inspector',
+    );
 
 final structuredErrors = ToggleableServiceExtensionDescription<bool>.from(
   extensions.structuredErrors,
@@ -503,8 +509,9 @@
   // TODO(https://github.com/flutter/website/issues/10666): link docs
 );
 
-final profilePlatformChannels =
-    ToggleableServiceExtensionDescription<bool>.from(
+final profilePlatformChannels = ToggleableServiceExtensionDescription<
+  bool
+>.from(
   extensions.profilePlatformChannels,
   title: 'Trace platform channels',
   iconAsset: 'icons/trackwidget-white.png',
diff --git a/packages/devtools_app/lib/src/service/service_manager.dart b/packages/devtools_app/lib/src/service/service_manager.dart
index 09d9d9c..c6a4635 100644
--- a/packages/devtools_app/lib/src/service/service_manager.dart
+++ b/packages/devtools_app/lib/src/service/service_manager.dart
@@ -10,14 +10,14 @@
 import 'package:vm_service/vm_service.dart' hide Error;
 
 import '../shared/analytics/analytics.dart' as ga;
-import '../shared/connected_app.dart';
 import '../shared/console/console_service.dart';
 import '../shared/diagnostics/inspector_service.dart';
-import '../shared/error_badge_manager.dart';
 import '../shared/globals.dart';
+import '../shared/managers/error_badge_manager.dart';
 import '../shared/server/server.dart' as server;
 import '../shared/title.dart';
-import '../shared/utils.dart';
+import '../shared/utils/utils.dart';
+import 'connected_app/connected_app.dart';
 import 'service_registrations.dart' as registrations;
 import 'timeline_streams.dart';
 import 'vm_flags.dart';
@@ -36,29 +36,30 @@
 
 class ServiceConnectionManager {
   ServiceConnectionManager() {
-    serviceManager = ServiceManager()
-      ..registerLifecycleCallback(
-        ServiceManagerLifecycle.beforeOpenVmService,
-        _beforeOpenVmService,
-      )
-      ..registerLifecycleCallback(
-        ServiceManagerLifecycle.afterOpenVmService,
-        _afterOpenVmService,
-      )
-      ..registerLifecycleCallback(
-        ServiceManagerLifecycle.beforeCloseVmService,
-        _beforeCloseVmService,
-      )
-      ..registerLifecycleCallback(
-        ServiceManagerLifecycle.afterCloseVmService,
-        _afterCloseVmService,
-      )
-      ..registerOverride(
-        ServiceManagerOverride.initIsolates,
-        (service) async => await serviceManager.isolateManager.init(
-          serviceManager.vm?.isolatesForDevToolsMode() ?? <IsolateRef>[],
-        ),
-      );
+    serviceManager =
+        ServiceManager()
+          ..registerLifecycleCallback(
+            ServiceManagerLifecycle.beforeOpenVmService,
+            _beforeOpenVmService,
+          )
+          ..registerLifecycleCallback(
+            ServiceManagerLifecycle.afterOpenVmService,
+            _afterOpenVmService,
+          )
+          ..registerLifecycleCallback(
+            ServiceManagerLifecycle.beforeCloseVmService,
+            _beforeCloseVmService,
+          )
+          ..registerLifecycleCallback(
+            ServiceManagerLifecycle.afterCloseVmService,
+            _afterCloseVmService,
+          )
+          ..registerOverride(
+            ServiceManagerOverride.initIsolates,
+            (service) async => await serviceManager.isolateManager.init(
+              serviceManager.vm?.isolatesForDevToolsMode() ?? <IsolateRef>[],
+            ),
+          );
   }
 
   late final ServiceManager<VmServiceWrapper> serviceManager;
@@ -100,15 +101,17 @@
   Future<void> _afterOpenVmService(VmServiceWrapper? service) async {
     // Re-initialize isolates when VM developer mode is enabled/disabled to
     // display/hide system isolates.
-    preferences.vmDeveloperModeEnabled
-        .addListener(_handleVmDeveloperModeChanged);
+    preferences.vmDeveloperModeEnabled.addListener(
+      _handleVmDeveloperModeChanged,
+    );
 
     // This needs to be called before calling
     // `ga.setupUserApplicationDimensions()` and before initializing
     // [_inspectorService], since both require access to an initialized
     // [serviceManager.connectedApp] object.
-    await serviceManager.connectedApp!
-        .initializeValues(onComplete: generateDevToolsTitle);
+    await serviceManager.connectedApp!.initializeValues(
+      onComplete: generateDevToolsTitle,
+    );
 
     // Set up analytics dimensions for the connected app.
     ga.setupUserApplicationDimensions();
@@ -135,9 +138,10 @@
     // Set [offlineController.previousConnectedApp] in case we need it for
     // viewing data after disconnect. This must be done before resetting the
     // rest of the service manager state.
-    final previousConnectedApp = serviceManager.connectedApp != null
-        ? OfflineConnectedApp.parse(serviceManager.connectedApp!.toJson())
-        : null;
+    final previousConnectedApp =
+        serviceManager.connectedApp != null
+            ? OfflineConnectedApp.parse(serviceManager.connectedApp!.toJson())
+            : null;
     offlineDataController.previousConnectedApp = previousConnectedApp;
 
     // This must be called before we close the VM service so that
@@ -159,8 +163,9 @@
     _inspectorService?.dispose();
     _inspectorService = null;
     serviceTrafficLogger?.dispose();
-    preferences.vmDeveloperModeEnabled
-        .removeListener(_handleVmDeveloperModeChanged);
+    preferences.vmDeveloperModeEnabled.removeListener(
+      _handleVmDeveloperModeChanged,
+    );
   }
 
   Future<void> _handleVmDeveloperModeChanged() async {
@@ -169,8 +174,9 @@
     final vmDeveloperModeEnabled = preferences.vmDeveloperModeEnabled.value;
     if (serviceManager.isolateManager.selectedIsolate.value!.isSystemIsolate! &&
         !vmDeveloperModeEnabled) {
-      serviceManager.isolateManager
-          .selectIsolate(serviceManager.isolateManager.isolates.value.first);
+      serviceManager.isolateManager.selectIsolate(
+        serviceManager.isolateManager.isolates.value.first,
+      );
     }
     await serviceManager.isolateManager.init(isolates);
   }
@@ -185,12 +191,11 @@
   ///
   /// Throws an Exception if no 'FlutterView' is present in this isolate.
   Future<String> get flutterViewId async {
-    final flutterViewListResponse =
-        await serviceManager.callServiceExtensionOnMainIsolate(
-      registrations.flutterListViews,
-    );
-    final views = (flutterViewListResponse.json!['views'] as List)
-        .cast<Map<String, Object?>>();
+    final flutterViewListResponse = await serviceManager
+        .callServiceExtensionOnMainIsolate(registrations.flutterListViews);
+    final views =
+        (flutterViewListResponse.json!['views'] as List)
+            .cast<Map<String, Object?>>();
 
     // Each isolate should only have one FlutterView.
     final flutterView = views.firstWhereOrNull(
@@ -223,9 +228,7 @@
 
     return await serviceManager.callServiceExtensionOnMainIsolate(
       registrations.flutterEngineEstimateRasterCache,
-      args: {
-        'viewId': viewId,
-      },
+      args: {'viewId': viewId},
     );
   }
 
@@ -239,9 +242,7 @@
 
     return await serviceManager.callServiceExtensionOnMainIsolate(
       registrations.renderFrameWithRasterStats,
-      args: {
-        'viewId': viewId,
-      },
+      args: {'viewId': viewId},
     );
   }
 
@@ -254,11 +255,11 @@
     const unknownRefreshRate = 0.0;
 
     final viewId = await flutterViewId;
-    final displayRefreshRateResponse =
-        await serviceManager.callServiceExtensionOnMainIsolate(
-      registrations.displayRefreshRate,
-      args: {'viewId': viewId},
-    );
+    final displayRefreshRateResponse = await serviceManager
+        .callServiceExtensionOnMainIsolate(
+          registrations.displayRefreshRate,
+          args: {'viewId': viewId},
+        );
     final double fps = displayRefreshRateResponse.json!['fps'];
 
     // The Flutter engine returns 0.0 if the refresh rate is unknown. Return
@@ -281,10 +282,7 @@
       registrations.dwdsSendEvent,
       args: {
         'type': 'DevtoolsEvent',
-        'payload': {
-          'screen': screen,
-          'action': action,
-        },
+        'payload': {'screen': screen, 'action': action},
       },
     );
   }
diff --git a/packages/devtools_app/lib/src/service/timeline_streams.dart b/packages/devtools_app/lib/src/service/timeline_streams.dart
index d5e8469..d5d8bc3 100644
--- a/packages/devtools_app/lib/src/service/timeline_streams.dart
+++ b/packages/devtools_app/lib/src/service/timeline_streams.dart
@@ -36,14 +36,14 @@
   final _streamDescriptions = <String, String>{
     dartTimelineStream:
         'Events emitted from dart:developer Timeline APIs (including'
-            ' Flutter framework events)',
+        ' Flutter framework events)',
     embedderTimelineStream:
         'Additional platform events (often emitted from the Flutter engine)',
     gcTimelineStream: 'Garbage collection',
     apiTimelineStream: 'Calls to the VM embedding API',
     compilerTimelineStream:
         'Compiler phases (loading code, compilation, optimization,'
-            ' etc.)',
+        ' etc.)',
     compilerVerboseTimelineStream: 'More detailed compiler phases',
     debuggerTimelineStream: 'Debugger paused events',
     isolateTimelineStream:
@@ -75,21 +75,19 @@
     final timelineFlags = await service!.getVMTimelineFlags();
     _streams
       ..clear()
-      ..addEntries(
-        [
-          for (final streamName in timelineFlags.availableStreams ?? <String>[])
-            MapEntry(
-              streamName,
-              TimelineStream(
-                name: streamName,
-                description: _streamDescriptions[streamName] ?? '',
-                advanced: _advancedStreams.contains(streamName),
-                recorded: timelineFlags.recordedStreams?.contains(streamName) ??
-                    false,
-              ),
+      ..addEntries([
+        for (final streamName in timelineFlags.availableStreams ?? <String>[])
+          MapEntry(
+            streamName,
+            TimelineStream(
+              name: streamName,
+              description: _streamDescriptions[streamName] ?? '',
+              advanced: _advancedStreams.contains(streamName),
+              recorded:
+                  timelineFlags.recordedStreams?.contains(streamName) ?? false,
             ),
-        ],
-      );
+          ),
+      ]);
   }
 
   /// Handles events from the VM service 'TimelineEvent' stream to track updates
@@ -120,10 +118,7 @@
     }
   }
 
-  void vmServiceOpened(
-    VmServiceWrapper service,
-    ConnectedApp connectedApp,
-  ) {
+  void vmServiceOpened(VmServiceWrapper service, ConnectedApp connectedApp) {
     cancelStreamSubscriptions();
     _service = service;
 
@@ -158,17 +153,19 @@
   /// Sends an update to the VM service that the new recorded value for [stream]
   /// should match [value].
   Future<void> updateTimelineStream(TimelineStream stream, bool value) async {
-    final recordedStreamNames = _streams.keys
-        .where((streamName) => _streams[streamName]!.recorded.value)
-        .toList();
+    final recordedStreamNames =
+        _streams.keys
+            .where((streamName) => _streams[streamName]!.recorded.value)
+            .toList();
     final alreadyBeingRecorded = recordedStreamNames.contains(stream.name);
     if (alreadyBeingRecorded && !value) {
       recordedStreamNames.remove(stream.name);
     } else if (!alreadyBeingRecorded && value) {
       recordedStreamNames.add(stream.name);
     }
-    await serviceConnection.serviceManager.service!
-        .setVMTimelineFlags(recordedStreamNames);
+    await serviceConnection.serviceManager.service!.setVMTimelineFlags(
+      recordedStreamNames,
+    );
   }
 }
 
diff --git a/packages/devtools_app/lib/src/service/vm_flags.dart b/packages/devtools_app/lib/src/service/vm_flags.dart
index 72ef638..fb8da58 100644
--- a/packages/devtools_app/lib/src/service/vm_flags.dart
+++ b/packages/devtools_app/lib/src/service/vm_flags.dart
@@ -43,12 +43,13 @@
     if (event.kind == EventKind.kVMFlagUpdate) {
       if (_flagNotifiers.containsKey(event.flag)) {
         final currentFlag = _flagNotifiers[event.flag]!.value;
-        _flagNotifiers[event.flag]!.value = Flag.parse({
-          'name': currentFlag.name,
-          'comment': currentFlag.comment,
-          'modified': true,
-          'valueAsString': event.newValue,
-        })!;
+        _flagNotifiers[event.flag]!.value =
+            Flag.parse({
+              'name': currentFlag.name,
+              'comment': currentFlag.comment,
+              'modified': true,
+              'valueAsString': event.newValue,
+            })!;
         _flags.value = await service.getFlagList();
       }
     }
diff --git a/packages/devtools_app/lib/src/service/vm_service_logger.dart b/packages/devtools_app/lib/src/service/vm_service_logger.dart
index dd3c240..d350e89 100644
--- a/packages/devtools_app/lib/src/service/vm_service_logger.dart
+++ b/packages/devtools_app/lib/src/service/vm_service_logger.dart
@@ -33,10 +33,7 @@
     final String? id = m['id'];
 
     messageBus.addEvent(
-      BusEvent(
-        eventName,
-        data: '⇨ #$id $method()\n$message',
-      ),
+      BusEvent(eventName, data: '⇨ #$id $method()\n$message'),
     );
   }
 
diff --git a/packages/devtools_app/lib/src/service/vm_service_wrapper.dart b/packages/devtools_app/lib/src/service/vm_service_wrapper.dart
index d0aa630..3e12a9b 100644
--- a/packages/devtools_app/lib/src/service/vm_service_wrapper.dart
+++ b/packages/devtools_app/lib/src/service/vm_service_wrapper.dart
@@ -20,7 +20,6 @@
 
 import '../screens/vm_developer/vm_service_private_extensions.dart';
 import '../shared/feature_flags.dart';
-import '../shared/globals.dart';
 import '../shared/primitives/utils.dart';
 import 'json_to_service_cache.dart';
 
@@ -85,11 +84,12 @@
 
   Future<void> get allFuturesCompleted => _allFuturesCompleter.future;
 
-  Completer<bool> _allFuturesCompleter = Completer<bool>()
-    // Mark the future as completed by default so if we don't track any
-    // futures but someone tries to wait on [allFuturesCompleted] they don't
-    // hang. The first tracked future will replace this with a new completer.
-    ..complete(true);
+  Completer<bool> _allFuturesCompleter =
+      Completer<bool>()
+        // Mark the future as completed by default so if we don't track any
+        // futures but someone tries to wait on [allFuturesCompleted] they don't
+        // hang. The first tracked future will replace this with a new completer.
+        ..complete(true);
 
   // A local cache of "fake" service objects. Used to convert JSON objects to
   // VM service response formats to be used with APIs that require them.
@@ -142,7 +142,9 @@
         // running with VM developer mode enabled. This data isn't accessible
         // in non-VM developer mode, so not requesting the code profile will
         // save on space and network usage.
-        '_code': preferences.vmDeveloperModeEnabled.value,
+        // TODO(bkonyi): re-enable when package:vm_service 15.0.0 is rolled
+        // into Flutter. See https://github.com/flutter/devtools/issues/8567.
+        // '_code': preferences.vmDeveloperModeEnabled.value,
       },
     ).then((e) => e as CpuSamples);
   }
@@ -163,12 +165,7 @@
     if (cachedObj != null) {
       return Future.value(cachedObj);
     }
-    return super.getObject(
-      isolateId,
-      objectId,
-      offset: offset,
-      count: count,
-    );
+    return super.getObject(isolateId, objectId, offset: offset, count: count);
   }
 
   Future<HeapSnapshotGraph> getHeapSnapshotGraph(
@@ -341,10 +338,7 @@
   ///
   /// All logs from this run will have matching unique ids, so that they can
   /// be associated together in the logs.
-  Future<T> _maybeLogWrappedFuture<T>(
-    String name,
-    Future<T> future,
-  ) async {
+  Future<T> _maybeLogWrappedFuture<T>(String name, Future<T> future) async {
     // If the logger is not accepting FINE logs, then we won't be logging any
     // messages. So just return the [future] as-is.
     if (!_log.isLoggable(Level.FINE)) return future;
@@ -356,10 +350,7 @@
       _log.fine('[$logId]-wrapFuture($name,...): Succeeded');
       return result;
     } catch (error) {
-      _log.severe(
-        '[$logId]-wrapFuture($name,...): Failed',
-        error,
-      );
+      _log.severe('[$logId]-wrapFuture($name,...): Failed', error);
       rethrow;
     }
   }
@@ -418,18 +409,14 @@
   }
 
   /// Forces the VM to perform a full garbage collection.
-  Future<Success?> collectAllGarbage() => _privateRpcInvoke(
-        'collectAllGarbage',
-        parser: Success.parse,
-      );
+  Future<Success?> collectAllGarbage() =>
+      _privateRpcInvoke('collectAllGarbage', parser: Success.parse);
 
   Future<InstanceRef?> getReachableSize(String isolateId, String targetId) =>
       _privateRpcInvoke(
         'getReachableSize',
         isolateId: isolateId,
-        args: {
-          'targetId': targetId,
-        },
+        args: {'targetId': targetId},
         parser: InstanceRef.parse,
       );
 
@@ -437,17 +424,15 @@
       _privateRpcInvoke(
         'getRetainedSize',
         isolateId: isolateId,
-        args: {
-          'targetId': targetId,
-        },
+        args: {'targetId': targetId},
         parser: InstanceRef.parse,
       );
 
   Future<ObjectStore?> getObjectStore(String isolateId) => _privateRpcInvoke(
-        'getObjectStore',
-        isolateId: isolateId,
-        parser: ObjectStore.parse,
-      );
+    'getObjectStore',
+    isolateId: isolateId,
+    parser: ObjectStore.parse,
+  );
 
   Future<dap.VariablesResponseBody?> dapVariablesRequest(
     dap.VariablesArguments args,
@@ -455,9 +440,7 @@
     final response = await _sendDapRequest('variables', args: args);
     if (response == null) return null;
 
-    return dap.VariablesResponseBody.fromJson(
-      response as Map<String, Object?>,
-    );
+    return dap.VariablesResponseBody.fromJson(response as Map<String, Object?>);
   }
 
   Future<dap.ScopesResponseBody?> dapScopesRequest(
@@ -466,9 +449,7 @@
     final response = await _sendDapRequest('scopes', args: args);
     if (response == null) return null;
 
-    return dap.ScopesResponseBody.fromJson(
-      response as Map<String, Object?>,
-    );
+    return dap.ScopesResponseBody.fromJson(response as Map<String, Object?>);
   }
 
   Future<dap.StackTraceResponseBody?> dapStackTraceRequest(
@@ -496,11 +477,7 @@
 
     final response = await sendDapRequest(
       jsonEncode(
-        dap.Request(
-          command: command,
-          seq: _dapSeq++,
-          arguments: args,
-        ),
+        dap.Request(command: command, seq: _dapSeq++, arguments: args),
       ),
     );
 
diff --git a/packages/devtools_app/lib/src/shared/analytics/_analytics_stub.dart b/packages/devtools_app/lib/src/shared/analytics/_analytics_stub.dart
index a6f5d1d..6392dd7 100644
--- a/packages/devtools_app/lib/src/shared/analytics/_analytics_stub.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/_analytics_stub.dart
@@ -28,10 +28,7 @@
 
 void jsHookupListenerForGA() {}
 
-void screen(
-  String screenName, [
-  int value = 0,
-]) {
+void screen(String screenName, [int value = 0]) {
   _log.fine('Event: screen(screenName:$screenName, value:$value)');
 }
 
diff --git a/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart b/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart
index 1cd77d5..ec56c46 100644
--- a/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart
@@ -16,12 +16,12 @@
 import 'package:unified_analytics/unified_analytics.dart' as ua;
 import 'package:web/web.dart';
 
-import '../dtd_manager_extensions.dart';
 import '../globals.dart';
+import '../managers/dtd_manager_extensions.dart';
+import '../primitives/query_parameters.dart';
 import '../primitives/utils.dart';
-import '../query_parameters.dart';
 import '../server/server.dart' as server;
-import '../utils.dart';
+import '../utils/utils.dart';
 import 'analytics_common.dart';
 import 'constants.dart' as gac;
 import 'gtags.dart';
@@ -67,7 +67,6 @@
 
     // NOTE: Do not reorder any of these. Order here must match the order in the
     // Google Analytics console.
-
     String? user_app, // dimension1 (flutter or web)
     String? user_build, // dimension2 (debug or profile)
     String? user_platform, // dimension3 (android/ios/fuchsia/linux/mac/windows)
@@ -76,11 +75,10 @@
     String? devtools_version, // dimension6 DevTools version #
     String? ide_launched, // dimension7 Devtools launched (CLI, VSCode, Android)
     String?
-        flutter_client_id, // dimension8 Flutter tool client_id (~/.flutter).
+    flutter_client_id, // dimension8 Flutter tool client_id (~/.flutter).
     String? is_external_build, // dimension9 External build or google3
     String? is_embedded, // dimension10 Whether devtools is embedded
     String? g3_username, // dimension11 g3 username (null for external users)
-
     // dimension12 IDE feature that launched Devtools
     // The following is a non-exhaustive list of possible values for this dimension:
     // "command" - VS Code command palette
@@ -93,7 +91,6 @@
     // "languageStatus" - launched from the language status popout
     String? ide_launched_feature,
     String? is_wasm, // dimension13 whether DevTools is running with WASM.
-
     // Performance screen metrics. See [PerformanceScreenMetrics].
     int? ui_duration_micros, // metric1
     int? raster_duration_micros, // metric2
@@ -148,53 +145,66 @@
       ide_launched_feature: ideLaunchedFeature,
       is_wasm: kIsWasm.toString(),
       // [PerformanceScreenMetrics]
-      ui_duration_micros: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.uiDuration?.inMicroseconds
-          : null,
-      raster_duration_micros: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.rasterDuration?.inMicroseconds
-          : null,
+      ui_duration_micros:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.uiDuration?.inMicroseconds
+              : null,
+      raster_duration_micros:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.rasterDuration?.inMicroseconds
+              : null,
       shader_compilation_duration_micros:
           screenMetrics is PerformanceScreenMetrics
               ? screenMetrics.shaderCompilationDuration?.inMicroseconds
               : null,
-      trace_event_count: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.traceEventCount
-          : null,
+      trace_event_count:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.traceEventCount
+              : null,
       // [ProfilerScreenMetrics]
-      cpu_sample_count: screenMetrics is ProfilerScreenMetrics
-          ? screenMetrics.cpuSampleCount
-          : null,
-      cpu_stack_depth: screenMetrics is ProfilerScreenMetrics
-          ? screenMetrics.cpuStackDepth
-          : null,
+      cpu_sample_count:
+          screenMetrics is ProfilerScreenMetrics
+              ? screenMetrics.cpuSampleCount
+              : null,
+      cpu_stack_depth:
+          screenMetrics is ProfilerScreenMetrics
+              ? screenMetrics.cpuStackDepth
+              : null,
       // [MemoryScreenMetrics]
-      heap_diff_objects_before: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapDiffObjectsBefore
-          : null,
-      heap_diff_objects_after: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapDiffObjectsAfter
-          : null,
-      heap_objects_total: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapObjectsTotal
-          : null,
+      heap_diff_objects_before:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapDiffObjectsBefore
+              : null,
+      heap_diff_objects_after:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapDiffObjectsAfter
+              : null,
+      heap_objects_total:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapObjectsTotal
+              : null,
       // [InspectorScreenMetrics]
-      root_set_count: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.rootSetCount
-          : null,
-      row_count: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.rowCount
-          : null,
-      inspector_tree_controller_id: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.inspectorTreeControllerId
-          : null,
+      root_set_count:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.rootSetCount
+              : null,
+      row_count:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.rowCount
+              : null,
+      inspector_tree_controller_id:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.inspectorTreeControllerId
+              : null,
       // [DeepLinkScreenMetrics]
-      android_app_id: screenMetrics is DeepLinkScreenMetrics
-          ? screenMetrics.androidAppId
-          : null,
-      ios_bundle_id: screenMetrics is DeepLinkScreenMetrics
-          ? screenMetrics.iosBundleId
-          : null,
+      android_app_id:
+          screenMetrics is DeepLinkScreenMetrics
+              ? screenMetrics.androidAppId
+              : null,
+      ios_bundle_id:
+          screenMetrics is DeepLinkScreenMetrics
+              ? screenMetrics.iosBundleId
+              : null,
       // [InspectorScreenMetrics]
       is_v2_inspector:
           screenMetrics is InspectorScreenMetrics ? screenMetrics.isV2 : null,
@@ -244,7 +254,6 @@
 
     // NOTE: Do not reorder any of these. Order here must match the order in the
     // Google Analytics console.
-
     String? user_app, // dimension1 (flutter or web)
     String? user_build, // dimension2 (debug or profile)
     String? user_platform, // dimension3 (android or ios)
@@ -256,7 +265,6 @@
     String? is_external_build, // dimension9 External build or google3
     String? is_embedded, // dimension10 Whether devtools is embedded
     String? g3_username, // dimension11 g3 username (null for external users)
-
     // dimension12 IDE feature that launched Devtools
     // The following is a non-exhaustive list of possible values for this dimension:
     // "command" - VS Code command palette
@@ -269,7 +277,6 @@
     // "languageStatus" - launched from the language status popout
     String? ide_launched_feature,
     String? is_wasm, // dimension13 whether DevTools is running with WASM.
-
     // Performance screen metrics. See [PerformanceScreenMetrics].
     int? ui_duration_micros, // metric1
     int? raster_duration_micros, // metric2
@@ -316,53 +323,66 @@
       ide_launched_feature: ideLaunchedFeature,
       is_wasm: kIsWasm.toString(),
       // [PerformanceScreenMetrics]
-      ui_duration_micros: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.uiDuration?.inMicroseconds
-          : null,
-      raster_duration_micros: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.rasterDuration?.inMicroseconds
-          : null,
-      trace_event_count: screenMetrics is PerformanceScreenMetrics
-          ? screenMetrics.traceEventCount
-          : null,
+      ui_duration_micros:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.uiDuration?.inMicroseconds
+              : null,
+      raster_duration_micros:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.rasterDuration?.inMicroseconds
+              : null,
+      trace_event_count:
+          screenMetrics is PerformanceScreenMetrics
+              ? screenMetrics.traceEventCount
+              : null,
       shader_compilation_duration_micros:
           screenMetrics is PerformanceScreenMetrics
               ? screenMetrics.shaderCompilationDuration?.inMicroseconds
               : null,
       // [ProfilerScreenMetrics]
-      cpu_sample_count: screenMetrics is ProfilerScreenMetrics
-          ? screenMetrics.cpuSampleCount
-          : null,
-      cpu_stack_depth: screenMetrics is ProfilerScreenMetrics
-          ? screenMetrics.cpuStackDepth
-          : null,
+      cpu_sample_count:
+          screenMetrics is ProfilerScreenMetrics
+              ? screenMetrics.cpuSampleCount
+              : null,
+      cpu_stack_depth:
+          screenMetrics is ProfilerScreenMetrics
+              ? screenMetrics.cpuStackDepth
+              : null,
       // [MemoryScreenMetrics]
-      heap_diff_objects_before: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapDiffObjectsBefore
-          : null,
-      heap_diff_objects_after: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapDiffObjectsAfter
-          : null,
-      heap_objects_total: screenMetrics is MemoryScreenMetrics
-          ? screenMetrics.heapObjectsTotal
-          : null,
+      heap_diff_objects_before:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapDiffObjectsBefore
+              : null,
+      heap_diff_objects_after:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapDiffObjectsAfter
+              : null,
+      heap_objects_total:
+          screenMetrics is MemoryScreenMetrics
+              ? screenMetrics.heapObjectsTotal
+              : null,
       // [InspectorScreenMetrics]
-      root_set_count: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.rootSetCount
-          : null,
-      row_count: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.rowCount
-          : null,
-      inspector_tree_controller_id: screenMetrics is InspectorScreenMetrics
-          ? screenMetrics.inspectorTreeControllerId
-          : null,
+      root_set_count:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.rootSetCount
+              : null,
+      row_count:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.rowCount
+              : null,
+      inspector_tree_controller_id:
+          screenMetrics is InspectorScreenMetrics
+              ? screenMetrics.inspectorTreeControllerId
+              : null,
       // [DeepLinkScreenMetrics]
-      android_app_id: screenMetrics is DeepLinkScreenMetrics
-          ? screenMetrics.androidAppId
-          : null,
-      ios_bundle_id: screenMetrics is DeepLinkScreenMetrics
-          ? screenMetrics.iosBundleId
-          : null,
+      android_app_id:
+          screenMetrics is DeepLinkScreenMetrics
+              ? screenMetrics.androidAppId
+              : null,
+      ios_bundle_id:
+          screenMetrics is DeepLinkScreenMetrics
+              ? screenMetrics.iosBundleId
+              : null,
       // [InspectorScreenMetrics]
       is_v2_inspector:
           screenMetrics is InspectorScreenMetrics ? screenMetrics.isV2 : null,
@@ -462,10 +482,7 @@
 // end marks.
 void timeStart(String screenName, String timedOperation) {
   final startTime = DateTime.now();
-  final operationKey = _operationKey(
-    screenName,
-    timedOperation,
-  );
+  final operationKey = _operationKey(screenName, timedOperation);
   _timedOperationsInProgress[operationKey] = startTime;
 }
 
@@ -478,10 +495,7 @@
   ScreenAnalyticsMetrics Function()? screenMetricsProvider,
 }) {
   final endTime = DateTime.now();
-  final operationKey = _operationKey(
-    screenName,
-    timedOperation,
-  );
+  final operationKey = _operationKey(screenName, timedOperation);
   final startTime = _timedOperationsInProgress.remove(operationKey);
   assert(startTime != null);
   if (startTime == null) {
@@ -504,10 +518,7 @@
 }
 
 void cancelTimingOperation(String screenName, String timedOperation) {
-  final operationKey = _operationKey(
-    screenName,
-    timedOperation,
-  );
+  final operationKey = _operationKey(screenName, timedOperation);
   final operation = _timedOperationsInProgress.remove(operationKey);
   assert(
     operation != null,
@@ -901,10 +912,7 @@
 }
 
 void _sendEvent(GtagEventDevTools gtagEvent) {
-  GTag.event(
-    gtagEvent.screen!,
-    gaEventProvider: () => gtagEvent,
-  );
+  GTag.event(gtagEvent.screen!, gaEventProvider: () => gtagEvent);
   final uaEvent = _uaEventFromGtagEvent(gtagEvent);
   unawaited(dtdManager.sendAnalyticsEvent(uaEvent));
 }
@@ -1040,7 +1048,8 @@
   final String? iosBundleId;
 
   @override
-  Map<String, Object> toMap() => (<String, Object?>{
+  Map<String, Object> toMap() =>
+      (<String, Object?>{
         'uiDurationMicros': uiDurationMicros,
         'rasterDurationMicros': rasterDurationMicros,
         'shaderCompilationDurationMicros': shaderCompilationDurationMicros,
@@ -1056,6 +1065,5 @@
         'isV2Inspector': isV2Inspector,
         'androidAppId': androidAppId,
         'iosBundleId': iosBundleId,
-      }..removeWhere((key, value) => value == null))
-          .cast<String, Object>();
+      }..removeWhere((key, value) => value == null)).cast<String, Object>();
 }
diff --git a/packages/devtools_app/lib/src/shared/analytics/analytics_controller.dart b/packages/devtools_app/lib/src/shared/analytics/analytics_controller.dart
index 50f0e21..b8102cc 100644
--- a/packages/devtools_app/lib/src/shared/analytics/analytics_controller.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/analytics_controller.dart
@@ -7,9 +7,8 @@
 import 'package:flutter/foundation.dart';
 
 import '../development_helpers.dart';
-import '../dtd_manager_extensions.dart';
 import '../globals.dart';
-
+import '../managers/dtd_manager_extensions.dart';
 import 'analytics.dart' as ga;
 
 Future<AnalyticsController> get analyticsController async {
@@ -23,7 +22,8 @@
   var shouldShowConsentMessage = false;
   try {
     enabled = await ga.isAnalyticsEnabled();
-    shouldShowConsentMessage = debugShowAnalyticsConsentMessage ||
+    shouldShowConsentMessage =
+        debugShowAnalyticsConsentMessage ||
         await ga.shouldShowAnalyticsConsentMessage();
   } catch (_) {
     // Ignore issues if analytics could not be initialized.
@@ -52,10 +52,10 @@
     this.legacyOnEnableAnalytics,
     this.legacyOnDisableAnalytics,
     this.legacyOnSetupAnalytics,
-  })  : analyticsEnabled = ValueNotifier<bool>(enabled),
-        _shouldPrompt = ValueNotifier<bool>(
-          shouldShowConsentMessage && consentMessage.isNotEmpty,
-        ) {
+  }) : analyticsEnabled = ValueNotifier<bool>(enabled),
+       _shouldPrompt = ValueNotifier<bool>(
+         shouldShowConsentMessage && consentMessage.isNotEmpty,
+       ) {
     if (_shouldPrompt.value) {
       unawaited(toggleAnalyticsEnabled(true));
     }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants.dart
index ee2fa75..0372549 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants.dart
@@ -4,19 +4,20 @@
 
 import 'package:devtools_shared/devtools_extensions.dart';
 
+import '../framework/screen.dart';
 import '../preferences/preferences.dart';
-import '../screen.dart';
 
 part 'constants/_cpu_profiler_constants.dart';
 part 'constants/_debugger_constants.dart';
 part 'constants/_deep_links_constants.dart';
+part 'constants/_editor_sidebar_constants.dart';
 part 'constants/_extension_constants.dart';
+part 'constants/_inspector_constants.dart';
+part 'constants/_logging_constants.dart';
 part 'constants/_memory_constants.dart';
 part 'constants/_network_constants.dart';
-part 'constants/_logging_constants.dart';
 part 'constants/_performance_constants.dart';
-part 'constants/_editor_sidebar_constants.dart';
-part 'constants/_inspector_constants.dart';
+part 'constants/_property_editor_sidebar_constants.dart';
 
 // Type of events (event_category):
 const screenViewEvent = 'screen'; // Active screen (tab selected).
@@ -91,11 +92,7 @@
 final defaultDetailsViewToWidgetDetails =
     InspectorDetailsViewType.widgetDetailsTree.name;
 
-enum HomeScreenEvents {
-  connectToApp,
-  connectToNewApp,
-  viewVmFlags,
-}
+enum HomeScreenEvents { connectToApp, connectToNewApp, viewVmFlags }
 
 // Logging UX actions:
 const structuredErrors = 'structuredErrors';
@@ -122,6 +119,7 @@
 const verboseLogging = 'verboseLogging';
 const inspectorHoverEvalMode = 'inspectorHoverEvalMode';
 const inspectorV2Enabled = 'inspectorV2Enabled';
+const inspectorAutoRefreshEnabled = 'inspectorAutoRefreshEnabled';
 const clearLogs = 'clearLogs';
 const copyLogs = 'copyLogs';
 
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_cpu_profiler_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_cpu_profiler_constants.dart
index 1bf8aee..706d4c6 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_cpu_profiler_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_cpu_profiler_constants.dart
@@ -11,9 +11,7 @@
   cpuProfileFlameChartHelp,
   cpuProfileProcessingTime,
   openDataFile,
-  loadDataFromFile;
+  loadDataFromFile,
 }
 
-enum CpuProfilerDocs {
-  profileGranularityDocs,
-}
+enum CpuProfilerDocs { profileGranularityDocs }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_editor_sidebar_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_editor_sidebar_constants.dart
index 792372d..bd0ba7c 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_editor_sidebar_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_editor_sidebar_constants.dart
@@ -16,10 +16,6 @@
   /// Analytics id to track events that come from the DTD editor sidebar.
   static String get id => 'editorSidebar';
 
-  /// Analytics id to track events that come from the legacy VS Code
-  /// (postMessage) Flutter sidebar.
-  static String get legacyId => 'vsCodeFlutterSidebar';
-
   /// Analytics event for when a request to enable a new platform type is sent
   /// to VS Code.
   static String enablePlatformType(String platformType) =>
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_inspector_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_inspector_constants.dart
index 9b38c3f..970d4b3 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_inspector_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_inspector_constants.dart
@@ -5,6 +5,4 @@
 part of '../constants.dart';
 
 /// Events for opening documentation for the inspector tab.
-enum InspectorDocs {
-  packageDirectoriesDocs;
-}
+enum InspectorDocs { packageDirectoriesDocs }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_logging_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_logging_constants.dart
index 8ca97f4..a1560e8 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_logging_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_logging_constants.dart
@@ -5,6 +5,4 @@
 part of '../constants.dart';
 
 /// Logging event constants specific for logging screen.
-enum LoggingEvents {
-  changeRetentionLimit,
-}
+enum LoggingEvents { changeRetentionLimit, changeDetailsFormat }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_memory_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_memory_constants.dart
index df2188a..aa288a7 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_memory_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_memory_constants.dart
@@ -5,10 +5,7 @@
 part of '../constants.dart';
 
 /// Analytics time constants specific for memory screen.
-enum MemoryTime {
-  calculateDiff,
-  updateValues,
-}
+enum MemoryTime { calculateDiff, updateValues }
 
 enum MemoryEvents {
   browseRefLimit,
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart
index 5ac4bbf..ea6f0f6 100644
--- a/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart
@@ -4,6 +4,4 @@
 
 part of '../constants.dart';
 
-enum NetworkEvent {
-  downloadAsHar,
-}
+enum NetworkEvent { downloadAsHar }
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart
new file mode 100644
index 0000000..0fc2ea4
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart
@@ -0,0 +1,18 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of '../constants.dart';
+
+// TODO(elliette): Send the following events from the property editor.
+enum PropertyEditorEvents {
+  /// Analytics event that is sent when the property editor is updated with new
+  /// properties.
+  widgetPropertiesUpdate,
+
+  /// Analytics event that is sent when a user requests a property edit.
+  applyEditRequest;
+
+  /// Analytics id to track events that come from the DTD editor sidebar.
+  static String get id => 'propertyEditorSidebar';
+}
diff --git a/packages/devtools_app/lib/src/shared/analytics/metrics.dart b/packages/devtools_app/lib/src/shared/analytics/metrics.dart
index 2b0e6b1..dc4ef4e 100644
--- a/packages/devtools_app/lib/src/shared/analytics/metrics.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/metrics.dart
@@ -95,10 +95,7 @@
 }
 
 class DeepLinkScreenMetrics extends ScreenAnalyticsMetrics {
-  DeepLinkScreenMetrics({
-    this.androidAppId,
-    this.iosBundleId,
-  });
+  DeepLinkScreenMetrics({this.androidAppId, this.iosBundleId});
 
   /// The Android app id of the Flutter project.
   final String? androidAppId;
diff --git a/packages/devtools_app/lib/src/shared/analytics/prompt.dart b/packages/devtools_app/lib/src/shared/analytics/prompt.dart
index 2945f24..33aaf3f 100644
--- a/packages/devtools_app/lib/src/shared/analytics/prompt.dart
+++ b/packages/devtools_app/lib/src/shared/analytics/prompt.dart
@@ -7,8 +7,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../common_widgets.dart';
-import '../utils.dart';
+import '../ui/common_widgets.dart';
+import '../utils/utils.dart';
 import 'analytics_controller.dart';
 
 /// Conditionally displays a prompt to request permission for collection of
@@ -43,18 +43,13 @@
 
         return Column(
           crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            if (showPrompt) child!,
-            Expanded(child: widget.child),
-          ],
+          children: [if (showPrompt) child!, Expanded(child: widget.child)],
         );
       },
       child: Card(
         shape: RoundedRectangleBorder(
           borderRadius: defaultBorderRadius,
-          side: BorderSide(
-            color: theme.focusColor,
-          ),
+          side: BorderSide(color: theme.focusColor),
         ),
         color: theme.canvasColor,
         margin: const EdgeInsets.only(bottom: denseSpacing),
@@ -91,9 +86,9 @@
 
   Widget _analyticsDescription(ThemeData theme) {
     final consentMessageRegExpResults =
-        parseAnalyticsConsentMessage(controller.consentMessage)
-            ?.map((e) => adjustLineBreaks(e))
-            .toList();
+        parseAnalyticsConsentMessage(
+          controller.consentMessage,
+        )?.map((e) => adjustLineBreaks(e)).toList();
 
     // When failing to parse the consent message, fallback to displaying the
     // consent message in its regular form.
@@ -166,8 +161,10 @@
 @visibleForTesting
 List<String>? parseAnalyticsConsentMessage(String consentMessage) {
   final results = <String>[];
-  final pattern =
-      RegExp(r'^([\S\s]*)(https?:\/\/[^\s]+)(\)\.)$', multiLine: true);
+  final pattern = RegExp(
+    r'^([\S\s]*)(https?:\/\/[^\s]+)(\)\.)$',
+    multiLine: true,
+  );
 
   final matches = pattern.allMatches(consentMessage);
   if (matches.isEmpty) {
@@ -193,7 +190,9 @@
 /// match the width of the view.
 @visibleForTesting
 String adjustLineBreaks(String value) {
-  final pattern =
-      RegExp(r'(?<!\r\n|\r|\n)(\r\n|\r|\n)(?!\r\n|\r|\n)', multiLine: true);
+  final pattern = RegExp(
+    r'(?<!\r\n|\r|\n)(\r\n|\r|\n)(?!\r\n|\r|\n)',
+    multiLine: true,
+  );
   return value.replaceAll(pattern, ' ');
 }
diff --git a/packages/devtools_app/lib/src/shared/charts/chart.dart b/packages/devtools_app/lib/src/shared/charts/chart.dart
index b0b7069..b283788 100644
--- a/packages/devtools_app/lib/src/shared/charts/chart.dart
+++ b/packages/devtools_app/lib/src/shared/charts/chart.dart
@@ -40,11 +40,7 @@
 }
 
 class Chart extends StatefulWidget {
-  Chart(
-    this.controller, {
-    super.key,
-    String title = '',
-  }) {
+  Chart(this.controller, {super.key, String title = ''}) {
     controller.title = title;
   }
 
@@ -99,26 +95,28 @@
     return RepaintBoundary(
       child: LayoutBuilder(
         // Inner container
-        builder: (_, constraints) => GestureDetector(
-          onTapDown: (TapDownDetails details) {
-            final xLocalPosition = details.localPosition.dx;
-            final timestampIndex =
-                controller.xCoordToTimestampIndex(xLocalPosition);
-            final timestamp = controller.xCoordToTimestamp(xLocalPosition);
-            controller.tapLocation.value = TapLocation(
-              details,
-              timestamp,
-              timestampIndex,
-            );
-          },
-          child: SizedBox(
-            width: constraints.widthConstraints().maxWidth,
-            height: constraints.widthConstraints().maxHeight,
-            child: CustomPaint(
-              painter: ChartPainter(controller, colorScheme),
+        builder:
+            (_, constraints) => GestureDetector(
+              onTapDown: (TapDownDetails details) {
+                final xLocalPosition = details.localPosition.dx;
+                final timestampIndex = controller.xCoordToTimestampIndex(
+                  xLocalPosition,
+                );
+                final timestamp = controller.xCoordToTimestamp(xLocalPosition);
+                controller.tapLocation.value = TapLocation(
+                  details,
+                  timestamp,
+                  timestampIndex,
+                );
+              },
+              child: SizedBox(
+                width: constraints.widthConstraints().maxWidth,
+                height: constraints.widthConstraints().maxHeight,
+                child: CustomPaint(
+                  painter: ChartPainter(controller, colorScheme),
+                ),
+              ),
             ),
-          ),
-        ),
       ),
     );
   }
@@ -128,11 +126,7 @@
 /// is for stacked traces. If the Trace is stacked then yBase is the previous
 /// trace's Y position. If a trace is not stacked then yBase is always 0.
 class PointAndBase {
-  PointAndBase(
-    this.x,
-    this.y, {
-    double? yBase,
-  }) : base = yBase;
+  PointAndBase(this.x, this.y, {double? yBase}) : base = yBase;
 
   final double x;
   final double y;
@@ -141,7 +135,7 @@
 
 class ChartPainter extends CustomPainter {
   ChartPainter(this.chartController, this.colorScheme) {
-//    marginTopY = createText(chartController.title, 1.5).height + paddingY;
+    //    marginTopY = createText(chartController.title, 1.5).height + paddingY;
   }
 
   final debugTrackPaintTime = false;
@@ -160,9 +154,10 @@
     //              sampling 1 point per horizontal pixel.
     final startTime = DateTime.now();
 
-    final axis = Paint()
-      ..strokeWidth = axisWidth
-      ..color = Colors.grey;
+    final axis =
+        Paint()
+          ..strokeWidth = axisWidth
+          ..color = Colors.grey;
 
     if (size != chartController.size) {
       chartController.size = size;
@@ -174,22 +169,15 @@
       chartController.xCanvasChart,
       chartController.yCanvasChart,
       (canavas) {
-        drawAxes(
-          canvas,
-          axis,
-          displayX: chartController.displayXAxis,
-        );
+        drawAxes(canvas, axis, displayX: chartController.displayXAxis);
       },
     );
 
     final traces = chartController.traces;
-    final tracesDataIndex = List<int>.generate(
-      traces.length,
-      (int index) {
-        final length = traces[index].data.length;
-        return length > 0 ? length - 1 : -1;
-      },
-    );
+    final tracesDataIndex = List<int>.generate(traces.length, (int index) {
+      final length = traces[index].data.length;
+      return length > 0 ? length - 1 : -1;
+    });
 
     /// Key is trace index and value is x,y point.
     final previousTracesData = <int, PointAndBase>{};
@@ -243,35 +231,65 @@
           final yValue = (trace.stacked) ? stackedY + traceData.y : traceData.y;
 
           final xTimestamp = traceData.timestamp;
-          final xCanvasCoord =
-              chartController.timestampToXCanvasCoord(xTimestamp);
+          final xCanvasCoord = chartController.timestampToXCanvasCoord(
+            xTimestamp,
+          );
           if (currentTimestamp == xTimestamp) {
             // Get ready to render on canvas. Remember old canvas state
             // and setup translations for x,y coordinates into the rendering
             // area of the chart.
-            drawTranslate(
-              canvas,
-              xTranslation,
-              yTranslation,
-              (canvas) {
-                final xCoord = xCanvasCoord;
-                final yCoord = chartController.yPositionToYCanvasCoord(yValue);
-                final hasMultipleExtensionEvents =
-                    traceData is DataAggregate ? traceData.count > 1 : false;
+            drawTranslate(canvas, xTranslation, yTranslation, (canvas) {
+              final xCoord = xCanvasCoord;
+              final yCoord = chartController.yPositionToYCanvasCoord(yValue);
+              final hasMultipleExtensionEvents =
+                  traceData is DataAggregate ? traceData.count > 1 : false;
 
-                // Is the visible Y-axis max larger.
-                if (yValue > visibleYMax) {
-                  visibleYMax = yValue;
-                }
+              // Is the visible Y-axis max larger.
+              if (yValue > visibleYMax) {
+                visibleYMax = yValue;
+              }
 
-                currentTracesData[index] = PointAndBase(
+              currentTracesData[index] = PointAndBase(
+                xCoord,
+                yCoord,
+                yBase: chartController.yPositionToYCanvasCoord(stackedY),
+              );
+
+              if (trace.chartType == ChartType.symbol) {
+                assert(!trace.stacked);
+                drawSymbol(
+                  canvas,
+                  trace.characteristics,
                   xCoord,
                   yCoord,
-                  yBase: chartController.yPositionToYCanvasCoord(stackedY),
+                  hasMultipleExtensionEvents,
+                  trace.symbolPath,
                 );
+              } else if (trace.chartType == ChartType.line) {
+                if (trace.characteristics.symbol == ChartSymbol.dashedLine) {
+                  // TODO(terry): Collect all points and draw a dashed line using
+                  // path_drawing package.
+                  drawDashed(
+                    canvas,
+                    trace.characteristics,
+                    xCoord,
+                    yCoord,
+                    chartController.tickWidth - 4,
+                  );
+                } else if (previousTracesData[index] != null) {
+                  final previous = previousTracesData[index]!;
+                  final current = currentTracesData[index]!;
 
-                if (trace.chartType == ChartType.symbol) {
-                  assert(!trace.stacked);
+                  // Stacked lines.
+                  // Drawline from previous plotted point to new point.
+                  drawConnectedLine(
+                    canvas,
+                    trace.characteristics,
+                    xCoord,
+                    yCoord,
+                    previous.x,
+                    previous.y,
+                  );
                   drawSymbol(
                     canvas,
                     trace.characteristics,
@@ -280,83 +298,41 @@
                     hasMultipleExtensionEvents,
                     trace.symbolPath,
                   );
-                } else if (trace.chartType == ChartType.line) {
-                  if (trace.characteristics.symbol == ChartSymbol.dashedLine) {
-                    // TODO(terry): Collect all points and draw a dashed line using
-                    // path_drawing package.
-                    drawDashed(
-                      canvas,
-                      trace.characteristics,
-                      xCoord,
-                      yCoord,
-                      chartController.tickWidth - 4,
-                    );
-                  } else if (previousTracesData[index] != null) {
-                    final previous = previousTracesData[index]!;
-                    final current = currentTracesData[index]!;
-
-                    // Stacked lines.
-                    // Drawline from previous plotted point to new point.
-                    drawConnectedLine(
-                      canvas,
-                      trace.characteristics,
-                      xCoord,
-                      yCoord,
-                      previous.x,
-                      previous.y,
-                    );
-                    drawSymbol(
-                      canvas,
-                      trace.characteristics,
-                      xCoord,
-                      yCoord,
-                      hasMultipleExtensionEvents,
-                      trace.symbolPath,
-                    );
-                    // TODO(terry): Honor z-order and also maybe path just on the traces e.g.,
-                    //              fill from top of trace 0 to top of trace 1 don't origin
-                    //              from zero.
-                    // Fill area between traces.
-                    drawFillArea(
-                      canvas,
-                      trace.characteristics,
-                      previous.x,
-                      previous.y,
-                      previous.base!,
-                      current.x,
-                      current.y,
-                      current.base!,
-                    );
-                  } else {
-                    // Draw point
-                    drawSymbol(
-                      canvas,
-                      trace.characteristics,
-                      xCoord,
-                      yCoord,
-                      hasMultipleExtensionEvents,
-                      trace.symbolPath,
-                    );
-                  }
+                  // TODO(terry): Honor z-order and also maybe path just on the traces e.g.,
+                  //              fill from top of trace 0 to top of trace 1 don't origin
+                  //              from zero.
+                  // Fill area between traces.
+                  drawFillArea(
+                    canvas,
+                    trace.characteristics,
+                    previous.x,
+                    previous.y,
+                    previous.base!,
+                    current.x,
+                    current.y,
+                    current.base!,
+                  );
+                } else {
+                  // Draw point
+                  drawSymbol(
+                    canvas,
+                    trace.characteristics,
+                    xCoord,
+                    yCoord,
+                    hasMultipleExtensionEvents,
+                    trace.symbolPath,
+                  );
                 }
-                tracesDataIndex[index]--;
-              },
-            );
+              }
+              tracesDataIndex[index]--;
+            });
 
             final tapLocation = chartController.tapLocation.value;
             if (tapLocation?.index == xTickIndex ||
                 tapLocation?.timestamp == currentTimestamp) {
-              drawTranslate(
-                canvas,
-                xTranslation,
-                yTranslation,
-                (canavas) {
-                  drawSelection(
-                    canvas,
-                    xCanvasCoord,
-                  );
-                },
-              );
+              drawTranslate(canvas, xTranslation, yTranslation, (canavas) {
+                drawSelection(canvas, xCanvasCoord);
+              });
             }
           }
 
@@ -378,36 +354,30 @@
 
     if (chartController.displayXAxis || chartController.displayXLabels) {
       // Y translation is below X-axis line.
-      drawTranslate(
+      drawTranslate(canvas, xTranslation, chartController.zeroYPosition + 1, (
         canvas,
-        xTranslation,
-        chartController.zeroYPosition + 1,
-        (canvas) {
-          // Draw the X-axis labels.
-          for (final timestamp in chartController.labelTimestamps) {
-            final xCoord = chartController.timestampToXCanvasCoord(timestamp);
-            drawXTick(canvas, timestamp, xCoord, axis, displayTime: true);
-          }
-        },
-      );
+      ) {
+        // Draw the X-axis labels.
+        for (final timestamp in chartController.labelTimestamps) {
+          final xCoord = chartController.timestampToXCanvasCoord(timestamp);
+          drawXTick(canvas, timestamp, xCoord, axis, displayTime: true);
+        }
+      });
 
       // X translation is left-most edge of chart widget.
-      drawTranslate(
+      drawTranslate(canvas, chartController.xCanvasChart, yTranslation, (
         canvas,
-        chartController.xCanvasChart,
-        yTranslation,
-        (canvas) {
-          // Rescale Y-axis to max visible Y range.
-          chartController.resetYMaxValue(visibleYMax);
+      ) {
+        // Rescale Y-axis to max visible Y range.
+        chartController.resetYMaxValue(visibleYMax);
 
-          // Draw Y-axis ticks and labels.
-          // TODO(terry): Optimization add a listener for Y-axis range changing
-          //              only need to redraw Y-axis if the range changed.
-          if (chartController.displayYLabels) {
-            drawYTicks(canvas, chartController, axis);
-          }
-        },
-      );
+        // Draw Y-axis ticks and labels.
+        // TODO(terry): Optimization add a listener for Y-axis range changing
+        //              only need to redraw Y-axis if the range changed.
+        if (chartController.displayYLabels) {
+          drawYTicks(canvas, chartController, axis);
+        }
+      });
     }
 
     drawTitle(canvas, size, chartController.title);
@@ -457,11 +427,7 @@
 
     // Left-side of chart
     if (displayY) {
-      canvas.drawLine(
-        const Offset(0, 0),
-        Offset(0, chartHeight),
-        axis,
-      );
+      canvas.drawLine(const Offset(0, 0), Offset(0, chartHeight), axis);
     }
 
     // Bottom line of chart.
@@ -475,9 +441,10 @@
   }
 
   void drawSelection(Canvas canvas, double x) {
-    final paint = Paint()
-      ..strokeWidth = 2.0
-      ..color = colorScheme.hoverSelectionBarColor;
+    final paint =
+        Paint()
+          ..strokeWidth = 2.0
+          ..color = colorScheme.hoverSelectionBarColor;
 
     // Draw the vertical selection bar.
     canvas.drawLine(
@@ -506,11 +473,7 @@
       drawText(labelName, canvas, -chartController.xCanvasChart / 2, yCoord);
 
       // Draw horizontal tick 6 pixels from Y-axis line.
-      canvas.drawLine(
-        Offset(0, yCoord),
-        Offset(-6, yCoord),
-        axis,
-      );
+      canvas.drawLine(Offset(0, yCoord), Offset(-6, yCoord), axis);
     }
   }
 
@@ -580,10 +543,7 @@
       final tp = createText(prettyTimestamp(timestamp), 1);
       tp.paint(
         canvas,
-        Offset(
-          xTickCoord - tp.width ~/ 2,
-          15.0 - tp.height ~/ 2,
-        ),
+        Offset(xTickCoord - tp.width ~/ 2, 15.0 - tp.height ~/ 2),
       );
     }
   }
@@ -644,12 +604,14 @@
         break;
     }
 
-    final paintFirst = Paint()
-      ..style = firstStyle
-      ..strokeWidth = characteristics.strokeWidth
-      ..color = aggregateEvents
-          ? characteristics.colorAggregate!
-          : characteristics.color;
+    final paintFirst =
+        Paint()
+          ..style = firstStyle
+          ..strokeWidth = characteristics.strokeWidth
+          ..color =
+              aggregateEvents
+                  ? characteristics.colorAggregate!
+                  : characteristics.color;
 
     switch (characteristics.symbol) {
       case ChartSymbol.dashedLine:
@@ -670,13 +632,15 @@
         canvas.drawCircle(Offset(x, y), characteristics.diameter, paintFirst);
 
         // Inner disc.
-        final paintSecond = Paint()
-          ..style = secondStyle
-          ..strokeWidth = 0
-          // TODO(terry): Aggregate for concentric maybe needed someday.
-          ..color = aggregateEvents
-              ? characteristics.colorAggregate!
-              : characteristics.concentricCenterColor;
+        final paintSecond =
+            Paint()
+              ..style = secondStyle
+              ..strokeWidth = 0
+              // TODO(terry): Aggregate for concentric maybe needed someday.
+              ..color =
+                  aggregateEvents
+                      ? characteristics.colorAggregate!
+                      : characteristics.concentricCenterColor;
         canvas.drawCircle(
           Offset(x, y),
           characteristics.concentricCenterDiameter,
@@ -691,10 +655,7 @@
       case ChartSymbol.triangleDown:
         // Draw symbol centered on [x,y] point (*).
         final path = symbolPathToDraw!.shift(
-          Offset(
-            x - characteristics.width / 2,
-            y - characteristics.height / 2,
-          ),
+          Offset(x - characteristics.width / 2, y - characteristics.height / 2),
         );
         canvas.drawPath(path, paintFirst);
         break;
@@ -710,13 +671,7 @@
     double tickWidth,
   ) {
     assert(characteristics.symbol == ChartSymbol.dashedLine);
-    drawLine(
-      canvas,
-      characteristics,
-      x,
-      y,
-      tickWidth,
-    );
+    drawLine(canvas, characteristics, x, y, tickWidth);
   }
 
   void drawConnectedLine(
@@ -727,10 +682,11 @@
     double endX,
     double endY,
   ) {
-    final paint = Paint()
-      ..style = PaintingStyle.stroke
-      ..strokeWidth = characteristics.strokeWidth
-      ..color = characteristics.color;
+    final paint =
+        Paint()
+          ..style = PaintingStyle.stroke
+          ..strokeWidth = characteristics.strokeWidth
+          ..color = characteristics.color;
 
     canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint);
   }
@@ -742,10 +698,11 @@
     double y,
     double tickWidth,
   ) {
-    final paint = Paint()
-      ..style = PaintingStyle.stroke
-      ..strokeWidth = characteristics.strokeWidth
-      ..color = characteristics.color;
+    final paint =
+        Paint()
+          ..style = PaintingStyle.stroke
+          ..strokeWidth = characteristics.strokeWidth
+          ..color = characteristics.color;
 
     canvas.drawLine(Offset(x, y), Offset(x + tickWidth, y), paint);
   }
@@ -762,16 +719,18 @@
     double y1,
     double y1Bottom,
   ) {
-    final paint = Paint()
-      ..style = PaintingStyle.fill
-      ..strokeWidth = characteristics.strokeWidth
-      ..color = characteristics.color.withAlpha(140);
+    final paint =
+        Paint()
+          ..style = PaintingStyle.fill
+          ..strokeWidth = characteristics.strokeWidth
+          ..color = characteristics.color.withAlpha(140);
 
-    final fillArea = Path()
-      ..moveTo(x0, y0)
-      ..lineTo(x1, y1)
-      ..lineTo(x1, y1Bottom)
-      ..lineTo(x0, y0Bottom);
+    final fillArea =
+        Path()
+          ..moveTo(x0, y0)
+          ..lineTo(x1, y1)
+          ..lineTo(x1, y1Bottom)
+          ..lineTo(x0, y0Bottom);
     fillArea.close();
     canvas.drawPath(fillArea, paint);
 
diff --git a/packages/devtools_app/lib/src/shared/charts/chart_controller.dart b/packages/devtools_app/lib/src/shared/charts/chart_controller.dart
index a8a1bea..65078e1 100644
--- a/packages/devtools_app/lib/src/shared/charts/chart_controller.dart
+++ b/packages/devtools_app/lib/src/shared/charts/chart_controller.dart
@@ -223,19 +223,22 @@
         final startOfLastNMinutes =
             // We need this cast to be able to return null if nothing is found.
             // ignore: unnecessary_cast
-            timestamps.reversed.firstWhereOrNull((timestamp) {
-          final currentDT = DateTime.fromMillisecondsSinceEpoch(timestamp);
-          final diff = lastDT.difference(currentDT);
-          if (diff.inMinutes >= duration.inMinutes) {
-            return true;
-          }
+            timestamps
+            .reversed
+            .firstWhereOrNull((timestamp) {
+              final currentDT = DateTime.fromMillisecondsSinceEpoch(timestamp);
+              final diff = lastDT.difference(currentDT);
+              if (diff.inMinutes >= duration.inMinutes) {
+                return true;
+              }
 
-          return false;
-        });
+              return false;
+            });
 
-        final ticksVisible = startOfLastNMinutes != null
-            ? timestampsLength - timestamps.indexOf(startOfLastNMinutes)
-            : timestampsLength + 1;
+        final ticksVisible =
+            startOfLastNMinutes != null
+                ? timestampsLength - timestamps.indexOf(startOfLastNMinutes)
+                : timestampsLength + 1;
         _tickWidth = canvasChartWidth / ticksVisible;
       } else {
         // No but lets scale x-axis based on the last two timestamps diffs we have.
@@ -245,8 +248,9 @@
         // Enough data (at least 2 points) to know how many ticks for the duration.
         if (length > 1) {
           final lastTS = DateTime.fromMillisecondsSinceEpoch(timestamps.last);
-          final previousTS =
-              DateTime.fromMillisecondsSinceEpoch(timestamps[length - 2]);
+          final previousTS = DateTime.fromMillisecondsSinceEpoch(
+            timestamps[length - 2],
+          );
           final diffTS = lastTS.difference(previousTS);
           final ticksPerMinute = oneMinuteInMs / diffTS.inMilliseconds;
           final ticksVisible = ticksPerMinute * duration.inMinutes;
@@ -358,10 +362,12 @@
     if (labelTimestamps.isEmpty) return;
 
     final rightLabelTimestamp = labelTimestamps.last;
-    final rightMostLabelDT =
-        DateTime.fromMillisecondsSinceEpoch(rightLabelTimestamp);
-    final rightMostTimestampDT =
-        DateTime.fromMillisecondsSinceEpoch(timestamps.last);
+    final rightMostLabelDT = DateTime.fromMillisecondsSinceEpoch(
+      rightLabelTimestamp,
+    );
+    final rightMostTimestampDT = DateTime.fromMillisecondsSinceEpoch(
+      timestamps.last,
+    );
 
     final nSeconds =
         rightMostTimestampDT.difference(rightMostLabelDT).inSeconds;
@@ -565,9 +571,9 @@
   /// but the multiple charts tied to the same timeline should be highlighted
   /// (selection point).
   TapLocation.copy(TapLocation original)
-      : tapDownDetails = null,
-        timestamp = original.timestamp,
-        index = original.index;
+    : tapDownDetails = null,
+      timestamp = original.timestamp,
+      index = original.index;
 
   final TapDownDetails? tapDownDetails;
 
diff --git a/packages/devtools_app/lib/src/shared/charts/chart_trace.dart b/packages/devtools_app/lib/src/shared/charts/chart_trace.dart
index 5ec81c8..74792a6 100644
--- a/packages/devtools_app/lib/src/shared/charts/chart_trace.dart
+++ b/packages/devtools_app/lib/src/shared/charts/chart_trace.dart
@@ -197,12 +197,7 @@
       _symbolPath = Path();
 
       _symbolPath!.addRect(
-        Rect.fromLTWH(
-          0,
-          0,
-          characteristics.width,
-          characteristics.height,
-        ),
+        Rect.fromLTWH(0, 0, characteristics.width, characteristics.height),
       );
 
       _symbolPath!.close();
@@ -270,10 +265,7 @@
   TraceNotifier();
 }
 
-enum ChartType {
-  symbol,
-  line,
-}
+enum ChartType { symbol, line }
 
 enum ChartSymbol {
   ring, // Lined circle
@@ -365,16 +357,17 @@
     // Fractional part of range.
     final fraction = range / pow(10, exponent);
     // Nice, rounded fraction.
-    final niceFraction = round
-        ? fraction.roundToDouble()
-        : switch (fraction) {
-            <= 1 => 1.0,
-            <= 2 => 2.0,
-            <= 3 => 3.0,
-            <= 5 => 5.0,
-            <= 7 => 7.0,
-            _ => 10.0,
-          };
+    final niceFraction =
+        round
+            ? fraction.roundToDouble()
+            : switch (fraction) {
+              <= 1 => 1.0,
+              <= 2 => 2.0,
+              <= 3 => 3.0,
+              <= 5 => 5.0,
+              <= 7 => 7.0,
+              _ => 10.0,
+            };
 
     return niceFraction * pow(10, exponent);
   }
diff --git a/packages/devtools_app/lib/src/shared/charts/flame_chart.dart b/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
index 721a1d9..2c97078 100644
--- a/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
+++ b/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
@@ -13,15 +13,15 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
-import '../common_widgets.dart';
 import '../primitives/extent_delegate_list.dart';
 import '../primitives/flutter_widgets/linked_scroll_controller.dart';
 import '../primitives/trees.dart';
 import '../primitives/utils.dart';
 import '../ui/colors.dart';
+import '../ui/common_widgets.dart';
 import '../ui/search.dart';
 import '../ui/utils.dart';
-import '../utils.dart';
+import '../utils/utils.dart';
 
 const rowPadding = 2.0;
 // Flame chart rows contain text so are not readable if they do not scale with
@@ -98,8 +98,11 @@
 // performance improvements. Optionally we could also do something clever with
 // grouping nodes that are close together until they are zoomed in (quad tree
 // like implementation).
-abstract class FlameChartState<T extends FlameChart,
-        V extends FlameChartDataMixin<V>> extends State<T>
+abstract class FlameChartState<
+  T extends FlameChart,
+  V extends FlameChartDataMixin<V>
+>
+    extends State<T>
     with AutoDisposeMixin, FlameChartColorMixin, TickerProviderStateMixin {
   int get rowOffsetForTopPadding => 2;
 
@@ -156,14 +159,16 @@
 
   TimeRange get visibleTimeRange {
     final horizontalScrollOffset = horizontalControllerGroup.offset;
-    final startMicros = horizontalScrollOffset < widget.startInset
-        ? startTimeOffset
-        : startTimeOffset +
-            (horizontalScrollOffset - widget.startInset) /
-                currentZoom /
-                startingPxPerMicro;
+    final startMicros =
+        horizontalScrollOffset < widget.startInset
+            ? startTimeOffset
+            : startTimeOffset +
+                (horizontalScrollOffset - widget.startInset) /
+                    currentZoom /
+                    startingPxPerMicro;
 
-    final endMicros = startTimeOffset +
+    final endMicros =
+        startTimeOffset +
         (horizontalScrollOffset - widget.startInset + widget.containerWidth) /
             currentZoom /
             startingPxPerMicro;
@@ -231,8 +236,9 @@
     )..addListener(_handleZoomControllerValueUpdate);
 
     verticalExtentDelegate = FixedExtentDelegate(
-      computeExtent: (index) =>
-          rows[index].nodes.isEmpty ? sectionSpacing : rowHeightWithPadding,
+      computeExtent:
+          (index) =>
+              rows[index].nodes.isEmpty ? sectionSpacing : rowHeightWithPadding,
       computeLength: () => rows.length,
     );
 
@@ -299,12 +305,7 @@
                 final chartOverlays = buildChartOverlays(constraints, context);
                 final flameChart = _buildFlameChart(constraints);
                 return chartOverlays.isNotEmpty
-                    ? Stack(
-                        children: [
-                          flameChart,
-                          ...chartOverlays,
-                        ],
-                      )
+                    ? Stack(children: [flameChart, ...chartOverlays])
                     : flameChart;
               },
             ),
@@ -395,10 +396,11 @@
       return;
     }
 
-    final hoverNodeData = _binarySearchForNode(
-      x: event.localPosition.dx + horizontalControllerGroup.offset,
-      nodesInRow: nodes,
-    )?.data;
+    final hoverNodeData =
+        _binarySearchForNode(
+          x: event.localPosition.dx + horizontalControllerGroup.offset,
+          nodesInRow: nodes,
+        )?.data;
     _hoveredNodeNotifier.value = hoverNodeData;
   }
 
@@ -419,7 +421,8 @@
   int _rowIndexForY(double dy) {
     final topPaddingHeight = rowOffsetForTopPadding * sectionSpacing;
     final adjustedDy = verticalControllerGroup.offset + dy;
-    final rowIndex = ((adjustedDy - topPaddingHeight) ~/ rowHeightWithPadding) +
+    final rowIndex =
+        ((adjustedDy - topPaddingHeight) ~/ rowHeightWithPadding) +
         rowOffsetForTopPadding;
     if (rowIndex < 0 || rowIndex >= rows.length) {
       return -1;
@@ -466,21 +469,13 @@
     final eventKey = event.physicalKey;
     if (eventKey == PhysicalKeyboardKey.keyW) {
       unawaited(
-        zoomTo(
-          math.min(
-            maxZoomLevel,
-            currentZoom + keyboardZoomInUnit,
-          ),
-        ),
+        zoomTo(math.min(maxZoomLevel, currentZoom + keyboardZoomInUnit)),
       );
       return KeyEventResult.handled;
     } else if (eventKey == PhysicalKeyboardKey.keyS) {
       unawaited(
         zoomTo(
-          math.max(
-            FlameChart.minZoomLevel,
-            currentZoom - keyboardZoomOutUnit,
-          ),
+          math.max(FlameChart.minZoomLevel, currentZoom - keyboardZoomOutUnit),
         ),
       );
       return KeyEventResult.handled;
@@ -511,10 +506,13 @@
     final fixedX = safeMouseHoverX + lastScrollOffset - widget.startInset;
 
     // Calculate the new horizontal scroll position.
-    final newScrollOffset = fixedX >= 0
-        ? fixedX * newZoom / previousZoom + widget.startInset - safeMouseHoverX
-        // We are in the fixed portion of the window - no need to transform.
-        : lastScrollOffset;
+    final newScrollOffset =
+        fixedX >= 0
+            ? fixedX * newZoom / previousZoom +
+                widget.startInset -
+                safeMouseHoverX
+            // We are in the fixed portion of the window - no need to transform.
+            : lastScrollOffset;
 
     setState(() {
       currentZoom = zoomController.value;
@@ -549,10 +547,7 @@
   /// this method, the call to this method should be placed inside of a
   /// postFrameCallback:
   /// `WidgetsBinding.instance.addPostFrameCallback((_) { ... });`.
-  FutureOr<void> scrollToX(
-    double offset, {
-    bool jump = false,
-  }) async {
+  FutureOr<void> scrollToX(double offset, {bool jump = false}) async {
     final target = offset.clamp(
       FlameChart.minScrollOffset,
       horizontalControllerGroup.position.maxScrollExtent,
@@ -624,7 +619,8 @@
     targetWidth ??= widget.containerWidth * 0.8;
     final startingWidth = durationMicros * startingPxPerMicro;
     final zoom = targetWidth / startingWidth;
-    final mouseXForZoom = (startMicros - startTimeOffset + durationMicros / 2) *
+    final mouseXForZoom =
+        (startMicros - startTimeOffset + durationMicros / 2) *
             startingPxPerMicro +
         widget.startInset;
     await zoomTo(zoom, forceMouseX: mouseXForZoom, jump: jump);
@@ -681,7 +677,8 @@
 }
 
 class ScrollingFlameChartRowState<V extends FlameChartDataMixin<V>>
-    extends State<ScrollingFlameChartRow<V>> with AutoDisposeMixin {
+    extends State<ScrollingFlameChartRow<V>>
+    with AutoDisposeMixin {
   late final ScrollController scrollController;
 
   late final _ScrollingFlameChartRowExtentDelegate _extentDelegate;
@@ -916,7 +913,7 @@
     } else {
       padding =
           (node.rect.left - chartStartInset) * zoomForNode(node, chartZoom) +
-              chartStartInset;
+          chartStartInset;
     }
     // Floating point rounding error can result in slightly negative padding.
     return math.max(0.0, padding);
@@ -943,11 +940,12 @@
     // Node right with zoom and insets taken into consideration.
     final nodeRight =
         (node.rect.right - chartStartInset) * nodeZoom + chartStartInset;
-    final padding = nextNode == null
-        ? chartWidth - nodeRight
-        : ((nextNode.rect.left - chartStartInset) * nextNodeZoom +
-                chartStartInset) -
-            nodeRight;
+    final padding =
+        nextNode == null
+            ? chartWidth - nodeRight
+            : ((nextNode.rect.left - chartStartInset) * nextNodeZoom +
+                    chartStartInset) -
+                nodeRight;
     // Floating point rounding error can result in slightly negative padding.
     return math.max(0.0, padding);
   }
@@ -987,11 +985,7 @@
 }
 
 class FlameChartSection {
-  FlameChartSection(
-    this.index, {
-    required this.startRow,
-    required this.endRow,
-  });
+  FlameChartSection(this.index, {required this.startRow, required this.endRow});
 
   final int index;
 
@@ -1096,28 +1090,25 @@
         activeSearchMatch: activeSearchMatch,
         colorScheme: theme.colorScheme,
       ),
-      child: zoomedWidth >= _minWidthForText
-          ? Text(
-              text,
-              textAlign: TextAlign.left,
-              overflow: TextOverflow.ellipsis,
-              style: theme.regularTextStyleWithColor(
-                _textColor(
-                  selected: selected,
-                  searchMatch: searchMatch,
-                  activeSearchMatch: activeSearchMatch,
-                  colorScheme: theme.colorScheme,
+      child:
+          zoomedWidth >= _minWidthForText
+              ? Text(
+                text,
+                textAlign: TextAlign.left,
+                overflow: TextOverflow.ellipsis,
+                style: theme.regularTextStyleWithColor(
+                  _textColor(
+                    selected: selected,
+                    searchMatch: searchMatch,
+                    activeSearchMatch: activeSearchMatch,
+                    colorScheme: theme.colorScheme,
+                  ),
                 ),
-              ),
-            )
-          : const SizedBox(),
+              )
+              : const SizedBox(),
     );
     return (hovered || !selectable)
-        ? DevToolsTooltip(
-            key: key,
-            message: data.tooltip,
-            child: node,
-          )
+        ? DevToolsTooltip(key: key, message: data.tooltip, child: node)
         : node;
   }
 
@@ -1156,8 +1147,10 @@
     // small events that have padding.
     //
     // See https://github.com/flutter/devtools/issues/1503 for details.
-    final zoomedLeft =
-        math.max(0.0, (rect.left - chartStartInset) * zoom + chartStartInset);
+    final zoomedLeft = math.max(
+      0.0,
+      (rect.left - chartStartInset) * zoom + chartStartInset,
+    );
     final zoomedWidth = math.max(0.0, rect.width * zoom);
     return Rect.fromLTWH(zoomedLeft, rect.top, zoomedWidth, rect.height);
   }
@@ -1353,9 +1346,11 @@
     final microsPerInterval = _microsPerInterval(intervalWidth);
     int timestampMicros = _startingTimestamp(intervalWidth, microsPerInterval);
     double lineX;
-    lineX = visible.left <= chartStartInset
-        ? chartStartInset - visible.left
-        : intervalWidth - ((visible.left - chartStartInset) % intervalWidth);
+    lineX =
+        visible.left <= chartStartInset
+            ? chartStartInset - visible.left
+            : intervalWidth -
+                ((visible.left - chartStartInset) % intervalWidth);
 
     while (lineX < constraints.maxWidth) {
       _paintTimestamp(canvas, timestampMicros, intervalWidth, lineX);
@@ -1426,9 +1421,10 @@
   }
 
   int _startingTimestamp(double intervalWidth, int microsPerInterval) {
-    final startingIntervalIndex = horizontalScrollOffset < chartStartInset
-        ? 0
-        : (horizontalScrollOffset - chartStartInset) ~/ intervalWidth + 1;
+    final startingIntervalIndex =
+        horizontalScrollOffset < chartStartInset
+            ? 0
+            : (horizontalScrollOffset - chartStartInset) ~/ intervalWidth + 1;
     return startingIntervalIndex * microsPerInterval;
   }
 
@@ -1448,13 +1444,13 @@
 
   @override
   int get hashCode => Object.hash(
-        zoom,
-        constraints,
-        flameChartWidth,
-        horizontalScrollOffset,
-        duration,
-        colorScheme,
-      );
+    zoom,
+    constraints,
+    flameChartWidth,
+    horizontalScrollOffset,
+    duration,
+    colorScheme,
+  );
 }
 
 class FlameChartHelpButton extends StatelessWidget {
@@ -1502,18 +1498,9 @@
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.end,
             children: [
-              Text(
-                'WASD • ',
-                style: theme.fixedFontStyle,
-              ),
-              Text(
-                'click + drag • ',
-                style: theme.fixedFontStyle,
-              ),
-              Text(
-                'click + fling • ',
-                style: theme.fixedFontStyle,
-              ),
+              Text('WASD • ', style: theme.fixedFontStyle),
+              Text('click + drag • ', style: theme.fixedFontStyle),
+              Text('click + fling • ', style: theme.fixedFontStyle),
             ],
           ),
         ),
@@ -1555,11 +1542,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return Container(
-      height: height,
-      width: width,
-      color: backgroundColor,
-    );
+    return Container(height: height, width: width, color: backgroundColor);
   }
 }
 
diff --git a/packages/devtools_app/lib/src/shared/charts/treemap.dart b/packages/devtools_app/lib/src/shared/charts/treemap.dart
index efdf3ef..80b8050 100644
--- a/packages/devtools_app/lib/src/shared/charts/treemap.dart
+++ b/packages/devtools_app/lib/src/shared/charts/treemap.dart
@@ -8,10 +8,10 @@
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 
-import '../common_widgets.dart';
 import '../primitives/byte_utils.dart';
 import '../primitives/trees.dart';
 import '../ui/colors.dart';
+import '../ui/common_widgets.dart';
 
 enum PivotType { pivotByMiddle, pivotBySize }
 
@@ -234,13 +234,15 @@
         final pivotAndList2Ratio = (pivotByteSize + list2Size) / totalByteSize;
         final pivotRatio = pivotByteSize / (pivotByteSize + list2Size);
 
-        final pivotWidth = isHorizontalRectangle
-            ? pivotAndList2Ratio * width
-            : pivotRatio * width;
+        final pivotWidth =
+            isHorizontalRectangle
+                ? pivotAndList2Ratio * width
+                : pivotRatio * width;
 
-        final pivotHeight = isHorizontalRectangle
-            ? pivotRatio * height
-            : pivotAndList2Ratio * height;
+        final pivotHeight =
+            isHorizontalRectangle
+                ? pivotRatio * height
+                : pivotAndList2Ratio * height;
 
         final pivotAspectRatio = pivotWidth / pivotHeight;
 
@@ -267,12 +269,14 @@
       final pivotAndList2Ratio =
           (pivotByteSize + list2ByteSize) / totalByteSize;
       final pivotRatio = pivotByteSize / (pivotByteSize + list2ByteSize);
-      pivotBestWidth = isHorizontalRectangle
-          ? pivotAndList2Ratio * width
-          : pivotRatio * width;
-      pivotBestHeight = isHorizontalRectangle
-          ? pivotRatio * height
-          : pivotAndList2Ratio * height;
+      pivotBestWidth =
+          isHorizontalRectangle
+              ? pivotAndList2Ratio * width
+              : pivotRatio * width;
+      pivotBestHeight =
+          isHorizontalRectangle
+              ? pivotRatio * height
+              : pivotAndList2Ratio * height;
     }
 
     final positionedTreemaps = <PositionedCell>[];
@@ -396,7 +400,8 @@
             // A key is required to force a rebuild of the tooltips for each cell.
             // Use tooltipMessage as the key to prevent rebuilds within a cell.
             key: Key(hoveredNode?.displayText() ?? ''),
-            richMessage: hoveredNode?.displayTextSpan(
+            richMessage:
+                hoveredNode?.displayTextSpan(
                   defaultTextStyle: defaultTextStyle,
                 ) ??
                 const TextSpan(text: ''),
@@ -474,10 +479,7 @@
 
   Column buildTreemapFromRoot() {
     final rootNode = widget.rootNode!;
-    final child = _TreeMapCell(
-      treeMapHeight: widget.height,
-      node: rootNode,
-    );
+    final child = _TreeMapCell(treeMapHeight: widget.height, node: rootNode);
     return Column(
       children: [
         if (widget.isOutermostLevel)
@@ -487,13 +489,14 @@
             onRootChangedCallback: widget.onRootChangedCallback,
           ),
         Expanded(
-          child: widget.isOutermostLevel
-              ? child
-              : _SelectableTreemapNode(
-                  node: rootNode,
-                  onRootChangedCallback: widget.onRootChangedCallback,
-                  child: child,
-                ),
+          child:
+              widget.isOutermostLevel
+                  ? child
+                  : _SelectableTreemapNode(
+                    node: rootNode,
+                    onRootChangedCallback: widget.onRootChangedCallback,
+                    child: child,
+                  ),
         ),
       ],
     );
@@ -503,10 +506,7 @@
   /// a cell in [positionedCells].
   ///
   /// If so, saves the matching hoveredNode.
-  void _onHover(
-    PointerHoverEvent event,
-    List<PositionedCell> positionedCells,
-  ) {
+  void _onHover(PointerHoverEvent event, List<PositionedCell> positionedCells) {
     final x = event.localPosition.dx;
     final y = event.localPosition.dy;
     final touchPoint = Offset(x, y);
@@ -524,10 +524,7 @@
 }
 
 class _TreeMapCell extends StatelessWidget {
-  const _TreeMapCell({
-    required this.treeMapHeight,
-    required this.node,
-  });
+  const _TreeMapCell({required this.treeMapHeight, required this.node});
 
   final double treeMapHeight;
   final TreemapNode node;
@@ -540,13 +537,14 @@
         border: Border.all(color: Colors.black87),
       ),
       child: Center(
-        child: treeMapHeight > Treemap.minHeightToDisplayCellText
-            ? _NameAndSizeText(
-                node: node,
-                color: node.showDiff ? Colors.white : Colors.black,
-                singleLine: false,
-              )
-            : const SizedBox(),
+        child:
+            treeMapHeight > Treemap.minHeightToDisplayCellText
+                ? _NameAndSizeText(
+                  node: node,
+                  color: node.showDiff ? Colors.white : Colors.black,
+                  singleLine: false,
+                )
+                : const SizedBox(),
       ),
     );
   }
@@ -577,9 +575,7 @@
         child: Container(
           height: Treemap.treeMapHeaderHeight,
           width: double.infinity,
-          decoration: BoxDecoration(
-            border: Border.all(color: Colors.black87),
-          ),
+          decoration: BoxDecoration(border: Border.all(color: Colors.black87)),
           child: _NameAndSizeText(
             node: rootNode,
             color: Theme.of(context).colorScheme.onSurface,
@@ -635,9 +631,10 @@
         builder: (context, index) {
           final node = pathFromRoot[index];
           return Breadcrumb(
-            text: index < pathFromRoot.length - 1
-                ? node.name
-                : node.displayText(),
+            text:
+                index < pathFromRoot.length - 1
+                    ? node.name
+                    : node.displayText(),
             isRoot: index == 0,
             onPressed: () => onRootChangedCallback(node),
           );
@@ -831,11 +828,11 @@
     required this.node,
     required super.child,
   }) : super(
-          left: rect.left,
-          top: rect.top,
-          width: rect.width,
-          height: rect.height,
-        );
+         left: rect.left,
+         top: rect.top,
+         width: rect.width,
+         height: rect.height,
+       );
 
   final Rect rect;
 
@@ -873,9 +870,10 @@
     rectPaint.color = node.displayColor;
     canvas.drawRect(bounds, rectPaint);
 
-    final borderPaint = Paint()
-      ..color = Colors.black45
-      ..style = PaintingStyle.stroke;
+    final borderPaint =
+        Paint()
+          ..color = Colors.black45
+          ..style = PaintingStyle.stroke;
     canvas.drawRect(bounds, borderPaint);
 
     if (positionedCell.width! > Treemap.minWidthToDisplayCellText &&
@@ -894,10 +892,7 @@
           positionedCell.left! + bounds.width / 2 - textPainter.width / 2;
       final centerY =
           positionedCell.top! + bounds.height / 2 - textPainter.height / 2;
-      textPainter.paint(
-        canvas,
-        Offset(centerX, centerY),
-      );
+      textPainter.paint(canvas, Offset(centerX, centerY));
     }
   }
 
diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart
index e00b987..721c75a 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart
@@ -10,10 +10,7 @@
   // listening for this command, then it will attempt to copy the contents
   // to the clipboard in the context of the parent frame.
   window.parent?.postMessage(
-    {
-      'command': 'clipboard-write',
-      'data': data,
-    }.jsify(),
+    {'command': 'clipboard-write', 'data': data}.jsify(),
     '*'.toJS,
   );
 }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
index 25eb106..eade739 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
@@ -65,8 +65,11 @@
   /// newly active [DragAndDrop] widgets accordingly.
   void hitTestAndUpdateActiveId(double x, double y) {
     final hitTestResult = HitTestResult();
-    RendererBinding.instance
-        .hitTestInView(hitTestResult, Offset(x, y), _viewId);
+    RendererBinding.instance.hitTestInView(
+      hitTestResult,
+      Offset(x, y),
+      _viewId,
+    );
 
     // Starting at bottom of [hitTestResult.path], look for the first
     // [DragAndDrop] widget. This widget will be marked by a [RenderMetaData]
@@ -94,11 +97,7 @@
 }
 
 class DragAndDrop extends StatefulWidget {
-  const DragAndDrop({
-    super.key,
-    required this.child,
-    this.handleDrop,
-  });
+  const DragAndDrop({super.key, required this.child, this.handleDrop});
 
   /// Callback to handle parsed data from drag and drop.
   ///
@@ -151,18 +150,19 @@
     _refreshDragAndDropManager(View.of(context).viewId);
     return MetaData(
       metaData: DragAndDropMetaData(state: this),
-      child: widget.handleDrop != null
-          ? ValueListenableBuilder<bool>(
-              valueListenable: _dragging,
-              builder: (context, dragging, _) {
-                // TODO(kenz): use AnimatedOpacity instead.
-                return Opacity(
-                  opacity: dragging ? 0.5 : 1.0,
-                  child: widget.child,
-                );
-              },
-            )
-          : widget.child,
+      child:
+          widget.handleDrop != null
+              ? ValueListenableBuilder<bool>(
+                valueListenable: _dragging,
+                builder: (context, dragging, _) {
+                  // TODO(kenz): use AnimatedOpacity instead.
+                  return Opacity(
+                    opacity: dragging ? 0.5 : 1.0,
+                    child: widget.child,
+                  );
+                },
+              )
+              : widget.child,
     );
   }
 
diff --git a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart
index d091c9c..f8fceb0 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart
@@ -17,7 +17,9 @@
 /// Return the url the application is launched from.
 Future<String> initializePlatform() async {
   // Clear out the unneeded HTML from index.html.
-  document.body!.querySelectorAll('.legacy-dart').forEach(
+  document.body!
+      .querySelectorAll('.legacy-dart')
+      .forEach(
         (Node element) {
           if (element.parentNode != null) {
             element.parentNode!.removeChild(element);
diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_desktop.dart
index 7529b7a..8a4e9fa 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_desktop.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_desktop.dart
@@ -15,10 +15,7 @@
   static final _fs = FileSystemDesktop();
 
   @override
-  void saveFile<T>({
-    required T content,
-    required String fileName,
-  }) {
+  void saveFile<T>({required T content, required String fileName}) {
     _fs.writeContentsToFile<T>(fileName, content);
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart
index ab38377..c948514 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart
@@ -17,10 +17,7 @@
   ExportControllerWeb() : super.impl();
 
   @override
-  void saveFile<T>({
-    required T content,
-    required String fileName,
-  }) {
+  void saveFile<T>({required T content, required String fileName}) {
     final element = document.createElement('a') as HTMLAnchorElement;
 
     final Blob blob;
diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart
index 2745f3e..b774c2d 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart
@@ -7,14 +7,15 @@
 import 'package:devtools_app_shared/service.dart';
 import 'package:intl/intl.dart';
 
+import '../../framework/screen.dart';
 import '../../globals.dart';
 import '../../primitives/encoding.dart';
 import '../../primitives/utils.dart';
-import '../../screen.dart';
-import '../../utils.dart';
+import '../../utils/utils.dart';
 import '_export_desktop.dart' if (dart.library.js_interop) '_export_web.dart';
 
-const nonDevToolsFileMessage = 'The imported file is not a Dart DevTools file.'
+const nonDevToolsFileMessage =
+    'The imported file is not a Dart DevTools file.'
     ' At this time, DevTools only supports importing files that were originally'
     ' exported from DevTools.';
 
@@ -34,9 +35,7 @@
 }
 
 class ImportController {
-  ImportController(
-    this._pushSnapshotScreenForImport,
-  );
+  ImportController(this._pushSnapshotScreenForImport);
 
   static const repeatImportTimeBufferMs = 500;
 
@@ -59,7 +58,8 @@
     previousImportTime = now;
 
     final json = jsonFile.data;
-    final isDevToolsSnapshot = json is Map<String, Object?> &&
+    final isDevToolsSnapshot =
+        json is Map<String, Object?> &&
         json[DevToolsExportKeys.devToolsSnapshot.name] == true;
     if (!isDevToolsSnapshot) {
       notificationService.push(nonDevToolsFileMessage);
@@ -89,8 +89,9 @@
       }
     }
 
-    final connectedApp =
-        OfflineConnectedApp.parse(devToolsOfflineData.connectedApp);
+    final connectedApp = OfflineConnectedApp.parse(
+      devToolsOfflineData.connectedApp,
+    );
     offlineDataController
       ..startShowingOfflineData(offlineApp: connectedApp)
       ..offlineDataJson = devToolsOfflineData.json;
@@ -153,10 +154,7 @@
   }
 
   /// Saves [content] to the [fileName].
-  void saveFile<T>({
-    required T content,
-    required String fileName,
-  });
+  void saveFile<T>({required T content, required String fileName});
 
   Map<String, Object?> generateDataForExport({
     required Map<String, Object?> offlineScreenData,
@@ -165,7 +163,8 @@
     final contents = {
       DevToolsExportKeys.devToolsSnapshot.name: true,
       DevToolsExportKeys.devToolsVersion.name: devToolsVersion,
-      DevToolsExportKeys.connectedApp.name: connectedApp?.toJson() ??
+      DevToolsExportKeys.connectedApp.name:
+          connectedApp?.toJson() ??
           serviceConnection.serviceManager.connectedApp!.toJson(),
       ...offlineScreenData,
     };
@@ -176,9 +175,6 @@
 
   String encode(Map<String, Object?> offlineScreenData) {
     final data = generateDataForExport(offlineScreenData: offlineScreenData);
-    return jsonEncode(
-      data,
-      toEncodable: toEncodable,
-    );
+    return jsonEncode(data, toEncodable: toEncodable);
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart
index 5af1b87..77681b9 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart
@@ -4,8 +4,4 @@
 
 export '_logger_io.dart' if (dart.library.js_interop) '_logger_web.dart';
 
-enum LogLevel {
-  debug,
-  warning,
-  error,
-}
+enum LogLevel { debug, warning, error }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart
index 1eec76c..bb32d53 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart
@@ -8,10 +8,7 @@
 
 class Notification {
   Notification(String title, {String body = ''}) {
-    _impl = web.Notification(
-      title,
-      web.NotificationOptions(body: body),
-    );
+    _impl = web.Notification(title, web.NotificationOptions(body: body));
   }
 
   late final web.Notification _impl;
diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart
index 063bdaa..cf86de7 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart
@@ -7,5 +7,5 @@
 Stream<PostMessageEvent> get onPostMessage =>
     throw UnsupportedError('unsupported platform');
 
-void postMessage(Object? _, String __) =>
+void postMessage(Object? _, String _) =>
     throw UnsupportedError('unsupported platform');
diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart
index 36d15b6..c5096f8 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart
@@ -10,10 +10,8 @@
 
 Stream<PostMessageEvent> get onPostMessage {
   return window.onMessage.map(
-    (message) => PostMessageEvent(
-      origin: message.origin,
-      data: message.data.dartify(),
-    ),
+    (message) =>
+        PostMessageEvent(origin: message.origin, data: message.data.dartify()),
   );
 }
 
diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart
index a13c74d..0263376 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart
@@ -6,10 +6,7 @@
     if (dart.library.js_interop) '_post_message_web.dart';
 
 class PostMessageEvent {
-  PostMessageEvent({
-    required this.origin,
-    required this.data,
-  });
+  PostMessageEvent({required this.origin, required this.data});
 
   final String origin;
   final Object? data;
diff --git a/packages/devtools_app/lib/src/shared/console/console.dart b/packages/devtools_app/lib/src/shared/console/console.dart
index 23aa981..27514c4 100644
--- a/packages/devtools_app/lib/src/shared/console/console.dart
+++ b/packages/devtools_app/lib/src/shared/console/console.dart
@@ -9,8 +9,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
-import '../common_widgets.dart';
 import '../primitives/utils.dart';
+import '../ui/common_widgets.dart';
 import 'console_service.dart';
 import 'widgets/expandable_variable.dart';
 
@@ -21,12 +21,7 @@
 /// Renders a Console widget with output [lines] and an optional [title] and
 /// [footer].
 class Console extends StatelessWidget {
-  const Console({
-    super.key,
-    required this.lines,
-    this.title,
-    this.footer,
-  });
+  const Console({super.key, required this.lines, this.title, this.footer});
 
   final Widget? title;
   final Widget? footer;
@@ -42,11 +37,7 @@
 }
 
 class ConsoleFrame extends StatelessWidget {
-  const ConsoleFrame({
-    super.key,
-    required this.child,
-    this.title,
-  });
+  const ConsoleFrame({super.key, required this.child, this.title});
 
   final Widget? title;
   final Widget child;
@@ -57,12 +48,7 @@
       padding: const EdgeInsets.only(bottom: densePadding),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.stretch,
-        children: [
-          if (title != null) title!,
-          Expanded(
-            child: child,
-          ),
-        ],
+        children: [if (title != null) title!, Expanded(child: child)],
       ),
     );
   }
@@ -72,10 +58,7 @@
 ///
 /// This is a ListView of text lines, with a monospace font and a border.
 class _ConsoleOutput extends StatefulWidget {
-  const _ConsoleOutput({
-    required this.lines,
-    this.footer,
-  });
+  const _ConsoleOutput({required this.lines, this.footer});
 
   final ValueListenable<List<ConsoleLine>> lines;
 
@@ -187,7 +170,7 @@
             physics: const ClampingScrollPhysics(
               parent: RangeMaintainingScrollPhysics(),
             ),
-            separatorBuilder: (_, __) {
+            separatorBuilder: (_, _) {
               return const PaddedDivider.noPadding();
             },
             itemBuilder: (context, index) {
diff --git a/packages/devtools_app/lib/src/shared/console/console_service.dart b/packages/devtools_app/lib/src/shared/console/console_service.dart
index 473d970..6c01025 100644
--- a/packages/devtools_app/lib/src/shared/console/console_service.dart
+++ b/packages/devtools_app/lib/src/shared/console/console_service.dart
@@ -24,23 +24,13 @@
 /// TODO(jacobr): support console lines that are structured error messages as
 /// well.
 class ConsoleLine {
-  factory ConsoleLine.text(
-    String text, {
-    bool forceScrollIntoView = false,
-  }) =>
-      TextConsoleLine(
-        text,
-        forceScrollIntoView: forceScrollIntoView,
-      );
+  factory ConsoleLine.text(String text, {bool forceScrollIntoView = false}) =>
+      TextConsoleLine(text, forceScrollIntoView: forceScrollIntoView);
 
   factory ConsoleLine.dartObjectNode(
     DartObjectNode variable, {
     bool forceScrollIntoView = false,
-  }) =>
-      VariableConsoleLine(
-        variable,
-        forceScrollIntoView: forceScrollIntoView,
-      );
+  }) => VariableConsoleLine(variable, forceScrollIntoView: forceScrollIntoView);
 
   ConsoleLine._(this.forceScrollIntoView);
 
@@ -50,7 +40,7 @@
 
 class TextConsoleLine extends ConsoleLine {
   TextConsoleLine(this.text, {bool forceScrollIntoView = false})
-      : super._(forceScrollIntoView);
+    : super._(forceScrollIntoView);
   final String text;
 
   @override
@@ -61,9 +51,7 @@
 
 class VariableConsoleLine extends ConsoleLine {
   VariableConsoleLine(this.variable, {bool forceScrollIntoView = false})
-      : super._(
-          forceScrollIntoView,
-        );
+    : super._(forceScrollIntoView);
   final DartObjectNode variable;
 
   @override
@@ -178,8 +166,9 @@
         last is TextConsoleLine) {
       _stdio.last = ConsoleLine.text('${last.text}${newLines.first}');
       if (newLines.length > 1) {
-        _stdio
-            .addAll(newLines.sublist(1).map((text) => ConsoleLine.text(text)));
+        _stdio.addAll(
+          newLines.sublist(1).map((text) => ConsoleLine.text(text)),
+        );
       }
     } else {
       _stdio.addAll(newLines.map((text) => ConsoleLine.text(text)));
@@ -267,7 +256,9 @@
       );
       autoDisposeStreamSubscription(
         serviceConnection
-            .serviceManager.service!.onExtensionEventWithHistorySafe
+            .serviceManager
+            .service!
+            .onExtensionEventWithHistorySafe
             .listen(_handleExtensionEvent),
       );
       _serviceInitialized = true;
diff --git a/packages/devtools_app/lib/src/shared/console/eval/auto_complete.dart b/packages/devtools_app/lib/src/shared/console/eval/auto_complete.dart
index d717a2f..c037e30 100644
--- a/packages/devtools_app/lib/src/shared/console/eval/auto_complete.dart
+++ b/packages/devtools_app/lib/src/shared/console/eval/auto_complete.dart
@@ -8,7 +8,7 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../connected_app.dart';
+import '../../../service/connected_app/connected_app.dart';
 import '../../globals.dart';
 import '../../ui/search.dart';
 import 'eval_service.dart';
@@ -59,10 +59,7 @@
         final libraryRef = await evalService.findOwnerLibrary(function);
         if (libraryRef != null) {
           result.addAll(
-            await libraryMemberAndImportsAutocompletes(
-              libraryRef,
-              evalService,
-            ),
+            await libraryMemberAndImportsAutocompletes(libraryRef, evalService),
           );
         }
       }
@@ -113,11 +110,13 @@
   LibraryRef libraryRef,
   EvalService evalService,
 ) async {
-  final autocompletes =
-      await _appState.cache.libraryMemberAndImportsAutocomplete.putIfAbsent(
-    libraryRef,
-    () => _libraryMemberAndImportsAutocompletes(libraryRef, evalService),
-  );
+  final autocompletes = await _appState
+      .cache
+      .libraryMemberAndImportsAutocomplete
+      .putIfAbsent(
+        libraryRef,
+        () => _libraryMemberAndImportsAutocompletes(libraryRef, evalService),
+      );
 
   return autocompletes.nonNulls.toSet();
 }
@@ -171,11 +170,11 @@
   LibraryRef libraryRef, {
   required bool includePrivates,
 }) async {
-  var result = (await _appState.cache.libraryMemberAutocomplete.putIfAbsent(
-    libraryRef,
-    () => _libraryMemberAutocompletes(evalService, libraryRef),
-  ))
-      .nonNulls;
+  var result =
+      (await _appState.cache.libraryMemberAutocomplete.putIfAbsent(
+        libraryRef,
+        () => _libraryMemberAutocompletes(evalService, libraryRef),
+      )).nonNulls;
   if (!includePrivates) {
     result = result.where((name) => !isPrivateMember(name));
   }
@@ -196,8 +195,9 @@
   final functions = library.functions;
   if (functions != null) {
     // The VM shows setters as `<member>=`.
-    final members =
-        functions.map((funcRef) => funcRef.name!.replaceAll('=', ''));
+    final members = functions.map(
+      (funcRef) => funcRef.name!.replaceAll('=', ''),
+    );
     result.addAll(members.nonNulls);
   }
   final classes = library.classes;
@@ -242,11 +242,7 @@
   final classRef = instance.classRef;
   if (classRef == null) return;
   result.addAll(
-    await _autoCompleteMembersFor(
-      classRef,
-      controller,
-      staticContext: false,
-    ),
+    await _autoCompleteMembersFor(classRef, controller, staticContext: false),
   );
   final clazz = await controller.classFor(classRef);
   final fields = clazz?.fields;
@@ -255,9 +251,7 @@
       .where((field) => field.isStatic ?? false)
       .map((field) => field.name);
   result.addAll(
-    fieldNames.nonNulls.where(
-      (member) => _isAccessible(member, clazz),
-    ),
+    fieldNames.nonNulls.where((member) => _isAccessible(member, clazz)),
   );
 }
 
@@ -319,34 +313,31 @@
 }
 
 bool _isOperator(FuncRef funcRef) => const {
-      '==',
-      '+',
-      '-',
-      '*',
-      '/',
-      '&',
-      '~',
-      '|',
-      '>',
-      '<',
-      '>=',
-      '<=',
-      '>>',
-      '<<',
-      '>>>',
-      '^',
-      '%',
-      '~/',
-      'unary-',
-    }.contains(funcRef.name);
+  '==',
+  '+',
+  '-',
+  '*',
+  '/',
+  '&',
+  '~',
+  '|',
+  '>',
+  '<',
+  '>=',
+  '<=',
+  '>>',
+  '<<',
+  '>>>',
+  '^',
+  '%',
+  '~/',
+  'unary-',
+}.contains(funcRef.name);
 
 bool _isConstructor(FuncRef funcRef, Class clazz) =>
     funcRef.name == clazz.name || funcRef.name!.startsWith('${clazz.name}.');
 
-bool _isAccessible(
-  String member,
-  Class? clazz,
-) {
+bool _isAccessible(String member, Class? clazz) {
   final frame = _appState.currentFrame.value!;
   final currentScript = frame.location!.script;
   return !isPrivateMember(member) ||
diff --git a/packages/devtools_app/lib/src/shared/console/eval/eval_service.dart b/packages/devtools_app/lib/src/shared/console/eval/eval_service.dart
index 5238e64..94d39e5 100644
--- a/packages/devtools_app/lib/src/shared/console/eval/eval_service.dart
+++ b/packages/devtools_app/lib/src/shared/console/eval/eval_service.dart
@@ -11,7 +11,7 @@
 import '../../../service/vm_service_wrapper.dart';
 import '../../globals.dart';
 import '../../memory/heap_object.dart';
-import '../../vm_utils.dart';
+import '../../utils/vm_utils.dart';
 import '../primitives/scope.dart';
 
 class EvalService extends DisposableController with AutoDisposeControllerMixin {
@@ -24,7 +24,11 @@
 
   String? get _isolateRefId {
     return serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value?.id;
+        .serviceManager
+        .isolateManager
+        .selectedIsolate
+        .value
+        ?.id;
   }
 
   /// Returns the class for the provided [ClassRef].
@@ -165,12 +169,12 @@
     }
 
     Future<Response> evalFunction() => _service.evaluateInFrame(
-          isolateRefId,
-          frame.index!,
-          expression,
-          disableBreakpoints: true,
-          scope: scope.value(isolateId: isolateRefId),
-        );
+      isolateRefId,
+      frame.index!,
+      expression,
+      disableBreakpoints: true,
+      scope: scope.value(isolateId: isolateRefId),
+    );
 
     return await _evalWithVariablesRefresh(evalFunction, isolateRefId);
   }
@@ -182,9 +186,9 @@
     final isolateId = isolateRef.id!;
 
     final theClass = (await serviceConnection.serviceManager.service!
-            .getClassList(isolateId))
-        .classes!
-        .firstWhereOrNull((ref) => object.className?.matches(ref) ?? false);
+        .getClassList(isolateId)).classes!.firstWhereOrNull(
+      (ref) => object.className?.matches(ref) ?? false,
+    );
 
     return await findInstance(isolateId, theClass?.id, object.code);
   }
diff --git a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart
index 2e204e2..7aa7abe 100644
--- a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart
+++ b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart
@@ -36,12 +36,10 @@
 /// specific as that is the only case we care about.
 // TODO(kenz): extend TreeNode class to share tree logic.
 class InspectorTreeNode {
-  InspectorTreeNode({
-    InspectorTreeNode? parent,
-    bool expandChildren = true,
-  })  : _children = <InspectorTreeNode>[],
-        _parent = parent,
-        _isExpanded = expandChildren;
+  InspectorTreeNode({InspectorTreeNode? parent, bool expandChildren = true})
+    : _children = <InspectorTreeNode>[],
+      _parent = parent,
+      _isExpanded = expandChildren;
 
   bool get showLinesToChildren {
     return _children.length > 1 && !_children.last.isProperty;
@@ -197,7 +195,8 @@
     // Iterate till getting the result to return.
     while (true) {
       final style = node.diagnostic?.style;
-      final indented = style != DiagnosticsTreeStyle.flat &&
+      final indented =
+          style != DiagnosticsTreeStyle.flat &&
           style != DiagnosticsTreeStyle.error;
       if (current == index) {
         return InspectorTreeRow(
@@ -205,7 +204,8 @@
           index: index,
           ticks: ticks,
           depth: depth,
-          lineToParent: !node.isProperty &&
+          lineToParent:
+              !node.isProperty &&
               index != 0 &&
               node.parent!.showLinesToChildren,
         );
@@ -278,10 +278,11 @@
 }
 
 /// Callback issued every time a node is added to the tree.
-typedef NodeAddedCallback = void Function(
-  InspectorTreeNode node,
-  RemoteDiagnosticsNode diagnosticsNode,
-);
+typedef NodeAddedCallback =
+    void Function(
+      InspectorTreeNode node,
+      RemoteDiagnosticsNode diagnosticsNode,
+    );
 
 class InspectorTreeConfig {
   InspectorTreeConfig({
diff --git a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree_v2.dart b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree_v2.dart
index ed97e4e..cdc4c5b 100644
--- a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree_v2.dart
+++ b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree_v2.dart
@@ -40,9 +40,9 @@
     InspectorTreeNode? parent,
     bool expandChildren = true,
     this.whenDirty,
-  })  : _children = <InspectorTreeNode>[],
-        _parent = parent,
-        _isExpanded = expandChildren;
+  }) : _children = <InspectorTreeNode>[],
+       _parent = parent,
+       _isExpanded = expandChildren;
 
   /// Callback that is called when the node is marked as dirty.
   void Function(InspectorTreeNode node)? whenDirty;
@@ -184,10 +184,11 @@
 }
 
 /// Callback issued every time a node is added to the tree.
-typedef NodeAddedCallback = void Function(
-  InspectorTreeNode node,
-  RemoteDiagnosticsNode diagnosticsNode,
-);
+typedef NodeAddedCallback =
+    void Function(
+      InspectorTreeNode node,
+      RemoteDiagnosticsNode diagnosticsNode,
+    );
 
 class InspectorTreeConfig {
   InspectorTreeConfig({
diff --git a/packages/devtools_app/lib/src/shared/console/primitives/assignment.dart b/packages/devtools_app/lib/src/shared/console/primitives/assignment.dart
index 470d970..08d274b 100644
--- a/packages/devtools_app/lib/src/shared/console/primitives/assignment.dart
+++ b/packages/devtools_app/lib/src/shared/console/primitives/assignment.dart
@@ -18,8 +18,12 @@
     const variableNameGroup = '([_a-zA-Z][_a-zA-Z0-9]{0,30})';
     const indexGroup = '([_012345])';
 
-    final regex =
-        RegExp(r'var\s+' '$variableNameGroup' r'\s*=\s*\$' '$indexGroup');
+    final regex = RegExp(
+      r'var\s+'
+      '$variableNameGroup'
+      r'\s*=\s*\$'
+      '$indexGroup',
+    );
 
     final matches = regex.allMatches(expression);
     if (matches.length != 1) return null;
diff --git a/packages/devtools_app/lib/src/shared/console/primitives/scope.dart b/packages/devtools_app/lib/src/shared/console/primitives/scope.dart
index 4576fad..553832c 100644
--- a/packages/devtools_app/lib/src/shared/console/primitives/scope.dart
+++ b/packages/devtools_app/lib/src/shared/console/primitives/scope.dart
@@ -5,7 +5,7 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../../globals.dart';
-import '../../vm_utils.dart';
+import '../../utils/vm_utils.dart';
 
 class EvalScope {
   /// Parameter `scope` for `serviceManager.manager.service!.evaluate(...)`.
@@ -48,10 +48,7 @@
     return result;
   }
 
-  Future<InstanceRef?> _refreshRef(
-    InstanceRef ref,
-    String isolateId,
-  ) async {
+  Future<InstanceRef?> _refreshRef(InstanceRef ref, String isolateId) async {
     Obj? object;
     try {
       object = await serviceConnection.serviceManager.service!.getObject(
diff --git a/packages/devtools_app/lib/src/shared/console/primitives/simple_items.dart b/packages/devtools_app/lib/src/shared/console/primitives/simple_items.dart
index 76e96ff..7f35dc1 100644
--- a/packages/devtools_app/lib/src/shared/console/primitives/simple_items.dart
+++ b/packages/devtools_app/lib/src/shared/console/primitives/simple_items.dart
@@ -3,6 +3,4 @@
 // found in the LICENSE file.
 
 // TODO(jacobr): add render, semantics, and layer trees.
-enum FlutterTreeType {
-  widget,
-}
+enum FlutterTreeType { widget }
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart b/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
index 847f2b0..ebfb183 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/console_pane.dart
@@ -6,8 +6,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
-import '../../common_widgets.dart';
 import '../../globals.dart';
+import '../../ui/common_widgets.dart';
 import '../console.dart';
 import '../console_service.dart';
 import 'evaluate.dart';
@@ -17,35 +17,34 @@
 
 class ConsolePaneHeader extends AreaPaneHeader {
   ConsolePaneHeader({super.key})
-      : super(
-          title: const Text('Console'),
-          roundedTopBorder: true,
-          actions: [
-            const ConsoleHelpLink(),
-            const SizedBox(width: densePadding),
-            CopyToClipboardControl(
-              dataProvider: () =>
-                  serviceConnection.consoleService.stdio.value.join('\n'),
-              buttonKey: ConsolePane.copyToClipboardButtonKey,
-            ),
-            const SizedBox(width: densePadding),
-            DeleteControl(
-              buttonKey: ConsolePane.clearStdioButtonKey,
-              tooltip: 'Clear console output',
-              onPressed: () => serviceConnection.consoleService.clearStdio(),
-            ),
-          ],
-        );
+    : super(
+        title: const Text('Console'),
+        roundedTopBorder: true,
+        actions: [
+          const ConsoleHelpLink(),
+          const SizedBox(width: densePadding),
+          CopyToClipboardControl(
+            dataProvider:
+                () => serviceConnection.consoleService.stdio.value.join('\n'),
+            buttonKey: ConsolePane.copyToClipboardButtonKey,
+          ),
+          const SizedBox(width: densePadding),
+          DeleteControl(
+            buttonKey: ConsolePane.clearStdioButtonKey,
+            tooltip: 'Clear console output',
+            onPressed: () => serviceConnection.consoleService.clearStdio(),
+          ),
+        ],
+      );
 }
 
 /// Display the stdout and stderr output from the process under debug.
 class ConsolePane extends StatelessWidget {
-  const ConsolePane({
-    super.key,
-  });
+  const ConsolePane({super.key});
 
-  static const copyToClipboardButtonKey =
-      Key('console_copy_to_clipboard_button');
+  static const copyToClipboardButtonKey = Key(
+    'console_copy_to_clipboard_button',
+  );
   static const clearStdioButtonKey = Key('console_clear_stdio_button');
 
   ValueListenable<List<ConsoleLine>> get stdio =>
@@ -63,14 +62,7 @@
     }
 
     return Column(
-      children: [
-        Expanded(
-          child: Console(
-            lines: stdio,
-            footer: footer,
-          ),
-        ),
-      ],
+      children: [Expanded(child: Console(lines: stdio, footer: footer))],
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/description.dart b/packages/devtools_app/lib/src/shared/console/widgets/description.dart
index 212342a..73b6d27 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/description.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/description.dart
@@ -8,8 +8,8 @@
 import '../../diagnostics/dart_object_node.dart';
 import '../../diagnostics/diagnostics_node.dart';
 import '../../diagnostics/tree_builder.dart';
-import '../../diagnostics_text_styles.dart';
 import '../../globals.dart';
+import '../../primitives/diagnostics_text_styles.dart';
 import '../../primitives/utils.dart';
 import '../../ui/hover.dart';
 import '../../ui/icons.dart';
@@ -79,12 +79,11 @@
 
   /// Approximates the width of the elements inside a [RemoteDiagnosticsNode]
   /// widget.
-  static double approximateNodeWidth(
-    RemoteDiagnosticsNode? diagnostic,
-  ) {
+  static double approximateNodeWidth(RemoteDiagnosticsNode? diagnostic) {
     // If we have rendered this node, then we know it's text style,
     // otherwise assume defaultFontSize for the TextStyle.
-    final textStyle = diagnostic?.descriptionTextStyleFromBuild ??
+    final textStyle =
+        diagnostic?.descriptionTextStyleFromBuild ??
         TextStyle(fontSize: defaultFontSize);
 
     final spans = DiagnosticsNodeDescription.buildDescriptionTextSpans(
@@ -110,8 +109,9 @@
         // If there is a description then a separator will show with the name.
         name += ': ';
       }
-      spanWidth +=
-          calculateTextSpanWidth(TextSpan(text: name, style: textStyle));
+      spanWidth += calculateTextSpanWidth(
+        TextSpan(text: name, style: textStyle),
+      );
     } else {
       final approximateIconWidth = IconKind.info.icon.width + iconPadding;
 
@@ -137,8 +137,9 @@
         if (match.group(2)?.isNotEmpty == true) {
           yield TextSpan(
             text: match.group(2),
-            style:
-                textStyle.merge(DiagnosticsTextStyles.unimportant(colorScheme)),
+            style: textStyle.merge(
+              DiagnosticsTextStyles.unimportant(colorScheme),
+            ),
           );
         }
         return;
@@ -161,10 +162,7 @@
       final preview = textPreview.replaceAll('\n', ' ');
       yield TextSpan(
         children: [
-          TextSpan(
-            text: ': ',
-            style: textStyle,
-          ),
+          TextSpan(text: ': ', style: textStyle),
           _buildHighlightedSearchPreview(
             preview,
             searchValue,
@@ -191,23 +189,26 @@
     diagnostic?.descriptionTextStyleFromBuild = textStyle;
 
     final textSpan = TextSpan(
-      children: buildDescriptionTextSpans(
-        description: description,
-        textStyle: textStyle,
-        colorScheme: colorScheme,
-        diagnostic: diagnostic,
-        searchValue: searchValue,
-        nodeDescriptionHighlightStyle: nodeDescriptionHighlightStyle,
-      ).toList(),
+      children:
+          buildDescriptionTextSpans(
+            description: description,
+            textStyle: textStyle,
+            colorScheme: colorScheme,
+            diagnostic: diagnostic,
+            searchValue: searchValue,
+            nodeDescriptionHighlightStyle: nodeDescriptionHighlightStyle,
+          ).toList(),
     );
 
     final diagnosticLocal = diagnostic!;
     final inspectorService = serviceConnection.inspectorService!;
 
     return HoverCardTooltip.async(
-      enabled: () =>
-          preferences.inspector.hoverEvalModeEnabled.value &&
-          diagnosticLocal.objectGroupApi != null,
+      enabled:
+          () =>
+              preferences.inspector.hoverEvalModeEnabled.value &&
+              diagnosticLocal.objectGroupApi != null &&
+              !isPrimitiveValueOrNull(description),
       asyncGenerateHoverCardData: ({
         required event,
         required isHoverStale,
@@ -215,8 +216,9 @@
         final group = inspectorService.createObjectGroup('hover');
 
         if (isHoverStale()) return Future.value();
-        final value =
-            await group.toObservatoryInstanceRef(diagnosticLocal.valueRef);
+        final value = await group.toObservatoryInstanceRef(
+          diagnosticLocal.valueRef,
+        );
 
         final variable = DartObjectNode.fromValue(
           value: value,
@@ -237,11 +239,7 @@
 
         return HoverCardData(
           title: diagnosticLocal.toStringShort(),
-          contents: Material(
-            child: ExpandableVariable(
-              variable: variable,
-            ),
-          ),
+          contents: Material(child: ExpandableVariable(variable: variable)),
         );
       },
       child: DescriptionDisplay(
@@ -264,9 +262,10 @@
 
     final theme = Theme.of(context);
     final colorScheme = theme.colorScheme;
-    final icon = customIconName != null
-        ? RemoteDiagnosticsNode.iconMaker.fromWidgetName(customIconName)
-        : diagnosticLocal.icon;
+    final icon =
+        customIconName != null
+            ? RemoteDiagnosticsNode.iconMaker.fromWidgetName(customIconName)
+            : diagnosticLocal.icon;
 
     final children = <Widget>[];
 
@@ -291,7 +290,7 @@
 
       final showDefaultValueLabel =
           diagnosticLocal.level == DiagnosticLevel.fine &&
-              diagnosticLocal.hasDefaultValue;
+          diagnosticLocal.hasDefaultValue;
 
       // Show the "default" value label at the start if the property name isn't
       // included:
@@ -306,10 +305,7 @@
 
       if (includeName && name?.isNotEmpty == true && diagnosticLocal.showName) {
         children.add(
-          Text(
-            '$name${diagnosticLocal.separator} ',
-            style: textStyle,
-          ),
+          Text('$name${diagnosticLocal.separator} ', style: textStyle),
         );
         // provide some contrast between the name and description if both are
         // present.
@@ -330,9 +326,10 @@
               final green = JsonUtils.getIntMember(properties, 'green');
               final blue = JsonUtils.getIntMember(properties, 'blue');
               String radix(int chan) => chan.toRadixString(16).padLeft(2, '0');
-              description = alpha == 255
-                  ? '#${radix(red)}${radix(green)}${radix(blue)}'
-                  : '#${radix(alpha)}${radix(red)}${radix(green)}${radix(blue)}';
+              description =
+                  alpha == 255
+                      ? '#${radix(red)}${radix(green)}${radix(blue)}'
+                      : '#${radix(alpha)}${radix(red)}${radix(green)}${radix(blue)}';
 
               final color = Color.fromARGB(alpha, red, green, blue);
               children.add(_paddedIcon(_colorIconMaker.getCustomIcon(color)));
@@ -391,29 +388,16 @@
           name != 'child') {
         if (name.startsWith('child ')) {
           children.add(
-            Text(
-              name,
-              style: DiagnosticsTextStyles.unimportant(colorScheme),
-            ),
+            Text(name, style: DiagnosticsTextStyles.unimportant(colorScheme)),
           );
         } else {
           children.add(Text(name, style: textStyle));
         }
 
         if (diagnosticLocal.showSeparator) {
-          children.add(
-            Text(
-              diagnosticLocal.separator,
-              style: textStyle,
-            ),
-          );
+          children.add(Text(diagnosticLocal.separator, style: textStyle));
           if (diagnosticLocal.separator != ' ' && descriptionText.isNotEmpty) {
-            children.add(
-              Text(
-                ' ',
-                style: textStyle,
-              ),
-            );
+            children.add(Text(' ', style: textStyle));
           }
         }
       }
@@ -451,20 +435,14 @@
         //  the nested row.
         diagnosticDescription = Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            diagnosticDescription,
-            _buildErrorText(colorScheme),
-          ],
+          children: [diagnosticDescription, _buildErrorText(colorScheme)],
         );
       } else if (multiline &&
           diagnosticLocal.hasCreationLocation &&
           !diagnosticLocal.isProperty) {
         diagnosticDescription = Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            diagnosticDescription,
-            _buildLocation(context),
-          ],
+          children: [diagnosticDescription, _buildLocation(context)],
         );
       }
 
@@ -498,9 +476,10 @@
           text: errorText,
           // When the node is selected, the background will be an error
           // color so don't render the text the same color.
-          style: isSelected
-              ? DiagnosticsTextStyles.regular(colorScheme)
-              : DiagnosticsTextStyles.error(colorScheme),
+          style:
+              isSelected
+                  ? DiagnosticsTextStyles.regular(colorScheme)
+                  : DiagnosticsTextStyles.error(colorScheme),
         ),
       ),
     );
@@ -513,25 +492,16 @@
     TextStyle highlightTextStyle,
   ) {
     if (searchValue == null || searchValue.isEmpty) {
-      return TextSpan(
-        text: '"$textPreview"',
-        style: textStyle,
-      );
+      return TextSpan(text: '"$textPreview"', style: textStyle);
     }
 
     if (textPreview.caseInsensitiveEquals(searchValue)) {
-      return TextSpan(
-        text: '"$textPreview"',
-        style: highlightTextStyle,
-      );
+      return TextSpan(text: '"$textPreview"', style: highlightTextStyle);
     }
 
     final matches = searchValue.caseInsensitiveAllMatches(textPreview);
     if (matches.isEmpty) {
-      return TextSpan(
-        text: '"$textPreview"',
-        style: textStyle,
-      );
+      return TextSpan(text: '"$textPreview"', style: textStyle);
     }
 
     final quoteSpan = TextSpan(text: '"', style: textStyle);
@@ -575,22 +545,7 @@
 
   @override
   Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    final colorScheme = theme.colorScheme;
-    return Container(
-      padding: const EdgeInsets.symmetric(horizontal: denseSpacing),
-      decoration: BoxDecoration(
-        borderRadius: defaultBorderRadius,
-        color: colorScheme.secondary,
-      ),
-      child: Text(
-        'default',
-        style: theme.regularTextStyleWithColor(
-          colorScheme.onSecondary,
-          backgroundColor: colorScheme.secondary,
-        ),
-      ),
-    );
+    return const RoundedLabel(labelText: 'default');
   }
 }
 
@@ -602,14 +557,14 @@
     this.actionLabel,
     this.actionCallback,
     this.overflow = TextOverflow.ellipsis,
-  })  : assert(
-          multiline ? actionLabel == null : true,
-          'Action labels are not supported for multiline descriptions',
-        ),
-        assert(
-          (actionLabel == null) == (actionCallback == null),
-          'Actions require both a label and a callback',
-        );
+  }) : assert(
+         multiline ? actionLabel == null : true,
+         'Action labels are not supported for multiline descriptions',
+       ),
+       assert(
+         (actionLabel == null) == (actionCallback == null),
+         'Actions require both a label and a callback',
+       );
 
   final TextSpan text;
   final bool multiline;
@@ -627,10 +582,7 @@
       return Row(
         children: [
           Flexible(
-            child: RichText(
-              overflow: TextOverflow.ellipsis,
-              text: text,
-            ),
+            child: RichText(overflow: TextOverflow.ellipsis, text: text),
           ),
           Flexible(
             child: TextButton(
@@ -638,18 +590,13 @@
                 textStyle: Theme.of(context).regularTextStyle,
               ),
               onPressed: actionCallback,
-              child: Text(
-                actionLabel!,
-              ),
+              child: Text(actionLabel!),
             ),
           ),
         ],
       );
     }
 
-    return RichText(
-      overflow: overflow,
-      text: text,
-    );
+    return RichText(overflow: overflow, text: text);
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart b/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart
index 7cd3de7..000faec 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/display_provider.dart
@@ -12,10 +12,10 @@
 
 import '../../diagnostics/dap_object_node.dart';
 import '../../diagnostics/dart_object_node.dart';
+import '../../framework/routing.dart';
+import '../../framework/screen.dart';
 import '../../globals.dart';
 import '../../primitives/utils.dart';
-import '../../routing.dart';
-import '../../screen.dart';
 import '../../ui/colors.dart';
 import 'description.dart';
 
@@ -78,9 +78,7 @@
     final displayValue = originalDisplayValue.replaceAll('\n', '\\n');
     final contents = InteractivityWrapper(
       onTap: widget.onTap,
-      menuButtons: _getMenuButtons(
-        context,
-      ),
+      menuButtons: _getMenuButtons(context),
       child: DevToolsTooltip(
         message: originalDisplayValue,
         child: Container(
@@ -93,22 +91,21 @@
                   overflow: TextOverflow.ellipsis,
                   TextSpan(
                     text: hasName ? widget.variable.name : null,
-                    style: widget.variable.artificialName
-                        ? theme.subtleFixedFontStyle
-                        : theme.fixedFontStyle.apply(
-                            color: theme.colorScheme.controlFlowSyntaxColor,
-                          ),
+                    style:
+                        widget.variable.artificialName
+                            ? theme.subtleFixedFontStyle
+                            : theme.fixedFontStyle.apply(
+                              color: theme.colorScheme.controlFlowSyntaxColor,
+                            ),
                     children: [
                       if (hasName)
-                        TextSpan(
-                          text: ': ',
-                          style: theme.fixedFontStyle,
-                        ),
+                        TextSpan(text: ': ', style: theme.fixedFontStyle),
                       TextSpan(
                         text: displayValue,
-                        style: widget.variable.artificialValue
-                            ? theme.subtleFixedFontStyle
-                            : _variableDisplayStyle(theme, widget.variable),
+                        style:
+                            widget.variable.artificialValue
+                                ? theme.subtleFixedFontStyle
+                                : _variableDisplayStyle(theme, widget.variable),
                       ),
                     ],
                   ),
@@ -146,9 +143,7 @@
     return contents;
   }
 
-  List<ContextMenuButtonItem> _getMenuButtons(
-    BuildContext context,
-  ) {
+  List<ContextMenuButtonItem> _getMenuButtons(BuildContext context) {
     return [
       if (widget.variable.isRerootable)
         ContextMenuButtonItem(
@@ -176,9 +171,7 @@
     ];
   }
 
-  void _handleInspect(
-    BuildContext context,
-  ) async {
+  void _handleInspect(BuildContext context) async {
     final router = DevToolsRouterDelegate.of(context);
     final inspectorService = serviceConnection.inspectorService;
     if (await widget.variable.inspectWidget()) {
@@ -222,19 +215,13 @@
     }
     switch (kind) {
       case InstanceKind.kString:
-        return style.apply(
-          color: theme.colorScheme.stringSyntaxColor,
-        );
+        return style.apply(color: theme.colorScheme.stringSyntaxColor);
       case InstanceKind.kInt:
       case InstanceKind.kDouble:
-        return style.apply(
-          color: theme.colorScheme.numericConstantSyntaxColor,
-        );
+        return style.apply(color: theme.colorScheme.numericConstantSyntaxColor);
       case InstanceKind.kBool:
       case InstanceKind.kNull:
-        return style.apply(
-          color: theme.colorScheme.modifierSyntaxColor,
-        );
+        return style.apply(color: theme.colorScheme.modifierSyntaxColor);
       default:
         return style;
     }
@@ -270,16 +257,10 @@
           color: theme.colorScheme.controlFlowSyntaxColor,
         ),
         children: [
-          TextSpan(
-            text: ': ',
-            style: theme.fixedFontStyle,
-          ),
+          TextSpan(text: ': ', style: theme.fixedFontStyle),
           // TODO(https://github.com/flutter/devtools/issues/6056): Change text
           // style based on variable type.
-          TextSpan(
-            text: value,
-            style: theme.subtleFixedFontStyle,
-          ),
+          TextSpan(text: value, style: theme.subtleFixedFontStyle),
         ],
       ),
     );
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/evaluate.dart b/packages/devtools_app/lib/src/shared/console/widgets/evaluate.dart
index 0f41f47..e9432c3 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/evaluate.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/evaluate.dart
@@ -16,24 +16,22 @@
 import '../../globals.dart';
 import '../../ui/search.dart';
 import '../../ui/utils.dart';
-import '../../utils.dart';
+import '../../utils/utils.dart';
 import '../eval/auto_complete.dart';
 import '../eval/eval_service.dart';
 import '../primitives/assignment.dart';
 import '../primitives/eval_history.dart';
 import 'help_dialog.dart';
 
-typedef AutoCompleteResultsFunction = Future<List<String>> Function(
-  EditingParts parts,
-  EvalService evalService,
-);
+typedef AutoCompleteResultsFunction =
+    Future<List<String>> Function(EditingParts parts, EvalService evalService);
 
 class ExpressionEvalField extends StatefulWidget {
   const ExpressionEvalField({
     super.key,
     AutoCompleteResultsFunction? getAutoCompleteResults,
   }) : getAutoCompleteResults =
-            getAutoCompleteResults ?? autoCompleteResultsFor;
+           getAutoCompleteResults ?? autoCompleteResultsFor;
 
   final AutoCompleteResultsFunction getAutoCompleteResults;
 
@@ -181,13 +179,11 @@
           ..clearSearchAutoComplete()
           ..clearCurrentSuggestion();
       } else {
-        final results = matches
-            .sublist(
-              0,
-              min(defaultTopMatchesLimit, matches.length),
-            )
-            .map((match) => AutoCompleteMatch(match))
-            .toList();
+        final results =
+            matches
+                .sublist(0, min(defaultTopMatchesLimit, matches.length))
+                .map((match) => AutoCompleteMatch(match))
+                .toList();
 
         _autoCompleteController
           ..searchAutoComplete.value = results
@@ -233,33 +229,33 @@
                 decoration: InputDecoration(
                   contentPadding: const EdgeInsets.all(denseSpacing),
                   border: const OutlineInputBorder(),
-                  focusedBorder:
-                      const OutlineInputBorder(borderSide: BorderSide.none),
-                  enabledBorder:
-                      const OutlineInputBorder(borderSide: BorderSide.none),
+                  focusedBorder: const OutlineInputBorder(
+                    borderSide: BorderSide.none,
+                  ),
+                  enabledBorder: const OutlineInputBorder(
+                    borderSide: BorderSide.none,
+                  ),
                   labelText: 'Eval. Enter "?" for help.',
                   labelStyle: Theme.of(context).subtleTextStyle,
                 ),
-                overlayXPositionBuilder:
-                    (String inputValue, TextStyle? inputStyle) {
+                overlayXPositionBuilder: (
+                  String inputValue,
+                  TextStyle? inputStyle,
+                ) {
                   // X-coordinate is equivalent to the width of the input text
                   // up to the last "." or the insertion point (cursor):
                   final indexOfDot = inputValue.lastIndexOf('.');
-                  final textSegment = indexOfDot != -1
-                      ? inputValue.substring(0, indexOfDot + 1)
-                      : inputValue;
+                  final textSegment =
+                      indexOfDot != -1
+                          ? inputValue.substring(0, indexOfDot + 1)
+                          : inputValue;
                   return calculateTextSpanWidth(
-                    TextSpan(
-                      text: textSegment,
-                      style: inputStyle,
-                    ),
+                    TextSpan(text: textSegment, style: inputStyle),
                   );
                 },
                 // Disable ligatures, so the suggestions of the auto complete work correcly.
                 style: Theme.of(context).fixedFontStyle.copyWith(
-                  fontFeatures: [
-                    const FontFeature.disable('liga'),
-                  ],
+                  fontFeatures: [const FontFeature.disable('liga')],
                 ),
               ),
             ),
@@ -306,10 +302,7 @@
     );
   }
 
-  List<String> _filterMatches(
-    List<String> previousMatches,
-    String activeWord,
-  ) {
+  List<String> _filterMatches(List<String> previousMatches, String activeWord) {
     return previousMatches
         .where((match) => match.startsWith(activeWord))
         .toList();
@@ -358,8 +351,10 @@
           );
           return;
         }
-        response =
-            await evalService.evalInRunningApp(isolateRef, expressionText);
+        response = await evalService.evalInRunningApp(
+          isolateRef,
+          expressionText,
+        );
       }
 
       // Display the response to the user.
@@ -445,8 +440,9 @@
     if (assignment == null) return false;
     const kSuccess = true;
 
-    final variable = serviceConnection.consoleService
-        .itemAt(assignment.consoleItemIndex + 1);
+    final variable = serviceConnection.consoleService.itemAt(
+      assignment.consoleItemIndex + 1,
+    );
     final value = variable?.value;
     if (value is! InstanceRef) {
       _emitToConsole(
@@ -455,15 +451,23 @@
       return kSuccess;
     }
 
-    final isolateId = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value?.id;
-    final isolateName = serviceConnection
-        .serviceManager.isolateManager.selectedIsolate.value?.name;
+    final isolateId =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value
+            ?.id;
+    final isolateName =
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value
+            ?.name;
 
     if (isolateId == null || isolateName == null) {
-      _emitToConsole(
-        'Selected isolate cannot be detected.',
-      );
+      _emitToConsole('Selected isolate cannot be detected.');
       return kSuccess;
     }
 
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/expandable_variable.dart b/packages/devtools_app/lib/src/shared/console/widgets/expandable_variable.dart
index 3e43d90..9800fa0 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/expandable_variable.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/expandable_variable.dart
@@ -7,9 +7,9 @@
 import 'package:flutter/material.dart' hide Stack;
 
 import '../../../shared/primitives/listenable.dart';
-import '../../../shared/tree.dart';
 import '../../diagnostics/dart_object_node.dart';
 import '../../diagnostics/tree_builder.dart';
+import '../../ui/tree_view.dart';
 import 'display_provider.dart';
 
 class ExpandableVariable extends StatelessWidget {
@@ -39,22 +39,22 @@
     // TODO(kenz): preserve expanded state of tree on switching frames and
     // on stepping.
     return TreeView<DartObjectNode>(
-      dataRootsListenable:
-          FixedValueListenable<List<DartObjectNode>>([variable]),
-      dataDisplayProvider: dataDisplayProvider ??
+      dataRootsListenable: FixedValueListenable<List<DartObjectNode>>([
+        variable,
+      ]),
+      dataDisplayProvider:
+          dataDisplayProvider ??
           (variable, onPressed) => DisplayProvider(
-                variable: variable,
-                onTap: onPressed,
-                onCopy: onCopy,
-              ),
+            variable: variable,
+            onTap: onPressed,
+            onCopy: onCopy,
+          ),
       onItemSelected: onItemPressed,
       isSelectable: isSelectable,
     );
   }
 
-  Future<void> onItemPressed(
-    DartObjectNode v,
-  ) async {
+  Future<void> onItemPressed(DartObjectNode v) async {
     // On expansion, lazily build the variables tree for performance reasons.
     if (v.isExpanded) {
       await Future.wait(v.children.map(buildVariablesTree));
diff --git a/packages/devtools_app/lib/src/shared/console/widgets/help_dialog.dart b/packages/devtools_app/lib/src/shared/console/widgets/help_dialog.dart
index 5b9d102..14e7637 100644
--- a/packages/devtools_app/lib/src/shared/console/widgets/help_dialog.dart
+++ b/packages/devtools_app/lib/src/shared/console/widgets/help_dialog.dart
@@ -8,7 +8,7 @@
 import 'package:flutter/material.dart';
 
 import '../../analytics/constants.dart' as gac;
-import '../../common_widgets.dart';
+import '../../ui/common_widgets.dart';
 
 const _documentationTopic = gac.console;
 
@@ -40,10 +40,7 @@
 Assign previously evaluated objects to variable using $0, $1 … $5.
 Example: ''',
                 ),
-                TextSpan(
-                  text: r'var x = $0',
-                  style: theme.fixedFontStyle,
-                ),
+                TextSpan(text: r'var x = $0', style: theme.fixedFontStyle),
               ],
             ),
           ),
@@ -51,14 +48,13 @@
             // TODO(polina-c): create content and change url.
             url: 'https://docs.flutter.dev/tools/devtools/console',
             gaScreenName: gac.console,
-            gaSelectedItemDescription:
-                gac.topicDocumentationLink(_documentationTopic),
+            gaSelectedItemDescription: gac.topicDocumentationLink(
+              _documentationTopic,
+            ),
           ),
         ],
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/development_helpers.dart b/packages/devtools_app/lib/src/shared/development_helpers.dart
index 4cfb1f4..504f341 100644
--- a/packages/devtools_app/lib/src/shared/development_helpers.dart
+++ b/packages/devtools_app/lib/src/shared/development_helpers.dart
@@ -9,7 +9,7 @@
 import 'package:logging/logging.dart';
 
 import 'globals.dart';
-import 'survey.dart';
+import 'managers/survey.dart';
 
 // This file contains helpers that can be used during local development. Any
 // changes to variables in this file (like flipping a bool to true or setting
@@ -24,11 +24,11 @@
 /// the Dart Tooling Daemon.
 ///
 /// Without using this flag, you would need to run DevTools with the DevTools
-/// server (devtools_tool serve) in order to pass a DTD URI to the DevTools
+/// server (dt serve) in order to pass a DTD URI to the DevTools
 /// server, which is not convenient for development.
 ///
 /// If you do need the DevTools server, then you should run
-/// `devtools_tool serve --dtd-uri=<uri>` instead of setting this debug flag.
+/// `dt serve --dtd-uri=<uri>` instead of setting this debug flag.
 ///
 /// You can use a real DTD URI from an IDE (VS Code or IntelliJ / Android
 /// Studio) using the "Copy DTD URI" action, or you can run a Dart or Flutter
@@ -59,8 +59,7 @@
 
 List<DevToolsExtensionConfig> debugHandleRefreshAvailableExtensions({
   bool includeRuntime = true,
-}) =>
-    StubDevToolsExtensions.extensions(includeRuntime: includeRuntime);
+}) => StubDevToolsExtensions.extensions(includeRuntime: includeRuntime);
 
 ExtensionEnabledState debugHandleExtensionEnabledState({
   required String extensionName,
@@ -195,18 +194,13 @@
   /// connection.
   static List<DevToolsExtensionConfig> extensions({
     bool includeRuntime = true,
-  }) =>
-      [
-        if (includeRuntime) ...[
-          fooExtension,
-          providerExtension,
-          someToolExtension,
-        ],
-        barExtension,
-        newerBarExtension,
-        bazExtension,
-        duplicateFooExtension,
-      ];
+  }) => [
+    if (includeRuntime) ...[fooExtension, providerExtension, someToolExtension],
+    barExtension,
+    newerBarExtension,
+    bazExtension,
+    duplicateFooExtension,
+  ];
 }
 
 /// Enable this flag to debug the DevTools survey logic.
@@ -219,24 +213,22 @@
 /// The survey metadata that will be used instead of the live data from
 /// 'https://storage.googleapis.com/flutter-uxr/surveys/devtools-survey-metadata.json'
 /// when [debugSurvey] is true.
-final debugSurveyMetadata = DevToolsSurvey.fromJson(
-  {
-    '_comments': [
-      'uniqueId must be updated with each new survey so DevTools knows to re-prompt users.',
-      'title should not exceed 45 characters.',
-      'startDate and endDate should follow ISO 8601 standard with a timezone offset.',
-    ],
-    'uniqueId': '2024-Q2',
-    'title': 'Take our survey to help us improve DevTools!',
-    'url': 'https://google.qualtrics.com/jfe/form/SV_2l4XcyscF8mQtDM',
-    'startDate': '2024-06-18T09:00:00-07:00',
-    'endDate': '2024-07-02T09:00:00-07:00',
-    'minDevToolsVersion': '2.35.0',
-    // This list is optional and can be used to limit the survey to a specific
-    // set of users based on their development environment.
-    'devEnvironments': ['Android-Studio', 'IntelliJ-IDEA', 'VSCode', 'CLI'],
-  },
-);
+final debugSurveyMetadata = DevToolsSurvey.fromJson({
+  '_comments': [
+    'uniqueId must be updated with each new survey so DevTools knows to re-prompt users.',
+    'title should not exceed 45 characters.',
+    'startDate and endDate should follow ISO 8601 standard with a timezone offset.',
+  ],
+  'uniqueId': '2024-Q2',
+  'title': 'Take our survey to help us improve DevTools!',
+  'url': 'https://google.qualtrics.com/jfe/form/SV_2l4XcyscF8mQtDM',
+  'startDate': '2024-06-18T09:00:00-07:00',
+  'endDate': '2024-07-02T09:00:00-07:00',
+  'minDevToolsVersion': '2.35.0',
+  // This list is optional and can be used to limit the survey to a specific
+  // set of users based on their development environment.
+  'devEnvironments': ['Android-Studio', 'IntelliJ-IDEA', 'VSCode', 'CLI'],
+});
 
 /// Enable this flag to debug Perfetto trace processing in the Performance
 /// screen.
@@ -264,10 +256,7 @@
 /// run to stdout.
 ///
 /// This will only time the operation when [debugTimers] is true.
-void debugTimeSync(
-  void Function() callback, {
-  required String debugName,
-}) {
+void debugTimeSync(void Function() callback, {required String debugName}) {
   if (!debugTimers) {
     callback();
     return;
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/dap_object_node.dart b/packages/devtools_app/lib/src/shared/diagnostics/dap_object_node.dart
index 0427229..0342e6e 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/dap_object_node.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/dap_object_node.dart
@@ -11,7 +11,7 @@
 
 class DapObjectNode extends TreeNode<DapObjectNode> {
   DapObjectNode({required this.variable, required VmServiceWrapper service})
-      : _service = service;
+    : _service = service;
 
   final dap.Variable variable;
   final VmServiceWrapper _service;
@@ -27,18 +27,11 @@
     if (!hasChildren) return;
 
     final variablesResponse = await _service.dapVariablesRequest(
-      dap.VariablesArguments(
-        variablesReference: variable.variablesReference,
-      ),
+      dap.VariablesArguments(variablesReference: variable.variablesReference),
     );
     final children = variablesResponse?.variables ?? [];
     for (final child in children) {
-      addChild(
-        DapObjectNode(
-          variable: child,
-          service: _service,
-        ),
-      );
+      addChild(DapObjectNode(variable: child, service: _service));
     }
   }
 
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart b/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
index 78d80c0..c0d4910 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
@@ -29,8 +29,8 @@
     this.artificialName = false,
     this.artificialValue = false,
     this.isRerootable = false,
-  })  : _offset = offset,
-        _childCount = childCount {
+  }) : _offset = offset,
+       _childCount = childCount {
     indentChildren = ref?.diagnostic?.style != DiagnosticsTreeStyle.flat;
   }
 
@@ -130,18 +130,18 @@
       artificialValue: true,
       childCount: list?.length ?? 0,
     )..addAllChildren([
-        if (list != null)
-          for (int i = 0; i < list.length; ++i)
-            DartObjectNode.fromValue(
-              name: '[$i]',
-              value: displayNameBuilder?.call(list[i]) ?? list[i],
-              isolateRef: isolateRef,
-              artificialName: true,
-              artificialValue: artificialChildValues,
-            )..addAllChildren([
-                if (childBuilder != null) ...childBuilder(list[i]),
-              ]),
-      ]);
+      if (list != null)
+        for (int i = 0; i < list.length; ++i)
+          DartObjectNode.fromValue(
+            name: '[$i]',
+            value: displayNameBuilder?.call(list[i]) ?? list[i],
+            isolateRef: isolateRef,
+            artificialName: true,
+            artificialValue: artificialChildValues,
+          )..addAllChildren([
+            if (childBuilder != null) ...childBuilder(list[i]),
+          ]),
+    ]);
   }
 
   factory DartObjectNode.create(
@@ -151,18 +151,12 @@
     final value = variable.value;
     return DartObjectNode._(
       name: variable.name,
-      ref: GenericInstanceRef(
-        isolateRef: isolateRef,
-        value: value,
-      ),
+      ref: GenericInstanceRef(isolateRef: isolateRef, value: value),
     );
   }
 
   factory DartObjectNode.text(String text) {
-    return DartObjectNode._(
-      text: text,
-      artificialName: true,
-    );
+    return DartObjectNode._(text: text, artificialName: true);
   }
 
   factory DartObjectNode.grouping(
@@ -316,9 +310,10 @@
         // to `children.length` if it's not provide (this means we don't get
         // the count until the record is expanded):
         final count = value.length ?? children.length;
-        valueStr = count == 0
-            ? 'Record'
-            : 'Record ($count ${pluralize('field', count)})';
+        valueStr =
+            count == 0
+                ? 'Record'
+                : 'Record ($count ${pluralize('field', count)})';
       } else if (value.valueAsString == null) {
         valueStr = value.classRef?.name ?? '';
       } else {
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/diagnostics_node.dart b/packages/devtools_app/lib/src/shared/diagnostics/diagnostics_node.dart
index 8b32110..9e723e0 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/diagnostics_node.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/diagnostics_node.dart
@@ -20,8 +20,9 @@
 
 final diagnosticLevelUtils = EnumUtils<DiagnosticLevel>(DiagnosticLevel.values);
 
-final treeStyleUtils =
-    EnumUtils<DiagnosticsTreeStyle>(DiagnosticsTreeStyle.values);
+final treeStyleUtils = EnumUtils<DiagnosticsTreeStyle>(
+  DiagnosticsTreeStyle.values,
+);
 
 /// Defines diagnostics data for a [value].
 ///
@@ -79,10 +80,7 @@
     final width = json['width'] as String?;
     final height = json['height'] as String?;
     if (width == null || height == null) return null;
-    return Size(
-      double.parse(width),
-      double.parse(height),
-    );
+    return Size(double.parse(width), double.parse(height));
   }
 
   static FlexFit deserializeFlexFit(String? flexFit) {
@@ -147,8 +145,8 @@
   RemoteDiagnosticsNode? _parentRenderElement;
 
   BoxConstraints get constraints => deserializeConstraints(
-        json['constraints'] as Map<String, Object?>? ?? {},
-      );
+    json['constraints'] as Map<String, Object?>? ?? {},
+  );
 
   BoxParentData get parentData =>
       deserializeParentData(json['parentData'] as Map<String, Object?>? ?? {});
@@ -240,11 +238,22 @@
   /// will make the name self-evident.
   bool get showName => getBooleanMember('showName', true);
 
+  /// Whether or not the node should be indented in the Inspector Tree.
+  bool get shouldIndent {
+    final value = json['shouldIndent'] as bool?;
+
+    return value ??
+        style != DiagnosticsTreeStyle.flat &&
+            style != DiagnosticsTreeStyle.error;
+  }
+
   /// Description to show if the node has no displayed properties or children.
   String? getEmptyBodyDescription() => getStringMember('emptyBodyDescription');
 
-  late DiagnosticsTreeStyle style =
-      getStyleMember('style', DiagnosticsTreeStyle.sparse);
+  late DiagnosticsTreeStyle style = getStyleMember(
+    'style',
+    DiagnosticsTreeStyle.sparse,
+  );
 
   /// Dart class defining the diagnostic node.
   /// For example, DiagnosticProperty<Color>, IntProperty, StringProperty, etc.
@@ -550,8 +559,10 @@
         default:
           return _valueProperties = Future.value();
       }
-      _valueProperties =
-          objectGroupApi?.getDartObjectProperties(valueRef, propertyNames);
+      _valueProperties = objectGroupApi?.getDartObjectProperties(
+        valueRef,
+        propertyNames,
+      );
     }
     return _valueProperties;
   }
@@ -653,8 +664,9 @@
     final newHiddenValue = !_groupIsHidden;
     _groupIsHidden = newHiddenValue;
     if (isHideableGroupLeader) {
-      _hideableGroupSubordinates
-          ?.forEach((node) => node.groupIsHidden = newHiddenValue);
+      _hideableGroupSubordinates?.forEach(
+        (node) => node.groupIsHidden = newHiddenValue,
+      );
     }
   }
 
@@ -688,11 +700,7 @@
   }
 
   Future<List<RemoteDiagnosticsNode>> _getChildrenHelper() {
-    return objectGroupApi!.getChildren(
-      valueRef,
-      isSummaryTree,
-      this,
-    );
+    return objectGroupApi!.getChildren(valueRef, isSummaryTree, this);
   }
 
   void _maybePopulateChildren() {
@@ -704,8 +712,12 @@
     if (jsonArray?.isNotEmpty == true) {
       final nodes = <RemoteDiagnosticsNode>[];
       for (final element in jsonArray!.cast<Map<String, Object?>>()) {
-        final child =
-            RemoteDiagnosticsNode(element, objectGroupApi, false, parent);
+        final child = RemoteDiagnosticsNode(
+          element,
+          objectGroupApi,
+          false,
+          parent,
+        );
         child.parent = this;
         nodes.add(child);
       }
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/generic_instance_reference.dart b/packages/devtools_app/lib/src/shared/diagnostics/generic_instance_reference.dart
index 04df29b..62f6c29 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/generic_instance_reference.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/generic_instance_reference.dart
@@ -6,7 +6,7 @@
 
 import '../memory/heap_object.dart';
 import '../memory/simple_items.dart';
-import '../vm_utils.dart';
+import '../utils/vm_utils.dart';
 import 'diagnostics_node.dart';
 
 /// True, if [ref] contains static or live information about references and thus
@@ -73,12 +73,12 @@
     ObjectReferences ref, {
     RefNodeType? refNodeType,
     HeapObject? heapSelection,
-  })  : refNodeType = refNodeType ?? ref.refNodeType,
-        super(
-          isolateRef: ref.isolateRef,
-          value: ref.value,
-          heapSelection: heapSelection ?? ref.heapSelection,
-        );
+  }) : refNodeType = refNodeType ?? ref.refNodeType,
+       super(
+         isolateRef: ref.isolateRef,
+         value: ref.value,
+         heapSelection: heapSelection ?? ref.heapSelection,
+       );
 
   final RefNodeType refNodeType;
 
@@ -119,8 +119,7 @@
   liveInRefs(RefDirection.inbound),
 
   /// Subitem of [liveRefRoot] for outbound live references.
-  liveOutRefs(RefDirection.outbound),
-  ;
+  liveOutRefs(RefDirection.outbound);
 
   const RefNodeType([this.direction]);
 
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart b/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
index 7e0299e..093bfac 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
@@ -21,7 +21,7 @@
 
 import '../console/primitives/simple_items.dart';
 import '../globals.dart';
-import '../utils.dart';
+import '../utils/utils.dart';
 import 'diagnostics_node.dart';
 import 'generic_instance_reference.dart';
 import 'object_group_api.dart';
@@ -38,15 +38,15 @@
     required this.serviceExtensionPrefix,
     required String inspectorLibraryUri,
     ValueListenable<IsolateRef?>? evalIsolate,
-  })  : assert(serviceConnection.serviceManager.connectedAppInitialized),
-        assert(serviceConnection.serviceManager.service != null),
-        clients = {},
-        inspectorLibrary = EvalOnDartLibrary(
-          inspectorLibraryUri,
-          serviceConnection.serviceManager.service!,
-          serviceManager: serviceConnection.serviceManager,
-          isolate: evalIsolate,
-        ) {
+  }) : assert(serviceConnection.serviceManager.connectedAppInitialized),
+       assert(serviceConnection.serviceManager.service != null),
+       clients = {},
+       inspectorLibrary = EvalOnDartLibrary(
+         inspectorLibraryUri,
+         serviceConnection.serviceManager.service!,
+         serviceManager: serviceConnection.serviceManager,
+         isolate: evalIsolate,
+       ) {
     _lastMainIsolate =
         serviceConnection.serviceManager.isolateManager.mainIsolate.value;
     addAutoDisposeListener(
@@ -124,8 +124,9 @@
   Future<bool> invokeBoolServiceMethodNoArgs(String methodName) async {
     return useDaemonApi
         ? await invokeServiceMethodDaemonNoGroupArgs(methodName) == true
-        : (await invokeServiceMethodObservatoryNoGroup(methodName))
-                ?.valueAsString ==
+        : (await invokeServiceMethodObservatoryNoGroup(
+              methodName,
+            ))?.valueAsString ==
             'true';
   }
 
@@ -159,17 +160,18 @@
     if (!serviceConnection.serviceManager.serviceExtensionManager
         .isServiceExtensionAvailable(callMethodName)) {
       final available = await serviceConnection
-          .serviceManager.serviceExtensionManager
+          .serviceManager
+          .serviceExtensionManager
           .waitForServiceExtensionAvailable(callMethodName);
       if (!available) return {'result': null};
     }
 
-    final r =
-        await serviceConnection.serviceManager.service!.callServiceExtension(
-      callMethodName,
-      isolateId: isolateRef!.id,
-      args: args,
-    );
+    final r = await serviceConnection.serviceManager.service!
+        .callServiceExtension(
+          callMethodName,
+          isolateId: isolateRef!.id,
+          args: args,
+        );
     final json = r.json!;
     if (json['errorMessage'] != null) {
       throw Exception('$methodName -- ${json['errorMessage']}');
@@ -182,22 +184,24 @@
 /// the inspector.
 class InspectorService extends InspectorServiceBase {
   InspectorService()
-      : super(
-          clientInspectorName: 'WidgetInspectorService',
-          serviceExtensionPrefix: inspectorExtensionPrefix,
-          inspectorLibraryUri: _inspectorLibraryUri,
-          evalIsolate:
-              serviceConnection.serviceManager.isolateManager.mainIsolate,
-        ) {
+    : super(
+        clientInspectorName: 'WidgetInspectorService',
+        serviceExtensionPrefix: inspectorExtensionPrefix,
+        inspectorLibraryUri: _inspectorLibraryUri,
+        evalIsolate:
+            serviceConnection.serviceManager.isolateManager.mainIsolate,
+      ) {
     // Note: We do not need to listen to event history here because the
     // inspector uses a separate API to get the current inspector selection.
     autoDisposeStreamSubscription(
-      serviceConnection.serviceManager.service!.onExtensionEvent
-          .listen(onExtensionVmServiceReceived),
+      serviceConnection.serviceManager.service!.onExtensionEvent.listen(
+        onExtensionVmServiceReceived,
+      ),
     );
     autoDisposeStreamSubscription(
-      serviceConnection.serviceManager.service!.onDebugEvent
-          .listen(onDebugVmServiceReceived),
+      serviceConnection.serviceManager.service!.onDebugEvent.listen(
+        onDebugVmServiceReceived,
+      ),
     );
   }
 
@@ -418,8 +422,10 @@
   RemoteDiagnosticsNode? _currentSelection;
 
   InspectorObjectGroupManager get _selectionGroups {
-    return _cachedSelectionGroups ??=
-        InspectorObjectGroupManager(this, 'selection');
+    return _cachedSelectionGroups ??= InspectorObjectGroupManager(
+      this,
+      'selection',
+    );
   }
 
   InspectorObjectGroupManager? _cachedSelectionGroups;
@@ -464,9 +470,8 @@
 /// This class has additional descenders in Google3.
 abstract class InspectorObjectGroupBase
     extends InspectorObjectGroupApi<RemoteDiagnosticsNode> {
-  InspectorObjectGroupBase(
-    String debugName,
-  ) : groupName = '${debugName}_${InspectorServiceBase.nextGroupId}' {
+  InspectorObjectGroupBase(String debugName)
+    : groupName = '${debugName}_${InspectorServiceBase.nextGroupId}' {
     InspectorServiceBase.nextGroupId++;
   }
 
@@ -492,12 +497,13 @@
   @override
   Future<void> dispose() {
     // No need to dispose the group if the isolate is already gone.
-    final disposeComplete = inspectorService.isolateRef != null
-        ? invokeVoidServiceMethod(
-            WidgetInspectorServiceExtensions.disposeGroup.name,
-            groupName,
-          )
-        : Future<void>.value();
+    final disposeComplete =
+        inspectorService.isolateRef != null
+            ? invokeVoidServiceMethod(
+              WidgetInspectorServiceExtensions.disposeGroup.name,
+              groupName,
+            )
+            : Future<void>.value();
     disposed = true;
     return disposeComplete;
   }
@@ -509,11 +515,11 @@
     if (disposed) return null;
     return useDaemonApi
         ? parseDiagnosticsNodeDaemon(
-            invokeServiceMethodDaemonInspectorRef(methodName, ref),
-          )
+          invokeServiceMethodDaemonInspectorRef(methodName, ref),
+        )
         : parseDiagnosticsNodeObservatory(
-            invokeServiceMethodObservatoryInspectorRef(methodName, ref),
-          );
+          invokeServiceMethodObservatoryInspectorRef(methodName, ref),
+        );
   }
 
   Future<RemoteDiagnosticsNode?> invokeServiceMethodWithArgReturningNode(
@@ -523,11 +529,11 @@
     if (disposed) return null;
     return useDaemonApi
         ? parseDiagnosticsNodeDaemon(
-            invokeServiceMethodDaemonArg(methodName, arg, groupName),
-          )
+          invokeServiceMethodDaemonArg(methodName, arg, groupName),
+        )
         : parseDiagnosticsNodeObservatory(
-            invokeServiceMethodObservatoryWithGroupName1(methodName, arg),
-          );
+          invokeServiceMethodObservatoryWithGroupName1(methodName, arg),
+        );
   }
 
   Future<Object?> invokeServiceMethodDaemonArg(
@@ -572,10 +578,9 @@
     String methodName, [
     String? objectGroup,
   ]) {
-    return invokeServiceMethodDaemonParams(
-      methodName,
-      {'objectGroup': objectGroup ?? groupName},
-    );
+    return invokeServiceMethodDaemonParams(methodName, {
+      'objectGroup': objectGroup ?? groupName,
+    });
   }
 
   Future<InstanceRef?> invokeServiceMethodObservatory1(
@@ -608,7 +613,8 @@
     if (!serviceConnection.serviceManager.serviceExtensionManager
         .isServiceExtensionAvailable(callMethodName)) {
       final available = await serviceConnection
-          .serviceManager.serviceExtensionManager
+          .serviceManager
+          .serviceExtensionManager
           .waitForServiceExtensionAvailable(callMethodName);
       if (!available) return null;
     }
@@ -625,12 +631,12 @@
     }
 
     return inspectorLibrary.addRequest(this, () async {
-      final r =
-          await serviceConnection.serviceManager.service!.callServiceExtension(
-        extension,
-        isolateId: inspectorService.isolateRef!.id,
-        args: args,
-      );
+      final r = await serviceConnection.serviceManager.service!
+          .callServiceExtension(
+            extension,
+            isolateId: inspectorService.isolateRef!.id,
+            args: args,
+          );
       if (disposed) return null;
       final json = r.json!;
       if (json['errorMessage'] != null) {
@@ -844,15 +850,15 @@
     if (disposed) return const [];
     return useDaemonApi
         ? parseDiagnosticsNodesDaemon(
-            invokeServiceMethodDaemonInspectorRef(methodName, instanceRef),
-            parent,
-            isProperty,
-          )
+          invokeServiceMethodDaemonInspectorRef(methodName, instanceRef),
+          parent,
+          isProperty,
+        )
         : parseDiagnosticsNodesObservatory(
-            invokeServiceMethodObservatoryInspectorRef(methodName, instanceRef),
-            parent,
-            isProperty,
-          );
+          invokeServiceMethodObservatoryInspectorRef(methodName, instanceRef),
+          parent,
+          isProperty,
+        );
   }
 
   /// Evaluate an expression where `object` references the [inspectorRef] or
@@ -937,10 +943,7 @@
 /// will be skipped. This means that clients should not have to write any
 /// special logic to handle orphaned requests.
 class ObjectGroup extends InspectorObjectGroupBase {
-  ObjectGroup(
-    super.debugName,
-    this.inspectorService,
-  );
+  ObjectGroup(super.debugName, this.inspectorService);
 
   @override
   final InspectorService inspectorService;
@@ -951,17 +954,22 @@
   Future<RemoteDiagnosticsNode?> getRoot(
     FlutterTreeType type, {
     bool isSummaryTree = false,
+    bool includeFullDetails = true,
   }) {
     // There is no excuse to call this method on a disposed group.
     assert(!disposed);
     switch (type) {
       case FlutterTreeType.widget:
-        return getRootWidgetTree(isSummaryTree: isSummaryTree);
+        return getRootWidgetTree(
+          isSummaryTree: isSummaryTree,
+          includeFullDetails: includeFullDetails,
+        );
     }
   }
 
   Future<RemoteDiagnosticsNode?> getRootWidgetTree({
     required bool isSummaryTree,
+    required bool includeFullDetails,
   }) {
     return parseDiagnosticsNodeDaemon(
       invokeServiceMethodDaemonParams(
@@ -970,12 +978,13 @@
           'groupName': groupName,
           'isSummaryTree': '$isSummaryTree',
           'withPreviews': 'true',
+          'fullDetails': '$includeFullDetails',
         },
       ),
     );
   }
 
-// TODO these ones could be not needed.
+  // TODO these ones could be not needed.
   /* TODO(jacobr): this probably isn't needed.
   Future<List<DiagnosticsPathNode>> getParentChain(DiagnosticsNode target) async {
     if (disposed) return null;
@@ -1053,19 +1062,19 @@
     }
     return useDaemonApi
         ? handleSetSelectionDaemon(
-            invokeServiceMethodDaemonInspectorRef(
-              WidgetInspectorServiceExtensions.setSelectionById.name,
-              selection,
-            ),
-            uiAlreadyUpdated,
-          )
+          invokeServiceMethodDaemonInspectorRef(
+            WidgetInspectorServiceExtensions.setSelectionById.name,
+            selection,
+          ),
+          uiAlreadyUpdated,
+        )
         : handleSetSelectionObservatory(
-            invokeServiceMethodObservatoryInspectorRef(
-              WidgetInspectorServiceExtensions.setSelectionById.name,
-              selection,
-            ),
-            uiAlreadyUpdated,
-          );
+          invokeServiceMethodObservatoryInspectorRef(
+            WidgetInspectorServiceExtensions.setSelectionById.name,
+            selection,
+          ),
+          uiAlreadyUpdated,
+        );
   }
 
   Future<bool> setSelection(GenericInstanceRef selection) async {
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/object_group_api.dart b/packages/devtools_app/lib/src/shared/diagnostics/object_group_api.dart
index a0630e8..216797e 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/object_group_api.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/object_group_api.dart
@@ -15,8 +15,7 @@
   Future<bool> setSelectionInspector(
     InspectorInstanceRef selection,
     bool uiAlreadyUpdated,
-  ) =>
-      throw UnimplementedError();
+  ) => throw UnimplementedError();
 
   Future<Map<String, InstanceRef>?> getEnumPropertyValues(
     InspectorInstanceRef ref,
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/primitives/source_location.dart b/packages/devtools_app/lib/src/shared/diagnostics/primitives/source_location.dart
index 246848c..1db0d0c 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/primitives/source_location.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/primitives/source_location.dart
@@ -18,10 +18,7 @@
   final Map<String, Object?> json;
   final InspectorSourceLocation? parent;
 
-  String? get path => JsonUtils.getStringMember(
-        json,
-        _JsonFields.file,
-      );
+  String? get path => JsonUtils.getStringMember(json, _JsonFields.file);
 
   String? getFile() {
     final fileName = path;
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/references.dart b/packages/devtools_app/lib/src/shared/diagnostics/references.dart
index ab77237..7c01c4e 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/references.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/references.dart
@@ -44,15 +44,10 @@
   final index = selection.heap.indexByCode[code];
   if (index == null) return selection;
 
-  return HeapObject(
-    selection.heap,
-    index: index,
-  );
+  return HeapObject(selection.heap, index: index);
 }
 
-Future<void> addChildReferences(
-  DartObjectNode variable,
-) async {
+Future<void> addChildReferences(DartObjectNode variable) async {
   final ref = variable.ref!;
   if (ref is! ObjectReferences) {
     throw StateError('Wrong type: ${ref.runtimeType}');
@@ -62,8 +57,10 @@
 
   switch (refNodeType) {
     case RefNodeType.refRoot:
-      final selection =
-          _refreshStaticSelection(ref.heapSelection, ref.instanceRef);
+      final selection = _refreshStaticSelection(
+        ref.heapSelection,
+        ref.instanceRef,
+      );
 
       variable.addAllChildren([
         if (ref.instanceRef != null)
@@ -104,41 +101,43 @@
 
       return;
     case RefNodeType.staticInRefs:
-      final children = ref.heapSelection
-          .references(ref.refNodeType.direction!)
-          .where((s) => !s.className!.isNull)
-          .map(
-            (s) => DartObjectNode.references(
-              s.className!.className,
-              ObjectReferences(
-                refNodeType: RefNodeType.staticInRefs,
-                heapSelection: s,
-                isolateRef: ref.isolateRef,
-                value: null,
-              ),
-              isRerootable: true,
-            ),
-          )
-          .toList();
+      final children =
+          ref.heapSelection
+              .references(ref.refNodeType.direction!)
+              .where((s) => !s.className!.isNull)
+              .map(
+                (s) => DartObjectNode.references(
+                  s.className!.className,
+                  ObjectReferences(
+                    refNodeType: RefNodeType.staticInRefs,
+                    heapSelection: s,
+                    isolateRef: ref.isolateRef,
+                    value: null,
+                  ),
+                  isRerootable: true,
+                ),
+              )
+              .toList();
       variable.addAllChildren(children);
       return;
     case RefNodeType.staticOutRefs:
-      final children = ref.heapSelection
-          .references(ref.refNodeType.direction!)
-          .where((s) => !s.className!.isNull)
-          .map(
-            (s) => DartObjectNode.references(
-              '${s.className!.className}, ${prettyPrintRetainedSize(s.retainedSize)}',
-              ObjectReferences(
-                refNodeType: RefNodeType.staticOutRefs,
-                heapSelection: s,
-                isolateRef: ref.isolateRef,
-                value: null,
-              ),
-              isRerootable: true,
-            ),
-          )
-          .toList();
+      final children =
+          ref.heapSelection
+              .references(ref.refNodeType.direction!)
+              .where((s) => !s.className!.isNull)
+              .map(
+                (s) => DartObjectNode.references(
+                  '${s.className!.className}, ${prettyPrintRetainedSize(s.retainedSize)}',
+                  ObjectReferences(
+                    refNodeType: RefNodeType.staticOutRefs,
+                    heapSelection: s,
+                    isolateRef: ref.isolateRef,
+                    value: null,
+                  ),
+                  isRerootable: true,
+                ),
+              )
+              .toList();
       variable.addAllChildren(children);
       return;
     case RefNodeType.liveRefRoot:
@@ -162,12 +161,11 @@
       final limit = preferences.memory.refLimit.value;
       final refs =
           (await serviceConnection.serviceManager.service!.getInboundReferences(
-                ref.isolateRef.id!,
-                value.id!,
-                limit + 1,
-              ))
-                  .references ??
-              [];
+            ref.isolateRef.id!,
+            value.id!,
+            limit + 1,
+          )).references ??
+          [];
 
       final refsToShow = min(limit, refs.length);
       final children = <DartObjectNode>[];
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/tree_builder.dart b/packages/devtools_app/lib/src/shared/diagnostics/tree_builder.dart
index 4dc184d..95673a7 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/tree_builder.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/tree_builder.dart
@@ -54,11 +54,7 @@
     if (properties == null || service == null || isolateRef == null) return;
     await _addExpandableChildren(
       variable,
-      await createVariablesForDiagnostics(
-        service,
-        properties,
-        isolateRef,
-      ),
+      await createVariablesForDiagnostics(service, properties, isolateRef),
       expandAll: true,
     );
   }
@@ -186,12 +182,7 @@
 }) {
   switch (value.kind) {
     case InstanceKind.kMap:
-      variable.addAllChildren(
-        createVariablesForMap(
-          value,
-          isolateRef,
-        ),
-      );
+      variable.addAllChildren(createVariablesForMap(value, isolateRef));
       break;
     case InstanceKind.kList:
       variable.addAllChildren(
@@ -199,9 +190,7 @@
       );
       break;
     case InstanceKind.kRecord:
-      variable.addAllChildren(
-        createVariablesForRecords(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForRecords(value, isolateRef));
       break;
     case InstanceKind.kUint8ClampedList:
     case InstanceKind.kUint8List:
@@ -217,29 +206,19 @@
     case InstanceKind.kInt32x4List:
     case InstanceKind.kFloat32x4List:
     case InstanceKind.kFloat64x2List:
-      variable.addAllChildren(
-        createVariablesForBytes(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForBytes(value, isolateRef));
       break;
     case InstanceKind.kRegExp:
-      variable.addAllChildren(
-        createVariablesForRegExp(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForRegExp(value, isolateRef));
       break;
     case InstanceKind.kClosure:
-      variable.addAllChildren(
-        createVariablesForClosure(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForClosure(value, isolateRef));
       break;
     case InstanceKind.kReceivePort:
-      variable.addAllChildren(
-        createVariablesForReceivePort(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForReceivePort(value, isolateRef));
       break;
     case InstanceKind.kType:
-      variable.addAllChildren(
-        createVariablesForType(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForType(value, isolateRef));
       break;
     case InstanceKind.kTypeParameter:
       variable.addAllChildren(
@@ -257,9 +236,7 @@
       );
       break;
     case InstanceKind.kStackTrace:
-      variable.addAllChildren(
-        createVariablesForStackTrace(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForStackTrace(value, isolateRef));
       break;
     case InstanceKind.kMirrorReference:
       variable.addAllChildren(
@@ -267,9 +244,7 @@
       );
       break;
     case InstanceKind.kUserTag:
-      variable.addAllChildren(
-        createVariablesForUserTag(value, isolateRef),
-      );
+      variable.addAllChildren(createVariablesForUserTag(value, isolateRef));
       break;
     default:
       break;
@@ -281,11 +256,7 @@
 
   if (value.fields != null && value.kind != InstanceKind.kRecord) {
     variable.addAllChildren(
-      createVariablesForFields(
-        value,
-        isolateRef,
-        existingNames: existingNames,
-      ),
+      createVariablesForFields(value, isolateRef, existingNames: existingNames),
     );
   }
 }
@@ -300,15 +271,11 @@
     switch (value.runtimeType) {
       case const (Func):
         final function = value as Func;
-        variable.addAllChildren(
-          createVariablesForFunc(function, isolateRef),
-        );
+        variable.addAllChildren(createVariablesForFunc(function, isolateRef));
         break;
       case const (Context):
         final context = value as Context;
-        variable.addAllChildren(
-          createVariablesForContext(context, isolateRef),
-        );
+        variable.addAllChildren(createVariablesForContext(context, isolateRef));
         break;
     }
   } else if (value is! String && value is! num && value is! bool) {
@@ -359,9 +326,7 @@
             );
           } catch (e) {
             if (e is! SentinelException) {
-              _log.warning(
-                'Caught $e accessing the value of an object',
-              );
+              _log.warning('Caught $e accessing the value of an object');
             }
           }
         }
@@ -399,11 +364,7 @@
   final diagnostic = ref.diagnostic;
   final value = variable.value;
 
-  await _addDiagnosticsIfNeeded(
-    diagnostic,
-    isolateRef,
-    variable,
-  );
+  await _addDiagnosticsIfNeeded(diagnostic, isolateRef, variable);
 
   try {
     if (ref is ObjectReferences) {
@@ -437,10 +398,7 @@
     expandAll,
   );
 
-  await _addInspectorItems(
-    variable,
-    isolateRef,
-  );
+  await _addInspectorItems(variable, isolateRef);
 
   variable.treeInitializeComplete = true;
 }
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/variable_factory.dart b/packages/devtools_app/lib/src/shared/diagnostics/variable_factory.dart
index b69ede4..75417b1 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/variable_factory.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/variable_factory.dart
@@ -12,7 +12,7 @@
 import 'package:vm_service/vm_service.dart';
 
 import '../memory/heap_object.dart';
-import '../vm_utils.dart';
+import '../utils/vm_utils.dart';
 import 'dart_object_node.dart';
 import 'diagnostics_node.dart';
 import 'object_group_api.dart';
@@ -305,8 +305,9 @@
   InspectorObjectGroupApi<RemoteDiagnosticsNode> objectGroup,
   IsolateRef? isolateRef,
 ) async {
-  final instanceRef =
-      await objectGroup.toObservatoryInstanceRef(diagnostic.valueRef);
+  final instanceRef = await objectGroup.toObservatoryInstanceRef(
+    diagnostic.valueRef,
+  );
   return DartObjectNode.fromValue(
     name: diagnostic.name,
     value: instanceRef,
@@ -469,10 +470,7 @@
 ) {
   final elements = instance.elements ?? [];
   return elements.map((element) {
-    return DartObjectNode.fromValue(
-      value: element,
-      isolateRef: isolateRef,
-    );
+    return DartObjectNode.fromValue(value: element, isolateRef: isolateRef);
   }).toList();
 }
 
@@ -555,10 +553,7 @@
     final name = field.name;
     if (name is! String) {
       result.add(
-        DartObjectNode.fromValue(
-          value: field.value,
-          isolateRef: isolateRef,
-        ),
+        DartObjectNode.fromValue(value: field.value, isolateRef: isolateRef),
       );
     } else {
       if (existingNames != null && existingNames.contains(name)) continue;
diff --git a/packages/devtools_app/lib/src/shared/diagnostics_text_styles.dart b/packages/devtools_app/lib/src/shared/diagnostics_text_styles.dart
deleted file mode 100644
index 983bfb9..0000000
--- a/packages/devtools_app/lib/src/shared/diagnostics_text_styles.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:flutter/material.dart';
-
-// Enum-like static classes are ok.
-// ignore: avoid_classes_with_only_static_members
-class DiagnosticsTextStyles {
-  static TextStyle unimportant(ColorScheme colorScheme) => TextStyle(
-        color:
-            colorScheme.isLight ? Colors.grey.shade500 : Colors.grey.shade600,
-      );
-
-  static TextStyle regular(ColorScheme colorScheme) => TextStyle(
-        // The font size when not specified seems to be 14, but specify here since we
-        // are scaling based on this font size in [IdeTheme].
-        fontSize: defaultFontSize,
-        color: colorScheme.onSurface,
-      );
-
-  static TextStyle warning(ColorScheme colorScheme) => TextStyle(
-        color: colorScheme.isLight
-            ? Colors.orange.shade900
-            : Colors.orange.shade400,
-      );
-
-  static TextStyle error(ColorScheme colorScheme) => TextStyle(
-        color: colorScheme.error,
-      );
-
-  static TextStyle link(ColorScheme colorScheme) => TextStyle(
-        color:
-            colorScheme.isLight ? Colors.blue.shade700 : Colors.blue.shade300,
-        decoration: TextDecoration.underline,
-      );
-
-  static const regularBold = TextStyle(
-    fontWeight: FontWeight.w700,
-  );
-
-  static TextStyle textStyleForLevel(
-    DiagnosticLevel level,
-    ColorScheme colorScheme,
-  ) {
-    switch (level) {
-      case DiagnosticLevel.hidden:
-        return unimportant(colorScheme);
-      case DiagnosticLevel.warning:
-        return warning(colorScheme);
-      case DiagnosticLevel.error:
-        return error(colorScheme);
-      case DiagnosticLevel.debug:
-      case DiagnosticLevel.info:
-      case DiagnosticLevel.fine:
-      default:
-        return regular(colorScheme);
-    }
-  }
-}
diff --git a/packages/devtools_app/lib/src/shared/dialogs.dart b/packages/devtools_app/lib/src/shared/dialogs.dart
deleted file mode 100644
index af3e5e2..0000000
--- a/packages/devtools_app/lib/src/shared/dialogs.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:flutter/material.dart';
-
-import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart';
-import 'globals.dart';
-import 'utils.dart';
-
-/// A dialog, that reports unexpected error and allows to copy details and create issue.
-class UnexpectedErrorDialog extends StatelessWidget {
-  const UnexpectedErrorDialog({
-    super.key,
-    required this.additionalInfo,
-  });
-
-  final String additionalInfo;
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-
-    return DevToolsDialog(
-      title: const Text('Unexpected Error'),
-      content: Text(
-        additionalInfo,
-        style: theme.fixedFontStyle,
-      ),
-      actions: [
-        DialogTextButton(
-          child: const Text('Copy details'),
-          onPressed: () => unawaited(
-            copyToClipboard(
-              additionalInfo,
-              successMessage: 'Error details copied to clipboard',
-            ),
-          ),
-        ),
-        DialogTextButton(
-          child: const Text('Create issue'),
-          onPressed: () => unawaited(
-            launchUrlWithErrorHandling(
-              devToolsEnvironmentParameters
-                  .issueTrackerLink(additionalInfo: additionalInfo)
-                  .url,
-            ),
-          ),
-        ),
-        const DialogCloseButton(),
-      ],
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_base.dart b/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_base.dart
index 0dbe8f0..8c6fd1a 100644
--- a/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_base.dart
+++ b/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_base.dart
@@ -5,8 +5,8 @@
 import 'package:flutter/material.dart';
 
 import '../../screens/debugger/codeview.dart';
-import '../common_widgets.dart';
 import '../diagnostics/inspector_service.dart';
+import '../ui/common_widgets.dart';
 
 abstract class DevToolsEnvironmentParameters {
   List<ScriptPopupMenuOption> buildExtraDebuggerScriptPopupMenuOptions();
diff --git a/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_external.dart b/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_external.dart
index b6e25bd..afc5758 100644
--- a/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_external.dart
+++ b/packages/devtools_app/lib/src/shared/environment_parameters/environment_parameters_external.dart
@@ -6,10 +6,10 @@
 
 import '../../screens/debugger/codeview.dart';
 import '../analytics/constants.dart' as gac;
-import '../common_widgets.dart';
 import '../diagnostics/inspector_service.dart';
 import '../globals.dart';
-import '../utils.dart';
+import '../ui/common_widgets.dart';
+import '../utils/utils.dart';
 import 'environment_parameters_base.dart';
 
 class ExternalDevToolsEnvironmentParameters
@@ -22,11 +22,12 @@
   GaLink issueTrackerLink({String? additionalInfo, String? issueTitle}) {
     return GaLink(
       display: _newDevToolsIssueUriDisplay,
-      url: newDevToolsGitHubIssueUriLengthSafe(
-        additionalInfo: additionalInfo,
-        issueTitle: issueTitle,
-        environment: issueLinkDetails(),
-      ).toString(),
+      url:
+          newDevToolsGitHubIssueUriLengthSafe(
+            additionalInfo: additionalInfo,
+            issueTitle: issueTitle,
+            environment: issueLinkDetails(),
+          ).toString(),
       gaScreenName: gac.devToolsMain,
       gaSelectedItemDescription: gac.feedbackLink,
     );
@@ -115,8 +116,10 @@
   }
 
   // Truncate the additional info if the URL is too long:
-  final truncatedInfo =
-      additionalInfo.substring(0, additionalInfo.length - lengthToCut);
+  final truncatedInfo = additionalInfo.substring(
+    0,
+    additionalInfo.length - lengthToCut,
+  );
 
   final truncatedUri = _newDevToolsGitHubIssueUri(
     additionalInfo: truncatedInfo,
@@ -137,10 +140,7 @@
     ...environment,
   ].join('\n');
 
-  return Uri.parse('https://$_newDevToolsIssueUriDisplay').replace(
-    queryParameters: {
-      'title': issueTitle,
-      'body': issueBody,
-    },
-  );
+  return Uri.parse(
+    'https://$_newDevToolsIssueUriDisplay',
+  ).replace(queryParameters: {'title': issueTitle, 'body': issueBody});
 }
diff --git a/packages/devtools_app/lib/src/shared/feature_flags.dart b/packages/devtools_app/lib/src/shared/feature_flags.dart
index 8901a3b..14151c9 100644
--- a/packages/devtools_app/lib/src/shared/feature_flags.dart
+++ b/packages/devtools_app/lib/src/shared/feature_flags.dart
@@ -23,8 +23,9 @@
 ///   "args": [
 ///     "--dart-define=enable_experiments=true"
 ///   ]
-const _experimentsEnabledByEnvironment =
-    bool.fromEnvironment('enable_experiments');
+const _experimentsEnabledByEnvironment = bool.fromEnvironment(
+  'enable_experiments',
+);
 
 bool _experimentsEnabledFromMain = false;
 
@@ -35,8 +36,10 @@
 @visibleForTesting
 bool get enableBeta => enableExperiments || !isExternalBuild;
 
-const _kMemoryDisconnectExperience =
-    bool.fromEnvironment('memory_disconnect_experience', defaultValue: true);
+const _kMemoryDisconnectExperience = bool.fromEnvironment(
+  'memory_disconnect_experience',
+  defaultValue: true,
+);
 
 // It is ok to have enum-like static only classes.
 // ignore: avoid_classes_with_only_static_members
@@ -84,11 +87,6 @@
   /// once extension support is added in g3.
   static bool devToolsExtensions = isExternalBuild;
 
-  /// Flag to enable the new Logging experience.
-  ///
-  /// https://github.com/flutter/devtools/issues/7703
-  static bool loggingV2 = enableExperiments;
-
   /// Flag to enable debugging via DAP.
   ///
   /// https://github.com/flutter/devtools/issues/6056
@@ -104,6 +102,11 @@
   /// https://github.com/flutter/devtools/issues/7856
   static bool wasmOptInSetting = true;
 
+  /// Flag to enable the Flutter Property Editor sidebar.
+  ///
+  /// https://github.com/flutter/devtools/issues/7854
+  static bool propertyEditor = enableExperiments;
+
   /// Stores a map of all the feature flags for debugging purposes.
   ///
   /// When adding a new flag, you are responsible for adding it to this map as
@@ -112,10 +115,10 @@
     'widgetRebuildStats': widgetRebuildStats,
     'memorySaveLoad': memorySaveLoad,
     'deepLinkIosCheck': deepLinkIosCheck,
-    'loggingV2': loggingV2,
     'dapDebugging': dapDebugging,
     'inspectorV2': inspectorV2,
     'wasmOptInSetting': wasmOptInSetting,
+    'propertyEditor': propertyEditor,
   };
 
   /// A helper to print the status of all the feature flags.
diff --git a/packages/devtools_app/lib/src/framework/app_error_handling.dart b/packages/devtools_app/lib/src/shared/framework/app_error_handling.dart
similarity index 88%
rename from packages/devtools_app/lib/src/framework/app_error_handling.dart
rename to packages/devtools_app/lib/src/shared/framework/app_error_handling.dart
index 46dca87..616e07a 100644
--- a/packages/devtools_app/lib/src/framework/app_error_handling.dart
+++ b/packages/devtools_app/lib/src/shared/framework/app_error_handling.dart
@@ -14,9 +14,9 @@
 import 'package:source_maps/source_maps.dart';
 import 'package:stack_trace/stack_trace.dart' as stack_trace;
 
-import '../shared/analytics/analytics.dart' as ga;
-import '../shared/analytics/analytics_common.dart';
-import '../shared/globals.dart';
+import '../analytics/analytics.dart' as ga;
+import '../analytics/analytics_common.dart';
+import '../globals.dart';
 
 final _log = Logger('app_error_handling');
 
@@ -103,12 +103,13 @@
   // Split the stack trace up into substrings of size
   // [ga4ParamValueCharacterLimit] so that we can send the stack trace in chunks
   // to GA4 through unified_analytics.
-  final stackTraceSubstrings = stackTrace
-      .toString()
-      .characters
-      .slices(ga4ParamValueCharacterLimit)
-      .map((slice) => slice.join())
-      .toList();
+  final stackTraceSubstrings =
+      stackTrace
+          .toString()
+          .characters
+          .slices(ga4ParamValueCharacterLimit)
+          .map((slice) => slice.join())
+          .toList();
   ga.reportError('$error', stackTraceSubstrings: stackTraceSubstrings);
 
   // Show error message in a notification pop-up:
@@ -131,10 +132,9 @@
 }
 
 Future<SingleMapping?> _initializeSourceMapping() async {
+  if (!kIsWeb) return null;
   try {
-    final sourceMapUri = Uri.parse(
-      'main.dart.${kIsWasm ? 'wasm' : 'js'}.map',
-    );
+    final sourceMapUri = Uri.parse('main.dart.${kIsWasm ? 'wasm' : 'js'}.map');
     final sourceMapFile = await get(sourceMapUri);
 
     return SingleMapping.fromJson(
@@ -153,19 +153,16 @@
 
   final mappedStackTrace = await _maybeMapStackTrace(originalStackTrace);
   // If mapping fails, revert back to the original stack trace:
-  final stackTrace = mappedStackTrace.toString().isEmpty
-      ? originalStackTrace
-      : mappedStackTrace;
+  final stackTrace =
+      mappedStackTrace.toString().isEmpty
+          ? originalStackTrace
+          : mappedStackTrace;
   return stack_trace.Trace.from(stackTrace);
 }
 
 Future<StackTrace> _maybeMapStackTrace(StackTrace stack) async {
   final sourceMapping = await _fetchSourceMapping();
   return sourceMapping != null
-      ? mapStackTrace(
-          sourceMapping,
-          stack,
-          minified: true,
-        )
+      ? mapStackTrace(sourceMapping, stack, minified: true)
       : stack;
 }
diff --git a/packages/devtools_app/lib/src/shared/framework_controller.dart b/packages/devtools_app/lib/src/shared/framework/framework_controller.dart
similarity index 95%
rename from packages/devtools_app/lib/src/shared/framework_controller.dart
rename to packages/devtools_app/lib/src/shared/framework/framework_controller.dart
index 2c8569c..556b8fd 100644
--- a/packages/devtools_app/lib/src/shared/framework_controller.dart
+++ b/packages/devtools_app/lib/src/shared/framework/framework_controller.dart
@@ -6,7 +6,7 @@
 
 import 'package:devtools_app_shared/shared.dart';
 
-import 'globals.dart';
+import '../globals.dart';
 
 /// This controller is used by the connection to the DevTools server to receive
 /// commands from the server, and to notify the server of DevTools state changes
@@ -36,10 +36,7 @@
   /// Notify the controller of a connect to VM event.
   void notifyConnectToVmEvent(Uri serviceProtocolUri, {bool notify = false}) {
     _connectVmController.add(
-      ConnectVmEvent(
-        serviceProtocolUri: serviceProtocolUri,
-        notify: notify,
-      ),
+      ConnectVmEvent(serviceProtocolUri: serviceProtocolUri, notify: notify),
     );
   }
 
diff --git a/packages/devtools_app/lib/src/shared/routing.dart b/packages/devtools_app/lib/src/shared/framework/routing.dart
similarity index 92%
rename from packages/devtools_app/lib/src/shared/routing.dart
rename to packages/devtools_app/lib/src/shared/framework/routing.dart
index 01e4293..f6e1200 100644
--- a/packages/devtools_app/lib/src/shared/routing.dart
+++ b/packages/devtools_app/lib/src/shared/framework/routing.dart
@@ -10,10 +10,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
-import '../framework/framework_core.dart';
-import 'globals.dart';
-import 'primitives/utils.dart';
-import 'query_parameters.dart';
+import '../../framework/framework_core.dart';
+import '../globals.dart';
+import '../primitives/query_parameters.dart';
+import '../primitives/utils.dart';
 
 const homeScreenId = '';
 const snapshotScreenId = 'snapshot';
@@ -100,13 +100,13 @@
         ChangeNotifier,
         PopNavigatorRouterDelegateMixin<DevToolsRouteConfiguration> {
   DevToolsRouterDelegate(this._getPage, [GlobalKey<NavigatorState>? key])
-      : navigatorKey = key ?? GlobalKey<NavigatorState>(),
-        _isTestMode = false;
+    : navigatorKey = key ?? GlobalKey<NavigatorState>(),
+      _isTestMode = false;
 
   @visibleForTesting
   DevToolsRouterDelegate.test(this._getPage, [GlobalKey<NavigatorState>? key])
-      : navigatorKey = key ?? GlobalKey<NavigatorState>(),
-        _isTestMode = true;
+    : navigatorKey = key ?? GlobalKey<NavigatorState>(),
+      _isTestMode = true;
 
   static DevToolsRouterDelegate of(BuildContext context) =>
       Router.of(context).routerDelegate as DevToolsRouterDelegate;
@@ -128,7 +128,8 @@
     String?,
     DevToolsQueryParams,
     DevToolsNavigationState?,
-  ) _getPage;
+  )
+  _getPage;
 
   /// A list of any routes/pages on the stack.
   ///
@@ -196,13 +197,12 @@
     Map<String, String?>? argUpdates,
     DevToolsNavigationState? state,
   ]) {
-    final newParams = currentConfiguration?.params.withUpdates(argUpdates) ??
+    final newParams =
+        currentConfiguration?.params.withUpdates(argUpdates) ??
         DevToolsQueryParams.empty();
 
     unawaited(
-      _replaceStack(
-        DevToolsRouteConfiguration(page, newParams, state),
-      ),
+      _replaceStack(DevToolsRouteConfiguration(page, newParams, state)),
     );
     notifyListeners();
   }
@@ -211,13 +211,10 @@
     bool clearUriParam = false,
     required bool clearScreenParam,
   }) {
-    navigate(
-      homeScreenId,
-      {
-        if (clearUriParam) 'uri': null,
-        if (clearScreenParam) 'screen': null,
-      },
-    );
+    navigate(homeScreenId, {
+      if (clearUriParam) 'uri': null,
+      if (clearScreenParam) 'screen': null,
+    });
   }
 
   /// Replaces the navigation stack with a new route.
@@ -264,11 +261,7 @@
     final newArgs = currentConfig.params.withUpdates(argUpdates);
 
     await _replaceStack(
-      DevToolsRouteConfiguration(
-        currentPage,
-        newArgs,
-        currentConfig.state,
-      ),
+      DevToolsRouteConfiguration(currentPage, newArgs, currentConfig.state),
     );
     notifyListeners();
   }
@@ -356,10 +349,7 @@
   DevToolsNavigationState({
     required this.kind,
     required Map<String, String?> state,
-  }) : _state = {
-          _kKind: kind,
-          ...state,
-        };
+  }) : _state = {_kKind: kind, ...state};
 
   DevToolsNavigationState._(this._state) : kind = _state[_kKind]!;
 
@@ -371,10 +361,7 @@
   final Map<String, String?> _state;
 
   bool hasChanges(DevToolsNavigationState? other) {
-    return !mapEquals(
-      {...state, ...?other?.state},
-      state,
-    );
+    return !mapEquals({...state, ...?other?.state}, state);
   }
 
   /// Creates a new [DevToolsNavigationState] by merging this instance with
@@ -384,10 +371,7 @@
   /// this instance (e.g., if both instances have state with the same key, the
   /// state in [other] will be used).
   DevToolsNavigationState merge(DevToolsNavigationState other) {
-    final newState = <String, String?>{
-      ..._state,
-      ...other._state,
-    };
+    final newState = <String, String?>{..._state, ...other._state};
     return DevToolsNavigationState(kind: kind, state: newState);
   }
 
diff --git a/packages/devtools_app/lib/src/shared/screen.dart b/packages/devtools_app/lib/src/shared/framework/screen.dart
similarity index 85%
rename from packages/devtools_app/lib/src/shared/screen.dart
rename to packages/devtools_app/lib/src/shared/framework/screen.dart
index 74c8971..8d5c93e 100644
--- a/packages/devtools_app/lib/src/shared/screen.dart
+++ b/packages/devtools_app/lib/src/shared/framework/screen.dart
@@ -12,10 +12,10 @@
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 
-import 'feature_flags.dart';
-import 'globals.dart';
-import 'primitives/listenable.dart';
-import 'ui/icons.dart';
+import '../feature_flags.dart';
+import '../globals.dart';
+import '../primitives/listenable.dart';
+import '../ui/icons.dart';
 
 final _log = Logger('screen.dart');
 
@@ -127,9 +127,9 @@
     this.requiresLibrary,
     this.tutorialVideoTimestamp,
   }) : assert(
-          icon == null || iconAsset == null,
-          'Only one of icon or iconAsset may be specified.',
-        );
+         icon == null || iconAsset == null,
+         'Only one of icon or iconAsset may be specified.',
+       );
 
   final String id;
   final String? title;
@@ -193,14 +193,14 @@
     this.requiresVmDeveloperMode = false,
     this.worksWithOfflineData = false,
     this.showFloatingDebuggerControls = true,
-  })  : assert(
-          title == null || titleGenerator == null,
-          'Only one of title or titleGenerator may be specified.',
-        ),
-        assert(
-          icon == null || iconAsset == null,
-          'Only one of icon or iconAsset may be specified.',
-        );
+  }) : assert(
+         title == null || titleGenerator == null,
+         'Only one of title or titleGenerator may be specified.',
+       ),
+       assert(
+         icon == null || iconAsset == null,
+         'Only one of icon or iconAsset may be specified.',
+       );
 
   const Screen.conditional({
     required String id,
@@ -219,21 +219,21 @@
     String? iconAsset,
     Key? tabKey,
   }) : this(
-          id,
-          requiresLibrary: requiresLibrary,
-          requiresConnection: requiresConnection,
-          requiresDartVm: requiresDartVm,
-          requiresFlutter: requiresFlutter,
-          requiresDebugBuild: requiresDebugBuild,
-          requiresVmDeveloperMode: requiresVmDeveloperMode,
-          worksWithOfflineData: worksWithOfflineData,
-          showFloatingDebuggerControls: showFloatingDebuggerControls,
-          title: title,
-          titleGenerator: titleGenerator,
-          icon: icon,
-          iconAsset: iconAsset,
-          tabKey: tabKey,
-        );
+         id,
+         requiresLibrary: requiresLibrary,
+         requiresConnection: requiresConnection,
+         requiresDartVm: requiresDartVm,
+         requiresFlutter: requiresFlutter,
+         requiresDebugBuild: requiresDebugBuild,
+         requiresVmDeveloperMode: requiresVmDeveloperMode,
+         worksWithOfflineData: worksWithOfflineData,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+         title: title,
+         titleGenerator: titleGenerator,
+         icon: icon,
+         iconAsset: iconAsset,
+         tabKey: tabKey,
+       );
 
   Screen.fromMetaData(
     ScreenMetaData metadata, {
@@ -242,22 +242,22 @@
     String Function()? titleGenerator,
     Key? tabKey,
   }) : this.conditional(
-          id: metadata.id,
-          requiresLibrary: metadata.requiresLibrary,
-          requiresConnection: metadata.requiresConnection,
-          requiresDartVm: metadata.requiresDartVm,
-          requiresFlutter: metadata.requiresFlutter,
-          requiresDebugBuild: metadata.requiresDebugBuild,
-          requiresVmDeveloperMode: metadata.requiresVmDeveloperMode,
-          worksWithOfflineData: metadata.worksWithOfflineData,
-          shouldShowForFlutterVersion: shouldShowForFlutterVersion,
-          showFloatingDebuggerControls: showFloatingDebuggerControls,
-          title: titleGenerator == null ? metadata.title : null,
-          titleGenerator: titleGenerator,
-          icon: metadata.icon,
-          iconAsset: metadata.iconAsset,
-          tabKey: tabKey,
-        );
+         id: metadata.id,
+         requiresLibrary: metadata.requiresLibrary,
+         requiresConnection: metadata.requiresConnection,
+         requiresDartVm: metadata.requiresDartVm,
+         requiresFlutter: metadata.requiresFlutter,
+         requiresDebugBuild: metadata.requiresDebugBuild,
+         requiresVmDeveloperMode: metadata.requiresVmDeveloperMode,
+         worksWithOfflineData: metadata.worksWithOfflineData,
+         shouldShowForFlutterVersion: shouldShowForFlutterVersion,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+         title: titleGenerator == null ? metadata.title : null,
+         titleGenerator: titleGenerator,
+         icon: metadata.icon,
+         iconAsset: metadata.iconAsset,
+         tabKey: tabKey,
+       );
 
   /// Whether to show floating debugger controls if the app is paused.
   ///
@@ -378,8 +378,9 @@
   Widget buildTab(BuildContext context) {
     final title = _userFacingTitle;
     return ValueListenableBuilder<int>(
-      valueListenable:
-          serviceConnection.errorBadgeManager.errorCountNotifier(screenId),
+      valueListenable: serviceConnection.errorBadgeManager.errorCountNotifier(
+        screenId,
+      ),
       builder: (context, count, _) {
         final tab = Tab(
           key: tabKey,
@@ -389,18 +390,16 @@
                 DevToolsIcon(
                   icon: icon,
                   iconAsset: iconAsset,
-                  size: iconAsset != null
-                      // Add 1.0 to adjust for margins on the screen icon assets.
-                      ? scaleByFontFactor(defaultIconSizeBeforeScaling + 1.0)
-                      : defaultIconSize,
+                  size:
+                      iconAsset != null
+                          // Add 1.0 to adjust for margins on the screen icon assets.
+                          ? scaleByFontFactor(defaultIconSizeBeforeScaling + 1.0)
+                          : defaultIconSize,
                 ),
               if (title.isNotEmpty)
                 Padding(
                   padding: const EdgeInsets.only(left: denseSpacing),
-                  child: Text(
-                    title,
-                    style: Theme.of(context).regularTextStyle,
-                  ),
+                  child: Text(title, style: Theme.of(context).regularTextStyle),
                 ),
             ],
           ),
@@ -450,7 +449,7 @@
     if (!requiresConnection) {
       final connected =
           serviceConnection.serviceManager.connectedState.value.connected &&
-              serviceConnection.serviceManager.connectedAppInitialized;
+          serviceConnection.serviceManager.connectedAppInitialized;
       // Do not use the disconnected body in offline mode, because the default
       // [buildScreenBody] should be used for offline states.
       if (!connected && !offlineDataController.showingOfflineData.value) {
@@ -490,13 +489,15 @@
     _log.finest('for offline mode: returning ${screen.worksWithOfflineData}');
     return (
       show: screen.worksWithOfflineData,
-      disabledReason: screen.worksWithOfflineData
-          ? null
-          : ScreenDisabledReason.offlineDataNotSupported,
+      disabledReason:
+          screen.worksWithOfflineData
+              ? null
+              : ScreenDisabledReason.offlineDataNotSupported,
     );
   }
 
-  final serviceReady = serviceConnection.serviceManager.isServiceAvailable &&
+  final serviceReady =
+      serviceConnection.serviceManager.isServiceAvailable &&
       serviceConnection.serviceManager.connectedApp!.connectedAppInitialized;
   if (!serviceReady) {
     if (!screen.requiresConnection) {
@@ -517,8 +518,9 @@
   if (screen.requiresLibrary != null) {
     if (serviceConnection.serviceManager.isolateManager.mainIsolate.value ==
             null ||
-        !serviceConnection.serviceManager
-            .libraryUriAvailableNow(screen.requiresLibrary)) {
+        !serviceConnection.serviceManager.libraryUriAvailableNow(
+          screen.requiresLibrary,
+        )) {
       _log.finest(
         'screen requires library ${screen.requiresLibrary}: returning false',
       );
@@ -572,9 +574,10 @@
 
   @override
   void paint(Canvas canvas, Size size) {
-    final paint = Paint()
-      ..color = colorScheme.errorContainer
-      ..style = PaintingStyle.fill;
+    final paint =
+        Paint()
+          ..color = colorScheme.errorContainer
+          ..style = PaintingStyle.fill;
 
     final countPainter = TextPainter(
       text: TextSpan(
@@ -612,10 +615,8 @@
 }
 
 class ShortcutsConfiguration {
-  const ShortcutsConfiguration({
-    required this.shortcuts,
-    required this.actions,
-  }) : assert(shortcuts.length == actions.length);
+  const ShortcutsConfiguration({required this.shortcuts, required this.actions})
+    : assert(shortcuts.length == actions.length);
 
   factory ShortcutsConfiguration.empty() {
     return ShortcutsConfiguration(shortcuts: {}, actions: {});
diff --git a/packages/devtools_app/lib/src/shared/globals.dart b/packages/devtools_app/lib/src/shared/globals.dart
index 37d2e63..a2250ca 100644
--- a/packages/devtools_app/lib/src/shared/globals.dart
+++ b/packages/devtools_app/lib/src/shared/globals.dart
@@ -10,17 +10,17 @@
 import '../extensions/extension_service.dart';
 import '../screens/debugger/breakpoint_manager.dart';
 import '../service/service_manager.dart';
-import '../shared/banner_messages.dart';
-import '../shared/notifications.dart';
 import 'console/eval/eval_service.dart';
 import 'environment_parameters/environment_parameters_base.dart';
-import 'framework_controller.dart';
-import 'offline_data.dart';
+import 'framework/framework_controller.dart';
+import 'managers/banner_messages.dart';
+import 'managers/notifications.dart';
+import 'managers/script_manager.dart';
+import 'managers/survey.dart';
+import 'offline/offline_data.dart';
 import 'preferences/preferences.dart';
 import 'primitives/message_bus.dart';
 import 'primitives/storage.dart';
-import 'scripts/script_manager.dart';
-import 'survey.dart';
 
 /// Whether this DevTools build is external.
 bool get isExternalBuild => _isExternalBuild;
diff --git a/packages/devtools_app/lib/src/shared/history_viewport.dart b/packages/devtools_app/lib/src/shared/history_viewport.dart
deleted file mode 100644
index 53a803f..0000000
--- a/packages/devtools_app/lib/src/shared/history_viewport.dart
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app_shared/ui.dart';
-import 'package:flutter/material.dart';
-
-import '../screens/debugger/common.dart';
-import 'common_widgets.dart';
-import 'primitives/history_manager.dart';
-
-/// A [Widget] that allows for displaying content based on the current state of a
-/// [HistoryManager]. Includes built-in controls for navigating back and forth
-/// through the content stored in the provided [HistoryManager].
-///
-/// [history] is the [HistoryManager] that contains the data to be displayed.
-///
-/// [contentBuilder] is invoked with the currently selected historical data
-/// when building the contents of the viewport.
-///
-/// If [controls] is provided, each [Widget] will be inserted with padding
-/// at the end of the viewport title bar.
-///
-/// If [generateTitle] is provided, the title string will be set to the
-/// returned value. If not provided, the title will be empty.
-class HistoryViewport<T> extends StatefulWidget {
-  const HistoryViewport({
-    super.key,
-    required this.history,
-    required this.contentBuilder,
-    this.controls,
-    this.generateTitle,
-    this.onChange,
-    this.historyEnabled = true,
-    this.onTitleTap,
-    this.titleIcon,
-  });
-
-  final HistoryManager<T> history;
-  final Widget Function(BuildContext, T?) contentBuilder;
-  final List<Widget>? controls;
-  final String Function(T?)? generateTitle;
-  final void Function(T?, T?)? onChange;
-  final bool historyEnabled;
-  final VoidCallback? onTitleTap;
-  final IconData? titleIcon;
-
-  @override
-  State<HistoryViewport<T>> createState() => _HistoryViewportState<T>();
-}
-
-class _HistoryViewportState<T> extends State<HistoryViewport<T>> {
-  TextStyle? _titleStyle;
-  Color? _iconColor;
-
-  void _updateTitleStyle(TextStyle style) {
-    setState(() {
-      _titleStyle = style;
-      _iconColor = style.color;
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final titleIcon = widget.titleIcon != null
-        ? Padding(
-            padding: const EdgeInsets.only(right: densePadding),
-            child: Icon(
-              widget.titleIcon,
-              size: defaultIconSize,
-              color: _iconColor,
-            ),
-          )
-        : const SizedBox.shrink();
-
-    return ValueListenableBuilder<T?>(
-      valueListenable: widget.history.current,
-      builder: (context, T? current, _) {
-        final theme = Theme.of(context);
-        final title = widget.generateTitle == null
-            ? '  '
-            : widget.generateTitle!(current);
-        final defaultTitleStyle = theme.textTheme.titleMedium!;
-        final titleWidget = debuggerSectionTitle(
-          theme,
-          child: Row(
-            children: [
-              if (widget.historyEnabled) ...[
-                ToolbarAction(
-                  icon: Icons.chevron_left,
-                  onPressed: widget.history.hasPrevious
-                      ? () {
-                          widget.history.moveBack();
-                          if (widget.onChange != null) {
-                            widget.onChange!(
-                              widget.history.current.value,
-                              widget.history.peekNext(),
-                            );
-                          }
-                        }
-                      : null,
-                ),
-                ToolbarAction(
-                  icon: Icons.chevron_right,
-                  onPressed: widget.history.hasNext
-                      ? () {
-                          final current = widget.history.current.value as T;
-                          widget.history.moveForward();
-                          if (widget.onChange != null) {
-                            widget.onChange!(
-                              widget.history.current.value,
-                              current,
-                            );
-                          }
-                        }
-                      : null,
-                ),
-                const SizedBox(width: denseSpacing),
-                const VerticalDivider(thickness: 1.0),
-                const SizedBox(width: defaultSpacing),
-              ],
-              Expanded(
-                child: widget.onTitleTap == null
-                    ? Row(
-                        children: [
-                          titleIcon,
-                          Text(
-                            title,
-                            style: defaultTitleStyle,
-                          ),
-                        ],
-                      )
-                    : MouseRegion(
-                        cursor: SystemMouseCursors.click,
-                        onExit: (_) {
-                          _updateTitleStyle(defaultTitleStyle);
-                        },
-                        onEnter: (_) {
-                          _updateTitleStyle(
-                            defaultTitleStyle.copyWith(
-                              color: theme.colorScheme.primary,
-                            ),
-                          );
-                        },
-                        child: GestureDetector(
-                          onTap: widget.onTitleTap,
-                          child: Row(
-                            children: [
-                              titleIcon,
-                              Expanded(
-                                child: Text(
-                                  title,
-                                  style: _titleStyle ??
-                                      theme.textTheme.titleMedium,
-                                ),
-                              ),
-                            ],
-                          ),
-                        ),
-                      ),
-              ),
-              if (widget.controls != null) ...[
-                const SizedBox(width: denseSpacing),
-                for (final widget in widget.controls!) ...[
-                  widget,
-                  const SizedBox(width: denseSpacing),
-                ],
-              ],
-            ],
-          ),
-        );
-        return RoundedOutlinedBorder(
-          child: Column(
-            children: [
-              titleWidget,
-              widget.contentBuilder(context, current),
-            ],
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/shared/http/_http_exception.dart b/packages/devtools_app/lib/src/shared/http/_http_exception.dart
index 9dcb029..8cde884 100644
--- a/packages/devtools_app/lib/src/shared/http/_http_exception.dart
+++ b/packages/devtools_app/lib/src/shared/http/_http_exception.dart
@@ -11,9 +11,10 @@
 
   @override
   String toString() {
-    final b = StringBuffer()
-      ..write('HttpException: ')
-      ..write(message);
+    final b =
+        StringBuffer()
+          ..write('HttpException: ')
+          ..write(message);
     if (uri != null) {
       b.write(', uri = $uri');
     }
diff --git a/packages/devtools_app/lib/src/shared/http/curl_command.dart b/packages/devtools_app/lib/src/shared/http/curl_command.dart
index 1176ef3..af35257 100644
--- a/packages/devtools_app/lib/src/shared/http/curl_command.dart
+++ b/packages/devtools_app/lib/src/shared/http/curl_command.dart
@@ -29,9 +29,7 @@
     );
   }
 
-  CurlCommand._({
-    required this.commandParts,
-  });
+  CurlCommand._({required this.commandParts});
 
   static const _lineBreak = '\\\n';
 
diff --git a/packages/devtools_app/lib/src/shared/http/http_request_data.dart b/packages/devtools_app/lib/src/shared/http/http_request_data.dart
index 67bcc5b..18bb438 100644
--- a/packages/devtools_app/lib/src/shared/http/http_request_data.dart
+++ b/packages/devtools_app/lib/src/shared/http/http_request_data.dart
@@ -52,9 +52,9 @@
     Map<String, Object?>? responseContent,
   ) {
     return DartIOHttpRequestData(
-      HttpProfileRequestRef.parse(modifiedRequestData)!,
-      requestFullDataFromVmService: false,
-    )
+        HttpProfileRequestRef.parse(modifiedRequestData)!,
+        requestFullDataFromVmService: false,
+      )
       .._responseBody = responseContent?['text'].toString()
       .._requestBody = requestPostData?['text'].toString();
   }
@@ -73,9 +73,9 @@
       isFetchingFullData = true;
       final updated = await serviceConnection.serviceManager.service!
           .getHttpProfileRequestWrapper(
-        _request.isolateId,
-        _request.id.toString(),
-      );
+            _request.isolateId,
+            _request.id.toString(),
+          );
       _request = updated;
       final fullRequest = _request as HttpProfileRequest;
       _responseBody = utf8.decode(fullRequest.responseBody!);
@@ -211,9 +211,10 @@
       requestCookies.isNotEmpty || responseCookies.isNotEmpty;
 
   /// A list of all cookies contained within the request headers.
-  List<Cookie> get requestCookies => _hasError
-      ? []
-      : DartIOHttpRequestData._parseCookies(_request.request?.cookies);
+  List<Cookie> get requestCookies =>
+      _hasError
+          ? []
+          : DartIOHttpRequestData._parseCookies(_request.request?.cookies);
 
   /// A list of all cookies contained within the response headers.
   List<Cookie> get responseCookies =>
@@ -313,9 +314,10 @@
     DateTime lastTime = _request.startTime;
     for (final instant in instantEvents) {
       final instantTime = instant.timestamp;
-      instant._timeRange = TimeRange()
-        ..start = Duration(microseconds: lastTime.microsecondsSinceEpoch)
-        ..end = Duration(microseconds: instantTime.microsecondsSinceEpoch);
+      instant._timeRange =
+          TimeRange()
+            ..start = Duration(microseconds: lastTime.microsecondsSinceEpoch)
+            ..end = Duration(microseconds: instantTime.microsecondsSinceEpoch);
       lastTime = instantTime;
     }
   }
@@ -326,13 +328,6 @@
   }
 
   @override
-  int get hashCode => Object.hash(
-        id,
-        method,
-        uri,
-        contentType,
-        type,
-        port,
-        startTimestamp,
-      );
+  int get hashCode =>
+      Object.hash(id, method, uri, contentType, type, port, startTimestamp);
 }
diff --git a/packages/devtools_app/lib/src/shared/http/http_service.dart b/packages/devtools_app/lib/src/shared/http/http_service.dart
index a6f207b..89dbdf3 100644
--- a/packages/devtools_app/lib/src/shared/http/http_service.dart
+++ b/packages/devtools_app/lib/src/shared/http/http_service.dart
@@ -13,16 +13,14 @@
 
 /// Enables or disables HTTP logging for all isolates.
 Future<void> toggleHttpRequestLogging(bool state) async {
-  await serviceConnection.serviceManager.service!
-      .forEachIsolate((isolate) async {
+  await serviceConnection.serviceManager.service!.forEachIsolate((
+    isolate,
+  ) async {
     final httpLoggingAvailable = await serviceConnection.serviceManager.service!
         .isHttpTimelineLoggingAvailableWrapper(isolate.id!);
     if (httpLoggingAvailable) {
       final future = serviceConnection.serviceManager.service!
-          .httpEnableTimelineLoggingWrapper(
-        isolate.id!,
-        state,
-      );
+          .httpEnableTimelineLoggingWrapper(isolate.id!, state);
       // The above call won't complete immediately if the isolate is paused, so
       // give up waiting after 500ms. However, the call will complete eventually
       // if the isolate is eventually resumed.
@@ -35,8 +33,7 @@
 
 bool get httpLoggingEnabled => httpLoggingState.value.enabled;
 
-ValueListenable<ServiceExtensionState> get httpLoggingState =>
-    serviceConnection.serviceManager.serviceExtensionManager
-        .getServiceExtensionState(
-      extensions.httpEnableTimelineLogging.extension,
-    );
+ValueListenable<ServiceExtensionState> get httpLoggingState => serviceConnection
+    .serviceManager
+    .serviceExtensionManager
+    .getServiceExtensionState(extensions.httpEnableTimelineLogging.extension);
diff --git a/packages/devtools_app/lib/src/shared/banner_messages.dart b/packages/devtools_app/lib/src/shared/managers/banner_messages.dart
similarity index 85%
rename from packages/devtools_app/lib/src/shared/banner_messages.dart
rename to packages/devtools_app/lib/src/shared/managers/banner_messages.dart
index fca5df6..43d4565 100644
--- a/packages/devtools_app/lib/src/shared/banner_messages.dart
+++ b/packages/devtools_app/lib/src/shared/managers/banner_messages.dart
@@ -9,14 +9,14 @@
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 
-import '../screens/performance/performance_utils.dart';
-import 'analytics/constants.dart' as gac;
-import 'common_widgets.dart';
-import 'connected_app.dart';
-import 'globals.dart';
-import 'http/http_service.dart' as http_service;
-import 'primitives/utils.dart';
-import 'screen.dart';
+import '../../screens/performance/performance_utils.dart';
+import '../../service/connected_app/connected_app.dart';
+import '../analytics/constants.dart' as gac;
+import '../framework/screen.dart';
+import '../globals.dart';
+import '../http/http_service.dart' as http_service;
+import '../primitives/utils.dart';
+import '../ui/common_widgets.dart';
 
 const _runInProfileModeDocsUrl = 'https://flutter.dev/to/use-profile-mode';
 
@@ -94,10 +94,9 @@
 
   @visibleForTesting
   bool isMessageVisible(BannerMessage message) {
-    return _messagesForScreen(message.screenId)
-        .value
-        .where((m) => m.key == message.key)
-        .isNotEmpty;
+    return _messagesForScreen(
+      message.screenId,
+    ).value.where((m) => m.key == message.key).isNotEmpty;
   }
 
   ListValueNotifier<BannerMessage> _messagesForScreen(String screenId) {
@@ -126,14 +125,10 @@
         ValueListenableBuilder<List<BannerMessage>>(
           valueListenable: messagesForScreen,
           builder: (context, messages, _) {
-            return Column(
-              children: messages,
-            );
+            return Column(children: messages);
           },
         ),
-        Expanded(
-          child: screen.build(context),
-        ),
+        Expanded(child: screen.build(context)),
       ],
     );
   }
@@ -170,9 +165,10 @@
     final theme = Theme.of(context);
     final colorScheme = theme.colorScheme;
     return Card(
-      color: messageType == BannerMessageType.error
-          ? colorScheme.errorContainer
-          : colorScheme.warningContainer,
+      color:
+          messageType == BannerMessageType.error
+              ? colorScheme.errorContainer
+              : colorScheme.warningContainer,
       margin: const EdgeInsets.only(bottom: intermediateSpacing),
       child: Padding(
         padding: const EdgeInsets.symmetric(
@@ -193,18 +189,20 @@
                         ? Icons.error_outline
                         : Icons.warning_amber_outlined,
                     size: actionsIconSize,
-                    color: messageType == BannerMessageType.error
-                        ? colorScheme.onErrorContainer
-                        : colorScheme.onWarningContainer,
+                    color:
+                        messageType == BannerMessageType.error
+                            ? colorScheme.onErrorContainer
+                            : colorScheme.onWarningContainer,
                   ),
                 ),
                 Expanded(
                   child: RichText(
                     text: TextSpan(
                       style: theme.regularTextStyle.copyWith(
-                        color: messageType == BannerMessageType.error
-                            ? colorScheme.onErrorContainer
-                            : colorScheme.onWarningContainer,
+                        color:
+                            messageType == BannerMessageType.error
+                                ? colorScheme.onErrorContainer
+                                : colorScheme.onWarningContainer,
                       ),
                       children: textSpans,
                     ),
@@ -215,12 +213,13 @@
                   icon: Icon(
                     Icons.close,
                     size: actionsIconSize,
-                    color: messageType == BannerMessageType.error
-                        ? colorScheme.onErrorContainer
-                        : colorScheme.onWarningContainer,
+                    color:
+                        messageType == BannerMessageType.error
+                            ? colorScheme.onErrorContainer
+                            : colorScheme.onWarningContainer,
                   ),
-                  onPressed: () =>
-                      bannerMessages.removeMessage(this, dismiss: true),
+                  onPressed:
+                      () => bannerMessages.removeMessage(this, dismiss: true),
                 ),
               ],
             ),
@@ -236,9 +235,7 @@
     required Key super.key,
     required List<TextSpan> super.textSpans,
     required super.screenId,
-  }) : super(
-          messageType: BannerMessageType.error,
-        );
+  }) : super(messageType: BannerMessageType.error);
 }
 
 // TODO(kenz): add "Do not show this again" option to warnings.
@@ -274,9 +271,7 @@
           screenId: screenId,
           style: theme.warningMessageLinkStyle,
         ),
-        const TextSpan(
-          text: '.',
-        ),
+        const TextSpan(text: '.'),
       ],
       screenId: screenId,
     );
@@ -330,7 +325,8 @@
       key: Key('ShaderJankMessage - $screenId'),
       textSpans: [
         TextSpan(
-          text: 'Shader compilation jank detected. $jankyFramesCount '
+          text:
+              'Shader compilation jank detected. $jankyFramesCount '
               '${pluralize('frame', jankyFramesCount)} janked with a total of '
               '$jankDurationText spent in shader compilation. To pre-compile '
               'shaders, see the instructions at ',
@@ -349,7 +345,8 @@
         const TextSpan(text: '.'),
         if (serviceConnection.serviceManager.connectedApp!.isIosApp) ...[
           const TextSpan(
-            text: '\n\nNote: this is a legacy solution with many pitfalls. '
+            text:
+                '\n\nNote: this is a legacy solution with many pitfalls. '
                 'Try ',
           ),
           GaLinkTextSpan(
@@ -363,9 +360,7 @@
             context: context,
             style: theme.errorMessageLinkStyle,
           ),
-          const TextSpan(
-            text: ' instead!',
-          ),
+          const TextSpan(text: ' instead!'),
         ],
       ],
       screenId: screenId,
@@ -375,7 +370,7 @@
 
 class HighCpuSamplingRateMessage {
   HighCpuSamplingRateMessage(this.screenId)
-      : key = Key('HighCpuSamplingRateMessage - $screenId');
+    : key = Key('HighCpuSamplingRateMessage - $screenId');
 
   final Key key;
 
@@ -412,7 +407,7 @@
 
 class HttpLoggingEnabledMessage {
   HttpLoggingEnabledMessage(this.screenId)
-      : key = Key('HttpLoggingEnabledMessage - $screenId');
+    : key = Key('HttpLoggingEnabledMessage - $screenId');
 
   final Key key;
 
@@ -431,15 +426,16 @@
         TextSpan(
           text: 'disabling http logging',
           style: theme.warningMessageLinkStyle,
-          recognizer: TapGestureRecognizer()
-            ..onTap = () async {
-              await http_service.toggleHttpRequestLogging(false).then((_) {
-                if (!http_service.httpLoggingEnabled) {
-                  notificationService.push('Http logging disabled.');
-                  bannerMessages.removeMessage(message);
-                }
-              });
-            },
+          recognizer:
+              TapGestureRecognizer()
+                ..onTap = () async {
+                  await http_service.toggleHttpRequestLogging(false).then((_) {
+                    if (!http_service.httpLoggingEnabled) {
+                      notificationService.push('Http logging disabled.');
+                      bannerMessages.removeMessage(message);
+                    }
+                  });
+                },
         ),
         const TextSpan(
           text: ' before profiling the memory of your application.',
@@ -470,9 +466,7 @@
           screenId: screenId,
           style: Theme.of(context).warningMessageLinkStyle,
         ),
-        const TextSpan(
-          text: '.',
-        ),
+        const TextSpan(text: '.'),
       ],
       screenId: screenId,
     );
@@ -522,10 +516,7 @@
   }
 }
 
-void maybePushDebugModeMemoryMessage(
-  BuildContext context,
-  String screenId,
-) {
+void maybePushDebugModeMemoryMessage(BuildContext context, String screenId) {
   if (offlineDataController.showingOfflineData.value) return;
   if (serviceConnection.serviceManager.connectedApp?.isDebugFlutterAppNow ??
       false) {
@@ -533,10 +524,7 @@
   }
 }
 
-void maybePushHttpLoggingMessage(
-  BuildContext context,
-  String screenId,
-) {
+void maybePushHttpLoggingMessage(BuildContext context, String screenId) {
   if (http_service.httpLoggingEnabled) {
     bannerMessages.addMessage(
       HttpLoggingEnabledMessage(screenId).build(context),
@@ -555,14 +543,14 @@
 
 extension BannerMessageThemeExtension on ThemeData {
   TextStyle get warningMessageLinkStyle => regularTextStyle.copyWith(
-        decoration: TextDecoration.underline,
-        color: colorScheme.onWarningContainerLink,
-      );
+    decoration: TextDecoration.underline,
+    color: colorScheme.onWarningContainerLink,
+  );
 
   TextStyle get errorMessageLinkStyle => regularTextStyle.copyWith(
-        decoration: TextDecoration.underline,
-        color: colorScheme.onErrorContainerLink,
-      );
+    decoration: TextDecoration.underline,
+    color: colorScheme.onErrorContainerLink,
+  );
 }
 
 GaLinkTextSpan _runInProfileModeTextSpan(
diff --git a/packages/devtools_app/lib/src/shared/dtd_manager_extensions.dart b/packages/devtools_app/lib/src/shared/managers/dtd_manager_extensions.dart
similarity index 91%
rename from packages/devtools_app/lib/src/shared/dtd_manager_extensions.dart
rename to packages/devtools_app/lib/src/shared/managers/dtd_manager_extensions.dart
index 4060d4d..0a41055 100644
--- a/packages/devtools_app/lib/src/shared/dtd_manager_extensions.dart
+++ b/packages/devtools_app/lib/src/shared/managers/dtd_manager_extensions.dart
@@ -27,8 +27,9 @@
   Future<String> analyticsConsentMessage() async {
     if (!hasConnection) return '';
     try {
-      final response =
-          await _dtd.analyticsGetConsentMessage(ua.DashTool.devtools);
+      final response = await _dtd.analyticsGetConsentMessage(
+        ua.DashTool.devtools,
+      );
       _log.finer('DTDManager.analyticsConsentMessage success');
       return response.value!;
     } catch (e) {
@@ -42,8 +43,9 @@
   Future<bool> shouldShowAnalyticsConsentMessage() async {
     if (!hasConnection) return false;
     try {
-      final response =
-          await _dtd.analyticsShouldShowConsentMessage(ua.DashTool.devtools);
+      final response = await _dtd.analyticsShouldShowConsentMessage(
+        ua.DashTool.devtools,
+      );
       final shouldShow = response.value!;
       _log.finer(
         'DTDManager.shouldShowAnalyticsConsentMessage result: $shouldShow',
@@ -70,8 +72,9 @@
   Future<bool> analyticsTelemetryEnabled() async {
     if (!hasConnection) return false;
     try {
-      final response =
-          await _dtd.analyticsTelemetryEnabled(ua.DashTool.devtools);
+      final response = await _dtd.analyticsTelemetryEnabled(
+        ua.DashTool.devtools,
+      );
       final enabled = response.value!;
       _log.finer('DTDManager.analyticsTelemetryEnabled result: $enabled');
       return enabled;
diff --git a/packages/devtools_app/lib/src/shared/error_badge_manager.dart b/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
similarity index 80%
rename from packages/devtools_app/lib/src/shared/error_badge_manager.dart
rename to packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
index 9d71a82..24ede88 100644
--- a/packages/devtools_app/lib/src/shared/error_badge_manager.dart
+++ b/packages/devtools_app/lib/src/shared/managers/error_badge_manager.dart
@@ -11,16 +11,16 @@
 import 'package:flutter/foundation.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../screens/inspector_shared/inspector_screen.dart';
-import '../screens/logging/logging_screen.dart';
-import '../screens/network/network_screen.dart';
-import '../screens/performance/performance_screen.dart';
-import '../service/service_extensions.dart' as extensions;
-import '../service/vm_service_wrapper.dart';
-import 'diagnostics/diagnostics_node.dart';
-import 'globals.dart';
-import 'primitives/listenable.dart';
-import 'query_parameters.dart';
+import '../../screens/inspector_shared/inspector_screen.dart';
+import '../../screens/logging/logging_screen.dart';
+import '../../screens/network/network_screen.dart';
+import '../../screens/performance/performance_screen.dart';
+import '../../service/service_extensions.dart' as extensions;
+import '../../service/vm_service_wrapper.dart';
+import '../diagnostics/diagnostics_node.dart';
+import '../globals.dart';
+import '../primitives/listenable.dart';
+import '../primitives/query_parameters.dart';
 
 class ErrorBadgeManager extends DisposableController
     with AutoDisposeControllerMixin {
@@ -31,20 +31,20 @@
   };
   final _activeErrors =
       <String, ValueNotifier<LinkedHashMap<String, DevToolsError>>>{
-    InspectorScreen.id: ValueNotifier<LinkedHashMap<String, DevToolsError>>(
-      LinkedHashMap<String, DevToolsError>(),
-    ),
-  };
+        InspectorScreen.id: ValueNotifier<LinkedHashMap<String, DevToolsError>>(
+          LinkedHashMap<String, DevToolsError>(),
+        ),
+      };
 
   void vmServiceOpened(VmServiceWrapper service) {
     // Ensure structured errors are enabled.
     unawaited(
       serviceConnection.serviceManager.serviceExtensionManager
           .setServiceExtensionState(
-        extensions.structuredErrors.extension,
-        enabled: true,
-        value: true,
-      ),
+            extensions.structuredErrors.extension,
+            enabled: true,
+            value: true,
+          ),
     );
 
     // Log Flutter extension events.
@@ -73,11 +73,16 @@
   InspectableWidgetError? _extractInspectableError(Event error) {
     // TODO(dantup): Switch to using the inspectorService from the serviceManager
     //  once Jacob's change to add it lands.
-    final node =
-        RemoteDiagnosticsNode(error.extensionData!.data, null, false, null);
+    final node = RemoteDiagnosticsNode(
+      error.extensionData!.data,
+      null,
+      false,
+      null,
+    );
 
-    final errorSummaryNode =
-        node.inlineProperties.firstWhereOrNull((p) => p.type == 'ErrorSummary');
+    final errorSummaryNode = node.inlineProperties.firstWhereOrNull(
+      (p) => p.type == 'ErrorSummary',
+    );
     final errorMessage = errorSummaryNode?.description;
     if (errorMessage == null) {
       return null;
@@ -92,8 +97,9 @@
       return null;
     }
 
-    final queryParams =
-        DevToolsQueryParams.fromUrl(devToolsUrlNode.getStringMember('value')!);
+    final queryParams = DevToolsQueryParams.fromUrl(
+      devToolsUrlNode.getStringMember('value')!,
+    );
     final inspectorRef = queryParams.inspectorRef ?? '';
 
     return InspectableWidgetError(errorMessage, inspectorRef);
@@ -149,8 +155,9 @@
     if (errors == null) return;
 
     final oldCount = errors.value.length;
-    final newValue =
-        Map.fromEntries(errors.value.entries.where((e) => isValid(e.key)));
+    final newValue = Map.fromEntries(
+      errors.value.entries.where((e) => isValid(e.key)),
+    );
     if (newValue.length != oldCount) {
       errors.value = newValue as LinkedHashMap<String, DevToolsError>;
     }
diff --git a/packages/devtools_app/lib/src/shared/notifications.dart b/packages/devtools_app/lib/src/shared/managers/notifications.dart
similarity index 87%
rename from packages/devtools_app/lib/src/shared/notifications.dart
rename to packages/devtools_app/lib/src/shared/managers/notifications.dart
index 943e1b9..c1af009 100644
--- a/packages/devtools_app/lib/src/shared/notifications.dart
+++ b/packages/devtools_app/lib/src/shared/managers/notifications.dart
@@ -8,8 +8,8 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import 'globals.dart';
-import 'utils.dart';
+import '../globals.dart';
+import '../utils/utils.dart';
 
 class NotificationMessage {
   NotificationMessage(
@@ -48,16 +48,9 @@
   /// Includes a button to close the notification if [isDismissible] is true,
   /// otherwise the notification will be automatically dismissed after
   /// [NotificationMessage.defaultDuration].
-  bool push(
-    String message, {
-    bool isDismissible = false,
-  }) =>
-      pushNotification(
-        NotificationMessage(
-          message,
-          isDismissible: isDismissible,
-        ),
-      );
+  bool push(String message, {bool isDismissible = false}) => pushNotification(
+    NotificationMessage(message, isDismissible: isDismissible),
+  );
 
   /// Pushes an error notification with [errorMessage] as the text.
   ///
@@ -80,9 +73,10 @@
           if (reportExplanation != null) 'Explanation:\n$reportExplanation',
           if (stackTrace != null) 'Stack trace:\n$stackTrace',
         ];
-        final additionalInfo = additionalInfoParts.isNotEmpty
-            ? additionalInfoParts.join('\n\n')
-            : null;
+        final additionalInfo =
+            additionalInfoParts.isNotEmpty
+                ? additionalInfoParts.join('\n\n')
+                : null;
         unawaited(
           launchUrlWithErrorHandling(
             devToolsEnvironmentParameters
@@ -102,9 +96,10 @@
         isDismissible: isDismissible,
         actions: [if (isReportable) reportErrorAction],
         // Double the duration so that the user has time to report the error:
-        duration: isReportable
-            ? NotificationMessage.defaultDuration * 2
-            : NotificationMessage.defaultDuration,
+        duration:
+            isReportable
+                ? NotificationMessage.defaultDuration * 2
+                : NotificationMessage.defaultDuration,
       ),
       allowDuplicates: allowDuplicates,
     );
@@ -168,9 +163,10 @@
     final theme = Theme.of(context);
     return DevToolsButton(
       label: label,
-      color: isPrimary
-          ? theme.colorScheme.onPrimary
-          : theme.colorScheme.onSecondaryContainer,
+      color:
+          isPrimary
+              ? theme.colorScheme.onPrimary
+              : theme.colorScheme.onSecondaryContainer,
       elevated: isPrimary,
       outlined: !isPrimary,
       onPressed: onPressed,
diff --git a/packages/devtools_app/lib/src/shared/scripts/script_manager.dart b/packages/devtools_app/lib/src/shared/managers/script_manager.dart
similarity index 100%
rename from packages/devtools_app/lib/src/shared/scripts/script_manager.dart
rename to packages/devtools_app/lib/src/shared/managers/script_manager.dart
diff --git a/packages/devtools_app/lib/src/shared/survey.dart b/packages/devtools_app/lib/src/shared/managers/survey.dart
similarity index 86%
rename from packages/devtools_app/lib/src/shared/survey.dart
rename to packages/devtools_app/lib/src/shared/managers/survey.dart
index 3c3dde7..9fa2574 100644
--- a/packages/devtools_app/lib/src/shared/survey.dart
+++ b/packages/devtools_app/lib/src/shared/managers/survey.dart
@@ -10,13 +10,13 @@
 import 'package:http/http.dart';
 import 'package:logging/logging.dart';
 
-import '../shared/notifications.dart';
-import 'analytics/analytics.dart' as ga;
-import 'development_helpers.dart';
-import 'globals.dart';
-import 'primitives/utils.dart';
-import 'server/server.dart' as server;
-import 'utils.dart';
+import '../analytics/analytics.dart' as ga;
+import '../development_helpers.dart';
+import '../globals.dart';
+import '../primitives/utils.dart';
+import '../server/server.dart' as server;
+import '../utils/utils.dart';
+import 'notifications.dart';
 
 final _log = Logger('survey');
 
@@ -74,16 +74,15 @@
       final actions = [
         NotificationAction(
           label: _noThanksLabel,
-          onPressed: () => _noThanksPressed(
-            message: message,
-          ),
+          onPressed: () => _noThanksPressed(message: message),
         ),
         NotificationAction(
           label: _takeSurveyLabel,
-          onPressed: () => _takeSurveyPressed(
-            surveyUrl: _generateSurveyUrl(survey.url!),
-            message: message,
-          ),
+          onPressed:
+              () => _takeSurveyPressed(
+                surveyUrl: _generateSurveyUrl(survey.url!),
+                message: message,
+              ),
           isPrimary: true,
         ),
       ];
@@ -143,9 +142,7 @@
     return null;
   }
 
-  void _noThanksPressed({
-    required String message,
-  }) async {
+  void _noThanksPressed({required String message}) async {
     await server.setSurveyActionTaken();
     notificationService.dismiss(message);
   }
@@ -183,9 +180,10 @@
         endDateAsString != null ? DateTime.parse(endDateAsString) : null;
     final title = json[_titleKey] as String?;
     final surveyUrl = json[_urlKey] as String?;
-    final minDevToolsVersion = minVersionAsString != null
-        ? SemanticVersion.parse(minVersionAsString)
-        : null;
+    final minDevToolsVersion =
+        minVersionAsString != null
+            ? SemanticVersion.parse(minVersionAsString)
+            : null;
     final devEnvironments =
         (json[_devEnvironmentsKey] as List?)?.cast<String>().toList();
     return DevToolsSurvey._(
@@ -239,17 +237,19 @@
 }
 
 extension ShowSurveyExtension on DevToolsSurvey {
-  bool get meetsDateRequirement => (startDate == null || endDate == null)
-      ? false
-      : Range(
-          startDate!.millisecondsSinceEpoch,
-          endDate!.millisecondsSinceEpoch,
-        ).contains(clock.now().millisecondsSinceEpoch);
+  bool get meetsDateRequirement =>
+      (startDate == null || endDate == null)
+          ? false
+          : Range(
+            startDate!.millisecondsSinceEpoch,
+            endDate!.millisecondsSinceEpoch,
+          ).contains(clock.now().millisecondsSinceEpoch);
 
   bool get meetsMinVersionRequirement =>
       minDevToolsVersion == null ||
-      SemanticVersion.parse(devToolsVersion)
-          .isSupported(minSupportedVersion: minDevToolsVersion!);
+      SemanticVersion.parse(
+        devToolsVersion,
+      ).isSupported(minSupportedVersion: minDevToolsVersion!);
 
   bool get meetsEnvironmentRequirement =>
       devEnvironments == null || devEnvironments!.contains(ga.ideLaunched);
diff --git a/packages/devtools_app/lib/src/shared/memory/class_name.dart b/packages/devtools_app/lib/src/shared/memory/class_name.dart
index 8794f71..5374334 100644
--- a/packages/devtools_app/lib/src/shared/memory/class_name.dart
+++ b/packages/devtools_app/lib/src/shared/memory/class_name.dart
@@ -38,8 +38,7 @@
     alias: '\$project',
     aliasDescription: 'classes of the project',
     classTooltip: 'project class',
-  ),
-  ;
+  );
 
   const ClassType({
     required this.color,
@@ -83,7 +82,7 @@
 class HeapClassName with Serializable {
   @visibleForTesting
   HeapClassName({required String? library, required this.className})
-      : library = _normalizeLibrary(library);
+    : library = _normalizeLibrary(library);
 
   factory HeapClassName.fromJson(Map<String, dynamic> json) {
     return HeapClassName(
@@ -94,10 +93,7 @@
 
   @override
   Map<String, dynamic> toJson() {
-    return {
-      _Json.className: className,
-      _Json.library: library,
-    };
+    return {_Json.className: className, _Json.library: library};
   }
 
   static final _instances = <HeapClassName>{};
@@ -116,12 +112,9 @@
   }
 
   static HeapClassName fromClassRef(ClassRef? classRef) => fromPath(
-        library: _library(
-          classRef?.library?.name,
-          classRef?.library?.uri,
-        ),
-        className: classRef?.name ?? '',
-      );
+    library: _library(classRef?.library?.name, classRef?.library?.uri),
+    className: classRef?.name ?? '',
+  );
 
   static HeapClassName fromHeapSnapshotClass(HeapSnapshotClass? theClass) =>
       fromPath(
@@ -203,7 +196,8 @@
   /// `dart:` or `package:`.
   /// Examples of such classes: Code, Function, Class, Field,
   /// number_symbols/NumberSymbols, vector_math_64/Matrix4.
-  late final isPackageless = library.isEmpty ||
+  late final isPackageless =
+      library.isEmpty ||
       (!library.startsWith(PackagePrefixes.dart) &&
           !library.startsWith(PackagePrefixes.genericDartPackage));
 
@@ -240,9 +234,9 @@
 
   static String _normalizeLibrary(String? library) =>
       (library ?? '').trim().replaceFirst(
-            RegExp('^${PackagePrefixes.dartInSnapshot}'),
-            PackagePrefixes.dart,
-          );
+        RegExp('^${PackagePrefixes.dartInSnapshot}'),
+        PackagePrefixes.dart,
+      );
 
   bool matches(ClassRef ref) {
     return HeapClassName.fromClassRef(ref) == this;
diff --git a/packages/devtools_app/lib/src/shared/memory/classes.dart b/packages/devtools_app/lib/src/shared/memory/classes.dart
index ea5c04e..5dc0c49 100644
--- a/packages/devtools_app/lib/src/shared/memory/classes.dart
+++ b/packages/devtools_app/lib/src/shared/memory/classes.dart
@@ -19,10 +19,11 @@
     from ??= _empty;
     subtract ??= _empty;
 
-    final result = ObjectSetStats()
-      ..instanceCount = from.instanceCount - subtract.instanceCount
-      ..shallowSize = from.shallowSize - subtract.shallowSize
-      ..retainedSize = from.retainedSize - subtract.retainedSize;
+    final result =
+        ObjectSetStats()
+          ..instanceCount = from.instanceCount - subtract.instanceCount
+          ..shallowSize = from.shallowSize - subtract.shallowSize
+          ..retainedSize = from.retainedSize - subtract.retainedSize;
 
     if (result.isZero) return null;
     return result;
@@ -111,22 +112,21 @@
 }
 
 @immutable
-
 /// List of classes with filtering support.
 ///
 /// Is used to provide a list of classes to widgets.
 class ClassDataList<T extends ClassData> {
   const ClassDataList(this._originalList)
-      : _appliedFilter = null,
-        _filtered = null;
+    : _appliedFilter = null,
+      _filtered = null;
 
   const ClassDataList._filtered({
     required List<T> original,
     required ClassFilter appliedFilter,
     required List<T> filtered,
-  })  : _originalList = original,
-        _appliedFilter = appliedFilter,
-        _filtered = filtered;
+  }) : _originalList = original,
+       _appliedFilter = appliedFilter,
+       _filtered = filtered;
 
   /// The list of classes after filtering.
   List<T> get list => _filtered ?? _originalList;
@@ -135,8 +135,9 @@
   final ClassFilter? _appliedFilter;
   final List<T>? _filtered;
 
-  Map<HeapClassName, T> asMap() =>
-      {for (final c in _originalList) c.className: c};
+  Map<HeapClassName, T> asMap() => {
+    for (final c in _originalList) c.className: c,
+  };
 
   ClassDataList<T> filtered(ClassFilter newFilter, String? rootPackage) {
     final filtered = ClassFilter.filter(
@@ -193,7 +194,8 @@
 
   @override
   // ignore: avoid-explicit-type-declaration, required to override base class.
-  final ObjectSet objects = ObjectSet();
+  final ObjectSet
+  objects = ObjectSet();
 
   @override
   final byPath = <PathFromRoot, ObjectSetStats>{};
@@ -204,15 +206,17 @@
     required List<int>? retainers,
     required List<int>? retainedSizes,
   }) {
-    final path = retainers == null
-        ? null
-        : PathFromRoot.forObject(
-            graph,
-            shortestRetainers: retainers,
-            index: index,
-          );
+    final path =
+        retainers == null
+            ? null
+            : PathFromRoot.forObject(
+              graph,
+              shortestRetainers: retainers,
+              index: index,
+            );
 
-    final excludeFromRetained = path != null &&
+    final excludeFromRetained =
+        path != null &&
         retainedSizes != null &&
         path.classes.contains(className);
 
@@ -224,10 +228,7 @@
     );
 
     if (path != null) {
-      byPath.putIfAbsent(
-        path,
-        () => ObjectSetStats(),
-      );
+      byPath.putIfAbsent(path, () => ObjectSetStats());
       byPath[path]!.countInstance(
         graph,
         index,
diff --git a/packages/devtools_app/lib/src/shared/memory/gc_stats.dart b/packages/devtools_app/lib/src/shared/memory/gc_stats.dart
index 6de639e..80a83a2 100644
--- a/packages/devtools_app/lib/src/shared/memory/gc_stats.dart
+++ b/packages/devtools_app/lib/src/shared/memory/gc_stats.dart
@@ -47,12 +47,12 @@
 
   @override
   Map<String, dynamic> toJson() => {
-        _Json.heap: heap,
-        _Json.usage: usage,
-        _Json.capacity: capacity,
-        _Json.collections: collections,
-        _Json.averageCollectionTime: averageCollectionTime,
-      };
+    _Json.heap: heap,
+    _Json.usage: usage,
+    _Json.capacity: capacity,
+    _Json.collections: collections,
+    _Json.averageCollectionTime: averageCollectionTime,
+  };
 
   static const usedKey = 'used';
   static const capacityKey = 'capacity';
diff --git a/packages/devtools_app/lib/src/shared/memory/heap_data.dart b/packages/devtools_app/lib/src/shared/memory/heap_data.dart
index 0b10c3b..1f6fb63 100644
--- a/packages/devtools_app/lib/src/shared/memory/heap_data.dart
+++ b/packages/devtools_app/lib/src/shared/memory/heap_data.dart
@@ -93,8 +93,9 @@
           continue;
         }
 
-        final className =
-            HeapClassName.fromHeapSnapshotClass(graph.classes[object.classId]);
+        final className = HeapClassName.fromHeapSnapshotClass(
+          graph.classes[object.classId],
+        );
 
         // Ignore sentinels, because their size is not known.
         if (className.isSentinel) {
@@ -105,10 +106,7 @@
         reachableSize += object.shallowSize;
 
         nameToClass
-            .putIfAbsent(
-              className,
-              () => SingleClassData(className: className),
-            )
+            .putIfAbsent(className, () => SingleClassData(className: className))
             .countInstance(
               graph,
               index: i,
diff --git a/packages/devtools_app/lib/src/shared/memory/retainers.dart b/packages/devtools_app/lib/src/shared/memory/retainers.dart
index c6fbf7e..5b2b984 100644
--- a/packages/devtools_app/lib/src/shared/memory/retainers.dart
+++ b/packages/devtools_app/lib/src/shared/memory/retainers.dart
@@ -13,19 +13,20 @@
 /// Shallow size of the given object.
 typedef ShallowSize = int Function(int index);
 
-typedef ShortestRetainersResult = ({
-  /// Retainer for each object in the graph.
-  ///
-  /// When a value at index i is 0, it means the object at index i
-  /// has no retainers.
-  /// Null is not used for no-retainer to save memory footprint.
-  List<int> retainers,
+typedef ShortestRetainersResult =
+    ({
+      /// Retainer for each object in the graph.
+      ///
+      /// When a value at index i is 0, it means the object at index i
+      /// has no retainers.
+      /// Null is not used for no-retainer to save memory footprint.
+      List<int> retainers,
 
-  /// Retained size for each object in the graph.
-  ///
-  /// If an object is unreachable, its retained size is 0.
-  List<int>? retainedSizes,
-});
+      /// Retained size for each object in the graph.
+      ///
+      /// If an object is unreachable, its retained size is 0.
+      List<int>? retainedSizes,
+    });
 
 /// Index of the sentinel object.
 const _sentinelIndex = 0;
diff --git a/packages/devtools_app/lib/src/shared/memory/retaining_path.dart b/packages/devtools_app/lib/src/shared/memory/retaining_path.dart
index 9577c61..8949c0f 100644
--- a/packages/devtools_app/lib/src/shared/memory/retaining_path.dart
+++ b/packages/devtools_app/lib/src/shared/memory/retaining_path.dart
@@ -42,18 +42,18 @@
   PathFromRoot._(
     this.path, {
     @visibleForTesting bool debugOmitClassesInRetainingPath = false,
-  })  : assert(() {
-          debugUsage.constructed++;
-          return true;
-        }()),
-        hashCode = path.isEmpty ? _hashOfEmptyPath : Object.hashAll(path),
-        classes = debugOmitClassesInRetainingPath ? const {} : path.toSet();
+  }) : assert(() {
+         debugUsage.constructed++;
+         return true;
+       }()),
+       hashCode = path.isEmpty ? _hashOfEmptyPath : Object.hashAll(path),
+       classes = debugOmitClassesInRetainingPath ? const {} : path.toSet();
 
   /// For objects directly referenced from root.
   const PathFromRoot._empty()
-      : path = const [],
-        classes = const {},
-        hashCode = _hashOfEmptyPath;
+    : path = const [],
+      classes = const {},
+      hashCode = _hashOfEmptyPath;
 
   factory PathFromRoot.forObject(
     HeapSnapshotGraph graph, {
diff --git a/packages/devtools_app/lib/src/shared/memory/simple_items.dart b/packages/devtools_app/lib/src/shared/memory/simple_items.dart
index aa71141..6ea861c 100644
--- a/packages/devtools_app/lib/src/shared/memory/simple_items.dart
+++ b/packages/devtools_app/lib/src/shared/memory/simple_items.dart
@@ -8,16 +8,10 @@
 import 'package:vm_service/vm_service.dart';
 
 /// Direction of reference between objects in memory.
-enum RefDirection {
-  inbound,
-  outbound,
-}
+enum RefDirection { inbound, outbound }
 
 class MemoryFootprint {
-  MemoryFootprint({
-    required this.dart,
-    required this.reachable,
-  });
+  MemoryFootprint({required this.dart, required this.reachable});
 
   /// Reachable and unreachable total dart heap size.
   final int dart;
diff --git a/packages/devtools_app/lib/src/shared/offline_data.dart b/packages/devtools_app/lib/src/shared/offline/offline_data.dart
similarity index 96%
rename from packages/devtools_app/lib/src/shared/offline_data.dart
rename to packages/devtools_app/lib/src/shared/offline/offline_data.dart
index ddb20ec..ab51df5 100644
--- a/packages/devtools_app/lib/src/shared/offline_data.dart
+++ b/packages/devtools_app/lib/src/shared/offline/offline_data.dart
@@ -8,9 +8,9 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/foundation.dart';
 
-import 'config_specific/import_export/import_export.dart';
-import 'globals.dart';
-import 'routing.dart';
+import '../config_specific/import_export/import_export.dart';
+import '../framework/routing.dart';
+import '../globals.dart';
 
 /// Controller that manages offline mode for DevTools.
 ///
@@ -179,8 +179,9 @@
   /// Exports the current screen data to a .json file and downloads the file to
   /// the user's Downloads directory.
   void exportData() {
-    final encodedData =
-        _exportController.encode(prepareOfflineScreenData().toJson());
+    final encodedData = _exportController.encode(
+      prepareOfflineScreenData().toJson(),
+    );
     _exportController.downloadFile(encodedData);
   }
 
@@ -233,7 +234,7 @@
   final Map<String, Object?> data;
 
   Map<String, Object?> toJson() => {
-        DevToolsExportKeys.activeScreenId.name: screenId,
-        screenId: data,
-      };
+    DevToolsExportKeys.activeScreenId.name: screenId,
+    screenId: data,
+  };
 }
diff --git a/packages/devtools_app/lib/src/shared/offline_screen.dart b/packages/devtools_app/lib/src/shared/offline/offline_screen.dart
similarity index 94%
rename from packages/devtools_app/lib/src/shared/offline_screen.dart
rename to packages/devtools_app/lib/src/shared/offline/offline_screen.dart
index 8a4f0a7..a6235c9 100644
--- a/packages/devtools_app/lib/src/shared/offline_screen.dart
+++ b/packages/devtools_app/lib/src/shared/offline/offline_screen.dart
@@ -7,10 +7,10 @@
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
 
-import 'analytics/constants.dart' as gac;
-import 'common_widgets.dart';
-import 'globals.dart';
-import 'screen.dart';
+import '../analytics/constants.dart' as gac;
+import '../framework/screen.dart';
+import '../globals.dart';
+import '../ui/common_widgets.dart';
 
 /// The screen used for displaying offline data (data that does not require a
 /// connected app to explore).
diff --git a/packages/devtools_app/lib/src/shared/preferences/_cpu_profiler_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_cpu_profiler_preferences.dart
new file mode 100644
index 0000000..27de1de
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/preferences/_cpu_profiler_preferences.dart
@@ -0,0 +1,24 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of 'preferences.dart';
+
+class CpuProfilerPreferencesController extends DisposableController
+    with AutoDisposeControllerMixin {
+  /// The active filter tag for the cpu profiler screen.
+  ///
+  /// This value caches the most recent filter settings.
+  final filterTag = ValueNotifier<String>('');
+
+  @visibleForTesting
+  static const filterStorageId = 'cpuProfiler.filter';
+
+  Future<void> init() async {
+    filterTag.value = await storage.getValue(filterStorageId) ?? '';
+    addAutoDisposeListener(
+      filterTag,
+      () => storage.setValue(filterStorageId, filterTag.value),
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/shared/preferences/_extension_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_extension_preferences.dart
index 89c73cf..66ac283 100644
--- a/packages/devtools_app/lib/src/shared/preferences/_extension_preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/_extension_preferences.dart
@@ -13,20 +13,17 @@
       '${gac.DevToolsExtensionEvents.showOnlyEnabledExtensionsSetting.name}';
 
   Future<void> init() async {
-    addAutoDisposeListener(
-      showOnlyEnabledExtensions,
-      () {
-        storage.setValue(
-          _showOnlyEnabledExtensionsId,
-          showOnlyEnabledExtensions.value.toString(),
-        );
-        ga.select(
-          gac.DevToolsExtensionEvents.extensionScreenId.name,
-          gac.DevToolsExtensionEvents.showOnlyEnabledExtensionsSetting.name,
-          value: showOnlyEnabledExtensions.value ? 1 : 0,
-        );
-      },
-    );
+    addAutoDisposeListener(showOnlyEnabledExtensions, () {
+      storage.setValue(
+        _showOnlyEnabledExtensionsId,
+        showOnlyEnabledExtensions.value.toString(),
+      );
+      ga.select(
+        gac.DevToolsExtensionEvents.extensionScreenId.name,
+        gac.DevToolsExtensionEvents.showOnlyEnabledExtensionsSetting.name,
+        value: showOnlyEnabledExtensions.value ? 1 : 0,
+      );
+    });
     showOnlyEnabledExtensions.value = await boolValueFromStorage(
       _showOnlyEnabledExtensionsId,
       defaultsTo: false,
diff --git a/packages/devtools_app/lib/src/shared/preferences/_inspector_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_inspector_preferences.dart
index 3f9eb40..bbe89bf 100644
--- a/packages/devtools_app/lib/src/shared/preferences/_inspector_preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/_inspector_preferences.dart
@@ -9,7 +9,7 @@
   widgetDetailsTree(nameOverride: 'Widget Details Tree');
 
   const InspectorDetailsViewType({String? nameOverride})
-      : _nameOverride = nameOverride;
+    : _nameOverride = nameOverride;
 
   final String? _nameOverride;
 
@@ -20,6 +20,7 @@
     with AutoDisposeControllerMixin {
   ValueListenable<bool> get hoverEvalModeEnabled => _hoverEvalMode;
   ValueListenable<bool> get inspectorV2Enabled => _inspectorV2Enabled;
+  ValueListenable<bool> get autoRefreshEnabled => _autoRefreshEnabled;
   ValueListenable<InspectorDetailsViewType> get defaultDetailsView =>
       _defaultDetailsView;
   ListValueNotifier<String> get pubRootDirectories => _pubRootDirectories;
@@ -30,6 +31,9 @@
 
   final _hoverEvalMode = ValueNotifier<bool>(false);
   final _inspectorV2Enabled = ValueNotifier<bool>(false);
+  // TODO(https://github.com/flutter/devtools/issues/1423): Default to true
+  // after verifying auto-refreshes are performant.
+  final _autoRefreshEnabled = ValueNotifier<bool>(false);
   final _pubRootDirectories = ListValueNotifier<String>([]);
   final _pubRootDirectoriesAreBusy = ValueNotifier<bool>(false);
   final _busyCounter = ValueNotifier<int>(0);
@@ -39,6 +43,7 @@
 
   static const _hoverEvalModeStorageId = 'inspector.hoverEvalMode';
   static const _inspectorV2EnabledStorageId = 'inspector.inspectorV2Enabled';
+  static const _autoRefreshEnabledStorageId = 'inspector.autoRefreshEnabled';
   static const _defaultDetailsViewStorageId =
       'inspector.defaultDetailsViewType';
   static const _customPubRootDirectoriesStoragePrefix =
@@ -51,8 +56,10 @@
         (await serviceConnection.serviceManager.tryToDetectMainRootInfo())
             ?.library;
     final rootLibUri = Uri.parse(rootLibUriString ?? '');
-    final directorySegments =
-        rootLibUri.pathSegments.sublist(0, rootLibUri.pathSegments.length - 1);
+    final directorySegments = rootLibUri.pathSegments.sublist(
+      0,
+      rootLibUri.pathSegments.length - 1,
+    );
     final rootLibDirectory = rootLibUri.replace(
       pathSegments: directorySegments,
     );
@@ -62,6 +69,7 @@
   Future<void> init() async {
     await _initHoverEvalMode();
     await _initInspectorV2Enabled();
+    await _initAutoRefreshEnabled();
     // TODO(jacobr): consider initializing this first as it is not blocking.
     _initPubRootDirectories();
     await _initDefaultInspectorDetailsView();
@@ -83,6 +91,14 @@
     );
   }
 
+  Future<void> _initAutoRefreshEnabled() async {
+    await _updateAutoRefreshEnabled();
+    _saveBooleanPreferenceChanges(
+      preferenceStorageId: _autoRefreshEnabledStorageId,
+      preferenceNotifier: _autoRefreshEnabled,
+    );
+  }
+
   Future<void> _updateHoverEvalMode() async {
     await _updateBooleanPreference(
       preferenceStorageId: _hoverEvalModeStorageId,
@@ -99,6 +115,16 @@
     );
   }
 
+  Future<void> _updateAutoRefreshEnabled() async {
+    await _updateBooleanPreference(
+      preferenceStorageId: _autoRefreshEnabledStorageId,
+      preferenceNotifier: _autoRefreshEnabled,
+      // TODO(https://github.com/flutter/devtools/issues/1423): Default to true
+      // after verifying auto-refreshes are performant.
+      defaultValue: false,
+    );
+  }
+
   void _saveBooleanPreferenceChanges({
     required String preferenceStorageId,
     required ValueNotifier<bool> preferenceNotifier,
@@ -133,12 +159,14 @@
   }
 
   Future<void> _updateInspectorDetailsViewSelection() async {
-    final inspectorDetailsView =
-        await storage.getValue(_defaultDetailsViewStorageId);
+    final inspectorDetailsView = await storage.getValue(
+      _defaultDetailsViewStorageId,
+    );
 
     if (inspectorDetailsView != null) {
-      _defaultDetailsView.value = InspectorDetailsViewType.values
-          .firstWhere((e) => e.name.toString() == inspectorDetailsView);
+      _defaultDetailsView.value = InspectorDetailsViewType.values.firstWhere(
+        (e) => e.name.toString() == inspectorDetailsView,
+      );
     }
   }
 
@@ -220,8 +248,9 @@
 
   @visibleForTesting
   Future<List<String>> readCachedPubRootDirectories() async {
-    final cachedDirectoriesJson =
-        await storage.getValue(_customPubRootStorageId());
+    final cachedDirectoriesJson = await storage.getValue(
+      _customPubRootStorageId(),
+    );
     if (cachedDirectoriesJson == null) return <String>[];
     final cachedDirectories = List<String>.from(
       jsonDecode(cachedDirectoriesJson),
@@ -251,8 +280,9 @@
   /// directories are for the current project so we make a best guess based on
   /// the root library for the main isolate.
   Future<String?> _inferPubRootDirectory() async {
-    final fileUriString = await serviceConnection.serviceManager
-        .mainIsolateRootLibraryUriAsString();
+    final fileUriString =
+        await serviceConnection.serviceManager
+            .mainIsolateRootLibraryUriAsString();
     if (fileUriString == null) {
       return null;
     }
@@ -288,9 +318,10 @@
     pubRootDirectory ??= (parts..removeLast()).join('/');
     // Make sure the root directory ends with /, otherwise we will patch with
     // other directories that start the same.
-    pubRootDirectory = pubRootDirectory.endsWith('/')
-        ? pubRootDirectory
-        : '$pubRootDirectory/';
+    pubRootDirectory =
+        pubRootDirectory.endsWith('/')
+            ? pubRootDirectory
+            : '$pubRootDirectory/';
     return pubRootDirectory;
   }
 
@@ -307,25 +338,21 @@
     }
   }
 
-  Future<void> _cachePubRootDirectories(
-    List<String> pubRootDirectories,
-  ) async {
+  Future<void> _cachePubRootDirectories(List<String> pubRootDirectories) async {
     final cachedDirectories = await readCachedPubRootDirectories();
     await storage.setValue(
       _customPubRootStorageId(),
-      jsonEncode([
-        ...cachedDirectories,
-        ...pubRootDirectories,
-      ]),
+      jsonEncode([...cachedDirectories, ...pubRootDirectories]),
     );
   }
 
   Future<void> _uncachePubRootDirectories(
     List<String> pubRootDirectories,
   ) async {
-    final directoriesToCache = (await readCachedPubRootDirectories())
-        .where((dir) => !pubRootDirectories.contains(dir))
-        .toList();
+    final directoriesToCache =
+        (await readCachedPubRootDirectories())
+            .where((dir) => !pubRootDirectories.contains(dir))
+            .toList();
     await storage.setValue(
       _customPubRootStorageId(),
       jsonEncode(directoriesToCache),
@@ -358,9 +385,7 @@
     });
   }
 
-  Future<void> removePubRootDirectories(
-    List<String> pubRootDirectories,
-  ) async {
+  Future<void> removePubRootDirectories(List<String> pubRootDirectories) async {
     if (!serviceConnection.serviceManager.connectedState.value.connected) {
       return;
     }
@@ -421,6 +446,11 @@
     _inspectorV2Enabled.value = inspectorV2Enabled;
   }
 
+  @visibleForTesting
+  void setAutoRefreshEnabled(bool autoRefreshEnabled) {
+    _autoRefreshEnabled.value = autoRefreshEnabled;
+  }
+
   void setDefaultInspectorDetailsView(InspectorDetailsViewType value) {
     _defaultDetailsView.value = value;
   }
diff --git a/packages/devtools_app/lib/src/shared/preferences/_logging_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_logging_preferences.dart
index 1294c18..f451a10 100644
--- a/packages/devtools_app/lib/src/shared/preferences/_logging_preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/_logging_preferences.dart
@@ -11,29 +11,79 @@
   /// The number of logs to retain on the logging table.
   final retentionLimit = ValueNotifier<int>(_defaultRetentionLimit);
 
-  static const _defaultRetentionLimit = 3000;
+  /// The [LoggingDetailsFormat] to use when displaying a log in the log details
+  /// view.
+  final detailsFormat = ValueNotifier<LoggingDetailsFormat>(
+    _defaultDetailsFormat,
+  );
+
+  /// The active filter tag for the logging screen.
+  ///
+  /// This value caches the most recent filter settings.
+  final filterTag = ValueNotifier<String>('');
+
+  static const _defaultRetentionLimit = 5000;
+  static const _defaultDetailsFormat = LoggingDetailsFormat.text;
 
   static const _retentionLimitStorageId = 'logging.retentionLimit';
 
+  @visibleForTesting
+  static const detailsFormatStorageId = 'logging.detailsFormat';
+
+  @visibleForTesting
+  static const filterStorageId = 'logging.filter';
+
   Future<void> init() async {
     retentionLimit.value =
         int.tryParse(await storage.getValue(_retentionLimitStorageId) ?? '') ??
-            _defaultRetentionLimit;
+        _defaultRetentionLimit;
+    addAutoDisposeListener(retentionLimit, () {
+      storage.setValue(
+        _retentionLimitStorageId,
+        retentionLimit.value.toString(),
+      );
+      ga.select(
+        gac.logging,
+        gac.LoggingEvents.changeRetentionLimit.name,
+        value: retentionLimit.value,
+      );
+    });
 
-    addAutoDisposeListener(
-      retentionLimit,
-      () {
-        storage.setValue(
-          _retentionLimitStorageId,
-          retentionLimit.value.toString(),
-        );
-
-        ga.select(
-          gac.logging,
-          gac.LoggingEvents.changeRetentionLimit.name,
-          value: retentionLimit.value,
-        );
-      },
+    final detailsFormatValueFromStorage = await storage.getValue(
+      detailsFormatStorageId,
     );
+    detailsFormat.value =
+        LoggingDetailsFormat.values.firstWhereOrNull(
+          (value) => detailsFormatValueFromStorage == value.name,
+        ) ??
+        _defaultDetailsFormat;
+    addAutoDisposeListener(detailsFormat, () {
+      storage.setValue(detailsFormatStorageId, detailsFormat.value.name);
+      ga.select(
+        gac.logging,
+        gac.LoggingEvents.changeDetailsFormat.name,
+        value: detailsFormat.value.index,
+      );
+    });
+
+    filterTag.value = await storage.getValue(filterStorageId) ?? '';
+    addAutoDisposeListener(
+      filterTag,
+      () => storage.setValue(filterStorageId, filterTag.value),
+    );
+  }
+}
+
+enum LoggingDetailsFormat {
+  json,
+  text;
+
+  LoggingDetailsFormat opposite() {
+    switch (this) {
+      case LoggingDetailsFormat.json:
+        return LoggingDetailsFormat.text;
+      case LoggingDetailsFormat.text:
+        return LoggingDetailsFormat.json;
+    }
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/preferences/_memory_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_memory_preferences.dart
index 3449d8a..cbc0016 100644
--- a/packages/devtools_app/lib/src/shared/preferences/_memory_preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/_memory_preferences.dart
@@ -24,63 +24,42 @@
   static const _refLimitStorageId = 'memory.refLimit';
 
   Future<void> init() async {
-    addAutoDisposeListener(
-      androidCollectionEnabled,
-      () {
-        storage.setValue(
-          _androidCollectionEnabledStorageId,
-          androidCollectionEnabled.value.toString(),
-        );
-        if (androidCollectionEnabled.value) {
-          ga.select(
-            gac.memory,
-            gac.MemoryEvents.androidChart.name,
-          );
-        }
-      },
-    );
+    addAutoDisposeListener(androidCollectionEnabled, () {
+      storage.setValue(
+        _androidCollectionEnabledStorageId,
+        androidCollectionEnabled.value.toString(),
+      );
+      if (androidCollectionEnabled.value) {
+        ga.select(gac.memory, gac.MemoryEvents.androidChart.name);
+      }
+    });
     androidCollectionEnabled.value = await boolValueFromStorage(
       _androidCollectionEnabledStorageId,
       defaultsTo: false,
     );
 
-    addAutoDisposeListener(
-      showChart,
-      () {
-        storage.setValue(
-          _showChartStorageId,
-          showChart.value.toString(),
-        );
+    addAutoDisposeListener(showChart, () {
+      storage.setValue(_showChartStorageId, showChart.value.toString());
 
-        ga.select(
-          gac.memory,
-          showChart.value
-              ? gac.MemoryEvents.showChart.name
-              : gac.MemoryEvents.hideChart.name,
-        );
-      },
-    );
+      ga.select(
+        gac.memory,
+        showChart.value
+            ? gac.MemoryEvents.showChart.name
+            : gac.MemoryEvents.hideChart.name,
+      );
+    });
     showChart.value = await boolValueFromStorage(
       _showChartStorageId,
       defaultsTo: true,
     );
 
-    addAutoDisposeListener(
-      refLimit,
-      () {
-        storage.setValue(
-          _refLimitStorageId,
-          refLimit.value.toString(),
-        );
+    addAutoDisposeListener(refLimit, () {
+      storage.setValue(_refLimitStorageId, refLimit.value.toString());
 
-        ga.select(
-          gac.memory,
-          gac.MemoryEvents.browseRefLimit.name,
-        );
-      },
-    );
+      ga.select(gac.memory, gac.MemoryEvents.browseRefLimit.name);
+    });
     refLimit.value =
         int.tryParse(await storage.getValue(_refLimitStorageId) ?? '') ??
-            _defaultRefLimit;
+        _defaultRefLimit;
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/preferences/_network_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_network_preferences.dart
new file mode 100644
index 0000000..77b6447
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/preferences/_network_preferences.dart
@@ -0,0 +1,24 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of 'preferences.dart';
+
+class NetworkPreferencesController extends DisposableController
+    with AutoDisposeControllerMixin {
+  /// The active filter tag for the network screen.
+  ///
+  /// This value caches the most recent filter settings.
+  final filterTag = ValueNotifier<String>('');
+
+  @visibleForTesting
+  static const filterStorageId = 'network.filter';
+
+  Future<void> init() async {
+    filterTag.value = await storage.getValue(filterStorageId) ?? '';
+    addAutoDisposeListener(
+      filterTag,
+      () => storage.setValue(filterStorageId, filterTag.value),
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/shared/preferences/_performance_preferences.dart b/packages/devtools_app/lib/src/shared/preferences/_performance_preferences.dart
index 177f5c1..0e6df81 100644
--- a/packages/devtools_app/lib/src/shared/preferences/_performance_preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/_performance_preferences.dart
@@ -20,39 +20,33 @@
       '${gac.performance}.${gac.PerformanceEvents.includeCpuSamplesInTimeline.name}';
 
   Future<void> init() async {
-    addAutoDisposeListener(
-      showFlutterFramesChart,
-      () {
-        storage.setValue(
-          _showFlutterFramesChartId,
-          showFlutterFramesChart.value.toString(),
-        );
-        ga.select(
-          gac.performance,
-          gac.PerformanceEvents.framesChartVisibility.name,
-          value: showFlutterFramesChart.value ? 1 : 0,
-        );
-      },
-    );
+    addAutoDisposeListener(showFlutterFramesChart, () {
+      storage.setValue(
+        _showFlutterFramesChartId,
+        showFlutterFramesChart.value.toString(),
+      );
+      ga.select(
+        gac.performance,
+        gac.PerformanceEvents.framesChartVisibility.name,
+        value: showFlutterFramesChart.value ? 1 : 0,
+      );
+    });
     showFlutterFramesChart.value = await boolValueFromStorage(
       _showFlutterFramesChartId,
       defaultsTo: true,
     );
 
-    addAutoDisposeListener(
-      includeCpuSamplesInTimeline,
-      () {
-        storage.setValue(
-          _includeCpuSamplesInTimelineId,
-          includeCpuSamplesInTimeline.value.toString(),
-        );
-        ga.select(
-          gac.performance,
-          gac.PerformanceEvents.includeCpuSamplesInTimeline.name,
-          value: includeCpuSamplesInTimeline.value ? 1 : 0,
-        );
-      },
-    );
+    addAutoDisposeListener(includeCpuSamplesInTimeline, () {
+      storage.setValue(
+        _includeCpuSamplesInTimelineId,
+        includeCpuSamplesInTimeline.value.toString(),
+      );
+      ga.select(
+        gac.performance,
+        gac.PerformanceEvents.includeCpuSamplesInTimeline.name,
+        value: includeCpuSamplesInTimeline.value ? 1 : 0,
+      );
+    });
     includeCpuSamplesInTimeline.value = await boolValueFromStorage(
       _includeCpuSamplesInTimelineId,
       defaultsTo: false,
diff --git a/packages/devtools_app/lib/src/shared/preferences/preferences.dart b/packages/devtools_app/lib/src/shared/preferences/preferences.dart
index 165e4e0..c137955 100644
--- a/packages/devtools_app/lib/src/shared/preferences/preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/preferences.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 import 'dart:convert';
 
+import 'package:collection/collection.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/foundation.dart';
@@ -18,13 +19,15 @@
 import '../diagnostics/inspector_service.dart';
 import '../feature_flags.dart';
 import '../globals.dart';
-import '../query_parameters.dart';
-import '../utils.dart';
+import '../primitives/query_parameters.dart';
+import '../utils/utils.dart';
 
+part '_cpu_profiler_preferences.dart';
 part '_extension_preferences.dart';
 part '_inspector_preferences.dart';
 part '_memory_preferences.dart';
 part '_logging_preferences.dart';
+part '_network_preferences.dart';
 part '_performance_preferences.dart';
 
 final _log = Logger('PreferencesController');
@@ -53,9 +56,7 @@
 /// DevTools preferences for general settings.
 ///
 /// These values are not stored in the DevTools storage file with a prefix.
-enum _GeneralPreferences {
-  verboseLogging,
-}
+enum _GeneralPreferences { verboseLogging }
 
 /// A controller for global application preferences.
 class PreferencesController extends DisposableController
@@ -76,26 +77,33 @@
   /// dart2js + canvaskit
   final wasmEnabled = ValueNotifier<bool>(false);
 
-  final verboseLoggingEnabled =
-      ValueNotifier<bool>(Logger.root.level == verboseLoggingLevel);
+  final verboseLoggingEnabled = ValueNotifier<bool>(
+    Logger.root.level == verboseLoggingLevel,
+  );
+
+  CpuProfilerPreferencesController get cpuProfiler => _cpuProfiler;
+  final _cpuProfiler = CpuProfilerPreferencesController();
+
+  ExtensionsPreferencesController get devToolsExtensions => _extensions;
+  final _extensions = ExtensionsPreferencesController();
 
   // TODO(https://github.com/flutter/devtools/issues/7860): Clean-up after
   // Inspector V2 has been released.
   InspectorPreferencesController get inspector => _inspector;
   final _inspector = InspectorPreferencesController();
 
-  MemoryPreferencesController get memory => _memory;
-  final _memory = MemoryPreferencesController();
-
   LoggingPreferencesController get logging => _logging;
   final _logging = LoggingPreferencesController();
 
+  MemoryPreferencesController get memory => _memory;
+  final _memory = MemoryPreferencesController();
+
+  NetworkPreferencesController get network => _network;
+  final _network = NetworkPreferencesController();
+
   PerformancePreferencesController get performance => _performance;
   final _performance = PerformancePreferencesController();
 
-  ExtensionsPreferencesController get devToolsExtensions => _extensions;
-  final _extensions = ExtensionsPreferencesController();
-
   Future<void> init() async {
     // Get the current values and listen for and write back changes.
     await _initDarkMode();
@@ -105,19 +113,23 @@
     }
     await _initVerboseLogging();
 
-    await inspector.init();
-    await memory.init();
-    await logging.init();
-    await performance.init();
+    await cpuProfiler.init();
     await devToolsExtensions.init();
+    await inspector.init();
+    await logging.init();
+    await memory.init();
+    await network.init();
+    await performance.init();
 
     setGlobal(PreferencesController, this);
   }
 
   Future<void> _initDarkMode() async {
-    final darkModeValue =
-        await storage.getValue(_UiPreferences.darkMode.storageKey);
-    final useDarkMode = (darkModeValue == null && useDarkThemeAsDefault) ||
+    final darkModeValue = await storage.getValue(
+      _UiPreferences.darkMode.storageKey,
+    );
+    final useDarkMode =
+        (darkModeValue == null && useDarkThemeAsDefault) ||
         darkModeValue == 'true';
     ga.impression(gac.devToolsMain, gac.startingTheme(darkMode: useDarkMode));
     toggleDarkModeTheme(useDarkMode);
@@ -199,7 +211,8 @@
       return;
     }
 
-    final shouldEnableWasm = enabledFromStorage || enabledFromQueryParams;
+    final shouldEnableWasm =
+        (enabledFromStorage || enabledFromQueryParams) && kIsWeb;
     assert(kIsWasm == shouldEnableWasm);
     // This should be a no-op if the flutter_bootstrap.js logic set the
     // renderer properly, but we call this to be safe in case something went
@@ -223,11 +236,13 @@
 
   @override
   void dispose() {
-    inspector.dispose();
-    memory.dispose();
-    logging.dispose();
-    performance.dispose();
+    cpuProfiler.dispose();
     devToolsExtensions.dispose();
+    inspector.dispose();
+    logging.dispose();
+    memory.dispose();
+    network.dispose();
+    performance.dispose();
     super.dispose();
   }
 
diff --git a/packages/devtools_app/lib/src/shared/primitives/byte_utils.dart b/packages/devtools_app/lib/src/shared/primitives/byte_utils.dart
index 31a40bb..08dd4c5 100644
--- a/packages/devtools_app/lib/src/shared/primitives/byte_utils.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/byte_utils.dart
@@ -72,8 +72,10 @@
     // TODO(peterdjlee): Round up to the respective digit when fractionDigits > 0.
     bytes = fractionDigits == 0 ? bytes + 511 : bytes;
   }
-  final bytesDisplay =
-      convertBytes(bytes, to: unit).toStringAsFixed(fractionDigits);
+  final bytesDisplay = convertBytes(
+    bytes,
+    to: unit,
+  ).toStringAsFixed(fractionDigits);
   final unitSuffix = includeUnit ? ' ${unit.display}' : '';
   return '$bytesDisplay$unitSuffix';
 }
@@ -106,7 +108,7 @@
   gb(multiplierCount: 3);
 
   const ByteUnit({required this.multiplierCount, String? display})
-      : _display = display;
+    : _display = display;
 
   static const unitMultiplier = 1024.0;
 
diff --git a/packages/devtools_app/lib/src/shared/collapsible_mixin.dart b/packages/devtools_app/lib/src/shared/primitives/collapsible_mixin.dart
similarity index 95%
rename from packages/devtools_app/lib/src/shared/collapsible_mixin.dart
rename to packages/devtools_app/lib/src/shared/primitives/collapsible_mixin.dart
index 98074c1..db907bd 100644
--- a/packages/devtools_app/lib/src/shared/collapsible_mixin.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/collapsible_mixin.dart
@@ -40,8 +40,10 @@
     super.initState();
     expandController = defaultAnimationController(this);
     expandCurve = defaultCurvedAnimation(expandController);
-    expandArrowAnimation =
-        Tween<double>(begin: 0.75, end: 1.0).animate(expandCurve);
+    expandArrowAnimation = Tween<double>(
+      begin: 0.75,
+      end: 1.0,
+    ).animate(expandCurve);
     if (isExpanded) {
       expandController.value = 1.0;
     }
diff --git a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
index d730fbd..df01e12 100644
--- a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
@@ -35,8 +35,9 @@
     super.semanticChildCount,
     super.dragStartBehavior,
     this.customPointerSignalHandler,
-  }) : _primary = primary ??
-            controller == null && identical(scrollDirection, Axis.vertical);
+  }) : _primary =
+           primary ??
+           controller == null && identical(scrollDirection, Axis.vertical);
 
   final void Function(PointerSignalEvent event)? customPointerSignalHandler;
 
@@ -266,8 +267,9 @@
   }) {
     final List<Future<void>> futures = <Future<void>>[];
 
-    CustomPointerScrollableState? scrollable =
-        CustomPointerScrollable.of(context);
+    CustomPointerScrollableState? scrollable = CustomPointerScrollable.of(
+      context,
+    );
     while (scrollable != null) {
       futures.add(
         scrollable.position!.ensureVisible(
@@ -333,11 +335,11 @@
 
     _position =
         controller?.createScrollPosition(_physics!, this, oldPosition) ??
-            ScrollPositionWithSingleContext(
-              physics: _physics!,
-              context: this,
-              oldPosition: oldPosition,
-            );
+        ScrollPositionWithSingleContext(
+          physics: _physics!,
+          context: this,
+          oldPosition: oldPosition,
+        );
     assert(position != null);
     controller?.attach(position!);
   }
@@ -350,7 +352,8 @@
 
   @override
   void didChangeDependencies() {
-    _devicePixelRatio = MediaQuery.maybeDevicePixelRatioOf(context) ??
+    _devicePixelRatio =
+        MediaQuery.maybeDevicePixelRatioOf(context) ??
         View.of(context).devicePixelRatio;
     _updatePosition();
     super.didChangeDependencies();
@@ -427,42 +430,42 @@
         case Axis.vertical:
           _gestureRecognizers = <Type, GestureRecognizerFactory>{
             VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<
-                VerticalDragGestureRecognizer>(
-              () => VerticalDragGestureRecognizer(),
-              (VerticalDragGestureRecognizer instance) {
-                instance
-                  ..onDown = _handleDragDown
-                  ..onStart = _handleDragStart
-                  ..onUpdate = _handleDragUpdate
-                  ..onEnd = _handleDragEnd
-                  ..onCancel = _handleDragCancel
-                  ..minFlingDistance = _physics?.minFlingDistance
-                  ..minFlingVelocity = _physics?.minFlingVelocity
-                  ..maxFlingVelocity = _physics?.maxFlingVelocity
-                  ..dragStartBehavior = widget.dragStartBehavior;
-              },
-            ),
+              VerticalDragGestureRecognizer
+            >(() => VerticalDragGestureRecognizer(), (
+              VerticalDragGestureRecognizer instance,
+            ) {
+              instance
+                ..onDown = _handleDragDown
+                ..onStart = _handleDragStart
+                ..onUpdate = _handleDragUpdate
+                ..onEnd = _handleDragEnd
+                ..onCancel = _handleDragCancel
+                ..minFlingDistance = _physics?.minFlingDistance
+                ..minFlingVelocity = _physics?.minFlingVelocity
+                ..maxFlingVelocity = _physics?.maxFlingVelocity
+                ..dragStartBehavior = widget.dragStartBehavior;
+            }),
           };
           break;
         case Axis.horizontal:
           _gestureRecognizers = <Type, GestureRecognizerFactory>{
             HorizontalDragGestureRecognizer:
                 GestureRecognizerFactoryWithHandlers<
-                    HorizontalDragGestureRecognizer>(
-              () => HorizontalDragGestureRecognizer(),
-              (HorizontalDragGestureRecognizer instance) {
-                instance
-                  ..onDown = _handleDragDown
-                  ..onStart = _handleDragStart
-                  ..onUpdate = _handleDragUpdate
-                  ..onEnd = _handleDragEnd
-                  ..onCancel = _handleDragCancel
-                  ..minFlingDistance = _physics?.minFlingDistance
-                  ..minFlingVelocity = _physics?.minFlingVelocity
-                  ..maxFlingVelocity = _physics?.maxFlingVelocity
-                  ..dragStartBehavior = widget.dragStartBehavior;
-              },
-            ),
+                  HorizontalDragGestureRecognizer
+                >(() => HorizontalDragGestureRecognizer(), (
+                  HorizontalDragGestureRecognizer instance,
+                ) {
+                  instance
+                    ..onDown = _handleDragDown
+                    ..onStart = _handleDragStart
+                    ..onUpdate = _handleDragUpdate
+                    ..onEnd = _handleDragEnd
+                    ..onCancel = _handleDragCancel
+                    ..minFlingDistance = _physics?.minFlingDistance
+                    ..minFlingVelocity = _physics?.minFlingVelocity
+                    ..maxFlingVelocity = _physics?.maxFlingVelocity
+                    ..dragStartBehavior = widget.dragStartBehavior;
+                }),
           };
           break;
       }
@@ -470,8 +473,9 @@
     _lastCanDrag = canDrag;
     _lastAxisDirection = widget.axis;
     if (_gestureDetectorKey.currentState != null) {
-      _gestureDetectorKey.currentState!
-          .replaceGestureRecognizers(_gestureRecognizers);
+      _gestureDetectorKey.currentState!.replaceGestureRecognizers(
+        _gestureRecognizers,
+      );
     }
   }
 
@@ -484,8 +488,9 @@
     if (_shouldIgnorePointer == value) return;
     _shouldIgnorePointer = value;
     if (_ignorePointerKey.currentContext != null) {
-      final RenderIgnorePointer renderBox = _ignorePointerKey.currentContext!
-          .findRenderObject() as RenderIgnorePointer;
+      final RenderIgnorePointer renderBox =
+          _ignorePointerKey.currentContext!.findRenderObject()
+              as RenderIgnorePointer;
       renderBox.ignoring = _shouldIgnorePointer;
     }
   }
@@ -553,9 +558,10 @@
   // Returns the offset that should result from applying [event] to the current
   // position, taking min/max scroll extent into account.
   double _targetScrollOffsetForPointerScroll(PointerScrollEvent event) {
-    double delta = widget.axis == Axis.horizontal
-        ? event.scrollDelta.dx
-        : event.scrollDelta.dy;
+    double delta =
+        widget.axis == Axis.horizontal
+            ? event.scrollDelta.dx
+            : event.scrollDelta.dy;
 
     if (axisDirectionIsReversed(widget.axisDirection)) {
       delta *= -1;
@@ -569,12 +575,15 @@
 
   void _receivedPointerSignal(PointerSignalEvent event) {
     if (event is PointerScrollEvent && position != null) {
-      final double targetScrollOffset =
-          _targetScrollOffsetForPointerScroll(event);
+      final double targetScrollOffset = _targetScrollOffsetForPointerScroll(
+        event,
+      );
       // Only express interest in the event if it would actually result in a scroll.
       if (targetScrollOffset != position!.pixels) {
-        GestureBinding.instance.pointerSignalResolver
-            .register(event, _handlePointerScroll);
+        GestureBinding.instance.pointerSignalResolver.register(
+          event,
+          _handlePointerScroll,
+        );
       }
     }
   }
@@ -584,8 +593,9 @@
     if (_physics != null && !_physics!.shouldAcceptUserOffset(position!)) {
       return;
     }
-    final double targetScrollOffset =
-        _targetScrollOffsetForPointerScroll(event as PointerScrollEvent);
+    final double targetScrollOffset = _targetScrollOffsetForPointerScroll(
+      event as PointerScrollEvent,
+    );
     if (targetScrollOffset != position!.pixels) {
       position!.jumpTo(targetScrollOffset);
     }
@@ -633,7 +643,8 @@
       result = _ScrollSemantics(
         key: _scrollSemanticsKey,
         position: position!,
-        allowImplicitScrolling: widget.physics?.allowImplicitScrolling ??
+        allowImplicitScrolling:
+            widget.physics?.allowImplicitScrolling ??
             _physics!.allowImplicitScrolling,
         semanticChildCount: widget.semanticChildCount,
         child: result,
@@ -736,10 +747,10 @@
     required bool allowImplicitScrolling,
     required int? semanticChildCount,
     RenderBox? child,
-  })  : _position = position,
-        _allowImplicitScrolling = allowImplicitScrolling,
-        _semanticChildCount = semanticChildCount,
-        super(child) {
+  }) : _position = position,
+       _allowImplicitScrolling = allowImplicitScrolling,
+       _semanticChildCount = semanticChildCount,
+       super(child) {
     position.addListener(markNeedsSemanticsUpdate);
   }
 
@@ -817,8 +828,10 @@
     }
     config.scrollIndex = firstVisibleIndex;
     node.updateWith(config: null, childrenInInversePaintOrder: excluded);
-    _innerNode!
-        .updateWith(config: config, childrenInInversePaintOrder: included);
+    _innerNode!.updateWith(
+      config: config,
+      childrenInInversePaintOrder: included,
+    );
   }
 
   @override
diff --git a/packages/devtools_app/lib/src/shared/primitives/diagnostics_text_styles.dart b/packages/devtools_app/lib/src/shared/primitives/diagnostics_text_styles.dart
new file mode 100644
index 0000000..9031d3c
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/primitives/diagnostics_text_styles.dart
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+
+// Enum-like static classes are ok.
+// ignore: avoid_classes_with_only_static_members
+class DiagnosticsTextStyles {
+  static TextStyle unimportant(ColorScheme colorScheme) => TextStyle(
+    color: colorScheme.isLight ? Colors.grey.shade500 : Colors.grey.shade600,
+  );
+
+  static TextStyle regular(ColorScheme colorScheme) => TextStyle(
+    // The font size when not specified seems to be 14, but specify here since we
+    // are scaling based on this font size in [IdeTheme].
+    fontSize: defaultFontSize,
+    color: colorScheme.onSurface,
+  );
+
+  static TextStyle warning(ColorScheme colorScheme) => TextStyle(
+    color:
+        colorScheme.isLight ? Colors.orange.shade900 : Colors.orange.shade400,
+  );
+
+  static TextStyle error(ColorScheme colorScheme) =>
+      TextStyle(color: colorScheme.error);
+
+  static TextStyle link(ColorScheme colorScheme) => TextStyle(
+    color: colorScheme.isLight ? Colors.blue.shade700 : Colors.blue.shade300,
+    decoration: TextDecoration.underline,
+  );
+
+  static const regularBold = TextStyle(fontWeight: FontWeight.w700);
+
+  static TextStyle textStyleForLevel(
+    DiagnosticLevel level,
+    ColorScheme colorScheme,
+  ) {
+    switch (level) {
+      case DiagnosticLevel.hidden:
+        return unimportant(colorScheme);
+      case DiagnosticLevel.warning:
+        return warning(colorScheme);
+      case DiagnosticLevel.error:
+        return error(colorScheme);
+      case DiagnosticLevel.debug:
+      case DiagnosticLevel.info:
+      case DiagnosticLevel.fine:
+      default:
+        return regular(colorScheme);
+    }
+  }
+}
diff --git a/packages/devtools_app/lib/src/shared/primitives/encoding.dart b/packages/devtools_app/lib/src/shared/primitives/encoding.dart
index 259fc59..52d077b 100644
--- a/packages/devtools_app/lib/src/shared/primitives/encoding.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/encoding.dart
@@ -124,12 +124,13 @@
 Object? toEncodable(Object? value) {
   return switch (value) {
     null => null,
-    final HeapSnapshotGraph value =>
-      HeapSnapshotGraphEncodeDecode.instance.toEncodable(value),
+    final HeapSnapshotGraph value => HeapSnapshotGraphEncodeDecode.instance
+        .toEncodable(value),
     final ByteData value => ByteDataEncodeDecode.instance.toEncodable(value),
     final DateTime value => DateTimeEncodeDecode.instance.toEncodable(value),
-    final IsolateRef value =>
-      IsolateRefEncodeDecode.instance.toEncodable(value),
+    final IsolateRef value => IsolateRefEncodeDecode.instance.toEncodable(
+      value,
+    ),
     final ClassRef value => ClassRefEncodeDecode.instance.toEncodable(value),
     final Serializable value => value.toJson(),
     _ =>
diff --git a/packages/devtools_app/lib/src/shared/primitives/extent_delegate_list.dart b/packages/devtools_app/lib/src/shared/primitives/extent_delegate_list.dart
index ee40bc6..5a6859d 100644
--- a/packages/devtools_app/lib/src/shared/primitives/extent_delegate_list.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/extent_delegate_list.dart
@@ -130,8 +130,8 @@
   FixedExtentDelegate({
     required double Function(int index) computeExtent,
     required int Function() computeLength,
-  })  : _computeExtent = computeExtent,
-        _computeLength = computeLength {
+  }) : _computeExtent = computeExtent,
+       _computeLength = computeLength {
     recompute();
   }
 
@@ -331,11 +331,15 @@
     assert(remainingExtent >= 0.0);
     final targetEndScrollOffset = scrollOffset + remainingExtent;
 
-    final firstIndex =
-        _extentDelegate!.minChildIndexForScrollOffset(scrollOffset);
-    final targetLastIndex = targetEndScrollOffset.isFinite
-        ? _extentDelegate!.maxChildIndexForScrollOffset(targetEndScrollOffset)
-        : null;
+    final firstIndex = _extentDelegate!.minChildIndexForScrollOffset(
+      scrollOffset,
+    );
+    final targetLastIndex =
+        targetEndScrollOffset.isFinite
+            ? _extentDelegate!.maxChildIndexForScrollOffset(
+              targetEndScrollOffset,
+            )
+            : null;
 
     if (firstChild != null) {
       final leadingGarbage = _calculateLeadingGarbage(firstIndex);
@@ -391,11 +395,14 @@
       trailingChildWithLayout = firstChild;
     }
 
-    double estimatedMaxScrollOffset =
-        _extentDelegate!.layoutOffset(_extentDelegate!.length);
-    for (int index = indexOf(trailingChildWithLayout!) + 1;
-        targetLastIndex == null || index <= targetLastIndex;
-        ++index) {
+    double estimatedMaxScrollOffset = _extentDelegate!.layoutOffset(
+      _extentDelegate!.length,
+    );
+    for (
+      int index = indexOf(trailingChildWithLayout!) + 1;
+      targetLastIndex == null || index <= targetLastIndex;
+      ++index
+    ) {
       RenderBox? child = childAfter(trailingChildWithLayout!);
       if (child == null || indexOf(child) != index) {
         child = insertAndLayoutChild(
@@ -414,8 +421,9 @@
       final childParentData =
           child.parentData as SliverMultiBoxAdaptorParentData;
       assert(childParentData.index == index);
-      childParentData.layoutOffset =
-          _extentDelegate!.layoutOffset(childParentData.index);
+      childParentData.layoutOffset = _extentDelegate!.layoutOffset(
+        childParentData.index,
+      );
     }
 
     final lastIndex = indexOf(lastChild!);
@@ -456,10 +464,12 @@
 
     final targetEndScrollOffsetForPaint =
         constraints.scrollOffset + constraints.remainingPaintExtent;
-    final targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite
-        ? _extentDelegate!
-            .maxChildIndexForScrollOffset(targetEndScrollOffsetForPaint)
-        : null;
+    final targetLastIndexForPaint =
+        targetEndScrollOffsetForPaint.isFinite
+            ? _extentDelegate!.maxChildIndexForScrollOffset(
+              targetEndScrollOffsetForPaint,
+            )
+            : null;
     assert(paintExtent <= estimatedMaxScrollOffset);
     geometry = SliverGeometry(
       scrollExtent: _extentDelegate!.layoutOffset(_extentDelegate!.length),
@@ -467,7 +477,8 @@
       cacheExtent: cacheExtent,
       maxPaintExtent: estimatedMaxScrollOffset,
       // Conservative to avoid flickering away the clip during scroll.
-      hasVisualOverflow: (targetLastIndexForPaint != null &&
+      hasVisualOverflow:
+          (targetLastIndexForPaint != null &&
               lastIndex >= targetLastIndexForPaint) ||
           constraints.scrollOffset > 0.0,
     );
diff --git a/packages/devtools_app/lib/src/shared/primitives/flutter_widgets/linked_scroll_controller.dart b/packages/devtools_app/lib/src/shared/primitives/flutter_widgets/linked_scroll_controller.dart
index 8b1e719..7452004 100644
--- a/packages/devtools_app/lib/src/shared/primitives/flutter_widgets/linked_scroll_controller.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/flutter_widgets/linked_scroll_controller.dart
@@ -54,11 +54,14 @@
 
   /// Creates a new controller that is linked to any existing ones.
   ScrollController addAndGet() {
-    final initialScrollOffset = _attachedControllers.isEmpty
-        ? 0.0
-        : _attachedControllers.first.position.pixels;
-    final controller =
-        _LinkedScrollController(this, initialScrollOffset: initialScrollOffset);
+    final initialScrollOffset =
+        _attachedControllers.isEmpty
+            ? 0.0
+            : _attachedControllers.first.position.pixels;
+    final controller = _LinkedScrollController(
+      this,
+      initialScrollOffset: initialScrollOffset,
+    );
     _allControllers.add(controller);
     controller.addListener(_offsetNotifier.notifyListeners);
     return controller;
diff --git a/packages/devtools_app/lib/src/utils/list_queue_value_notifier.dart b/packages/devtools_app/lib/src/shared/primitives/list_queue_value_notifier.dart
similarity index 100%
rename from packages/devtools_app/lib/src/utils/list_queue_value_notifier.dart
rename to packages/devtools_app/lib/src/shared/primitives/list_queue_value_notifier.dart
diff --git a/packages/devtools_app/lib/src/shared/query_parameters.dart b/packages/devtools_app/lib/src/shared/primitives/query_parameters.dart
similarity index 100%
rename from packages/devtools_app/lib/src/shared/query_parameters.dart
rename to packages/devtools_app/lib/src/shared/primitives/query_parameters.dart
diff --git a/packages/devtools_app/lib/src/shared/primitives/trace_event.dart b/packages/devtools_app/lib/src/shared/primitives/trace_event.dart
index fd975e1..c5f76c6 100644
--- a/packages/devtools_app/lib/src/shared/primitives/trace_event.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/trace_event.dart
@@ -9,14 +9,14 @@
 class ChromeTraceEvent {
   /// Creates a timeline event given JSON-encoded event data.
   ChromeTraceEvent(this.json)
-      : name = json[nameKey] as String?,
-        category = json[categoryKey] as String?,
-        phase = json[phaseKey] as String?,
-        processId = json[processIdKey] as int?,
-        threadId = json[threadIdKey] as int?,
-        duration = json[durationKey] as int?,
-        timestampMicros = json[timestampKey] as int?,
-        args = json[argsKey] as Map<String, Object?>?;
+    : name = json[nameKey] as String?,
+      category = json[categoryKey] as String?,
+      phase = json[phaseKey] as String?,
+      processId = json[processIdKey] as int?,
+      threadId = json[threadIdKey] as int?,
+      duration = json[durationKey] as int?,
+      timestampMicros = json[timestampKey] as int?,
+      args = json[argsKey] as Map<String, Object?>?;
 
   static const nameKey = 'name';
   static const categoryKey = 'cat';
@@ -120,6 +120,7 @@
   }
 
   @override
-  String toString() => '[$idKey: $id] [$phaseKey: $phase] '
+  String toString() =>
+      '[$idKey: $id] [$phaseKey: $phase] '
       '$name - [$timestampKey: $timestampMicros] [$durationKey: $duration]';
 }
diff --git a/packages/devtools_app/lib/src/shared/primitives/trees.dart b/packages/devtools_app/lib/src/shared/primitives/trees.dart
index 82ad74c..574c433 100644
--- a/packages/devtools_app/lib/src/shared/primitives/trees.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/trees.dart
@@ -201,10 +201,7 @@
   }
 
   T? firstChildWithCondition(bool Function(T node) condition) {
-    return breadthFirstTraversal<T>(
-      this as T,
-      returnCondition: condition,
-    );
+    return breadthFirstTraversal<T>(this as T, returnCondition: condition);
   }
 
   /// Locates the first sub-node in the tree at level [level].
@@ -228,8 +225,9 @@
       // When this condition is called, we have already ensured that
       // [level] < [depth], so at least one child is guaranteed to meet the
       // firstWhere condition.
-      (currentNode, levelWithOffset) => currentNode.children
-          .firstWhere((n) => n.depth + n.level > levelWithOffset),
+      (currentNode, levelWithOffset) => currentNode.children.firstWhere(
+        (n) => n.depth + n.level > levelWithOffset,
+      ),
     );
   }
 
@@ -254,8 +252,9 @@
       // When this condition is called, we have already ensured that
       // [level] < [depth], so at least one child is guaranteed to meet the
       // lastWhere condition.
-      (currentNode, levelWithOffset) => currentNode.children
-          .lastWhere((n) => n.depth + n.level > levelWithOffset),
+      (currentNode, levelWithOffset) => currentNode.children.lastWhere(
+        (n) => n.depth + n.level > levelWithOffset,
+      ),
     );
   }
 
@@ -337,11 +336,7 @@
   bool Function(T node)? returnCondition,
   void Function(T node)? action,
 }) {
-  return _treeTraversal(
-    root,
-    returnCondition: returnCondition,
-    action: action,
-  );
+  return _treeTraversal(root, returnCondition: returnCondition, action: action);
 }
 
 /// Traverses a tree in depth-first preorder order.
@@ -409,10 +404,7 @@
   return flatList;
 }
 
-void _traverse<T extends TreeNode<T>>(
-  T node,
-  bool Function(T) callback,
-) {
+void _traverse<T extends TreeNode<T>>(T node, bool Function(T) callback) {
   final shouldContinue = callback(node);
   if (shouldContinue) {
     for (final child in node.children) {
diff --git a/packages/devtools_app/lib/src/shared/primitives/utils.dart b/packages/devtools_app/lib/src/shared/primitives/utils.dart
index 1ff4f60..ff47546 100644
--- a/packages/devtools_app/lib/src/shared/primitives/utils.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/utils.dart
@@ -168,7 +168,7 @@
 /// Use in long calculations, to release UI thread after each N steps.
 class UiReleaser {
   UiReleaser({this.stepsBetweenDelays = 100000, this.delayLength = 0})
-      : assert(stepsBetweenDelays > 0);
+    : assert(stepsBetweenDelays > 0);
 
   final int stepsBetweenDelays;
   final int delayLength;
@@ -365,7 +365,7 @@
 /// specified rate and that at most one async [callback] is running at a time.
 class RateLimiter {
   RateLimiter(double requestsPerSecond, this.callback)
-      : delayBetweenRequests = 1000 ~/ requestsPerSecond;
+    : delayBetweenRequests = 1000 ~/ requestsPerSecond;
 
   final RateLimiterCallback callback;
   Completer<void>? _pendingRequest;
@@ -446,10 +446,7 @@
 /// remaining time units supported by [Duration] - (seconds, minutes, etc.). If
 /// you add a unit of time to this enum, modify the toString() method in
 /// [TimeRange] to handle the new case.
-enum TimeUnit {
-  microseconds,
-  milliseconds,
-}
+enum TimeUnit { microseconds, milliseconds }
 
 class TimeRange {
   TimeRange({this.singleAssignment = true});
@@ -675,10 +672,7 @@
   int get hashCode => Object.hash(begin, end);
 }
 
-enum SortDirection {
-  ascending,
-  descending,
-}
+enum SortDirection { ascending, descending }
 
 /// A Range-like class that works for inclusive ranges of lines in source code.
 class LineRange {
@@ -827,17 +821,21 @@
       .map(
         (entry) => TextSpan(
           text: entry.text,
-          style: entry.style.isEmpty
-              ? defaultStyle
-              : TextStyle(
-                  color: entry.fgColor != null
-                      ? colorFromAnsi(entry.fgColor!)
-                      : null,
-                  backgroundColor: entry.bgColor != null
-                      ? colorFromAnsi(entry.bgColor!)
-                      : null,
-                  fontWeight: entry.bold ? FontWeight.bold : FontWeight.normal,
-                ),
+          style:
+              entry.style.isEmpty
+                  ? defaultStyle
+                  : TextStyle(
+                    color:
+                        entry.fgColor != null
+                            ? colorFromAnsi(entry.fgColor!)
+                            : null,
+                    backgroundColor:
+                        entry.bgColor != null
+                            ? colorFromAnsi(entry.bgColor!)
+                            : null,
+                    fontWeight:
+                        entry.bold ? FontWeight.bold : FontWeight.normal,
+                  ),
         ),
       )
       .toList();
@@ -869,17 +867,17 @@
   String describeKeys({bool isMacOS = false}) {
     // Put the modifiers first. If it has a synonym, then it's something like
     // shiftLeft, altRight, etc.
-    final sortedKeys = keys.toList()
-      ..sort((a, b) {
-        final aIsModifier = a.synonyms.isNotEmpty || _modifiers.contains(a);
-        final bIsModifier = b.synonyms.isNotEmpty || _modifiers.contains(b);
-        if (aIsModifier && !bIsModifier) {
-          return -1;
-        } else if (bIsModifier && !aIsModifier) {
-          return 1;
-        }
-        return a.keyLabel.compareTo(b.keyLabel);
-      });
+    final sortedKeys =
+        keys.toList()..sort((a, b) {
+          final aIsModifier = a.synonyms.isNotEmpty || _modifiers.contains(a);
+          final bIsModifier = b.synonyms.isNotEmpty || _modifiers.contains(b);
+          if (aIsModifier && !bIsModifier) {
+            return -1;
+          } else if (bIsModifier && !aIsModifier) {
+            return 1;
+          }
+          return a.keyLabel.compareTo(b.keyLabel);
+        });
 
     return sortedKeys.map((key) {
       if (_modifiers.contains(key)) {
@@ -939,8 +937,10 @@
       return contains(pattern);
     } else if (pattern is String) {
       final lowerCase = _lowercaseLookup.putIfAbsent(this, () => toLowerCase());
-      final strLowerCase =
-          _lowercaseLookup.putIfAbsent(pattern, () => pattern.toLowerCase());
+      final strLowerCase = _lowercaseLookup.putIfAbsent(
+        pattern,
+        () => pattern.toLowerCase(),
+      );
       return lowerCase.contains(strLowerCase);
     }
     throw Exception(
@@ -1052,10 +1052,7 @@
 /// @param isUTC - if true for testing, the UTC locale is used (instead of
 /// the user's locale). Tests will then pass when run in any timezone. All
 /// formatted timestamps are displayed using the UTC locale.
-String prettyTimestamp(
-  int? timestamp, {
-  bool isUtc = false,
-}) {
+String prettyTimestamp(int? timestamp, {bool isUtc = false}) {
   if (timestamp == null) return '';
   final timestampDT = DateTime.fromMillisecondsSinceEpoch(
     timestamp,
diff --git a/packages/devtools_app/lib/src/shared/server/_deep_links_api.dart b/packages/devtools_app/lib/src/shared/server/_deep_links_api.dart
index 3cfb382..8313ad9 100644
--- a/packages/devtools_app/lib/src/shared/server/_deep_links_api.dart
+++ b/packages/devtools_app/lib/src/shared/server/_deep_links_api.dart
@@ -8,9 +8,7 @@
   if (isDevToolsServerAvailable) {
     final uri = Uri(
       path: DeeplinkApi.androidBuildVariants,
-      queryParameters: {
-        DeeplinkApi.deeplinkRootPathPropertyName: path,
-      },
+      queryParameters: {DeeplinkApi.deeplinkRootPathPropertyName: path},
     );
     final resp = await request(uri.toString());
     if (resp?.statusOk ?? false) {
@@ -51,9 +49,7 @@
   if (isDevToolsServerAvailable) {
     final uri = Uri(
       path: DeeplinkApi.iosBuildOptions,
-      queryParameters: {
-        DeeplinkApi.deeplinkRootPathPropertyName: path,
-      },
+      queryParameters: {DeeplinkApi.deeplinkRootPathPropertyName: path},
     );
     final resp = await request(uri.toString());
     if (resp?.statusOk ?? false) {
diff --git a/packages/devtools_app/lib/src/shared/server/_preferences_api.dart b/packages/devtools_app/lib/src/shared/server/_preferences_api.dart
index a75381e..374388d 100644
--- a/packages/devtools_app/lib/src/shared/server/_preferences_api.dart
+++ b/packages/devtools_app/lib/src/shared/server/_preferences_api.dart
@@ -12,9 +12,7 @@
 
   final uri = Uri(
     path: PreferencesApi.getPreferenceValue,
-    queryParameters: {
-      PreferencesApi.preferenceKeyProperty: key,
-    },
+    queryParameters: {PreferencesApi.preferenceKeyProperty: key},
   );
   final resp = await request(uri.toString());
   if (resp?.statusOk ?? false) {
diff --git a/packages/devtools_app/lib/src/shared/server/server.dart b/packages/devtools_app/lib/src/shared/server/server.dart
index 583996a..e8e7d12 100644
--- a/packages/devtools_app/lib/src/shared/server/server.dart
+++ b/packages/devtools_app/lib/src/shared/server/server.dart
@@ -82,10 +82,7 @@
   }
 }
 
-DevToolsJsonFile _devToolsJsonFileFromResponse(
-  Response resp,
-  String filePath,
-) {
+DevToolsJsonFile _devToolsJsonFileFromResponse(Response resp, String filePath) {
   final data = json.decode(resp.body) as Map;
   final lastModified = data['lastModifiedTime'];
   final lastModifiedTime =
diff --git a/packages/devtools_app/lib/src/shared/server/server_api_client.dart b/packages/devtools_app/lib/src/shared/server/server_api_client.dart
index bd63735..547143d 100644
--- a/packages/devtools_app/lib/src/shared/server/server_api_client.dart
+++ b/packages/devtools_app/lib/src/shared/server/server_api_client.dart
@@ -11,7 +11,7 @@
 import 'package:logging/logging.dart';
 
 import '../config_specific/notifications/notifications.dart';
-import '../framework_controller.dart';
+import '../framework/framework_controller.dart';
 import '../globals.dart';
 
 final _log = Logger('lib/src/shared/server_api_client');
@@ -40,17 +40,19 @@
   ///
   /// - http://foo/devtools => http://foo/devtools/api
   @visibleForTesting
-  static Uri apiUriFor(Uri baseUri) => baseUri.path.endsWith('devtools')
-      ? baseUri.resolve('devtools/api/')
-      : baseUri.resolve('api/');
+  static Uri apiUriFor(Uri baseUri) =>
+      baseUri.path.endsWith('devtools')
+          ? baseUri.resolve('devtools/api/')
+          : baseUri.resolve('api/');
 
   static Future<DevToolsServerConnection?> connect() async {
     final apiUri = apiUriFor(Uri.base);
     final pingUri = apiUri.resolve('ping');
 
     try {
-      final response =
-          await http.get(pingUri).timeout(const Duration(seconds: 5));
+      final response = await http
+          .get(pingUri)
+          .timeout(const Duration(seconds: 5));
       // When running with the local dev server Flutter may serve its index page
       // for missing files to support the hashless url strategy. Check the response
       // content to confirm it came from our server.
@@ -187,16 +189,13 @@
 
   void _notifyCurrentPage(PageChangeEvent page) {
     unawaited(
-      _callMethod(
-        'currentPage',
-        {
-          'id': page.id,
-          // TODO(kenz): see if we need to change the client code on the
-          // DevTools server to be aware of the type of embedded mode (many vs.
-          // one).
-          'embedded': page.embedMode.embedded,
-        },
-      ),
+      _callMethod('currentPage', {
+        'id': page.id,
+        // TODO(kenz): see if we need to change the client code on the
+        // DevTools server to be aware of the type of embedded mode (many vs.
+        // one).
+        'embedded': page.embedMode.embedded,
+      }),
     );
   }
 
diff --git a/packages/devtools_app/lib/src/shared/table/_flat_table.dart b/packages/devtools_app/lib/src/shared/table/_flat_table.dart
index 38c06f9..1a0fe90 100644
--- a/packages/devtools_app/lib/src/shared/table/_flat_table.dart
+++ b/packages/devtools_app/lib/src/shared/table/_flat_table.dart
@@ -26,20 +26,23 @@
     super.pinBehavior = FlatTablePinBehavior.none,
     super.columnGroups,
     super.autoScrollContent = false,
+    super.startScrolledAtBottom = false,
     super.onItemSelected,
     super.preserveVerticalScrollPosition = false,
     super.includeColumnGroupHeaders = true,
     super.sizeColumnsToFit = true,
+    super.rowHeight,
     super.selectionNotifier,
   }) : super(
-          searchMatchesNotifier: searchController.searchMatches,
-          activeSearchMatchNotifier: searchController.activeSearchMatch,
-          onDataSorted: () => WidgetsBinding.instance.addPostFrameCallback((_) {
-            // This needs to be in a post frame callback so that the search
-            // matches are not updated in the middle of a table build.
-            searchController.refreshSearchMatches();
-          }),
-        );
+         searchMatchesNotifier: searchController.searchMatches,
+         activeSearchMatchNotifier: searchController.activeSearchMatch,
+         onDataSorted:
+             () => WidgetsBinding.instance.addPostFrameCallback((_) {
+               // This needs to be in a post frame callback so that the search
+               // matches are not updated in the middle of a table build.
+               searchController.refreshSearchMatches();
+             }),
+       );
 }
 
 /// A table that displays in a collection of [data], based on a collection of
@@ -56,6 +59,7 @@
     required this.columns,
     this.columnGroups,
     this.autoScrollContent = false,
+    this.startScrolledAtBottom = false,
     this.onItemSelected,
     required this.defaultSortColumn,
     required this.defaultSortDirection,
@@ -69,6 +73,7 @@
     this.includeColumnGroupHeaders = true,
     this.tallHeaders = false,
     this.sizeColumnsToFit = true,
+    this.rowHeight,
     this.headerColor,
     this.fillWithEmptyRows = false,
     this.enableHoverHandling = false,
@@ -93,6 +98,8 @@
   /// table fits in view (e.g. so that there is no horizontal scrolling).
   final bool sizeColumnsToFit;
 
+  final double? rowHeight;
+
   // TODO(kenz): should we enable this behavior by default? Does it ever matter
   // to preserve the order of the original data passed to a flat table?
   /// Whether table sorting should sort the original data list instead of
@@ -137,6 +144,10 @@
   /// Auto-scrolling the table to keep new content visible.
   final bool autoScrollContent;
 
+  /// Determines whether the table should be scrolled to the bottom of the
+  /// scrollable area on the initial build of the table.
+  final bool startScrolledAtBottom;
+
   /// Factory that creates keys for each row in this table.
   final Key Function(T data) keyFactory;
 
@@ -266,7 +277,8 @@
     FlatTable<T> oldWidget,
     FlatTable<T> newWidget,
   ) {
-    final columnsChanged = !collectionEquals(
+    final columnsChanged =
+        !collectionEquals(
           oldWidget.columns.map((c) => c.config),
           newWidget.columns.map((c) => c.config),
         ) ||
@@ -280,25 +292,27 @@
   @override
   Widget build(BuildContext context) {
     Widget buildTable(List<double> columnWidths) => DevToolsTable<T>(
-          tableController: tableController,
-          columnWidths: columnWidths,
-          autoScrollContent: widget.autoScrollContent,
-          rowBuilder: _buildRow,
-          activeSearchMatchNotifier: widget.activeSearchMatchNotifier,
-          rowItemExtent: defaultRowHeight,
-          preserveVerticalScrollPosition: widget.preserveVerticalScrollPosition,
-          tallHeaders: widget.tallHeaders,
-          headerColor: widget.headerColor,
-          fillWithEmptyRows: widget.fillWithEmptyRows,
-          enableHoverHandling: widget.enableHoverHandling,
-        );
+      tableController: tableController,
+      columnWidths: columnWidths,
+      autoScrollContent: widget.autoScrollContent,
+      startScrolledAtBottom: widget.startScrolledAtBottom,
+      rowBuilder: _buildRow,
+      activeSearchMatchNotifier: widget.activeSearchMatchNotifier,
+      rowItemExtent: widget.rowHeight ?? defaultRowHeight,
+      preserveVerticalScrollPosition: widget.preserveVerticalScrollPosition,
+      tallHeaders: widget.tallHeaders,
+      headerColor: widget.headerColor,
+      fillWithEmptyRows: widget.fillWithEmptyRows,
+      enableHoverHandling: widget.enableHoverHandling,
+    );
     if (widget.sizeColumnsToFit || tableController.columnWidths == null) {
       return LayoutBuilder(
-        builder: (context, constraints) => buildTable(
-          tableController.computeColumnWidthsSizeToFit(
-            constraints.maxWidth,
-          ),
-        ),
+        builder:
+            (context, constraints) => buildTable(
+              tableController.computeColumnWidthsSizeToFit(
+                constraints.maxWidth,
+              ),
+            ),
       );
     }
     return buildTable(tableController.columnWidths!);
diff --git a/packages/devtools_app/lib/src/shared/table/_table_column.dart b/packages/devtools_app/lib/src/shared/table/_table_column.dart
index da2e4f0..29d8a98 100644
--- a/packages/devtools_app/lib/src/shared/table/_table_column.dart
+++ b/packages/devtools_app/lib/src/shared/table/_table_column.dart
@@ -57,7 +57,8 @@
     ColumnData<T> column,
     SortDirection direction, {
     ColumnData<T>? secondarySortColumn,
-  })? onSortChanged;
+  })?
+  onSortChanged;
 
   @override
   Widget build(BuildContext context) {
@@ -80,28 +81,30 @@
           const SizedBox(width: densePadding),
         ],
         Expanded(
-          child: column.titleTooltip != null
-              ? DevToolsTooltip(
-                  message: column.titleTooltip,
-                  padding: const EdgeInsets.all(denseSpacing),
-                  child: title,
-                )
-              : title,
+          child:
+              column.titleTooltip != null
+                  ? DevToolsTooltip(
+                    message: column.titleTooltip,
+                    padding: const EdgeInsets.all(denseSpacing),
+                    child: title,
+                  )
+                  : title,
         ),
       ],
     );
 
     return column.includeHeader
         ? InkWell(
-            canRequestFocus: false,
-            onTap: column.supportsSorting
-                ? () => _handleSortChange(
-                      column,
-                      secondarySortColumn: secondarySortColumn,
-                    )
-                : null,
-            child: headerContent,
-          )
+          canRequestFocus: false,
+          onTap:
+              column.supportsSorting
+                  ? () => _handleSortChange(
+                    column,
+                    secondarySortColumn: secondarySortColumn,
+                  )
+                  : null,
+          child: headerContent,
+        )
         : headerContent;
   }
 
@@ -143,9 +146,7 @@
     return Container(
       padding: const EdgeInsets.symmetric(horizontal: defaultSpacing),
       decoration: BoxDecoration(
-        border: Border(
-          bottom: defaultBorderSide(Theme.of(context)),
-        ),
+        border: Border(bottom: defaultBorderSide(Theme.of(context))),
       ),
       child: ListView.builder(
         scrollDirection: Axis.horizontal,
@@ -190,11 +191,7 @@
         width: columnGroupSpacing,
         decoration: BoxDecoration(
           gradient: LinearGradient(
-            colors: [
-              Colors.black,
-              Theme.of(context).focusColor,
-              Colors.black,
-            ],
+            colors: [Colors.black, Theme.of(context).focusColor, Colors.black],
           ),
         ),
       ),
diff --git a/packages/devtools_app/lib/src/shared/table/_table_row.dart b/packages/devtools_app/lib/src/shared/table/_table_row.dart
index 49998db..8dc2874 100644
--- a/packages/devtools_app/lib/src/shared/table/_table_row.dart
+++ b/packages/devtools_app/lib/src/shared/table/_table_row.dart
@@ -4,18 +4,9 @@
 
 part of 'table.dart';
 
-enum _TableRowType {
-  data,
-  columnHeader,
-  columnGroupHeader,
-  filler,
-}
+enum _TableRowType { data, columnHeader, columnGroupHeader, filler }
 
-enum _TableRowPartDisplayType {
-  column,
-  columnSpacer,
-  columnGroupSpacer,
-}
+enum _TableRowPartDisplayType { column, columnSpacer, columnGroupSpacer }
 
 /// Presents a [node] as a row in a table.
 ///
@@ -43,12 +34,12 @@
     this.displayTreeGuidelines = false,
     this.searchMatchesNotifier,
     this.activeSearchMatchNotifier,
-  })  : sortColumn = null,
-        sortDirection = null,
-        secondarySortColumn = null,
-        onSortChanged = null,
-        _rowType = _TableRowType.data,
-        tall = false;
+  }) : sortColumn = null,
+       sortDirection = null,
+       secondarySortColumn = null,
+       onSortChanged = null,
+       _rowType = _TableRowType.data,
+       tall = false;
 
   /// Constructs a [TableRow] that is empty.
   const TableRow.filler({
@@ -58,23 +49,23 @@
     required this.columnWidths,
     this.columnGroups,
     this.backgroundColor,
-  })  : node = null,
-        isExpanded = false,
-        isExpandable = false,
-        isSelected = false,
-        onPressed = null,
-        expandableColumn = null,
-        isShown = true,
-        sortColumn = null,
-        sortDirection = null,
-        secondarySortColumn = null,
-        onSortChanged = null,
-        searchMatchesNotifier = null,
-        activeSearchMatchNotifier = null,
-        tall = false,
-        enableHoverHandling = false,
-        displayTreeGuidelines = false,
-        _rowType = _TableRowType.filler;
+  }) : node = null,
+       isExpanded = false,
+       isExpandable = false,
+       isSelected = false,
+       onPressed = null,
+       expandableColumn = null,
+       isShown = true,
+       sortColumn = null,
+       sortDirection = null,
+       secondarySortColumn = null,
+       onSortChanged = null,
+       searchMatchesNotifier = null,
+       activeSearchMatchNotifier = null,
+       tall = false,
+       enableHoverHandling = false,
+       displayTreeGuidelines = false,
+       _rowType = _TableRowType.filler;
 
   /// Constructs a [TableRow] that presents the column titles instead
   /// of any [node].
@@ -91,17 +82,17 @@
     this.onPressed,
     this.tall = false,
     this.backgroundColor,
-  })  : node = null,
-        isExpanded = false,
-        isExpandable = false,
-        isSelected = false,
-        expandableColumn = null,
-        isShown = true,
-        searchMatchesNotifier = null,
-        activeSearchMatchNotifier = null,
-        displayTreeGuidelines = false,
-        enableHoverHandling = false,
-        _rowType = _TableRowType.columnHeader;
+  }) : node = null,
+       isExpanded = false,
+       isExpandable = false,
+       isSelected = false,
+       expandableColumn = null,
+       isShown = true,
+       searchMatchesNotifier = null,
+       activeSearchMatchNotifier = null,
+       displayTreeGuidelines = false,
+       enableHoverHandling = false,
+       _rowType = _TableRowType.columnHeader;
 
   /// Constructs a [TableRow] that presents column group titles instead of any
   /// [node].
@@ -117,18 +108,18 @@
     this.onPressed,
     this.tall = false,
     this.backgroundColor,
-  })  : node = null,
-        isExpanded = false,
-        isExpandable = false,
-        isSelected = false,
-        expandableColumn = null,
-        columns = const [],
-        isShown = true,
-        searchMatchesNotifier = null,
-        activeSearchMatchNotifier = null,
-        displayTreeGuidelines = false,
-        enableHoverHandling = false,
-        _rowType = _TableRowType.columnGroupHeader;
+  }) : node = null,
+       isExpanded = false,
+       isExpandable = false,
+       isSelected = false,
+       expandableColumn = null,
+       columns = const [],
+       isShown = true,
+       searchMatchesNotifier = null,
+       activeSearchMatchNotifier = null,
+       displayTreeGuidelines = false,
+       enableHoverHandling = false,
+       _rowType = _TableRowType.columnGroupHeader;
 
   final LinkedScrollControllerGroup linkedScrollControllerGroup;
 
@@ -188,7 +179,8 @@
     ColumnData<T> column,
     SortDirection direction, {
     ColumnData<T>? secondarySortColumn,
-  })? onSortChanged;
+  })?
+  onSortChanged;
 
   final ValueListenable<List<T>>? searchMatchesNotifier;
 
@@ -280,26 +272,25 @@
       onPressed = () => widgetOnPressed(node);
     }
 
-    final row = tableRowFor(
-      context,
-      onPressed: onPressed,
-    );
+    final row = tableRowFor(context, onPressed: onPressed);
 
     final box = SizedBox(
-      height: widget._rowType == _TableRowType.data
-          ? defaultRowHeight
-          : defaultHeaderHeight +
-              (widget.tall ? scaleByFontFactor(densePadding) : 0.0),
+      height:
+          widget._rowType == _TableRowType.data
+              ? defaultRowHeight
+              : defaultHeaderHeight +
+                  (widget.tall ? scaleByFontFactor(densePadding) : 0.0),
       child: Material(
         color: _searchAwareBackgroundColor(),
-        child: onPressed != null
-            ? InkWell(
-                canRequestFocus: false,
-                key: contentKey,
-                onTap: onPressed,
-                child: row,
-              )
-            : row,
+        child:
+            onPressed != null
+                ? InkWell(
+                  canRequestFocus: false,
+                  key: contentKey,
+                  onTap: onPressed,
+                  child: row,
+                )
+                : row,
       ),
     );
     return box;
@@ -347,14 +338,15 @@
     if (widget.isSelected) {
       return colorScheme.selectedRowBackgroundColor;
     }
-    final searchAwareBackgroundColor = isSearchMatch
-        ? Color.alphaBlend(
-            isActiveSearchMatch
-                ? activeSearchMatchColorOpaque
-                : searchMatchColorOpaque,
-            backgroundColor,
-          )
-        : backgroundColor;
+    final searchAwareBackgroundColor =
+        isSearchMatch
+            ? Color.alphaBlend(
+              isActiveSearchMatch
+                  ? activeSearchMatchColorOpaque
+                  : searchMatchColorOpaque,
+              backgroundColor,
+            )
+            : backgroundColor;
     return searchAwareBackgroundColor;
   }
 
@@ -422,17 +414,19 @@
         content = const SizedBox.shrink();
       } else if (widget._rowType == _TableRowType.columnHeader) {
         Widget defaultHeaderRenderer() => _ColumnHeader(
-              column: column,
-              isSortColumn: column == widget.sortColumn,
-              secondarySortColumn: widget.secondarySortColumn,
-              sortDirection: widget.sortDirection!,
-              onSortChanged: widget.onSortChanged,
-            );
+          column: column,
+          isSortColumn: column == widget.sortColumn,
+          secondarySortColumn: widget.secondarySortColumn,
+          sortDirection: widget.sortDirection!,
+          onSortChanged: widget.onSortChanged,
+        );
 
         // ignore: avoid-unrelated-type-assertions, false positive.
         if (column is ColumnHeaderRenderer) {
-          content = (column as ColumnHeaderRenderer)
-              .buildHeader(context, defaultHeaderRenderer);
+          content = (column as ColumnHeaderRenderer).buildHeader(
+            context,
+            defaultHeaderRenderer,
+          );
         }
         // If ColumnHeaderRenderer.build returns null, fall back to the default
         // rendering.
@@ -461,9 +455,7 @@
               if (column.getCaption(node) != null)
                 TextSpan(
                   text: ' ${column.getCaption(node)}',
-                  style: const TextStyle(
-                    fontStyle: FontStyle.italic,
-                  ),
+                  style: const TextStyle(fontStyle: FontStyle.italic),
                 ),
             ],
             style: column.contentTextStyle(
@@ -489,34 +481,30 @@
         }
 
         if (column == widget.expandableColumn) {
-          final expandIndicator = widget.isExpandable
-              ? ValueListenableBuilder(
-                  valueListenable: expandController,
-                  builder: (context, _, __) {
-                    return RotationTransition(
-                      turns: expandArrowAnimation,
-                      child: Icon(
-                        Icons.expand_more,
-                        color: theme.colorScheme.onSurface,
-                        size: defaultIconSize,
-                      ),
-                    );
-                  },
-                )
-              : SizedBox(width: defaultIconSize, height: defaultIconSize);
+          final expandIndicator =
+              widget.isExpandable
+                  ? ValueListenableBuilder(
+                    valueListenable: expandController,
+                    builder: (context, _, _) {
+                      return RotationTransition(
+                        turns: expandArrowAnimation,
+                        child: Icon(
+                          Icons.expand_more,
+                          color: theme.colorScheme.onSurface,
+                          size: defaultIconSize,
+                        ),
+                      );
+                    },
+                  )
+                  : SizedBox(width: defaultIconSize, height: defaultIconSize);
           content = Row(
             mainAxisSize: MainAxisSize.min,
-            children: [
-              expandIndicator,
-              Expanded(child: content),
-            ],
+            children: [expandIndicator, Expanded(child: content)],
           );
         }
         content = Padding(
           padding: EdgeInsets.only(left: padding),
-          child: ClipRect(
-            child: content,
-          ),
+          child: ClipRect(child: content),
         );
       } else {
         throw Exception(
@@ -526,27 +514,18 @@
 
       content = SizedBox(
         width: columnWidth,
-        child: Align(
-          alignment: _alignmentFor(column),
-          child: content,
-        ),
+        child: Align(alignment: _alignmentFor(column), child: content),
       );
       if (widget.displayTreeGuidelines &&
           node != null &&
           node is TreeNode &&
           column is TreeColumnData) {
         content = CustomPaint(
-          painter: _RowGuidelinePainter(
-            node.level,
-            theme.colorScheme,
-          ),
+          painter: _RowGuidelinePainter(node.level, theme.colorScheme),
           child: content,
         );
       }
-      return DefaultTextStyle(
-        style: theme.regularTextStyle,
-        child: content,
-      );
+      return DefaultTextStyle(style: theme.regularTextStyle, child: content);
     }
 
     if (widget._rowType == _TableRowType.columnGroupHeader) {
@@ -565,28 +544,25 @@
         physics: const ClampingScrollPhysics(),
         controller: scrollController,
         extentDelegate: rowExtentDelegate,
-        childrenDelegate: SliverChildBuilderDelegate(
-          (context, int i) {
-            final columnIndexMap = _columnIndexMapHelper(_rowDisplayParts);
-            final displayTypeForIndex = _rowDisplayParts[i];
-            switch (displayTypeForIndex) {
-              case _TableRowPartDisplayType.column:
-                final index = columnIndexMap[i]!;
-                return columnFor(
-                  widget.columns[index],
-                  widget.columnWidths[index],
-                );
-              case _TableRowPartDisplayType.columnSpacer:
-                return const SizedBox(
-                  width: columnSpacing,
-                  child: VerticalDivider(width: columnSpacing),
-                );
-              case _TableRowPartDisplayType.columnGroupSpacer:
-                return const _ColumnGroupSpacer();
-            }
-          },
-          childCount: _rowDisplayParts.length,
-        ),
+        childrenDelegate: SliverChildBuilderDelegate((context, int i) {
+          final columnIndexMap = _columnIndexMapHelper(_rowDisplayParts);
+          final displayTypeForIndex = _rowDisplayParts[i];
+          switch (displayTypeForIndex) {
+            case _TableRowPartDisplayType.column:
+              final index = columnIndexMap[i]!;
+              return columnFor(
+                widget.columns[index],
+                widget.columnWidths[index],
+              );
+            case _TableRowPartDisplayType.columnSpacer:
+              return const SizedBox(
+                width: columnSpacing,
+                child: VerticalDivider(width: columnSpacing),
+              );
+            case _TableRowPartDisplayType.columnGroupSpacer:
+              return const _ColumnGroupSpacer();
+          }
+        }, childCount: _rowDisplayParts.length),
       ),
     );
 
diff --git a/packages/devtools_app/lib/src/shared/table/_tree_table.dart b/packages/devtools_app/lib/src/shared/table/_tree_table.dart
index 81bbc7c..024a2ad 100644
--- a/packages/devtools_app/lib/src/shared/table/_tree_table.dart
+++ b/packages/devtools_app/lib/src/shared/table/_tree_table.dart
@@ -4,11 +4,7 @@
 
 part of 'table.dart';
 
-enum TreeTableScrollKind {
-  up,
-  down,
-  parent,
-}
+enum TreeTableScrollKind { up, down, parent }
 
 /// A table that shows [TreeNode]s.
 ///
@@ -42,10 +38,10 @@
     this.tallHeaders = false,
     this.headerColor,
     ValueNotifier<Selection<T?>>? selectionNotifier,
-  })  : selectionNotifier = selectionNotifier ??
-            ValueNotifier<Selection<T?>>(Selection.empty()),
-        assert(columns.contains(treeColumn)),
-        assert(columns.contains(defaultSortColumn));
+  }) : selectionNotifier =
+           selectionNotifier ?? ValueNotifier<Selection<T?>>(Selection.empty()),
+       assert(columns.contains(treeColumn)),
+       assert(columns.contains(defaultSortColumn));
 
   /// Factory that creates keys for each row in this table.
   final Key Function(T) keyFactory;
@@ -207,7 +203,8 @@
     TreeTable<T> oldWidget,
     TreeTable<T> newWidget,
   ) {
-    final columnsChanged = !collectionEquals(
+    final columnsChanged =
+        !collectionEquals(
           oldWidget.columns.map((c) => c.title),
           newWidget.columns.map((c) => c.title),
         ) ||
@@ -336,10 +333,7 @@
 
     // If there is no selected node, choose the first one.
     if (widget.selectionNotifier.value.node == null) {
-      widget.selectionNotifier.value = Selection(
-        node: _data[0],
-        nodeIndex: 0,
-      );
+      widget.selectionNotifier.value = Selection(node: _data[0], nodeIndex: 0);
     }
 
     final selection = widget.selectionNotifier.value;
@@ -397,8 +391,10 @@
     // occupies space in the viewport so we must subtract 1 for that, and we
     // subtract 1 to account for the fact that a partial row could be displayed
     // at the top and bottom of the view.
-    final minCompleteItemsInView =
-        max((viewportHeight / defaultRowHeight).floor() - 2, 0);
+    final minCompleteItemsInView = max(
+      (viewportHeight / defaultRowHeight).floor() - 2,
+      0,
+    );
     final lastItemIndex = firstItemIndex + minCompleteItemsInView - 1;
     late int newSelectedNodeIndex;
 
@@ -412,9 +408,10 @@
         newSelectedNodeIndex = max(selectedNodeIndex! - 1, 0);
         break;
       case TreeTableScrollKind.parent:
-        newSelectedNodeIndex = selectionValue.node!.parent != null
-            ? max(_data.indexOf(selectionValue.node!.parent!), 0)
-            : 0;
+        newSelectedNodeIndex =
+            selectionValue.node!.parent != null
+                ? max(_data.indexOf(selectionValue.node!.parent!), 0)
+                : 0;
         break;
     }
 
@@ -458,10 +455,7 @@
 class _RowGuidelinePainter extends CustomPainter {
   _RowGuidelinePainter(this.level, this.colorScheme);
 
-  static const _treeGuidelineColors = [
-    Color(0xFF13B9FD),
-    Color(0xFF5BC43B),
-  ];
+  static const _treeGuidelineColors = [Color(0xFF13B9FD), Color(0xFF5BC43B)];
 
   final int level;
   final ColorScheme colorScheme;
diff --git a/packages/devtools_app/lib/src/shared/table/column_widths.dart b/packages/devtools_app/lib/src/shared/table/column_widths.dart
index f52814c..362abde 100644
--- a/packages/devtools_app/lib/src/shared/table/column_widths.dart
+++ b/packages/devtools_app/lib/src/shared/table/column_widths.dart
@@ -55,15 +55,15 @@
     maxWidth = max(0, maxWidth);
     double available = maxWidth;
     // Columns sorted by increasing minWidth.
-    final sortedColumns = columns.toList()
-      ..sort((a, b) {
-        if (a.minWidthPx != null && b.minWidthPx != null) {
-          return a.minWidthPx!.compareTo(b.minWidthPx!);
-        }
-        if (a.minWidthPx != null) return -1;
-        if (b.minWidthPx != null) return 1;
-        return 0;
-      });
+    final sortedColumns =
+        columns.toList()..sort((a, b) {
+          if (a.minWidthPx != null && b.minWidthPx != null) {
+            return a.minWidthPx!.compareTo(b.minWidthPx!);
+          }
+          if (a.minWidthPx != null) return -1;
+          if (b.minWidthPx != null) return 1;
+          return 0;
+        });
 
     for (final col in columns) {
       if (col.fixedWidthPx != null) {
diff --git a/packages/devtools_app/lib/src/shared/table/table.dart b/packages/devtools_app/lib/src/shared/table/table.dart
index d9fe6cf..0e40c50 100644
--- a/packages/devtools_app/lib/src/shared/table/table.dart
+++ b/packages/devtools_app/lib/src/shared/table/table.dart
@@ -11,15 +11,15 @@
 import 'package:flutter/material.dart' hide TableRow;
 import 'package:flutter/services.dart';
 
-import '../collapsible_mixin.dart';
-import '../common_widgets.dart';
+import '../primitives/collapsible_mixin.dart';
 import '../primitives/extent_delegate_list.dart';
 import '../primitives/flutter_widgets/linked_scroll_controller.dart';
 import '../primitives/trees.dart';
 import '../primitives/utils.dart';
+import '../ui/common_widgets.dart';
 import '../ui/search.dart';
 import '../ui/utils.dart';
-import '../utils.dart';
+import '../utils/utils.dart';
 import 'column_widths.dart';
 import 'table_controller.dart';
 import 'table_data.dart';
@@ -32,20 +32,22 @@
 // TODO(devoncarew): We need to render the selected row with a different
 // background color.
 
-typedef IndexedScrollableWidgetBuilder = Widget Function({
-  required BuildContext context,
-  required LinkedScrollControllerGroup linkedScrollControllerGroup,
-  required int index,
-  required List<double> columnWidths,
-  required bool isPinned,
-  required bool enableHoverHandling,
-});
+typedef IndexedScrollableWidgetBuilder =
+    Widget Function({
+      required BuildContext context,
+      required LinkedScrollControllerGroup linkedScrollControllerGroup,
+      required int index,
+      required List<double> columnWidths,
+      required bool isPinned,
+      required bool enableHoverHandling,
+    });
 
-typedef TableKeyEventHandler = KeyEventResult Function(
-  KeyEvent event,
-  ScrollController scrollController,
-  BoxConstraints constraints,
-);
+typedef TableKeyEventHandler =
+    KeyEventResult Function(
+      KeyEvent event,
+      ScrollController scrollController,
+      BoxConstraints constraints,
+    );
 
 class Selection<T> {
   Selection({
@@ -56,10 +58,10 @@
   }) : assert(nodeIndex == null || nodeIndexCalculator == null);
 
   Selection.empty()
-      : node = null,
-        nodeIndex = null,
-        nodeIndexCalculator = null,
-        scrollIntoView = true;
+    : node = null,
+      nodeIndex = null,
+      nodeIndexCalculator = null,
+      scrollIntoView = true;
 
   final T? node;
   // TODO (carolynqu): get rid of nodeIndex and only use nodeIndexCalculator, https://github.com/flutter/devtools/issues/4266
@@ -81,6 +83,7 @@
     this.focusNode,
     this.handleKeyEvent,
     this.autoScrollContent = false,
+    this.startScrolledAtBottom = false,
     this.preserveVerticalScrollPosition = false,
     this.activeSearchMatchNotifier,
     this.rowItemExtent,
@@ -92,6 +95,7 @@
 
   final TableControllerBase<T> tableController;
   final bool autoScrollContent;
+  final bool startScrolledAtBottom;
   final List<double> columnWidths;
   final IndexedScrollableWidgetBuilder rowBuilder;
   final double? rowItemExtent;
@@ -135,12 +139,19 @@
 
     _linkedHorizontalScrollControllerGroup = LinkedScrollControllerGroup();
 
-    final initialScrollOffset = widget.preserveVerticalScrollPosition
-        ? widget.tableController.tableUiState.scrollOffset
-        : 0.0;
+    final initialScrollOffset =
+        widget.preserveVerticalScrollPosition
+            ? widget.tableController.tableUiState.scrollOffset
+            : 0.0;
     widget.tableController.initScrollController(initialScrollOffset);
     scrollController = widget.tableController.verticalScrollController!;
 
+    if (widget.startScrolledAtBottom) {
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        unawaited(scrollController.autoScrollToBottom(jump: true));
+      });
+    }
+
     pinnedScrollController = ScrollController();
 
     adjustedColumnWidths = List.of(widget.columnWidths);
@@ -150,7 +161,8 @@
   void didUpdateWidget(covariant DevToolsTable<T> oldWidget) {
     super.didUpdateWidget(oldWidget);
 
-    final notifiersChanged = widget.tableController.tableData !=
+    final notifiersChanged =
+        widget.tableController.tableData !=
             oldWidget.tableController.tableData ||
         widget.selectionNotifier != oldWidget.selectionNotifier ||
         widget.activeSearchMatchNotifier != oldWidget.activeSearchMatchNotifier;
@@ -188,9 +200,10 @@
           final nodeIndex = selection.nodeIndex;
 
           if (selection.scrollIntoView && node != null) {
-            final selectedDisplayRow = nodeIndexCalculator != null
-                ? nodeIndexCalculator(node)!
-                : nodeIndex!;
+            final selectedDisplayRow =
+                nodeIndexCalculator != null
+                    ? nodeIndexCalculator(node)!
+                    : nodeIndex!;
 
             final newPos = selectedDisplayRow * defaultRowHeight;
 
@@ -220,7 +233,8 @@
     if (index == -1) return;
 
     final y = index * defaultRowHeight;
-    final indexInView = y > scrollController.offset &&
+    final indexInView =
+        y > scrollController.offset &&
         y < scrollController.offset + scrollController.position.extentInside;
     if (!indexInView) {
       await scrollController.animateTo(
@@ -336,9 +350,9 @@
   }
 
   double _pinnedDataHeight(BoxConstraints tableConstraints) => min(
-        widget.rowItemExtent! * pinnedData.length,
-        tableConstraints.maxHeight / 2,
-      );
+    widget.rowItemExtent! * pinnedData.length,
+    tableConstraints.maxHeight / 2,
+  );
 
   int _dataRowCount(
     BoxConstraints tableConstraints,
@@ -350,7 +364,8 @@
 
     var maxHeight = tableConstraints.maxHeight;
     final columnHeadersCount = showColumnGroupHeader ? 2 : 1;
-    maxHeight -= columnHeadersCount *
+    maxHeight -=
+        columnHeadersCount *
         (defaultHeaderHeight +
             (widget.tallHeaders ? scaleByFontFactor(densePadding) : 0.0));
 
@@ -387,7 +402,8 @@
     final columnGroups = widget.tableController.columnGroups;
     final includeColumnGroupHeaders =
         widget.tableController.includeColumnGroupHeaders;
-    final showColumnGroupHeader = columnGroups != null &&
+    final showColumnGroupHeader =
+        columnGroups != null &&
         columnGroups.isNotEmpty &&
         includeColumnGroupHeaders;
     final tableUiState = widget.tableController.tableUiState;
@@ -422,6 +438,7 @@
                     tall: widget.tallHeaders,
                     backgroundColor: widget.headerColor,
                   ),
+                // TODO(kenz): add support for excluding column headers.
                 TableRow<T>.tableColumnHeader(
                   key: const Key('Table header'),
                   linkedScrollControllerGroup:
@@ -447,11 +464,9 @@
                         controller: pinnedScrollController,
                         itemCount: pinnedData.length,
                         itemExtent: widget.rowItemExtent,
-                        itemBuilder: (context, index) => _buildItem(
-                          context,
-                          index,
-                          isPinned: true,
-                        ),
+                        itemBuilder:
+                            (context, index) =>
+                                _buildItem(context, index, isPinned: true),
                       ),
                     ),
                   ),
@@ -466,18 +481,22 @@
                       onTapDown: (a) => widget.focusNode?.requestFocus(),
                       child: Focus(
                         autofocus: true,
-                        onKeyEvent: (_, event) => widget.handleKeyEvent != null
-                            ? widget.handleKeyEvent!(
-                                event,
-                                scrollController,
-                                constraints,
-                              )
-                            : KeyEventResult.ignored,
+                        onKeyEvent:
+                            (_, event) =>
+                                widget.handleKeyEvent != null
+                                    ? widget.handleKeyEvent!(
+                                      event,
+                                      scrollController,
+                                      constraints,
+                                    )
+                                    : KeyEventResult.ignored,
                         focusNode: widget.focusNode,
                         child: ListView.builder(
                           controller: scrollController,
-                          itemCount:
-                              _dataRowCount(constraints, showColumnGroupHeader),
+                          itemCount: _dataRowCount(
+                            constraints,
+                            showColumnGroupHeader,
+                          ),
                           itemExtent: widget.rowItemExtent,
                           itemBuilder: _buildItem,
                         ),
diff --git a/packages/devtools_app/lib/src/shared/table/table_controller.dart b/packages/devtools_app/lib/src/shared/table/table_controller.dart
index 11e4a13..eb21d26 100644
--- a/packages/devtools_app/lib/src/shared/table/table_controller.dart
+++ b/packages/devtools_app/lib/src/shared/table/table_controller.dart
@@ -19,11 +19,7 @@
 ///   - [FlatTablePinBehavior.pinCopyToTop] creates a copy of the original item
 ///     and inserts it into the list of pinned items, leaving the original item
 ///     in the list of unpinned items.
-enum FlatTablePinBehavior {
-  none,
-  pinOriginalToTop,
-  pinCopyToTop,
-}
+enum FlatTablePinBehavior { none, pinOriginalToTop, pinCopyToTop }
 
 class FlatTableController<T> extends TableControllerBase<T> {
   FlatTableController({
@@ -74,9 +70,7 @@
     if (sortOriginalData) {
       _modifiableOriginalData = data;
     } else {
-      _unmodifiableOriginalData = UnmodifiableListView(
-        List<T>.of(data),
-      );
+      _unmodifiableOriginalData = UnmodifiableListView(List<T>.of(data));
     }
 
     // Look up the UI state for [key], and sort accordingly.
@@ -156,8 +150,8 @@
     super.columnGroups,
     required this.treeColumn,
     this.autoExpandRoots = false,
-  })  : assert(columns.contains(treeColumn)),
-        assert(columns.contains(defaultSortColumn));
+  }) : assert(columns.contains(treeColumn)),
+       assert(columns.contains(defaultSortColumn));
 
   /// The column of the table to treat as expandable.
   final TreeColumnData<T> treeColumn;
@@ -178,7 +172,9 @@
     }
     // TODO(kenz): instead of using the maximum tree depth, consider using the
     // maximum depth of expanded nodes.
-    maxTableDepth = dataRoots.map((root) => root.depth).fold(
+    maxTableDepth = dataRoots
+        .map((root) => root.depth)
+        .fold(
           0,
           (currentMaxDepth, nextDepth) => max(currentMaxDepth, nextDepth),
         );
@@ -202,12 +198,12 @@
   }) {
     pinnedData = <T>[];
     int sortFunction(T a, T b) => _compareData<T>(
-          a,
-          b,
-          column,
-          direction,
-          secondarySortColumn: secondarySortColumn,
-        );
+      a,
+      b,
+      column,
+      direction,
+      secondarySortColumn: secondarySortColumn,
+    );
     void sort(T dataObject) {
       dataObject.children
         ..sort(sortFunction)
@@ -275,8 +271,9 @@
   ScrollController? verticalScrollController;
 
   void initScrollController([double initialScrollOffset = 0.0]) {
-    verticalScrollController =
-        ScrollController(initialScrollOffset: initialScrollOffset);
+    verticalScrollController = ScrollController(
+      initialScrollOffset: initialScrollOffset,
+    );
   }
 
   void storeScrollPosition() {
@@ -358,10 +355,8 @@
 }
 
 class TableData<T> {
-  const TableData({
-    required this.data,
-    String? key,
-  }) : key = key ?? defaultDataKey;
+  const TableData({required this.data, String? key})
+    : key = key ?? defaultDataKey;
 
   factory TableData.empty() => TableData<T>(data: const []);
 
diff --git a/packages/devtools_app/lib/src/shared/table/table_data.dart b/packages/devtools_app/lib/src/shared/table/table_data.dart
index e711a90..fc3aa1a 100644
--- a/packages/devtools_app/lib/src/shared/table/table_data.dart
+++ b/packages/devtools_app/lib/src/shared/table/table_data.dart
@@ -132,11 +132,7 @@
   }
 }
 
-enum ColumnAlignment {
-  left,
-  right,
-  center,
-}
+enum ColumnAlignment { left, right, center }
 
 mixin PinnableListEntry {
   /// Determines if the row should be pinned to the top of the table.
@@ -157,9 +153,9 @@
     required Range range,
     String? tooltip,
   }) : this(
-          title: maybeWrapWithTooltip(child: Text(title), tooltip: tooltip),
-          range: range,
-        );
+         title: maybeWrapWithTooltip(child: Text(title), tooltip: tooltip),
+         range: range,
+       );
 
   final Widget title;
 
@@ -212,10 +208,7 @@
     this.percentageOnly = false,
     double columnWidth = _defaultTimeColumnWidth,
     super.titleTooltip,
-  }) : super(
-          title,
-          fixedWidthPx: scaleByFontFactor(columnWidth),
-        );
+  }) : super(title, fixedWidthPx: scaleByFontFactor(columnWidth));
 
   static const _defaultTimeColumnWidth = 120.0;
 
@@ -244,9 +237,10 @@
   }
 
   @override
-  double getValue(T dataObject) => percentageOnly
-      ? percentAsDoubleProvider(dataObject)
-      : timeProvider!(dataObject).inMicroseconds.toDouble();
+  double getValue(T dataObject) =>
+      percentageOnly
+          ? percentAsDoubleProvider(dataObject)
+          : timeProvider!(dataObject).inMicroseconds.toDouble();
 
   @override
   String getDisplayValue(T dataObject) {
@@ -295,10 +289,7 @@
     this.percentageOnly = false,
     double columnWidth = _defaultMemoryColumnWidth,
     super.titleTooltip,
-  }) : super(
-          title,
-          fixedWidthPx: scaleByFontFactor(columnWidth),
-        );
+  }) : super(title, fixedWidthPx: scaleByFontFactor(columnWidth));
 
   static const _defaultMemoryColumnWidth =
       TimeAndPercentageColumn._defaultTimeColumnWidth;
@@ -328,9 +319,10 @@
   }
 
   @override
-  double getValue(T dataObject) => percentageOnly
-      ? percentAsDoubleProvider(dataObject)
-      : sizeProvider!(dataObject).toDouble();
+  double getValue(T dataObject) =>
+      percentageOnly
+          ? percentAsDoubleProvider(dataObject)
+          : sizeProvider!(dataObject).toDouble();
 
   @override
   String getDisplayValue(T dataObject) {
diff --git a/packages/devtools_app/lib/src/shared/ui/colors.dart b/packages/devtools_app/lib/src/shared/ui/colors.dart
index d25ce3d..2d2c697 100644
--- a/packages/devtools_app/lib/src/shared/ui/colors.dart
+++ b/packages/devtools_app/lib/src/shared/ui/colors.dart
@@ -21,14 +21,8 @@
 ];
 
 final rasterColorPalette = [
-  const ColorPair(
-    background: mainRasterColor,
-    foreground: Colors.white,
-  ),
-  const ColorPair(
-    background: Color(0xFF386EB6),
-    foreground: Colors.white,
-  ),
+  const ColorPair(background: mainRasterColor, foreground: Colors.white),
+  const ColorPair(background: Color(0xFF386EB6), foreground: Colors.white),
 ];
 
 // TODO(jacobr): merge this with other color scheme extensions.
@@ -68,44 +62,23 @@
 const treemapDeferredColor = Color(0xFFBDBDBD);
 
 const appCodeColor = ThemedColorPair(
-  background: ThemedColor(
-    light: Color(0xFFFA7B17),
-    dark: Color(0xFFFCAD70),
-  ),
+  background: ThemedColor(light: Color(0xFFFA7B17), dark: Color(0xFFFCAD70)),
   foreground: ThemedColor.fromSingle(Color(0xFF202124)),
 );
 
 const nativeCodeColor = ThemedColorPair(
-  background: ThemedColor(
-    light: Color(0xFF007B83),
-    dark: Color(0xFF72B6C6),
-  ),
-  foreground: ThemedColor(
-    light: Color(0xFFF8F9FA),
-    dark: Color(0xFF202124),
-  ),
+  background: ThemedColor(light: Color(0xFF007B83), dark: Color(0xFF72B6C6)),
+  foreground: ThemedColor(light: Color(0xFFF8F9FA), dark: Color(0xFF202124)),
 );
 
 const flutterCoreColor = ThemedColorPair(
-  background: ThemedColor(
-    light: Color(0xFF6864D3),
-    dark: Color(0xFF928EF9),
-  ),
-  foreground: ThemedColor(
-    light: Color(0xFFF8F9FA),
-    dark: Color(0xFF202124),
-  ),
+  background: ThemedColor(light: Color(0xFF6864D3), dark: Color(0xFF928EF9)),
+  foreground: ThemedColor(light: Color(0xFFF8F9FA), dark: Color(0xFF202124)),
 );
 
 const dartCoreColor = ThemedColorPair(
-  background: ThemedColor(
-    light: Color(0xFF1D649C),
-    dark: Color(0xFF6887F7),
-  ),
-  foreground: ThemedColor(
-    light: Color(0xFFF8F9FA),
-    dark: Color(0xFF202124),
-  ),
+  background: ThemedColor(light: Color(0xFF1D649C), dark: Color(0xFF6887F7)),
+  foreground: ThemedColor(light: Color(0xFFF8F9FA), dark: Color(0xFF202124)),
 );
 
 extension SyntaxHighlightingExtension on ColorScheme {
diff --git a/packages/devtools_app/lib/src/shared/common_widgets.dart b/packages/devtools_app/lib/src/shared/ui/common_widgets.dart
similarity index 85%
rename from packages/devtools_app/lib/src/shared/common_widgets.dart
rename to packages/devtools_app/lib/src/shared/ui/common_widgets.dart
index 868d3f6..7cf17b6 100644
--- a/packages/devtools_app/lib/src/shared/common_widgets.dart
+++ b/packages/devtools_app/lib/src/shared/ui/common_widgets.dart
@@ -13,19 +13,18 @@
 import 'package:flutter/services.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../screens/debugger/debugger_controller.dart';
-import 'analytics/analytics.dart' as ga;
-import 'analytics/constants.dart' as gac;
-import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart';
-import 'console/widgets/expandable_variable.dart';
-import 'diagnostics/dart_object_node.dart';
-import 'diagnostics/tree_builder.dart';
-import 'globals.dart';
-import 'primitives/flutter_widgets/linked_scroll_controller.dart';
-import 'primitives/utils.dart';
-import 'routing.dart';
-import 'ui/icons.dart';
-import 'utils.dart';
+import '../analytics/analytics.dart' as ga;
+import '../analytics/constants.dart' as gac;
+import '../config_specific/copy_to_clipboard/copy_to_clipboard.dart';
+import '../console/widgets/expandable_variable.dart';
+import '../diagnostics/dart_object_node.dart';
+import '../diagnostics/tree_builder.dart';
+import '../framework/routing.dart';
+import '../globals.dart';
+import '../primitives/flutter_widgets/linked_scroll_controller.dart';
+import '../primitives/utils.dart';
+import '../utils/utils.dart';
+import 'icons.dart';
 
 double get assumedMonospaceCharacterWidth =>
     scaleByFontFactor(_assumedMonospaceCharacterWidth);
@@ -46,9 +45,10 @@
 TextStyle primaryColor(TextStyle style, BuildContext context) {
   final theme = Theme.of(context);
   return style.copyWith(
-    color: (theme.brightness == Brightness.light)
-        ? theme.primaryColor
-        : theme.colorScheme.secondary,
+    color:
+        (theme.brightness == Brightness.light)
+            ? theme.primaryColor
+            : theme.colorScheme.secondary,
     fontWeight: FontWeight.w400,
   );
 }
@@ -81,13 +81,14 @@
     super.outlined,
     super.tooltipPadding,
   }) : super(
-          onPressed: onPressed != null
-              ? () {
-                  ga.select(gaScreen, gaSelection);
-                  onPressed();
-                }
-              : null,
-        );
+         onPressed:
+             onPressed != null
+                 ? () {
+                   ga.select(gaScreen, gaSelection);
+                   onPressed();
+                 }
+                 : null,
+       );
 
   factory GaDevToolsButton.iconOnly({
     required IconData icon,
@@ -118,10 +119,7 @@
     super.outlined = true,
     super.minScreenWidthForTextBeforeScaling,
     bool iconOnly = false,
-  }) : super(
-          label: iconOnly ? null : 'Pause',
-          icon: Icons.pause,
-        );
+  }) : super(label: iconOnly ? null : 'Pause', icon: Icons.pause);
 }
 
 class ResumeButton extends GaDevToolsButton {
@@ -134,10 +132,7 @@
     super.outlined = true,
     super.minScreenWidthForTextBeforeScaling,
     bool iconOnly = false,
-  }) : super(
-          label: iconOnly ? null : 'Resume',
-          icon: Icons.play_arrow,
-        );
+  }) : super(label: iconOnly ? null : 'Resume', icon: Icons.play_arrow);
 }
 
 /// A button that groups pause and resume controls and automatically manages
@@ -246,11 +241,11 @@
     Color? colorOverride,
     String? labelOverride,
   }) : super(
-          icon: _icon(recording),
-          label: labelOverride ?? _label(recording),
-          color: colorOverride ?? _color(recording),
-          tooltip: tooltipOverride ?? _tooltip(recording),
-        );
+         icon: _icon(recording),
+         label: labelOverride ?? _label(recording),
+         color: colorOverride ?? _color(recording),
+         tooltip: tooltipOverride ?? _tooltip(recording),
+       );
 
   static IconData _icon(bool recording) =>
       recording ? Icons.stop : Icons.fiber_manual_record;
@@ -284,10 +279,10 @@
     super.tooltip = 'Start recording',
     String? labelOverride,
   }) : super(
-          onPressed: recording ? null : onPressed,
-          icon: Icons.fiber_manual_record,
-          label: labelOverride ?? 'Record',
-        );
+         onPressed: recording ? null : onPressed,
+         icon: Icons.fiber_manual_record,
+         label: labelOverride ?? 'Record',
+       );
 }
 
 /// Button to stop recording data.
@@ -306,10 +301,10 @@
     super.minScreenWidthForTextBeforeScaling,
     super.tooltip = 'Stop recording',
   }) : super(
-          onPressed: !recording ? null : onPressed,
-          icon: Icons.stop,
-          label: 'Stop',
-        );
+         onPressed: !recording ? null : onPressed,
+         icon: Icons.stop,
+         label: 'Stop',
+       );
 }
 
 class SettingsOutlinedButton extends GaDevToolsButton {
@@ -329,10 +324,7 @@
     required super.gaSelection,
     required super.onPressed,
     super.outlined = true,
-  }) : super(
-          icon: Icons.help_outline,
-          tooltip: 'Help',
-        );
+  }) : super(icon: Icons.help_outline, tooltip: 'Help');
 }
 
 class ExpandAllButton extends StatelessWidget {
@@ -428,7 +420,7 @@
   Widget build(BuildContext context) {
     return ValueListenableBuilder<bool>(
       valueListenable: show,
-      builder: (_, show, __) {
+      builder: (_, show, _) {
         return GaDevToolsButton(
           key: key,
           tooltip: tooltip,
@@ -511,9 +503,7 @@
           const SizedBox(height: defaultSpacing),
           SizedBox(
             width: 200.0,
-            child: LinearProgressIndicator(
-              value: progressValue,
-            ),
+            child: LinearProgressIndicator(value: progressValue),
           ),
         ],
       ),
@@ -571,9 +561,7 @@
                 padding: const EdgeInsets.only(right: defaultSpacing),
                 child: ExitOfflineButton(gaScreen: gaScreen),
               ),
-            Expanded(
-              child: controlsBuilder(offline),
-            ),
+            Expanded(child: controlsBuilder(offline)),
           ],
         );
       },
@@ -643,6 +631,41 @@
 
   @override
   Widget build(BuildContext context) {
+    return SmallAction(
+      onPressed: onPressed,
+      tooltip: tooltip,
+      style: style,
+      gaScreen: gaScreen,
+      gaSelection: gaSelection,
+      child: Icon(
+        icon,
+        size: size ?? actionsIconSize,
+        color: color ?? Theme.of(context).colorScheme.onSurface,
+      ),
+    );
+  }
+}
+
+class SmallAction extends StatelessWidget {
+  const SmallAction({
+    required this.child,
+    required this.onPressed,
+    this.tooltip,
+    super.key,
+    this.style,
+    this.gaScreen,
+    this.gaSelection,
+  }) : assert((gaScreen == null) == (gaSelection == null));
+
+  final TextStyle? style;
+  final Widget child;
+  final String? tooltip;
+  final VoidCallback? onPressed;
+  final String? gaScreen;
+  final String? gaSelection;
+
+  @override
+  Widget build(BuildContext context) {
     final button = TextButton(
       style: TextButton.styleFrom(
         padding: EdgeInsets.zero,
@@ -655,19 +678,12 @@
         }
         onPressed?.call();
       },
-      child: Icon(
-        icon,
-        size: size ?? actionsIconSize,
-        color: color ?? Theme.of(context).colorScheme.onSurface,
-      ),
+      child: child,
     );
 
     return tooltip == null
         ? button
-        : DevToolsTooltip(
-            message: tooltip,
-            child: button,
-          );
+        : DevToolsTooltip(message: tooltip, child: button);
   }
 }
 
@@ -681,9 +697,9 @@
     this.iconAsset,
     this.color,
   }) : assert(
-          (icon == null) != (iconAsset == null),
-          'Exactly one of icon and iconAsset must be specified.',
-        );
+         (icon == null) != (iconAsset == null),
+         'Exactly one of icon and iconAsset must be specified.',
+       );
 
   /// The icon to use for this scaffold action.
   ///
@@ -776,10 +792,7 @@
         ),
       ]);
     }
-    return Row(
-      mainAxisSize: MainAxisSize.min,
-      children: childrenWithOutlines,
-    );
+    return Row(mainAxisSize: MainAxisSize.min, children: childrenWithOutlines);
   }
 }
 
@@ -798,9 +811,9 @@
 }
 
 BoxDecoration roundedBorderDecoration(BuildContext context) => BoxDecoration(
-      border: Border.all(color: Theme.of(context).focusColor),
-      borderRadius: defaultBorderRadius,
-    );
+  border: Border.all(color: Theme.of(context).focusColor),
+  borderRadius: defaultBorderRadius,
+);
 
 class LeftBorder extends StatelessWidget {
   const LeftBorder({super.key, this.child});
@@ -809,8 +822,9 @@
 
   @override
   Widget build(BuildContext context) {
-    final leftBorder =
-        Border(left: BorderSide(color: Theme.of(context).focusColor));
+    final leftBorder = Border(
+      left: BorderSide(color: Theme.of(context).focusColor),
+    );
 
     return Container(
       decoration: BoxDecoration(border: leftBorder),
@@ -828,11 +842,8 @@
 ///
 /// Only one of [message] or [richMessage] can be specified.
 class CenteredMessage extends StatelessWidget {
-  const CenteredMessage({
-    this.message,
-    this.richMessage,
-    super.key,
-  }) : assert((message == null) != (richMessage == null));
+  const CenteredMessage({this.message, this.richMessage, super.key})
+    : assert((message == null) != (richMessage == null));
 
   final String? message;
 
@@ -848,9 +859,7 @@
         style: Theme.of(context).regularTextStyle,
       );
     } else {
-      child = RichText(
-        text: TextSpan(children: richMessage),
-      );
+      child = RichText(text: TextSpan(children: richMessage));
     }
     return Center(child: child);
   }
@@ -863,17 +872,11 @@
 
   @override
   Widget build(BuildContext context) {
-    const indicator = Center(
-      child: CircularProgressIndicator(),
-    );
+    const indicator = Center(child: CircularProgressIndicator());
 
     if (size == null) return indicator;
 
-    return SizedBox(
-      width: size,
-      height: size,
-      child: indicator,
-    );
+    return SizedBox(width: size, height: size, child: indicator);
   }
 }
 
@@ -1073,34 +1076,27 @@
     } else {
       displayText = text;
     }
-    return SelectableText(
-      displayText,
-      style: style,
-    );
+    return SelectableText(displayText, style: style);
   }
 }
 
 class JsonViewer extends StatefulWidget {
-  const JsonViewer({
-    super.key,
-    required this.encodedJson,
-  });
+  JsonViewer({super.key, required this.encodedJson, this.scrollable = true})
+    : assert(encodedJson.isNotEmpty);
 
   final String encodedJson;
+  final bool scrollable;
 
   @override
   State<JsonViewer> createState() => _JsonViewerState();
 }
 
-class _JsonViewerState extends State<JsonViewer>
-    with ProvidedControllerMixin<DebuggerController, JsonViewer> {
+class _JsonViewerState extends State<JsonViewer> {
   late Future<void> _initializeTree;
   late DartObjectNode variable;
   static const jsonEncoder = JsonEncoder.withIndent('  ');
 
-  Future<void> _buildAndExpand(
-    DartObjectNode variable,
-  ) async {
+  Future<void> _buildAndExpand(DartObjectNode variable) async {
     // Build the root node
     await buildVariablesTree(variable);
     // Build the contents of all children
@@ -1142,7 +1138,9 @@
   @override
   void didUpdateWidget(JsonViewer oldWidget) {
     super.didUpdateWidget(oldWidget);
-    _updateVariablesTree();
+    if (oldWidget.encodedJson != widget.encodedJson) {
+      _updateVariablesTree();
+    }
   }
 
   @override
@@ -1150,52 +1148,40 @@
     super.dispose();
     // Remove the JSON object from the fake service cache to avoid holding on
     // to large objects indefinitely.
-    serviceConnection.serviceManager.service!.fakeServiceCache
-        .removeJsonObject(variable.value as Instance);
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    // Currently a redundant check, but adding it anyway to prevent future
-    // bugs being introduced.
-    if (!initController()) {
-      return;
-    }
-    // Any additional initialization code should be added after this line.
+    serviceConnection.serviceManager.service!.fakeServiceCache.removeJsonObject(
+      variable.value as Instance,
+    );
   }
 
   @override
   Widget build(BuildContext context) {
+    Widget child = FutureBuilder(
+      future: _initializeTree,
+      builder: (context, snapshot) {
+        if (snapshot.connectionState != ConnectionState.done) {
+          return Container();
+        }
+        return ExpandableVariable(
+          variable: variable,
+          onCopy: (copiedVariable) {
+            unawaited(
+              copyToClipboard(
+                jsonEncoder.convert(
+                  serviceConnection.serviceManager.service!.fakeServiceCache
+                      .instanceToJson(copiedVariable.value as Instance),
+                ),
+                successMessage: 'JSON copied to clipboard',
+              ),
+            );
+          },
+        );
+      },
+    );
+    if (widget.scrollable) {
+      child = SingleChildScrollView(child: child);
+    }
     return SelectionArea(
-      child: Padding(
-        padding: const EdgeInsets.all(denseSpacing),
-        child: SingleChildScrollView(
-          child: FutureBuilder(
-            future: _initializeTree,
-            builder: (context, snapshot) {
-              if (snapshot.connectionState != ConnectionState.done) {
-                return Container();
-              }
-              return ExpandableVariable(
-                variable: variable,
-                onCopy: (copiedVariable) {
-                  unawaited(
-                    copyToClipboard(
-                      jsonEncoder.convert(
-                        serviceConnection
-                            .serviceManager.service!.fakeServiceCache
-                            .instanceToJson(copiedVariable.value as Instance),
-                      ),
-                      successMessage: 'JSON copied to clipboard',
-                    ),
-                  );
-                },
-              );
-            },
-          ),
-        ),
-      ),
+      child: Padding(padding: const EdgeInsets.all(denseSpacing), child: child),
     );
   }
 }
@@ -1229,10 +1215,7 @@
           mainAxisSize: MainAxisSize.min,
           mainAxisAlignment: MainAxisAlignment.end,
           children: [
-            Text(
-              'More info',
-              style: theme.linkTextStyle,
-            ),
+            Text('More info', style: theme.linkTextStyle),
             const SizedBox(width: densePadding),
             Icon(
               Icons.launch,
@@ -1270,11 +1253,7 @@
       child: Row(
         mainAxisSize: MainAxisSize.min,
         children: [
-          Icon(
-            icon,
-            size: defaultIconSize,
-            color: color,
-          ),
+          Icon(icon, size: defaultIconSize, color: color),
           const SizedBox(width: densePadding),
           Padding(
             padding: const EdgeInsets.only(bottom: densePadding),
@@ -1304,17 +1283,14 @@
     required super.context,
     TextStyle? style,
   }) : super(
-          link: link,
-          onTap: () {
-            if (link.gaScreenName != null &&
-                link.gaSelectedItemDescription != null) {
-              ga.select(
-                link.gaScreenName!,
-                link.gaSelectedItemDescription!,
-              );
-            }
-          },
-        );
+         link: link,
+         onTap: () {
+           if (link.gaScreenName != null &&
+               link.gaSelectedItemDescription != null) {
+             ga.select(link.gaScreenName!, link.gaSelectedItemDescription!);
+           }
+         },
+       );
 }
 
 class GaLink extends Link {
@@ -1330,11 +1306,7 @@
 }
 
 class Legend extends StatelessWidget {
-  const Legend({
-    super.key,
-    required this.entries,
-    this.dense = false,
-  });
+  const Legend({super.key, required this.entries, this.dense = false});
 
   double get legendSquareSize =>
       dense ? scaleByFontFactor(12.0) : scaleByFontFactor(16.0);
@@ -1347,13 +1319,7 @@
   Widget build(BuildContext context) {
     final textStyle = dense ? Theme.of(context).legendTextStyle : null;
     final legendItems = entries
-        .map(
-          (entry) => _legendItem(
-            entry.description,
-            entry.color,
-            textStyle,
-          ),
-        )
+        .map((entry) => _legendItem(entry.description, entry.color, textStyle))
         .toList()
         .joinWith(const SizedBox(height: denseRowSpacing));
     return Column(
@@ -1371,10 +1337,7 @@
           color: color,
         ),
         const SizedBox(width: denseSpacing),
-        Text(
-          description,
-          style: style,
-        ),
+        Text(description, style: style),
       ],
     );
   }
@@ -1416,19 +1379,20 @@
 
   @override
   Widget build(BuildContext context) {
-    final onPressed = dataProvider == null
-        ? null
-        : () {
-            if (gaScreen != null && gaItem != null) {
-              ga.select(gaScreen!, gaItem!);
-            }
-            unawaited(
-              copyToClipboard(
-                dataProvider!() ?? '',
-                successMessage: successMessage,
-              ),
-            );
-          };
+    final onPressed =
+        dataProvider == null
+            ? null
+            : () {
+              if (gaScreen != null && gaItem != null) {
+                ga.select(gaScreen!, gaItem!);
+              }
+              unawaited(
+                copyToClipboard(
+                  dataProvider!() ?? '',
+                  successMessage: successMessage,
+                ),
+              );
+            };
     final size = this.size ?? defaultIconSize;
     return SizedBox(
       height: size,
@@ -1648,10 +1612,7 @@
               child: Row(
                 children: [
                   RichText(
-                    text: TextSpan(
-                      text: ' • ',
-                      style: theme.subtleTextStyle,
-                    ),
+                    text: TextSpan(text: ' • ', style: theme.subtleTextStyle),
                   ),
                   Flexible(
                     child: RichText(
@@ -1715,18 +1676,12 @@
       child: Row(
         mainAxisSize: MainAxisSize.min,
         children: [
-          if (isScreenWiderThan(
-            context,
-            minScreenWidthForTextBeforeScaling,
-          ))
+          if (isScreenWiderThan(context, minScreenWidthForTextBeforeScaling))
             Flexible(
               child: RichText(
                 overflow: TextOverflow.visible,
                 maxLines: 3,
-                text: TextSpan(
-                  text: title,
-                  style: theme.regularTextStyle,
-                ),
+                text: TextSpan(text: title, style: theme.regularTextStyle),
               ),
             ),
           NotifierSwitch(
@@ -1764,25 +1719,21 @@
     final minSdkVersion = isFlutterApp ? '2.8.0' : '2.15.0';
     return SelectableText.rich(
       TextSpan(
-        text: 'Warning: you should no longer be launching DevTools from'
+        text:
+            'Warning: you should no longer be launching DevTools from'
             ' pub.\n\n',
         style: theme.subtleTextStyle.copyWith(color: theme.colorScheme.error),
         children: [
           TextSpan(
-            text: 'DevTools version 2.8.0 will be the last version to '
+            text:
+                'DevTools version 2.8.0 will be the last version to '
                 'be shipped on pub. As of $sdkName\nversion >= '
                 '$minSdkVersion, DevTools should be launched by running '
                 'the ',
             style: theme.subtleTextStyle,
           ),
-          TextSpan(
-            text: '`dart devtools`',
-            style: theme.subtleFixedFontStyle,
-          ),
-          TextSpan(
-            text: '\ncommand.',
-            style: theme.subtleTextStyle,
-          ),
+          TextSpan(text: '`dart devtools`', style: theme.subtleFixedFontStyle),
+          TextSpan(text: '\ncommand.', style: theme.subtleTextStyle),
         ],
       ),
     );
@@ -1841,11 +1792,7 @@
   }
 
   Widget _icon({Color? color}) {
-    return Icon(
-      widget.icon,
-      size: widget.size,
-      color: color,
-    );
+    return Icon(widget.icon, size: widget.size, color: color);
   }
 }
 
@@ -1872,7 +1819,8 @@
     BuildContext context,
     List<Object?> values,
     Widget? child,
-  ) builder;
+  )
+  builder;
 
   final Widget? child;
 
@@ -1882,7 +1830,8 @@
 }
 
 class _MultiValueListenableBuilderState
-    extends State<MultiValueListenableBuilder> with AutoDisposeMixin {
+    extends State<MultiValueListenableBuilder>
+    with AutoDisposeMixin {
   @override
   void initState() {
     super.initState();
@@ -1891,28 +1840,20 @@
 
   @override
   Widget build(BuildContext context) {
-    return widget.builder(
-      context,
-      [for (final listenable in widget.listenables) listenable.value],
-      widget.child,
-    );
+    return widget.builder(context, [
+      for (final listenable in widget.listenables) listenable.value,
+    ], widget.child);
   }
 }
 
 class SmallCircularProgressIndicator extends StatelessWidget {
-  const SmallCircularProgressIndicator({
-    super.key,
-    required this.valueColor,
-  });
+  const SmallCircularProgressIndicator({super.key, required this.valueColor});
 
   final Animation<Color?> valueColor;
 
   @override
   Widget build(BuildContext context) {
-    return CircularProgressIndicator(
-      strokeWidth: 2,
-      valueColor: valueColor,
-    );
+    return CircularProgressIndicator(strokeWidth: 2, valueColor: valueColor);
   }
 }
 
@@ -1935,9 +1876,7 @@
     return Card(
       elevation: defaultElevation,
       color: Theme.of(context).scaffoldBackgroundColor,
-      shape: RoundedRectangleBorder(
-        borderRadius: defaultBorderRadius,
-      ),
+      shape: RoundedRectangleBorder(borderRadius: defaultBorderRadius),
       child: Container(
         width: width,
         height: height,
@@ -2059,10 +1998,7 @@
     return Padding(
       padding: const EdgeInsets.symmetric(horizontal: _paddingWidth),
       child: OutlineDecoration.onlyLeft(
-        child: SizedBox(
-          width: _lineWidth,
-          height: height,
-        ),
+        child: SizedBox(width: _lineWidth, height: height),
       ),
     );
   }
@@ -2160,8 +2096,11 @@
   Widget build(BuildContext context) {
     return MenuAnchor(
       menuChildren: menuChildren,
-      builder:
-          (BuildContext context, MenuController controller, Widget? child) {
+      builder: (
+        BuildContext context,
+        MenuController controller,
+        Widget? child,
+      ) {
         return SizedBox(
           width: buttonWidth,
           child: ToolbarAction(
@@ -2185,18 +2124,22 @@
   }
 }
 
-/// A Widget for displaying a setting field, that sets and integer value.
+/// A Widget for displaying a setting field that sets an integer value.
 class PositiveIntegerSetting extends StatefulWidget {
   const PositiveIntegerSetting({
     super.key,
     required this.title,
     required this.subTitle,
     required this.notifier,
+    this.minimumValue = 0,
+    this.width = 150.0,
   });
 
   final String title;
   final String subTitle;
   final ValueNotifier<int> notifier;
+  final int minimumValue;
+  final double width;
 
   @override
   State<PositiveIntegerSetting> createState() => _PositiveIntegerSettingState();
@@ -2206,6 +2149,8 @@
     with AutoDisposeMixin {
   late final TextEditingController _textEditingController;
 
+  final _formKey = GlobalKey<FormState>();
+
   @override
   void initState() {
     super.initState();
@@ -2215,8 +2160,8 @@
       () => _textEditingController.text = widget.notifier.value.toString(),
     );
 
-    _textEditingController = TextEditingController()
-      ..text = widget.notifier.value.toString();
+    _textEditingController =
+        TextEditingController()..text = widget.notifier.value.toString();
   }
 
   @override
@@ -2228,38 +2173,62 @@
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
+    final textFieldValidationText = 'Enter an integer > ${widget.minimumValue}';
     return Row(
+      crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         Expanded(
           child: Column(
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
               Text(widget.title),
-              Text(
-                widget.subTitle,
-                style: theme.subtleTextStyle,
-              ),
+              Text(widget.subTitle, style: theme.subtleTextStyle),
             ],
           ),
         ),
         const SizedBox(width: defaultSpacing),
         SizedBox(
-          height: defaultTextFieldHeight,
-          width: defaultTextFieldNumberWidth,
-          child: TextField(
-            textAlignVertical: TextAlignVertical.top,
-            style: theme.regularTextStyle,
-            decoration: singleLineDialogTextFieldDecoration,
-            controller: _textEditingController,
-            inputFormatters: <TextInputFormatter>[
-              // Only positive integers.
-              FilteringTextInputFormatter.allow(
-                RegExp(r'^[1-9][0-9]*'),
+          width: scaleByFontFactor(widget.width),
+          child: Form(
+            key: _formKey,
+            child: TextFormField(
+              enableInteractiveSelection: false,
+              textAlignVertical: TextAlignVertical.top,
+              style: theme.regularTextStyle,
+              controller: _textEditingController,
+              inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+              decoration: InputDecoration(
+                labelText: 'Integer > ${widget.minimumValue}',
+                border: const OutlineInputBorder(),
+                errorBorder: OutlineInputBorder(
+                  borderSide: BorderSide(color: theme.colorScheme.error),
+                ),
+                focusedErrorBorder: OutlineInputBorder(
+                  borderSide: BorderSide(color: theme.colorScheme.error),
+                ),
               ),
-            ],
-            onChanged: (String text) {
-              widget.notifier.value = int.parse(text);
-            },
+              validator: (value) {
+                if (value.isNullOrEmpty) {
+                  return textFieldValidationText;
+                }
+                final intValue = int.tryParse(value!);
+                if (intValue == null || intValue < widget.minimumValue) {
+                  return textFieldValidationText;
+                }
+                return null;
+              },
+              onChanged: (String text) {
+                if (_formKey.currentState!.validate()) {
+                  int value;
+                  try {
+                    value = int.parse(text);
+                  } catch (_) {
+                    value = 0;
+                  }
+                  widget.notifier.value = value;
+                }
+              },
+            ),
           ),
         ),
       ],
diff --git a/packages/devtools_app/lib/src/shared/ui/dialogs.dart b/packages/devtools_app/lib/src/shared/ui/dialogs.dart
new file mode 100644
index 0000000..e6b7119
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/ui/dialogs.dart
@@ -0,0 +1,53 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+
+import '../config_specific/copy_to_clipboard/copy_to_clipboard.dart';
+import '../globals.dart';
+import '../utils/utils.dart';
+
+/// A dialog, that reports unexpected error and allows to copy details and create issue.
+class UnexpectedErrorDialog extends StatelessWidget {
+  const UnexpectedErrorDialog({super.key, required this.additionalInfo});
+
+  final String additionalInfo;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+
+    return DevToolsDialog(
+      title: const Text('Unexpected Error'),
+      content: Text(additionalInfo, style: theme.fixedFontStyle),
+      actions: [
+        DialogTextButton(
+          child: const Text('Copy details'),
+          onPressed:
+              () => unawaited(
+                copyToClipboard(
+                  additionalInfo,
+                  successMessage: 'Error details copied to clipboard',
+                ),
+              ),
+        ),
+        DialogTextButton(
+          child: const Text('Create issue'),
+          onPressed:
+              () => unawaited(
+                launchUrlWithErrorHandling(
+                  devToolsEnvironmentParameters
+                      .issueTrackerLink(additionalInfo: additionalInfo)
+                      .url,
+                ),
+              ),
+        ),
+        const DialogCloseButton(),
+      ],
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/shared/editable_list.dart b/packages/devtools_app/lib/src/shared/ui/editable_list.dart
similarity index 87%
rename from packages/devtools_app/lib/src/shared/editable_list.dart
rename to packages/devtools_app/lib/src/shared/ui/editable_list.dart
index 988454a..0de3f35 100644
--- a/packages/devtools_app/lib/src/shared/editable_list.dart
+++ b/packages/devtools_app/lib/src/shared/ui/editable_list.dart
@@ -9,8 +9,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
+import '../config_specific/copy_to_clipboard/copy_to_clipboard.dart';
 import 'common_widgets.dart';
-import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart';
 
 /// A widget that displays the contents of [entries].
 ///
@@ -27,8 +27,8 @@
     this.onRefreshTriggered,
     void Function(String)? onEntryAdded,
     void Function(String)? onEntryRemoved,
-  })  : onEntryAdded = onEntryAdded ?? ((entry) => entries.add(entry)),
-        onEntryRemoved = onEntryRemoved ?? ((entry) => entries.remove(entry));
+  }) : onEntryAdded = onEntryAdded ?? ((entry) => entries.add(entry)),
+       onEntryRemoved = onEntryRemoved ?? ((entry) => entries.remove(entry));
 
   /// The values that will be displayed in the editable list.
   ///
@@ -90,7 +90,7 @@
         widget.entries,
         widget.isRefreshing ?? ValueNotifier<bool>(false),
       ],
-      builder: (_, __, ____) {
+      builder: (_, _, _) {
         return Column(
           mainAxisSize: MainAxisSize.min,
           crossAxisAlignment: CrossAxisAlignment.start,
@@ -179,19 +179,19 @@
           const SizedBox(width: densePadding),
           isRefreshing?.value ?? false
               ? SizedBox(
-                  width: defaultTextFieldHeight,
-                  height: defaultTextFieldHeight,
-                  child: const Padding(
-                    padding: EdgeInsets.all(densePadding),
-                    child: CircularProgressIndicator(),
-                  ),
-                )
-              : RefreshButton(
-                  gaScreen: gaScreen,
-                  gaSelection: gaRefreshSelection,
-                  onPressed: onRefresh,
-                  minScreenWidthForTextBeforeScaling: double.maxFinite,
+                width: defaultTextFieldHeight,
+                height: defaultTextFieldHeight,
+                child: const Padding(
+                  padding: EdgeInsets.all(densePadding),
+                  child: CircularProgressIndicator(),
                 ),
+              )
+              : RefreshButton(
+                gaScreen: gaScreen,
+                gaSelection: gaRefreshSelection,
+                onPressed: onRefresh,
+                minScreenWidthForTextBeforeScaling: double.maxFinite,
+              ),
         ],
       ),
     );
@@ -242,18 +242,12 @@
   Widget build(BuildContext context) {
     return Row(
       children: [
-        Expanded(
-          child: Text(entry),
-        ),
-        EditableListCopyDirectoryButton(
-          value: entry,
-        ),
+        Expanded(child: Text(entry)),
+        EditableListCopyDirectoryButton(value: entry),
         const SizedBox(width: denseSpacing),
         EditableListRemoveDirectoryButton(
           onPressed: () {
-            onEntryRemoved(
-              entry,
-            );
+            onEntryRemoved(entry);
           },
         ),
       ],
@@ -263,10 +257,7 @@
 
 @visibleForTesting
 class EditableListCopyDirectoryButton extends StatelessWidget {
-  const EditableListCopyDirectoryButton({
-    super.key,
-    required this.value,
-  });
+  const EditableListCopyDirectoryButton({super.key, required this.value});
 
   final String value;
 
diff --git a/packages/devtools_app/lib/src/shared/file_import.dart b/packages/devtools_app/lib/src/shared/ui/file_import.dart
similarity index 88%
rename from packages/devtools_app/lib/src/shared/file_import.dart
rename to packages/devtools_app/lib/src/shared/ui/file_import.dart
index 79b36cb..d5ad85c 100644
--- a/packages/devtools_app/lib/src/shared/file_import.dart
+++ b/packages/devtools_app/lib/src/shared/ui/file_import.dart
@@ -9,13 +9,13 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import 'analytics/analytics.dart' as ga;
-import 'analytics/constants.dart' as gac;
+import '../analytics/analytics.dart' as ga;
+import '../analytics/constants.dart' as gac;
+import '../config_specific/drag_and_drop/drag_and_drop.dart';
+import '../config_specific/import_export/import_export.dart';
+import '../globals.dart';
+import '../primitives/utils.dart';
 import 'common_widgets.dart';
-import 'config_specific/drag_and_drop/drag_and_drop.dart';
-import 'config_specific/import_export/import_export.dart';
-import 'globals.dart';
-import 'primitives/utils.dart';
 
 class OpenSaveButtonGroup extends StatelessWidget {
   const OpenSaveButtonGroup({
@@ -37,12 +37,15 @@
           tooltip: 'Open a file that was previously saved from DevTools',
           onPressed: () async {
             ga.select(screenId, gac.openFile);
-            final importedFile =
-                await importFileFromPicker(acceptedTypes: const ['json']);
+            final importedFile = await importFileFromPicker(
+              acceptedTypes: const ['json'],
+            );
             if (importedFile != null) {
-              // ignore: use_build_context_synchronously, intentional use.
-              Provider.of<ImportController>(context, listen: false)
-                  .importData(importedFile, expectedScreenId: screenId);
+              Provider.of<ImportController>(
+                // ignore: use_build_context_synchronously, intentional use.
+                context,
+                listen: false,
+              ).importData(importedFile, expectedScreenId: screenId);
             } else {
               notificationService.push(
                 'Something went wrong. Could not open selected file.',
@@ -53,12 +56,13 @@
         ButtonGroupItemData(
           icon: Icons.file_download,
           tooltip: 'Save this screen\'s data for offline viewing',
-          onPressed: onSave != null
-              ? () {
-                  ga.select(screenId, gac.saveFile);
-                  onSave!.call();
-                }
-              : null,
+          onPressed:
+              onSave != null
+                  ? () {
+                    ga.select(screenId, gac.saveFile);
+                    onSave!.call();
+                  }
+                  : null,
         ),
       ],
     );
@@ -122,10 +126,7 @@
       mainAxisAlignment: MainAxisAlignment.center,
       children: [
         if (title != null) ...[
-          Text(
-            title,
-            style: TextStyle(fontSize: scaleByFontFactor(18.0)),
-          ),
+          Text(title, style: TextStyle(fontSize: scaleByFontFactor(18.0))),
           const SizedBox(height: extraLargeSpacing),
         ],
         CenteredMessage(message: widget.instructions),
@@ -156,10 +157,7 @@
       children: [
         Expanded(
           // TODO(kenz): improve drag over highlight.
-          child: DragAndDrop(
-            handleDrop: _handleImportedFile,
-            child: child,
-          ),
+          child: DragAndDrop(handleDrop: _handleImportedFile, child: child),
         ),
       ],
     );
@@ -226,9 +224,10 @@
               gaSelection: widget.gaSelectionAction!,
               label: widget.actionText!,
               elevated: true,
-              onPressed: importedFile != null
-                  ? () => widget.onAction!(importedFile!)
-                  : null,
+              onPressed:
+                  importedFile != null
+                      ? () => widget.onAction!(importedFile!)
+                      : null,
             ),
           ],
         ),
@@ -237,8 +236,9 @@
   }
 
   Future<void> _importFile() async {
-    final importedFile =
-        await importFileFromPicker(acceptedTypes: widget.extensions);
+    final importedFile = await importFileFromPicker(
+      acceptedTypes: widget.extensions,
+    );
     if (importedFile != null) {
       _handleImportedFile(importedFile);
     }
@@ -357,7 +357,8 @@
     DevToolsJsonFile firstImportedFile,
     DevToolsJsonFile secondImportedFile,
     void Function(String error) onError,
-  ) onAction;
+  )
+  onAction;
 
   @override
   State<DualFileImportContainer> createState() =>
@@ -450,13 +451,14 @@
               label: widget.actionText,
               icon: Icons.highlight,
               elevated: true,
-              onPressed: firstImportedFile != null && secondImportedFile != null
-                  ? () => widget.onAction(
+              onPressed:
+                  firstImportedFile != null && secondImportedFile != null
+                      ? () => widget.onAction(
                         firstImportedFile!,
                         secondImportedFile!,
                         (error) => notificationService.push(error),
                       )
-                  : null,
+                      : null,
             ),
           ],
         ),
diff --git a/packages/devtools_app/lib/src/shared/ui/filter.dart b/packages/devtools_app/lib/src/shared/ui/filter.dart
index 9af60fa..f5daf46 100644
--- a/packages/devtools_app/lib/src/shared/ui/filter.dart
+++ b/packages/devtools_app/lib/src/shared/ui/filter.dart
@@ -3,16 +3,19 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 
 import 'package:codicon/codicon.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
-
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 
-import '../common_widgets.dart';
 import '../primitives/utils.dart';
+import 'common_widgets.dart';
+
+typedef QueryFilterArgs<T> = Map<String, QueryFilterArgument<T>>;
+typedef SettingFilters<T> = List<SettingFilter<T, Object>>;
 
 // TODO(kenz): consider breaking this up for flat data filtering and tree data
 // filtering.
@@ -21,68 +24,87 @@
 /// storing the state of those filters.
 ///
 /// To use this mixin, you must implement [filterData] as well as either or both
-/// [createToggleFilters] and [createQueryFilterArgs].
+/// [createSettingFilters] and [createQueryFilterArgs].
 ///
 /// Classes mixing in [FilterControllerMixin] must also extend
 /// [DisposableController] and mixin [AutoDisposeControllerMixin], and a class
 /// can subscribe to updates to the active filter by calling
-/// [subscribeToFilterChanges].
+/// [initFilterController].
 mixin FilterControllerMixin<T> on DisposableController
     implements AutoDisposeControllerMixin {
-  static const filterTagSeparator = '-#-';
-
-  final filteredData = ListValueNotifier<T>([]);
+  final filteredData = ListValueNotifier<T>(<T>[]);
 
   final useRegExp = ValueNotifier<bool>(false);
 
-  // TODO(kenz): replace [Filter] class with a record when available.
+  /// The notifier that stores the current filter tag in DevTools preferences.
+  ///
+  /// This should be overriden as a getter by subclasses to support persisting
+  /// the most recent filter to DevTools preferences.
+  ValueNotifier<String>? filterTagNotifier;
+
   ValueListenable<Filter<T>> get activeFilter => _activeFilter;
 
   late final _activeFilter = ValueNotifier<Filter<T>>(
     Filter(
-      queryFilter: QueryFilter.empty(args: _queryFilterArgs),
-      toggleFilters: _toggleFilters,
+      queryFilter: QueryFilter.empty(args: queryFilterArgs),
+      settingFilters: settingFilters,
     ),
   );
 
-  void setActiveFilter({
-    String? query,
-    List<ToggleFilter<T>>? toggleFilters,
-  }) {
+  void setActiveFilter({String? query, SettingFilters<T>? settingFilters}) {
     _activeFilter.value = Filter(
-      queryFilter: query != null
-          ? QueryFilter.parse(
-              query,
-              args: _queryFilterArgs,
-              useRegExp: useRegExp.value,
-            )
-          : QueryFilter.empty(args: _queryFilterArgs),
-      toggleFilters: toggleFilters ?? _toggleFilters,
+      queryFilter:
+          query != null
+              ? QueryFilter.parse(
+                query,
+                args: queryFilterArgs,
+                useRegExp: useRegExp.value,
+              )
+              : QueryFilter.empty(args: queryFilterArgs),
+      settingFilters: settingFilters ?? this.settingFilters,
     );
   }
 
-  void subscribeToFilterChanges() {
+  void initFilterController() {
+    if (filterTagNotifier != null) {
+      final tag = FilterTag.parse(filterTagNotifier!.value);
+      setFilterFromTag(tag);
+    }
     addAutoDisposeListener(activeFilter, () {
       filterData(activeFilter.value);
+      filterTagNotifier?.value = activeFilterTag();
     });
   }
 
-  late final List<ToggleFilter<T>> _toggleFilters = createToggleFilters();
+  /// Creates the setting filters for this filter controller.
+  ///
+  /// This method should be overridden by subclasses to support filtering by
+  /// settings (e.g. check box, dropdown selection, etc.).
+  SettingFilters<T> createSettingFilters() => [];
 
-  List<ToggleFilter<T>> createToggleFilters() => [];
+  @visibleForTesting
+  late final SettingFilters<T> settingFilters = createSettingFilters();
 
-  late final _queryFilterArgs = createQueryFilterArgs();
+  /// Creates the query filter arguments for this filter controller.
+  ///
+  /// This method should be overridden by subclasses to support filtering by
+  /// query arguments in addition to raw String matches. For example, a filter
+  /// query with arguments may look like 'foo category:bar type:baz'. In this
+  /// example, 'category' and 'type' would need to be defined as query filter
+  /// arguments.
+  QueryFilterArgs<T> createQueryFilterArgs() =>
+      <String, QueryFilterArgument<T>>{};
 
-  Map<String, QueryFilterArgument> createQueryFilterArgs() =>
-      <String, QueryFilterArgument>{};
+  @visibleForTesting
+  late final queryFilterArgs = createQueryFilterArgs();
 
   bool get isFilterActive {
     final filter = activeFilter.value;
     final queryFilterActive = !filter.queryFilter.isEmpty;
-    final toggleFilterActive = filter.toggleFilters.any(
-      (filter) => filter.enabled.value,
+    final settingFilterActive = filter.settingFilters.any(
+      (filter) => filter.enabled,
     );
-    return queryFilterActive || toggleFilterActive;
+    return queryFilterActive || settingFilterActive;
   }
 
   // TODO(kenz): de-dupe the filtering logic in overrides of this method.
@@ -96,68 +118,85 @@
     _activeFilter.value = filter;
   }
 
+  /// The filter tag as a String for the currently active filter.
+  ///
+  /// See also [FilterTag].
   String activeFilterTag() {
-    final activeFilter = _activeFilter.value;
-    final suffixList = <String>[];
-    for (final toggleFilter in activeFilter.toggleFilters) {
-      if (toggleFilter.enabled.value) {
-        suffixList.add(toggleFilter.name);
+    final filter = _activeFilter.value;
+    return FilterTag(
+      query: filter.queryFilter.query,
+      settingFilterValues:
+          settingFilters.map((filter) => filter.valueAsJson).toList(),
+      useRegExp: useRegExp.value,
+    ).tag;
+  }
+
+  /// Sets the active filter state from the given [tag].
+  ///
+  /// See also [FilterTag].
+  void setFilterFromTag(FilterTag? tag) {
+    if (tag == null) return;
+
+    useRegExp.value = tag.useRegExp;
+
+    final valuesFromTag = tag.settingFilterValues.map((value) {
+      assert(
+        value.length == 1,
+        'Each setting filter map should only have one entry.',
+      );
+      return (id: value.keys.first, value: value.values.first);
+    });
+    final settingFilterIds = settingFilters.map((filter) => filter.id);
+    for (final settingFilterValue in valuesFromTag) {
+      if (settingFilterIds.contains(settingFilterValue.id)) {
+        final settingFilter = settingFilters.firstWhere(
+          (filter) => filter.id == settingFilterValue.id,
+        );
+        settingFilter.setting.value = settingFilterValue.value!;
       }
     }
-    final toggleFilterTag = suffixList.join(',');
-    final queryFilterTag = activeFilter.queryFilter.query.toLowerCase();
-    return [
-      toggleFilterTag,
-      queryFilterTag,
-      if (queryFilterTag.isNotEmpty && useRegExp.value) 'regexp',
-    ].where((e) => e.isNotEmpty).join(filterTagSeparator);
+
+    setActiveFilter(query: tag.query, settingFilters: settingFilters);
   }
 
   void _resetToDefaultFilter() {
     // Reset all filter values.
-    for (final toggleFilter in _toggleFilters) {
-      toggleFilter.enabled.value = toggleFilter.enabledByDefault;
+    for (final settingFilter in settingFilters) {
+      settingFilter.setting.value = settingFilter.defaultValue;
     }
-    _queryFilterArgs.forEach((key, value) => value.reset());
+    queryFilterArgs.forEach((key, value) => value.reset());
   }
 
   void resetFilter() {
     _resetToDefaultFilter();
     _activeFilter.value = Filter(
-      queryFilter: QueryFilter.empty(args: _queryFilterArgs),
-      toggleFilters: _toggleFilters,
+      queryFilter: QueryFilter.empty(args: queryFilterArgs),
+      settingFilters: settingFilters,
     );
   }
 }
 
-/// Dialog to manage toggleable filter settings.
+/// Dialog to manage filter settings.
 ///
 /// This dialog interacts with a [FilterControllerMixin] to manage and preserve
-/// the toggleable filter state managed by the dialog.
+/// the filter state.
 class FilterDialog<T> extends StatefulWidget {
   FilterDialog({
     super.key,
     required this.controller,
-    this.includeQueryFilter = true,
-    this.queryInstructions,
-  })  : assert(
-          !includeQueryFilter ||
-              (queryInstructions != null &&
-                  controller._queryFilterArgs.isNotEmpty),
-        ),
-        toggleFilterValuesAtOpen = List.generate(
-          controller.activeFilter.value.toggleFilters.length,
-          (index) =>
-              controller.activeFilter.value.toggleFilters[index].enabled.value,
-        );
+    required this.filteredItem,
+  }) : assert(controller.queryFilterArgs.isNotEmpty),
+       settingFilterValuesAtOpen = List.generate(
+         controller.activeFilter.value.settingFilters.length,
+         (index) =>
+             controller.activeFilter.value.settingFilters[index].setting.value,
+       );
 
   final FilterControllerMixin<T> controller;
 
-  final String? queryInstructions;
+  final String filteredItem;
 
-  final bool includeQueryFilter;
-
-  final List<bool> toggleFilterValuesAtOpen;
+  final List<Object> settingFilterValuesAtOpen;
 
   @override
   State<FilterDialog<T>> createState() => _FilterDialogState<T>();
@@ -199,31 +238,11 @@
         mainAxisSize: MainAxisSize.min,
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          if (widget.includeQueryFilter) ...[
-            DevToolsClearableTextField(
-              autofocus: true,
-              labelText: 'Filter Query',
-              controller: queryTextFieldController,
-              additionalSuffixActions: [
-                DevToolsToggleButton(
-                  icon: Codicons.regex,
-                  message: 'Use regular expressions',
-                  outlined: false,
-                  isSelected: pendingUseRegExp,
-                  onPressed: () => setState(() {
-                    pendingUseRegExp = !pendingUseRegExp;
-                  }),
-                ),
-              ],
-            ),
-            const SizedBox(height: defaultSpacing),
-            if (widget.queryInstructions != null) ...[
-              DialogHelpText(helpText: widget.queryInstructions!),
-              const SizedBox(height: defaultSpacing),
-            ],
-          ],
-          for (final toggleFilter in widget.controller._toggleFilters) ...[
-            ToggleFilterElement(filter: toggleFilter),
+          for (final filter in widget.controller.settingFilters) ...[
+            if (filter is ToggleFilter<T>)
+              _ToggleFilterElement(filter: filter)
+            else
+              _SettingFilterElement(filter: filter),
           ],
         ],
       ),
@@ -234,10 +253,8 @@
     widget.controller
       ..useRegExp.value = pendingUseRegExp
       ..setActiveFilter(
-        query: widget.includeQueryFilter
-            ? queryTextFieldController.value.text
-            : null,
-        toggleFilters: widget.controller._toggleFilters,
+        query: widget.controller.activeFilter.value.queryFilter.query,
+        settingFilters: widget.controller.settingFilters,
       );
   }
 
@@ -247,66 +264,186 @@
   }
 
   void _restoreOldValues() {
-    for (var i = 0; i < widget.controller._toggleFilters.length; i++) {
-      final filter = widget.controller._toggleFilters[i];
-      filter.enabled.value = widget.toggleFilterValuesAtOpen[i];
+    for (var i = 0; i < widget.controller.settingFilters.length; i++) {
+      final filter = widget.controller.settingFilters[i];
+      filter.setting.value = widget.settingFilterValuesAtOpen[i];
     }
   }
 }
 
-class ToggleFilterElement extends StatelessWidget {
-  const ToggleFilterElement({super.key, required this.filter});
+class _ToggleFilterElement extends StatelessWidget {
+  const _ToggleFilterElement({required this.filter});
 
   final ToggleFilter filter;
 
   @override
   Widget build(BuildContext context) {
     Widget content = InkWell(
-      onTap: () => filter.enabled.value = !filter.enabled.value,
+      onTap: () => filter.setting.value = !filter.setting.value,
       child: Row(
         children: [
-          NotifierCheckbox(notifier: filter.enabled),
+          NotifierCheckbox(notifier: filter.setting),
           Text(filter.name),
         ],
       ),
     );
     if (filter.tooltip != null) {
-      content = DevToolsTooltip(
-        message: filter.tooltip,
-        child: content,
-      );
+      content = DevToolsTooltip(message: filter.tooltip, child: content);
+    }
+    return content;
+  }
+}
+
+class _SettingFilterElement extends StatelessWidget {
+  const _SettingFilterElement({required this.filter});
+
+  final SettingFilter filter;
+
+  static const _leadingInset = 6.0;
+
+  @override
+  Widget build(BuildContext context) {
+    Widget content = Padding(
+      // This padding is required to left-align [_SettingFilterElement]s with
+      // [_ToggleFilterElement] checkboxes in the dialog.
+      padding: const EdgeInsets.only(left: _leadingInset),
+      child: Row(
+        children: [
+          Text(filter.name),
+          const BulletSpacer(),
+          ValueListenableBuilder(
+            valueListenable: filter.setting,
+            builder: (context, value, _) {
+              return RoundedDropDownButton(
+                value: value,
+                items: [
+                  for (int i = 0; i < filter.possibleValues.length; i++)
+                    DropdownMenuItem(
+                      value: filter.possibleValues[i],
+                      child: Text(
+                        '${filter.possibleValueDisplays?[i] ?? filter.possibleValues[i]}',
+                      ),
+                    ),
+                ],
+                onChanged: (value) => filter.setting.value = value!,
+              );
+            },
+          ),
+        ],
+      ),
+    );
+    if (filter.tooltip != null) {
+      content = DevToolsTooltip(message: filter.tooltip, child: content);
     }
     return content;
   }
 }
 
 class Filter<T> {
-  Filter({required this.queryFilter, required this.toggleFilters});
+  Filter({required this.queryFilter, required this.settingFilters});
 
   final QueryFilter queryFilter;
 
-  final List<ToggleFilter<T>> toggleFilters;
+  final SettingFilters<T> settingFilters;
 
-  bool get isEmpty => queryFilter.isEmpty && toggleFilters.isEmpty;
+  bool get isEmpty => queryFilter.isEmpty && settingFilters.isEmpty;
 }
 
-class ToggleFilter<T> {
+/// A boolean setting filter that can only be set to the value of true or false.
+class ToggleFilter<T> extends SettingFilter<T, bool> {
   ToggleFilter({
-    required this.name,
-    required this.includeCallback,
-    this.tooltip,
-    this.enabledByDefault = false,
-  }) : enabled = ValueNotifier<bool>(enabledByDefault);
+    required super.id,
+    required super.name,
+    required bool Function(T element) includeCallback,
+    required super.defaultValue,
+    super.tooltip,
+  }) : super(
+         possibleValues: [true, false],
+         includeCallback: (T element, bool _) => includeCallback(element),
+         enabledCallback: (bool filterValue) => filterValue,
+       );
+}
 
+/// A filter setting that can be set to any of the predefined values
+/// [possibleValues].
+///
+/// The generic type [V] must be a json encodable type, since this value will
+/// be JSON encoded and decoded during the creation and parsing of [FilterTag]
+/// objects.
+class SettingFilter<T, V> {
+  SettingFilter({
+    required this.id,
+    required this.name,
+    required bool Function(T element, V currentFilterValue) includeCallback,
+    required bool Function(V filterValue) enabledCallback,
+    required this.defaultValue,
+    required this.possibleValues,
+    this.possibleValueDisplays,
+    this.tooltip,
+  }) : _includeCallback = includeCallback,
+       _enabledCallback = enabledCallback,
+       setting = ValueNotifier<V>(defaultValue),
+       assert(possibleValues.contains(defaultValue)),
+       assert(
+         possibleValueDisplays == null ||
+             possibleValues.length == possibleValueDisplays.length,
+       );
+
+  /// The unique id for this setting filter.
+  ///
+  /// This value will be used when reading and writing setting filter values to
+  /// DevTools preferences on disk.
+  final String id;
+
+  /// The name of this setting filter.
   final String name;
 
-  final bool Function(T element) includeCallback;
+  /// The set of possible values that [setting] can be set to.
+  final List<V> possibleValues;
 
+  /// An optional List of values to use for the display of the setting filter
+  /// options in a dropdown menu.
+  ///
+  /// If null, the String representation of each value in [possibleValues] will
+  /// be used for the dropdown menu items instead.
+  ///
+  /// The length and order of this List should match that of [possibleValues].
+  final List<String>? possibleValueDisplays;
+
+  /// The default value of the filter.
+  ///
+  /// This will be used to set the initial [setting] of the filter, and may be set
+  /// again later if the user triggers "reset to default" behavior from the
+  /// filter dialog or from some other source.
+  final V defaultValue;
+
+  /// The current value of this setting filter.
+  ///
+  /// Filter dialogs and other filter affordances will read this value and
+  /// listen to this notifier for changes.
+  final ValueNotifier<V> setting;
+
+  /// The tooltip to describe the setting filter.
   final String? tooltip;
 
-  final bool enabledByDefault;
+  /// The callback that determines whether a data element should be included
+  /// based on the filter criteria.
+  final bool Function(T element, V currentFilterValue) _includeCallback;
 
-  final ValueNotifier<bool> enabled;
+  /// The callback that determines whether this filter is enabled based on the
+  /// current value of the filter.
+  final bool Function(V filterValue) _enabledCallback;
+
+  /// Whether a data element should be included based on the current state of the
+  /// filter.
+  bool includeData(T data) {
+    return !enabled || _includeCallback(data, setting.value);
+  }
+
+  /// Whether this filter is enabled based on the current value of the filter.
+  bool get enabled => _enabledCallback(setting.value);
+
+  Map<String, Object?> get valueAsJson => {id: setting.value};
 }
 
 class QueryFilter {
@@ -316,7 +453,7 @@
     this.isEmpty = false,
   });
 
-  factory QueryFilter.empty({required Map<String, QueryFilterArgument> args}) {
+  factory QueryFilter.empty({required QueryFilterArgs args}) {
     return QueryFilter._(
       filterArguments: args,
       substringExpressions: <Pattern>[],
@@ -326,7 +463,7 @@
 
   factory QueryFilter.parse(
     String query, {
-    required Map<String, QueryFilterArgument> args,
+    required QueryFilterArgs args,
     required bool useRegExp,
   }) {
     if (query.isEmpty) {
@@ -347,21 +484,25 @@
         if (value.isNotEmpty) {
           for (final arg in args.values) {
             if (arg.matchesKey(part)) {
-              arg.isNegative =
-                  part.startsWith(QueryFilterArgument.negativePrefix);
-              final valueStrings =
-                  value.split(QueryFilterArgument.valueSeparator);
-              arg.values = useRegExp
-                  ? valueStrings
-                      .map((v) => RegExp(v, caseSensitive: false))
-                      .toList()
-                  : valueStrings;
+              arg.isNegative = part.startsWith(
+                QueryFilterArgument.negativePrefix,
+              );
+              final valueStrings = value.split(
+                QueryFilterArgument.valueSeparator,
+              );
+              arg.values =
+                  useRegExp
+                      ? valueStrings
+                          .map((v) => RegExp(v, caseSensitive: false))
+                          .toList()
+                      : valueStrings;
             }
           }
         }
       } else {
-        substringExpressions
-            .add(useRegExp ? RegExp(part, caseSensitive: false) : part);
+        substringExpressions.add(
+          useRegExp ? RegExp(part, caseSensitive: false) : part,
+        );
       }
     }
 
@@ -382,23 +523,25 @@
     );
   }
 
-  final Map<String, QueryFilterArgument> filterArguments;
+  final QueryFilterArgs filterArguments;
 
   final List<Pattern> substringExpressions;
 
   final bool isEmpty;
 
-  String get query => isEmpty
-      ? ''
-      : [
-          ...substringExpressions.toStringList(),
-          for (final arg in filterArguments.values) arg.display,
-        ].join(' ').trim();
+  String get query =>
+      isEmpty
+          ? ''
+          : [
+            ...substringExpressions.toStringList(),
+            for (final arg in filterArguments.values) arg.display,
+          ].join(' ').trim();
 }
 
 class QueryFilterArgument<T> {
   QueryFilterArgument({
     required this.keys,
+    required this.exampleUsages,
     required this.dataValueProvider,
     required this.substringMatch,
     this.values = const [],
@@ -415,6 +558,8 @@
 
   final bool substringMatch;
 
+  final List<String> exampleUsages;
+
   List<Pattern> values;
 
   bool isNegative;
@@ -444,9 +589,10 @@
 
     var matches = false;
     for (final value in values) {
-      matches = substringMatch
-          ? dataValue.caseInsensitiveContains(value)
-          : dataValue.caseInsensitiveEquals(value);
+      matches =
+          substringMatch
+              ? dataValue.caseInsensitiveContains(value)
+              : dataValue.caseInsensitiveEquals(value);
       if (matches) break;
     }
     return isNegative ? !matches : matches;
@@ -466,8 +612,6 @@
   }
 }
 
-// TODO:: Change screens that use [DevtoolsFilterButton] to use a [StandaloneFilterField]
-// instead.
 /// A text field for controlling the filter query for a [FilterControllerMixin].
 ///
 /// This text field has a button to open a dialog for toggling any toggleable
@@ -476,10 +620,13 @@
   const StandaloneFilterField({
     super.key,
     required this.controller,
+    required this.filteredItem,
   });
 
   final FilterControllerMixin<T> controller;
 
+  final String filteredItem;
+
   @override
   State<StandaloneFilterField<T>> createState() =>
       _StandaloneFilterFieldState<T>();
@@ -513,9 +660,54 @@
             builder: (context, useRegExp, _) {
               return DevToolsClearableTextField(
                 autofocus: true,
-                labelText: 'Filter',
+                hintText: 'Filter',
                 controller: queryTextFieldController,
+                prefixIcon:
+                    widget.controller.settingFilters.isNotEmpty
+                        ? Container(
+                          height: inputDecorationElementHeight,
+                          padding: const EdgeInsets.only(
+                            left: densePadding,
+                            right: denseSpacing,
+                          ),
+                          child: ValueListenableBuilder<Filter>(
+                            valueListenable: widget.controller.activeFilter,
+                            builder: (context, _, _) {
+                              return DevToolsFilterButton(
+                                message: 'More filters',
+                                onPressed: () {
+                                  unawaited(
+                                    showDialog(
+                                      context: context,
+                                      builder:
+                                          (context) => FilterDialog(
+                                            controller: widget.controller,
+                                            filteredItem: widget.filteredItem,
+                                          ),
+                                    ),
+                                  );
+                                },
+                                isFilterActive:
+                                    widget.controller.isFilterActive,
+                              );
+                            },
+                          ),
+                        )
+                        : null,
                 additionalSuffixActions: [
+                  if (widget.controller.queryFilterArgs.isNotEmpty)
+                    InputDecorationSuffixButton.help(
+                      onPressed: () {
+                        showDevToolsDialog(
+                          context: context,
+                          title: 'Filter Syntax',
+                          content: _FilterSyntax(
+                            controller: widget.controller,
+                            filteredItem: widget.filteredItem,
+                          ),
+                        );
+                      },
+                    ),
                   DevToolsToggleButton(
                     icon: Codicons.regex,
                     message: 'Use regular expressions',
@@ -525,7 +717,7 @@
                       widget.controller.useRegExp.value = !useRegExp;
                       widget.controller.setActiveFilter(
                         query: queryTextFieldController.value.text,
-                        toggleFilters: widget.controller._toggleFilters,
+                        settingFilters: widget.controller.settingFilters,
                       );
                     },
                   ),
@@ -533,30 +725,123 @@
                 onChanged: (_) {
                   widget.controller.setActiveFilter(
                     query: queryTextFieldController.value.text,
-                    toggleFilters: widget.controller._toggleFilters,
+                    settingFilters: widget.controller.settingFilters,
                   );
                 },
               );
             },
           ),
         ),
-        const SizedBox(width: defaultSpacing),
-        DevToolsFilterButton(
-          message: 'Filter Settings',
-          onPressed: () {
-            unawaited(
-              showDialog(
-                context: context,
-                builder: (context) => FilterDialog(
-                  controller: widget.controller,
-                  includeQueryFilter: false,
+      ],
+    );
+  }
+}
+
+class _FilterSyntax<T> extends StatelessWidget {
+  const _FilterSyntax({required this.controller, required this.filteredItem});
+
+  final FilterControllerMixin<T> controller;
+  final String filteredItem;
+
+  static const _separator = ', ';
+
+  @override
+  Widget build(BuildContext context) {
+    final queryFilterArgs = controller.queryFilterArgs.values;
+    final filterKeys = queryFilterArgs.map(
+      (arg) => arg.keys.map((key) => "'$key'").join(_separator),
+    );
+    final filterExampleUsages = queryFilterArgs.map(
+      (arg) => arg.exampleUsages.map((usage) => "'$usage'").join(_separator),
+    );
+
+    final usageTextStyle = Theme.of(context).fixedFontStyle;
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        Text('''
+Type a query to show or hide specific ${pluralize(filteredItem, 2)}.
+
+Any text that is not paired with an available filter key below will
+be queried against all available data for each $filteredItem.
+
+Available filters:
+'''),
+        Padding(
+          padding: const EdgeInsets.only(left: defaultSpacing),
+          child: Row(
+            children: [
+              Flexible(
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    for (final key in filterKeys)
+                      Text(key, style: usageTextStyle),
+                  ],
                 ),
               ),
-            );
-          },
-          isFilterActive: false,
+              const SizedBox(width: extraLargeSpacing),
+              Flexible(
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    for (final exampleUsage in filterExampleUsages)
+                      Text('(e.g. $exampleUsage)', style: usageTextStyle),
+                  ],
+                ),
+              ),
+            ],
+          ),
         ),
       ],
     );
   }
 }
+
+/// A class that stores information for a filter tag, which is a string
+/// representation of a filter state.
+///
+/// This tag is used to identify filters in caches, like in user preferences
+/// or in other screen specific data caches.
+class FilterTag {
+  FilterTag({
+    required this.query,
+    required this.settingFilterValues,
+    required this.useRegExp,
+  });
+
+  static FilterTag? parse(String value) {
+    final parts = value.split(filterTagSeparator);
+    if (parts.length < 2) return null;
+    try {
+      final useRegExp = parts.last == useRegExpTag;
+      final query = parts[0].trim();
+      final settingFilterValues =
+          (jsonDecode(parts[1]) as List).cast<Map<String, Object?>>();
+      return FilterTag(
+        query: query,
+        settingFilterValues: settingFilterValues,
+        useRegExp: useRegExp,
+      );
+    } catch (_) {
+      // Return null for any parsing error.
+      return null;
+    }
+  }
+
+  static const filterTagSeparator = '|';
+  static const useRegExpTag = 'regexp';
+
+  final String query;
+  final List<Map<String, Object?>> settingFilterValues;
+  final bool useRegExp;
+
+  String get tag => [
+    query.trim(),
+    jsonEncode(settingFilterValues),
+    if (useRegExp) useRegExpTag,
+  ].join(filterTagSeparator);
+}
diff --git a/packages/devtools_app/lib/src/shared/ui/history_viewport.dart b/packages/devtools_app/lib/src/shared/ui/history_viewport.dart
new file mode 100644
index 0000000..2baa799
--- /dev/null
+++ b/packages/devtools_app/lib/src/shared/ui/history_viewport.dart
@@ -0,0 +1,183 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+
+import '../../screens/debugger/common.dart';
+import '../primitives/history_manager.dart';
+import 'common_widgets.dart';
+
+/// A [Widget] that allows for displaying content based on the current state of a
+/// [HistoryManager]. Includes built-in controls for navigating back and forth
+/// through the content stored in the provided [HistoryManager].
+///
+/// [history] is the [HistoryManager] that contains the data to be displayed.
+///
+/// [contentBuilder] is invoked with the currently selected historical data
+/// when building the contents of the viewport.
+///
+/// If [controls] is provided, each [Widget] will be inserted with padding
+/// at the end of the viewport title bar.
+///
+/// If [generateTitle] is provided, the title string will be set to the
+/// returned value. If not provided, the title will be empty.
+class HistoryViewport<T> extends StatefulWidget {
+  const HistoryViewport({
+    super.key,
+    required this.history,
+    required this.contentBuilder,
+    this.controls,
+    this.generateTitle,
+    this.onChange,
+    this.historyEnabled = true,
+    this.onTitleTap,
+    this.titleIcon,
+  });
+
+  final HistoryManager<T> history;
+  final Widget Function(BuildContext, T?) contentBuilder;
+  final List<Widget>? controls;
+  final String Function(T?)? generateTitle;
+  final void Function(T?, T?)? onChange;
+  final bool historyEnabled;
+  final VoidCallback? onTitleTap;
+  final IconData? titleIcon;
+
+  @override
+  State<HistoryViewport<T>> createState() => _HistoryViewportState<T>();
+}
+
+class _HistoryViewportState<T> extends State<HistoryViewport<T>> {
+  TextStyle? _titleStyle;
+  Color? _iconColor;
+
+  void _updateTitleStyle(TextStyle style) {
+    setState(() {
+      _titleStyle = style;
+      _iconColor = style.color;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final titleIcon =
+        widget.titleIcon != null
+            ? Padding(
+              padding: const EdgeInsets.only(right: densePadding),
+              child: Icon(
+                widget.titleIcon,
+                size: defaultIconSize,
+                color: _iconColor,
+              ),
+            )
+            : const SizedBox.shrink();
+
+    return ValueListenableBuilder<T?>(
+      valueListenable: widget.history.current,
+      builder: (context, T? current, _) {
+        final theme = Theme.of(context);
+        final title =
+            widget.generateTitle == null
+                ? '  '
+                : widget.generateTitle!(current);
+        final defaultTitleStyle = theme.textTheme.titleMedium!;
+        final titleWidget = debuggerSectionTitle(
+          theme,
+          child: Row(
+            children: [
+              if (widget.historyEnabled) ...[
+                ToolbarAction(
+                  icon: Icons.chevron_left,
+                  onPressed:
+                      widget.history.hasPrevious
+                          ? () {
+                            widget.history.moveBack();
+                            if (widget.onChange != null) {
+                              widget.onChange!(
+                                widget.history.current.value,
+                                widget.history.peekNext(),
+                              );
+                            }
+                          }
+                          : null,
+                ),
+                ToolbarAction(
+                  icon: Icons.chevron_right,
+                  onPressed:
+                      widget.history.hasNext
+                          ? () {
+                            final current = widget.history.current.value as T;
+                            widget.history.moveForward();
+                            if (widget.onChange != null) {
+                              widget.onChange!(
+                                widget.history.current.value,
+                                current,
+                              );
+                            }
+                          }
+                          : null,
+                ),
+                const SizedBox(width: denseSpacing),
+                const VerticalDivider(thickness: 1.0),
+                const SizedBox(width: defaultSpacing),
+              ],
+              Expanded(
+                child:
+                    widget.onTitleTap == null
+                        ? Row(
+                          children: [
+                            titleIcon,
+                            Text(title, style: defaultTitleStyle),
+                          ],
+                        )
+                        : MouseRegion(
+                          cursor: SystemMouseCursors.click,
+                          onExit: (_) {
+                            _updateTitleStyle(defaultTitleStyle);
+                          },
+                          onEnter: (_) {
+                            _updateTitleStyle(
+                              defaultTitleStyle.copyWith(
+                                color: theme.colorScheme.primary,
+                              ),
+                            );
+                          },
+                          child: GestureDetector(
+                            onTap: widget.onTitleTap,
+                            child: Row(
+                              children: [
+                                titleIcon,
+                                Expanded(
+                                  child: Text(
+                                    title,
+                                    style:
+                                        _titleStyle ??
+                                        theme.textTheme.titleMedium,
+                                  ),
+                                ),
+                              ],
+                            ),
+                          ),
+                        ),
+              ),
+              if (widget.controls != null) ...[
+                const SizedBox(width: denseSpacing),
+                for (final widget in widget.controls!) ...[
+                  widget,
+                  const SizedBox(width: denseSpacing),
+                ],
+              ],
+            ],
+          ),
+        );
+        return RoundedOutlinedBorder(
+          child: Column(
+            children: [titleWidget, widget.contentBuilder(context, current)],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/shared/ui/hover.dart b/packages/devtools_app/lib/src/shared/ui/hover.dart
index b05b1fe..67c84f0 100644
--- a/packages/devtools_app/lib/src/shared/ui/hover.dart
+++ b/packages/devtools_app/lib/src/shared/ui/hover.dart
@@ -11,18 +11,18 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
-import '../common_widgets.dart';
+import 'common_widgets.dart';
 import 'utils.dart';
 
 double get _maxHoverCardHeight => scaleByFontFactor(250.0);
 
 TextStyle get _hoverTitleTextStyle => fixBlurryText(
-      TextStyle(
-        fontWeight: FontWeight.normal,
-        fontSize: scaleByFontFactor(15.0),
-        decoration: TextDecoration.none,
-      ),
-    );
+  TextStyle(
+    fontWeight: FontWeight.normal,
+    fontSize: scaleByFontFactor(15.0),
+    decoration: TextDecoration.none,
+  ),
+);
 
 /// Regex for valid Dart identifiers.
 final _identifier = RegExp(r'^[a-zA-Z0-9]|_|\$');
@@ -66,6 +66,25 @@
   return word;
 }
 
+bool isPrimitiveValueOrNull(String valueAsString) {
+  if (valueAsString.isEmpty) return false;
+  final isNull = valueAsString == 'null';
+  final isBool = valueAsString == 'true' || valueAsString == 'false';
+  final isInt = int.tryParse(valueAsString) != null;
+  final isDouble = double.tryParse(valueAsString) != null;
+
+  bool isString = false;
+  if (valueAsString.length > 2) {
+    final firstChar = valueAsString[0];
+    final lastChar = valueAsString[valueAsString.length - 1];
+    isString =
+        [firstChar, lastChar].every((char) => char == '"') ||
+        [firstChar, lastChar].every((char) => char == "'");
+  }
+
+  return isNull || isBool || isInt || isDouble || isString;
+}
+
 /// Returns the index in the Textspan's plainText for which the hover offset is
 /// located.
 int _hoverIndexFor(double dx, TextSpan line) {
@@ -196,16 +215,16 @@
     required HoverCardController hoverCardController,
     String? title,
   }) : this(
-          context: context,
-          contents: contents,
-          width: width,
-          position: Offset(
-            math.max(0, event.position.dx - (width / 2.0)),
-            event.position.dy + _hoverYOffset,
-          ),
-          title: title,
-          hoverCardController: hoverCardController,
-        );
+         context: context,
+         contents: contents,
+         width: width,
+         position: Offset(
+           math.max(0, event.position.dx - (width / 2.0)),
+           event.position.dy + _hoverYOffset,
+         ),
+         title: title,
+         hoverCardController: hoverCardController,
+       );
 
   late OverlayEntry _overlayEntry;
 
@@ -271,18 +290,18 @@
   }
 }
 
-typedef AsyncGenerateHoverCardDataFunc = Future<HoverCardData?> Function({
-  required PointerHoverEvent event,
+typedef AsyncGenerateHoverCardDataFunc =
+    Future<HoverCardData?> Function({
+      required PointerHoverEvent event,
 
-  /// Returns true if the HoverCard is no longer visible.
-  ///
-  /// Use this callback to short circuit long running tasks.
-  required bool Function() isHoverStale,
-});
+      /// Returns true if the HoverCard is no longer visible.
+      ///
+      /// Use this callback to short circuit long running tasks.
+      required bool Function() isHoverStale,
+    });
 
-typedef SyncGenerateHoverCardDataFunc = HoverCardData Function(
-  PointerHoverEvent event,
-);
+typedef SyncGenerateHoverCardDataFunc =
+    HoverCardData Function(PointerHoverEvent event);
 
 /// A hover card based tooltip.
 class HoverCardTooltip extends StatefulWidget {
@@ -312,8 +331,8 @@
     required this.generateHoverCardData,
     required this.child,
     this.disposable,
-  })  : asyncGenerateHoverCardData = null,
-        asyncTimeout = null;
+  }) : asyncGenerateHoverCardData = null,
+       asyncTimeout = null;
 
   static const _hoverDelay = Duration(milliseconds: 500);
   static double get defaultHoverWidth => scaleByFontFactor(450.0);
@@ -408,9 +427,10 @@
     HoverCard? spinnerHoverCard;
     final hoverCardDataFuture = asyncGenerateHoverCardData(
       event: event,
-      isHoverStale: () =>
-          spinnerHoverCard != null &&
-          !_hoverCardController.isHoverCardStillActive(spinnerHoverCard),
+      isHoverStale:
+          () =>
+              spinnerHoverCard != null &&
+              !_hoverCardController.isHoverCardStillActive(spinnerHoverCard),
     );
     final hoverCardDataCompleter = _hoverCardDataCompleter(hoverCardDataFuture);
     // If we have set the async hover card to show up only after a timeout,
@@ -449,9 +469,7 @@
       hoverCardController: _hoverCardController,
     );
 
-    _setHoverCard(
-      spinnerHoverCard,
-    );
+    _setHoverCard(spinnerHoverCard);
 
     // The spinner is showing, we can now generate the HoverCardData
     final hoverCardData = await hoverCardDataCompleter.future;
diff --git a/packages/devtools_app/lib/src/shared/ui/icons.dart b/packages/devtools_app/lib/src/shared/ui/icons.dart
index a7ce424..90a406e 100644
--- a/packages/devtools_app/lib/src/shared/ui/icons.dart
+++ b/packages/devtools_app/lib/src/shared/ui/icons.dart
@@ -77,18 +77,12 @@
     return Container(
       width: defaultIconSize,
       height: defaultIconSize,
-      decoration: BoxDecoration(
-        shape: BoxShape.circle,
-        color: color,
-      ),
+      decoration: BoxDecoration(shape: BoxShape.circle, color: color),
       alignment: Alignment.center,
       child: Text(
         text,
         textAlign: TextAlign.center,
-        style: TextStyle(
-          fontSize: scaleByFontFactor(9.0),
-          color: textColor,
-        ),
+        style: TextStyle(fontSize: scaleByFontFactor(9.0), color: textColor),
       ),
     );
   }
@@ -154,7 +148,7 @@
 
 class IconKind {
   const IconKind(this.name, this.icon, [AssetImageIcon? abstractIcon])
-      : abstractIcon = abstractIcon ?? icon;
+    : abstractIcon = abstractIcon ?? icon;
 
   static IconKind classIcon = const IconKind(
     'class',
@@ -258,10 +252,7 @@
         ),
         greyPaint,
       )
-      ..drawRect(
-        iconRect,
-        Paint()..color = color,
-      )
+      ..drawRect(iconRect, Paint()..color = color)
       ..drawRect(
         iconRect,
         Paint()
@@ -297,8 +288,8 @@
     this.color,
     double? height,
     double? width,
-  })  : _width = width,
-        _height = height;
+  }) : _width = width,
+       _height = height;
 
   final String asset;
   final Color? color;
@@ -334,17 +325,12 @@
 /// A widget that renders either an [icon] from a font glyph or an [iconAsset]
 /// from the app bundle.
 class DevToolsIcon extends StatelessWidget {
-  DevToolsIcon({
-    super.key,
-    this.icon,
-    this.iconAsset,
-    this.color,
-    double? size,
-  })  : assert(
-          (icon == null) != (iconAsset == null),
-          'Exactly one of icon and iconAsset must be specified.',
-        ),
-        size = size ?? defaultIconSize;
+  DevToolsIcon({super.key, this.icon, this.iconAsset, this.color, double? size})
+    : assert(
+        (icon == null) != (iconAsset == null),
+        'Exactly one of icon and iconAsset must be specified.',
+      ),
+      size = size ?? defaultIconSize;
 
   /// The icon to use for this screen's tab.
   ///
@@ -364,16 +350,12 @@
   Widget build(BuildContext context) {
     final color = this.color ?? Theme.of(context).colorScheme.onSurface;
     return icon != null
-        ? Icon(
-            icon,
-            size: size,
-            color: color,
-          )
+        ? Icon(icon, size: size, color: color)
         : AssetImageIcon(
-            asset: iconAsset!,
-            height: size,
-            width: size,
-            color: color,
-          );
+          asset: iconAsset!,
+          height: size,
+          width: size,
+          color: color,
+        );
   }
 }
diff --git a/packages/devtools_app/lib/src/shared/ui/search.dart b/packages/devtools_app/lib/src/shared/ui/search.dart
index 9afe445..acf8c10 100644
--- a/packages/devtools_app/lib/src/shared/ui/search.dart
+++ b/packages/devtools_app/lib/src/shared/ui/search.dart
@@ -13,12 +13,12 @@
 import 'package:flutter/services.dart';
 import 'package:logging/logging.dart';
 
-import '../common_widgets.dart';
 import '../primitives/trees.dart';
 import '../primitives/utils.dart';
 import '../ui/utils.dart';
-import '../utils.dart';
+import '../utils/utils.dart';
 import 'colors.dart';
+import 'common_widgets.dart';
 
 // TODO(https://github.com/flutter/devtools/issues/5416): break this file up
 // into managable pieces.
@@ -42,7 +42,8 @@
 
   set search(String value) {
     final previousSearchValue = _searchNotifier.value;
-    final shouldSearchPreviousMatches = previousSearchValue.isNotEmpty &&
+    final shouldSearchPreviousMatches =
+        previousSearchValue.isNotEmpty &&
         value.caseInsensitiveContains(previousSearchValue);
     _searchNotifier.value = value;
     refreshSearchMatches(searchPreviousMatches: shouldSearchPreviousMatches);
@@ -188,8 +189,8 @@
       matchIndex.value = 1; // first item because [matchIndex] us 1-based
     }
     _activeSearchMatch.value?.isActiveSearchMatch = false;
-    _activeSearchMatch.value = searchMatches.value[activeMatchIndex]
-      ..isActiveSearchMatch = true;
+    _activeSearchMatch.value =
+        searchMatches.value[activeMatchIndex]..isActiveSearchMatch = true;
     onMatchChanged(activeMatchIndex);
   }
 
@@ -199,7 +200,8 @@
   /// If [matchesForSearch] is overridden in such a way that
   /// [currentDataToSearchThrough] is not used, then this getter does not need
   /// to be implemented.
-  Iterable<T> get currentDataToSearchThrough => throw UnimplementedError(
+  Iterable<T> get currentDataToSearchThrough =>
+      throw UnimplementedError(
         'Implement this getter in order to use the default'
         ' [matchesForSearch] behavior.',
       );
@@ -248,8 +250,8 @@
   void initSearch() {
     _searchTextFieldController?.dispose();
     _searchFieldFocusNode?.dispose();
-    _searchTextFieldController = SearchTextEditingController()
-      ..text = _searchNotifier.value;
+    _searchTextFieldController =
+        SearchTextEditingController()..text = _searchNotifier.value;
     _searchFieldFocusNode = FocusNode(debugLabel: 'search-field');
   }
 
@@ -280,8 +282,8 @@
     required this.onTap,
     bool bottom = true, // If false placed above.
     bool maxWidth = true,
-  })  : isBottom = bottom,
-        isMaxWidth = maxWidth;
+  }) : isBottom = bottom,
+       isMaxWidth = maxWidth;
 
   final AutoCompleteSearchControllerMixin controller;
   final GlobalKey searchFieldKey;
@@ -328,32 +330,33 @@
 
     final colorScheme = Theme.of(context).colorScheme;
 
-    final autoCompleteTextStyle = Theme.of(context)
-        .regularTextStyle
-        .copyWith(color: colorScheme.contrastTextColor);
+    final autoCompleteTextStyle = Theme.of(
+      context,
+    ).regularTextStyle.copyWith(color: colorScheme.contrastTextColor);
 
-    final autoCompleteHighlightedTextStyle =
-        Theme.of(context).regularTextStyle.copyWith(
-              fontWeight: FontWeight.bold,
-            );
+    final autoCompleteHighlightedTextStyle = Theme.of(
+      context,
+    ).regularTextStyle.copyWith(fontWeight: FontWeight.bold);
 
-    final tileContents = searchAutoComplete.value
-        .map(
-          (match) => _maybeHighlightMatchText(
-            match,
-            autoCompleteTextStyle,
-            autoCompleteHighlightedTextStyle,
-          ),
-        )
-        .toList();
+    final tileContents =
+        searchAutoComplete.value
+            .map(
+              (match) => _maybeHighlightMatchText(
+                match,
+                autoCompleteTextStyle,
+                autoCompleteHighlightedTextStyle,
+              ),
+            )
+            .toList();
 
     // When there are no tiles present, we don't need to display the
     // auto complete list.
     if (tileContents.isEmpty) return const SizedBox.shrink();
 
-    final tileEntryHeight = tileContents.isEmpty
-        ? 0.0
-        : calculateTextSpanHeight(tileContents.first) + denseSpacing;
+    final tileEntryHeight =
+        tileContents.isEmpty
+            ? 0.0
+            : calculateTextSpanHeight(tileContents.first) + denseSpacing;
 
     // Find the searchField and place overlay below bottom of TextField and
     // make overlay width of TextField. This is also we decide the height of
@@ -367,15 +370,17 @@
     final maxAreaForPopup = areaHeight - tileEntryHeight;
     // TODO(terry): Scrolling doesn't work so max popup height is also total
     //              matches to use.
-    topMatchesLimit = min(
-      defaultTopMatchesLimit,
-      (maxAreaForPopup / tileEntryHeight) - 1, // zero based.
-    ).truncate();
+    topMatchesLimit =
+        min(
+          defaultTopMatchesLimit,
+          (maxAreaForPopup / tileEntryHeight) - 1, // zero based.
+        ).truncate();
 
     // Total tiles visible.
-    final totalTiles = bottom
-        ? searchAutoComplete.value.length
-        : (maxAreaForPopup / tileEntryHeight).truncateToDouble();
+    final totalTiles =
+        bottom
+            ? searchAutoComplete.value.length
+            : (maxAreaForPopup / tileEntryHeight).truncateToDouble();
 
     final autoCompleteTiles = <AutoCompleteTile>[];
     final count = min(searchAutoComplete.value.length, totalTiles);
@@ -403,9 +408,10 @@
 
     return Positioned(
       key: searchAutoCompleteKey,
-      width: isMaxWidth
-          ? box.size.width
-          : AutoCompleteSearchControllerMixin.minPopupWidth,
+      width:
+          isMaxWidth
+              ? box.size.width
+              : AutoCompleteSearchControllerMixin.minPopupWidth,
       height: count * tileEntryHeight,
       child: CompositedTransformFollower(
         link: controller.autoCompleteLayerLink,
@@ -432,19 +438,16 @@
     TextStyle highlightedTextStyle,
   ) {
     return match.transformAutoCompleteMatch<TextSpan>(
-      transformMatchedSegment: (segment) => TextSpan(
-        text: segment,
-        style: highlightedTextStyle,
-      ),
-      transformUnmatchedSegment: (segment) => TextSpan(
-        text: segment,
-        style: regularTextStyle,
-      ),
-      combineSegments: (segments) => TextSpan(
-        text: segments.first.text,
-        style: segments.first.style,
-        children: segments.sublist(1),
-      ),
+      transformMatchedSegment:
+          (segment) => TextSpan(text: segment, style: highlightedTextStyle),
+      transformUnmatchedSegment:
+          (segment) => TextSpan(text: segment, style: regularTextStyle),
+      combineSegments:
+          (segments) => TextSpan(
+            text: segments.first.text,
+            style: segments.first.style,
+            children: segments.sublist(1),
+          ),
     );
   }
 }
@@ -489,10 +492,7 @@
                   currentHoveredIndex == index ? highlightColor : defaultColor,
               padding: const EdgeInsets.symmetric(horizontal: denseSpacing),
               alignment: Alignment.centerLeft,
-              child: Text.rich(
-                textSpan,
-                maxLines: 1,
-              ),
+              child: Text.rich(textSpan, maxLines: 1),
             );
           },
         ),
@@ -559,9 +559,10 @@
 
   final _currentHoveredIndex = ValueNotifier<int>(0);
 
-  String? get currentHoveredText => searchAutoComplete.value.isNotEmpty
-      ? searchAutoComplete.value[currentHoveredIndex.value].text
-      : null;
+  String? get currentHoveredText =>
+      searchAutoComplete.value.isNotEmpty
+          ? searchAutoComplete.value[currentHoveredIndex.value].text
+          : null;
 
   /// Last X position of caret in search field, used for pop-up position.
   double xPosition = 0.0;
@@ -608,8 +609,9 @@
 
   void updateCurrentSuggestion(String activeWord) {
     final hoveredText = currentHoveredText;
-    final suggestion =
-        hoveredText?.substring(min(activeWord.length, hoveredText.length));
+    final suggestion = hoveredText?.substring(
+      min(activeWord.length, hoveredText.length),
+    );
 
     if (suggestion == null || suggestion.isEmpty) {
       clearCurrentSuggestion();
@@ -715,10 +717,7 @@
       var lastSpaceIndex = selectionValue.lastIndexOf(handleFields ? '.' : ' ');
       lastSpaceIndex = lastSpaceIndex >= 0 ? lastSpaceIndex + 1 : 0;
 
-      activeWord = selectionValue.substring(
-        lastSpaceIndex,
-        startSelection,
-      );
+      activeWord = selectionValue.substring(lastSpaceIndex, startSelection);
 
       var variableStart = -1;
       // Validate activeWord is really a word.
@@ -742,10 +741,7 @@
           break;
         } else {
           lastSpaceIndex += variableStart;
-          activeWord = selectionValue.substring(
-            lastSpaceIndex,
-            startSelection,
-          );
+          activeWord = selectionValue.substring(lastSpaceIndex, startSelection);
           break;
         }
       }
@@ -788,17 +784,16 @@
 typedef SelectAutoComplete = void Function(String selection);
 
 /// Callback to handle highlighting item in the drop-down list.
-typedef HighlightAutoComplete = void Function(
-  AutoCompleteSearchControllerMixin controller,
-  bool directionDown,
-);
+typedef HighlightAutoComplete =
+    void Function(
+      AutoCompleteSearchControllerMixin controller,
+      bool directionDown,
+    );
 
 /// Provided by clients to specify where the autocomplete overlay should be
 /// positioned relative to the input text.
-typedef OverlayXPositionBuilder = double Function(
-  String inputValue,
-  TextStyle? inputStyle,
-);
+typedef OverlayXPositionBuilder =
+    double Function(String inputValue, TextStyle? inputStyle);
 
 class SearchTextEditingController extends TextEditingController {
   String? _suggestionText;
@@ -898,11 +893,8 @@
     this.onClose,
     this.searchFieldWidth = defaultSearchFieldWidth,
     double? searchFieldHeight,
-    EdgeInsets? containerPadding,
     super.key,
-  })  : searchFieldHeight = searchFieldHeight ?? defaultTextFieldHeight,
-        containerPadding =
-            containerPadding ?? const EdgeInsets.only(top: _defaultTopPadding);
+  }) : searchFieldHeight = searchFieldHeight ?? defaultTextFieldHeight;
 
   final T searchController;
 
@@ -910,9 +902,6 @@
 
   final double searchFieldHeight;
 
-  /// The padding for the [Container] that contains the search text field.
-  final EdgeInsets containerPadding;
-
   /// Whether the search text field should be enabled.
   final bool searchFieldEnabled;
 
@@ -928,10 +917,6 @@
   /// triggered.
   final VoidCallback? onClose;
 
-  /// Padding to ensure the 'Search' hint on the text field is not cut off for
-  /// the default text field height [defaultTextFieldHeight].
-  static const _defaultTopPadding = 3.0;
-
   @override
   State<SearchField> createState() => _SearchFieldState();
 }
@@ -943,16 +928,16 @@
 
   @override
   Widget build(BuildContext context) {
-    return Container(
+    return SizedBox(
       width: widget.searchFieldWidth,
       height: widget.searchFieldHeight,
-      padding: widget.containerPadding,
       child: StatelessSearchField(
         controller: searchController,
         searchFieldEnabled: widget.searchFieldEnabled,
         shouldRequestFocus: widget.shouldRequestFocus,
         supportsNavigation: widget.supportsNavigation,
         onClose: widget.onClose,
+        searchFieldHeight: widget.searchFieldHeight,
       ),
     );
   }
@@ -976,7 +961,7 @@
     required this.searchFieldEnabled,
     required this.shouldRequestFocus,
     this.searchFieldKey,
-    this.label = 'Search',
+    this.label,
     this.decoration,
     this.supportsNavigation = false,
     this.onClose,
@@ -984,6 +969,7 @@
     this.prefix,
     this.suffix,
     this.style,
+    this.searchFieldHeight,
   });
 
   final SearchControllerMixin<T> controller;
@@ -1000,7 +986,7 @@
   final bool supportsNavigation;
 
   /// Label for the search field's input text decoration.
-  final String label;
+  final String? label;
 
   /// Optional callback called when the search field suffix close action is
   /// triggered.
@@ -1026,11 +1012,19 @@
   /// Optional key for the search text field.
   final GlobalKey? searchFieldKey;
 
+  final double? searchFieldHeight;
+
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
     final textStyle = style ?? theme.regularTextStyle;
 
+    void onChanged(String value) {
+      this.onChanged?.call(value);
+      controller.search = value;
+      controller.searchFieldFocusNode?.requestFocus();
+    }
+
     final searchField = TextField(
       key: searchFieldKey,
       autofocus: true,
@@ -1038,11 +1032,7 @@
       focusNode: controller.searchFieldFocusNode,
       controller: controller.searchTextFieldController,
       style: textStyle,
-      onChanged: (value) {
-        onChanged?.call(value);
-        controller.search = value;
-        controller.searchFieldFocusNode?.requestFocus();
-      },
+      onChanged: onChanged,
       onEditingComplete: () {
         controller.searchFieldFocusNode?.requestFocus();
       },
@@ -1052,41 +1042,49 @@
       // Guarantee that the TextField on all platforms renders in the same
       // color for border, label text, and cursor. Primarly, so golden screen
       // snapshots will compare with the exact color.
-      decoration: decoration ??
+      decoration:
+          decoration ??
           InputDecoration(
+            constraints: BoxConstraints(
+              minHeight: searchFieldHeight ?? defaultTextFieldHeight,
+            ),
             contentPadding: const EdgeInsets.symmetric(
-              horizontal: denseSpacing,
-              vertical: densePadding,
+              horizontal: densePadding,
             ),
             border: const OutlineInputBorder(),
+            hintText: 'Search',
+            hintStyle: theme.subtleTextStyle,
             labelText: label,
             labelStyle: theme.subtleTextStyle,
-            // TODO(kenz): add the search icon to the search field.
-            prefix: prefix != null
-                ? Row(
-                    mainAxisSize: MainAxisSize.min,
-                    mainAxisAlignment: MainAxisAlignment.end,
-                    children: <Widget>[
-                      prefix!,
-                      SizedBox(
-                        height: inputDecorationElementHeight,
-                        width: defaultIconSize,
-                        child: Transform.rotate(
-                          angle: degToRad(90),
-                          child: PaddedDivider.vertical(),
+            prefixIcon: Icon(Icons.search, size: defaultIconSize),
+            prefix:
+                prefix != null
+                    ? Row(
+                      mainAxisSize: MainAxisSize.min,
+                      mainAxisAlignment: MainAxisAlignment.end,
+                      children: <Widget>[
+                        prefix!,
+                        SizedBox(
+                          height: inputDecorationElementHeight,
+                          width: defaultIconSize,
+                          child: Transform.rotate(
+                            angle: degToRad(90),
+                            child: PaddedDivider.vertical(),
+                          ),
                         ),
-                      ),
-                    ],
-                  )
-                : null,
-            suffix: suffix ??
-                ((supportsNavigation || onClose != null)
-                    ? _SearchFieldSuffix(
-                        controller: controller,
-                        supportsNavigation: supportsNavigation,
-                        onClose: onClose,
-                      )
-                    : null),
+                      ],
+                    )
+                    : null,
+            suffix:
+                suffix ??
+                _SearchFieldSuffix(
+                  controller: controller,
+                  supportsNavigation: supportsNavigation,
+                  onClose: () {
+                    onClose?.call();
+                    onChanged('');
+                  },
+                ),
           ),
     );
 
@@ -1349,10 +1347,14 @@
 
   @override
   Widget build(BuildContext context) {
-    assert(supportsNavigation || onClose != null);
     return supportsNavigation
         ? SearchNavigationControls(controller, onClose: onClose)
-        : InputDecorationSuffixButton.close(onPressed: onClose);
+        : InputDecorationSuffixButton.close(
+          onPressed: () {
+            controller.searchTextFieldController.clear();
+            onClose?.call();
+          },
+        );
   }
 }
 
@@ -1385,13 +1387,14 @@
                   child: SizedBox(
                     width: scaleByFontFactor(smallProgressSize),
                     height: scaleByFontFactor(smallProgressSize),
-                    child: isSearchInProgress
-                        ? SmallCircularProgressIndicator(
-                            valueColor: AlwaysStoppedAnimation<Color?>(
-                              Theme.of(context).regularTextStyle.color,
-                            ),
-                          )
-                        : const SizedBox(),
+                    child:
+                        isSearchInProgress
+                            ? SmallCircularProgressIndicator(
+                              valueColor: AlwaysStoppedAnimation<Color?>(
+                                Theme.of(context).regularTextStyle.color,
+                              ),
+                            )
+                            : const SizedBox(),
                   ),
                 ),
                 _matchesStatus(numMatches),
@@ -1411,8 +1414,12 @@
                   icon: Icons.keyboard_arrow_down,
                   onPressed: numMatches > 1 ? controller.nextMatch : null,
                 ),
-                if (onClose != null)
-                  InputDecorationSuffixButton.close(onPressed: onClose),
+                InputDecorationSuffixButton.close(
+                  onPressed: () {
+                    controller.searchTextFieldController.clear();
+                    onClose?.call();
+                  },
+                ),
               ],
             );
           },
@@ -1448,7 +1455,8 @@
   /// [SearchControllerMixin.matchesForSearch] is overridden in such a way that
   /// [matchesSearchToken] is not used, then this method does not need to be
   /// implemented.
-  bool matchesSearchToken(RegExp regExpSearch) => throw UnimplementedError(
+  bool matchesSearchToken(RegExp regExpSearch) =>
+      throw UnimplementedError(
         'Implement this method in order to use the default'
         ' [SearchControllerMixin.matchesForSearch] behavior.',
       );
@@ -1474,8 +1482,7 @@
   List<SearchableDataMixin> matchesForSearch(
     String search, {
     bool searchPreviousMatches = false,
-  }) =>
-      const [];
+  }) => const [];
 }
 
 class AutoCompleteMatch {
@@ -1500,13 +1507,17 @@
     for (final segment in matchedSegments) {
       if (previousEndIndex < segment.begin) {
         // Add the unmatched segment before the current matched segment:
-        final segmentBefore =
-            text.substring(previousEndIndex, segment.begin as int);
+        final segmentBefore = text.substring(
+          previousEndIndex,
+          segment.begin as int,
+        );
         segments.add(transformUnmatchedSegment(segmentBefore));
       }
       // Add the matched segment:
-      final matchedSegment =
-          text.substring(segment.begin as int, segment.end as int);
+      final matchedSegment = text.substring(
+        segment.begin as int,
+        segment.end as int,
+      );
       segments.add(transformMatchedSegment(matchedSegment));
       previousEndIndex = segment.end as int;
     }
diff --git a/packages/devtools_app/lib/src/shared/side_panel.dart b/packages/devtools_app/lib/src/shared/ui/side_panel.dart
similarity index 91%
rename from packages/devtools_app/lib/src/shared/side_panel.dart
rename to packages/devtools_app/lib/src/shared/ui/side_panel.dart
index 570c8f4..24eeab6 100644
--- a/packages/devtools_app/lib/src/shared/side_panel.dart
+++ b/packages/devtools_app/lib/src/shared/ui/side_panel.dart
@@ -11,8 +11,8 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_markdown/flutter_markdown.dart';
 
-import 'primitives/utils.dart';
-import 'utils.dart';
+import '../primitives/utils.dart';
+import '../utils/utils.dart';
 
 class SidePanelViewer extends StatefulWidget {
   const SidePanelViewer({
@@ -51,8 +51,10 @@
     super.initState();
 
     visibilityController = longAnimationController(this);
-    visibilityAnimation =
-        Tween<double>(begin: 1.0, end: 0).animate(visibilityController);
+    visibilityAnimation = Tween<double>(
+      begin: 1.0,
+      end: 0,
+    ).animate(visibilityController);
 
     _initListeners();
   }
@@ -160,9 +162,7 @@
         clipBehavior: Clip.hardEdge,
         shape: RoundedRectangleBorder(
           borderRadius: defaultBorderRadius,
-          side: BorderSide(
-            color: theme.focusColor,
-          ),
+          side: BorderSide(color: theme.focusColor),
         ),
         child: Column(
           children: [
@@ -180,12 +180,13 @@
             markdownData.isNullOrEmpty
                 ? Text(textIfMarkdownDataEmpty ?? '')
                 : Expanded(
-                    child: Markdown(
-                      data: markdownData!,
-                      onTapLink: (text, url, title) =>
-                          unawaited(launchUrlWithErrorHandling(url!)),
-                    ),
+                  child: Markdown(
+                    data: markdownData!,
+                    onTapLink:
+                        (text, url, title) =>
+                            unawaited(launchUrlWithErrorHandling(url!)),
                   ),
+                ),
           ],
         ),
       ),
diff --git a/packages/devtools_app/lib/src/shared/ui/tab.dart b/packages/devtools_app/lib/src/shared/ui/tab.dart
index e957d78..518d05a 100644
--- a/packages/devtools_app/lib/src/shared/ui/tab.dart
+++ b/packages/devtools_app/lib/src/shared/ui/tab.dart
@@ -24,11 +24,9 @@
     required this.gaId,
     this.trailing,
     super.child,
-  })  : assert(text != null || child != null || icon != null),
-        assert(text == null || child == null),
-        super(
-          height: calculateHeight(icon, text, child),
-        );
+  }) : assert(text != null || child != null || icon != null),
+       assert(text == null || child == null),
+       super(height: calculateHeight(icon, text, child));
 
   factory DevToolsTab.create({
     Key? key,
@@ -40,10 +38,7 @@
       key: key ?? ValueKey<String>(tabName),
       gaId: '${gaPrefix}_$tabName',
       trailing: trailing,
-      child: Text(
-        tabName,
-        overflow: TextOverflow.ellipsis,
-      ),
+      child: Text(tabName, overflow: TextOverflow.ellipsis),
     );
   }
 
@@ -87,9 +82,9 @@
     this.initialSelectedIndex,
     this.analyticsSessionIdentifier,
   }) : trailingWidgets = List.generate(
-          tabs.length,
-          (index) => tabs[index].tab.trailing ?? const SizedBox(),
-        );
+         tabs.length,
+         (index) => tabs[index].tab.trailing ?? const SizedBox(),
+       );
 
   final List<TabAndView> tabs;
 
@@ -133,10 +128,7 @@
     _tabController?.removeListener(_onTabChanged);
     _tabController?.dispose();
 
-    _tabController = TabController(
-      length: widget.tabs.length,
-      vsync: this,
-    );
+    _tabController = TabController(length: widget.tabs.length, vsync: this);
 
     final initialIndex = widget.initialSelectedIndex;
     if (initialIndex != null) {
@@ -187,7 +179,8 @@
     super.didUpdateWidget(oldWidget);
     if (oldWidget.tabs != widget.tabs ||
         oldWidget.gaScreen != widget.gaScreen) {
-      final isNewSession = oldWidget.analyticsSessionIdentifier !=
+      final isNewSession =
+          oldWidget.analyticsSessionIdentifier !=
               widget.analyticsSessionIdentifier &&
           widget.analyticsSessionIdentifier != null;
       _initTabController(isNewSession: isNewSession);
diff --git a/packages/devtools_app/lib/src/shared/tree.dart b/packages/devtools_app/lib/src/shared/ui/tree_view.dart
similarity index 88%
rename from packages/devtools_app/lib/src/shared/tree.dart
rename to packages/devtools_app/lib/src/shared/ui/tree_view.dart
index 39d1af0..1f3d061 100644
--- a/packages/devtools_app/lib/src/shared/tree.dart
+++ b/packages/devtools_app/lib/src/shared/ui/tree_view.dart
@@ -9,8 +9,8 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart' hide Stack;
 
-import 'collapsible_mixin.dart';
-import 'primitives/trees.dart';
+import '../primitives/collapsible_mixin.dart';
+import '../primitives/trees.dart';
 
 double get defaultTreeViewRowHeight => scaleByFontFactor(20.0);
 
@@ -87,8 +87,8 @@
             final T item = dataFlatList[index];
             return _TreeViewItem<T>(
               item,
-              buildDisplay: (onPressed) =>
-                  widget.dataDisplayProvider(item, onPressed),
+              buildDisplay:
+                  (onPressed) => widget.dataDisplayProvider(item, onPressed),
               onItemSelected: _onItemSelected,
               onItemExpanded: _onItemExpanded,
             );
@@ -147,10 +147,7 @@
 
   void _updateItems() {
     setState(() {
-      dataFlatList = buildFlatList(
-        dataRoots,
-        onTraverse: widget.onTraverse,
-      );
+      dataFlatList = buildFlatList(dataRoots, onTraverse: widget.onTraverse);
     });
   }
 }
@@ -180,23 +177,21 @@
   Widget build(BuildContext context) {
     return Container(
       margin: EdgeInsets.only(left: nodeIndent(widget.data)),
-      color: widget.data.isSelected
-          ? Theme.of(context).colorScheme.selectedRowBackgroundColor
-          : null,
+      color:
+          widget.data.isSelected
+              ? Theme.of(context).colorScheme.selectedRowBackgroundColor
+              : null,
       child: Row(
         mainAxisSize: MainAxisSize.min,
         children: [
           widget.data.isExpandable
               ? InkWell(
-                  onTap: _onExpanded,
-                  child: RotationTransition(
-                    turns: expandArrowAnimation,
-                    child: Icon(
-                      Icons.keyboard_arrow_down,
-                      size: defaultIconSize,
-                    ),
-                  ),
-                )
+                onTap: _onExpanded,
+                child: RotationTransition(
+                  turns: expandArrowAnimation,
+                  child: Icon(Icons.keyboard_arrow_down, size: defaultIconSize),
+                ),
+              )
               : SizedBox(width: defaultIconSize),
           Expanded(child: widget.buildDisplay(_onSelected)),
         ],
diff --git a/packages/devtools_app/lib/src/shared/ui/utils.dart b/packages/devtools_app/lib/src/shared/ui/utils.dart
index f996a41..353e99f 100644
--- a/packages/devtools_app/lib/src/shared/ui/utils.dart
+++ b/packages/devtools_app/lib/src/shared/ui/utils.dart
@@ -157,7 +157,8 @@
         // offset controller.
         double delta = 0.0;
         if (widget.offsetController.position.hasContentDimensions) {
-          delta = widget.offsetController.offset -
+          delta =
+              widget.offsetController.offset -
               widget.offsetController.position.maxScrollExtent +
               widget.offsetController.position.minScrollExtent;
           if (widget.offsetController.position.hasViewportDimension) {
@@ -165,22 +166,21 @@
             // The viewport dimension from the offsetController may be one frame
             // behind the true viewport dimension. We add this delta so the
             // scrollbar always appears stuck to the side of the viewport.
-            delta += widget.offsetControllerViewportDimension -
+            delta +=
+                widget.offsetControllerViewportDimension -
                 widget.offsetController.position.viewportDimension;
           }
         }
-        final offset = widget.axis == Axis.vertical
-            ? Offset(delta, 0.0)
-            : Offset(0.0, delta);
+        final offset =
+            widget.axis == Axis.vertical
+                ? Offset(delta, 0.0)
+                : Offset(0.0, delta);
         return Transform.translate(
           offset: offset,
           child: Scrollbar(
             thumbVisibility: widget.isAlwaysShown,
             controller: widget.controller,
-            child: Transform.translate(
-              offset: -offset,
-              child: child,
-            ),
+            child: Transform.translate(offset: -offset, child: child),
           ),
         );
       },
@@ -190,10 +190,7 @@
 }
 
 /// Scrolls to [position] if [position] is not already visible in the scroll view.
-void maybeScrollToPosition(
-  ScrollController scrollController,
-  double position,
-) {
+void maybeScrollToPosition(ScrollController scrollController, double position) {
   final extentVisible = Range(
     scrollController.offset,
     scrollController.offset + scrollController.position.extentInside,
@@ -246,9 +243,7 @@
 class ThemedColor {
   const ThemedColor({required this.light, required this.dark});
 
-  const ThemedColor.fromSingle(Color color)
-      : light = color,
-        dark = color;
+  const ThemedColor.fromSingle(Color color) : light = color, dark = color;
 
   final Color light;
 
diff --git a/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart b/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
index 4cdae9a..4e9a8b1 100644
--- a/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
+++ b/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
@@ -12,12 +12,12 @@
 import '../../screens/profiler/cpu_profile_service.dart';
 import '../../screens/profiler/sampling_rate.dart';
 import '../analytics/constants.dart' as gac;
-import '../banner_messages.dart';
-import '../common_widgets.dart';
 import '../globals.dart';
+import '../managers/banner_messages.dart';
 import '../primitives/utils.dart';
 import '../table/table.dart';
 import '../table/table_data.dart';
+import 'common_widgets.dart';
 import 'drop_down_button.dart';
 
 /// DropdownButton that controls the value of the 'profile_period' vm flag.
@@ -175,18 +175,20 @@
   }
 
   void _updateFromController() {
-    flags = (serviceConnection.vmFlagManager.flags.value?.flags ?? [])
-        .map((flag) => _DialogFlag(flag))
-        .toList();
+    flags =
+        (serviceConnection.vmFlagManager.flags.value?.flags ?? [])
+            .map((flag) => _DialogFlag(flag))
+            .toList();
     _refilter();
   }
 
   void _refilter() {
     final filter = filterController.text.trim().toLowerCase();
 
-    filteredFlags = filter.isEmpty
-        ? flags
-        : flags.where((flag) => flag.filterText.contains(filter)).toList();
+    filteredFlags =
+        filter.isEmpty
+            ? flags
+            : flags.where((flag) => flag.filterText.contains(filter)).toList();
   }
 
   @override
@@ -210,16 +212,10 @@
         mainAxisSize: MainAxisSize.min,
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          SizedBox(
-            width: 875,
-            height: 375,
-            child: _FlagTable(filteredFlags),
-          ),
+          SizedBox(width: 875, height: 375, child: _FlagTable(filteredFlags)),
         ],
       ),
-      actions: const [
-        DialogCloseButton(),
-      ],
+      actions: const [DialogCloseButton()],
     );
   }
 }
@@ -250,11 +246,7 @@
 }
 
 class _NameColumn extends ColumnData<_DialogFlag> {
-  _NameColumn()
-      : super(
-          'Name',
-          fixedWidthPx: scaleByFontFactor(180),
-        );
+  _NameColumn() : super('Name', fixedWidthPx: scaleByFontFactor(180));
 
   @override
   String getValue(_DialogFlag dataObject) => dataObject.name ?? '';
@@ -262,10 +254,7 @@
 
 class _DescriptionColumn extends ColumnData<_DialogFlag> {
   _DescriptionColumn()
-      : super.wide(
-          'Description',
-          minWidthPx: scaleByFontFactor(100),
-        );
+    : super.wide('Description', minWidthPx: scaleByFontFactor(100));
 
   @override
   String getValue(_DialogFlag dataObject) => dataObject.description ?? '';
@@ -279,12 +268,12 @@
 
 class _ValueColumn extends ColumnData<_DialogFlag> {
   _ValueColumn()
-      : super(
-          'Value',
-          fixedWidthPx: scaleByFontFactor(100),
-          headerAlignment: TextAlign.right,
-          alignment: ColumnAlignment.right,
-        );
+    : super(
+        'Value',
+        fixedWidthPx: scaleByFontFactor(100),
+        headerAlignment: TextAlign.right,
+        alignment: ColumnAlignment.right,
+      );
 
   @override
   String getValue(_DialogFlag dataObject) => dataObject.value ?? '';
@@ -295,9 +284,10 @@
 
 class _DialogFlag {
   _DialogFlag(this.flag)
-      : filterText = '${flag.name?.toLowerCase()}\n'
-            '${flag.comment?.toLowerCase()}\n'
-            '${flag.valueAsString?.toLowerCase()}';
+    : filterText =
+          '${flag.name?.toLowerCase()}\n'
+              '${flag.comment?.toLowerCase()}\n'
+              '${flag.valueAsString?.toLowerCase()}';
 
   final Flag flag;
   final String filterText;
diff --git a/packages/devtools_app/lib/src/shared/future_work_tracker.dart b/packages/devtools_app/lib/src/shared/utils/future_work_tracker.dart
similarity index 97%
rename from packages/devtools_app/lib/src/shared/future_work_tracker.dart
rename to packages/devtools_app/lib/src/shared/utils/future_work_tracker.dart
index 55e2367..7962983 100644
--- a/packages/devtools_app/lib/src/shared/future_work_tracker.dart
+++ b/packages/devtools_app/lib/src/shared/utils/future_work_tracker.dart
@@ -6,7 +6,7 @@
 
 import 'package:flutter/foundation.dart';
 
-import 'primitives/utils.dart';
+import '../primitives/utils.dart';
 
 /// Class that tracks whether work defined by when futures complete is still in
 /// progress.
diff --git a/packages/devtools_app/lib/src/shared/profiler_utils.dart b/packages/devtools_app/lib/src/shared/utils/profiler_utils.dart
similarity index 91%
rename from packages/devtools_app/lib/src/shared/profiler_utils.dart
rename to packages/devtools_app/lib/src/shared/utils/profiler_utils.dart
index d7bb1bf..37f2869 100644
--- a/packages/devtools_app/lib/src/shared/profiler_utils.dart
+++ b/packages/devtools_app/lib/src/shared/utils/profiler_utils.dart
@@ -5,13 +5,13 @@
 import 'package:devtools_app_shared/ui.dart';
 import 'package:flutter/material.dart';
 
-import '../screens/debugger/codeview_controller.dart';
-import '../screens/debugger/debugger_screen.dart';
-import '../screens/vm_developer/vm_developer_common_widgets.dart';
-import 'globals.dart';
-import 'primitives/trees.dart';
-import 'primitives/utils.dart';
-import 'routing.dart';
+import '../../screens/debugger/codeview_controller.dart';
+import '../../screens/debugger/debugger_screen.dart';
+import '../../screens/vm_developer/vm_developer_common_widgets.dart';
+import '../framework/routing.dart';
+import '../globals.dart';
+import '../primitives/trees.dart';
+import '../primitives/utils.dart';
 
 mixin ProfilableDataMixin<T extends TreeNode<T>> on TreeNode<T> {
   ProfileMetaData get profileMetaData;
@@ -34,8 +34,10 @@
 
   set inclusiveSampleCount(int? count) => _inclusiveSampleCount = count;
 
-  late double totalTimeRatio =
-      safeDivide(inclusiveSampleCount, profileMetaData.sampleCount);
+  late double totalTimeRatio = safeDivide(
+    inclusiveSampleCount,
+    profileMetaData.sampleCount,
+  );
 
   late Duration totalTime = Duration(
     microseconds:
@@ -43,23 +45,21 @@
             .round(),
   );
 
-  late double selfTimeRatio =
-      safeDivide(exclusiveSampleCount, profileMetaData.sampleCount);
+  late double selfTimeRatio = safeDivide(
+    exclusiveSampleCount,
+    profileMetaData.sampleCount,
+  );
 
   late Duration selfTime = Duration(
     microseconds:
         (selfTimeRatio * profileMetaData.time!.duration.inMicroseconds).round(),
   );
 
-  double get inclusiveSampleRatio => safeDivide(
-        inclusiveSampleCount,
-        profileMetaData.sampleCount,
-      );
+  double get inclusiveSampleRatio =>
+      safeDivide(inclusiveSampleCount, profileMetaData.sampleCount);
 
-  double get exclusiveSampleRatio => safeDivide(
-        exclusiveSampleCount,
-        profileMetaData.sampleCount,
-      );
+  double get exclusiveSampleRatio =>
+      safeDivide(exclusiveSampleCount, profileMetaData.sampleCount);
 
   /// Returns the number of samples this data node is a part of.
   ///
@@ -96,10 +96,7 @@
 }
 
 class ProfileMetaData {
-  const ProfileMetaData({
-    required this.sampleCount,
-    required this.time,
-  });
+  const ProfileMetaData({required this.sampleCount, required this.time});
 
   final int sampleCount;
 
@@ -297,12 +294,7 @@
           ).buildTextSpan(context),
         );
       } else {
-        sourceTextSpans.add(
-          TextSpan(
-            text: sourceDisplay,
-            style: fontStyle,
-          ),
-        );
+        sourceTextSpans.add(TextSpan(text: sourceDisplay, style: fontStyle));
       }
     }
     final richText = RichText(
@@ -319,9 +311,7 @@
       // does not extend all the way to the end of the row.
       return Row(
         mainAxisSize: MainAxisSize.min,
-        children: [
-          Flexible(child: richText),
-        ],
+        children: [Flexible(child: richText)],
       );
     }
     return richText;
diff --git a/packages/devtools_app/lib/src/shared/utils.dart b/packages/devtools_app/lib/src/shared/utils/utils.dart
similarity index 92%
rename from packages/devtools_app/lib/src/shared/utils.dart
rename to packages/devtools_app/lib/src/shared/utils/utils.dart
index 3adb7f1..acb2659 100644
--- a/packages/devtools_app/lib/src/shared/utils.dart
+++ b/packages/devtools_app/lib/src/shared/utils/utils.dart
@@ -19,24 +19,21 @@
 import 'package:provider/provider.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../../devtools.dart' as devtools;
-
-import 'common_widgets.dart';
-import 'connected_app.dart';
-import 'globals.dart';
-import 'primitives/utils.dart';
-import 'query_parameters.dart';
+import '../../../devtools.dart' as devtools;
+import '../../service/connected_app/connected_app.dart';
+import '../globals.dart';
+import '../primitives/query_parameters.dart';
+import '../primitives/utils.dart';
+import '../ui/common_widgets.dart';
 
 final _log = Logger('lib/src/shared/utils');
 
 /// Logging to debug console only in debug runs.
 void debugLogger(String message) {
-  assert(
-    () {
-      _log.info(message);
-      return true;
-    }(),
-  );
+  assert(() {
+    _log.info(message);
+    return true;
+  }());
 }
 
 /// Whether DevTools is using a dark theme.
@@ -101,11 +98,7 @@
     vmServiceConnection = ConnectionDescription(
       title: 'VM Service Connection',
       description: description,
-      actions: [
-        CopyToClipboardControl(
-          dataProvider: () => description,
-        ),
-      ],
+      actions: [CopyToClipboardControl(dataProvider: () => description)],
     );
   }
 
@@ -124,7 +117,8 @@
       ),
       ConnectionDescription(
         title: 'Framework / Engine',
-        description: '${flutterVersion.frameworkRevision} / '
+        description:
+            '${flutterVersion.frameworkRevision} / '
             '${flutterVersion.engineRevision}',
       ),
     },
@@ -148,8 +142,9 @@
       connectedApp,
       includeVmServiceConnection: false,
     );
-    final deviceDescription = descriptionEntries
-        .map((entry) => '${entry.title}: ${entry.description}');
+    final deviceDescription = descriptionEntries.map(
+      (entry) => '${entry.title}: ${entry.description}',
+    );
     issueDescriptionItems.addAll([
       '**Connected Device**:',
       ...deviceDescription,
@@ -252,10 +247,8 @@
   /// [callback] is triggered once immediately, and then every [duration] the
   /// timer checks to see if the previous [callback] call has finished running.
   /// If it has finished, then then next call to [callback] will begin.
-  DebounceTimer.periodic(
-    Duration duration,
-    Future<void> Function() callback,
-  ) : _callback = callback {
+  DebounceTimer.periodic(Duration duration, Future<void> Function() callback)
+    : _callback = callback {
     // Start running the first call to the callback.
     _runCallback();
 
@@ -347,9 +340,11 @@
 
       final chunkUpperIndexLimit = min(length, chunkStartingIndex + _chunkSize);
 
-      for (int indexIterator = chunkStartingIndex;
-          indexIterator < chunkUpperIndexLimit;
-          indexIterator++) {
+      for (
+        int indexIterator = chunkStartingIndex;
+        indexIterator < chunkUpperIndexLimit;
+        indexIterator++
+      ) {
         // If our localWorkId is no longer active, then do not continue working
         if (localWorkId != _workId) return completer.complete(false);
         callback(indexIterator);
diff --git a/packages/devtools_app/lib/src/shared/vm_utils.dart b/packages/devtools_app/lib/src/shared/utils/vm_utils.dart
similarity index 85%
rename from packages/devtools_app/lib/src/shared/vm_utils.dart
rename to packages/devtools_app/lib/src/shared/utils/vm_utils.dart
index 3e3f009..04367e1 100644
--- a/packages/devtools_app/lib/src/shared/vm_utils.dart
+++ b/packages/devtools_app/lib/src/shared/utils/vm_utils.dart
@@ -5,8 +5,8 @@
 import 'package:collection/collection.dart';
 import 'package:vm_service/vm_service.dart';
 
-import 'globals.dart';
-import 'memory/class_name.dart';
+import '../globals.dart';
+import '../memory/class_name.dart';
 
 bool isPrimitiveInstanceKind(String? kind) {
   return kind == InstanceKind.kBool ||
@@ -41,13 +41,10 @@
     isolateId,
     classId,
     preferences.memory.refLimit.value,
-  ))
-      .instances
-      ?.firstWhereOrNull(
-        (instance) =>
-            (instance is InstanceRef) &&
-            (instance.identityHashCode == hashCode),
-      );
+  )).instances?.firstWhereOrNull(
+    (instance) =>
+        (instance is InstanceRef) && (instance.identityHashCode == hashCode),
+  );
 
   if (result is InstanceRef) return result;
   return null;
diff --git a/packages/devtools_app/lib/src/standalone_ui/api/dart_tooling_api.dart b/packages/devtools_app/lib/src/standalone_ui/api/dart_tooling_api.dart
deleted file mode 100644
index 4bd43a7..0000000
--- a/packages/devtools_app/lib/src/standalone_ui/api/dart_tooling_api.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'vs_code_api.dart';
-
-/// *** LEGACY API for postMessage, replaced by DTD version (`EditorClient`) ***
-///
-/// An API exposed to Dart tooling surfaces.
-///
-/// APIs are grouped into child APIs that are exposed as fields. Each field is a
-/// `Future` that will return null if the requested API is unavailable (for
-/// example the VS Code APIs if not running inside VS Code, or the LSP APIs if
-/// no LSP server is available).
-abstract interface class PostMessageToolApi {
-  /// Access to APIs provided by VS Code and/or the Dart/Flutter VS Code
-  /// extensions.
-  Future<VsCodeApi?> get vsCode;
-
-  void dispose();
-}
diff --git a/packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart b/packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart
deleted file mode 100644
index 30a6567..0000000
--- a/packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:devtools_app_shared/utils.dart';
-import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2;
-import 'package:logging/logging.dart';
-import 'package:stream_channel/stream_channel.dart';
-
-import '../../../service/editor/api_classes.dart';
-import '../../../service/editor/editor_client.dart';
-import '../../../shared/analytics/constants.dart';
-import '../../../shared/config_specific/logger/logger_helpers.dart';
-import '../../../shared/config_specific/post_message/post_message.dart';
-import '../../../shared/constants.dart';
-import '../dart_tooling_api.dart';
-import '../vs_code_api.dart';
-import 'vs_code_api.dart';
-
-// TODO(https://github.com/flutter/devtools/issues/7055): migrate away from
-// postMessage and use the Dart Tooling Daemon to communicate between Dart
-// tooling surfaces.
-
-/// Whether to enable verbose logging for postMessage communication.
-///
-/// This is useful for debugging when running inside VS Code.
-///
-/// TODO(dantup): Make a way for this to be enabled by users at runtime for
-///  troubleshooting. This could be via a message from VS Code, or something
-///  that passes a query param.
-const _enablePostMessageVerboseLogging = false;
-
-final _log = Logger('tooling_api');
-
-/// *** LEGACY API for postMessage, replaced by DTD version (`EditorClient`) ***
-///
-/// An API used by Dart tooling surfaces to interact with Dart tools that expose
-/// APIs such as Dart-Code and the LSP server.
-class PostMessageToolApiImpl implements PostMessageToolApi {
-  PostMessageToolApiImpl.rpc(this._rpc) {
-    unawaited(_rpc.listen());
-  }
-
-  /// Connects the API using 'postMessage'. This is only available when running
-  /// on web and hosted inside an iframe (such as inside a VS Code webview).
-  factory PostMessageToolApiImpl.postMessage() {
-    if (_enablePostMessageVerboseLogging) {
-      setDevToolsLoggingLevel(verboseLoggingLevel);
-    }
-    final postMessageController = StreamController<Object?>();
-    postMessageController.stream.listen((message) {
-      // TODO(dantup): Using fine here doesn't work even though the
-      // `setDevToolsLoggingLevel` call above seems like it should show finest
-      // logs. For now, use info (which always logs) with a condition here
-      // and below.
-      if (_enablePostMessageVerboseLogging) {
-        _log.info('==> $message');
-      }
-      postMessage(message, '*');
-    });
-    final channel = StreamChannel(
-      onPostMessage.map((event) {
-        if (_enablePostMessageVerboseLogging) {
-          _log.info('<== ${jsonEncode(event.data)}');
-        }
-        return event.data;
-      }),
-      postMessageController,
-    );
-    return PostMessageToolApiImpl.rpc(
-      json_rpc_2.Peer.withoutJson(channel),
-    );
-  }
-
-  final json_rpc_2.Peer _rpc;
-
-  /// An API that provides Access to APIs related to VS Code, such as executing
-  /// VS Code commands or interacting with the Dart/Flutter extensions.
-  ///
-  /// Lazy-initialized and completes with `null` if VS Code is not available.
-  @override
-  late final vsCode = VsCodeApiImpl.tryConnect(_rpc);
-
-  @override
-  void dispose() {
-    unawaited(_rpc.close());
-  }
-}
-
-/// A client for interacting with the legacy postMessage API with the same
-/// interface as the newer DTD-based [EditorClient].
-///
-/// This class allows the sidebar to use the new [EditorClient] APIs while still
-/// being compatible with postMessage based clients.
-class PostMessageEditorClient extends DisposableController
-    with AutoDisposeControllerMixin
-    implements EditorClient {
-  PostMessageEditorClient(this._api) {
-    // In PostMessage world, we just get events with the entire new list so
-    // we must figure out what the actual changes are so we can produce the
-    // same kinds of events as the new DTD version.
-    autoDisposeStreamSubscription(_api.devicesChanged.listen(_devicesChanged));
-    autoDisposeStreamSubscription(
-      _api.debugSessionsChanged.listen(_debugSessionsChanged),
-    );
-
-    // Trigger the initial initialization now we have the handlers set up.
-    // In the old postMessage world, this is how we get the initial set of
-    // devices/sessions.
-    unawaited(_api.initialize());
-  }
-
-  @override
-  String get gaId => EditorSidebar.legacyId;
-
-  /// Handles the `postMessage` [VsCodeDevicesEvent] and converts the updates
-  /// into events in the format of the new DTD `editor` event stream.
-  void _devicesChanged(VsCodeDevicesEvent e) {
-    final supportedDevices = e.devices.map(
-      (d) => EditorDevice.fromJson({...d.toJson(), 'supported': true}),
-    );
-    final unsupportedDevices = e.unsupportedDevices?.map(
-          (d) => EditorDevice.fromJson({...d.toJson(), 'supported': false}),
-        ) ??
-        <EditorDevice>[];
-    final newDevices = supportedDevices.followedBy(unsupportedDevices).toList();
-    final newIds = newDevices.map((d) => d.id).toSet();
-    final oldIds = _currentDevices.map((d) => d.id).toSet();
-
-    // Devices that are not in the new set have been removed.
-    for (final id in oldIds.difference(newIds)) {
-      _eventController.add(DeviceRemovedEvent(deviceId: id));
-    }
-    // Devices in the new set have either been changed or were added.
-    for (final device in newDevices) {
-      if (oldIds.contains(device.id)) {
-        _eventController.add(DeviceChangedEvent(device: device));
-      } else {
-        _eventController.add(DeviceAddedEvent(device: device));
-      }
-    }
-    // And record the updated set.
-    _currentDevices
-      ..clear()
-      ..addAll(newDevices);
-
-    // Finally, handle if the selection changed.
-    if (e.selectedDeviceId != _currentSelectedDeviceId) {
-      _currentSelectedDeviceId = e.selectedDeviceId;
-      _eventController.add(
-        DeviceSelectedEvent(deviceId: _currentSelectedDeviceId),
-      );
-    }
-  }
-
-  /// Handles the `postMessage` [VsCodeDebugSessionsEvent] and converts the
-  /// updates into events in the format of the new DTD `editor` event stream.
-  void _debugSessionsChanged(VsCodeDebugSessionsEvent e) {
-    final newIds = e.sessions.map((d) => d.id).toSet();
-    final oldIds = _currentDebugSessions.map((d) => d.id).toSet();
-
-    // Sessions that are not in the new set have been removed.
-    for (final id in oldIds.difference(newIds)) {
-      _eventController.add(DebugSessionStoppedEvent(debugSessionId: id));
-    }
-    // Sessions in the new set have either been changed or were added.
-    for (final session in e.sessions) {
-      if (oldIds.contains(session.id)) {
-        _eventController.add(DebugSessionChangedEvent(debugSession: session));
-      } else {
-        _eventController.add(DebugSessionStartedEvent(debugSession: session));
-      }
-    }
-    // And record the updated set.
-    _currentDebugSessions
-      ..clear()
-      ..addAll(e.sessions);
-  }
-
-  final VsCodeApi _api;
-  final _currentDevices = <EditorDevice>[];
-  String? _currentSelectedDeviceId;
-  final _currentDebugSessions = <EditorDebugSession>[];
-  final _eventController = StreamController<EditorEvent>();
-
-  @override
-  Future<void> close() async {}
-
-  @override
-  Future<void> enablePlatformType(String platformType) async {
-    await _api.enablePlatformType(platformType);
-  }
-
-  @override
-  Stream<EditorEvent> get event => _eventController.stream;
-
-  @override
-  Stream<ServiceRegistrationChange> get editorServiceChanged =>
-      const Stream.empty();
-
-  @override
-  Future<GetDevicesResult> getDevices() async {
-    return GetDevicesResult(
-      devices: _currentDevices,
-      selectedDeviceId: _currentSelectedDeviceId,
-    );
-  }
-
-  @override
-  Future<GetDebugSessionsResult> getDebugSessions() async {
-    return GetDebugSessionsResult(debugSessions: _currentDebugSessions);
-  }
-
-  @override
-  Future<void> hotReload(String debugSessionId) async {
-    await _api.hotReload(debugSessionId);
-  }
-
-  @override
-  Future<void> hotRestart(String debugSessionId) async {
-    await _api.hotRestart(debugSessionId);
-  }
-
-  @override
-  Future<void> openDevToolsPage(
-    String? debugSessionId, {
-    String? page,
-    bool? forceExternal,
-    bool? requiresDebugSession,
-    bool? prefersDebugSession,
-  }) async {
-    await _api.openDevToolsPage(
-      debugSessionId,
-      page: page,
-      forceExternal: forceExternal,
-      requiresDebugSession: requiresDebugSession,
-      prefersDebugSession: prefersDebugSession,
-    );
-  }
-
-  @override
-  Future<void> selectDevice(EditorDevice? device) async {
-    await _api.selectDevice(device!.id);
-  }
-
-  @override
-  bool get supportsGetDevices => true; // Always true because we handle locally.
-
-  @override
-  bool get supportsGetDebugSessions =>
-      true; // Always true because we handle locally.
-
-  @override
-  bool get supportsSelectDevice => _api.capabilities.selectDevice;
-
-  @override
-  bool get supportsHotReload => _api.capabilities.hotReload;
-
-  @override
-  bool get supportsHotRestart => _api.capabilities.hotRestart;
-
-  @override
-  bool get supportsOpenDevToolsForceExternal =>
-      _api.capabilities.openDevToolsExternally;
-
-  @override
-  bool get supportsOpenDevToolsPage => _api.capabilities.openDevToolsPage;
-}
diff --git a/packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart b/packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
deleted file mode 100644
index 4a27f38..0000000
--- a/packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2;
-import 'package:meta/meta.dart';
-
-import '../../../service/editor/api_classes.dart';
-import '../vs_code_api.dart';
-
-final class VsCodeApiImpl implements VsCodeApi {
-  VsCodeApiImpl._(this.rpc, Map<String, Object?> capabilities) {
-    this.capabilities = VsCodeCapabilitiesImpl(capabilities);
-    devicesChanged = events(VsCodeApi.jsonDevicesChangedEvent)
-        .map(VsCodeDevicesEventImpl.fromJson);
-
-    debugSessionsChanged = events(VsCodeApi.jsonDebugSessionsChangedEvent)
-        .map(VsCodeDebugSessionsEventImpl.fromJson);
-  }
-
-  static Future<VsCodeApi?> tryConnect(json_rpc_2.Peer rpc) async {
-    final capabilities =
-        await VsCodeApiImpl.tryGetCapabilities(rpc, VsCodeApi.jsonApiName);
-    return capabilities != null ? VsCodeApiImpl._(rpc, capabilities) : null;
-  }
-
-  static Future<Map<String, Object?>?> tryGetCapabilities(
-    json_rpc_2.Peer rpc,
-    String apiName,
-  ) async {
-    try {
-      final response = await rpc.sendRequest('$apiName.getCapabilities')
-          as Map<Object?, Object?>;
-      return response.cast<String, Object?>();
-    } catch (_) {
-      // Any error initializing should disable this functionality.
-      return null;
-    }
-  }
-
-  @protected
-  final json_rpc_2.Peer rpc;
-
-  @protected
-  Future<T> sendRequest<T>(String method, [Object? parameters]) async {
-    return (await rpc.sendRequest('$apiName.$method', parameters)) as T;
-  }
-
-  /// Listens for an event '[apiName].[name]' that has a Map for parameters.
-  @protected
-  Stream<Map<String, Object?>> events(String name) {
-    final streamController = StreamController<Map<String, Object?>>.broadcast();
-    unawaited(rpc.done.then((_) => streamController.close()));
-    rpc.registerMethod('$apiName.$name', (json_rpc_2.Parameters parameters) {
-      streamController.add(parameters.asMap.cast<String, Object?>());
-    });
-    return streamController.stream;
-  }
-
-  @override
-  Future<void> initialize() => sendRequest(VsCodeApi.jsonInitializeMethod);
-
-  String get apiName => VsCodeApi.jsonApiName;
-
-  @override
-  late final Stream<VsCodeDevicesEvent> devicesChanged;
-
-  @override
-  late final Stream<VsCodeDebugSessionsEvent> debugSessionsChanged;
-
-  @override
-  late final VsCodeCapabilities capabilities;
-
-  @override
-  Future<bool> selectDevice(String id) {
-    return sendRequest(
-      VsCodeApi.jsonSelectDeviceMethod,
-      {VsCodeApi.jsonIdParameter: id},
-    );
-  }
-
-  @override
-  Future<bool> enablePlatformType(String platformType) {
-    return sendRequest(
-      VsCodeApi.jsonEnablePlatformTypeMethod,
-      {VsCodeApi.jsonPlatformTypeParameter: platformType},
-    );
-  }
-
-  @override
-  Future<void> openDevToolsPage(
-    String? debugSessionId, {
-    String? page,
-    bool? forceExternal,
-    bool? requiresDebugSession,
-    bool? prefersDebugSession,
-  }) {
-    return sendRequest(
-      VsCodeApi.jsonOpenDevToolsPageMethod,
-      {
-        VsCodeApi.jsonDebugSessionIdParameter: debugSessionId,
-        VsCodeApi.jsonPageParameter: page,
-        VsCodeApi.jsonForceExternalParameter: forceExternal,
-        VsCodeApi.jsonRequiresDebugSessionParameter: requiresDebugSession,
-        VsCodeApi.jsonPrefersDebugSessionParameter: prefersDebugSession,
-      },
-    );
-  }
-
-  @override
-  Future<void> hotReload(String debugSessionId) {
-    return sendRequest(
-      VsCodeApi.jsonHotReloadMethod,
-      {
-        VsCodeApi.jsonDebugSessionIdParameter: debugSessionId,
-      },
-    );
-  }
-
-  @override
-  Future<void> hotRestart(String debugSessionId) {
-    return sendRequest(
-      VsCodeApi.jsonHotRestartMethod,
-      {
-        VsCodeApi.jsonDebugSessionIdParameter: debugSessionId,
-      },
-    );
-  }
-}
-
-class VsCodeDevicesEventImpl implements VsCodeDevicesEvent {
-  VsCodeDevicesEventImpl({
-    required this.selectedDeviceId,
-    required this.devices,
-    required this.unsupportedDevices,
-  });
-
-  VsCodeDevicesEventImpl.fromJson(Map<String, Object?> json)
-      : this(
-          selectedDeviceId:
-              json[VsCodeDevicesEvent.jsonSelectedDeviceIdField] as String?,
-          devices: (json[VsCodeDevicesEvent.jsonDevicesField] as List)
-              .map((item) => Map<String, Object?>.from(item))
-              .map(
-                (map) => EditorDevice.fromJson({
-                  'supported': true,
-                  ...map,
-                }),
-              )
-              .toList(),
-          unsupportedDevices:
-              (json[VsCodeDevicesEvent.jsonUnsupportedDevicesField] as List?)
-                  ?.map((item) => Map<String, Object?>.from(item))
-                  .map(
-                    (map) => EditorDevice.fromJson({
-                      'supported': false,
-                      ...map,
-                    }),
-                  )
-                  .toList(),
-        );
-
-  @override
-  final String? selectedDeviceId;
-
-  @override
-  final List<EditorDevice> devices;
-
-  @override
-  final List<EditorDevice>? unsupportedDevices;
-
-  Map<String, Object?> toJson() => {
-        VsCodeDevicesEvent.jsonSelectedDeviceIdField: selectedDeviceId,
-        VsCodeDevicesEvent.jsonDevicesField: devices,
-        VsCodeDevicesEvent.jsonUnsupportedDevicesField: unsupportedDevices,
-      };
-}
-
-class VsCodeDebugSessionsEventImpl implements VsCodeDebugSessionsEvent {
-  VsCodeDebugSessionsEventImpl({
-    required this.sessions,
-  });
-
-  VsCodeDebugSessionsEventImpl.fromJson(Map<String, Object?> json)
-      : this(
-          sessions: (json[VsCodeDebugSessionsEvent.jsonSessionsField] as List)
-              .map((item) => Map<String, Object?>.from(item))
-              .map((map) => EditorDebugSession.fromJson(map))
-              .toList(),
-        );
-
-  @override
-  final List<EditorDebugSession> sessions;
-
-  Map<String, Object?> toJson() => {
-        VsCodeDebugSessionsEvent.jsonSessionsField: sessions,
-      };
-}
-
-class VsCodeCapabilitiesImpl implements VsCodeCapabilities {
-  VsCodeCapabilitiesImpl(this._raw);
-
-  final Map<String, Object?>? _raw;
-
-  @override
-  bool get selectDevice =>
-      _raw?[VsCodeCapabilities.jsonSelectDeviceField] == true;
-
-  @override
-  bool get openDevToolsPage =>
-      _raw?[VsCodeCapabilities.openDevToolsPageField] == true;
-
-  @override
-  bool get openDevToolsExternally =>
-      _raw?[VsCodeCapabilities.openDevToolsExternallyField] == true;
-
-  @override
-  bool get openDevToolsWithOptionalDebugSessionFlags =>
-      _raw?[
-          VsCodeCapabilities.openDevToolsWithOptionalDebugSessionFlagsField] ==
-      true;
-
-  @override
-  bool get hotReload => _raw?[VsCodeCapabilities.hotReloadField] == true;
-
-  @override
-  bool get hotRestart => _raw?[VsCodeCapabilities.hotRestartField] == true;
-}
diff --git a/packages/devtools_app/lib/src/standalone_ui/api/vs_code_api.dart b/packages/devtools_app/lib/src/standalone_ui/api/vs_code_api.dart
deleted file mode 100644
index c1eb078..0000000
--- a/packages/devtools_app/lib/src/standalone_ui/api/vs_code_api.dart
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import '../../service/editor/api_classes.dart';
-
-/// This class defines the API exposed by the Dart/Flutter extensions in VS
-/// Code (and must match the implementation there).
-///
-/// All changes to this file should be backwards-compatible and use
-/// [VsCodeCapabilities] to advertise which capabilities are available and
-/// handle any changes in behaviour.
-abstract interface class VsCodeApi {
-  /// The capabilities of the instance of VS Code / Dart VS Code extension that
-  /// we are connected to.
-  ///
-  /// All API calls should be guarded by checks of capabilities because the API
-  /// may change over time.
-  VsCodeCapabilities get capabilities;
-
-  /// Informs the VS Code extension we are initialized, allowing it to send
-  /// initial events to all streams with an initial set of data.
-  Future<void> initialize();
-
-  /// A stream of events for whenever the set of devices (or selected device)
-  /// change in VS Code.
-  ///
-  /// An event with initial devices is sent after [initialize] is called.
-  Stream<VsCodeDevicesEvent> get devicesChanged;
-
-  /// A stream of events for whenever the set of debug sessions change or are
-  /// updated in VS Code.
-  ///
-  /// An event with initial sessions is sent after [initialize] is called.
-  Stream<VsCodeDebugSessionsEvent> get debugSessionsChanged;
-
-  /// Changes the current Flutter device.
-  ///
-  /// The selected device is the same one shown in the status bar in VS Code.
-  /// Calling this API will update the device for the whole VS Code extension.
-  Future<bool> selectDevice(String id);
-
-  /// Enables the selected platform type.
-  ///
-  /// Calling this method may cause a UI prompt in the editor to ask the user to
-  /// confirm they'd like to run `flutter create` to create the required files
-  /// for the project to support this platform type.
-  ///
-  /// This method has no capability because it is only (and always) valid to
-  /// call if some `unsupportedDevices` were provided in a device event.
-  Future<bool> enablePlatformType(String platformType);
-
-  /// Opens a specific DevTools [page] for the debug session with ID
-  /// [debugSessionId].
-  ///
-  /// Depending on user settings, this may open embedded (the default) or in an
-  /// external browser window.
-  ///
-  /// If [debugSessionId] is `null` the [requiresDebugSession] flag will
-  /// indicate whether the editor must select (or ask the user) for a debug
-  /// session. If [requiresDebugSession] is `false` but [prefersDebugSession] is
-  /// `true`, then the editor should use or prompt for a debug session if one
-  /// is available, but otherwise launch without a debug session.
-  ///
-  /// If [requiresDebugSession] is `null` (or if the
-  /// `openDevToolsWithOptionalDebugSessionFlags` capability is `false`) then
-  /// the editor will try to make this decision automatically (which may be
-  /// inaccurate for pages it does not know about, like extensions).
-  Future<void> openDevToolsPage(
-    String? debugSessionId, {
-    String? page,
-    bool? forceExternal,
-    bool? requiresDebugSession,
-    bool? prefersDebugSession,
-  });
-
-  /// Sends a Hot Reload request to the debug session with ID [debugSessionId].
-  Future<void> hotReload(String debugSessionId);
-
-  /// Sends a Hot Restart request to the debug session with ID [debugSessionId].
-  Future<void> hotRestart(String debugSessionId);
-
-  static const jsonApiName = 'vsCode';
-
-  static const jsonInitializeMethod = 'initialize';
-
-  static const jsonSelectDeviceMethod = 'selectDevice';
-  static const jsonOpenDevToolsPageMethod = 'openDevToolsPage';
-  static const jsonHotReloadMethod = 'hotReload';
-  static const jsonHotRestartMethod = 'hotRestart';
-  static const jsonEnablePlatformTypeMethod = 'enablePlatformType';
-
-  static const jsonDevicesChangedEvent = 'devicesChanged';
-  static const jsonDebugSessionsChangedEvent = 'debugSessionsChanged';
-
-  static const jsonCommandParameter = 'command';
-  static const jsonArgumentsParameter = 'arguments';
-  static const jsonIdParameter = 'id';
-  static const jsonPageParameter = 'page';
-  static const jsonForceExternalParameter = 'forceExternal';
-  static const jsonDebugSessionIdParameter = 'debugSessionId';
-  static const jsonPlatformTypeParameter = 'platformType';
-  static const jsonRequiresDebugSessionParameter = 'requiresDebugSession';
-  static const jsonPrefersDebugSessionParameter = 'prefersDebugSession';
-}
-
-/// This class defines a device event sent by the Dart/Flutter extensions in VS
-/// Code (and must match the implementation there).
-///
-/// All changes to this file should be backwards-compatible and use
-/// [VsCodeCapabilities] to advertise which capabilities are available and
-/// handle any changes in behaviour.
-abstract interface class VsCodeDevicesEvent {
-  /// The ID of the selected Flutter device in VS Code.
-  ///
-  /// This device can be changed with the `selectDevice` method but can also
-  /// be changed by the VS Code extension (which will emit a new event).
-  String? get selectedDeviceId;
-
-  /// A list of the devices that are available to select.
-  List<EditorDevice> get devices;
-
-  /// A list of the devices that are unavailable to select because the platform
-  /// is not enabled.
-  ///
-  /// A devices platform type can be enabled by calling the `enablePlatformType`
-  /// method.
-  ///
-  /// This field is nullable because it was not in the initial sidebar API so
-  /// older versions of VS Code might not provide it.
-  List<EditorDevice>? get unsupportedDevices;
-
-  static const jsonSelectedDeviceIdField = 'selectedDeviceId';
-  static const jsonDevicesField = 'devices';
-  static const jsonUnsupportedDevicesField = 'unsupportedDevices';
-}
-
-/// This class defines a debug session event sent by the Dart/Flutter extensions
-/// in VS Code (and must match the implementation there).
-///
-/// All changes to this file should be backwards-compatible and use
-/// [VsCodeCapabilities] to advertise which capabilities are available and
-/// handle any changes in behaviour.
-abstract interface class VsCodeDebugSessionsEvent {
-  /// A list of debug sessions that are currently active in VS Code.
-  List<EditorDebugSession> get sessions;
-
-  static const jsonSessionsField = 'sessions';
-}
-
-/// This class defines the capabilities provided by the current version of the
-/// Dart/Flutter extensions in VS Code.
-///
-/// All changes to this file should be backwards-compatible and use
-/// [VsCodeCapabilities] to advertise which capabilities are available and
-/// handle any changes in behaviour.
-abstract interface class VsCodeCapabilities {
-  /// Whether the `selectDevice` method is available to call to change the
-  /// selected Flutter device.
-  bool get selectDevice;
-
-  /// Whether the `openDevToolsPage` method is available call to open a specific
-  /// DevTools page.
-  bool get openDevToolsPage;
-
-  /// Whether the `openDevToolsPage` method can be called without a `page`
-  /// argument and with a 'forceExternal` flag to open DevTools in a browser
-  /// regardless of user settings.
-  bool get openDevToolsExternally;
-
-  /// Whether the `openDevToolsPage` method can be called with the
-  /// `requiresDebugSession` and `prefersDebugSession` flags to indicate
-  /// whether the editor should select/prompt for a debug session if one was not
-  /// provided.
-  bool get openDevToolsWithOptionalDebugSessionFlags;
-
-  /// Whether the `hotReload` method is available call to hot reload a specific
-  /// debug session.
-  bool get hotReload;
-
-  /// Whether the `hotRestart` method is available call to restart a specific
-  /// debug session.
-  bool get hotRestart;
-
-  static const jsonSelectDeviceField = 'selectDevice';
-  static const openDevToolsPageField = 'openDevToolsPage';
-  static const openDevToolsExternallyField = 'openDevToolsExternally';
-  static const openDevToolsWithOptionalDebugSessionFlagsField =
-      'openDevToolsWithOptionalDebugSessionFlags';
-  static const hotReloadField = 'hotReload';
-  static const hotRestartField = 'hotRestart';
-}
diff --git a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_sidebar.dart b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_sidebar.dart
new file mode 100644
index 0000000..1b64d51
--- /dev/null
+++ b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_sidebar.dart
@@ -0,0 +1,280 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+import '../../../shared/primitives/utils.dart';
+
+class PropertyEditorSidebar extends StatelessWidget {
+  const PropertyEditorSidebar({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text('Property Editor', style: Theme.of(context).textTheme.titleMedium),
+        const PaddedDivider.noPadding(),
+        const _PropertiesList(),
+      ],
+    );
+  }
+}
+
+class _PropertiesList extends StatelessWidget {
+  const _PropertiesList();
+
+  static const itemPadding = densePadding;
+
+  @override
+  Widget build(BuildContext context) {
+    // TODO(https://github.com/flutter/devtools/issues/8546) Switch to scrollable
+    // ListView when this has been moved into its own panel.
+    return Column(
+      children: <Widget>[
+        ..._properties.map(
+          (property) => _EditablePropertyItem(property: property),
+        ),
+      ].joinWith(const PaddedDivider.noPadding()),
+    );
+  }
+}
+
+class _EditablePropertyItem extends StatelessWidget {
+  const _EditablePropertyItem({required this.property});
+
+  final _WidgetProperty property;
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Flexible(
+          flex: 3,
+          child: Padding(
+            padding: const EdgeInsets.all(_PropertiesList.itemPadding),
+            child: _PropertyInput(property: property),
+          ),
+        ),
+        if (property.isRequired || property.isDefault) ...[
+          Flexible(child: _PropertyLabels(property: property)),
+        ] else
+          const Spacer(),
+      ],
+    );
+  }
+}
+
+class _PropertyLabels extends StatelessWidget {
+  const _PropertyLabels({required this.property});
+
+  final _WidgetProperty property;
+
+  @override
+  Widget build(BuildContext context) {
+    final colorScheme = Theme.of(context).colorScheme;
+    final isRequired = property.isRequired;
+    final isDefault = property.isDefault;
+
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        if (isRequired)
+          Padding(
+            padding: const EdgeInsets.all(_PropertiesList.itemPadding),
+            child: RoundedLabel(
+              labelText: 'required',
+              backgroundColor: colorScheme.primary,
+              textColor: colorScheme.onPrimary,
+            ),
+          ),
+        if (isDefault)
+          const Padding(
+            padding: EdgeInsets.all(_PropertiesList.itemPadding),
+            child: RoundedLabel(labelText: 'default'),
+          ),
+      ],
+    );
+  }
+}
+
+class _PropertyInput extends StatelessWidget {
+  const _PropertyInput({required this.property});
+
+  final _WidgetProperty property;
+
+  @override
+  Widget build(BuildContext context) {
+    final decoration = InputDecoration(
+      helperText: '',
+      errorText: property.errorText,
+      isDense: true,
+      label: Text(property.name),
+      border: const OutlineInputBorder(),
+    );
+
+    switch (property.type) {
+      case 'enum':
+      case 'bool':
+        final options =
+            property.type == 'bool' ? ['true', 'false'] : property.options;
+        return DropdownButtonFormField(
+          value: property.valueDisplay,
+          decoration: decoration,
+          items:
+              (options ?? []).map((option) {
+                return DropdownMenuItem(
+                  value: option,
+                  // TODO(https://github.com/flutter/devtools/issues/8531) Handle onTap.
+                  onTap: () {},
+                  child: Text(option),
+                );
+              }).toList(),
+          onChanged: (_) {},
+        );
+      case 'double':
+      case 'int':
+      case 'string':
+        return TextFormField(
+          initialValue: property.valueDisplay,
+          enabled: property.isEditable,
+          autovalidateMode: AutovalidateMode.onUserInteraction,
+          validator: _inputValidator,
+          inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],
+          decoration: decoration,
+          style: Theme.of(context).fixedFontStyle,
+          // TODO(https://github.com/flutter/devtools/issues/8531) Handle onChanged.
+          onChanged: (_) {},
+        );
+      default:
+        return Text(property.valueDisplay);
+    }
+  }
+
+  String? _inputValidator(String? inputValue) {
+    final isDouble = property.type == 'double';
+    final isInt = property.type == 'int';
+
+    // Only validate numeric types.
+    if (!isDouble && !isInt) {
+      return null;
+    }
+
+    final validationMessage =
+        'Please enter ${isInt ? 'an integer' : 'a double'}.';
+    if (inputValue == null || inputValue == '') {
+      return validationMessage;
+    }
+    final numValue =
+        isInt ? int.tryParse(inputValue) : double.tryParse(inputValue);
+    if (numValue == null) {
+      return validationMessage;
+    }
+    return null;
+  }
+}
+
+class _WidgetProperty {
+  const _WidgetProperty({
+    required this.name,
+    required this.type,
+    required this.isNullable,
+    this.value,
+    this.displayValue,
+    this.isEditable = true,
+    this.isRequired = false,
+    this.hasArgument = true,
+    this.isDefault = false,
+    this.errorText,
+    this.options,
+    // ignore: unused_element_parameter, TODO(https://github.com/flutter/devtools/issues/8532): Support colors.
+    this.swatches,
+    // ignore: unused_element_parameter, TODO(https://github.com/flutter/devtools/issues/8532): Support objects.
+    this.properties,
+  });
+
+  final String name;
+  final String type;
+  final bool isNullable;
+  final Object? value;
+  final String? displayValue;
+  final bool isEditable;
+  final bool isRequired;
+  final bool hasArgument;
+  final bool isDefault;
+  final String? errorText;
+  final List<String>? options;
+  final List<String>? swatches;
+  final List<_WidgetProperty>? properties;
+
+  String get valueDisplay => displayValue ?? value.toString();
+}
+
+// TODO(https://github.com/flutter/devtools/issues/8531): Connect to DTD and delete hard-coded properties.
+const _titleProperty = _WidgetProperty(
+  name: 'title',
+  value: 'Hello world!',
+  type: 'string',
+  isNullable: false,
+  isRequired: true,
+  hasArgument: false,
+);
+
+const _widthProperty = _WidgetProperty(
+  name: 'width',
+  displayValue: '100.0',
+  type: 'double',
+  isEditable: false,
+  errorText: 'Some reason for why this can\'t be edited.',
+  isNullable: false,
+  value: 20.0,
+  isRequired: true,
+  hasArgument: false,
+);
+
+const _heightProperty = _WidgetProperty(
+  name: 'height',
+  type: 'double',
+  isNullable: false,
+  value: 20.0,
+  isDefault: true,
+  isRequired: true,
+);
+
+const _softWrapProperty = _WidgetProperty(
+  name: 'softWrap',
+  type: 'bool',
+  isNullable: false,
+  value: true,
+  isDefault: true,
+);
+
+const _alignProperty = _WidgetProperty(
+  name: 'align',
+  type: 'enum',
+  isNullable: false,
+  value: 'Alignment.center',
+  options: [
+    'Alignment.bottomCenter',
+    'Alignment.bottomLeft',
+    'Alignment.bottomRight',
+    'Alignment.center',
+    'Alignment.centerLeft',
+    'Alignment.centerRight',
+    'Alignment.topCenter',
+    'Alignment.topLeft',
+    'Alignment.topRight',
+  ],
+);
+
+const _properties = [
+  _titleProperty,
+  _widthProperty,
+  _heightProperty,
+  _alignProperty,
+  _softWrapProperty,
+];
diff --git a/packages/devtools_app/lib/src/standalone_ui/standalone_screen.dart b/packages/devtools_app/lib/src/standalone_ui/standalone_screen.dart
index 1af27cd..bb5954f 100644
--- a/packages/devtools_app/lib/src/standalone_ui/standalone_screen.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/standalone_screen.dart
@@ -4,9 +4,8 @@
 
 import 'package:flutter/material.dart';
 
-import '../shared/common_widgets.dart';
 import '../shared/globals.dart';
-import 'api/impl/dart_tooling_api.dart';
+import '../shared/ui/common_widgets.dart';
 import 'vs_code/flutter_panel.dart';
 
 /// "Screens" that are intended for standalone use only, likely for embedding
@@ -16,24 +15,32 @@
 /// meaning that this screen will not be part of DevTools' normal navigation.
 /// The only way to access a standalone screen is directly from the url.
 enum StandaloneScreenType {
+  // TODO(elliette): Add property editor as a standalone screen, see:
+  // https://github.com/flutter/devtools/issues/8546
   editorSidebar,
-  vsCodeFlutterPanel; // Legacy postMessage version.
+  vsCodeFlutterPanel; // Legacy postMessage version, shows an upgrade message.
 
   Widget get screen {
     return switch (this) {
-      StandaloneScreenType.vsCodeFlutterPanel =>
-        VsCodePostMessageSidebarPanel(PostMessageToolApiImpl.postMessage()),
-      StandaloneScreenType.editorSidebar => ValueListenableBuilder(
-          // TODO(dantup): Add a timeout here so if dtdManager.connection
-          //  doesn't complete after some period we can give some kind of
-          //  useful message.
-          valueListenable: dtdManager.connection,
-          builder: (context, data, _) {
-            return data == null
-                ? const CenteredCircularProgressIndicator()
-                : DtdEditorSidebarPanel(data);
-          },
+      StandaloneScreenType.vsCodeFlutterPanel => const Padding(
+        padding: EdgeInsets.all(8.0),
+        child: CenteredMessage(
+          message:
+              'The Flutter sidebar for this SDK requires v3.96 or '
+              'newer of the Dart VS Code extension',
         ),
+      ),
+      StandaloneScreenType.editorSidebar => ValueListenableBuilder(
+        // TODO(dantup): Add a timeout here so if dtdManager.connection
+        //  doesn't complete after some period we can give some kind of
+        //  useful message.
+        valueListenable: dtdManager.connection,
+        builder: (context, data, _) {
+          return data == null
+              ? const CenteredCircularProgressIndicator()
+              : EditorSidebarPanel(data);
+        },
+      ),
     };
   }
 }
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/debug_sessions.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/debug_sessions.dart
index 8ee9ef4..f63616d 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/debug_sessions.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/debug_sessions.dart
@@ -34,10 +34,7 @@
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
-        Text(
-          'Debug Sessions',
-          style: Theme.of(context).textTheme.titleMedium,
-        ),
+        Text('Debug Sessions', style: Theme.of(context).textTheme.titleMedium),
         if (sessions.isEmpty)
           const Padding(
             padding: EdgeInsets.only(left: borderPadding),
@@ -45,11 +42,10 @@
           )
         else
           Table(
-            columnWidths: const {
-              0: FlexColumnWidth(),
-            },
-            defaultColumnWidth:
-                FixedColumnWidth(actionsIconSize + denseSpacing),
+            columnWidths: const {0: FlexColumnWidth()},
+            defaultColumnWidth: FixedColumnWidth(
+              actionsIconSize + denseSpacing,
+            ),
             defaultVerticalAlignment: TableCellVerticalAlignment.middle,
             children: [
               for (final session in sessions.values)
@@ -65,39 +61,33 @@
     final isDebug = mode == 'debug';
     final isFlutter = session.debuggerType?.contains('Flutter') ?? false;
 
-    final label = session.flutterMode != null
-        ? '${session.name} (${session.flutterMode})'
-        : session.name;
+    final label =
+        session.flutterMode != null
+            ? '${session.name} (${session.flutterMode})'
+            : session.name;
 
     return TableRow(
       children: [
-        Text(
-          label,
-          style: Theme.of(context).regularTextStyle,
-        ),
+        Text(label, style: Theme.of(context).regularTextStyle),
         IconButton(
-          onPressed: editor.supportsHotReload && (isDebug || !isFlutter)
-              ? () {
-                  ga.select(
-                    editor.gaId,
-                    gac.hotReload,
-                  );
-                  unawaited(editor.hotReload(session.id));
-                }
-              : null,
+          onPressed:
+              editor.supportsHotReload && (isDebug || !isFlutter)
+                  ? () {
+                    ga.select(editor.gaId, gac.hotReload);
+                    unawaited(editor.hotReload(session.id));
+                  }
+                  : null,
           tooltip: 'Hot Reload',
           icon: Icon(hotReloadIcon, size: actionsIconSize),
         ),
         IconButton(
-          onPressed: editor.supportsHotRestart && (isDebug || !isFlutter)
-              ? () {
-                  ga.select(
-                    editor.gaId,
-                    gac.hotRestart,
-                  );
-                  unawaited(editor.hotRestart(session.id));
-                }
-              : null,
+          onPressed:
+              editor.supportsHotRestart && (isDebug || !isFlutter)
+                  ? () {
+                    ga.select(editor.gaId, gac.hotRestart);
+                    unawaited(editor.hotRestart(session.id));
+                  }
+                  : null,
           tooltip: 'Hot Restart',
           icon: Icon(hotRestartIcon, size: actionsIconSize),
         ),
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/devices.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/devices.dart
index 574a707..3890fe5 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/devices.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/devices.dart
@@ -18,15 +18,16 @@
     required Map<String, EditorDevice> devices,
     required this.selectedDeviceId,
     super.key,
-  })  : supportedDevices = {
-          for (final MapEntry(key: id, value: device) in devices.entries)
-            if (device.supported) id: device,
-        },
-        unsupportedDevicePlatformTypes = devices.values
-            .where((device) => !device.supported)
-            .map((device) => device.platformType)
-            .nonNulls
-            .toSet();
+  }) : supportedDevices = {
+         for (final MapEntry(key: id, value: device) in devices.entries)
+           if (device.supported) id: device,
+       },
+       unsupportedDevicePlatformTypes =
+           devices.values
+               .where((device) => !device.supported)
+               .map((device) => device.platformType)
+               .nonNulls
+               .toSet();
 
   final EditorClient editor;
   final Map<String, EditorDevice> supportedDevices;
@@ -39,10 +40,7 @@
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
-        Text(
-          'Devices',
-          style: theme.textTheme.titleMedium,
-        ),
+        Text('Devices', style: theme.textTheme.titleMedium),
         if (supportedDevices.isEmpty)
           const Text('Connect a device or enable web/desktop platforms.')
         else
@@ -56,10 +54,7 @@
                   isSelected: device.id == selectedDeviceId,
                 ),
               for (final platformType in unsupportedDevicePlatformTypes)
-                _createPlatformTypeEnablerRow(
-                  theme,
-                  platformType,
-                ),
+                _createPlatformTypeEnablerRow(theme, platformType),
             ],
           ),
       ],
@@ -72,9 +67,10 @@
     required bool isSelected,
   }) {
     final backgroundColor = isSelected ? theme.colorScheme.secondary : null;
-    final foregroundColor = isSelected
-        ? theme.colorScheme.onSecondary
-        : theme.colorScheme.onSurface;
+    final foregroundColor =
+        isSelected
+            ? theme.colorScheme.onSecondary
+            : theme.colorScheme.onSurface;
 
     return TableRow(
       decoration: BoxDecoration(color: backgroundColor),
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/devtools_view.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/devtools_view.dart
index 458c7b6..fbc62f7 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/devtools_view.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/devtools_view.dart
@@ -11,9 +11,9 @@
 import '../../../service/editor/editor_client.dart';
 import '../../../shared/analytics/analytics.dart' as ga;
 import '../../../shared/analytics/constants.dart' as gac;
-import '../../../shared/common_widgets.dart';
+import '../../../shared/framework/screen.dart';
 import '../../../shared/primitives/utils.dart';
-import '../../../shared/screen.dart';
+import '../../../shared/ui/common_widgets.dart';
 import 'extensions_view.dart';
 import 'shared.dart';
 
@@ -79,10 +79,7 @@
         Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           children: [
-            Text(
-              'DevTools',
-              style: theme.textTheme.titleMedium,
-            ),
+            Text('DevTools', style: theme.textTheme.titleMedium),
             if (editor.supportsOpenDevToolsForceExternal)
               ToolbarAction(
                 icon: Icons.open_in_browser_outlined,
@@ -92,21 +89,20 @@
                     editor.gaId,
                     gac.EditorSidebar.openDevToolsExternally.name,
                   );
-                  unawaited(
-                    editor.openDevToolsPage(null, forceExternal: true),
-                  );
+                  unawaited(editor.openDevToolsPage(null, forceExternal: true));
                 },
               ),
           ],
         ),
         Table(
           defaultVerticalAlignment: TableCellVerticalAlignment.middle,
-          columnWidths: singleColumn
-              ? null
-              : const <int, TableColumnWidth>{
-                  0: FlexColumnWidth(),
-                  1: FlexColumnWidth(),
-                },
+          columnWidths:
+              singleColumn
+                  ? null
+                  : const <int, TableColumnWidth>{
+                    0: FlexColumnWidth(),
+                    1: FlexColumnWidth(),
+                  },
           children: generateRows(singleColumn),
         ),
       ],
@@ -125,12 +121,13 @@
               editor: editor,
               singleColumn: singleColumn,
               hasDebugSessions: debugSessions.isNotEmpty,
-              onPressed: (data) => openDevToolsScreen(
-                screenId: data.screenId,
-                requiresDebugSession: data.requiresDebugSession,
-                prefersDebugSession: data.prefersDebugSession,
-                editor: editor,
-              ),
+              onPressed:
+                  (data) => openDevToolsScreen(
+                    screenId: data.screenId,
+                    requiresDebugSession: data.requiresDebugSession,
+                    prefersDebugSession: data.prefersDebugSession,
+                    editor: editor,
+                  ),
             ),
           )
           .toList();
@@ -147,12 +144,13 @@
           editor: editor,
           singleColumn: singleColumn,
           hasDebugSessions: debugSessions.isNotEmpty,
-          onPressed: (data) => openDevToolsScreen(
-            screenId: data.screenId,
-            requiresDebugSession: data.requiresDebugSession,
-            prefersDebugSession: data.prefersDebugSession,
-            editor: editor,
-          ),
+          onPressed:
+              (data) => openDevToolsScreen(
+                screenId: data.screenId,
+                requiresDebugSession: data.requiresDebugSession,
+                prefersDebugSession: data.prefersDebugSession,
+                editor: editor,
+              ),
         ),
       );
     }
@@ -179,9 +177,10 @@
       ScreenMetaData.debugger ||
       ScreenMetaData.vmTools ||
       // This screen will be removed from the first party DevTools screens soon.
-      // If the user depends on package:provider, the provider extension should
-      // show up in the DevTools extensions list instead.
-      ScreenMetaData.provider ||
+          // If the user depends on package:provider, the provider extension should
+          // show up in the DevTools extensions list instead.
+          ScreenMetaData
+          .provider ||
       ScreenMetaData.simple =>
         false,
       _ => true,
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/shared.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/shared.dart
index c9a8fea..7a176b9 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/shared.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/shared.dart
@@ -12,14 +12,15 @@
 import '../../../shared/analytics/constants.dart' as gac;
 import '../../../shared/ui/icons.dart';
 
-typedef DevToolsButtonData = ({
-  String label,
-  IconData? icon,
-  String? iconAsset,
-  String screenId,
-  bool requiresDebugSession,
-  bool prefersDebugSession,
-});
+typedef DevToolsButtonData =
+    ({
+      String label,
+      IconData? icon,
+      String? iconAsset,
+      String screenId,
+      bool requiresDebugSession,
+      bool prefersDebugSession,
+    });
 
 TableRow createDevToolsScreenRow({
   required DevToolsButtonData dataLeft,
@@ -33,14 +34,15 @@
     !singleColumn || dataRight == null,
     'dataRight must be null is singleColumn is true',
   );
-  final cellRight = dataRight != null
-      ? _DevToolsScreenButton(
-          data: dataRight,
-          editor: editor,
-          hasDebugSessions: hasDebugSessions,
-          onPressed: onPressed,
-        )
-      : const SizedBox();
+  final cellRight =
+      dataRight != null
+          ? _DevToolsScreenButton(
+            data: dataRight,
+            editor: editor,
+            hasDebugSessions: hasDebugSessions,
+            onPressed: onPressed,
+          )
+          : const SizedBox();
   return TableRow(
     children: [
       _DevToolsScreenButton(
@@ -108,10 +110,7 @@
   required bool prefersDebugSession,
   required EditorClient editor,
 }) {
-  ga.select(
-    editor.gaId,
-    gac.EditorSidebar.openDevToolsScreen(screenId),
-  );
+  ga.select(editor.gaId, gac.EditorSidebar.openDevToolsScreen(screenId));
   unawaited(
     editor.openDevToolsPage(
       null,
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/sidebar_extensions_controller.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/sidebar_extensions_controller.dart
index b9b6795..c4a9071 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/sidebar_extensions_controller.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/devtools/sidebar_extensions_controller.dart
@@ -76,8 +76,9 @@
   /// [_deduplicateAndUpdate].
   ValueListenable<List<DevToolsExtensionConfig>> get uniqueExtensions =>
       _uniqueExtensions;
-  final _uniqueExtensions =
-      ValueNotifier<List<DevToolsExtensionConfig>>(<DevToolsExtensionConfig>[]);
+  final _uniqueExtensions = ValueNotifier<List<DevToolsExtensionConfig>>(
+    <DevToolsExtensionConfig>[],
+  );
 
   /// The set of extension hashcodes that have been ignored due to being a
   /// duplicate of some kind.
@@ -104,8 +105,9 @@
     await _initialized.future;
 
     // Cleanup state for debug sessions that are no longer available.
-    final removed =
-        _debugSessions.keys.toSet().difference(newDebugSessions.keys.toSet());
+    final removed = _debugSessions.keys.toSet().difference(
+      newDebugSessions.keys.toSet(),
+    );
     for (final sessionId in removed) {
       final session = _debugSessions[sessionId]!;
       final rootFileUri = session.projectRootFileUri;
@@ -123,8 +125,7 @@
         (await dtdManager.projectRoots(
               depth: staticExtensionsSearchDepth,
               forceRefresh: true,
-            ))
-                ?.uris ??
+            ))?.uris ??
             <Uri>[],
       );
     }
@@ -159,15 +160,16 @@
   // watch event from the IDE or analysis server to signal when any
   // package_config.json file in the workspace changes.
   Future<void> _detectExtensions(Uri? projectRootUri) async {
-    final extensionService = projectRootUri == null
-        ?
-        // A null [projectRootUri] indicates that this is an extensions lookup
-        // for the entire workspace, not a debug session with a fixed root URI.
-        ExtensionService(ignoreServiceConnection: true)
-        : ExtensionService(
-            fixedAppRoot: projectRootUri,
-            ignoreServiceConnection: true,
-          );
+    final extensionService =
+        projectRootUri == null
+            ?
+            // A null [projectRootUri] indicates that this is an extensions lookup
+            // for the entire workspace, not a debug session with a fixed root URI.
+            ExtensionService(ignoreServiceConnection: true)
+            : ExtensionService(
+              fixedAppRoot: projectRootUri,
+              ignoreServiceConnection: true,
+            );
     assert(
       !_extensionServiceByRootUri.containsKey(projectRootUri),
       'The initialization for the ExtensionService for root uri '
@@ -205,12 +207,13 @@
       extensionType: 'all',
     );
 
-    final deduped = allExtensions
-        .where(
-          (ext) =>
-              !_ignoredExtensionsByHashCode.contains(identityHashCode(ext)),
-        )
-        .toList();
+    final deduped =
+        allExtensions
+            .where(
+              (ext) =>
+                  !_ignoredExtensionsByHashCode.contains(identityHashCode(ext)),
+            )
+            .toList();
     _uniqueExtensions.value = deduped..sort();
   }
 
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
index 494319e..def6b2a 100644
--- a/packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
@@ -12,10 +12,7 @@
 import '../../service/editor/api_classes.dart';
 import '../../service/editor/editor_client.dart';
 import '../../shared/analytics/analytics.dart' as ga;
-import '../../shared/analytics/constants.dart' as gac;
-import '../../shared/common_widgets.dart';
-import '../api/dart_tooling_api.dart';
-import '../api/impl/dart_tooling_api.dart';
+import '../../shared/ui/common_widgets.dart';
 import 'debug_sessions.dart';
 import 'devices.dart';
 import 'devtools/devtools_view.dart';
@@ -24,19 +21,17 @@
 ///
 /// Provides some basic functionality to improve discoverability of features
 /// such as creation of new projects, device selection and DevTools features.
-class DtdEditorSidebarPanel extends StatefulWidget {
-  // TODO(dantup): Remove the Dtd prefix from these classes when the postMessage
-  //  versions are removed.
-  const DtdEditorSidebarPanel(this.dtd, {super.key});
+class EditorSidebarPanel extends StatefulWidget {
+  const EditorSidebarPanel(this.dtd, {super.key});
 
   final DartToolingDaemon dtd;
 
   @override
-  State<DtdEditorSidebarPanel> createState() => _DtdEditorSidebarPanelState();
+  State<EditorSidebarPanel> createState() => _EditorSidebarPanelState();
 }
 
-class _DtdEditorSidebarPanelState extends State<DtdEditorSidebarPanel> {
-  _DtdEditorSidebarPanelState();
+class _EditorSidebarPanelState extends State<EditorSidebarPanel> {
+  _EditorSidebarPanelState();
 
   Future<EditorClient>? _editor;
 
@@ -44,7 +39,7 @@
   void initState() {
     super.initState();
 
-    final editor = DtdEditorClient(widget.dtd);
+    final editor = EditorClient(widget.dtd);
     ga.screen(editor.gaId);
     unawaited(_editor = editor.initialized.then((_) => editor));
   }
@@ -56,12 +51,15 @@
         Expanded(
           child: FutureBuilder(
             future: _editor,
-            builder: (context, snapshot) =>
-                switch ((snapshot.connectionState, snapshot.data)) {
-              (ConnectionState.done, final editor?) =>
-                _EditorConnectedPanel(editor),
-              _ => const CenteredCircularProgressIndicator(),
-            },
+            builder:
+                (context, snapshot) => switch ((
+                  snapshot.connectionState,
+                  snapshot.data,
+                )) {
+                  (ConnectionState.done, final editor?) =>
+                    _EditorConnectedPanel(editor),
+                  _ => const CenteredCircularProgressIndicator(),
+                },
           ),
         ),
       ],
@@ -69,55 +67,6 @@
   }
 }
 
-/// A general Flutter sidebar panel for embedding inside postMessage-based
-/// editors.
-///
-/// Provides some basic functionality to improve discoverability of features
-/// such as creation of new projects, device selection and DevTools features.
-class VsCodePostMessageSidebarPanel extends StatefulWidget {
-  const VsCodePostMessageSidebarPanel(this.api, {super.key});
-
-  final PostMessageToolApi api;
-
-  @override
-  State<VsCodePostMessageSidebarPanel> createState() =>
-      _VsCodePostMessageSidebarPanelState();
-}
-
-class _VsCodePostMessageSidebarPanelState
-    extends State<VsCodePostMessageSidebarPanel> {
-  @override
-  void initState() {
-    super.initState();
-    ga.screen(gac.EditorSidebar.legacyId);
-  }
-
-  @override
-  void dispose() {
-    widget.api.dispose();
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      children: [
-        FutureBuilder(
-          future: widget.api.vsCode,
-          builder: (context, snapshot) =>
-              switch ((snapshot.connectionState, snapshot.data)) {
-            (ConnectionState.done, final vsCodeApi?) =>
-              _EditorConnectedPanel(PostMessageEditorClient(vsCodeApi)),
-            (ConnectionState.done, null) =>
-              const Text('VS Code is not available'),
-            _ => const CenteredCircularProgressIndicator(),
-          },
-        ),
-      ],
-    );
-  }
-}
-
 /// The panel shown once we know an editor is available.
 class _EditorConnectedPanel extends StatefulWidget {
   const _EditorConnectedPanel(this.editor);
diff --git a/packages/devtools_app/lib/src/standalone_ui/vs_code/property_editor_panel.dart b/packages/devtools_app/lib/src/standalone_ui/vs_code/property_editor_panel.dart
new file mode 100644
index 0000000..88d165e
--- /dev/null
+++ b/packages/devtools_app/lib/src/standalone_ui/vs_code/property_editor_panel.dart
@@ -0,0 +1,114 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:dtd/dtd.dart';
+import 'package:flutter/material.dart';
+
+import '../../service/editor/editor_client.dart';
+import '../../shared/analytics/analytics.dart' as ga;
+import '../../shared/analytics/constants.dart';
+import '../../shared/ui/common_widgets.dart';
+import '../ide_shared/property_editor/property_editor_sidebar.dart';
+
+/// The side panel for the Property Editor.
+class PropertyEditorSidebarPanel extends StatefulWidget {
+  const PropertyEditorSidebarPanel(this.dtd, {super.key});
+
+  final DartToolingDaemon dtd;
+
+  @override
+  State<PropertyEditorSidebarPanel> createState() =>
+      _PropertyEditorSidebarPanelState();
+}
+
+class _PropertyEditorSidebarPanelState
+    extends State<PropertyEditorSidebarPanel> {
+  _PropertyEditorSidebarPanelState();
+
+  Future<EditorClient>? _editor;
+
+  @override
+  void initState() {
+    super.initState();
+
+    final editor = EditorClient(widget.dtd);
+    ga.screen(PropertyEditorEvents.id);
+    unawaited(_editor = editor.initialized.then((_) => editor));
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Align(
+      alignment: Alignment.topCenter,
+      child: FutureBuilder(
+        future: _editor,
+        builder:
+            (context, snapshot) => switch ((
+              snapshot.connectionState,
+              snapshot.data,
+            )) {
+              (ConnectionState.done, final editor?) =>
+                _PropertyEditorConnectedPanel(editor),
+              _ => const CenteredCircularProgressIndicator(),
+            },
+      ),
+    );
+  }
+}
+
+/// The property editor panel shown once we know an editor is available.
+class _PropertyEditorConnectedPanel extends StatefulWidget {
+  const _PropertyEditorConnectedPanel(this.editor);
+
+  final EditorClient editor;
+
+  @override
+  State<_PropertyEditorConnectedPanel> createState() =>
+      _PropertyEditorConnectedPanelState();
+}
+
+class _PropertyEditorConnectedPanelState
+    extends State<_PropertyEditorConnectedPanel>
+    with AutoDisposeMixin {
+  late final ScrollController scrollController;
+
+  @override
+  void initState() {
+    super.initState();
+    scrollController = ScrollController();
+  }
+
+  @override
+  void dispose() {
+    scrollController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scrollbar(
+      controller: scrollController,
+      thumbVisibility: true,
+      child: SingleChildScrollView(
+        controller: scrollController,
+        child: const Padding(
+          padding: EdgeInsets.fromLTRB(
+            denseSpacing,
+            defaultSpacing,
+            defaultSpacing, // Additional right padding for scroll bar.
+            defaultSpacing,
+          ),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [PropertyEditorSidebar()],
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/packages/devtools_app/macos/Runner.xcodeproj/project.pbxproj b/packages/devtools_app/macos/Runner.xcodeproj/project.pbxproj
index bb5fef6..e9c8ca8 100644
--- a/packages/devtools_app/macos/Runner.xcodeproj/project.pbxproj
+++ b/packages/devtools_app/macos/Runner.xcodeproj/project.pbxproj
@@ -21,6 +21,7 @@
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+		11595299B00138FF6A219878 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46084CB9F244837191E61B73 /* Pods_RunnerTests.framework */; };
 		331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
 		335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
 		33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
@@ -62,6 +63,7 @@
 
 /* Begin PBXFileReference section */
 		11BB555C0F1767B9B5CB7CE0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+		13053082F27293B7166BCBED /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
 		331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
@@ -78,11 +80,14 @@
 		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
 		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
 		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+		46084CB9F244837191E61B73 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		5062035DDDD18FB35E98D5B6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		68C587FFA5A0B8F46A0C5150 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
 		A7CE48BF63861DD9F3A9FA2F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
+		BDEB23F5F07C7F498EB77EA2 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
+		E11974409F5281249C10F0E1 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -90,6 +95,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				11595299B00138FF6A219878 /* Pods_RunnerTests.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -185,6 +191,9 @@
 				11BB555C0F1767B9B5CB7CE0 /* Pods-Runner.debug.xcconfig */,
 				68C587FFA5A0B8F46A0C5150 /* Pods-Runner.release.xcconfig */,
 				A7CE48BF63861DD9F3A9FA2F /* Pods-Runner.profile.xcconfig */,
+				E11974409F5281249C10F0E1 /* Pods-RunnerTests.debug.xcconfig */,
+				BDEB23F5F07C7F498EB77EA2 /* Pods-RunnerTests.release.xcconfig */,
+				13053082F27293B7166BCBED /* Pods-RunnerTests.profile.xcconfig */,
 			);
 			name = Pods;
 			path = Pods;
@@ -194,6 +203,7 @@
 			isa = PBXGroup;
 			children = (
 				5062035DDDD18FB35E98D5B6 /* Pods_Runner.framework */,
+				46084CB9F244837191E61B73 /* Pods_RunnerTests.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -205,6 +215,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
 			buildPhases = (
+				3E485AF46E5EF6A810E8A04C /* [CP] Check Pods Manifest.lock */,
 				331C80D1294CF70F00263BE5 /* Sources */,
 				331C80D2294CF70F00263BE5 /* Frameworks */,
 				331C80D3294CF70F00263BE5 /* Resources */,
@@ -349,6 +360,28 @@
 			shellPath = /bin/sh;
 			shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
 		};
+		3E485AF46E5EF6A810E8A04C /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
 		DC1C8B6797A659BE5B59B986 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -439,6 +472,7 @@
 /* Begin XCBuildConfiguration section */
 		331C80DB294CF71000263BE5 /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = E11974409F5281249C10F0E1 /* Pods-RunnerTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CURRENT_PROJECT_VERSION = 1;
@@ -453,6 +487,7 @@
 		};
 		331C80DC294CF71000263BE5 /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = BDEB23F5F07C7F498EB77EA2 /* Pods-RunnerTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CURRENT_PROJECT_VERSION = 1;
@@ -467,6 +502,7 @@
 		};
 		331C80DD294CF71000263BE5 /* Profile */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 13053082F27293B7166BCBED /* Pods-RunnerTests.profile.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CURRENT_PROJECT_VERSION = 1;
diff --git a/packages/devtools_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/devtools_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 98cea03..cadfa60 100644
--- a/packages/devtools_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/devtools_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -59,6 +59,7 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
+      enableGPUValidationMode = "1"
       allowLocationSimulation = "YES">
       <BuildableProductRunnable
          runnableDebuggingMode = "0">
diff --git a/packages/devtools_app/pubspec.yaml b/packages/devtools_app/pubspec.yaml
index 931357a..67c2585 100644
--- a/packages/devtools_app/pubspec.yaml
+++ b/packages/devtools_app/pubspec.yaml
@@ -2,15 +2,17 @@
 description: Web-based performance tooling for Dart and Flutter.
 publish_to: none
 
-# Note: this version should only be updated by running tools/update_version.dart
-# that updates all versions of devtools packages (devtools_app, devtools_test).
-version: 2.40.2
+# Note: this version should only be updated by running the 'dt update-version'
+# command that updates the version here and in 'devtools.dart'.
+version: 2.42.0-dev.0
 
 repository: https://github.com/flutter/devtools/tree/master/packages/devtools_app
 
 environment:
-  sdk: ">=3.4.0-275.0.dev <4.0.0"
-  flutter: ">=3.21.0-15.0.pre"
+  sdk: ">=3.7.0-160.0.dev <4.0.0"
+  flutter: ">=3.27.0-1.0.pre.563"
+
+resolution: workspace
 
 dependencies:
   ansi_up: ^1.0.0
@@ -21,9 +23,9 @@
   collection: ^1.15.0
   dap: ^1.1.0
   dds_service_extensions: ^2.0.0
-  devtools_app_shared: ^0.2.2
-  devtools_extensions: ^0.3.0-wip
-  devtools_shared: ^10.0.1
+  devtools_app_shared:
+  devtools_extensions:
+  devtools_shared:
   dtd: ^2.4.0
   file: ^7.0.0
   file_selector: ^1.0.0
@@ -76,7 +78,7 @@
   mockito: ^5.4.1
   stager: ^1.0.1
   test: ^1.21.0
-  web_benchmarks: ^2.0.0
+  web_benchmarks: ^4.0.0
   webkit_inspection_protocol: ">=0.5.0 <2.0.0"
 
 flutter:
@@ -109,7 +111,7 @@
     - packages/perfetto_ui_compiled/dist/devtools/devtools_shared.css
     - packages/perfetto_ui_compiled/dist/devtools/devtools_theme_handler.js
     # The version number for all the Perfetto asset paths below is updated by running
-    # `devtools_tool update-perfetto`.
+    # `dt update-perfetto`.
     - packages/perfetto_ui_compiled/dist/v34.0-16f63abe3/engine_bundle.js
     - packages/perfetto_ui_compiled/dist/v34.0-16f63abe3/frontend_bundle.js
     - packages/perfetto_ui_compiled/dist/v34.0-16f63abe3/manifest.json
@@ -166,11 +168,3 @@
 dependency_overrides:
   ansi_up:
     path: ../../third_party/packages/ansi_up
-  devtools_shared:
-    path: ../devtools_shared
-  devtools_app_shared:
-    path: ../devtools_app_shared
-  devtools_test:
-    path: ../devtools_test
-  devtools_extensions:
-    path: ../devtools_extensions
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index a3aa786..037681b 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -1,41 +1,19 @@
 This is draft for future release notes, that are going to land on
 [the Flutter website](https://docs.flutter.dev/tools/devtools/release-notes).
 
-# DevTools 2.40.0 release notes
+# DevTools 2.41.0 release notes
 
-The 2.40.0 release of the Dart and Flutter DevTools
+The 2.41.0 release of the Dart and Flutter DevTools
 includes the following changes among other general improvements.
 To learn more about DevTools, check out the
 [DevTools overview](/tools/devtools/overview).
 
 ## General updates
 
-* Add a setting that allows users to opt-in to loading DevTools
-with WebAssembly. - [#8270](https://github.com/flutter/devtools/pull/8270)
-
-    ![Wasm opt-in setting](images/wasm_setting.png "DevTools setting to opt into wasm.")
-
-* Removed the legacy Provider screen from DevTools. The `package:provider` tool is now
-distributed as a DevTools extension from `package:provider`. Upgrade your `package:provider`
-dependency to use the extension. - [#8364](https://github.com/flutter/devtools/pull/8364)
-
-* Fixed a bug that was causing the DevTools release notes to always
-show. - [#8277](https://github.com/flutter/devtools/pull/8277)
-
-* Added support for loading extensions in pub workspaces
-  [8347](https://github.com/flutter/devtools/pull/8347).
-
-* Mapped error stacktraces to use the Dart source code locations so that they are human-
-  readable. - [#8385](https://github.com/flutter/devtools/pull/8385)
-
-* Added handling for IDE theme change events to update embedded DevTools UI. -
-[#8336](https://github.com/flutter/devtools/pull/8336)
-
-* Fixed a bug that was causing data filters to be cleared when clearing data
-on the Network and Logging screens. - [#8407](https://github.com/flutter/devtools/pull/8407)
-
-* Fixed a bug that was causing the navigator to lose state when opening the VM
-Flags dialog. - [#8413](https://github.com/flutter/devtools/pull/8413)
+* Persist filter settings across sessions. - [#8447](https://github.com/flutter/devtools/pull/8447),
+[#8456](https://github.com/flutter/devtools/pull/8456)
+[#8470](https://github.com/flutter/devtools/pull/8470)
+* Tables match IDE theme when embedded in an IDE. - [#8498](https://github.com/flutter/devtools/pull/8498)
 
 * Persist filter settings across sessions. - [#8447](https://github.com/flutter/devtools/pull/8447),
 [#8456](https://github.com/flutter/devtools/pull/8456)
@@ -45,16 +23,14 @@
 
 ## Inspector updates
 
-- Added a setting to the Flutter Inspector controls that allows users to opt-in to the newly redesigned Flutter Inspector. - [#8342](https://github.com/flutter/devtools/pull/8342)
-
-  ![New inspector opt-in setting](images/new_inspector.png "DevTools setting to opt into the new Flutter Inspector.")
+- Added an option to the [new Inspector's](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.40.1#inspector-updates)
+  settings to allow auto-refreshing the widget tree after a hot-reload. - [#8483](https://github.com/flutter/devtools/pull/8483)
 - Fixed an [issue](https://github.com/flutter/devtools/issues/8487) where the [new Inspector](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.40.1#inspector-updates) would not load after a hot-restart in the legacy Inspector. - [#8491](https://github.com/flutter/devtools/pull/8491)
 - Fixed an [issue](https://github.com/flutter/devtools/issues/8465) preventing scrolling to an implementation widget in the [new Inspector](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.40.1#inspector-updates) after selecting it on the device. - [#8494](https://github.com/flutter/devtools/pull/8494)
 
 ## Performance updates
 
-* Fixed an issue with the "Refreshing timeline" overlay that was getting shown
-when it should not have been. - [#8318](https://github.com/flutter/devtools/pull/8318)
+TODO: Remove this section if there are not any general updates.
 
 ## CPU profiler updates
 
@@ -70,11 +46,43 @@
 
 ## Network profiler updates
 
-* Resolved an issue in .har export where response content was sometimes missing in the data. - [#8333](https://github.com/flutter/devtools/pull/8333)
+* Added a filter text field to the top-level Network profiler controls. -
+[#8469](https://github.com/flutter/devtools/pull/8469)
+    ![Network filter field](images/network_filter.png "Network filter field")
 
 ## Logging updates
 
-TODO: Remove this section if there are not any general updates.
+* Fetch log details immediately upon receiving logs so that log data is not lost
+due to lazy loading. - [#8421](https://github.com/flutter/devtools/pull/8421)
+
+* Reduce initial page load time. - [#8500](https://github.com/flutter/devtools/pull/8500)
+
+* Added support for displaying metadata, such as log
+severity, category, zone, and isolate -
+[#8419](https://github.com/flutter/devtools/pull/8419),
+[#8439](https://github.com/flutter/devtools/pull/8439),
+[#8441](https://github.com/flutter/devtools/pull/8441). It is now also possible to
+search and filter by these metadata values. - [#8473](https://github.com/flutter/devtools/pull/8473)
+    ![Logging metadata display](images/log_metadata.png "Logging metadata display")
+
+* Add a filter text field to the top-level Logging controls. -
+[#8427](https://github.com/flutter/devtools/pull/8427)
+    ![Logging filter](images/log_filter.png "Logging filter")
+
+* Added support for filtering by log severity / levels. -
+[#8433](https://github.com/flutter/devtools/pull/8433)
+    ![Log level filter](images/log_level_filter.png "Log level filter")
+
+* Added a setting to set the log retention limit. - [#8493](https://github.com/flutter/devtools/pull/8493)
+
+* Added a button to toggle the log details display between raw text and JSON. -
+[#8445](https://github.com/flutter/devtools/pull/8445)
+
+* Fixed a bug where logs would get out of order after midnight. - 
+[#8420](https://github.com/flutter/devtools/pull/8420)
+
+* Automatically scroll logs table to the bottom on the initial load -
+[#8437](https://github.com/flutter/devtools/pull/8437)
 
 ## App size tool updates
 
@@ -82,13 +90,15 @@
 
 ## Deep links tool updates
 
-- Added support for validating iOS deep link settings. - [#8394](https://github.com/flutter/devtools/pull/8394)
-
-  ![Deep link validator for iOS](images/deep_link_ios.png "DevTools Deep link validator Page")
+TODO: Remove this section if there are not any general updates.
 
 ## VS Code Sidebar updates
 
-TODO: Remove this section if there are not any general updates.
+- The legacy `postMessage` version of the VS Code sidebar has been removed in
+  favor of the DTD-powered version. Trying to access the legacy sidebar will
+  show a message advising to update your Dart VS Code extension. The Dart VS
+  Code extension was the only user of the legacy sidebar and migrated off in
+  v3.96.
 
 ## DevTools Extension updates
 
@@ -97,4 +107,4 @@
 ## Full commit history
 
 To find a complete list of changes in this release, check out the
-[DevTools git log](https://github.com/flutter/devtools/tree/v2.40.0).
+[DevTools git log](https://github.com/flutter/devtools/tree/v2.41.0).
diff --git a/packages/devtools_app/release_notes/README.md b/packages/devtools_app/release_notes/README.md
index 5d8506e..478ae22 100644
--- a/packages/devtools_app/release_notes/README.md
+++ b/packages/devtools_app/release_notes/README.md
@@ -55,7 +55,7 @@
 
 Draft release notes on a local `flutter/website` branch using the following command:
 ```console
-devtools_tool release-notes -w /Users/me/absolute/path/to/flutter/website
+dt release-notes -w /Users/me/absolute/path/to/flutter/website
 ```
 
 Clean up the drafted notes on your local `flutter/website` branch and open a PR.
diff --git a/packages/devtools_app/release_notes/images/deep_link_ios.png b/packages/devtools_app/release_notes/images/deep_link_ios.png
deleted file mode 100644
index 4ca808b..0000000
--- a/packages/devtools_app/release_notes/images/deep_link_ios.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/log_filter.png b/packages/devtools_app/release_notes/images/log_filter.png
new file mode 100644
index 0000000..e2d71f1
--- /dev/null
+++ b/packages/devtools_app/release_notes/images/log_filter.png
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/log_level_filter.png b/packages/devtools_app/release_notes/images/log_level_filter.png
new file mode 100644
index 0000000..13815a4
--- /dev/null
+++ b/packages/devtools_app/release_notes/images/log_level_filter.png
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/log_metadata.png b/packages/devtools_app/release_notes/images/log_metadata.png
new file mode 100644
index 0000000..28dd936
--- /dev/null
+++ b/packages/devtools_app/release_notes/images/log_metadata.png
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/network_filter.png b/packages/devtools_app/release_notes/images/network_filter.png
new file mode 100644
index 0000000..f723867
--- /dev/null
+++ b/packages/devtools_app/release_notes/images/network_filter.png
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/new_inspector.png b/packages/devtools_app/release_notes/images/new_inspector.png
deleted file mode 100644
index f5d75ab..0000000
--- a/packages/devtools_app/release_notes/images/new_inspector.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/release_notes/images/wasm_setting.png b/packages/devtools_app/release_notes/images/wasm_setting.png
deleted file mode 100644
index 73bbc65..0000000
--- a/packages/devtools_app/release_notes/images/wasm_setting.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/app_size/app_size_screen_test.dart b/packages/devtools_app/test/app_size/app_size_screen_test.dart
deleted file mode 100644
index fc6574b..0000000
--- a/packages/devtools_app/test/app_size/app_size_screen_test.dart
+++ /dev/null
@@ -1,756 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:convert';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/app_size/app_size_table.dart';
-import 'package:devtools_app/src/shared/file_import.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_test_utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-import '../test_infra/test_data/app_size/deferred_app.dart';
-import '../test_infra/test_data/app_size/diff_deferred_app.dart';
-import '../test_infra/test_data/app_size/diff_no_deferred_app.dart';
-import '../test_infra/test_data/app_size/new_v8.dart';
-import '../test_infra/test_data/app_size/old_v8.dart';
-import '../test_infra/test_data/app_size/sizes.dart';
-import '../test_infra/test_data/app_size/unsupported_file.dart';
-
-void main() {
-  setUp(() {
-    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(NotificationService, NotificationService());
-  });
-
-  final lastModifiedTime = DateTime.parse('2020-07-28 13:29:00');
-
-  final oldV8JsonFile = DevToolsJsonFile(
-    name: 'lib/src/app_size/stub_data/old_v8.dart',
-    lastModifiedTime: lastModifiedTime,
-    data: json.decode(oldV8),
-  );
-
-  final newV8JsonFile = DevToolsJsonFile(
-    name: 'lib/src/app_size/stub_data/new_v8.dart',
-    lastModifiedTime: lastModifiedTime,
-    data: json.decode(newV8),
-  );
-
-  final deferredAppFile = DevToolsJsonFile(
-    name: 'lib/src/app_size/stub_data/deferred_app.dart',
-    lastModifiedTime: lastModifiedTime,
-    data: json.decode(deferredApp),
-  );
-
-  DevToolsJsonFile(
-    name: 'lib/src/app_size/stub_data/diff_deferred_app.dart',
-    lastModifiedTime: lastModifiedTime,
-    data: json.decode(diffDeferredApp),
-  );
-
-  DevToolsJsonFile(
-    name: 'lib/src/app_size/stub_data/diff_no_deferred_app.dart',
-    lastModifiedTime: lastModifiedTime,
-    data: json.decode(diffNonDeferredApp),
-  );
-
-  late AppSizeScreen screen;
-  late AppSizeTestController appSizeController;
-  FakeServiceConnectionManager fakeServiceConnection;
-
-  const windowSize = Size(2560.0, 1338.0);
-
-  Future<void> pumpAppSizeScreen(
-    WidgetTester tester, {
-    required AppSizeTestController controller,
-  }) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const AppSizeBody(),
-        appSize: controller,
-      ),
-    );
-    deferredLoadingSupportEnabled = true;
-    await tester.pumpAndSettle(const Duration(seconds: 1));
-    expect(find.byType(AppSizeBody), findsOneWidget);
-  }
-
-  Future<void> loadDataAndPump(
-    WidgetTester tester, {
-    DevToolsJsonFile? data,
-  }) async {
-    data ??= newV8JsonFile;
-    appSizeController.loadTreeFromJsonFile(
-      jsonFile: data,
-      onError: (error) => {},
-    );
-    await tester.pumpAndSettle();
-  }
-
-  group('AppSizeScreen', () {
-    setUp(() {
-      screen = AppSizeScreen();
-      appSizeController = AppSizeTestController();
-      fakeServiceConnection = FakeServiceConnectionManager();
-      setGlobal(ServiceConnectionManager, fakeServiceConnection);
-      when(
-        fakeServiceConnection.errorBadgeManager.errorCountNotifier('app-size'),
-      ).thenReturn(ValueNotifier<int>(0));
-    });
-
-    testWidgets('builds its tab', (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.buildTab),
-          appSize: appSizeController,
-        ),
-      );
-      expect(find.text('App Size'), findsOneWidget);
-    });
-
-    testWidgetsWithWindowSize(
-      'builds initial content',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreen(
-          tester,
-          controller: appSizeController,
-        );
-
-        expect(find.byType(AppSizeBody), findsOneWidget);
-        expect(find.byType(TabBar), findsOneWidget);
-
-        expect(find.byKey(AppSizeScreen.analysisTabKey), findsOneWidget);
-        expect(find.byKey(AppSizeScreen.diffTabKey), findsOneWidget);
-
-        await loadDataAndPump(tester);
-
-        // Verify the state of the splitter.
-        final splitFinder = find.byType(SplitPane);
-        expect(splitFinder, findsOneWidget);
-        final SplitPane splitter = tester.widget(splitFinder);
-        expect(splitter.initialFractions[0], equals(0.67));
-        expect(splitter.initialFractions[1], equals(0.33));
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deferred content',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreen(
-          tester,
-          controller: appSizeController,
-        );
-        await loadDataAndPump(tester, data: deferredAppFile);
-
-        // Verify the dropdown for selecting app units exists.
-        final appUnitDropdownFinder = _findDropdownButton<AppUnit>();
-        expect(appUnitDropdownFinder, findsOneWidget);
-
-        // Verify the entire app is shown.
-        final breadcrumbs = _fetchBreadcrumbs(tester);
-        expect(breadcrumbs.length, 1);
-        expect(breadcrumbs.first.text, equals('Entire App [39.8 MB]'));
-        expect(find.richText('Main [39.5 MB]'), findsOneWidget);
-
-        // Open the dropdown.
-        await tester.tap(appUnitDropdownFinder);
-        await tester.pumpAndSettle();
-
-        // Verify the menu items in the dropdown are expected.
-        final entireAppMenuItemFinder =
-            _findMenuItemWithText<AppUnit>('Entire App');
-        expect(entireAppMenuItemFinder, findsOneWidget);
-        final mainMenuItemFinder = _findMenuItemWithText<AppUnit>('Main');
-        expect(mainMenuItemFinder, findsOneWidget);
-        final deferredMenuItemFinder =
-            _findMenuItemWithText<AppUnit>('Deferred');
-        expect(deferredMenuItemFinder, findsOneWidget);
-
-        // Select the main unit.
-        await tester.tap(find.text('Main').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the main unit is shown.
-        final mainBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(mainBreadcrumbs.length, 1);
-        expect(mainBreadcrumbs.first.text, equals('Main [39.5 MB]'));
-        expect(find.richText('appsize_app.app [39.5 MB]'), findsOneWidget);
-
-        // Open the dropdown.
-        await tester.tap(appUnitDropdownFinder);
-        await tester.pumpAndSettle();
-
-        // Select the deferred units.
-        await tester.tap(find.text('Deferred').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the deferred units are shown.
-        final deferredBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(deferredBreadcrumbs.length, 1);
-        expect(deferredBreadcrumbs.first.text, equals('Deferred [344.3 KB]'));
-        expect(
-          find.richText('flutter_assets [344.3 KB] (Deferred)'),
-          findsOneWidget,
-        );
-      },
-    );
-  });
-
-  group('SnapshotView', () {
-    setUp(() {
-      screen = AppSizeScreen();
-      appSizeController = AppSizeTestController();
-    });
-
-    testWidgetsWithWindowSize(
-      'imports file and loads data',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreen(
-          tester,
-          controller: appSizeController,
-        );
-
-        expect(find.byKey(AppSizeScreen.diffTypeDropdownKey), findsNothing);
-        expect(find.byType(ClearButton), findsOneWidget);
-
-        expect(find.byType(FileImportContainer), findsOneWidget);
-        expect(find.text(AnalysisView.importInstructions), findsOneWidget);
-        expect(find.text('No File Selected'), findsOneWidget);
-
-        appSizeController.loadTreeFromJsonFile(
-          jsonFile: newV8JsonFile,
-          onError: (error) => {},
-          delayed: true,
-        );
-        await tester.pump(const Duration(milliseconds: 500));
-        expect(find.text(AppSizeScreen.loadingMessage), findsOneWidget);
-        await tester.pumpAndSettle();
-
-        expect(find.byType(FileImportContainer), findsNothing);
-        expect(find.text(AnalysisView.importInstructions), findsNothing);
-        expect(find.text('No File Selected'), findsNothing);
-        expect(find.byType(AnalysisView), findsOneWidget);
-        expect(
-          find.text(
-            'Dart AOT snapshot: lib/src/app_size/stub_data/new_v8.dart - 7/28/2020 1:29 PM',
-          ),
-          findsOneWidget,
-        );
-        expect(
-          find.byKey(AppSizeScreen.analysisViewTreemapKey),
-          findsOneWidget,
-        );
-
-        final breadcrumbs = _fetchBreadcrumbs(tester);
-        expect(breadcrumbs.length, 1);
-        expect(breadcrumbs.first.text, equals('Root [6.0 MB]'));
-        expect(find.byType(BreadcrumbNavigator), findsOneWidget);
-
-        expect(find.richText('package:flutter'), findsOneWidget);
-        expect(find.richText('dart:core'), findsOneWidget);
-
-        expect(find.byType(AppSizeAnalysisTable), findsOneWidget);
-        expect(find.byType(AppSizeDiffTable), findsNothing);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'clears data',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreen(
-          tester,
-          controller: appSizeController,
-        );
-
-        await loadDataAndPump(tester);
-
-        await tester.tap(find.byType(ClearButton));
-        await tester.pumpAndSettle();
-
-        expect(find.byType(FileImportContainer), findsOneWidget);
-        expect(find.text(AnalysisView.importInstructions), findsOneWidget);
-        expect(find.text('No File Selected'), findsOneWidget);
-      },
-    );
-  });
-
-  group('DiffView', () {
-    setUp(() {
-      screen = AppSizeScreen();
-      appSizeController = AppSizeTestController();
-    });
-
-    Future<void> loadDiffTabAndSettle(WidgetTester tester) async {
-      await pumpAppSizeScreen(
-        tester,
-        controller: appSizeController,
-      );
-      await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
-      await tester.pumpAndSettle();
-    }
-
-    Future<void> loadDiffDataAndPump(
-      WidgetTester tester,
-      DevToolsJsonFile oldJsonFile,
-      DevToolsJsonFile newJsonFile,
-    ) async {
-      appSizeController.loadDiffTreeFromJsonFiles(
-        oldFile: oldJsonFile,
-        newFile: newJsonFile,
-        onError: (error) => {},
-      );
-      await tester.pumpAndSettle();
-    }
-
-    testWidgetsWithWindowSize(
-      'builds initial content',
-      windowSize,
-      (WidgetTester tester) async {
-        await loadDiffTabAndSettle(tester);
-
-        expect(find.byKey(AppSizeScreen.diffTypeDropdownKey), findsOneWidget);
-        expect(find.byKey(AppSizeScreen.appUnitDropdownKey), findsNothing);
-        expect(find.byType(ClearButton), findsOneWidget);
-
-        expect(find.byType(DualFileImportContainer), findsOneWidget);
-        expect(find.byType(FileImportContainer), findsNWidgets(2));
-        expect(find.text(DiffView.importOldInstructions), findsOneWidget);
-        expect(find.text(DiffView.importNewInstructions), findsOneWidget);
-        expect(find.text('No File Selected'), findsNWidgets(2));
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'imports files and loads data',
-      windowSize,
-      (WidgetTester tester) async {
-        await loadDiffTabAndSettle(tester);
-
-        appSizeController.loadDiffTreeFromJsonFiles(
-          oldFile: oldV8JsonFile,
-          newFile: newV8JsonFile,
-          onError: (error) => {},
-          delayed: true,
-        );
-        await tester.pump(const Duration(milliseconds: 500));
-        expect(find.text(AppSizeScreen.loadingMessage), findsOneWidget);
-        await tester.pumpAndSettle();
-
-        expect(find.byType(FileImportContainer), findsNothing);
-        expect(find.text(DiffView.importOldInstructions), findsNothing);
-        expect(find.text(DiffView.importNewInstructions), findsNothing);
-        expect(find.text('No File Selected'), findsNothing);
-
-        expect(find.byType(DiffView), findsOneWidget);
-
-        expect(
-          find.text(
-            'Diffing Dart AOT snapshots: lib/src/app_size/stub_data/old_v8.dart - 7/28/2020 1:29 PM (OLD)    vs    (NEW) lib/src/app_size/stub_data/new_v8.dart - 7/28/2020 1:29 PM',
-          ),
-          findsOneWidget,
-        );
-        expect(
-          find.byKey(AppSizeScreen.diffViewTreemapKey),
-          findsOneWidget,
-        );
-        expect(
-          find.byKey(AppSizeScreen.analysisViewTreemapKey),
-          findsNothing,
-        );
-
-        final breadcrumbs = _fetchBreadcrumbs(tester);
-        expect(breadcrumbs.length, 1);
-        expect(breadcrumbs.first.text, equals('Root [+1.5 MB]'));
-        expect(find.richText('package:pointycastle'), findsOneWidget);
-        expect(find.richText('package:flutter'), findsOneWidget);
-
-        expect(find.byType(AppSizeAnalysisTable), findsNothing);
-        expect(find.byType(AppSizeDiffTable), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'loads data and shows different tree types',
-      windowSize,
-      (WidgetTester tester) async {
-        await loadDiffTabAndSettle(tester);
-
-        await loadDiffDataAndPump(tester, oldV8JsonFile, newV8JsonFile);
-
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        await tester.tap(find.text('Increase Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        final breadcrumbs = _fetchBreadcrumbs(tester);
-        expect(breadcrumbs.length, 1);
-        expect(breadcrumbs.first.text, equals('Root [+1.6 MB]'));
-
-        expect(find.richText('package:pointycastle'), findsOneWidget);
-        expect(find.richText('package:flutter'), findsOneWidget);
-
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        await tester.tap(find.text('Decrease Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        expect(find.richText('Root'), findsOneWidget);
-        expect(find.richText('package:memory'), findsOneWidget);
-        expect(find.richText('package:flutter'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'clears data',
-      windowSize,
-      (WidgetTester tester) async {
-        await loadDiffTabAndSettle(tester);
-
-        await loadDiffDataAndPump(tester, oldV8JsonFile, newV8JsonFile);
-
-        await tester.tap(find.byType(ClearButton));
-        await tester.pumpAndSettle();
-
-        expect(find.byType(DualFileImportContainer), findsOneWidget);
-        expect(find.byType(FileImportContainer), findsNWidgets(2));
-        expect(find.text(DiffView.importOldInstructions), findsOneWidget);
-        expect(find.text(DiffView.importNewInstructions), findsOneWidget);
-        expect(find.text('No File Selected'), findsNWidgets(2));
-      },
-    );
-  });
-
-  group('AppSizeController', () {
-    setUp(() {
-      screen = AppSizeScreen();
-      appSizeController = AppSizeTestController();
-    });
-
-    Future<void> pumpAppSizeScreenWithContext(
-      WidgetTester tester, {
-      required AppSizeTestController controller,
-    }) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          MaterialApp(
-            builder: (context, child) => child!,
-            home: Builder(
-              builder: (context) {
-                return const AppSizeBody();
-              },
-            ),
-          ),
-          appSize: controller,
-        ),
-      );
-      deferredLoadingSupportEnabled = true;
-      await tester.pumpAndSettle(const Duration(seconds: 1));
-      expect(find.byType(AppSizeBody), findsOneWidget);
-    }
-
-    Future<void> loadDiffTreeAndPump(
-      WidgetTester tester,
-      String firstFile,
-      String secondFile,
-    ) async {
-      appSizeController.loadDiffTreeFromJsonFiles(
-        oldFile: DevToolsJsonFile(
-          name: '',
-          lastModifiedTime: lastModifiedTime,
-          data: json.decode(firstFile),
-        ),
-        newFile: DevToolsJsonFile(
-          name: '',
-          lastModifiedTime: lastModifiedTime,
-          data: json.decode(secondFile),
-        ),
-        onError: (error) => notificationService.push(error),
-      );
-      await tester.pumpAndSettle();
-    }
-
-    testWidgetsWithWindowSize(
-      'outputs error notifications for invalid input on the snapshot tab',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreenWithContext(
-          tester,
-          controller: appSizeController,
-        );
-
-        appSizeController.loadTreeFromJsonFile(
-          jsonFile: DevToolsJsonFile(
-            name: 'unsupported_file.json',
-            lastModifiedTime: lastModifiedTime,
-            data: unsupportedFile,
-          ),
-          onError: (error) => notificationService.push(error),
-        );
-        await tester.pumpAndSettle();
-        expect(
-          find.text(AppSizeController.unsupportedFileTypeError),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'outputs error notifications for invalid input on the diff tab',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreenWithContext(
-          tester,
-          controller: appSizeController,
-        );
-        await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
-        await tester.pumpAndSettle();
-
-        await loadDiffTreeAndPump(tester, newV8, newV8);
-        expect(
-          find.text(AppSizeController.identicalFilesError),
-          findsOneWidget,
-        );
-
-        await loadDiffTreeAndPump(tester, instructionSizes, newV8);
-        expect(
-          find.text(AppSizeController.differentTypesError),
-          findsOneWidget,
-        );
-
-        await loadDiffTreeAndPump(tester, unsupportedFile, unsupportedFile);
-        expect(
-          find.text(AppSizeController.unsupportedFileTypeError),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deferred content for diff table',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpAppSizeScreen(
-          tester,
-          controller: appSizeController,
-        );
-        await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
-        await tester.pumpAndSettle();
-
-        await loadDiffTreeAndPump(tester, diffNonDeferredApp, diffDeferredApp);
-
-        // Verify the dropdown for selecting app units exists.
-        final appUnitDropdownFinder = _findDropdownButton<AppUnit>();
-        expect(appUnitDropdownFinder, findsOneWidget);
-
-        // Open the app unit dropdown.
-        await tester.tap(appUnitDropdownFinder);
-        await tester.pumpAndSettle();
-
-        // Verify the menu items in the dropdown are expected.
-        final entireAppMenuItemFinder =
-            _findMenuItemWithText<AppUnit>('Entire App');
-        expect(entireAppMenuItemFinder, findsOneWidget);
-        final mainMenuItemFinder = _findMenuItemWithText<AppUnit>('Main');
-        expect(mainMenuItemFinder, findsOneWidget);
-        final deferredMenuItemFinder =
-            _findMenuItemWithText<AppUnit>('Deferred');
-        expect(deferredMenuItemFinder, findsOneWidget);
-
-        // Select the main unit.
-        await tester.tap(find.richText('Main').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the main unit is shown for entire app.
-        final mainBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(mainBreadcrumbs.length, 1);
-        expect(
-          mainBreadcrumbs.first.text,
-          equals(
-            '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [-344.3 KB]',
-          ),
-        );
-        expect(
-          find.richText('packages/cupertino_icons/assets [-276.8 KB]'),
-          findsOneWidget,
-        );
-
-        // Open the diffType dropdown.
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        // Select increase only.
-        await tester.tap(find.text('Increase Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the main unit is shown for increase only.
-        final mainIncreaseBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(mainIncreaseBreadcrumbs.length, 1);
-        expect(
-          mainIncreaseBreadcrumbs.first.text,
-          equals(
-            '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [0 B]',
-          ),
-        );
-
-        // Open the diffType dropdown.
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        // Select decrease only.
-        await tester.tap(find.text('Decrease Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the main unit is shown for decrease only.
-        final mainDecreaseBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(mainDecreaseBreadcrumbs.length, 1);
-        expect(
-          mainDecreaseBreadcrumbs.first.text,
-          equals(
-            '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [-344.3 KB]',
-          ),
-        );
-        expect(
-          find.richText('packages/cupertino_icons/assets [-276.8 KB]'),
-          findsOneWidget,
-        );
-
-        // Open the diffType dropdown.
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        // Select entire app.
-        await tester.tap(find.text('Combined').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Open the app unit dropdown.
-        await tester.tap(appUnitDropdownFinder);
-        await tester.pumpAndSettle();
-
-        // Select the deferred units.
-        await tester.tap(find.text('Deferred').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the deferred units are shown for entire app.
-        final deferredBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(deferredBreadcrumbs.length, 1);
-        expect(
-          deferredBreadcrumbs.first.text,
-          equals('/Deferred/flutter_assets [+344.3 KB]'),
-        );
-        expect(
-          find.richText('packages/cupertino_icons/assets [+276.8 KB]'),
-          findsOneWidget,
-        );
-
-        // Open the diffType dropdown.
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        // Select increase only.
-        await tester.tap(find.text('Increase Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the deferred unit is shown for increase only.
-        final deferredIncreaseBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(deferredIncreaseBreadcrumbs.length, 1);
-        expect(
-          deferredIncreaseBreadcrumbs.first.text,
-          equals(
-            '/Deferred/flutter_assets [+344.3 KB]',
-          ),
-        );
-        expect(
-          find.richText('packages/cupertino_icons/assets [+276.8 KB]'),
-          findsOneWidget,
-        );
-
-        // Open the diffType dropdown.
-        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
-        await tester.pumpAndSettle();
-
-        // Select decrease only.
-        await tester.tap(find.text('Decrease Only').hitTestable());
-        await tester.pumpAndSettle();
-
-        // Verify the main unit is shown for decrease only.
-        final deferredDecreaseBreadcrumbs = _fetchBreadcrumbs(tester);
-        expect(deferredDecreaseBreadcrumbs.length, 1);
-        expect(
-          deferredDecreaseBreadcrumbs.first.text,
-          equals(
-            '/Deferred/flutter_assets [0 B]',
-          ),
-        );
-      },
-    );
-  });
-}
-
-class AppSizeTestController extends AppSizeController {
-  @override
-  void loadTreeFromJsonFile({
-    required DevToolsJsonFile jsonFile,
-    required void Function(String error) onError,
-    bool delayed = false,
-  }) async {
-    if (delayed) {
-      await delay();
-    }
-    super.loadTreeFromJsonFile(jsonFile: jsonFile, onError: onError);
-  }
-
-  @override
-  void loadDiffTreeFromJsonFiles({
-    required DevToolsJsonFile oldFile,
-    required DevToolsJsonFile newFile,
-    required void Function(String error) onError,
-    bool delayed = false,
-  }) async {
-    if (delayed) {
-      await delay();
-    }
-    super.loadDiffTreeFromJsonFiles(
-      oldFile: oldFile,
-      newFile: newFile,
-      onError: onError,
-    );
-  }
-}
-
-List<Breadcrumb> _fetchBreadcrumbs(WidgetTester tester) {
-  return tester
-      .widgetList(find.byType(Breadcrumb))
-      .map((widget) => widget as Breadcrumb)
-      .toList();
-}
-
-Finder _findDropdownButton<T>() {
-  return find.byType(DropdownButton<T>);
-}
-
-Finder _findMenuItemWithText<T>(String text) {
-  return find.descendant(
-    of: find.byType(DropdownMenuItem<T>),
-    matching: find.richText(text).hitTestable(),
-  );
-}
diff --git a/packages/devtools_app/test/cpu_profiler/cpu_profiler_test.dart b/packages/devtools_app/test/cpu_profiler/cpu_profiler_test.dart
deleted file mode 100644
index 3bf5ad8..0000000
--- a/packages/devtools_app/test/cpu_profiler/cpu_profiler_test.dart
+++ /dev/null
@@ -1,794 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/profiler/common.dart';
-import 'package:devtools_app/src/screens/profiler/cpu_profile_transformer.dart';
-import 'package:devtools_app/src/screens/profiler/cpu_profiler.dart';
-import 'package:devtools_app/src/screens/profiler/cpu_profiler_controller.dart';
-import 'package:devtools_app/src/screens/profiler/panes/bottom_up.dart';
-import 'package:devtools_app/src/screens/profiler/panes/call_tree.dart';
-import 'package:devtools_app/src/screens/profiler/panes/controls/cpu_profiler_controls.dart';
-import 'package:devtools_app/src/screens/profiler/panes/cpu_flame_chart.dart';
-import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table.dart';
-import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_controller.dart';
-import 'package:devtools_app/src/shared/charts/flame_chart.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../test_infra/test_data/cpu_profiler/cpu_profile.dart';
-import '../test_infra/utils/test_utils.dart';
-
-void main() {
-  late CpuProfiler cpuProfiler;
-  late CpuProfileData cpuProfileData;
-  late CpuProfilerController controller;
-
-  final fakeServiceManager = FakeServiceConnectionManager();
-  final app = fakeServiceManager.serviceManager.connectedApp!;
-  when(app.isFlutterNativeAppNow).thenReturn(false);
-  when(app.isFlutterAppNow).thenReturn(false);
-  when(app.isDebugFlutterAppNow).thenReturn(false);
-
-  setUp(() async {
-    setCharacterWidthForTables();
-    setGlobal(ServiceConnectionManager, fakeServiceManager);
-
-    final transformer = CpuProfileTransformer();
-    controller = CpuProfilerController();
-    cpuProfileData = CpuProfileData.fromJson(goldenCpuProfileDataJson);
-    await transformer.processData(
-      cpuProfileData,
-      processId: 'test',
-    );
-
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(OfflineDataController, OfflineDataController());
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(BannerMessagesController, BannerMessagesController());
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(IdeTheme, IdeTheme());
-    final mockScriptManager = MockScriptManager();
-    when(mockScriptManager.sortedScripts).thenReturn(
-      ValueNotifier<List<ScriptRef>>([]),
-    );
-    when(mockScriptManager.scriptRefForUri(any)).thenReturn(
-      ScriptRef(
-        uri: 'package:test/script.dart',
-        id: 'script.dart',
-      ),
-    );
-    setGlobal(ScriptManager, mockScriptManager);
-  });
-
-  group('CpuProfiler', () {
-    const windowSize = Size(2000.0, 1000.0);
-
-    testWidgetsWithWindowSize(
-      'builds for empty cpuProfileData',
-      windowSize,
-      (WidgetTester tester) async {
-        cpuProfileData = CpuProfileData.fromJson(emptyCpuProfileDataJson);
-        cpuProfiler = CpuProfiler(
-          data: cpuProfileData,
-          controller: controller,
-        );
-        await tester.pumpWidget(wrap(cpuProfiler));
-        expect(find.byType(TabBar), findsOneWidget);
-        expect(find.byType(CpuBottomUpTable), findsOneWidget);
-        expect(find.byType(CpuCallTreeTable), findsNothing);
-        expect(find.byType(CpuMethodTable), findsNothing);
-        expect(find.byType(CpuProfileFlameChart), findsNothing);
-        expect(find.byType(CpuProfileStats), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsOneWidget);
-        expect(find.byType(CollapseAllButton), findsOneWidget);
-        expect(find.byType(FlameChartHelpButton), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsNothing,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsNothing,
-        );
-        expect(find.byKey(ProfilerTab.bottomUp.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.callTree.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.methodTable.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.cpuFlameChart.key), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds for valid cpuProfileData',
-      windowSize,
-      (WidgetTester tester) async {
-        cpuProfiler = CpuProfiler(
-          data: cpuProfileData,
-          controller: controller,
-        );
-        await tester.pumpWidget(wrap(cpuProfiler));
-        expect(find.byType(TabBar), findsOneWidget);
-        expect(find.byType(CpuBottomUpTable), findsOneWidget);
-        expect(find.byType(CpuProfileStats), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsOneWidget);
-        expect(find.byType(CollapseAllButton), findsOneWidget);
-        expect(find.byType(FlameChartHelpButton), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsNothing,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsNothing,
-        );
-        expect(find.byKey(ProfilerTab.bottomUp.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.callTree.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.methodTable.key), findsOneWidget);
-        expect(find.byKey(ProfilerTab.cpuFlameChart.key), findsOneWidget);
-      },
-    );
-
-    group('profile views', () {
-      late ProfilerScreenController controller;
-
-      Future<void> loadData() async {
-        for (final filter in controller
-            .cpuProfilerController.activeFilter.value.toggleFilters) {
-          filter.enabled.value = false;
-        }
-        final data = CpuProfilePair(
-          functionProfile: cpuProfileData,
-          // Function and code profiles have the same structure, so just use
-          // the function profile in place of a dedicated code profile for
-          // testing since we don't care about the contents as much as we
-          // care about testing the ability to switch between function and
-          // code profile views.
-          codeProfile: cpuProfileData,
-        );
-        await data.process(
-          transformer: controller.cpuProfilerController.transformer,
-          processId: 'test',
-        );
-        // Call this to force the value of `_dataByTag[userTagNone]` to be set.
-        controller.cpuProfilerController.loadProcessedData(
-          data,
-          storeAsUserTagNone: true,
-        );
-      }
-
-      setUp(() async {
-        controller = ProfilerScreenController();
-
-        // Await a small delay to allow the ProfilerScreenController to complete
-        // initialization.
-        await Future.delayed(const Duration(seconds: 1));
-
-        cpuProfileData =
-            CpuProfileData.fromJson(cpuProfileDataWithUserTagsJson);
-      });
-
-      testWidgetsWithWindowSize(
-        'shows function / code view selector when in VM developer mode',
-        windowSize,
-        (WidgetTester tester) async {
-          await tester.runAsync(() async {
-            // We need to pump the entire `ProfilerScreenBody` widget because the
-            // CpuProfiler widget has `cpuProfileData` passed in from there, and
-            // CpuProfiler needs to be rebuilt on data updates.
-            await tester.pumpWidget(
-              wrapWithControllers(
-                const ProfilerScreenBody(),
-                profiler: controller,
-              ),
-            );
-            await tester.pump();
-
-            // Verify the profile view dropdown is not visible.
-            expect(find.byType(ModeDropdown), findsNothing);
-
-            // Enabling VM developer mode will clear the current profile as it's
-            // possible there's no code profile associated with it.
-            preferences.toggleVmDeveloperMode(true);
-            await tester.pumpAndSettle();
-            expect(find.byType(CpuProfiler), findsNothing);
-
-            // Verify the profile view dropdown appears when toggling VM developer
-            // mode and data is present.
-            await loadData();
-            await tester.pumpAndSettle();
-            expect(find.byType(ModeDropdown), findsOneWidget);
-
-            // Verify the profile view dropdown is no longer visible.
-            preferences.toggleVmDeveloperMode(false);
-            await tester.pumpAndSettle();
-            expect(find.byType(ModeDropdown), findsNothing);
-          });
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'resets view to function when leaving VM developer mode',
-        windowSize,
-        (WidgetTester tester) async {
-          await tester.runAsync(() async {
-            // We need to pump the entire `ProfilerScreenBody` widget because the
-            // CpuProfiler widget has `cpuProfileData` passed in from there, and
-            // CpuProfiler needs to be rebuilt on data updates.
-            await tester.pumpWidget(
-              wrapWithControllers(
-                const ProfilerScreenBody(),
-                profiler: controller,
-              ),
-            );
-            await tester.pump();
-
-            // Verify the profile view dropdown is not visible.
-            expect(find.byType(ModeDropdown), findsNothing);
-
-            // The default view is the function profile, even when the profile view
-            // selector isn't visible.
-            expect(
-              controller.cpuProfilerController.viewType.value,
-              CpuProfilerViewType.function,
-            );
-
-            // Enable VM developer mode and reset the profile data.
-            preferences.toggleVmDeveloperMode(true);
-            await tester.pumpAndSettle();
-            expect(find.byType(CpuProfiler), findsNothing);
-            await loadData();
-            await tester.pumpAndSettle();
-
-            // Verify the function profile view is still selected.
-            expect(
-              controller.cpuProfilerController.viewType.value,
-              CpuProfilerViewType.function,
-            );
-            expect(find.text('View: Function'), findsOneWidget);
-
-            // Switch to the code profile view.
-            await tester.tap(find.byType(ModeDropdown));
-            await tester.pumpAndSettle();
-            expect(find.text('View: Function'), findsWidgets);
-            expect(find.text('View: Code'), findsWidgets);
-            await tester.tap(find.text('View: Code').last);
-            await tester.pumpAndSettle();
-            expect(
-              controller.cpuProfilerController.viewType.value,
-              CpuProfilerViewType.code,
-            );
-            expect(find.byType(ModeDropdown), findsOneWidget);
-            expect(find.text('View: Code'), findsOneWidget);
-
-            // Disabling VM developer mode will reset the view to the function
-            // profile as the dropdown will no longer be visible.
-            preferences.toggleVmDeveloperMode(false);
-            await tester.pumpAndSettle();
-            expect(
-              controller.cpuProfilerController.viewType.value,
-              CpuProfilerViewType.function,
-            );
-          });
-        },
-      );
-    });
-
-    testWidgetsWithWindowSize(
-      'switches tabs',
-      windowSize,
-      (WidgetTester tester) async {
-        cpuProfiler = CpuProfiler(
-          data: cpuProfileData,
-          controller: controller,
-        );
-        await tester.pumpWidget(wrap(cpuProfiler));
-        expect(find.byType(TabBar), findsOneWidget);
-        expect(find.byType(CpuBottomUpTable), findsOneWidget);
-        expect(find.byType(CpuCallTreeTable), findsNothing);
-        expect(find.byType(CpuMethodTable), findsNothing);
-        expect(find.byType(CpuProfileFlameChart), findsNothing);
-        expect(find.byType(DevToolsFilterButton), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsOneWidget);
-        expect(find.byType(CollapseAllButton), findsOneWidget);
-        expect(find.byType(FlameChartHelpButton), findsNothing);
-        expect(find.byType(ModeDropdown), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsNothing,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsNothing,
-        );
-
-        await tester.tap(find.text('Call Tree'));
-        await tester.pumpAndSettle();
-        expect(find.byType(CpuBottomUpTable), findsNothing);
-        expect(find.byType(CpuCallTreeTable), findsOneWidget);
-        expect(find.byType(CpuMethodTable), findsNothing);
-        expect(find.byType(CpuProfileFlameChart), findsNothing);
-        expect(find.byType(DevToolsFilterButton), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsOneWidget);
-        expect(find.byType(CollapseAllButton), findsOneWidget);
-        expect(find.byType(FlameChartHelpButton), findsNothing);
-        expect(find.byType(ModeDropdown), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsNothing,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsNothing,
-        );
-
-        await tester.tap(find.text('Method Table'));
-        await tester.pumpAndSettle();
-        expect(find.byType(CpuBottomUpTable), findsNothing);
-        expect(find.byType(CpuCallTreeTable), findsNothing);
-        expect(find.byType(CpuMethodTable), findsOneWidget);
-        expect(find.byType(CpuProfileFlameChart), findsNothing);
-        expect(find.byType(DevToolsFilterButton), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsNothing);
-        expect(find.byType(CollapseAllButton), findsNothing);
-        expect(find.byType(FlameChartHelpButton), findsNothing);
-        expect(find.byType(ModeDropdown), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsOneWidget,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsNothing,
-        );
-
-        await tester.tap(find.text('CPU Flame Chart'));
-        await tester.pumpAndSettle();
-        expect(find.byType(CpuBottomUpTable), findsNothing);
-        expect(find.byType(CpuCallTreeTable), findsNothing);
-        expect(find.byType(CpuMethodTable), findsNothing);
-        expect(find.byType(CpuProfileFlameChart), findsOneWidget);
-        expect(find.byType(DevToolsFilterButton), findsOneWidget);
-        expect(find.byType(UserTagDropdown), findsOneWidget);
-        expect(find.byType(ExpandAllButton), findsNothing);
-        expect(find.byType(CollapseAllButton), findsNothing);
-        expect(find.byType(FlameChartHelpButton), findsOneWidget);
-        expect(find.byType(ModeDropdown), findsNothing);
-        expect(
-          find.byType(SearchField<MethodTableController>),
-          findsNothing,
-        );
-        expect(
-          find.byType(SearchField<CpuProfilerController>),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'can expand and collapse data',
-      windowSize,
-      (WidgetTester tester) async {
-        cpuProfiler = CpuProfiler(
-          data: cpuProfileData,
-          controller: controller,
-        );
-        await tester.pumpWidget(wrap(cpuProfiler));
-        await tester.tap(find.text('Call Tree'));
-        await tester.pumpAndSettle();
-
-        expect(cpuProfileData.cpuProfileRoot.isExpanded, isFalse);
-        await tester.tap(find.byType(ExpandAllButton));
-        expect(cpuProfiler.callTreeRoots.first.isExpanded, isTrue);
-        await tester.tap(find.byType(CollapseAllButton));
-        expect(cpuProfiler.callTreeRoots.first.isExpanded, isFalse);
-
-        await tester.tap(find.text('Bottom Up'));
-        await tester.pumpAndSettle();
-        for (final root in cpuProfiler.bottomUpRoots) {
-          expect(root.isExpanded, isFalse);
-        }
-        await tester.tap(find.byType(ExpandAllButton));
-        for (final root in cpuProfiler.bottomUpRoots) {
-          expect(root.isExpanded, isTrue);
-        }
-        await tester.tap(find.byType(CollapseAllButton));
-        for (final root in cpuProfiler.bottomUpRoots) {
-          expect(root.isExpanded, isFalse);
-        }
-      },
-    );
-
-    group('UserTag filters', () {
-      late ProfilerScreenController controller;
-
-      setUp(() async {
-        controller = ProfilerScreenController();
-
-        // Await a small delay to allow the ProfilerScreenController to complete
-        // initialization.
-        await Future.delayed(const Duration(seconds: 1));
-
-        cpuProfileData =
-            CpuProfileData.fromJson(cpuProfileDataWithUserTagsJson);
-        await controller.cpuProfilerController.transformer.processData(
-          cpuProfileData,
-          processId: 'test',
-        );
-        // Call this to force the value of `_dataByTag[userTagNone]` to be set.
-        controller.cpuProfilerController.loadProcessedData(
-          CpuProfilePair(
-            functionProfile: cpuProfileData,
-            // Function and code profiles have the same structure, so just use
-            // the function profile in place of a dedicated code profile for
-            // testing since we don't care about the contents as much as we
-            // care about testing the ability to switch between function and
-            // code profile views.
-            codeProfile: cpuProfileData,
-          ),
-          storeAsUserTagNone: true,
-        );
-      });
-
-      testWidgetsWithWindowSize(
-        'can filter data by user tag',
-        windowSize,
-        (WidgetTester tester) async {
-          await tester.runAsync(() async {
-            // We need to pump the entire `ProfilerScreenBody` widget because the
-            // CpuProfiler widget has `cpuProfileData` passed in from there, and
-            // CpuProfiler needs to be rebuilt on data updates.
-            await tester.pumpWidget(
-              wrapWithControllers(
-                const ProfilerScreenBody(),
-                profiler: controller,
-              ),
-            );
-            await tester.pump();
-
-            expect(controller.cpuProfilerController.userTags.length, equals(3));
-
-            expect(find.byType(UserTagDropdown), findsOneWidget);
-            // There is a Text widget and a RichText widget.
-            expect(
-              find.text('Filter by tag: userTagA', skipOffstage: false),
-              findsWidgets,
-            );
-            expect(
-              find.text('Filter by tag: userTagB', skipOffstage: false),
-              findsWidgets,
-            );
-            expect(
-              find.text('Filter by tag: userTagC', skipOffstage: false),
-              findsWidgets,
-            );
-            expect(
-              find.text('Group by: User Tag', skipOffstage: false),
-              findsWidgets,
-            );
-
-            await tester.tap(find.text('Call Tree'));
-            await tester.pumpAndSettle();
-            expect(find.byType(CpuCallTreeTable), findsOneWidget);
-            await tester.tap(find.text('Expand All'));
-            await tester.pumpAndSettle();
-
-            expect(
-              controller.cpuProfileData!.profileMetaData.time!.duration
-                  .inMicroseconds,
-              equals(250),
-            );
-
-            expect(find.richTextContaining('Frame1'), findsOneWidget);
-            expect(find.richTextContaining('Frame2'), findsOneWidget);
-            expect(find.richTextContaining('Frame3'), findsOneWidget);
-            expect(find.richTextContaining('Frame4'), findsOneWidget);
-            expect(find.richTextContaining('Frame5'), findsOneWidget);
-            expect(find.richTextContaining('Frame6'), findsOneWidget);
-            expect(find.text('userTagA'), findsNothing);
-            expect(find.text('userTagB'), findsNothing);
-            expect(find.text('userTagC'), findsNothing);
-
-            await tester.tap(find.byType(UserTagDropdown));
-            await tester.pumpAndSettle();
-            await tester.tap(find.text('Filter by tag: userTagA').last);
-
-            // Await a small delay to allow the CpuProfilerController to finish
-            // processing data for the new user tag.
-            await Future.delayed(const Duration(seconds: 1));
-            await tester.pumpAndSettle();
-
-            await tester.tap(find.text('Expand All'));
-            await tester.pumpAndSettle();
-            expect(
-              controller.cpuProfileData!.profileMetaData.time!.duration
-                  .inMicroseconds,
-              equals(100),
-            );
-            expect(find.richTextContaining('Frame1'), findsNothing);
-            expect(find.richTextContaining('Frame2'), findsOneWidget);
-            expect(find.richTextContaining('Frame3'), findsNothing);
-            expect(find.richTextContaining('Frame4'), findsNothing);
-            expect(find.richTextContaining('Frame5'), findsOneWidget);
-            expect(find.richTextContaining('Frame6'), findsNothing);
-            expect(find.text('userTagA'), findsNothing);
-            expect(find.text('userTagB'), findsNothing);
-            expect(find.text('userTagC'), findsNothing);
-
-            await tester.tap(find.byType(UserTagDropdown));
-            await tester.pumpAndSettle();
-            await tester.tap(find.text('Filter by tag: userTagB').last);
-
-            // Await a small delay to allow the CpuProfilerController to finish
-            // processing data for the new user tag.
-            await Future.delayed(const Duration(seconds: 1));
-            await tester.pumpAndSettle();
-
-            await tester.tap(find.text('Expand All'));
-            await tester.pumpAndSettle();
-            expect(
-              controller.cpuProfileData!.profileMetaData.time!.duration
-                  .inMicroseconds,
-              equals(50),
-            );
-            expect(find.richTextContaining('Frame1'), findsNothing);
-            expect(find.richTextContaining('Frame2'), findsOneWidget);
-            expect(find.richTextContaining('Frame3'), findsNothing);
-            expect(find.richTextContaining('Frame4'), findsNothing);
-            expect(find.richTextContaining('Frame5'), findsNothing);
-            expect(find.richTextContaining('Frame6'), findsNothing);
-            expect(find.text('userTagA'), findsNothing);
-            expect(find.text('userTagB'), findsNothing);
-            expect(find.text('userTagC'), findsNothing);
-
-            await tester.tap(find.byType(UserTagDropdown));
-            await tester.pumpAndSettle();
-            await tester.tap(find.text('Filter by tag: userTagC').last);
-
-            // Await a small delay to allow the CpuProfilerController to finish
-            // processing data for the new user tag.
-            await Future.delayed(const Duration(seconds: 1));
-            await tester.pumpAndSettle();
-
-            await tester.tap(find.text('Expand All'));
-            await tester.pumpAndSettle();
-            expect(
-              controller.cpuProfileData!.profileMetaData.time!.duration
-                  .inMicroseconds,
-              equals(100),
-            );
-            expect(find.richTextContaining('Frame1'), findsNothing);
-            expect(find.richTextContaining('Frame2'), findsNothing);
-            expect(find.richTextContaining('Frame3'), findsNothing);
-            expect(find.richTextContaining('Frame4'), findsNothing);
-            expect(find.richTextContaining('Frame5'), findsOneWidget);
-            expect(find.richTextContaining('Frame6'), findsOneWidget);
-            expect(find.text('userTagA'), findsNothing);
-            expect(find.text('userTagB'), findsNothing);
-            expect(find.text('userTagC'), findsNothing);
-          });
-        },
-      );
-    });
-
-    group('Group by ', () {
-      late ProfilerScreenController controller;
-
-      setUp(() async {
-        controller = ProfilerScreenController();
-
-        // Await a small delay to allow the ProfilerScreenController to complete
-        // initialization.
-        await Future.delayed(const Duration(seconds: 1));
-
-        preferences.toggleVmDeveloperMode(true);
-        cpuProfileData =
-            CpuProfileData.fromJson(cpuProfileDataWithUserTagsJson);
-        for (final filter in controller
-            .cpuProfilerController.activeFilter.value.toggleFilters) {
-          filter.enabled.value = false;
-        }
-        final data = CpuProfilePair(
-          functionProfile: cpuProfileData,
-          // Function and code profiles have the same structure, so just use
-          // the function profile in place of a dedicated code profile for
-          // testing since we don't care about the contents as much as we
-          // care about testing the ability to switch between function and
-          // code profile views.
-          codeProfile: cpuProfileData,
-        );
-        await data.process(
-          transformer: controller.cpuProfilerController.transformer,
-          processId: 'test',
-        );
-        // Call this to force the value of `_dataByTag[userTagNone]` to be set.
-        controller.cpuProfilerController.loadProcessedData(
-          data,
-          storeAsUserTagNone: true,
-        );
-      });
-
-      testWidgetsWithWindowSize('user tags', windowSize, (tester) async {
-        await tester.runAsync(() async {
-          // We need to pump the entire `ProfilerScreenBody` widget because the
-          // CpuProfiler widget has `cpuProfileData` passed in from there, and
-          // CpuProfiler needs to be rebuilt on data updates.
-          await tester.pumpWidget(
-            wrapWithControllers(
-              const ProfilerScreenBody(),
-              profiler: controller,
-            ),
-          );
-          await tester.pump();
-
-          await tester.tap(find.text('Call Tree'));
-          await tester.pumpAndSettle();
-          expect(find.byType(CpuCallTreeTable), findsOneWidget);
-          await tester.tap(find.byType(UserTagDropdown));
-          await tester.pumpAndSettle();
-          await tester.tap(find.text('Group by: User Tag').last);
-
-          // Await a small delay to allow the CpuProfilerController to finish
-          // processing data for the new user tag.
-          await Future.delayed(const Duration(seconds: 1));
-          await tester.pumpAndSettle();
-
-          await tester.tap(find.text('Expand All'));
-          await tester.pumpAndSettle();
-
-          expect(find.richTextContaining('Frame1'), findsNWidgets(3));
-          expect(find.richTextContaining('Frame2'), findsNWidgets(2));
-          expect(find.richTextContaining('Frame3'), findsNWidgets(1));
-          expect(find.richTextContaining('Frame4'), findsNWidgets(1));
-          expect(find.richTextContaining('Frame5'), findsNWidgets(2));
-          expect(find.richTextContaining('Frame6'), findsNWidgets(1));
-          expect(find.richText('userTagA'), findsOneWidget);
-          expect(find.richText('userTagB'), findsOneWidget);
-          expect(find.richText('userTagC'), findsOneWidget);
-        });
-      });
-
-      testWidgetsWithWindowSize('VM tags', windowSize, (tester) async {
-        await tester.runAsync(() async {
-          // We need to pump the entire `ProfilerScreenBody` widget because the
-          // CpuProfiler widget has `cpuProfileData` passed in from there, and
-          // CpuProfiler needs to be rebuilt on data updates.
-          await tester.pumpWidget(
-            wrapWithControllers(
-              const ProfilerScreenBody(),
-              profiler: controller,
-            ),
-          );
-          await tester.pump();
-
-          await tester.tap(find.text('Call Tree'));
-          await tester.pumpAndSettle();
-          expect(find.byType(CpuCallTreeTable), findsOneWidget);
-          await tester.tap(find.byType(UserTagDropdown));
-          await tester.pumpAndSettle();
-          await tester.tap(find.text('Group by: VM Tag').last);
-
-          // Await a small delay to allow the CpuProfilerController to finish
-          // processing data for the new user tag.
-          await Future.delayed(const Duration(seconds: 1));
-          await tester.pumpAndSettle();
-
-          await tester.tap(find.text('Expand All'));
-          await tester.pumpAndSettle();
-
-          expect(find.richTextContaining('Frame1'), findsNWidgets(3));
-          expect(find.richTextContaining('Frame2'), findsNWidgets(2));
-          expect(find.richTextContaining('Frame3'), findsNWidgets(1));
-          expect(find.richTextContaining('Frame4'), findsNWidgets(1));
-          expect(find.richTextContaining('Frame5'), findsNWidgets(2));
-          expect(find.richTextContaining('Frame6'), findsNWidgets(1));
-          expect(find.richText('vmTagA'), findsOneWidget);
-          expect(find.richText('vmTagB'), findsOneWidget);
-          expect(find.richText('vmTagC'), findsOneWidget);
-
-          // Check that disabling VM developer mode when grouping by VM tag
-          // automatically resets the view to 'Filter by tag: none'.
-          preferences.toggleVmDeveloperMode(false);
-          await tester.pumpAndSettle();
-          expect(find.byType(CpuCallTreeTable), findsOneWidget);
-          expect(find.text('Filter by tag: none'), findsOneWidget);
-          await tester.tap(find.byType(UserTagDropdown));
-          await tester.pumpAndSettle();
-          expect(find.text('Group by: VM Tag'), findsNothing);
-        });
-      });
-    });
-  });
-
-  group('$CpuProfileStats', () {
-    testWidgets('displays correctly', (WidgetTester tester) async {
-      final metadata = CpuProfileMetaData(
-        sampleCount: 100,
-        samplePeriod: 100,
-        stackDepth: 128,
-        time: TimeRange()
-          ..start = const Duration()
-          ..end = const Duration(microseconds: 10000),
-      );
-      await tester.pumpWidget(wrap(CpuProfileStats(metadata: metadata)));
-      await tester.pumpAndSettle();
-
-      expect(
-        find.byTooltip('The duration of time spanned by the CPU samples'),
-        findsOneWidget,
-      );
-      expect(
-        find.byTooltip('The number of samples included in the profile'),
-        findsOneWidget,
-      );
-      expect(
-        find.byTooltip(
-          'The frequency at which samples are collected by the profiler'
-          ' (once every 100 micros)',
-        ),
-        findsOneWidget,
-      );
-      expect(
-        find.byTooltip('The maximum stack trace depth of a collected sample'),
-        findsOneWidget,
-      );
-      expect(find.text('Duration: 10.0 ms'), findsOneWidget);
-      expect(find.text('Sample count: 100'), findsOneWidget);
-      expect(find.text('Sampling rate: 10000 Hz'), findsOneWidget);
-      expect(find.text('Sampling depth: 128'), findsOneWidget);
-    });
-
-    testWidgets(
-      'displays correctly for invalid data',
-      (WidgetTester tester) async {
-        final metadata = CpuProfileMetaData(
-          sampleCount: 100,
-          samplePeriod: 0,
-          stackDepth: 128,
-          time: TimeRange()
-            ..start = const Duration()
-            ..end = const Duration(microseconds: 10000),
-        );
-        await tester.pumpWidget(wrap(CpuProfileStats(metadata: metadata)));
-        await tester.pumpAndSettle();
-
-        expect(
-          find.byTooltip('The duration of time spanned by the CPU samples'),
-          findsOneWidget,
-        );
-        expect(
-          find.byTooltip('The number of samples included in the profile'),
-          findsOneWidget,
-        );
-        expect(
-          find.byTooltip(
-            'The frequency at which samples are collected by the profiler',
-          ),
-          findsOneWidget,
-        );
-        expect(
-          find.byTooltip('The maximum stack trace depth of a collected sample'),
-          findsOneWidget,
-        );
-        expect(find.text('Duration: 10.0 ms'), findsOneWidget);
-        expect(find.text('Sample count: 100'), findsOneWidget);
-        expect(find.text('Sampling rate: -- Hz'), findsOneWidget);
-        expect(find.text('Sampling depth: 128'), findsOneWidget);
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/cpu_profiler/method_table/method_table_test.dart b/packages/devtools_app/test/cpu_profiler/method_table/method_table_test.dart
deleted file mode 100644
index 56a2880..0000000
--- a/packages/devtools_app/test/cpu_profiler/method_table/method_table_test.dart
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/profiler/cpu_profile_transformer.dart';
-import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table.dart';
-import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_controller.dart';
-import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_model.dart';
-import 'package:devtools_app/src/shared/table/table.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../test_infra/matchers/matchers.dart';
-import '../../test_infra/test_data/cpu_profiler/simple_profile_2.dart';
-import '../../test_infra/utils/test_utils.dart';
-
-void main() {
-  late MethodTableController methodTableController;
-
-  setUp(() async {
-    setCharacterWidthForTables();
-    setGlobal(
-      ServiceConnectionManager,
-      createMockServiceConnectionWithDefaults(),
-    );
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(OfflineDataController, OfflineDataController());
-    final mockScriptManager = MockScriptManager();
-    when(mockScriptManager.sortedScripts).thenReturn(
-      ValueNotifier<List<ScriptRef>>([]),
-    );
-    when(mockScriptManager.scriptRefForUri(any)).thenReturn(
-      ScriptRef(
-        uri: 'package:test/script.dart',
-        id: 'script.dart',
-      ),
-    );
-    setGlobal(ScriptManager, mockScriptManager);
-    final data = CpuProfileData.fromJson(simpleCpuProfile2);
-    await CpuProfileTransformer().processData(data, processId: 'test');
-    methodTableController = MethodTableController(
-      dataNotifier: FixedValueListenable<CpuProfileData>(data),
-    )..createMethodTableGraph(data);
-  });
-
-  const windowSize = Size(2000.0, 1000.0);
-
-  Future<void> pumpMethodTable(WidgetTester tester) async {
-    await tester.pumpWidget(
-      wrap(
-        CpuMethodTable(
-          methodTableController: methodTableController,
-        ),
-      ),
-    );
-    await tester.pumpAndSettle();
-    expect(find.byType(CpuMethodTable), findsOneWidget);
-  }
-
-  group('$CpuMethodTable', () {
-    testWidgetsWithWindowSize(
-      'loads methods with no selection',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpMethodTable(tester);
-        expect(
-          find.byType(SearchableFlatTable<MethodTableGraphNode>),
-          findsOneWidget,
-        );
-        expect(find.text('Method'), findsOneWidget);
-        expect(find.text('Total %'), findsOneWidget);
-        expect(find.text('Self %'), findsOneWidget);
-        expect(find.text('Caller %'), findsNothing);
-        expect(find.text('Callee %'), findsNothing);
-        expect(
-          find.text('Select a method to view its call graph.'),
-          findsOneWidget,
-        );
-        await expectLater(
-          find.byType(CpuMethodTable),
-          matchesDevToolsGolden(
-            '../../test_infra/goldens/cpu_profiler/method_table_no_selection.png',
-          ),
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'loads methods with selection',
-      windowSize,
-      (WidgetTester tester) async {
-        final interestingNode = methodTableController.methods.value.first;
-        expect(interestingNode.name, 'A');
-        expect(interestingNode.predecessors, isNotEmpty);
-        expect(interestingNode.successors, isNotEmpty);
-
-        methodTableController.selectedNode.value = interestingNode;
-        await pumpMethodTable(tester);
-        expect(
-          find.byType(SearchableFlatTable<MethodTableGraphNode>),
-          findsOneWidget,
-        );
-        expect(find.byType(FlatTable<MethodTableGraphNode>), findsNWidgets(2));
-        expect(find.text('Method'), findsNWidgets(3));
-        expect(find.text('Total %'), findsOneWidget);
-        expect(find.text('Self %'), findsOneWidget);
-        expect(find.text('Caller %'), findsOneWidget);
-        expect(find.text('Callee %'), findsOneWidget);
-        expect(
-          find.text('Select a method to view its call graph.'),
-          findsNothing,
-        );
-        await expectLater(
-          find.byType(CpuMethodTable),
-          matchesDevToolsGolden(
-            '../../test_infra/goldens/cpu_profiler/method_table_with_selection.png',
-          ),
-        );
-
-        methodTableController.selectedNode.value =
-            methodTableController.methods.value.last;
-        await tester.pumpAndSettle();
-        await expectLater(
-          find.byType(CpuMethodTable),
-          matchesDevToolsGolden(
-            '../../test_infra/goldens/cpu_profiler/method_table_with_selection_2.png',
-          ),
-        );
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/debugger/debugger_evaluation_test.dart b/packages/devtools_app/test/debugger/debugger_evaluation_test.dart
deleted file mode 100644
index 6e18a47..0000000
--- a/packages/devtools_app/test/debugger/debugger_evaluation_test.dart
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/console/eval/auto_complete.dart';
-import 'package:devtools_app_shared/service.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../test_infra/flutter_test_driver.dart';
-import '../test_infra/flutter_test_environment.dart';
-import '../test_infra/flutter_test_storage.dart';
-
-void main() {
-  setGlobal(Storage, FlutterTestStorage());
-
-  final env = FlutterTestEnvironment(
-    const FlutterRunConfiguration(withDebugger: true),
-  );
-
-  late Disposable isAlive;
-  late DebuggerController debuggerController;
-  late EvalOnDartLibrary eval;
-
-  setUp(() async {
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(BreakpointManager, BreakpointManager());
-    setGlobal(EvalService, EvalService());
-    setGlobal(NotificationService, NotificationService());
-    isAlive = Disposable();
-    await env.setupEnvironment();
-    debuggerController = DebuggerController();
-
-    eval = EvalOnDartLibrary(
-      'package:flutter_app/src/autocomplete.dart',
-      serviceConnection.serviceManager.service!,
-      serviceManager: serviceConnection.serviceManager,
-      disableBreakpoints: false,
-    );
-  });
-
-  tearDown(() async {
-    await debuggerController.resume();
-    isAlive.dispose();
-    debuggerController.dispose();
-    await env.tearDownEnvironment();
-  });
-
-  tearDownAll(() async {
-    await env.tearDownEnvironment(force: true);
-  });
-
-  Future<void> runMethodAndWaitForPause(String method) async {
-    unawaited(eval.eval(method, isAlive: isAlive));
-
-    await whenMatches(debuggerController.selectedStackFrame, (f) => f != null);
-  }
-
-  group(
-    'EvalOnDartLibrary',
-    () {
-      test(
-        'returns scoped variables when EditingParts is not a field',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'foo',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(['foo', 'foobar']),
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'b',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(['bar', 'baz']),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-
-      test(
-        'returns filtered members when EditingParts is a field ',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'f',
-                leftSide: 'foo.',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(['field1', 'field2', 'func1', 'func2']),
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'fu',
-                leftSide: 'foo.',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(['func1', 'func2']),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-
-      test(
-        'returns filtered members when EditingParts is a class name ',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                leftSide: 'FooClass.',
-                activeWord: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([
-              'staticField1',
-              'staticField2',
-              'namedConstructor',
-              'factory1',
-              'staticMethod',
-            ]),
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'fa',
-                leftSide: 'FooClass.',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(['factory1']),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-      test(
-        'returns privates only from library',
-        // TODO(https://github.com/flutter/devtools/issues/7099): unskip once
-        // this test flake is fixed.
-        skip: true,
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          await expectLater(
-            autoCompleteResultsFor(
-              EditingParts(
-                activeWord: '_',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            completion(
-              unorderedEquals(
-                [
-                  '_privateField2',
-                  '_privateField1',
-                  '_PrivateClass',
-                ],
-              ),
-            ),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-      test(
-        'returns exported members from import',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'exportedField',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([
-              'exportedField',
-            ]),
-          );
-
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'ExportedClass',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([
-              'ExportedClass',
-            ]),
-          );
-
-          // Privates are not exported
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: '_privateExportedField',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([]),
-          );
-
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: '_PrivateExportedClass',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([]),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-
-      test(
-        'returns prefixes of libraries imported',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'developer',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([
-              'developer',
-            ]),
-          );
-
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                activeWord: 'math',
-                leftSide: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals([
-              'math',
-            ]),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-
-      test(
-        'returns no operators for int',
-        () async {
-          await runMethodAndWaitForPause(
-            'AnotherClass().pauseWithScopedVariablesMethod()',
-          );
-          expect(
-            await autoCompleteResultsFor(
-              EditingParts(
-                leftSide: '7.',
-                activeWord: '',
-                rightSide: '',
-              ),
-              evalService,
-            ),
-            equals(
-              [
-                'hashCode',
-                'bitLength',
-                'toString',
-                'remainder',
-                'abs',
-                'sign',
-                'isEven',
-                'isOdd',
-                'isNaN',
-                'isNegative',
-                'isInfinite',
-                'isFinite',
-                'toUnsigned',
-                'toSigned',
-                'compareTo',
-                'round',
-                'floor',
-                'ceil',
-                'truncate',
-                'roundToDouble',
-                'floorToDouble',
-                'ceilToDouble',
-                'truncateToDouble',
-                'clamp',
-                'toInt',
-                'toDouble',
-                'toStringAsFixed',
-                'toStringAsExponential',
-                'toStringAsPrecision',
-                'toRadixString',
-                'modPow',
-                'modInverse',
-                'gcd',
-                'noSuchMethod',
-                'runtimeType',
-              ],
-            ),
-          );
-        },
-        timeout: const Timeout.factor(8),
-      );
-    },
-  );
-}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_call_stack_test.dart b/packages/devtools_app/test/debugger/debugger_screen_call_stack_test.dart
deleted file mode 100644
index 22ee58d..0000000
--- a/packages/devtools_app/test/debugger/debugger_screen_call_stack_test.dart
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
-import 'package:devtools_app/src/shared/diagnostics/primitives/source_location.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-void main() {
-  late FakeServiceConnectionManager fakeServiceConnection;
-  late MockDebuggerController debuggerController;
-  late MockScriptManager scriptManager;
-
-  const windowSize = Size(4000.0, 4000.0);
-
-  setUp(() {
-    fakeServiceConnection = FakeServiceConnectionManager();
-    scriptManager = MockScriptManager();
-    mockConnectedApp(
-      fakeServiceConnection.serviceManager.connectedApp!,
-      isProfileBuild: false,
-      isFlutterApp: true,
-      isWebApp: false,
-    );
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(BreakpointManager, BreakpointManager());
-    setGlobal(ScriptManager, scriptManager);
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
-    debuggerController = createMockDebuggerControllerWithDefaults();
-  });
-
-  Future<void> pumpDebuggerScreen(
-    WidgetTester tester,
-    DebuggerController controller,
-  ) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const DebuggerWindows(),
-        debugger: controller,
-      ),
-    );
-  }
-
-  testWidgetsWithWindowSize(
-    'Call Stack shows items',
-    windowSize,
-    (WidgetTester tester) async {
-      final stackFrames = [
-        Frame(
-          index: 0,
-          code: CodeRef(
-            name: 'testCodeRef',
-            id: 'testCodeRef',
-            kind: CodeKind.kDart,
-          ),
-          location: SourceLocation(
-            script:
-                ScriptRef(uri: 'package:test/script.dart', id: 'script.dart'),
-            tokenPos: 10,
-          ),
-          kind: FrameKind.kRegular,
-        ),
-        Frame(
-          index: 1,
-          location: SourceLocation(
-            script:
-                ScriptRef(uri: 'package:test/script1.dart', id: 'script1.dart'),
-            tokenPos: 10,
-          ),
-          kind: FrameKind.kRegular,
-        ),
-        Frame(
-          index: 2,
-          code: CodeRef(
-            name: '[Unoptimized] testCodeRef2',
-            id: 'testCodeRef2',
-            kind: CodeKind.kDart,
-          ),
-          location: SourceLocation(
-            script:
-                ScriptRef(uri: 'package:test/script2.dart', id: 'script2.dart'),
-            tokenPos: 10,
-          ),
-          kind: FrameKind.kRegular,
-        ),
-        Frame(
-          index: 3,
-          code: CodeRef(
-            name: 'testCodeRef3.<anonymous closure>',
-            id: 'testCodeRef3.closure',
-            kind: CodeKind.kDart,
-          ),
-          location: SourceLocation(
-            script:
-                ScriptRef(uri: 'package:test/script3.dart', id: 'script3.dart'),
-            tokenPos: 10,
-          ),
-          kind: FrameKind.kRegular,
-        ),
-        Frame(
-          index: 4,
-          location: SourceLocation(
-            script:
-                ScriptRef(uri: 'package:test/script4.dart', id: 'script4.dart'),
-            tokenPos: 10,
-          ),
-          kind: FrameKind.kAsyncSuspensionMarker,
-        ),
-        Frame(
-          index: 5,
-          code: CodeRef(
-            name: '_createTimer',
-            id: 'testJsCodeRefId',
-            kind: CodeKind.kNative,
-          ),
-          location: SourceLocation(
-            script: ScriptRef(
-              uri: 'http://localhost:63691/dwds/src/injected/client.js',
-              id: 'jsScriptId',
-            ),
-            tokenPos: 12,
-          ),
-          kind: FrameKind.kRegular,
-        ),
-      ];
-
-      final stackFramesWithLocation =
-          stackFrames.map<StackFrameAndSourcePosition>((frame) {
-        return StackFrameAndSourcePosition(
-          frame,
-          position: SourcePosition(
-            line: stackFrames.indexOf(frame),
-            column: 10,
-          ),
-        );
-      }).toList();
-
-      when(debuggerController.stackFramesWithLocation)
-          .thenReturn(ValueNotifier(stackFramesWithLocation));
-      final codeViewController = debuggerController.codeViewController;
-      when(codeViewController.showFileOpener).thenReturn(ValueNotifier(false));
-      await pumpDebuggerScreen(tester, debuggerController);
-
-      expect(find.text('Call Stack'), findsOneWidget);
-
-      // Stack frame 0
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text.toPlainText().contains('testCodeRef script.dart:0'),
-        ),
-        findsOneWidget,
-      );
-
-      // verify that the frame has a tooltip
-      expect(
-        find.byTooltip('testCodeRef script.dart:0'),
-        findsOneWidget,
-      );
-
-      // Stack frame 1
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text.toPlainText().contains('<none> script1.dart:1'),
-        ),
-        findsOneWidget,
-      );
-      // Stack frame 2
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text.toPlainText().contains('testCodeRef2 script2.dart:2'),
-        ),
-        findsOneWidget,
-      );
-      // Stack frame 3
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text
-                  .toPlainText()
-                  .contains('testCodeRef3.<closure> script3.dart:3'),
-        ),
-        findsOneWidget,
-      );
-      // Stack frame 4
-      expect(find.text('<async break>'), findsOneWidget);
-      // Stack frame 5
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text
-                  .toPlainText()
-                  .contains('<native code: _createTimer> client.js'),
-        ),
-        findsOneWidget,
-      );
-    },
-  );
-}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_dap_variables_test.dart b/packages/devtools_app/test/debugger/debugger_screen_dap_variables_test.dart
deleted file mode 100644
index 275e9aa..0000000
--- a/packages/devtools_app/test/debugger/debugger_screen_dap_variables_test.dart
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:dap/dap.dart' as dap;
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/diagnostics/dap_object_node.dart';
-import 'package:devtools_app/src/shared/feature_flags.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-void main() {
-  late FakeServiceConnectionManager fakeServiceConnection;
-  late MockDebuggerController debuggerController;
-  late MockScriptManager scriptManager;
-  late MockVmServiceWrapper vmService;
-
-  const windowSize = Size(2500, 1500);
-
-  setUp(() {
-    FeatureFlags.dapDebugging = true;
-    vmService = createMockVmServiceWrapperWithDefaults();
-    fakeServiceConnection = FakeServiceConnectionManager(service: vmService);
-    scriptManager = MockScriptManager();
-
-    mockConnectedApp(
-      fakeServiceConnection.serviceManager.connectedApp!,
-      isProfileBuild: false,
-      isFlutterApp: true,
-      isWebApp: false,
-    );
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(ScriptManager, scriptManager);
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(BreakpointManager, BreakpointManager());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
-    debuggerController = createMockDebuggerControllerWithDefaults();
-  });
-
-  tearDown(() {
-    fakeServiceConnection.appState.setDapVariables(
-      [],
-    );
-  });
-
-  Future<void> pumpDebuggerScreen(
-    WidgetTester tester,
-    DebuggerController controller,
-  ) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const DebuggerWindows(),
-        debugger: controller,
-      ),
-    );
-  }
-
-  testWidgetsWithWindowSize(
-    'Shows non-expandable variables',
-    windowSize,
-    (WidgetTester tester) async {
-      final node = DapObjectNode(
-        service: vmService,
-        variable: dap.Variable(
-          name: 'myInt',
-          value: '10',
-          variablesReference: 0,
-        ),
-      );
-
-      fakeServiceConnection.appState.setDapVariables(
-        [node],
-      );
-      await pumpDebuggerScreen(tester, debuggerController);
-      expect(find.text('Variables'), findsOneWidget);
-
-      // Variables should include the int.
-      final intFinder = find.text('myInt: 10');
-      expect(intFinder, findsOneWidget);
-
-      // The int is not expandable.
-      final expandArrowFinder = find.byIcon(Icons.keyboard_arrow_down);
-      expect(expandArrowFinder, findsNothing);
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'Shows expandable variables',
-    windowSize,
-    (WidgetTester tester) async {
-      when(vmService.dapVariablesRequest(any)).thenAnswer((_) async {
-        return dap.VariablesResponseBody(
-          variables: [
-            dap.Variable(
-              name: 'myString',
-              value: '"myString"',
-              variablesReference: 0,
-            ),
-          ],
-        );
-      });
-
-      final node = DapObjectNode(
-        service: vmService,
-        variable: dap.Variable(
-          name: 'myList',
-          value: 'List (1 item)',
-          variablesReference: 1,
-        ),
-      );
-      await node.fetchChildren();
-
-      fakeServiceConnection.appState.setDapVariables(
-        [node],
-      );
-      await pumpDebuggerScreen(tester, debuggerController);
-      expect(find.text('Variables'), findsOneWidget);
-
-      // Variables should include the list.
-      final listFinder = find.text('myList: List (1 item)');
-      expect(listFinder, findsOneWidget);
-
-      // Initially the string is not visible.
-      final stringFinder = find.text('myString: "myString"');
-      expect(stringFinder, findsNothing);
-
-      // Expand the list.
-      final expandArrowFinder = find.byIcon(Icons.keyboard_arrow_down);
-      expect(expandArrowFinder, findsOneWidget);
-      await tester.tap(expandArrowFinder.first);
-      await tester.pump();
-
-      // String is now visible.
-      expect(stringFinder, findsOneWidget);
-    },
-  );
-}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_explorer_visibility_test.dart b/packages/devtools_app/test/debugger/debugger_screen_explorer_visibility_test.dart
deleted file mode 100644
index c407322..0000000
--- a/packages/devtools_app/test/debugger/debugger_screen_explorer_visibility_test.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-void main() {
-  const windowSize = Size(4000.0, 4000.0);
-
-  late FakeServiceConnectionManager fakeServiceConnection;
-  late MockScriptManager scriptManager;
-  late MockDebuggerController debuggerController;
-  late CodeViewController mockCodeViewController;
-  final scripts = [
-    ScriptRef(uri: 'package:test/script.dart', id: 'test-script'),
-  ];
-
-  group(
-    'FileExplorer',
-    () {
-      setUp(() {
-        fakeServiceConnection = FakeServiceConnectionManager();
-        scriptManager = MockScriptManager();
-        mockConnectedApp(
-          fakeServiceConnection.serviceManager.connectedApp!,
-          isFlutterApp: true,
-          isProfileBuild: false,
-          isWebApp: false,
-        );
-        setGlobal(ServiceConnectionManager, fakeServiceConnection);
-        setGlobal(IdeTheme, IdeTheme());
-        setGlobal(NotificationService, NotificationService());
-        setGlobal(ScriptManager, scriptManager);
-        setGlobal(BreakpointManager, BreakpointManager());
-        setGlobal(
-          DevToolsEnvironmentParameters,
-          ExternalDevToolsEnvironmentParameters(),
-        );
-        setGlobal(PreferencesController, PreferencesController());
-        fakeServiceConnection.consoleService.ensureServiceInitialized();
-        when(
-          fakeServiceConnection.errorBadgeManager
-              .errorCountNotifier('debugger'),
-        ).thenReturn(ValueNotifier<int>(0));
-        final mockProgramExplorerController =
-            createMockProgramExplorerControllerWithDefaults();
-        mockCodeViewController = createMockCodeViewControllerWithDefaults(
-          programExplorerController: mockProgramExplorerController,
-        );
-        debuggerController = createMockDebuggerControllerWithDefaults(
-          codeViewController: mockCodeViewController,
-        );
-
-        when(scriptManager.sortedScripts).thenReturn(ValueNotifier(scripts));
-
-        when(mockProgramExplorerController.rootObjectNodes).thenReturn(
-          ValueNotifier(
-            [
-              VMServiceObjectNode(
-                mockCodeViewController.programExplorerController,
-                'package:test',
-                null,
-              ),
-            ],
-          ),
-        );
-        when(mockCodeViewController.showFileOpener)
-            .thenReturn(ValueNotifier(false));
-      });
-
-      Future<void> pumpDebuggerScreen(
-        WidgetTester tester,
-        DebuggerController controller,
-      ) async {
-        await tester.pumpWidget(
-          wrapWithControllers(
-            DebuggerSourceAndControls(
-              shownFirstScript: () => true,
-              setShownFirstScript: (_) {},
-            ),
-            debugger: controller,
-          ),
-        );
-      }
-
-      testWidgetsWithWindowSize(
-        'visible',
-        windowSize,
-        (WidgetTester tester) async {
-          // File Explorer view is shown
-          when(mockCodeViewController.fileExplorerVisible)
-              .thenReturn(ValueNotifier(true));
-          await pumpDebuggerScreen(tester, debuggerController);
-          // One for the button and one for the title of the File Explorer view.
-          expect(find.text('File Explorer'), findsNWidgets(2));
-
-          // test for items in the libraries tree
-          expect(
-            find.text(scripts.first.uri!.split('/').first),
-            findsOneWidget,
-          );
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'hidden',
-        windowSize,
-        (WidgetTester tester) async {
-          // File Explorer view is hidden
-          when(mockCodeViewController.fileExplorerVisible)
-              .thenReturn(ValueNotifier(false));
-          await pumpDebuggerScreen(tester, debuggerController);
-          expect(find.text('File Explorer'), findsOneWidget);
-        },
-      );
-    },
-  );
-}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_test.dart b/packages/devtools_app/test/debugger/debugger_screen_test.dart
deleted file mode 100644
index 8e8f3a1..0000000
--- a/packages/devtools_app/test/debugger/debugger_screen_test.dart
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:codicon/codicon.dart';
-import 'package:collection/collection.dart';
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
-import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../test_infra/test_data/debugger/vm_service_object_tree.dart';
-import '../test_infra/utils/debugger_utils.dart';
-import '../test_infra/utils/test_utils.dart';
-import '../test_infra/utils/tree_utils.dart';
-
-void main() {
-  final screen = DebuggerScreen();
-
-  const windowSize = Size(4000.0, 4000.0);
-  const smallWindowSize = Size(1200.0, 1100.0);
-
-  late MockDebuggerController debuggerController;
-  late TestProgramExplorerController programExplorerController;
-  late VMServiceObjectNode libraryNode;
-
-  setUp(() async {
-    final fakeServiceConnection = FakeServiceConnectionManager();
-    final scriptManager = MockScriptManager();
-    when(scriptManager.getScript(any)).thenAnswer(
-      (_) => Future<Script>.value(testScript),
-    );
-    mockConnectedApp(
-      fakeServiceConnection.serviceManager.connectedApp!,
-      isFlutterApp: true,
-      isProfileBuild: false,
-      isWebApp: false,
-    );
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(ScriptManager, scriptManager);
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(BreakpointManager, BreakpointManager());
-    setGlobal(EvalService, MockEvalService());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(BannerMessagesController, BannerMessagesController());
-    fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
-
-    programExplorerController = TestProgramExplorerController(
-      initializer: (controller) {
-        libraryNode = VMServiceObjectNode(controller, 'fooLib', testLib);
-        libraryNode.script = testScript;
-        libraryNode.location = ScriptLocation(testScript);
-        controller.rootObjectNodesInternal.add(libraryNode);
-      },
-    );
-    await programExplorerController.initialize();
-    await programExplorerController.selectNode(libraryNode);
-
-    final codeViewController = createMockCodeViewControllerWithDefaults(
-      programExplorerController: programExplorerController,
-    );
-    when(codeViewController.showFileOpener).thenReturn(ValueNotifier(false));
-    when(codeViewController.fileExplorerVisible)
-        .thenReturn(ValueNotifier(true));
-    debuggerController = createMockDebuggerControllerWithDefaults(
-      codeViewController: codeViewController,
-    );
-  });
-
-  Future<void> pumpConsole(
-    WidgetTester tester,
-    DebuggerController controller,
-  ) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        Row(
-          children: [
-            Flexible(child: ConsolePaneHeader()),
-            const Expanded(child: ConsolePane()),
-          ],
-        ),
-        debugger: controller,
-      ),
-    );
-  }
-
-  testWidgets('builds its tab', (WidgetTester tester) async {
-    await tester.pumpWidget(wrap(Builder(builder: screen.buildTab)));
-    expect(find.text('Debugger'), findsOneWidget);
-  });
-
-  testWidgetsWithWindowSize(
-    'has Console / stdio area',
-    windowSize,
-    (WidgetTester tester) async {
-      serviceConnection.consoleService.appendStdio('test stdio');
-
-      await pumpConsole(tester, debuggerController);
-
-      expect(find.text('Console'), findsOneWidget);
-
-      // test for stdio output.
-      expect(find.text('test stdio'), findsOneWidget);
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'debugger controls running',
-    windowSize,
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.build),
-          debugger: debuggerController,
-        ),
-      );
-
-      expect(
-        findDebuggerButtonWithIcon(Codicons.debugPause),
-        findsOneWidget,
-      );
-      final pause = getWidgetFromFinder<OutlinedButton>(
-        findDebuggerButtonWithIcon(Codicons.debugPause),
-      );
-      expect(pause.onPressed, isNotNull);
-
-      expect(
-        findDebuggerButtonWithIcon(Codicons.debugContinue),
-        findsOneWidget,
-      );
-      final resume = getWidgetFromFinder<OutlinedButton>(
-        findDebuggerButtonWithIcon(Codicons.debugContinue),
-      );
-      expect(resume.onPressed, isNull);
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'debugger controls break on exceptions',
-    windowSize,
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.build),
-          debugger: debuggerController,
-        ),
-      );
-      expect(
-        find.text("Don't stop on exceptions", skipOffstage: false),
-        findsOneWidget,
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'debugger controls break on exceptions abbreviated on small window',
-    smallWindowSize,
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.build),
-          debugger: debuggerController,
-        ),
-      );
-      expect(
-        find.text('Ignore exceptions', skipOffstage: false),
-        findsOneWidget,
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'node selection state',
-    windowSize,
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.build),
-          debugger: debuggerController,
-        ),
-      );
-      final delegate = tester
-          .firstWidget<MaterialApp>(find.byType(MaterialApp))
-          .routerDelegate as DevToolsRouterDelegate;
-      final libNode = programExplorerController.rootObjectNodes.value.first;
-      final libScript = libraryNode.script!;
-      final libScriptRef = ScriptRef(
-        id: libScript.id!,
-        uri: libScript.uri,
-      );
-
-      // Select the library node and ensure the outline is populated.
-      final libNodeFinder = find.text(libNode.name);
-      expect(libNodeFinder, findsOneWidget);
-      await tester.tap(libNodeFinder);
-      await tester.pump();
-
-      expect(programExplorerController.scriptSelection, libNode);
-
-      CodeViewSourceLocationNavigationState? state =
-          CodeViewSourceLocationNavigationState.fromState(
-        delegate.currentConfiguration!.state,
-      );
-
-      expect(state, isNotNull);
-      expect(state!.script, libScriptRef);
-      expect(state.line, 0);
-
-      // There should be three children total, one root with two children.
-      expect(programExplorerController.outlineNodes.value.length, 1);
-      expect(programExplorerController.outlineNodes.value.numNodes, 3);
-
-      // Select one of them and check that the outline selection has been
-      // updated.
-      final outlineNode = programExplorerController.outlineNodes.value.first;
-      final outlineNodeFinder = find.text(outlineNode.name);
-      expect(outlineNodeFinder, findsOneWidget);
-      await tester.tap(outlineNodeFinder);
-      await tester.pump();
-
-      expect(programExplorerController.scriptSelection, libNode);
-      expect(
-        programExplorerController.outlineNodes.value
-            .singleWhereOrNull((e) => e.isSelected),
-        outlineNode,
-      );
-      state = CodeViewSourceLocationNavigationState.fromState(
-        delegate.currentConfiguration!.state,
-      );
-      expect(state, isNotNull);
-      expect(state!.script, libScriptRef);
-      expect(state.line, testClassRef.location!.line);
-    },
-  );
-}
diff --git a/packages/devtools_app/test/debugger/debugger_scripts_test.dart b/packages/devtools_app/test/debugger/debugger_scripts_test.dart
deleted file mode 100644
index 845517c..0000000
--- a/packages/devtools_app/test/debugger/debugger_scripts_test.dart
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/debugger/codeview.dart';
-import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
-import 'package:devtools_app/src/shared/diagnostics/primitives/source_location.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../test_infra/utils/test_utils.dart';
-
-void main() {
-  late CodeViewController codeViewController;
-  late MockDebuggerController mockDebuggerController;
-
-  const smallWindowSize = Size(1200.0, 1000.0);
-
-  void initializeGlobalsAndMockApp() {
-    final fakeServiceConnection = FakeServiceConnectionManager();
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(BreakpointManager, BreakpointManager());
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(ScriptManager, MockScriptManager());
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-
-    mockConnectedApp(
-      fakeServiceConnection.serviceManager.connectedApp!,
-      isProfileBuild: false,
-      isFlutterApp: true,
-      isWebApp: false,
-    );
-  }
-
-  Future<void> pumpDebuggerScreen(
-    WidgetTester tester,
-    DebuggerController controller,
-  ) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        DebuggerSourceAndControls(
-          shownFirstScript: () => true,
-          setShownFirstScript: (_) {},
-        ),
-        debugger: controller,
-      ),
-    );
-  }
-
-  Future<void> showScript(ScriptRef scriptRef) async {
-    await codeViewController.showScriptLocation(
-      ScriptLocation(
-        scriptRef,
-        location: const SourcePosition(line: 1, column: 1),
-      ),
-    );
-  }
-
-  setUpAll(() async {
-    initializeGlobalsAndMockApp();
-    await SyntaxHighlighter.initialize();
-    codeViewController = CodeViewController();
-    mockDebuggerController = createMockDebuggerControllerWithDefaults(
-      codeViewController: codeViewController,
-    );
-  });
-
-  group('for a script with < 100000 lines', () {
-    setUpAll(() {
-      when(scriptManager.getScriptCached(mockScriptRef)).thenReturn(mockScript);
-    });
-
-    testWidgetsWithWindowSize(
-      'lines of the script are visible',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockScriptRef);
-        await tester.pumpAndSettle();
-
-        expectFirstNLinesContain(
-          [
-            '// Copyright 2019 The Flutter team. All rights reserved',
-            '// Use of this source code is governed by a BSD-style license that can be',
-            '// found in the LICENSE file.',
-          ],
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'lines of the script are highlighted',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(firstNLinesAreHighlighted(10), isTrue);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'script name is visible',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(
-          find.text('package:gallery/main.dart'),
-          findsOneWidget,
-        );
-      },
-    );
-  });
-
-  group('for a script with > 100000 lines', () {
-    setUpAll(() {
-      when(scriptManager.getScriptCached(mockLargeScriptRef))
-          .thenReturn(mockLargeScript);
-    });
-
-    testWidgetsWithWindowSize(
-      'script name is visible',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockLargeScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(
-          find.text('package:front_end/src/fasta/kernel/body_builder.dart'),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'lines of the script are visible',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockLargeScriptRef);
-        await tester.pumpAndSettle();
-
-        expectFirstNLinesContain(
-          [
-            '// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file',
-            '// for details. All rights reserved. Use of this source code is governed by a',
-            '// BSD-style license that can be found in the LICENSE file.',
-          ],
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'lines of the script are not highlighted',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockLargeScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(firstNLinesAreHighlighted(10), isFalse);
-      },
-    );
-  });
-
-  group('for a script with no source', () {
-    setUpAll(() {
-      when(scriptManager.getScriptCached(mockScriptRef)).thenReturn(mockScript);
-      when(scriptManager.getScriptCached(mockEmptyScriptRef))
-          .thenReturn(mockEmptyScript);
-    });
-
-    testWidgetsWithWindowSize(
-      'script name does not update',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(
-          find.text('package:gallery/main.dart'),
-          findsOneWidget,
-        );
-
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockEmptyScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(
-          find.text('package:gallery/main.dart'),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'lines of the script do not update',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockScriptRef);
-        await tester.pumpAndSettle();
-
-        expectFirstNLinesContain(
-          [
-            '// Copyright 2019 The Flutter team. All rights reserved',
-            '// Use of this source code is governed by a BSD-style license that can be',
-            '// found in the LICENSE file.',
-          ],
-        );
-
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        await showScript(mockEmptyScriptRef);
-        await tester.pumpAndSettle();
-
-        expectFirstNLinesContain(
-          [
-            '// Copyright 2019 The Flutter team. All rights reserved',
-            '// Use of this source code is governed by a BSD-style license that can be',
-            '// found in the LICENSE file.',
-          ],
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'an error message is shown',
-      smallWindowSize,
-      (WidgetTester tester) async {
-        await pumpDebuggerScreen(tester, mockDebuggerController);
-        // Dismiss any previous notifications:
-        notificationService
-            .dismiss('Failed to parse package:gallery/src/unknown.dart.');
-        await tester.pumpAndSettle();
-
-        await showScript(mockEmptyScriptRef);
-        await tester.pumpAndSettle();
-
-        expect(
-          notificationService.activeMessages.first.text,
-          equals('Failed to parse package:gallery/src/unknown.dart.'),
-        );
-      },
-    );
-  });
-}
-
-bool firstNLinesAreHighlighted(int n) {
-  bool containsNonHighlightedLine = false;
-  final lines = find.byType(LineItem);
-  for (int i = 0; i < n; i++) {
-    final line = getWidgetFromFinder<LineItem>(lines.at(i));
-    if (line.lineContents.children == null) {
-      containsNonHighlightedLine = true;
-    }
-  }
-  return !containsNonHighlightedLine;
-}
-
-void expectFirstNLinesContain(List<String> stringMatches) {
-  final lines = find.byType(LineItem);
-  expect(lines, findsAtLeastNWidgets(stringMatches.length));
-  for (int i = 0; i < stringMatches.length; i++) {
-    final stringMatch = stringMatches[i];
-    final line = getWidgetFromFinder<LineItem>(lines.at(i));
-    expect(line.lineContents.toPlainText(), contains(stringMatch));
-  }
-}
diff --git a/packages/devtools_app/test/debugger/file_search_test.dart b/packages/devtools_app/test/debugger/file_search_test.dart
deleted file mode 100644
index d4e055c..0000000
--- a/packages/devtools_app/test/debugger/file_search_test.dart
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/debugger/file_search.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-void main() {
-  final codeViewController = createMockCodeViewControllerWithDefaults();
-  final scriptManager = MockScriptManager();
-
-  Widget buildFileSearch() {
-    return MaterialApp(
-      home: Scaffold(
-        body: Card(
-          child: FileSearchField(
-            codeViewController: codeViewController,
-          ),
-        ),
-      ),
-    );
-  }
-
-  when(scriptManager.sortedScripts).thenReturn(ValueNotifier(mockScriptRefs));
-  setGlobal(IdeTheme, IdeTheme());
-  setGlobal(ScriptManager, scriptManager);
-
-  testWidgetsWithWindowSize(
-    'Selecting search sets current file',
-    const Size(1000.0, 4000.0),
-    (WidgetTester tester) async {
-      await tester.pumpWidget(buildFileSearch());
-      final FileSearchFieldState state =
-          tester.state(find.byType(FileSearchField));
-      final autoCompleteController = state.autoCompleteController;
-
-      autoCompleteController.search = 'cat';
-
-      await tester.pumpAndSettle();
-
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATerpillar.dart',
-            // Exact full path matches:
-            'zoo:animals/CATs/meow.dart',
-            'zoo:animals/CATs/purr.dart',
-            'kitchen:food/CATering/party.dart',
-            // Fuzzy matches:
-            'zoo:animals/insects/CicAda.darT',
-            'kitchen:food/milk/CArTon.dart',
-            'travel:adventure/CAve_Tours_europe.dart',
-          ],
-        ),
-      );
-
-      final tileFinder = find.byType(AutoCompleteTile);
-      expect(tileFinder, findsNWidgets(7));
-
-      await tester.tap(tileFinder.at(3));
-
-      expect(
-        autoCompleteController.search,
-        equals('kitchen:food/catering/party.dart'),
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'Search returns expected files',
-    const Size(1000.0, 4000.0),
-    (WidgetTester tester) async {
-      await tester.pumpWidget(buildFileSearch());
-      final FileSearchFieldState state =
-          tester.state(find.byType(FileSearchField));
-      final autoCompleteController = state.autoCompleteController;
-
-      autoCompleteController.search = '';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Show all results (truncated to 10):
-            'zoo:animals/cats/meow.dart',
-            'zoo:animals/cats/purr.dart',
-            'zoo:animals/dogs/bark.dart',
-            'zoo:animals/dogs/growl.dart',
-            'zoo:animals/insects/caterpillar.dart',
-            'zoo:animals/insects/cicada.dart',
-            'kitchen:food/catering/party.dart',
-            'kitchen:food/carton/milk.dart',
-            'kitchen:food/milk/carton.dart',
-            'travel:adventure/cave_tours_europe.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'c';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/Caterpillar.dart',
-            'zoo:animals/insects/Cicada.dart',
-            'kitchen:food/milk/Carton.dart',
-            'travel:adventure/Cave_tours_europe.dart',
-            // Exact full path matches:
-            'zoo:animals/Cats/meow.dart',
-            'zoo:animals/Cats/purr.dart',
-            'kitChen:food/catering/party.dart',
-            'kitChen:food/carton/milk.dart',
-            'travel:Canada/banff.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'ca';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CAterpillar.dart',
-            'zoo:animals/insects/ciCAda.dart',
-            'kitchen:food/milk/CArton.dart',
-            'travel:adventure/CAve_tours_europe.dart',
-            // Exact full path matches:
-            'zoo:animals/CAts/meow.dart',
-            'zoo:animals/CAts/purr.dart',
-            'kitchen:food/CAtering/party.dart',
-            'kitchen:food/CArton/milk.dart',
-            'travel:CAnada/banff.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'cat';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATerpillar.dart',
-            // Exact full path matches:
-            'zoo:animals/CATs/meow.dart',
-            'zoo:animals/CATs/purr.dart',
-            'kitchen:food/CATering/party.dart',
-            // Fuzzy matches:
-            'zoo:animals/insects/CicAda.darT',
-            'kitchen:food/milk/CArTon.dart',
-            'travel:adventure/CAve_Tours_europe.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'cate';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATErpillar.dart',
-            // Exact full path matches:
-            'kitchen:food/CATEring/party.dart',
-            // Fuzzy matches:
-            'travel:adventure/CAve_Tours_Europe.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'cater';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATERpillar.dart',
-            // Exact full path matches:
-            'kitchen:food/CATERing/party.dart',
-            // Fuzzy matches:
-            'travel:adventure/CAve_Tours_EuRope.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'caterp';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATERPillar.dart',
-            // Fuzzy matches:
-            'travel:adventure/CAve_Tours_EuRoPe.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'caterpi';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'zoo:animals/insects/CATERPIllar.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'caterpie';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-          preserveCases: true,
-        ),
-        equals([
-          'No files found.',
-        ]),
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'Multi token search returns expected files',
-    const Size(1000.0, 4000.0),
-    (WidgetTester tester) async {
-      await tester.pumpWidget(buildFileSearch());
-      final FileSearchFieldState state =
-          tester.state(find.byType(FileSearchField));
-      final autoCompleteController = state.autoCompleteController;
-
-      autoCompleteController.search = '';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Show all results (truncated to 10):
-            'zoo:animals/cats/meow.dart',
-            'zoo:animals/cats/purr.dart',
-            'zoo:animals/dogs/bark.dart',
-            'zoo:animals/dogs/growl.dart',
-            'zoo:animals/insects/caterpillar.dart',
-            'zoo:animals/insects/cicada.dart',
-            'kitchen:food/catering/party.dart',
-            'kitchen:food/carton/milk.dart',
-            'kitchen:food/milk/carton.dart',
-            'travel:adventure/cave_tours_europe.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'f';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact file name matches:
-            'travel:canada/banFf.dart',
-            // Exact full path matches:
-            'kitchen:Food/catering/party.dart',
-            'kitchen:Food/carton/milk.dart',
-            'kitchen:Food/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'fo';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOod/catering/party.dart',
-            'kitchen:FOod/carton/milk.dart',
-            'kitchen:FOod/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'foo';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOd/catering/party.dart',
-            'kitchen:FOOd/carton/milk.dart',
-            'kitchen:FOOd/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/catering/party.dart',
-            'kitchen:FOOD/carton/milk.dart',
-            'kitchen:FOOD/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food ';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/catering/party.dart',
-            'kitchen:FOOD/carton/milk.dart',
-            'kitchen:FOOD/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food c';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitChen:FOOD/catering/party.dart',
-            'kitChen:FOOD/carton/milk.dart',
-            'kitChen:FOOD/milk/carton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food ca';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/CAtering/party.dart',
-            'kitchen:FOOD/CArton/milk.dart',
-            'kitchen:FOOD/milk/CArton.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food car';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/CARton/milk.dart',
-            'kitchen:FOOD/milk/CARton.dart',
-            // Fuzzy matches:
-            'kitchen:FOOD/CAteRing/party.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food cart';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/CARTon/milk.dart',
-            'kitchen:FOOD/milk/CARTon.dart',
-            // Fuzzy matches:
-            'kitchen:FOOD/CAteRing/parTy.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food carto';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:FOOD/CARTOn/milk.dart',
-            'kitchen:FOOD/milk/CARTOn.dart',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'carton.dar';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:food/milk/CARTON.DARt',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'carton.dart';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-        ),
-        equals(
-          [
-            // Exact full path matches:
-            'kitchen:food/milk/CARTON.DART',
-          ],
-        ),
-      );
-
-      autoCompleteController.search = 'food cartwheel';
-      expect(
-        getAutoCompleteMatch(
-          autoCompleteController.searchAutoComplete.value,
-          preserveCases: true,
-        ),
-        equals(
-          [
-            'No files found.',
-          ],
-        ),
-      );
-    },
-  );
-}
-
-List<String> getAutoCompleteMatch(
-  List<AutoCompleteMatch> matches, {
-  bool preserveCases = false,
-}) {
-  return matches
-      .map(
-        (match) => match.transformAutoCompleteMatch<String>(
-          transformMatchedSegment: (segment) =>
-              preserveCases ? segment : segment.toUpperCase(),
-          transformUnmatchedSegment: (segment) =>
-              preserveCases ? segment : segment.toLowerCase(),
-          combineSegments: (segments) => segments.join(),
-        ),
-      )
-      .toList();
-}
diff --git a/packages/devtools_app/test/debugger/variables_test.dart b/packages/devtools_app/test/debugger/variables_test.dart
deleted file mode 100644
index 716c709..0000000
--- a/packages/devtools_app/test/debugger/variables_test.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/console/widgets/expandable_variable.dart';
-import 'package:devtools_app/src/shared/diagnostics/dart_object_node.dart';
-import 'package:devtools_app/src/shared/tree.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  setUp(() {
-    final service = createMockVmServiceWrapperWithDefaults();
-    final manager = FakeServiceConnectionManager(service: service);
-    setGlobal(ServiceConnectionManager, manager);
-  });
-
-  group('debugger variables', () {
-    late DartObjectNode objectNode;
-
-    setUp(() {
-      setGlobal(IdeTheme, IdeTheme());
-      objectNode = DartObjectNode.text('test node');
-    });
-
-    Future<void> pumpExpandableVariable(
-      WidgetTester tester,
-      DartObjectNode? variable,
-    ) async {
-      await tester.pumpWidget(
-        wrap(
-          ExpandableVariable(
-            variable: variable,
-          ),
-        ),
-      );
-      await tester.pumpAndSettle();
-      expect(find.byType(ExpandableVariable), findsOneWidget);
-    }
-
-    testWidgets(
-      'ExpandableVariable builds without error',
-      (WidgetTester tester) async {
-        await pumpExpandableVariable(tester, objectNode);
-        expect(find.byType(TreeView<DartObjectNode>), findsOneWidget);
-        expect(
-          find.byKey(ExpandableVariable.emptyExpandableVariableKey),
-          findsNothing,
-        );
-      },
-    );
-
-    testWidgets(
-      'ExpandableVariable builds for null variable',
-      (WidgetTester tester) async {
-        await pumpExpandableVariable(tester, null);
-        expect(find.byType(TreeView<DartObjectNode>), findsNothing);
-        expect(
-          find.byKey(ExpandableVariable.emptyExpandableVariableKey),
-          findsOneWidget,
-        );
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/deep_link_vlidation/deep_links_screen_test.dart b/packages/devtools_app/test/deep_link_vlidation/deep_links_screen_test.dart
deleted file mode 100644
index c99dd1b..0000000
--- a/packages/devtools_app/test/deep_link_vlidation/deep_links_screen_test.dart
+++ /dev/null
@@ -1,693 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/deep_link_validation/deep_link_list_view.dart';
-import 'package:devtools_app/src/screens/deep_link_validation/deep_links_model.dart';
-import 'package:devtools_app/src/screens/deep_link_validation/project_root_selection/root_selector.dart';
-import 'package:devtools_app/src/screens/deep_link_validation/project_root_selection/select_project_view.dart';
-import 'package:devtools_app/src/screens/deep_link_validation/validation_details_view.dart';
-import 'package:devtools_app/src/shared/feature_flags.dart';
-import 'package:devtools_app_shared/service.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_deeplink.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:dtd/dtd.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-import '../test_infra/test_data/deep_link/fake_responses.dart';
-import '../test_infra/utils/deep_links_utils.dart';
-
-final xcodeBuildOptions = XcodeBuildOptions.fromJson(
-  '''{"configurations":["debug", "release"],"targets":["runner","runnerTests"]}''',
-);
-
-void main() {
-  // ignore: avoid-redundant-async, false positive.
-  setUp(() async {
-    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(NotificationService, NotificationService());
-
-    final mockDtdManager = MockDTDManager();
-    final rootUri1 = Uri.parse('file:///Users/me/package_root_1');
-    final rootUri2 = Uri.parse('file:///Users/me/package_root_2');
-    when(mockDtdManager.projectRoots()).thenAnswer((_) async {
-      return UriList(uris: [rootUri1, rootUri2]);
-    });
-    setGlobal(DTDManager, mockDtdManager);
-    FeatureFlags.deepLinkIosCheck = true;
-  });
-
-  late DeepLinksScreen screen;
-  late TestDeepLinksController deepLinksController;
-
-  const windowSize = Size(2560.0, 1338.0);
-
-  Future<void> pumpDeepLinkScreen(
-    WidgetTester tester, {
-    required DeepLinksController controller,
-  }) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const DeepLinkPage(),
-        deepLink: controller,
-      ),
-    );
-    await tester.pump(const Duration(seconds: 1));
-    expect(find.byType(DeepLinkPage), findsOneWidget);
-  }
-
-  group('DeepLinkScreen', () {
-    setUp(() {
-      screen = DeepLinksScreen();
-      deepLinksController = TestDeepLinksController();
-    });
-
-    testWidgets('builds its tab', (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          Builder(builder: screen.buildTab),
-          deepLink: deepLinksController,
-        ),
-      );
-      expect(find.text('Deep Links'), findsOneWidget);
-    });
-
-    testWidgetsWithWindowSize(
-      'builds initial content',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.byType(SelectProjectView), findsOneWidget);
-        expect(find.byType(ProjectRootsDropdown), findsOneWidget);
-        expect(find.byType(ProjectRootTextField), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deeplink list page with no links',
-      windowSize,
-      (WidgetTester tester) async {
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = []
-          ..fakeIosDomains = [];
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(deepLinksController.pagePhase.value, PagePhase.noLinks);
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deeplink list page with links',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [defaultDomain];
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(deepLinksController.pagePhase.value, PagePhase.linksValidated);
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-        expect(find.byType(ValidationDetailView), findsNothing);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deeplink list page with default ios and android configurations',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'profile', 'release'],
-            iosBuildOptions: XcodeBuildOptions.fromJson(
-              '''{"configurations":["debug", "release"],"targets":["runner","runnerTests"]}''',
-            ),
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [defaultDomain];
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(deepLinksController.pagePhase.value, PagePhase.linksValidated);
-        expect(deepLinksController.selectedAndroidVariantIndex.value, 2);
-        expect(deepLinksController.selectedIosConfigurationIndex.value, 1);
-        expect(deepLinksController.selectedIosTargetIndex.value, 0);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds deeplink list page with split screen',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        deepLinksController.autoSelectLink(TableViewType.domainView);
-        deepLinksController.displayOptionsNotifier.value =
-            DisplayOptions(showSplitScreen: true);
-
-        await tester.pumpAndSettle();
-
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-        expect(find.byType(ValidationDetailView), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'shows notification cards when there are domain errors',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          hasAndroidDomainErrors: true,
-          iosValidationResponse: iosValidationResponseWithError,
-        );
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-        expect(find.byType(NotificationCard), findsOneWidget);
-      },
-    );
-    testWidgetsWithWindowSize(
-      'shows notification cards when there are path errors',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson(defaultDomain, hasPathError: true),
-          ]
-          ..fakeIosDomains = [defaultDomain];
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-        expect(find.byType(NotificationCard), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'taps the action button in notification cards to go to the split screen',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          hasAndroidDomainErrors: true,
-          iosValidationResponse: iosValidationResponseWithError,
-        );
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        await tester.tap(find.text('Fix domain'));
-        await tester.pumpAndSettle();
-
-        expect(find.byType(DeepLinkPage), findsOneWidget);
-        expect(find.byType(DeepLinkListView), findsOneWidget);
-        expect(find.byType(NotificationCard), findsNothing);
-        expect(find.byType(ValidationDetailView), findsOneWidget);
-        expect(find.text('Digital assets link file'), findsOneWidget);
-        expect(find.text('Apple-App-Site-Association file'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'Don\'t show domain errors when they are just warnings',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          iosValidationResponse: iosValidationResponseWithWarning,
-        );
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com'),
-            androidDeepLinkJson('www.google.com'),
-          ]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('example.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        await tester.tap(find.text('example.com'));
-        await tester.pumpAndSettle(const Duration(milliseconds: 500));
-
-        final domainErrors =
-            deepLinksController.selectedLink.value!.domainErrors;
-        expect(domainErrors.length, 0);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'search links',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
-          ..fakeIosDomains = [
-            'www.domain1.com',
-            'www.domain2.com',
-            'www.google.com',
-          ];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('www.domain2.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        deepLinksController.searchContent = 'goo';
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('www.domain1.com'), findsNothing);
-        expect(find.text('www.domain2.com'), findsNothing);
-        expect(find.text('www.google.com'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'filter links with os',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com'),
-            androidDeepLinkJson('www.google.com'),
-          ]
-          ..fakeIosDomains = [
-            'www.domain2.com',
-            'www.google.com',
-          ];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('www.domain2.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        // Only show Android links.
-        deepLinksController.updateDisplayOptions(
-          removedFilter: FilterOption.ios,
-        );
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('www.domain2.com'), findsNothing);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        // Only show iOS links.
-        deepLinksController.updateDisplayOptions(
-          addedFilter: FilterOption.ios,
-          removedFilter: FilterOption.android,
-        );
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('www.domain1.com'), findsNothing);
-        expect(find.text('www.domain2.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'filter links with validation result',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          iosValidationResponse: iosValidationResponseWithError,
-        );
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com'),
-            androidDeepLinkJson('www.google.com'),
-          ]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('example.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        // Only show links with domain error.
-        deepLinksController.updateDisplayOptions(
-          removedFilter: FilterOption.noIssue,
-        );
-
-        await tester.pumpAndSettle();
-        expect(find.text('www.domain1.com'), findsNothing);
-        expect(find.text('example.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsNothing);
-
-        // Only show links with no issue.
-        deepLinksController.updateDisplayOptions(
-          removedFilter: FilterOption.failedDomainCheck,
-        );
-        deepLinksController.updateDisplayOptions(
-          addedFilter: FilterOption.noIssue,
-        );
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('example.com'), findsNothing);
-        expect(find.text('www.google.com'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'domain errors are correct',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          iosValidationResponse: iosValidationResponseWithError,
-        );
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com'),
-            androidDeepLinkJson('www.google.com'),
-          ]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('example.com'), findsOneWidget);
-        expect(find.text('www.google.com'), findsOneWidget);
-
-        await tester.tap(find.text('example.com'));
-        await tester.pumpAndSettle(const Duration(milliseconds: 500));
-
-        final domainErrors =
-            deepLinksController.selectedLink.value!.domainErrors;
-        expect(domainErrors.length, 3);
-        expect(domainErrors[0], IosDomainError.existence);
-        expect(domainErrors[1].title, IosDomainError.appIdentifier.title);
-        expect(
-          (domainErrors[2] as IosDomainError).subcheckErrors.single,
-          AASAfileFormatSubCheck.componentPercentEncodedFormat,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'sort links',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController(
-          iosValidationResponse: iosValidationResponseWithError,
-        );
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeIosDomains = [defaultDomain, 'domain1.com', 'domain2.com'];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('domain1.com'), findsOneWidget);
-        expect(find.text('domain2.com'), findsOneWidget);
-        expect(find.text('example.com'), findsOneWidget);
-
-        // Sort with a-z.
-        deepLinksController.updateDisplayOptions(
-          domainSortingOption: SortingOption.aToZ,
-        );
-        await tester.pumpAndSettle();
-
-        var widgetACenter = tester.getCenter(find.text('domain1.com'));
-        var widgetBCenter = tester.getCenter(find.text('domain2.com'));
-        var widgetCCenter = tester.getCenter(find.text('example.com'));
-
-        expect(widgetACenter.dy < widgetBCenter.dy, true);
-        expect(widgetBCenter.dy < widgetCCenter.dy, true);
-
-        // Sort with z-a.
-        deepLinksController.updateDisplayOptions(
-          domainSortingOption: SortingOption.zToA,
-        );
-        await tester.pumpAndSettle();
-
-        widgetACenter = tester.getCenter(find.text('domain1.com'));
-        widgetBCenter = tester.getCenter(find.text('domain2.com'));
-        widgetCCenter = tester.getCenter(find.text('example.com'));
-
-        expect(widgetCCenter.dy < widgetBCenter.dy, true);
-        expect(widgetBCenter.dy < widgetACenter.dy, true);
-
-        // Sort with error on top. `example.com` is the one with error.
-        deepLinksController.updateDisplayOptions(
-          domainSortingOption: SortingOption.errorOnTop,
-        );
-        await tester.pumpAndSettle();
-
-        widgetACenter = tester.getCenter(find.text('domain1.com'));
-        widgetBCenter = tester.getCenter(find.text('domain2.com'));
-        widgetCCenter = tester.getCenter(find.text('example.com'));
-
-        expect(widgetCCenter.dy < widgetACenter.dy, true);
-        expect(widgetCCenter.dy < widgetBCenter.dy, true);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'show scheme or missing scheme',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com'),
-            androidDeepLinkJson('www.domain2.com', scheme: null),
-          ];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        expect(find.text('www.domain1.com'), findsOneWidget);
-        expect(find.text('www.domain2.com'), findsOneWidget);
-
-        expect(find.text('missing scheme'), findsOneWidget);
-        expect(find.text('http'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'path view',
-      windowSize,
-      (WidgetTester tester) async {
-        final deepLinksController = TestDeepLinksController();
-
-        deepLinksController
-          ..selectedProject.value = FlutterProject(
-            path: '/abc',
-            androidVariants: ['debug', 'release'],
-            iosBuildOptions: xcodeBuildOptions,
-          )
-          ..fakeAndroidDeepLinks = [
-            androidDeepLinkJson('www.domain1.com', path: '/path1'),
-            androidDeepLinkJson(
-              'www.domain2.com',
-              path: '/path2',
-              hasPathError: true,
-            ),
-            androidDeepLinkJson('www.domain3.com', path: '/path3'),
-          ]
-          ..fakeIosDomains = [defaultDomain];
-
-        await pumpDeepLinkScreen(
-          tester,
-          controller: deepLinksController,
-        );
-
-        await tester.tap(find.text('Path view'));
-        await tester.pumpAndSettle(const Duration(milliseconds: 500));
-
-        expect(find.text('/path1'), findsOneWidget);
-        expect(find.text('/path2'), findsOneWidget);
-        expect(find.text('/path3'), findsOneWidget);
-        expect(find.text('/ios-path1'), findsOneWidget);
-        expect(find.text('NOT /ios-path2'), findsOneWidget);
-
-        // Only show links with path error.
-        deepLinksController.updateDisplayOptions(
-          removedFilter: FilterOption.noIssue,
-        );
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('/path1'), findsNothing);
-        expect(find.text('/path2'), findsOneWidget);
-        expect(find.text('/path3'), findsNothing);
-        expect(find.text('/ios-path1'), findsNothing);
-        expect(find.text('NOT /ios-path2'), findsNothing);
-
-        // Only show links with no issue.
-        deepLinksController.updateDisplayOptions(
-          removedFilter: FilterOption.failedPathCheck,
-        );
-        deepLinksController.updateDisplayOptions(
-          addedFilter: FilterOption.noIssue,
-        );
-
-        await tester.pumpAndSettle();
-
-        expect(find.text('/path1'), findsOneWidget);
-        expect(find.text('/path2'), findsNothing);
-        expect(find.text('/path3'), findsOneWidget);
-        expect(find.text('/ios-path1'), findsOneWidget);
-        expect(find.text('NOT /ios-path2'), findsOneWidget);
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/extensions/extension_analytics_test.dart b/packages/devtools_app/test/extensions/extension_analytics_test.dart
index 7dcecb7..5c0fe4c 100644
--- a/packages/devtools_app/test/extensions/extension_analytics_test.dart
+++ b/packages/devtools_app/test/extensions/extension_analytics_test.dart
@@ -8,82 +8,76 @@
 
 void main() {
   group('DevTools extension analytics', () {
-    test(
-      'uses extension name for public package',
-      () {
-        final public = StubDevToolsExtensions.providerExtension;
-        expect(public.isPubliclyHosted, true);
-        expect(public.name, 'provider');
-        expect(public.analyticsSafeName, 'provider');
-        expect(
-          DevToolsExtensionEvents.extensionScreenName(public),
-          'extension-provider',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionFeedback(public),
-          'extensionFeedback-provider',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionEnableManual(public),
-          'extensionEnable-manual-provider',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionEnablePrompt(public),
-          'extensionEnable-prompt-provider',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionDisableManual(public),
-          'extensionDisable-manual-provider',
-        );
+    test('uses extension name for public package', () {
+      final public = StubDevToolsExtensions.providerExtension;
+      expect(public.isPubliclyHosted, true);
+      expect(public.name, 'provider');
+      expect(public.analyticsSafeName, 'provider');
+      expect(
+        DevToolsExtensionEvents.extensionScreenName(public),
+        'extension-provider',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionFeedback(public),
+        'extensionFeedback-provider',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionEnableManual(public),
+        'extensionEnable-manual-provider',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionEnablePrompt(public),
+        'extensionEnable-prompt-provider',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionDisableManual(public),
+        'extensionDisable-manual-provider',
+      );
 
-        expect(
-          DevToolsExtensionEvents.extensionDisablePrompt(public),
-          'extensionDisable-prompt-provider',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionForceReload(public),
-          'extensionForceReload-provider',
-        );
-      },
-    );
+      expect(
+        DevToolsExtensionEvents.extensionDisablePrompt(public),
+        'extensionDisable-prompt-provider',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionForceReload(public),
+        'extensionForceReload-provider',
+      );
+    });
 
-    test(
-      'does not use extension name for private package',
-      () {
-        final private = StubDevToolsExtensions.fooExtension;
-        expect(private.isPubliclyHosted, false);
-        expect(private.name, 'foo');
-        expect(private.analyticsSafeName, 'private');
-        expect(
-          DevToolsExtensionEvents.extensionScreenName(private),
-          'extension-private',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionFeedback(private),
-          'extensionFeedback-private',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionEnableManual(private),
-          'extensionEnable-manual-private',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionEnablePrompt(private),
-          'extensionEnable-prompt-private',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionDisableManual(private),
-          'extensionDisable-manual-private',
-        );
+    test('does not use extension name for private package', () {
+      final private = StubDevToolsExtensions.fooExtension;
+      expect(private.isPubliclyHosted, false);
+      expect(private.name, 'foo');
+      expect(private.analyticsSafeName, 'private');
+      expect(
+        DevToolsExtensionEvents.extensionScreenName(private),
+        'extension-private',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionFeedback(private),
+        'extensionFeedback-private',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionEnableManual(private),
+        'extensionEnable-manual-private',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionEnablePrompt(private),
+        'extensionEnable-prompt-private',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionDisableManual(private),
+        'extensionDisable-manual-private',
+      );
 
-        expect(
-          DevToolsExtensionEvents.extensionDisablePrompt(private),
-          'extensionDisable-prompt-private',
-        );
-        expect(
-          DevToolsExtensionEvents.extensionForceReload(private),
-          'extensionForceReload-private',
-        );
-      },
-    );
+      expect(
+        DevToolsExtensionEvents.extensionDisablePrompt(private),
+        'extensionDisable-prompt-private',
+      );
+      expect(
+        DevToolsExtensionEvents.extensionForceReload(private),
+        'extensionForceReload-private',
+      );
+    });
   });
 }
diff --git a/packages/devtools_app/test/extensions/extension_screen_test.dart b/packages/devtools_app/test/extensions/extension_screen_test.dart
index 8ae118c..3b081c4 100644
--- a/packages/devtools_app/test/extensions/extension_screen_test.dart
+++ b/packages/devtools_app/test/extensions/extension_screen_test.dart
@@ -30,8 +30,9 @@
       setGlobal(OfflineDataController, OfflineDataController());
       fooScreen = ExtensionScreen(StubDevToolsExtensions.fooExtension);
       barScreen = ExtensionScreen(StubDevToolsExtensions.barExtension);
-      providerScreen =
-          ExtensionScreen(StubDevToolsExtensions.providerExtension);
+      providerScreen = ExtensionScreen(
+        StubDevToolsExtensions.providerExtension,
+      );
 
       setGlobal(
         ExtensionService,
@@ -72,99 +73,81 @@
       );
     });
 
-    testWidgetsWithWindowSize(
-      'renders for unactivated state',
-      windowSize,
-      (tester) async {
-        await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
-        expect(find.byType(ExtensionView), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
-        expect(
-          find.richTextContaining('package:foo extension'),
-          findsOneWidget,
-        );
-        expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
-        expect(find.richTextContaining('Report an issue'), findsOneWidget);
-        expect(_extensionContextMenuFinder, findsNothing);
-        expect(find.byType(EnableExtensionPrompt), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionView), findsNothing);
+    testWidgetsWithWindowSize('renders for unactivated state', windowSize, (
+      tester,
+    ) async {
+      await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
+      expect(find.byType(ExtensionView), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
+      expect(find.richTextContaining('package:foo extension'), findsOneWidget);
+      expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
+      expect(find.richTextContaining('Report an issue'), findsOneWidget);
+      expect(_extensionContextMenuFinder, findsNothing);
+      expect(find.byType(EnableExtensionPrompt), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionView), findsNothing);
 
-        await tester.pumpWidget(wrap(Builder(builder: barScreen.build)));
-        expect(find.byType(ExtensionView), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
-        expect(
-          find.richTextContaining('package:bar extension'),
-          findsOneWidget,
-        );
-        expect(find.richTextContaining('(v2.0.0)'), findsOneWidget);
-        expect(find.richTextContaining('Report an issue'), findsOneWidget);
-        expect(_extensionContextMenuFinder, findsNothing);
-        expect(find.byType(EnableExtensionPrompt), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionView), findsNothing);
+      await tester.pumpWidget(wrap(Builder(builder: barScreen.build)));
+      expect(find.byType(ExtensionView), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
+      expect(find.richTextContaining('package:bar extension'), findsOneWidget);
+      expect(find.richTextContaining('(v2.0.0)'), findsOneWidget);
+      expect(find.richTextContaining('Report an issue'), findsOneWidget);
+      expect(_extensionContextMenuFinder, findsNothing);
+      expect(find.byType(EnableExtensionPrompt), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionView), findsNothing);
 
-        await tester.pumpWidget(wrap(Builder(builder: providerScreen.build)));
-        expect(find.byType(ExtensionView), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
-        expect(
-          find.richTextContaining('package:provider extension'),
-          findsOneWidget,
-        );
-        expect(find.richTextContaining('(v3.0.0)'), findsOneWidget);
-        expect(find.richTextContaining('Report an issue'), findsOneWidget);
-        expect(_extensionContextMenuFinder, findsNothing);
-        expect(find.byType(EnableExtensionPrompt), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionView), findsNothing);
-      },
-    );
+      await tester.pumpWidget(wrap(Builder(builder: providerScreen.build)));
+      expect(find.byType(ExtensionView), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
+      expect(
+        find.richTextContaining('package:provider extension'),
+        findsOneWidget,
+      );
+      expect(find.richTextContaining('(v3.0.0)'), findsOneWidget);
+      expect(find.richTextContaining('Report an issue'), findsOneWidget);
+      expect(_extensionContextMenuFinder, findsNothing);
+      expect(find.byType(EnableExtensionPrompt), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionView), findsNothing);
+    });
 
-    testWidgetsWithWindowSize(
-      'renders for enabled state',
-      windowSize,
-      (tester) async {
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.fooExtension,
-          enable: true,
-        );
+    testWidgetsWithWindowSize('renders for enabled state', windowSize, (
+      tester,
+    ) async {
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.fooExtension,
+        enable: true,
+      );
 
-        await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
-        expect(find.byType(ExtensionView), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
-        expect(
-          find.richTextContaining('package:foo extension'),
-          findsOneWidget,
-        );
-        expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
-        expect(find.richTextContaining('Report an issue'), findsOneWidget);
-        await _verifyContextMenuContents(tester);
-        expect(find.byType(EnableExtensionPrompt), findsNothing);
-        expect(find.byType(EmbeddedExtensionView), findsOneWidget);
-        expect(find.byType(ExtensionRequiresConnection), findsNothing);
-      },
-    );
+      await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
+      expect(find.byType(ExtensionView), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
+      expect(find.richTextContaining('package:foo extension'), findsOneWidget);
+      expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
+      expect(find.richTextContaining('Report an issue'), findsOneWidget);
+      await _verifyContextMenuContents(tester);
+      expect(find.byType(EnableExtensionPrompt), findsNothing);
+      expect(find.byType(EmbeddedExtensionView), findsOneWidget);
+      expect(find.byType(ExtensionRequiresConnection), findsNothing);
+    });
 
-    testWidgetsWithWindowSize(
-      'renders for disabled state',
-      windowSize,
-      (tester) async {
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.fooExtension,
-          enable: false,
-        );
+    testWidgetsWithWindowSize('renders for disabled state', windowSize, (
+      tester,
+    ) async {
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.fooExtension,
+        enable: false,
+      );
 
-        await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
-        expect(find.byType(ExtensionView), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
-        expect(
-          find.richTextContaining('package:foo extension'),
-          findsOneWidget,
-        );
-        expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
-        expect(find.richTextContaining('Report an issue'), findsOneWidget);
-        expect(_extensionContextMenuFinder, findsNothing);
-        expect(find.byType(EnableExtensionPrompt), findsOneWidget);
-        expect(find.byType(EmbeddedExtensionView), findsNothing);
-      },
-    );
+      await tester.pumpWidget(wrap(Builder(builder: fooScreen.build)));
+      expect(find.byType(ExtensionView), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionHeader), findsOneWidget);
+      expect(find.richTextContaining('package:foo extension'), findsOneWidget);
+      expect(find.richTextContaining('(v1.0.0)'), findsOneWidget);
+      expect(find.richTextContaining('Report an issue'), findsOneWidget);
+      expect(_extensionContextMenuFinder, findsNothing);
+      expect(find.byType(EnableExtensionPrompt), findsOneWidget);
+      expect(find.byType(EmbeddedExtensionView), findsNothing);
+    });
 
     testWidgetsWithWindowSize(
       'renders when embedded without a connected app',
@@ -232,10 +215,7 @@
         expect(find.byType(EnableExtensionPrompt), findsNothing);
         expect(find.byType(EmbeddedExtensionView), findsOneWidget);
         expect(find.byType(ExtensionRequiresConnection), findsNothing);
-        await _verifyContextMenuContents(
-          tester,
-          autoDismiss: false,
-        );
+        await _verifyContextMenuContents(tester, autoDismiss: false);
 
         await tester.tap(find.text('Disable extension'));
         await tester.pumpAndSettle();
@@ -276,6 +256,6 @@
 }
 
 Finder get _extensionContextMenuFinder => find.descendant(
-      of: find.byType(EmbeddedExtensionHeader),
-      matching: find.byType(ContextMenuButton),
-    );
+  of: find.byType(EmbeddedExtensionHeader),
+  matching: find.byType(ContextMenuButton),
+);
diff --git a/packages/devtools_app/test/extensions/extension_service_test.dart b/packages/devtools_app/test/extensions/extension_service_test.dart
index 4a61d07..bcf8cb6 100644
--- a/packages/devtools_app/test/extensions/extension_service_test.dart
+++ b/packages/devtools_app/test/extensions/extension_service_test.dart
@@ -24,8 +24,9 @@
       final mockServiceConnection = createMockServiceConnectionWithDefaults();
       mockServiceManager =
           mockServiceConnection.serviceManager as MockServiceManager;
-      when(mockServiceManager.connectedState)
-          .thenReturn(ValueNotifier(const ConnectedState(true)));
+      when(
+        mockServiceManager.connectedState,
+      ).thenReturn(ValueNotifier(const ConnectedState(true)));
       setGlobal(ServiceConnectionManager, mockServiceConnection);
     });
 
@@ -46,10 +47,12 @@
       expect(service.runtimeExtensions.length, 3);
       expect(service.availableExtensions.length, 5);
 
-      final ignoredStaticExtensions =
-          service.staticExtensions.where(service.isExtensionIgnored);
-      final ignoredRuntimeExtensions =
-          service.runtimeExtensions.where(service.isExtensionIgnored);
+      final ignoredStaticExtensions = service.staticExtensions.where(
+        service.isExtensionIgnored,
+      );
+      final ignoredRuntimeExtensions = service.runtimeExtensions.where(
+        service.isExtensionIgnored,
+      );
       expect(ignoredStaticExtensions.length, 2);
       expect(ignoredStaticExtensions.map((e) => e.identifier).toList(), [
         'bar_2.0.0',
@@ -59,8 +62,9 @@
     });
 
     test('initialize with ignoreServiceConnection', () async {
-      when(mockServiceManager.connectedState)
-          .thenReturn(ValueNotifier(const ConnectedState(false)));
+      when(
+        mockServiceManager.connectedState,
+      ).thenReturn(ValueNotifier(const ConnectedState(false)));
 
       final service = ExtensionService(ignoreServiceConnection: true);
       expect(service.staticExtensions, isEmpty);
@@ -72,8 +76,9 @@
       expect(service.runtimeExtensions, isEmpty);
       expect(service.availableExtensions.length, 3);
 
-      final ignoredStaticExtensions =
-          service.staticExtensions.where(service.isExtensionIgnored);
+      final ignoredStaticExtensions = service.staticExtensions.where(
+        service.isExtensionIgnored,
+      );
       expect(ignoredStaticExtensions.length, 1);
       expect(ignoredStaticExtensions.map((e) => e.identifier).toList(), [
         'bar_2.0.0', // Duplicate: older version of an existing extension.
@@ -98,10 +103,12 @@
         );
       }
 
-      var barEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.barExtension);
-      var newerBarEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.newerBarExtension);
+      var barEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.barExtension,
+      );
+      var newerBarEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.newerBarExtension,
+      );
       expect(barEnabledState, ExtensionEnabledState.none);
       expect(newerBarEnabledState, ExtensionEnabledState.none);
       expect(
@@ -117,10 +124,12 @@
       );
 
       // Verify enabled states for all matching extensions have been updated.
-      barEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.barExtension);
-      newerBarEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.newerBarExtension);
+      barEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.barExtension,
+      );
+      newerBarEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.newerBarExtension,
+      );
       expect(barEnabledState, ExtensionEnabledState.enabled);
       expect(newerBarEnabledState, ExtensionEnabledState.enabled);
       expect(
@@ -130,10 +139,12 @@
         ExtensionEnabledState.enabled,
       );
 
-      var fooEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.fooExtension);
-      var duplicateFooEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.duplicateFooExtension);
+      var fooEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.fooExtension,
+      );
+      var duplicateFooEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.duplicateFooExtension,
+      );
       expect(fooEnabledState, ExtensionEnabledState.none);
       expect(duplicateFooEnabledState, ExtensionEnabledState.none);
       expect(
@@ -149,10 +160,12 @@
       );
 
       // Verify enabled states for all matching extensions have been updated.
-      fooEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.fooExtension);
-      duplicateFooEnabledState =
-          await enabledOnDisk(StubDevToolsExtensions.duplicateFooExtension);
+      fooEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.fooExtension,
+      );
+      duplicateFooEnabledState = await enabledOnDisk(
+        StubDevToolsExtensions.duplicateFooExtension,
+      );
       expect(fooEnabledState, ExtensionEnabledState.disabled);
       expect(duplicateFooEnabledState, ExtensionEnabledState.disabled);
       expect(
diff --git a/packages/devtools_app/test/extensions/extension_settings_test.dart b/packages/devtools_app/test/extensions/extension_settings_test.dart
index bbc2ab7..b41db51 100644
--- a/packages/devtools_app/test/extensions/extension_settings_test.dart
+++ b/packages/devtools_app/test/extensions/extension_settings_test.dart
@@ -29,8 +29,9 @@
       final mockServiceConnection = createMockServiceConnectionWithDefaults();
       final mockServiceManager =
           mockServiceConnection.serviceManager as MockServiceManager;
-      when(mockServiceManager.connectedState)
-          .thenReturn(ValueNotifier(const ConnectedState(true)));
+      when(
+        mockServiceManager.connectedState,
+      ).thenReturn(ValueNotifier(const ConnectedState(true)));
       setGlobal(ServiceConnectionManager, mockServiceConnection);
 
       setGlobal(
@@ -53,45 +54,43 @@
       resetDevToolsExtensionEnabledStates();
     });
 
-    testWidgets(
-      'builds dialog with no available extensions',
-      (WidgetTester tester) async {
-        await tester.pumpWidget(
-          wrap(const ExtensionSettingsDialog(extensions: [])),
-        );
-        await tester.pumpAndSettle();
-        expect(find.text('DevTools Extensions'), findsOneWidget);
-        expect(
-          find.textContaining('Extensions are provided by the pub packages'),
-          findsOneWidget,
-        );
-        expect(find.text('No extensions available.'), findsOneWidget);
-        expect(find.byType(ListView), findsNothing);
-        expect(find.byType(ExtensionSetting), findsNothing);
-      },
-    );
+    testWidgets('builds dialog with no available extensions', (
+      WidgetTester tester,
+    ) async {
+      await tester.pumpWidget(
+        wrap(const ExtensionSettingsDialog(extensions: [])),
+      );
+      await tester.pumpAndSettle();
+      expect(find.text('DevTools Extensions'), findsOneWidget);
+      expect(
+        find.textContaining('Extensions are provided by the pub packages'),
+        findsOneWidget,
+      );
+      expect(find.text('No extensions available.'), findsOneWidget);
+      expect(find.byType(ListView), findsNothing);
+      expect(find.byType(ExtensionSetting), findsNothing);
+    });
 
-    testWidgets(
-      'builds dialog with available extensions',
-      (WidgetTester tester) async {
-        await tester.pumpWidget(wrap(dialog));
-        await tester.pumpAndSettle();
-        expect(find.text('DevTools Extensions'), findsOneWidget);
-        expect(
-          find.textContaining('Extensions are provided by the pub packages'),
-          findsOneWidget,
-        );
-        expect(find.text('No extensions available.'), findsNothing);
-        expect(find.byType(ListView), findsOneWidget);
-        expect(find.byType(ExtensionSetting), findsNWidgets(5));
-        await expectLater(
-          find.byWidget(dialog),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/extensions/settings_state_none.png',
-          ),
-        );
-      },
-    );
+    testWidgets('builds dialog with available extensions', (
+      WidgetTester tester,
+    ) async {
+      await tester.pumpWidget(wrap(dialog));
+      await tester.pumpAndSettle();
+      expect(find.text('DevTools Extensions'), findsOneWidget);
+      expect(
+        find.textContaining('Extensions are provided by the pub packages'),
+        findsOneWidget,
+      );
+      expect(find.text('No extensions available.'), findsNothing);
+      expect(find.byType(ListView), findsOneWidget);
+      expect(find.byType(ExtensionSetting), findsNWidgets(5));
+      await expectLater(
+        find.byWidget(dialog),
+        matchesDevToolsGolden(
+          '../test_infra/goldens/extensions/settings_state_none.png',
+        ),
+      );
+    });
 
     testWidgets(
       'pressing toggle buttons makes calls to the $ExtensionService',
@@ -134,38 +133,46 @@
           ExtensionEnabledState.none,
         );
 
-        final barSetting = tester
-            .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
-            .where(
-              (setting) => setting.extension.name.caseInsensitiveEquals('bar'),
-            )
-            .first;
-        final bazSetting = tester
-            .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
-            .where(
-              (setting) => setting.extension.name.caseInsensitiveEquals('baz'),
-            )
-            .first;
-        final fooSetting = tester
-            .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
-            .where(
-              (setting) => setting.extension.name.caseInsensitiveEquals('foo'),
-            )
-            .first;
-        final providerSetting = tester
-            .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
-            .where(
-              (setting) =>
-                  setting.extension.name.caseInsensitiveEquals('provider'),
-            )
-            .first;
-        final someToolSetting = tester
-            .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
-            .where(
-              (setting) =>
-                  setting.extension.name.caseInsensitiveEquals('some_tool'),
-            )
-            .first;
+        final barSetting =
+            tester
+                .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
+                .where(
+                  (setting) =>
+                      setting.extension.name.caseInsensitiveEquals('bar'),
+                )
+                .first;
+        final bazSetting =
+            tester
+                .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
+                .where(
+                  (setting) =>
+                      setting.extension.name.caseInsensitiveEquals('baz'),
+                )
+                .first;
+        final fooSetting =
+            tester
+                .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
+                .where(
+                  (setting) =>
+                      setting.extension.name.caseInsensitiveEquals('foo'),
+                )
+                .first;
+        final providerSetting =
+            tester
+                .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
+                .where(
+                  (setting) =>
+                      setting.extension.name.caseInsensitiveEquals('provider'),
+                )
+                .first;
+        final someToolSetting =
+            tester
+                .widgetList<ExtensionSetting>(find.byType(ExtensionSetting))
+                .where(
+                  (setting) =>
+                      setting.extension.name.caseInsensitiveEquals('some_tool'),
+                )
+                .first;
 
         // Disable the 'bar' extension.
         await tester.tap(
@@ -257,48 +264,47 @@
       },
     );
 
-    testWidgets(
-      'toggle buttons update for changes to value notifiers',
-      (WidgetTester tester) async {
-        await tester.pumpWidget(wrap(dialog));
-        await tester.pumpAndSettle();
-        await expectLater(
-          find.byWidget(dialog),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/extensions/settings_state_none.png',
-          ),
-        );
+    testWidgets('toggle buttons update for changes to value notifiers', (
+      WidgetTester tester,
+    ) async {
+      await tester.pumpWidget(wrap(dialog));
+      await tester.pumpAndSettle();
+      await expectLater(
+        find.byWidget(dialog),
+        matchesDevToolsGolden(
+          '../test_infra/goldens/extensions/settings_state_none.png',
+        ),
+      );
 
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.barExtension,
-          enable: false,
-        );
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.bazExtension,
-          enable: false,
-        );
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.fooExtension,
-          enable: true,
-        );
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.providerExtension,
-          enable: true,
-        );
-        await extensionService.setExtensionEnabledState(
-          StubDevToolsExtensions.someToolExtension,
-          enable: true,
-        );
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.barExtension,
+        enable: false,
+      );
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.bazExtension,
+        enable: false,
+      );
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.fooExtension,
+        enable: true,
+      );
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.providerExtension,
+        enable: true,
+      );
+      await extensionService.setExtensionEnabledState(
+        StubDevToolsExtensions.someToolExtension,
+        enable: true,
+      );
 
-        await tester.pumpWidget(wrap(dialog));
-        await tester.pumpAndSettle();
-        await expectLater(
-          find.byWidget(dialog),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/extensions/settings_state_modified.png',
-          ),
-        );
-      },
-    );
+      await tester.pumpWidget(wrap(dialog));
+      await tester.pumpAndSettle();
+      await expectLater(
+        find.byWidget(dialog),
+        matchesDevToolsGolden(
+          '../test_infra/goldens/extensions/settings_state_modified.png',
+        ),
+      );
+    });
   });
 }
diff --git a/packages/devtools_app/test/framework/disconnect_observer_test.dart b/packages/devtools_app/test/framework/disconnect_observer_test.dart
new file mode 100644
index 0000000..f216075
--- /dev/null
+++ b/packages/devtools_app/test/framework/disconnect_observer_test.dart
@@ -0,0 +1,174 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/disconnect_observer.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app_shared/shared.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../test_infra/matchers/matchers.dart';
+
+void main() {
+  group('DisconnectObserver', () {
+    late FakeServiceConnectionManager fakeServiceConnectionManager;
+
+    setUp(() {
+      fakeServiceConnectionManager = FakeServiceConnectionManager();
+      setGlobal(ServiceConnectionManager, fakeServiceConnectionManager);
+      setGlobal(FrameworkController, FrameworkController());
+      setGlobal(OfflineDataController, OfflineDataController());
+      setGlobal(IdeTheme, IdeTheme());
+    });
+
+    Future<void> pumpDisconnectObserver(
+      WidgetTester tester, {
+      Widget child = const Placeholder(),
+    }) async {
+      await tester.pumpWidget(
+        wrap(
+          Builder(
+            builder: (context) {
+              return DisconnectObserver(
+                routerDelegate: DevToolsRouterDelegate.of(context),
+                child: child,
+              );
+            },
+          ),
+        ),
+      );
+      await tester.pumpAndSettle();
+    }
+
+    void verifyObserverState(
+      WidgetTester tester, {
+      required bool connected,
+      required bool showingOverlay,
+    }) {
+      final DisconnectObserverState state = tester.state(
+        find.byType(DisconnectObserver),
+      );
+      expect(state.currentConnectionState.connected, connected);
+      expect(
+        state.currentDisconnectedOverlay,
+        showingOverlay ? isNotNull : isNull,
+      );
+      expect(
+        find.text('Disconnected'),
+        showingOverlay ? findsOneWidget : findsNothing,
+      );
+      expect(
+        find.byType(ConnectToNewAppButton),
+        showingOverlay && !isEmbedded() ? findsOneWidget : findsNothing,
+      );
+      expect(
+        find.text('Run a new debug session to reconnect.'),
+        showingOverlay && isEmbedded() ? findsOneWidget : findsNothing,
+      );
+      expect(
+        find.text('Review recent data (offline)'),
+        showingOverlay && offlineDataController.offlineDataJson.isNotEmpty
+            ? findsOneWidget
+            : findsNothing,
+      );
+    }
+
+    testWidgets('initialized in a disconnected state', (
+      WidgetTester tester,
+    ) async {
+      fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+      await pumpDisconnectObserver(tester);
+      verifyObserverState(tester, connected: false, showingOverlay: false);
+    });
+
+    testWidgets('initialized in a connected state', (
+      WidgetTester tester,
+    ) async {
+      await pumpDisconnectObserver(tester);
+      verifyObserverState(tester, connected: true, showingOverlay: false);
+    });
+
+    testWidgets('handles connection changes', (WidgetTester tester) async {
+      fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+      await pumpDisconnectObserver(tester);
+      verifyObserverState(tester, connected: false, showingOverlay: false);
+
+      // Establish a connection.
+      fakeServiceConnectionManager.serviceManager.setConnectedState(true);
+      await tester.pumpAndSettle();
+      verifyObserverState(tester, connected: true, showingOverlay: false);
+
+      // Trigger a disconnect.
+      fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+      await tester.pumpAndSettle();
+      verifyObserverState(tester, connected: false, showingOverlay: true);
+
+      // Trigger a reconnect.
+      fakeServiceConnectionManager.serviceManager.setConnectedState(true);
+      await tester.pumpAndSettle();
+      verifyObserverState(tester, connected: true, showingOverlay: false);
+    });
+
+    group('disconnected overlay', () {
+      Future<void> showOverlayAndVerifyContents(WidgetTester tester) async {
+        await pumpDisconnectObserver(tester);
+        verifyObserverState(tester, connected: true, showingOverlay: false);
+        fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+        await tester.pumpAndSettle();
+        verifyObserverState(tester, connected: false, showingOverlay: true);
+      }
+
+      testWidgets('builds for embedded mode', (WidgetTester tester) async {
+        setGlobal(IdeTheme, IdeTheme(embedMode: EmbedMode.embedOne));
+        await showOverlayAndVerifyContents(tester);
+      });
+
+      testWidgets('builds for reviewing history', (WidgetTester tester) async {
+        offlineDataController.offlineDataJson = {'foo': 'bar'};
+        await showOverlayAndVerifyContents(tester);
+      });
+
+      // Regression test for https://github.com/flutter/devtools/issues/8050.
+      testWidgets('hides widgets at lower z-index', (
+        WidgetTester tester,
+      ) async {
+        await pumpDisconnectObserver(
+          tester,
+          child: Container(height: 100.0, width: 100.0, color: Colors.red),
+        );
+        verifyObserverState(tester, connected: true, showingOverlay: false);
+        // At this point the red container should be visible.
+        await expectLater(
+          find.byType(MaterialApp),
+          matchesDevToolsGolden(
+            '../test_infra/goldens/shared/disconnect_observer_connected.png',
+          ),
+        );
+
+        // Trigger a disconnect.
+        fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+        await tester.pumpAndSettle();
+
+        verifyObserverState(tester, connected: false, showingOverlay: true);
+        // Once the disconnect overlay is showing, the red container should
+        // be hidden.
+        await expectLater(
+          find.byType(MaterialApp),
+          matchesDevToolsGolden(
+            '../test_infra/goldens/shared/disconnect_observer_disconnected.png',
+          ),
+        );
+      });
+    });
+
+    // TODO(kenz): test navigation that occurs by clicking on buttons. This will
+    // require either modifying the test wrappers to take a set of routes or
+    // writing an integration test for this user journey.
+  });
+}
diff --git a/packages/devtools_app/test/shared/home_screen_test.dart b/packages/devtools_app/test/framework/home_screen_test.dart
similarity index 95%
rename from packages/devtools_app/test/shared/home_screen_test.dart
rename to packages/devtools_app/test/framework/home_screen_test.dart
index 0ca97f3..fc532da 100644
--- a/packages/devtools_app/test/shared/home_screen_test.dart
+++ b/packages/devtools_app/test/framework/home_screen_test.dart
@@ -73,10 +73,12 @@
       fakeServiceConnection = FakeServiceConnectionManager();
       when(fakeServiceConnection.serviceManager.vm.version).thenReturn('1.9.1');
       when(fakeServiceConnection.serviceManager.vm.targetCPU).thenReturn('x64');
-      when(fakeServiceConnection.serviceManager.vm.architectureBits)
-          .thenReturn(64);
-      when(fakeServiceConnection.serviceManager.vm.operatingSystem)
-          .thenReturn('android');
+      when(
+        fakeServiceConnection.serviceManager.vm.architectureBits,
+      ).thenReturn(64);
+      when(
+        fakeServiceConnection.serviceManager.vm.operatingSystem,
+      ).thenReturn('android');
       final app = fakeServiceConnection.serviceManager.connectedApp!;
       mockConnectedApp(
         app,
diff --git a/packages/devtools_app/test/framework/initializer_test.dart b/packages/devtools_app/test/framework/initializer_test.dart
new file mode 100644
index 0000000..9fb586b
--- /dev/null
+++ b/packages/devtools_app/test/framework/initializer_test.dart
@@ -0,0 +1,110 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/initializer.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+  group('Initializer', () {
+    const initializedKey = Key('initialized');
+    const waitingText = 'Waiting for VM service connection...';
+    const cannotConnectText = 'Cannot connect to VM service.';
+
+    late FakeServiceConnectionManager fakeServiceConnectionManager;
+
+    setUp(() {
+      fakeServiceConnectionManager = FakeServiceConnectionManager();
+      when(
+        fakeServiceConnectionManager.serviceManager.connectedApp!.isDartWebApp,
+      ).thenAnswer((_) => Future.value(false));
+      setGlobal(ServiceConnectionManager, fakeServiceConnectionManager);
+      setGlobal(FrameworkController, FrameworkController());
+      setGlobal(OfflineDataController, OfflineDataController());
+      setGlobal(IdeTheme, IdeTheme());
+    });
+
+    Future<void> pumpInitializer(WidgetTester tester) async {
+      await tester.pumpWidget(
+        wrap(Initializer(builder: (_) => const SizedBox(key: initializedKey))),
+      );
+      await tester.pump();
+    }
+
+    Future<void> advanceTimer(WidgetTester tester) async {
+      // Wait a short delay to let the initializer timer advance.
+      await tester.pumpAndSettle(const Duration(seconds: 3));
+    }
+
+    testWidgets('immediately calls builder when connection exists', (
+      WidgetTester tester,
+    ) async {
+      await pumpInitializer(tester);
+      expect(find.text(waitingText), findsNothing);
+      expect(find.text(cannotConnectText), findsNothing);
+      expect(find.byType(ConnectToNewAppButton), findsNothing);
+      expect(find.byKey(initializedKey), findsOneWidget);
+
+      // Verify expectations are still true after the timer advances.
+      await advanceTimer(tester);
+      expect(find.text(waitingText), findsNothing);
+      expect(find.text(cannotConnectText), findsNothing);
+      expect(find.byType(ConnectToNewAppButton), findsNothing);
+      expect(find.byKey(initializedKey), findsOneWidget);
+    });
+
+    testWidgets('calls builder late if connection is established late', (
+      WidgetTester tester,
+    ) async {
+      fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+
+      await pumpInitializer(tester);
+      expect(find.text(waitingText), findsOneWidget);
+      expect(find.text(cannotConnectText), findsNothing);
+      expect(find.byType(ConnectToNewAppButton), findsNothing);
+      expect(find.byKey(initializedKey), findsNothing);
+
+      fakeServiceConnectionManager.serviceManager.setConnectedState(true);
+      await tester.pump();
+
+      expect(find.text(waitingText), findsNothing);
+      expect(find.text(cannotConnectText), findsNothing);
+      expect(find.byType(ConnectToNewAppButton), findsNothing);
+      expect(find.byKey(initializedKey), findsOneWidget);
+
+      // Verify expectations are still true after the timer advances.
+      await advanceTimer(tester);
+      expect(find.text(waitingText), findsNothing);
+      expect(find.text(cannotConnectText), findsNothing);
+      expect(find.byType(ConnectToNewAppButton), findsNothing);
+      expect(find.byKey(initializedKey), findsOneWidget);
+    });
+
+    testWidgets(
+      'shows cannot connect message if connection is not established',
+      (WidgetTester tester) async {
+        fakeServiceConnectionManager.serviceManager.setConnectedState(false);
+
+        await pumpInitializer(tester);
+        expect(find.text(waitingText), findsOneWidget);
+        expect(find.text(cannotConnectText), findsNothing);
+        expect(find.byType(ConnectToNewAppButton), findsNothing);
+        expect(find.byKey(initializedKey), findsNothing);
+
+        await advanceTimer(tester);
+        expect(find.text(waitingText), findsNothing);
+        expect(find.text(cannotConnectText), findsOneWidget);
+        expect(find.byType(ConnectToNewAppButton), findsOneWidget);
+        expect(find.byKey(initializedKey), findsNothing);
+      },
+    );
+  });
+}
diff --git a/packages/devtools_app/test/shared/notifications_test.dart b/packages/devtools_app/test/framework/notifications_test.dart
similarity index 72%
rename from packages/devtools_app/test/shared/notifications_test.dart
rename to packages/devtools_app/test/framework/notifications_test.dart
index c7944dc..2054362 100644
--- a/packages/devtools_app/test/shared/notifications_test.dart
+++ b/packages/devtools_app/test/framework/notifications_test.dart
@@ -6,7 +6,7 @@
 
 import 'package:devtools_app/src/framework/notifications_view.dart';
 import 'package:devtools_app/src/shared/globals.dart';
-import 'package:devtools_app/src/shared/notifications.dart';
+import 'package:devtools_app/src/shared/managers/notifications.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
 import 'package:flutter/material.dart';
@@ -57,58 +57,55 @@
       expect(find.text(notification), findsNWidgets(2));
     });
 
-    testWidgets(
-      'dismissible notifications can be dismissed',
-      (WidgetTester tester) async {
-        const notification = 'This is a notification!';
-        await showNotification(
-          () => notificationService.push(notification, isDismissible: true),
-          tester: tester,
-        );
+    testWidgets('dismissible notifications can be dismissed', (
+      WidgetTester tester,
+    ) async {
+      const notification = 'This is a notification!';
+      await showNotification(
+        () => notificationService.push(notification, isDismissible: true),
+        tester: tester,
+      );
 
-        final closeButton = find.byType(IconButton);
-        expect(closeButton, findsOneWidget);
-        await tester.tap(closeButton);
+      final closeButton = find.byType(IconButton);
+      expect(closeButton, findsOneWidget);
+      await tester.tap(closeButton);
 
-        // Verify the notification has been dismissed:
-        await tester.pumpAndSettle();
-        expect(find.text(notification), findsNothing);
-      },
-    );
+      // Verify the notification has been dismissed:
+      await tester.pumpAndSettle();
+      expect(find.text(notification), findsNothing);
+    });
 
-    testWidgets(
-      'non-dismissible notifications expire',
-      (WidgetTester tester) async {
-        const notification = 'This is a notification!';
-        await showNotification(
-          () => notificationService.push(notification),
-          tester: tester,
-        );
+    testWidgets('non-dismissible notifications expire', (
+      WidgetTester tester,
+    ) async {
+      const notification = 'This is a notification!';
+      await showNotification(
+        () => notificationService.push(notification),
+        tester: tester,
+      );
 
-        expect(find.text(notification), findsOneWidget);
+      expect(find.text(notification), findsOneWidget);
 
-        // Wait for the notification to disappear.
-        await tester.pumpAndSettle(NotificationMessage.defaultDuration);
-        expect(find.text(notification), findsNothing);
-      },
-    );
+      // Wait for the notification to disappear.
+      await tester.pumpAndSettle(NotificationMessage.defaultDuration);
+      expect(find.text(notification), findsNothing);
+    });
 
-    testWidgets(
-      'dismissible notifications do not expire',
-      (WidgetTester tester) async {
-        const notification = 'This is a notification!';
-        await showNotification(
-          () => notificationService.push(notification, isDismissible: true),
-          tester: tester,
-        );
+    testWidgets('dismissible notifications do not expire', (
+      WidgetTester tester,
+    ) async {
+      const notification = 'This is a notification!';
+      await showNotification(
+        () => notificationService.push(notification, isDismissible: true),
+        tester: tester,
+      );
 
-        // Wait for the default dismiss duration.
-        await tester.pumpAndSettle(NotificationMessage.defaultDuration);
+      // Wait for the default dismiss duration.
+      await tester.pumpAndSettle(NotificationMessage.defaultDuration);
 
-        // Verify the notification is still there.
-        expect(find.text(notification), findsOneWidget);
-      },
-    );
+      // Verify the notification is still there.
+      expect(find.text(notification), findsOneWidget);
+    });
 
     testWidgets('persist across routes', (WidgetTester tester) async {
       const notification = 'Navigating to /details';
diff --git a/packages/devtools_app/test/shared/release_notes_test.dart b/packages/devtools_app/test/framework/release_notes_test.dart
similarity index 100%
rename from packages/devtools_app/test/shared/release_notes_test.dart
rename to packages/devtools_app/test/framework/release_notes_test.dart
diff --git a/packages/devtools_app/test/shared/about_dialog_test.dart b/packages/devtools_app/test/framework/scaffold/about_dialog_test.dart
similarity index 83%
rename from packages/devtools_app/test/shared/about_dialog_test.dart
rename to packages/devtools_app/test/framework/scaffold/about_dialog_test.dart
index 90d708e..bd0c073 100644
--- a/packages/devtools_app/test/shared/about_dialog_test.dart
+++ b/packages/devtools_app/test/framework/scaffold/about_dialog_test.dart
@@ -4,7 +4,7 @@
 
 import 'package:devtools_app/devtools.dart' as devtools;
 import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/framework/about_dialog.dart';
+import 'package:devtools_app/src/framework/scaffold/about_dialog.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
 import 'package:devtools_test/devtools_test.dart';
@@ -20,12 +20,15 @@
       aboutDialog = DevToolsAboutDialog(ReleaseNotesController());
       final fakeServiceConnection = FakeServiceConnectionManager();
       when(fakeServiceConnection.serviceManager.vm.version).thenReturn('1.9.1');
-      when(fakeServiceConnection.serviceManager.vm.targetCPU)
-          .thenReturn('arm64');
-      when(fakeServiceConnection.serviceManager.vm.architectureBits)
-          .thenReturn(64);
-      when(fakeServiceConnection.serviceManager.vm.operatingSystem)
-          .thenReturn('android');
+      when(
+        fakeServiceConnection.serviceManager.vm.targetCPU,
+      ).thenReturn('arm64');
+      when(
+        fakeServiceConnection.serviceManager.vm.architectureBits,
+      ).thenReturn(64);
+      when(
+        fakeServiceConnection.serviceManager.vm.operatingSystem,
+      ).thenReturn('android');
 
       mockConnectedApp(
         fakeServiceConnection.serviceManager.connectedApp!,
@@ -63,10 +66,7 @@
       );
       expect(findSubstring('CONTRIBUTING'), findsOneWidget);
       expect(find.textContaining('connect with us on'), findsOneWidget);
-      expect(
-        findSubstring('Discord'),
-        findsOneWidget,
-      );
+      expect(findSubstring('Discord'), findsOneWidget);
     });
   });
 }
diff --git a/packages/devtools_app/test/shared/scaffold_debugger_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_debugger_test.dart
similarity index 83%
rename from packages/devtools_app/test/shared/scaffold_debugger_test.dart
rename to packages/devtools_app/test/framework/scaffold/scaffold_debugger_test.dart
index cdf5fd5..cf4aa49 100644
--- a/packages/devtools_app/test/shared/scaffold_debugger_test.dart
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_debugger_test.dart
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/framework/scaffold.dart';
-import 'package:devtools_app/src/shared/framework_controller.dart';
-import 'package:devtools_app/src/shared/survey.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
 import 'package:devtools_app_shared/service.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
@@ -24,21 +24,21 @@
     mockServiceManager =
         mockServiceConnection.serviceManager as MockServiceManager;
     when(mockServiceManager.service).thenReturn(null);
-    when(mockServiceManager.connectedState).thenReturn(
-      ValueNotifier<ConnectedState>(const ConnectedState(false)),
-    );
+    when(
+      mockServiceManager.connectedState,
+    ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
     when(mockServiceManager.isolateManager).thenReturn(FakeIsolateManager());
-    when(mockServiceConnection.appState).thenReturn(
-      AppState(
-        mockServiceManager.isolateManager.selectedIsolate,
-      ),
-    );
+    when(
+      mockServiceConnection.appState,
+    ).thenReturn(AppState(mockServiceManager.isolateManager.selectedIsolate));
 
     final mockErrorBadgeManager = MockErrorBadgeManager();
-    when(mockServiceConnection.errorBadgeManager)
-        .thenReturn(mockErrorBadgeManager);
-    when(mockErrorBadgeManager.errorCountNotifier(any))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      mockServiceConnection.errorBadgeManager,
+    ).thenReturn(mockErrorBadgeManager);
+    when(
+      mockErrorBadgeManager.errorCountNotifier(any),
+    ).thenReturn(ValueNotifier<int>(0));
     when(mockServiceManager.isMainIsolatePaused).thenReturn(false);
 
     setGlobal(ServiceConnectionManager, mockServiceConnection);
@@ -119,12 +119,12 @@
     bool showFloatingDebuggerControls = true,
     Key? tabKey,
   }) : super(
-          name,
-          title: name,
-          icon: Icons.computer,
-          tabKey: tabKey,
-          showFloatingDebuggerControls: showFloatingDebuggerControls,
-        );
+         name,
+         title: name,
+         icon: Icons.computer,
+         tabKey: tabKey,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+       );
 
   final String name;
   final Key key;
diff --git a/packages/devtools_app/test/framework/scaffold/scaffold_debugging_controls_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_debugging_controls_test.dart
new file mode 100644
index 0000000..b1c9754
--- /dev/null
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_debugging_controls_test.dart
@@ -0,0 +1,126 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+  late MockServiceConnectionManager mockServiceConnection;
+  late MockServiceManager mockServiceManager;
+
+  setUp(() {
+    mockServiceConnection = createMockServiceConnectionWithDefaults();
+    mockServiceManager =
+        mockServiceConnection.serviceManager as MockServiceManager;
+
+    when(mockServiceManager.service).thenReturn(null);
+    when(
+      mockServiceManager.connectedState,
+    ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
+    when(mockServiceManager.isolateManager).thenReturn(FakeIsolateManager());
+    when(
+      mockServiceConnection.appState,
+    ).thenReturn(AppState(mockServiceManager.isolateManager.selectedIsolate));
+
+    final mockErrorBadgeManager = MockErrorBadgeManager();
+    when(
+      mockServiceConnection.errorBadgeManager,
+    ).thenReturn(mockErrorBadgeManager);
+    when(
+      mockErrorBadgeManager.errorCountNotifier(any),
+    ).thenReturn(ValueNotifier<int>(0));
+
+    setGlobal(ServiceConnectionManager, mockServiceConnection);
+    setGlobal(FrameworkController, FrameworkController());
+    setGlobal(SurveyService, SurveyService());
+    setGlobal(OfflineDataController, OfflineDataController());
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BannerMessagesController, BannerMessagesController());
+  });
+
+  testWidgets('displays floating debugger controls', (
+    WidgetTester tester,
+  ) async {
+    final connectedApp = MockConnectedApp();
+    mockConnectedApp(
+      connectedApp,
+      isFlutterApp: true,
+      isProfileBuild: false,
+      isWebApp: false,
+    );
+    when(mockServiceManager.connectedAppInitialized).thenReturn(true);
+    when(mockServiceManager.connectedApp).thenReturn(connectedApp);
+    when(mockServiceManager.isolateManager).thenReturn(FakeIsolateManager());
+    when(
+      mockServiceConnection.appState,
+    ).thenReturn(AppState(mockServiceManager.isolateManager.selectedIsolate));
+    final mockDebuggerController = MockDebuggerController();
+    final state =
+        serviceConnection.serviceManager.isolateManager.mainIsolateState!
+            as MockIsolateState;
+    when(state.isPaused).thenReturn(ValueNotifier(true));
+    when(mockServiceManager.isMainIsolatePaused).thenReturn(false);
+
+    await tester.pumpWidget(
+      wrapWithControllers(
+        DevToolsScaffold(
+          page: _screen1.screenId,
+          screens: const [_screen1, _screen2],
+        ),
+        debugger: mockDebuggerController,
+        analytics: AnalyticsController(
+          enabled: false,
+          shouldShowConsentMessage: false,
+          consentMessage: 'fake message',
+        ),
+        releaseNotes: ReleaseNotesController(),
+      ),
+    );
+    expect(find.byKey(_k1), findsOneWidget);
+    expect(find.byKey(_k2), findsNothing);
+    expect(find.byType(FloatingDebuggerControls), findsOneWidget);
+  });
+}
+
+class _TestScreen extends Screen {
+  const _TestScreen(
+    this.name,
+    this.key, {
+    bool showFloatingDebuggerControls = true,
+    Key? tabKey,
+  }) : super(
+         name,
+         title: name,
+         icon: Icons.computer,
+         tabKey: tabKey,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+       );
+
+  final String name;
+  final Key key;
+
+  @override
+  Widget buildScreenBody(BuildContext context) {
+    return SizedBox(key: key);
+  }
+}
+
+// Keys and tabs for use in the test.
+const _k1 = Key('body key 1');
+const _k2 = Key('body key 2');
+const _t1 = Key('tab key 1');
+const _t2 = Key('tab key 2');
+const _screen1 = _TestScreen('screen1', _k1, tabKey: _t1);
+const _screen2 = _TestScreen('screen2', _k2, tabKey: _t2);
diff --git a/packages/devtools_app/test/shared/scaffold_no_app_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_no_app_test.dart
similarity index 80%
rename from packages/devtools_app/test/shared/scaffold_no_app_test.dart
rename to packages/devtools_app/test/framework/scaffold/scaffold_no_app_test.dart
index 0731c20..550f2a2 100644
--- a/packages/devtools_app/test/shared/scaffold_no_app_test.dart
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_no_app_test.dart
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/framework/scaffold.dart';
-import 'package:devtools_app/src/shared/framework_controller.dart';
-import 'package:devtools_app/src/shared/survey.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
 import 'package:devtools_app_shared/service.dart';
 import 'package:devtools_app_shared/ui.dart';
 import 'package:devtools_app_shared/utils.dart';
@@ -25,15 +25,17 @@
         mockServiceConnection.serviceManager as MockServiceManager;
     when(mockServiceManager.service).thenReturn(null);
     when(mockServiceManager.connectedAppInitialized).thenReturn(false);
-    when(mockServiceManager.connectedState).thenReturn(
-      ValueNotifier<ConnectedState>(const ConnectedState(false)),
-    );
+    when(
+      mockServiceManager.connectedState,
+    ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
 
     final mockErrorBadgeManager = MockErrorBadgeManager();
-    when(mockServiceConnection.errorBadgeManager)
-        .thenReturn(mockErrorBadgeManager);
-    when(mockErrorBadgeManager.errorCountNotifier(any))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      mockServiceConnection.errorBadgeManager,
+    ).thenReturn(mockErrorBadgeManager);
+    when(
+      mockErrorBadgeManager.errorCountNotifier(any),
+    ).thenReturn(ValueNotifier<int>(0));
 
     setGlobal(ServiceConnectionManager, mockServiceConnection);
     setGlobal(FrameworkController, FrameworkController());
@@ -82,12 +84,12 @@
     bool showFloatingDebuggerControls = true,
     Key? tabKey,
   }) : super(
-          name,
-          title: name,
-          icon: Icons.computer,
-          tabKey: tabKey,
-          showFloatingDebuggerControls: showFloatingDebuggerControls,
-        );
+         name,
+         title: name,
+         icon: Icons.computer,
+         tabKey: tabKey,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+       );
 
   final String name;
   final Key key;
diff --git a/packages/devtools_app/test/framework/scaffold/scaffold_profile_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_profile_test.dart
new file mode 100644
index 0000000..a585a51
--- /dev/null
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_profile_test.dart
@@ -0,0 +1,121 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+  late MockServiceConnectionManager mockServiceConnection;
+  late MockServiceManager mockServiceManager;
+
+  setUp(() {
+    mockServiceConnection = createMockServiceConnectionWithDefaults();
+    mockServiceManager =
+        mockServiceConnection.serviceManager as MockServiceManager;
+    when(mockServiceManager.service).thenReturn(null);
+    when(
+      mockServiceManager.connectedState,
+    ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
+    when(mockServiceManager.isolateManager).thenReturn(FakeIsolateManager());
+    when(
+      mockServiceConnection.appState,
+    ).thenReturn(AppState(mockServiceManager.isolateManager.selectedIsolate));
+
+    final mockErrorBadgeManager = MockErrorBadgeManager();
+    when(
+      mockServiceConnection.errorBadgeManager,
+    ).thenReturn(mockErrorBadgeManager);
+    when(
+      mockErrorBadgeManager.errorCountNotifier(any),
+    ).thenReturn(ValueNotifier<int>(0));
+
+    setGlobal(ServiceConnectionManager, mockServiceConnection);
+    setGlobal(FrameworkController, FrameworkController());
+    setGlobal(SurveyService, SurveyService());
+    setGlobal(OfflineDataController, OfflineDataController());
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BannerMessagesController, BannerMessagesController());
+  });
+
+  testWidgets('does not display floating debugger controls in profile mode', (
+    WidgetTester tester,
+  ) async {
+    final connectedApp = MockConnectedApp();
+    mockConnectedApp(
+      connectedApp,
+      isFlutterApp: true,
+      isProfileBuild: true,
+      isWebApp: false,
+    );
+    when(mockServiceManager.connectedAppInitialized).thenReturn(true);
+    when(mockServiceManager.connectedApp).thenReturn(connectedApp);
+    final mockDebuggerController = MockDebuggerController();
+
+    final state =
+        serviceConnection.serviceManager.isolateManager.mainIsolateState!
+            as MockIsolateState;
+    when(state.isPaused).thenReturn(ValueNotifier(true));
+
+    await tester.pumpWidget(
+      wrapWithControllers(
+        DevToolsScaffold(
+          page: _screen1.screenId,
+          screens: const [_screen1, _screen2],
+        ),
+        debugger: mockDebuggerController,
+        analytics: AnalyticsController(
+          enabled: false,
+          shouldShowConsentMessage: false,
+          consentMessage: 'fake message',
+        ),
+        releaseNotes: ReleaseNotesController(),
+      ),
+    );
+    expect(find.byKey(_k1), findsOneWidget);
+    expect(find.byKey(_k2), findsNothing);
+    expect(find.byType(FloatingDebuggerControls), findsNothing);
+  });
+}
+
+class _TestScreen extends Screen {
+  const _TestScreen(
+    this.name,
+    this.key, {
+    bool showFloatingDebuggerControls = true,
+    Key? tabKey,
+  }) : super(
+         name,
+         title: name,
+         icon: Icons.computer,
+         tabKey: tabKey,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+       );
+
+  final String name;
+  final Key key;
+
+  @override
+  Widget buildScreenBody(BuildContext context) {
+    return SizedBox(key: key);
+  }
+}
+
+// Keys and tabs for use in the test.
+const _k1 = Key('body key 1');
+const _k2 = Key('body key 2');
+const _t1 = Key('tab key 1');
+const _t2 = Key('tab key 2');
+const _screen1 = _TestScreen('screen1', _k1, tabKey: _t1);
+const _screen2 = _TestScreen('screen2', _k2, tabKey: _t2);
diff --git a/packages/devtools_app/test/shared/scaffold_test.dart b/packages/devtools_app/test/framework/scaffold/scaffold_test.dart
similarity index 71%
rename from packages/devtools_app/test/shared/scaffold_test.dart
rename to packages/devtools_app/test/framework/scaffold/scaffold_test.dart
index a5b6f85..8f4c538 100644
--- a/packages/devtools_app/test/shared/scaffold_test.dart
+++ b/packages/devtools_app/test/framework/scaffold/scaffold_test.dart
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/framework/scaffold.dart';
-import 'package:devtools_app/src/shared/framework_controller.dart';
-import 'package:devtools_app/src/shared/query_parameters.dart';
-import 'package:devtools_app/src/shared/survey.dart';
+import 'package:devtools_app/src/framework/scaffold/scaffold.dart';
+import 'package:devtools_app/src/shared/framework/framework_controller.dart';
+import 'package:devtools_app/src/shared/managers/survey.dart';
+import 'package:devtools_app/src/shared/primitives/query_parameters.dart';
 import 'package:devtools_app_shared/service.dart';
 import 'package:devtools_app_shared/shared.dart';
 import 'package:devtools_app_shared/ui.dart';
@@ -26,15 +26,17 @@
         mockServiceConnection.serviceManager as MockServiceManager;
     when(mockServiceManager.service).thenReturn(null);
     when(mockServiceManager.connectedAppInitialized).thenReturn(false);
-    when(mockServiceManager.connectedState).thenReturn(
-      ValueNotifier<ConnectedState>(const ConnectedState(false)),
-    );
+    when(
+      mockServiceManager.connectedState,
+    ).thenReturn(ValueNotifier<ConnectedState>(const ConnectedState(false)));
 
     final mockErrorBadgeManager = MockErrorBadgeManager();
-    when(mockServiceConnection.errorBadgeManager)
-        .thenReturn(mockErrorBadgeManager);
-    when(mockErrorBadgeManager.errorCountNotifier(any))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      mockServiceConnection.errorBadgeManager,
+    ).thenReturn(mockErrorBadgeManager);
+    when(
+      mockErrorBadgeManager.errorCountNotifier(any),
+    ).thenReturn(ValueNotifier<int>(0));
 
     setGlobal(ServiceConnectionManager, mockServiceConnection);
     setGlobal(FrameworkController, FrameworkController());
@@ -162,19 +164,18 @@
     },
   );
 
-  testWidgets(
-    'displays tabs with only one screen',
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapScaffold(
-          DevToolsScaffold(page: _screen1.screenId, screens: const [_screen1]),
-        ),
-      );
-      expect(find.byType(DevToolsAppBar), findsOneWidget);
-      expect(find.byKey(_k1), findsOneWidget);
-      expect(find.byKey(_t1), findsOneWidget);
-    },
-  );
+  testWidgets('displays tabs with only one screen', (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapScaffold(
+        DevToolsScaffold(page: _screen1.screenId, screens: const [_screen1]),
+      ),
+    );
+    expect(find.byType(DevToolsAppBar), findsOneWidget);
+    expect(find.byKey(_k1), findsOneWidget);
+    expect(find.byKey(_t1), findsOneWidget);
+  });
 
   testWidgets('displays only the selected screen', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -203,39 +204,37 @@
     expect(find.byKey(_k2), findsNothing);
   });
 
-  testWidgets(
-    'displays the requested initial page',
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapScaffold(
-          DevToolsScaffold(
-            screens: const [_screen1, _screen2],
-            page: _screen2.screenId,
-          ),
+  testWidgets('displays the requested initial page', (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapScaffold(
+        DevToolsScaffold(
+          screens: const [_screen1, _screen2],
+          page: _screen2.screenId,
         ),
-      );
-      expect(find.byType(DevToolsAppBar), findsOneWidget);
+      ),
+    );
+    expect(find.byType(DevToolsAppBar), findsOneWidget);
 
-      expect(find.byKey(_k1), findsNothing);
-      expect(find.byKey(_k2), findsOneWidget);
-    },
-  );
+    expect(find.byKey(_k1), findsNothing);
+    expect(find.byKey(_k2), findsOneWidget);
+  });
 
-  testWidgets(
-    'hides the app bar for EmbedMode.embedOne',
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapScaffold(
-          DevToolsScaffold(
-            screens: const [_screen1, _screen2],
-            page: _screen2.screenId,
-            embedMode: EmbedMode.embedOne,
-          ),
+  testWidgets('hides the app bar for EmbedMode.embedOne', (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapScaffold(
+        DevToolsScaffold(
+          screens: const [_screen1, _screen2],
+          page: _screen2.screenId,
+          embedMode: EmbedMode.embedOne,
         ),
-      );
-      expect(find.byType(DevToolsAppBar), findsNothing);
-    },
-  );
+      ),
+    );
+    expect(find.byType(DevToolsAppBar), findsNothing);
+  });
 
   testWidgets(
     'hides the app bar for EmbedMode.embedMany with a single simple screen',
@@ -252,46 +251,44 @@
     },
   );
 
-  testWidgets(
-    'shows the app bar for EmbedMode.embedMany',
-    (WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapScaffold(
-          DevToolsScaffold(
-            screens: const [_screen2],
-            page: _screen2.screenId,
-            embedMode: EmbedMode.embedMany,
-          ),
+  testWidgets('shows the app bar for EmbedMode.embedMany', (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapScaffold(
+        DevToolsScaffold(
+          screens: const [_screen2],
+          page: _screen2.screenId,
+          embedMode: EmbedMode.embedMany,
         ),
-      );
-      expect(find.byType(DevToolsAppBar), findsOneWidget);
-    },
-  );
+      ),
+    );
+    expect(find.byType(DevToolsAppBar), findsOneWidget);
+  });
 
-  testWidgets(
-    'uses empty actions as default when embedded',
-    (WidgetTester tester) async {
-      var scaffold = DevToolsScaffold(
-        screens: const [_screen1, _screen2],
-        page: _screen1.screenId,
-      );
-      expect(scaffold.actions.length, 4);
+  testWidgets('uses empty actions as default when embedded', (
+    WidgetTester tester,
+  ) async {
+    var scaffold = DevToolsScaffold(
+      screens: const [_screen1, _screen2],
+      page: _screen1.screenId,
+    );
+    expect(scaffold.actions.length, 4);
 
-      scaffold = DevToolsScaffold(
-        screens: const [_screen1, _screen2],
-        page: _screen1.screenId,
-        embedMode: EmbedMode.embedOne,
-      );
-      expect(scaffold.actions, isEmpty);
+    scaffold = DevToolsScaffold(
+      screens: const [_screen1, _screen2],
+      page: _screen1.screenId,
+      embedMode: EmbedMode.embedOne,
+    );
+    expect(scaffold.actions, isEmpty);
 
-      scaffold = DevToolsScaffold(
-        screens: const [_screen1, _screen2],
-        page: _screen1.screenId,
-        embedMode: EmbedMode.embedMany,
-      );
-      expect(scaffold.actions, isEmpty);
-    },
-  );
+    scaffold = DevToolsScaffold(
+      screens: const [_screen1, _screen2],
+      page: _screen1.screenId,
+      embedMode: EmbedMode.embedMany,
+    );
+    expect(scaffold.actions, isEmpty);
+  });
 }
 
 class _TestScreen extends Screen {
@@ -301,12 +298,12 @@
     bool showFloatingDebuggerControls = true,
     Key? tabKey,
   }) : super(
-          name,
-          title: name,
-          icon: Icons.computer,
-          tabKey: tabKey,
-          showFloatingDebuggerControls: showFloatingDebuggerControls,
-        );
+         name,
+         title: name,
+         icon: Icons.computer,
+         tabKey: tabKey,
+         showFloatingDebuggerControls: showFloatingDebuggerControls,
+       );
 
   final String name;
   final Key key;
diff --git a/packages/devtools_app/test/http/curl_command_test.dart b/packages/devtools_app/test/http/curl_command_test.dart
index 8956235..817d8a4 100644
--- a/packages/devtools_app/test/http/curl_command_test.dart
+++ b/packages/devtools_app/test/http/curl_command_test.dart
@@ -209,9 +209,7 @@
 }
 
 class _TestDartIOHttpRequestData extends DartIOHttpRequestData {
-  _TestDartIOHttpRequestData(
-    this._request,
-  ) : super(_request);
+  _TestDartIOHttpRequestData(this._request) : super(_request);
 
   final HttpProfileRequest _request;
 
diff --git a/packages/devtools_app/test/inspector/diagnostics_test.dart b/packages/devtools_app/test/inspector/diagnostics_test.dart
deleted file mode 100644
index 7f04c26..0000000
--- a/packages/devtools_app/test/inspector/diagnostics_test.dart
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be found
-// in the LICENSE file.
-
-import 'dart:convert';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/ui/utils.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  group('DiagnosticsNodeDescription', () {
-    final renderObjectJson = jsonDecode(
-      '''
-        {
-          "properties": [
-            {
-              "description": "horizontal",
-              "name": "direction"
-            },
-            {
-              "description": "start",
-              "name": "mainAxisAlignment"
-            },
-            {
-              "description": "max",
-              "name": "mainAxisSize"
-            },
-            {
-              "description": "center",
-              "name": "crossAxisAlignment"
-            },
-            {
-              "description": "ltr",
-              "name": "textDirection"
-            },
-            {
-              "description": "down",
-              "name": "verticalDirection"
-            }
-          ]
-        }
-      ''',
-    );
-    setUp(() {
-      setGlobal(
-        DevToolsEnvironmentParameters,
-        ExternalDevToolsEnvironmentParameters(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(IdeTheme, IdeTheme());
-      setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-    });
-
-    group('hover eval', () {
-      final nodeJson = <String, Object?>{
-        'widgetRuntimeType': 'Row',
-        'renderObject': renderObjectJson,
-        'hasChildren': false,
-        'children': [],
-      };
-      final inspectorService = MockInspectorObjectGroupBase();
-      final diagnostic = RemoteDiagnosticsNode(
-        nodeJson,
-        inspectorService,
-        false,
-        null,
-      );
-      late DiagnosticsNodeDescription diagnosticsNodeDescription;
-
-      setUp(() {
-        preferences.inspector.setHoverEvalMode(true);
-        diagnosticsNodeDescription = DiagnosticsNodeDescription(
-          diagnostic,
-        );
-      });
-
-      testWidgets(
-        'can be enabled from preferences',
-        (WidgetTester tester) async {
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), true);
-        },
-      );
-
-      testWidgets(
-        'can be disabled from preferences',
-        (WidgetTester tester) async {
-          preferences.inspector.setHoverEvalMode(false);
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), false);
-        },
-      );
-
-      testWidgets(
-        'disabled when inspector service not set',
-        (WidgetTester tester) async {
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-          diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), false);
-        },
-      );
-    });
-
-    group('approximateNodeWidth', () {
-      const epsilon = 7.0;
-      testWidgets(
-        'property diagnostics node with name and description',
-        (WidgetTester tester) async {
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'children': [],
-            'description':
-                'this is a showname description, which will show up after the name',
-            'showName': true,
-            'name': 'THE NAME to be shown',
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            true,
-            null,
-          );
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // There are many rich texts, containg the name, and description.
-          final allRichTexts = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .evaluate()
-              .map((e) => e.widget as RichText);
-          final measuredWidthOfAllRichTexts = allRichTexts.fold<double>(
-            0,
-            (previousValue, richText) =>
-                previousValue +
-                calculateTextSpanWidth(
-                  richText.text as TextSpan,
-                ),
-          );
-          expect(
-            approximatedWidth,
-            moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
-          );
-        },
-      );
-
-      testWidgets(
-        'diagnostics node with icon and description',
-        (WidgetTester tester) async {
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'description': 'This is the description',
-            'children': [],
-            'showName': false,
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedTextWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // The icon is part of the clickable width, so we include it.
-          final measuredIconWidth = tester
-              .getSize(
-                find.descendant(
-                  of: diagnosticsNodeFind,
-                  matching: find.byType(AssetImageIcon),
-                ),
-              )
-              .width;
-
-          // There is only one rich text widget, containing the description.
-          final richTextWidget = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .first
-              .evaluate()
-              .first
-              .widget as RichText;
-          final measuredTextWidth =
-              calculateTextSpanWidth(richTextWidget.text as TextSpan);
-
-          expect(
-            approximatedTextWidth,
-            moreOrLessEquals(
-              measuredTextWidth + measuredIconWidth,
-              epsilon: epsilon,
-            ),
-          );
-        },
-      );
-
-      testWidgets(
-        'error node with different fontSize',
-        (WidgetTester tester) async {
-          // Nodes with normal levels default to using the default fontSize, so
-          // using an error level node allows us to test different font sizes.
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'children': [],
-            'description':
-                'this is a showname description, which will show up after the name',
-            'showName': true,
-            'name': 'THE NAME to be shown',
-            'level': 'error',
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-
-          //Use a textStyle that is much larger than the normal style
-          const textStyle = TextStyle(fontSize: 24.0, fontFamily: 'Roboto');
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-            style: textStyle,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // There are many rich texts, containg the name, and description.
-          final allRichTexts = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .evaluate()
-              .map((e) => e.widget as RichText);
-
-          final measuredWidthOfAllRichTexts =
-              allRichTexts.fold<double>(0, (previousValue, richText) {
-            final originalTextSpan = richText.text as TextSpan;
-
-            return previousValue +
-                calculateTextSpanWidth(
-                  originalTextSpan,
-                );
-          });
-
-          expect(
-            approximatedWidth,
-            moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
-          );
-        },
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/inspector/layout_explorer/flex/arrow_test.dart b/packages/devtools_app/test/inspector/layout_explorer/flex/arrow_test.dart
deleted file mode 100644
index ef1cd5b..0000000
--- a/packages/devtools_app/test/inspector/layout_explorer/flex/arrow_test.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/screens/inspector/layout_explorer/ui/arrow.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../../test_infra/matchers/matchers.dart';
-
-void main() {
-  group('Arrow Golden Tests', () {
-    group('Unidirectional', () {
-      Widget buildUnidirectionalArrowWrapper(ArrowType type) => Directionality(
-            textDirection: TextDirection.ltr,
-            child: SizedBox(
-              width: 100,
-              height: 100,
-              child: ArrowWrapper.unidirectional(
-                type: type,
-                arrowColor: Colors.black,
-                arrowHeadSize: 8.0,
-                child: Container(
-                  width: 10,
-                  height: 10,
-                  color: Colors.red,
-                ),
-              ),
-            ),
-          );
-      testWidgets(
-        'left',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.left);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_left.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'up',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.up);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_up.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'right',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.right);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_right.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'down',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.down);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_down.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-    });
-
-    group('Bidirectional', () {
-      Widget buildBidirectionalArrowWrapper(Axis direction) => Directionality(
-            textDirection: TextDirection.ltr,
-            child: SizedBox(
-              width: 100,
-              height: 100,
-              child: ArrowWrapper.bidirectional(
-                direction: direction,
-                arrowColor: Colors.black,
-                arrowHeadSize: 8.0,
-                child: Container(
-                  width: 10,
-                  height: 10,
-                  color: Colors.red,
-                ),
-              ),
-            ),
-          );
-      testWidgets(
-        'horizontal',
-        (WidgetTester tester) async {
-          final widget = buildBidirectionalArrowWrapper(Axis.horizontal);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_bidirectional_horizontal.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'vertical',
-        (WidgetTester tester) async {
-          final widget = buildBidirectionalArrowWrapper(Axis.vertical);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_bidirectional_vertical.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/inspector/layout_explorer/layout_explorer_serialization_delegate.dart b/packages/devtools_app/test/inspector/layout_explorer/layout_explorer_serialization_delegate.dart
deleted file mode 100644
index 0a38fd1..0000000
--- a/packages/devtools_app/test/inspector/layout_explorer/layout_explorer_serialization_delegate.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/rendering.dart';
-import 'package:flutter/widgets.dart';
-
-/// This class is merely a duplicate of the InspectorSerializationDelegate inside the `assets/scripts/inspector_polyfill_script.dart`.
-/// This delegate is used for testing Widget Diagnostics Node so that we don't have to create manual JSON each time we want to create new test cases.
-/// TODO(adalberht): Ask Jacob on what's the better solution on code reuse between the Layout Explorer polyfillscripts and the code inside test package?
-class LayoutExplorerSerializationDelegate
-    extends InspectorSerializationDelegate {
-  LayoutExplorerSerializationDelegate({
-    String super.groupName = '',
-    super.subtreeDepth,
-    required super.service,
-  }) : super(
-          summaryTree: true,
-          addAdditionalPropertiesCallback: (node, delegate) {
-            final additionalJson = <String, Object>{};
-            final value = node.value;
-            if (value is Element) {
-              final renderObject = value.renderObject!;
-              additionalJson['renderObject'] =
-                  renderObject.toDiagnosticsNode().toJsonMap(
-                        delegate.copyWith(
-                          subtreeDepth: 0,
-                          includeProperties: true,
-                        ),
-                      );
-              // Required for test.
-              // ignore: invalid_use_of_protected_member
-              final constraints = renderObject.constraints;
-
-              final constraintsProperty = <String, Object>{
-                'type': constraints.runtimeType.toString(),
-                'description': constraints.toString(),
-              };
-              if (constraints is BoxConstraints) {
-                constraintsProperty.addAll(<String, Object>{
-                  'minWidth': constraints.minWidth.toString(),
-                  'minHeight': constraints.minHeight.toString(),
-                  'maxWidth': constraints.maxWidth.toString(),
-                  'maxHeight': constraints.maxHeight.toString(),
-                });
-              }
-              additionalJson['constraints'] = constraintsProperty;
-
-              if (renderObject is RenderBox) {
-                additionalJson['size'] = <String, Object>{
-                  'width': renderObject.size.width.toString(),
-                  'height': renderObject.size.height.toString(),
-                };
-
-                final parentData = renderObject.parentData;
-                if (parentData is FlexParentData) {
-                  additionalJson['flexFactor'] = parentData.flex ?? 0;
-                  additionalJson['flexFit'] =
-                      (parentData.fit ?? FlexFit.tight).name;
-                }
-              }
-            }
-            return additionalJson;
-          },
-        );
-}
diff --git a/packages/devtools_app/test/inspector/utils/inspector_tree.dart b/packages/devtools_app/test/inspector/utils/inspector_tree.dart
deleted file mode 100644
index c637f58..0000000
--- a/packages/devtools_app/test/inspector/utils/inspector_tree.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/rendering.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-/// Create an `InspectorTreeControllerFlutter` from a single `RemoteDiagnosticsNode`
-InspectorTreeController inspectorTreeControllerFromNode(
-  RemoteDiagnosticsNode node,
-) {
-  final controller = InspectorTreeController()
-    ..config = InspectorTreeConfig(
-      onNodeAdded: (_, __) {},
-      onClientActiveChange: (_) {},
-    );
-
-  controller.root = InspectorTreeNode()
-    ..appendChild(
-      InspectorTreeNode()..diagnostic = node,
-    );
-
-  return controller;
-}
-
-/// Replicates the functionality of `getRootWidgetSummaryTreeWithPreviews` from
-/// inspector_polyfill_script.dart
-Future<RemoteDiagnosticsNode> widgetToInspectorTreeDiagnosticsNode({
-  required Widget widget,
-  required WidgetTester tester,
-}) async {
-  await tester.pumpWidget(wrap(widget));
-  final element = find.byWidget(widget).evaluate().first;
-  final nodeJson =
-      element.toDiagnosticsNode(style: DiagnosticsTreeStyle.dense).toJsonMap(
-            InspectorSerializationDelegate(
-              service: WidgetInspectorService.instance,
-              subtreeDepth: 1000000,
-              summaryTree: true,
-              addAdditionalPropertiesCallback: (node, delegate) {
-                final additionalJson = <String, Object>{};
-
-                final value = node.value;
-                if (value is Element) {
-                  final renderObject = value.renderObject;
-                  if (renderObject is RenderParagraph) {
-                    additionalJson['textPreview'] =
-                        renderObject.text.toPlainText();
-                  }
-                }
-
-                return additionalJson;
-              },
-            ),
-          );
-
-  return RemoteDiagnosticsNode(nodeJson, null, false, null);
-}
diff --git a/packages/devtools_app/test/inspector_v2/diagnostics_test.dart b/packages/devtools_app/test/inspector_v2/diagnostics_test.dart
deleted file mode 100644
index 94ce998..0000000
--- a/packages/devtools_app/test/inspector_v2/diagnostics_test.dart
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be found
-// in the LICENSE file.
-
-import 'dart:convert';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/feature_flags.dart';
-import 'package:devtools_app/src/shared/ui/utils.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  group('DiagnosticsNodeDescription', () {
-    final renderObjectJson = jsonDecode(
-      '''
-        {
-          "properties": [
-            {
-              "description": "horizontal",
-              "name": "direction"
-            },
-            {
-              "description": "start",
-              "name": "mainAxisAlignment"
-            },
-            {
-              "description": "max",
-              "name": "mainAxisSize"
-            },
-            {
-              "description": "center",
-              "name": "crossAxisAlignment"
-            },
-            {
-              "description": "ltr",
-              "name": "textDirection"
-            },
-            {
-              "description": "down",
-              "name": "verticalDirection"
-            }
-          ]
-        }
-      ''',
-    );
-    setUp(() {
-      setEnableExperiments();
-      setGlobal(
-        DevToolsEnvironmentParameters,
-        ExternalDevToolsEnvironmentParameters(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(IdeTheme, IdeTheme());
-      setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-    });
-
-    group('hover eval', () {
-      final nodeJson = <String, Object?>{
-        'widgetRuntimeType': 'Row',
-        'renderObject': renderObjectJson,
-        'hasChildren': false,
-        'children': [],
-      };
-      final inspectorService = MockInspectorObjectGroupBase();
-      final diagnostic = RemoteDiagnosticsNode(
-        nodeJson,
-        inspectorService,
-        false,
-        null,
-      );
-      late DiagnosticsNodeDescription diagnosticsNodeDescription;
-
-      setUp(() {
-        preferences.inspector.setHoverEvalMode(true);
-        diagnosticsNodeDescription = DiagnosticsNodeDescription(
-          diagnostic,
-        );
-      });
-
-      testWidgets(
-        'can be enabled from preferences',
-        (WidgetTester tester) async {
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), true);
-        },
-      );
-
-      testWidgets(
-        'can be disabled from preferences',
-        (WidgetTester tester) async {
-          preferences.inspector.setHoverEvalMode(false);
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), false);
-        },
-      );
-
-      testWidgets(
-        'disabled when inspector service not set',
-        (WidgetTester tester) async {
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-          diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final hoverCardTooltip =
-              tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
-          expect(hoverCardTooltip.enabled(), false);
-        },
-      );
-    });
-
-    group('approximateNodeWidth', () {
-      const epsilon = 7.0;
-      testWidgets(
-        'property diagnostics node with name and description',
-        (WidgetTester tester) async {
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'children': [],
-            'description':
-                'this is a showname description, which will show up after the name',
-            'showName': true,
-            'name': 'THE NAME to be shown',
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            true,
-            null,
-          );
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // There are many rich texts, containg the name, and description.
-          final allRichTexts = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .evaluate()
-              .map((e) => e.widget as RichText);
-          final measuredWidthOfAllRichTexts = allRichTexts.fold<double>(
-            0,
-            (previousValue, richText) =>
-                previousValue +
-                calculateTextSpanWidth(
-                  richText.text as TextSpan,
-                ),
-          );
-          expect(
-            approximatedWidth,
-            moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
-          );
-        },
-      );
-
-      testWidgets(
-        'diagnostics node with icon and description',
-        (WidgetTester tester) async {
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'description': 'This is the description',
-            'children': [],
-            'showName': false,
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedTextWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // The icon is part of the clickable width, so we include it.
-          final measuredIconWidth = tester
-              .getSize(
-                find.descendant(
-                  of: diagnosticsNodeFind,
-                  matching: find.byType(AssetImageIcon),
-                ),
-              )
-              .width;
-
-          // There is only one rich text widget, containing the description.
-          final richTextWidget = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .first
-              .evaluate()
-              .first
-              .widget as RichText;
-          final measuredTextWidth =
-              calculateTextSpanWidth(richTextWidget.text as TextSpan);
-
-          expect(
-            approximatedTextWidth,
-            moreOrLessEquals(
-              measuredTextWidth + measuredIconWidth,
-              epsilon: epsilon,
-            ),
-          );
-        },
-      );
-
-      testWidgets(
-        'error node with different fontSize',
-        (WidgetTester tester) async {
-          // Nodes with normal levels default to using the default fontSize, so
-          // using an error level node allows us to test different font sizes.
-          final nodeJson = <String, Object?>{
-            'widgetRuntimeType': 'Row',
-            'renderObject': renderObjectJson,
-            'hasChildren': false,
-            'children': [],
-            'description':
-                'this is a showname description, which will show up after the name',
-            'showName': true,
-            'name': 'THE NAME to be shown',
-            'level': 'error',
-          };
-          final diagnosticWithoutService = RemoteDiagnosticsNode(
-            nodeJson,
-            null,
-            false,
-            null,
-          );
-
-          //Use a textStyle that is much larger than the normal style
-          const textStyle = TextStyle(fontSize: 24.0, fontFamily: 'Roboto');
-          final diagnosticsNodeDescription = DiagnosticsNodeDescription(
-            diagnosticWithoutService,
-            style: textStyle,
-          );
-
-          await tester.pumpWidget(wrap(diagnosticsNodeDescription));
-
-          final approximatedWidth =
-              DiagnosticsNodeDescription.approximateNodeWidth(
-            diagnosticWithoutService,
-          );
-
-          final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
-          // There are many rich texts, containg the name, and description.
-          final allRichTexts = find
-              .descendant(
-                of: diagnosticsNodeFind,
-                matching: find.byType(RichText),
-              )
-              .evaluate()
-              .map((e) => e.widget as RichText);
-
-          final measuredWidthOfAllRichTexts =
-              allRichTexts.fold<double>(0, (previousValue, richText) {
-            final originalTextSpan = richText.text as TextSpan;
-
-            return previousValue +
-                calculateTextSpanWidth(
-                  originalTextSpan,
-                );
-          });
-
-          expect(
-            approximatedWidth,
-            moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
-          );
-        },
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/inspector_v2/inspector_integration_test.dart b/packages/devtools_app/test/inspector_v2/inspector_integration_test.dart
deleted file mode 100644
index 6337b57..0000000
--- a/packages/devtools_app/test/inspector_v2/inspector_integration_test.dart
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart'
-    hide InspectorScreenBodyState, InspectorScreenBody;
-import 'package:devtools_app/src/screens/inspector/inspector_screen_body.dart'
-    as legacy;
-import 'package:devtools_app/src/screens/inspector_v2/inspector_screen_body.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';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../test_infra/flutter_test_driver.dart' show FlutterRunConfiguration;
-import '../test_infra/flutter_test_environment.dart';
-import '../test_infra/matchers/matchers.dart';
-
-// Note: This test uses packages/devtools_app/test/test_infra/fixtures/flutter_app
-// running on the flutter-tester device.
-
-// This is a bit conservative to ensure we do not get flakes due to
-// slow interactions with the VM Service. This delay could likely be
-// reduced to under 1 second without introducing flakes.
-const inspectorChangeSettleTime = Duration(seconds: 2);
-
-void main() {
-  // 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),
-  );
-
-  env.afterEverySetup = () async {
-    final service = serviceConnection.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(
-        'WidgetInspectorService.instance.selection.clear()',
-        isAlive: null,
-      );
-    }
-  };
-
-  setUp(() async {
-    await env.setupEnvironment();
-    // Enable the V2 inspector:
-    preferences.inspector.setInspectorV2Enabled(true);
-  });
-
-  tearDown(() async {
-    await env.tearDownEnvironment(force: true);
-    // Re-set changes to preferences:
-    preferences.inspector.setInspectorV2Enabled(false);
-  });
-
-  group('screenshot tests', () {
-    testWidgetsWithWindowSize(
-      'initial load',
-      windowSize,
-      (WidgetTester tester) async {
-        expect(serviceConnection.serviceManager.service, equals(env.service));
-        expect(serviceConnection.serviceManager.isolateManager, isNotNull);
-
-        await _loadInspectorUI(tester);
-
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_initial_load.png',
-          ),
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'loads after a hot-restart',
-      windowSize,
-      (WidgetTester tester) async {
-        // Load the inspector panel.
-        await _loadInspectorUI(tester);
-
-        // Expect the Center widget to be visible in the widget tree.
-        final centerWidgetFinder = find.richText('Center');
-        expect(centerWidgetFinder, findsOneWidget);
-
-        // Trigger a hot-restart and wait for the first Flutter frame.
-        await env.flutter!.hotRestart();
-        await _waitForFlutterFrame(tester, isInitialLoad: false);
-
-        // Wait for the Center widget to be visible again.
-        final centerWidgetFinderWithRetries = await retryUntilFound(
-          centerWidgetFinder,
-          tester: tester,
-        );
-        expect(centerWidgetFinderWithRetries, findsOneWidget);
-
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_after_hot_restart.png',
-          ),
-        );
-      },
-      skip: true, // https://github.com/flutter/devtools/issues/8179
-    );
-
-    testWidgetsWithWindowSize(
-      'widget selection',
-      windowSize,
-      (WidgetTester tester) async {
-        await _loadInspectorUI(tester);
-
-        // Select the Center widget (row index #16)
-        await tester.tap(find.richText('Center'));
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_select_center.png',
-          ),
-        );
-
-        // Verify the properties are displayed:
-        verifyPropertyIsVisible(
-          name: 'alignment',
-          value: 'Alignment.center',
-          tester: tester,
-        );
-        verifyPropertyIsVisible(
-          name: 'dependencies',
-          value: '[Directionality]',
-          tester: tester,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'expand and collapse implementation widgets',
-      windowSize,
-      (WidgetTester tester) async {
-        await _loadInspectorUI(tester);
-
-        // Before hidden widgets are expanded, confirm the HeroControllerScope
-        // is hidden:
-        final hideableNodeFinder = findNodeMatching('HeroControllerScope');
-        expect(hideableNodeFinder, findsNothing);
-
-        // Expand the hidden group that contains the HeroControllerScope:
-        final expandButton = findExpandCollapseButtonForNode(
-          nodeDescription: '71 more widgets...',
-          isExpand: true,
-        );
-        await tester.tap(expandButton);
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_implementation_widgets_expanded.png',
-          ),
-        );
-
-        // Confirm the HeroControllerScope is visible, and select it:
-        expect(hideableNodeFinder, findsOneWidget);
-        await tester.tap(hideableNodeFinder);
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_hideable_widget_selected.png',
-          ),
-        );
-
-        // Collapse the hidden group that contains the HeroControllerScope:
-        final collapseButton = findExpandCollapseButtonForNode(
-          nodeDescription: 'ScrollConfiguration',
-          isExpand: false,
-        );
-        await tester.tap(collapseButton);
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_implementation_widgets_collapsed.png',
-          ),
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'search for implementation widgets',
-      windowSize,
-      (WidgetTester tester) async {
-        await _loadInspectorUI(tester);
-
-        // Before searching, confirm the HeroControllerScope is hidden:
-        final hideableNodeFinder = findNodeMatching('HeroControllerScope');
-        expect(hideableNodeFinder, findsNothing);
-
-        // Search for the HeroControllerScope:
-        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.pumpAndSettle(inspectorChangeSettleTime);
-        await tester.tap(find.byIcon(Icons.close));
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-        // Confirm the HeroControllerScope 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',
-          ),
-        );
-      },
-    );
-  });
-
-  testWidgetsWithWindowSize(
-    'hide all implementation widgets',
-    windowSize,
-    (WidgetTester tester) async {
-      await _loadInspectorUI(tester);
-
-      // Give time for the initial animation to complete.
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Confirm the hidden widgets are visible behind affordances like "X more
-      // widgets".
-      expect(
-        find.richTextContaining('more widgets...'),
-        findsWidgets,
-      );
-
-      // Tap the "Show Implementation Widgets" button (selected by default).
-      final showImplementationWidgetsButton = find.descendant(
-        of: find.byType(DevToolsToggleButton),
-        matching: find.text('Show Implementation Widgets'),
-      );
-      expect(showImplementationWidgetsButton, findsOneWidget);
-      await tester.tap(showImplementationWidgetsButton);
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Confirm that the hidden widgets are no longer visible.
-      expect(find.richTextContaining('more widgets...'), findsNothing);
-      await expectLater(
-        find.byType(InspectorScreenBody),
-        matchesDevToolsGolden(
-          '../test_infra/goldens/integration_inspector_v2_implementation_widgets_hidden.png',
-        ),
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'can revert to legacy inspector',
-    windowSize,
-    (WidgetTester tester) async {
-      await _loadInspectorUI(tester);
-
-      // Select the Center widget (row index #16)
-      await tester.tap(find.richText('Center'));
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Disable Inspector V2:
-      await toggleV2Inspector(tester);
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Verify the legacy inspector is visible:
-      await expectLater(
-        find.byType(legacy.InspectorScreenBody),
-        matchesDevToolsGolden(
-          '../test_infra/goldens/integration_inspector_v2_revert_to_legacy.png',
-        ),
-      );
-    },
-  );
-
-  // Test to verify https://github.com/flutter/devtools/issues/8487 is fixed.
-  testWidgetsWithWindowSize(
-    'revert to legacy inspector, hot-restart, and back to new inspector',
-    windowSize,
-    (WidgetTester tester) async {
-      await _loadInspectorUI(tester);
-
-      // Disable Inspector V2.
-      await toggleV2Inspector(tester);
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Verify the legacy inspector is visible.
-      expect(find.richTextContaining('Widget Details Tree'), findsOneWidget);
-
-      // Trigger a hot restart.
-      await env.flutter!.hotRestart();
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Enable Inspector V2.
-      await toggleV2Inspector(tester);
-      await tester.pumpAndSettle(inspectorChangeSettleTime);
-
-      // Verify the legacy inspector is not visible.
-      expect(find.richTextContaining('Widget Details Tree'), findsNothing);
-
-      // Wait for the widget tree to load.
-      final centerWidgetFinder = find.richText('Center');
-      final centerWidgetFinderWithRetries = await retryUntilFound(
-        centerWidgetFinder,
-        tester: tester,
-        retries: 10,
-      );
-      expect(centerWidgetFinderWithRetries, findsOneWidget);
-    },
-    skip: true, // https://github.com/flutter/devtools/issues/8490
-  );
-
-  group('widget errors', () {
-    testWidgetsWithWindowSize(
-      'show navigator and error labels',
-      windowSize,
-      (WidgetTester tester) async {
-        await env.setupEnvironment(
-          config: const FlutterRunConfiguration(
-            withDebugger: true,
-            entryScript: 'lib/overflow_errors.dart',
-          ),
-        );
-        expect(serviceConnection.serviceManager.service, equals(env.service));
-        expect(serviceConnection.serviceManager.isolateManager, isNotNull);
-
-        final screen = InspectorScreen();
-        await tester.pumpWidget(
-          wrapWithInspectorControllers(
-            Builder(builder: screen.build),
-          ),
-        );
-        await tester.pumpAndSettle(const Duration(seconds: 1));
-        await _waitForFlutterFrame(tester);
-
-        await env.flutter!.hotReload();
-        // Give time for the initial animation to complete.
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_errors_1_initial_load.png',
-          ),
-        );
-
-        // Navigate so one of the errors is selected.
-        for (var i = 0; i < 2; i++) {
-          await tester.tap(find.byIcon(Icons.keyboard_arrow_down));
-          await tester.pumpAndSettle(inspectorChangeSettleTime);
-        }
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_v2_errors_2_error_selected.png',
-          ),
-        );
-      },
-    );
-  });
-}
-
-Future<void> _loadInspectorUI(WidgetTester tester) async {
-  final screen = InspectorScreen();
-  await tester.pumpWidget(
-    wrapWithInspectorControllers(
-      Builder(builder: screen.build),
-    ),
-  );
-  await tester.pump(const Duration(seconds: 1));
-  await _waitForFlutterFrame(tester);
-
-  await tester.pumpAndSettle(inspectorChangeSettleTime);
-}
-
-Future<void> _waitForFlutterFrame(
-  WidgetTester tester, {
-  bool isInitialLoad = true,
-}) async {
-  final state = tester.state(find.byType(InspectorScreenBody))
-      as InspectorScreenBodyState;
-  final controller = state.controller;
-  while (!controller.flutterAppFrameReady) {
-    // On the initial load, we might have instantiated the controller after the
-    // first Flutter frame was sent. In which case, calling `maybeLoadUI` is
-    // necessary to ensure we detect that the widget tree is ready.
-    if (isInitialLoad) {
-      await controller.maybeLoadUI();
-    }
-    await tester.pump(safePumpDuration);
-  }
-}
-
-Finder findNodeMatching(String text) => find.ancestor(
-      of: find.richTextContaining(text),
-      matching: find.byType(DescriptionDisplay),
-    );
-
-Finder findExpandCollapseButtonForNode({
-  required String nodeDescription,
-  required bool isExpand,
-}) {
-  final hiddenNodeFinder = findNodeMatching(nodeDescription);
-  expect(hiddenNodeFinder, findsOneWidget);
-
-  final expandCollapseButtonFinder = find.descendant(
-    of: hiddenNodeFinder,
-    matching: find.byType(TextButton),
-  );
-  expect(expandCollapseButtonFinder, findsOneWidget);
-
-  final expandCollapseButtonTextFinder = find.descendant(
-    of: expandCollapseButtonFinder,
-    matching: find.text(isExpand ? '(expand)' : '(collapse)'),
-  );
-  expect(expandCollapseButtonTextFinder, findsOneWidget);
-
-  return expandCollapseButtonFinder;
-}
-
-Future<void> toggleV2Inspector(WidgetTester tester) async {
-  final inspectorSwitch = find.byType(DevToolsSwitch);
-  expect(inspectorSwitch, findsOneWidget);
-  await tester.tap(inspectorSwitch);
-}
-
-void verifyPropertyIsVisible({
-  required String name,
-  required String value,
-  required WidgetTester tester,
-}) {
-  // Verify the property name is visible:
-  final propertyNameFinder = find.descendant(
-    of: find.byType(PropertyName),
-    matching: find.text(name),
-  );
-  expect(propertyNameFinder, findsOneWidget);
-
-  // Verify the property value is visible:
-  final propertyValueFinder = find.descendant(
-    of: find.byType(PropertyValue),
-    matching: find.richText(value),
-  );
-  expect(propertyValueFinder, findsOneWidget);
-
-  // Verify the property name and value are aligned:
-  final propertyNameCenter = tester.getCenter(propertyNameFinder);
-  final propertyValueCenter = tester.getCenter(propertyValueFinder);
-  expect(propertyNameCenter.dy, equals(propertyValueCenter.dy));
-}
-
-bool areHorizontallyAligned(
-  Finder widgetAFinder,
-  Finder widgetBFinder, {
-  required WidgetTester tester,
-}) {
-  final widgetACenter = tester.getCenter(widgetAFinder);
-  final widgetBCenter = tester.getCenter(widgetBFinder);
-
-  return widgetACenter.dy == widgetBCenter.dy;
-}
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/arrow_test.dart b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/arrow_test.dart
deleted file mode 100644
index e7aeeb5..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/arrow_test.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/screens/inspector_v2/layout_explorer/ui/arrow.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../../test_infra/matchers/matchers.dart';
-
-void main() {
-  group('Arrow Golden Tests', () {
-    group('Unidirectional', () {
-      Widget buildUnidirectionalArrowWrapper(ArrowType type) => Directionality(
-            textDirection: TextDirection.ltr,
-            child: SizedBox(
-              width: 100,
-              height: 100,
-              child: ArrowWrapper.unidirectional(
-                type: type,
-                arrowColor: Colors.black,
-                arrowHeadSize: 8.0,
-                child: Container(
-                  width: 10,
-                  height: 10,
-                  color: Colors.red,
-                ),
-              ),
-            ),
-          );
-      testWidgets(
-        'left',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.left);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_left.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'up',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.up);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_up.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'right',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.right);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_right.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'down',
-        (WidgetTester tester) async {
-          final widget = buildUnidirectionalArrowWrapper(ArrowType.down);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_unidirectional_down.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-    });
-
-    group('Bidirectional', () {
-      Widget buildBidirectionalArrowWrapper(Axis direction) => Directionality(
-            textDirection: TextDirection.ltr,
-            child: SizedBox(
-              width: 100,
-              height: 100,
-              child: ArrowWrapper.bidirectional(
-                direction: direction,
-                arrowColor: Colors.black,
-                arrowHeadSize: 8.0,
-                child: Container(
-                  width: 10,
-                  height: 10,
-                  color: Colors.red,
-                ),
-              ),
-            ),
-          );
-      testWidgets(
-        'horizontal',
-        (WidgetTester tester) async {
-          final widget = buildBidirectionalArrowWrapper(Axis.horizontal);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_bidirectional_horizontal.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-      testWidgets(
-        'vertical',
-        (WidgetTester tester) async {
-          final widget = buildBidirectionalArrowWrapper(Axis.vertical);
-          await tester.pumpWidget(widget);
-          await expectLater(
-            find.byWidget(widget),
-            matchesDevToolsGolden('goldens/arrow_bidirectional_vertical.png'),
-          );
-        },
-        skip: kIsWeb,
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_horizontal.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_horizontal.png
deleted file mode 100644
index d4db9f0..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_horizontal.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_vertical.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_vertical.png
deleted file mode 100644
index dd855d2..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_bidirectional_vertical.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_down.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_down.png
deleted file mode 100644
index 6e864b5..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_down.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_left.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_left.png
deleted file mode 100644
index ad28c36..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_left.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_right.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_right.png
deleted file mode 100644
index aca9379..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_right.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_up.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_up.png
deleted file mode 100644
index d3f2615..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/arrow_unidirectional_up.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_column_layout.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_column_layout.png
deleted file mode 100644
index b900850..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_column_layout.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_row_layout.png b/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_row_layout.png
deleted file mode 100644
index a2ac431..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/flex/goldens/story_of_row_layout.png
+++ /dev/null
Binary files differ
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_serialization_delegate.dart b/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_serialization_delegate.dart
deleted file mode 100644
index 0a38fd1..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_serialization_delegate.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/rendering.dart';
-import 'package:flutter/widgets.dart';
-
-/// This class is merely a duplicate of the InspectorSerializationDelegate inside the `assets/scripts/inspector_polyfill_script.dart`.
-/// This delegate is used for testing Widget Diagnostics Node so that we don't have to create manual JSON each time we want to create new test cases.
-/// TODO(adalberht): Ask Jacob on what's the better solution on code reuse between the Layout Explorer polyfillscripts and the code inside test package?
-class LayoutExplorerSerializationDelegate
-    extends InspectorSerializationDelegate {
-  LayoutExplorerSerializationDelegate({
-    String super.groupName = '',
-    super.subtreeDepth,
-    required super.service,
-  }) : super(
-          summaryTree: true,
-          addAdditionalPropertiesCallback: (node, delegate) {
-            final additionalJson = <String, Object>{};
-            final value = node.value;
-            if (value is Element) {
-              final renderObject = value.renderObject!;
-              additionalJson['renderObject'] =
-                  renderObject.toDiagnosticsNode().toJsonMap(
-                        delegate.copyWith(
-                          subtreeDepth: 0,
-                          includeProperties: true,
-                        ),
-                      );
-              // Required for test.
-              // ignore: invalid_use_of_protected_member
-              final constraints = renderObject.constraints;
-
-              final constraintsProperty = <String, Object>{
-                'type': constraints.runtimeType.toString(),
-                'description': constraints.toString(),
-              };
-              if (constraints is BoxConstraints) {
-                constraintsProperty.addAll(<String, Object>{
-                  'minWidth': constraints.minWidth.toString(),
-                  'minHeight': constraints.minHeight.toString(),
-                  'maxWidth': constraints.maxWidth.toString(),
-                  'maxHeight': constraints.maxHeight.toString(),
-                });
-              }
-              additionalJson['constraints'] = constraintsProperty;
-
-              if (renderObject is RenderBox) {
-                additionalJson['size'] = <String, Object>{
-                  'width': renderObject.size.width.toString(),
-                  'height': renderObject.size.height.toString(),
-                };
-
-                final parentData = renderObject.parentData;
-                if (parentData is FlexParentData) {
-                  additionalJson['flexFactor'] = parentData.flex ?? 0;
-                  additionalJson['flexFit'] =
-                      (parentData.fit ?? FlexFit.tight).name;
-                }
-              }
-            }
-            return additionalJson;
-          },
-        );
-}
diff --git a/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_test_utils.dart b/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_test_utils.dart
deleted file mode 100644
index 6d7ac94..0000000
--- a/packages/devtools_app/test/inspector_v2/layout_explorer/layout_explorer_test_utils.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/shared/diagnostics/diagnostics_node.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'layout_explorer_serialization_delegate.dart';
-
-Future<RemoteDiagnosticsNode> widgetToLayoutExplorerRemoteDiagnosticsNode({
-  required Widget widget,
-  required WidgetTester tester,
-  int subtreeDepth = 1,
-}) async {
-  await tester.pumpWidget(MaterialApp(home: Scaffold(body: widget)));
-  final element = find.byWidget(widget).evaluate().first;
-  final nodeJson =
-      element.toDiagnosticsNode(style: DiagnosticsTreeStyle.dense).toJsonMap(
-            LayoutExplorerSerializationDelegate(
-              subtreeDepth: subtreeDepth,
-              service: WidgetInspectorService.instance,
-            ),
-          );
-  return RemoteDiagnosticsNode(nodeJson, null, false, null);
-}
diff --git a/packages/devtools_app/test/inspector_v2/utils/inspector_tree.dart b/packages/devtools_app/test/inspector_v2/utils/inspector_tree.dart
deleted file mode 100644
index 86600d4..0000000
--- a/packages/devtools_app/test/inspector_v2/utils/inspector_tree.dart
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart'
-    hide InspectorTreeController, InspectorTreeConfig, InspectorTreeNode;
-import 'package:devtools_app/src/screens/inspector_v2/inspector_tree_controller.dart';
-import 'package:devtools_app/src/shared/console/eval/inspector_tree_v2.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/rendering.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-/// Create an `InspectorTreeControllerFlutter` from a single `RemoteDiagnosticsNode`
-InspectorTreeController inspectorTreeControllerFromNode(
-  RemoteDiagnosticsNode node,
-) {
-  final controller = InspectorTreeController()
-    ..config = InspectorTreeConfig(
-      onNodeAdded: (_, __) {},
-      onClientActiveChange: (_) {},
-    );
-
-  controller.root = InspectorTreeNode()
-    ..appendChild(
-      InspectorTreeNode()..diagnostic = node,
-    );
-
-  return controller;
-}
-
-/// Replicates the functionality of `getRootWidgetSummaryTreeWithPreviews` from
-/// inspector_polyfill_script.dart
-Future<RemoteDiagnosticsNode> widgetToInspectorTreeDiagnosticsNode({
-  required Widget widget,
-  required WidgetTester tester,
-}) async {
-  await tester.pumpWidget(wrap(widget));
-  final element = find.byWidget(widget).evaluate().first;
-  final nodeJson =
-      element.toDiagnosticsNode(style: DiagnosticsTreeStyle.dense).toJsonMap(
-            InspectorSerializationDelegate(
-              service: WidgetInspectorService.instance,
-              subtreeDepth: 1000000,
-              summaryTree: true,
-              addAdditionalPropertiesCallback: (node, delegate) {
-                final additionalJson = <String, Object>{};
-
-                final value = node.value;
-                if (value is Element) {
-                  final renderObject = value.renderObject;
-                  if (renderObject is RenderParagraph) {
-                    additionalJson['textPreview'] =
-                        renderObject.text.toPlainText();
-                  }
-                }
-
-                return additionalJson;
-              },
-            ),
-          );
-
-  return RemoteDiagnosticsNode(nodeJson, null, false, null);
-}
diff --git a/packages/devtools_app/test/integration_test_infra/in_file_args_test.dart b/packages/devtools_app/test/integration_test_infra/in_file_args_test.dart
index 867a614..964316b 100644
--- a/packages/devtools_app/test/integration_test_infra/in_file_args_test.dart
+++ b/packages/devtools_app/test/integration_test_infra/in_file_args_test.dart
@@ -24,25 +24,19 @@
     name: 'empty',
     input: '',
     testAppDevice: TestAppDevice.flutterTester,
-    output: TestFileArgs.parse(
-      {
-        TestFileArgItems.experimentsOn: _defaultArgs.experimentsOn,
-        TestFileArgItems.appPath: _defaultArgs.appPath,
-      },
-      testAppDevice: TestAppDevice.flutterTester,
-    ),
+    output: TestFileArgs.parse({
+      TestFileArgItems.experimentsOn: _defaultArgs.experimentsOn,
+      TestFileArgItems.appPath: _defaultArgs.appPath,
+    }, testAppDevice: TestAppDevice.flutterTester),
   ),
   _InFileTestArgsTest(
     name: 'empty',
     input: '',
     testAppDevice: TestAppDevice.cli,
-    output: TestFileArgs.parse(
-      {
-        TestFileArgItems.experimentsOn: _defaultArgsForCliDevice.experimentsOn,
-        TestFileArgItems.appPath: _defaultArgsForCliDevice.appPath,
-      },
-      testAppDevice: TestAppDevice.cli,
-    ),
+    output: TestFileArgs.parse({
+      TestFileArgItems.experimentsOn: _defaultArgsForCliDevice.experimentsOn,
+      TestFileArgItems.appPath: _defaultArgsForCliDevice.appPath,
+    }, testAppDevice: TestAppDevice.cli),
   ),
   _InFileTestArgsTest(
     name: 'non-empty',
@@ -59,13 +53,10 @@
 import 'dart:ui' as ui;
 ''',
     testAppDevice: TestAppDevice.flutterTester,
-    output: TestFileArgs.parse(
-      {
-        TestFileArgItems.experimentsOn: true,
-        TestFileArgItems.appPath: _testAppPath,
-      },
-      testAppDevice: TestAppDevice.flutterTester,
-    ),
+    output: TestFileArgs.parse({
+      TestFileArgItems.experimentsOn: true,
+      TestFileArgItems.appPath: _testAppPath,
+    }, testAppDevice: TestAppDevice.flutterTester),
   ),
 ];
 
diff --git a/packages/devtools_app/test/legacy_integration_tests/app.dart b/packages/devtools_app/test/legacy_integration_tests/app.dart
index 4d96fdf..22f039d 100644
--- a/packages/devtools_app/test/legacy_integration_tests/app.dart
+++ b/packages/devtools_app/test/legacy_integration_tests/app.dart
@@ -12,8 +12,9 @@
   late BrowserTabInstance tabInstance;
 
   setUp(() async {
-    appFixture =
-        await CliAppFixture.create('test/test_infra/fixtures/logging_app.dart');
+    appFixture = await CliAppFixture.create(
+      'test/test_infra/fixtures/logging_app.dart',
+    );
     tabInstance = await browserManager.createNewTab();
   });
 
diff --git a/packages/devtools_app/test/legacy_integration_tests/debugger.dart b/packages/devtools_app/test/legacy_integration_tests/debugger.dart
index d64229d..e428751 100644
--- a/packages/devtools_app/test/legacy_integration_tests/debugger.dart
+++ b/packages/devtools_app/test/legacy_integration_tests/debugger.dart
@@ -357,8 +357,9 @@
   }
 
   Future<String?> getConsoleContents() async {
-    final response =
-        await tools.tabInstance.send('debugger.getConsoleContents');
+    final response = await tools.tabInstance.send(
+      'debugger.getConsoleContents',
+    );
     return response.result as String?;
   }
 
@@ -392,8 +393,9 @@
   }
 
   Future<List<String>> getCallStackFrames() async {
-    final response =
-        await tools.tabInstance.send('debugger.getCallStackFrames');
+    final response = await tools.tabInstance.send(
+      'debugger.getCallStackFrames',
+    );
     final result = response.result as List<Object?>;
     return result.cast<String>();
   }
diff --git a/packages/devtools_app/test/legacy_integration_tests/integration.dart b/packages/devtools_app/test/legacy_integration_tests/integration.dart
index 68fe9dc..f43d960 100644
--- a/packages/devtools_app/test/legacy_integration_tests/integration.dart
+++ b/packages/devtools_app/test/legacy_integration_tests/integration.dart
@@ -41,8 +41,9 @@
     // wait for app initialization
     await Future.wait([
       waitForConnection
-          ? tabInstance.onEvent
-              .firstWhere((msg) => msg.event == 'app.devToolsReady')
+          ? tabInstance.onEvent.firstWhere(
+            (msg) => msg.event == 'app.devToolsReady',
+          )
           : Future<void>.value(),
       tabInstance.getBrowserChannel(),
     ]);
@@ -105,21 +106,21 @@
     tab.onConsoleAPICalled
         .where((ConsoleAPIEvent event) => event.type == 'log')
         .listen((ConsoleAPIEvent event) {
-      if (event.args.isNotEmpty) {
-        final message = event.args.first;
-        final value = '${message.value}';
-        if (value.startsWith('[') && value.endsWith(']')) {
-          try {
-            final msg = jsonDecode(value.substring(1, value.length - 1));
-            if (msg is Map) {
-              _handleBrowserMessage(msg);
+          if (event.args.isNotEmpty) {
+            final message = event.args.first;
+            final value = '${message.value}';
+            if (value.startsWith('[') && value.endsWith(']')) {
+              try {
+                final msg = jsonDecode(value.substring(1, value.length - 1));
+                if (msg is Map) {
+                  _handleBrowserMessage(msg);
+                }
+              } catch (_) {
+                // ignore
+              }
             }
-          } catch (_) {
-            // ignore
           }
-        }
-      }
-    });
+        });
   }
 
   final ChromeTab tab;
@@ -184,8 +185,9 @@
     // In Headless Chrome, we get Inspector.detached when we close the last
     // target rather than a response.
     await Future.any(<Future<Object>>[
-      tab.wipConnection!.onNotification
-          .firstWhere((n) => n.method == 'Inspector.detached'),
+      tab.wipConnection!.onNotification.firstWhere(
+        (n) => n.method == 'Inspector.detached',
+      ),
       tab.wipConnection!.target.closeTarget(tab.wipTab.id),
     ]);
   }
@@ -318,9 +320,7 @@
     return WebBuildFixture._(process, url, verbose);
   }
 
-  static Future<void> build({
-    bool verbose = false,
-  }) async {
+  static Future<void> build({bool verbose = false}) async {
     final clean = await _runFlutter(['clean']);
     expect(await clean.exitCode, 0);
     final pubGet = await _runFlutter(['pub', 'get']);
diff --git a/packages/devtools_app/test/legacy_integration_tests/logging.dart b/packages/devtools_app/test/legacy_integration_tests/logging.dart
index e773131..c4f9099 100644
--- a/packages/devtools_app/test/legacy_integration_tests/logging.dart
+++ b/packages/devtools_app/test/legacy_integration_tests/logging.dart
@@ -12,8 +12,9 @@
   late BrowserTabInstance tabInstance;
 
   setUp(() async {
-    appFixture =
-        await CliAppFixture.create('test/test_infra/fixtures/logging_app.dart');
+    appFixture = await CliAppFixture.create(
+      'test/test_infra/fixtures/logging_app.dart',
+    );
     tabInstance = await browserManager.createNewTab();
   });
 
diff --git a/packages/devtools_app/test/logging/logging_controller_test.dart b/packages/devtools_app/test/logging/logging_controller_test.dart
deleted file mode 100644
index 8fd1ea2..0000000
--- a/packages/devtools_app/test/logging/logging_controller_test.dart
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm')
-library;
-
-import 'dart:convert';
-
-import 'package:devtools_app/src/screens/logging/logging_controller.dart';
-import 'package:devtools_app/src/service/service_manager.dart';
-import 'package:devtools_app/src/shared/primitives/message_bus.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  group('LoggingController', () {
-    late LoggingController controller;
-    setGlobal(MessageBus, MessageBus());
-
-    void addStdoutData(String message) {
-      controller.log(
-        LogData(
-          'stdout',
-          jsonEncode({'kind': 'stdout', 'message': message}),
-          0,
-          summary: message,
-        ),
-      );
-    }
-
-    void addGcData(String message) {
-      controller.log(
-        LogData(
-          'gc',
-          jsonEncode({'kind': 'gc', 'message': message}),
-          0,
-          summary: message,
-        ),
-      );
-    }
-
-    void addLogWithKind(String kind) {
-      controller.log(LogData(kind, jsonEncode({'foo': 'test_data'}), 0));
-    }
-
-    setUp(() {
-      setGlobal(
-        ServiceConnectionManager,
-        FakeServiceConnectionManager(),
-      );
-
-      controller = LoggingController();
-    });
-
-    test('initial state', () {
-      expect(controller.data, isEmpty);
-      expect(controller.filteredData.value, isEmpty);
-      expect(controller.activeFilter.value.isEmpty, isFalse);
-    });
-
-    test('receives data', () {
-      expect(controller.data, isEmpty);
-
-      addStdoutData('Abc.');
-
-      expect(controller.data, isNotEmpty);
-      expect(controller.filteredData.value, isNotEmpty);
-
-      expect(controller.data.first.summary, contains('Abc'));
-    });
-
-    test('clear', () {
-      addStdoutData('Abc.');
-
-      expect(controller.data, isNotEmpty);
-      expect(controller.filteredData.value, isNotEmpty);
-
-      controller.clear();
-
-      expect(controller.data, isEmpty);
-      expect(controller.filteredData.value, isEmpty);
-    });
-
-    test('matchesForSearch', () {
-      addStdoutData('abc');
-      addStdoutData('def');
-      addStdoutData('abc ghi');
-      addLogWithKind('Flutter.Navigation');
-      addLogWithKind('Flutter.Error');
-      addGcData('gc1');
-      addGcData('gc2');
-
-      expect(controller.filteredData.value, hasLength(5));
-      expect(controller.matchesForSearch('abc').length, equals(2));
-      expect(controller.matchesForSearch('ghi').length, equals(1));
-      expect(controller.matchesForSearch('abcd').length, equals(0));
-      expect(controller.matchesForSearch('Flutter*').length, equals(2));
-      expect(controller.matchesForSearch('').length, equals(0));
-
-      // Search by event kind.
-      expect(controller.matchesForSearch('stdout').length, equals(3));
-      expect(controller.matchesForSearch('flutter.*').length, equals(2));
-
-      // Search with incorrect case.
-      expect(controller.matchesForSearch('STDOUT').length, equals(3));
-    });
-
-    test('matchesForSearch sets isSearchMatch property', () {
-      addStdoutData('abc');
-      addStdoutData('def');
-      addStdoutData('abc ghi');
-      addLogWithKind('Flutter.Navigation');
-      addLogWithKind('Flutter.Error');
-      addGcData('gc1');
-      addGcData('gc2');
-
-      expect(controller.filteredData.value, hasLength(5));
-      controller.search = 'abc';
-      var matches = controller.searchMatches.value;
-      expect(matches.length, equals(2));
-      verifyIsSearchMatch(controller.filteredData.value, matches);
-
-      controller.search = 'Flutter.';
-      matches = controller.searchMatches.value;
-      expect(matches.length, equals(2));
-      verifyIsSearchMatch(controller.filteredData.value, matches);
-    });
-
-    test('filterData', () {
-      addStdoutData('abc');
-      addStdoutData('def');
-      addStdoutData('abc ghi');
-      addLogWithKind('Flutter.Navigation');
-      addLogWithKind('Flutter.Error');
-
-      // The following logs should all be filtered by default.
-      addGcData('gc1');
-      addGcData('gc2');
-      addLogWithKind('Flutter.FirstFrame');
-      addLogWithKind('Flutter.FrameworkInitialization');
-      addLogWithKind('Flutter.Frame');
-      addLogWithKind('Flutter.ImageSizesForFrame');
-      addLogWithKind('Flutter.ServiceExtensionStateChanged');
-
-      // At this point data is filtered by the default toggle filter values.
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(5));
-
-      // Test query filters assuming default toggle filters are all enabled.
-      for (final filter in controller.activeFilter.value.toggleFilters) {
-        filter.enabled.value = true;
-      }
-
-      controller.setActiveFilter(query: 'abc');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(2));
-
-      controller.setActiveFilter(query: 'def');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(1));
-
-      controller.setActiveFilter(query: 'abc def');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(3));
-
-      controller.setActiveFilter(query: 'k:stdout');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(3));
-
-      controller.setActiveFilter(query: '-k:stdout');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(2));
-
-      controller.setActiveFilter(query: 'k:stdout abc');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(2));
-
-      controller.setActiveFilter(query: 'k:stdout,flutter.navigation');
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(4));
-
-      controller.setActiveFilter();
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(5));
-
-      // Test toggle filters.
-      final verboseFlutterFrameworkFilter =
-          controller.activeFilter.value.toggleFilters[0];
-      final verboseFlutterServiceFilter =
-          controller.activeFilter.value.toggleFilters[1];
-      final gcFilter = controller.activeFilter.value.toggleFilters[2];
-
-      verboseFlutterFrameworkFilter.enabled.value = false;
-      controller.setActiveFilter();
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(9));
-
-      verboseFlutterServiceFilter.enabled.value = false;
-      controller.setActiveFilter();
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(10));
-
-      gcFilter.enabled.value = false;
-      controller.setActiveFilter();
-      expect(controller.data, hasLength(12));
-      expect(controller.filteredData.value, hasLength(12));
-    });
-  });
-
-  group('LogData', () {
-    test(
-      'pretty prints when details are json, and returns its details otherwise.',
-      () {
-        final nonJson = LogData('some kind', 'Not json', 0);
-        final json = LogData(
-          'some kind',
-          '{"firstValue": "value", "otherValue": "value2"}',
-          1,
-        );
-        final nullDetails = LogData('some kind', null, 1);
-        const prettyJson = '{\n'
-            '  "firstValue": "value",\n'
-            '  "otherValue": "value2"\n'
-            '}';
-
-        expect(json.prettyPrinted(), prettyJson);
-        expect(nonJson.prettyPrinted(), 'Not json');
-        expect(nullDetails.prettyPrinted(), null);
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_data_test.dart b/packages/devtools_app/test/logging/logging_screen_data_test.dart
deleted file mode 100644
index 09b480a..0000000
--- a/packages/devtools_app/test/logging/logging_screen_data_test.dart
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm')
-library;
-
-import 'package:ansicolor/ansicolor.dart';
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/logging/_log_details.dart';
-import 'package:devtools_app/src/screens/logging/_logs_table.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-void main() {
-  late MockLoggingController mockLoggingController;
-  const windowSize = Size(1000.0, 1000.0);
-  final fakeServiceConnection = FakeServiceConnectionManager();
-
-  Future<void> pumpLoggingScreen(WidgetTester tester) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const LoggingScreenBody(),
-        logging: mockLoggingController,
-      ),
-    );
-  }
-
-  setUp(() {
-    mockLoggingController =
-        createMockLoggingControllerWithDefaults(data: fakeLogData);
-
-    when(fakeServiceConnection.serviceManager.connectedApp!.isFlutterWebAppNow)
-        .thenReturn(false);
-    when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-        .thenReturn(false);
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('logging'))
-        .thenReturn(ValueNotifier<int>(0));
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(IdeTheme, IdeTheme());
-  });
-
-  testWidgetsWithWindowSize(
-    'shows log items',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpLoggingScreen(tester);
-      await tester.pumpAndSettle();
-      expect(find.byType(LogsTable), findsOneWidget);
-      expect(
-        find.byKey(ValueKey(fakeLogData.first)),
-        findsOneWidget,
-      );
-      expect(
-        find.byKey(ValueKey(fakeLogData.last)),
-        findsOneWidget,
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'can show non-computing log data',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpLoggingScreen(tester);
-      await tester.pumpAndSettle();
-      await tester.tap(find.byKey(ValueKey(fakeLogData[6])));
-      await tester.pumpAndSettle();
-      expect(
-        find.selectableText('log event 6'),
-        findsOneWidget,
-        reason:
-            'The log details should be visible both in the details section.',
-      );
-      expect(
-        find.selectableText('log event 6'),
-        findsOneWidget,
-        reason: 'The log details should be visible both in the table.',
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'can show null log data',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpLoggingScreen(tester);
-      await tester.pumpAndSettle();
-      await tester.tap(find.byKey(ValueKey(fakeLogData[7])));
-      await tester.pumpAndSettle();
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'search field can enter text',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpLoggingScreen(tester);
-      verifyNever(mockLoggingController.clear());
-
-      final textFieldFinder = find.byType(TextField);
-      expect(textFieldFinder, findsOneWidget);
-      final textField = tester.widget(textFieldFinder) as TextField;
-      expect(textField.enabled, isTrue);
-      await tester.enterText(find.byType(TextField), 'abc');
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'Copy to clipboard button enables/disables correctly',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpLoggingScreen(tester);
-
-      // Locates the copy to clipboard button's IconButton.
-      ToolbarAction copyButton() => find
-          .byKey(LogDetails.copyToClipboardButtonKey)
-          .evaluate()
-          .first
-          .widget as ToolbarAction;
-
-      expect(
-        copyButton().onPressed,
-        isNull,
-        reason:
-            'Copy to clipboard button should be disabled when no logs are selected',
-      );
-
-      await tester.tap(find.byKey(ValueKey(fakeLogData[5])));
-      await tester.pumpAndSettle();
-
-      expect(
-        copyButton().onPressed,
-        isNotNull,
-        reason:
-            'Copy to clipboard button should be enabled when a log with content is selected',
-      );
-
-      await tester.tap(find.byKey(ValueKey(fakeLogData[7])));
-      await tester.pumpAndSettle();
-
-      expect(
-        copyButton().onPressed,
-        isNull,
-        reason:
-            'Copy to clipboard button should be disabled when the log details are null',
-      );
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'can compute details of non-json log data',
-    windowSize,
-    (WidgetTester tester) async {
-      const index = 8;
-      final log = fakeLogData[index];
-
-      await pumpLoggingScreen(tester);
-      await tester.pumpAndSettle();
-      await tester.tap(find.byKey(ValueKey(log)));
-      await tester.pump();
-      expect(
-        find.selectableText(nonJsonOutput),
-        findsNothing,
-        reason:
-            "The details of the log haven't computed yet, so they shouldn't "
-            'be available.',
-      );
-
-      await tester.pumpAndSettle();
-      expect(find.selectableText(nonJsonOutput), findsOneWidget);
-    },
-  );
-
-  testWidgetsWithWindowSize(
-    'can show details of json log data',
-    windowSize,
-    (WidgetTester tester) async {
-      const index = 9;
-      bool containsJson(Widget widget) {
-        if (widget is! SelectableText) return false;
-        final content = widget.data!.trim();
-        return content.startsWith('{') && content.endsWith('}');
-      }
-
-      final findJson = find.descendant(
-        of: find.byType(LogDetails),
-        matching: find.byWidgetPredicate(containsJson),
-      );
-
-      await pumpLoggingScreen(tester);
-      await tester.pumpAndSettle();
-      await tester.tap(find.byKey(ValueKey(fakeLogData[index])));
-      await tester.pump();
-
-      expect(
-        findJson,
-        findsNothing,
-        reason:
-            "The details of the log haven't computed yet, so they shouldn't be available.",
-      );
-
-      await tester.pumpAndSettle();
-      expect(findJson, findsOneWidget);
-    },
-  );
-}
-
-const totalLogs = 10;
-
-final fakeLogData = List<LogData>.generate(totalLogs, _generate);
-
-LogData _generate(int i) {
-  String? details = 'log event $i';
-  String kind = 'kind $i';
-  String? computedDetails;
-  switch (i) {
-    case 9:
-      computedDetails = jsonOutput;
-      break;
-    case 8:
-      computedDetails = nonJsonOutput;
-      break;
-    case 7:
-      details = null;
-      break;
-    case 5:
-      kind = 'stdout';
-      details = _ansiCodesOutput();
-      break;
-    default:
-      break;
-  }
-
-  final detailsComputer = computedDetails == null
-      ? null
-      : () =>
-          Future.delayed(const Duration(seconds: 1), () => computedDetails!);
-  return LogData(kind, details, i, detailsComputer: detailsComputer);
-}
-
-const nonJsonOutput = 'Non-json details for log number 8';
-const jsonOutput = '{\n"Details": "of log event 9",\n"logEvent": "9"\n}\n';
-
-String _ansiCodesOutput() {
-  final sb = StringBuffer();
-  sb.write('Ansi color codes processed for ');
-  final pen = AnsiPen()..rgb(r: 0.8, g: 0.3, b: 0.4, bg: true);
-  sb.write(pen('log 5'));
-  return sb.toString();
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_test.dart b/packages/devtools_app/test/logging/logging_screen_test.dart
deleted file mode 100644
index 85bf049..0000000
--- a/packages/devtools_app/test/logging/logging_screen_test.dart
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm')
-library;
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/logging/_log_details.dart';
-import 'package:devtools_app/src/screens/logging/_logs_table.dart';
-import 'package:devtools_app/src/screens/logging/_message_column.dart';
-import 'package:devtools_app/src/service/service_extension_widgets.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-import '../test_infra/utils/test_utils.dart';
-
-void main() {
-  late LoggingScreen screen;
-  late MockLoggingController mockLoggingController;
-  const windowSize = Size(1000.0, 1000.0);
-
-  group('Logging Screen', () {
-    Future<void> pumpLoggingScreen(WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          const LoggingScreenBody(),
-          logging: mockLoggingController,
-        ),
-      );
-    }
-
-    setUp(() {
-      mockLoggingController = createMockLoggingControllerWithDefaults();
-
-      final fakeServiceConnection = FakeServiceConnectionManager();
-      when(
-        fakeServiceConnection.serviceManager.connectedApp!.isFlutterWebAppNow,
-      ).thenReturn(false);
-      when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-          .thenReturn(false);
-      when(
-        fakeServiceConnection.errorBadgeManager.errorCountNotifier('logging'),
-      ).thenReturn(ValueNotifier<int>(0));
-      setGlobal(NotificationService, NotificationService());
-      setGlobal(
-        DevToolsEnvironmentParameters,
-        ExternalDevToolsEnvironmentParameters(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(ServiceConnectionManager, fakeServiceConnection);
-      setGlobal(IdeTheme, IdeTheme());
-
-      screen = LoggingScreen();
-    });
-
-    testWidgets('builds its tab', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(Builder(builder: screen.buildTab)));
-      expect(find.text('Logging'), findsOneWidget);
-    });
-
-    testWidgetsWithWindowSize(
-      'copy log contents',
-      windowSize,
-      (WidgetTester tester) async {
-        final logA = LogData('TEST A', 'Log A', 123);
-        final logB = LogData('TEST B', 'Log B', 124);
-        mockLoggingController = createMockLoggingControllerWithDefaults(
-          data: [
-            logA,
-            logB,
-          ],
-        );
-
-        String clipboardContents = '';
-        setupClipboardCopyListener(
-          clipboardContentsCallback: (contents) {
-            clipboardContents = contents ?? '';
-          },
-        );
-
-        await pumpLoggingScreen(tester);
-        await tester.tap(find.byTooltip('Copy filtered logs'));
-
-        expect(
-          clipboardContents,
-          equals(
-            [
-              '${logA.timestamp} [${logA.kind}] ${logA.details}',
-              '${logB.timestamp} [${logB.kind}] ${logB.details}',
-            ].joinWithTrailing('\n'),
-          ),
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds with no data',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpLoggingScreen(tester);
-        expect(find.byType(LoggingScreenBody), findsOneWidget);
-        expect(find.byType(LogsTable), findsOneWidget);
-        expect(find.byType(LogDetails), findsOneWidget);
-        expect(find.byType(ClearButton), findsOneWidget);
-        expect(find.byType(TextField), findsOneWidget);
-        expect(find.byType(DevToolsFilterButton), findsOneWidget);
-        expect(find.byType(SettingsOutlinedButton), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'can clear logs',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpLoggingScreen(tester);
-        verifyNever(mockLoggingController.clear());
-        await tester.tap(find.byType(ClearButton));
-        verify(mockLoggingController.clear()).called(1);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'search field is disabled with no data',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpLoggingScreen(tester);
-        verifyNever(mockLoggingController.clear());
-
-        final textFieldFinder = find.byType(TextField);
-        expect(textFieldFinder, findsOneWidget);
-        final textField = tester.widget(textFieldFinder) as TextField;
-        expect(textField.enabled, isFalse);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'can toggle structured errors',
-      windowSize,
-      (WidgetTester tester) async {
-        final serviceConnection = FakeServiceConnectionManager();
-        when(serviceConnection.serviceManager.connectedApp!.isFlutterWebAppNow)
-            .thenReturn(false);
-        when(serviceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-            .thenReturn(false);
-        setGlobal(
-          ServiceConnectionManager,
-          serviceConnection,
-        );
-        await pumpLoggingScreen(tester);
-
-        await tester.tap(find.byType(SettingsOutlinedButton));
-        await tester.pump();
-        Switch toggle = tester.widget(
-          find.descendant(
-            of: find.byType(StructuredErrorsToggle),
-            matching: find.byType(Switch),
-          ),
-        );
-        expect(toggle.value, false);
-
-        serviceConnection.serviceManager.serviceExtensionManager
-            .fakeServiceExtensionStateChanged(
-          structuredErrors.extension,
-          'true',
-        );
-        await tester.pumpAndSettle();
-        toggle = tester.widget(find.byType(Switch));
-        expect(toggle.value, true);
-
-        // TODO(djshuckerow): Hook up fake extension state querying.
-      },
-    );
-
-    group('MessageColumn', () {
-      late MessageColumn column;
-
-      setUp(() {
-        column = MessageColumn();
-      });
-
-      test('compare sorts logs correctly', () {
-        final a = LogData('test', 'Hello world', 1);
-        final b = LogData('test', 'Test test test', 1);
-        expect(column.compare(a, b), equals(-1));
-      });
-
-      test('compare special cases sorting for frame logs', () {
-        final a = LogData('flutter.frame', '#9  3.6ms ', 1);
-        final b = LogData('flutter.frame', '#10  3.6ms ', 1);
-        expect(column.compare(a, b), equals(-1));
-
-        // The number of spaces between the frame number and duration as well
-        // as after the duration can be inconsistent. Verify that the regexp
-        // still works.
-        final c = LogData('flutter.frame', '#10 3.6ms', 1);
-        final d = LogData('flutter.frame', '#9  3.6ms ', 1);
-        expect(column.compare(c, d), equals(1));
-
-        final e = LogData('flutter.frame', '#10  3.6ms ', 1);
-        final f = LogData('flutter.frame', '#9foo  3.6ms ', 1);
-        expect(column.compare(e, f), equals(-1));
-
-        final l1 = LogData('flutter.frame', '#2  3.6ms ', 1);
-        final l2 = LogData('flutter.frame', '#2NOTAMATCH  3.6ms ', 1);
-        final l3 = LogData('flutter.frame', '#10  3.6ms ', 1);
-        final l4 = LogData('flutter.frame', '#10NOTAMATCH  3.6ms ', 1);
-        final l5 = LogData('flutter.frame', '#11  3.6ms ', 1);
-        final l6 = LogData('flutter.frame', '#11NOTAMATCH  3.6ms ', 1);
-        final list = [l1, l2, l3, l4, l5, l6];
-        list.sort(column.compare);
-
-        expect(list[0], equals(l1));
-        expect(list[1], equals(l3));
-        expect(list[2], equals(l5));
-        expect(list[3], equals(l4));
-        expect(list[4], equals(l6));
-        expect(list[5], equals(l2));
-      });
-    });
-  });
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_v2/logging_controller_v2_test.dart b/packages/devtools_app/test/logging/logging_screen_v2/logging_controller_v2_test.dart
deleted file mode 100644
index aa94046..0000000
--- a/packages/devtools_app/test/logging/logging_screen_v2/logging_controller_v2_test.dart
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// ignore_for_file: unused_import
-
-@TestOn('vm')
-library;
-
-import 'dart:convert';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/shared/primitives/message_bus.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  group('LoggingControllerV2', () {
-    late LoggingControllerV2 controller;
-
-    void addStdoutData(String message) {
-      controller.log(
-        LogDataV2(
-          'stdout',
-          jsonEncode({'kind': 'stdout', 'message': message}),
-          0,
-          summary: message,
-        ),
-      );
-    }
-
-    setUp(() {
-      setGlobal(
-        ServiceConnectionManager,
-        FakeServiceConnectionManager(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(IdeTheme, getIdeTheme());
-      setGlobal(MessageBus, MessageBus());
-      TestWidgetsFlutterBinding.ensureInitialized();
-      controller = LoggingControllerV2();
-      controller.loggingModel.tableWidth = 100.0;
-    });
-
-    testWidgets('initial state', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(const Text('')));
-      expect(controller.loggingModel.logCount, 0);
-      expect(controller.loggingModel.filteredLogCount, 0);
-      expect(controller.loggingModel.selectedLogCount, 0);
-      expect(
-        controller.loggingModel.activeFilter.value.toggleFilters.length,
-        3,
-      );
-      expect(
-        controller.loggingModel.activeFilter.value.queryFilter.isEmpty,
-        isTrue,
-      );
-    });
-
-    testWidgets('receives data', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(const Text('')));
-      expect(controller.loggingModel.logCount, 0);
-      expect(controller.loggingModel.filteredLogCount, 0);
-      expect(controller.loggingModel.selectedLogCount, 0);
-
-      addStdoutData('Abc.');
-
-      expect(controller.loggingModel.logCount, 1);
-      expect(controller.loggingModel.filteredLogCount, 1);
-      expect(controller.loggingModel.selectedLogCount, 0);
-
-      expect(
-        controller.loggingModel.filteredLogAt(0).summary,
-        contains('Abc'),
-      );
-    });
-
-    testWidgets('clear', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(const Text('')));
-      addStdoutData('Abc.');
-
-      expect(controller.loggingModel.logCount, 1);
-      expect(controller.loggingModel.filteredLogCount, 1);
-
-      controller.clear();
-
-      expect(controller.loggingModel.logCount, 0);
-      expect(controller.loggingModel.filteredLogCount, 0);
-    });
-  });
-
-  group('LogData', () {
-    test(
-      'pretty prints when details are json, and returns its details otherwise.',
-      () {
-        final nonJson = LogData('some kind', 'Not json', 0);
-        final json = LogData(
-          'some kind',
-          '{"firstValue": "value", "otherValue": "value2"}',
-          1,
-        );
-        final nullDetails = LogData('some kind', null, 1);
-        const prettyJson = '{\n'
-            '  "firstValue": "value",\n'
-            '  "otherValue": "value2"\n'
-            '}';
-
-        expect(json.prettyPrinted(), prettyJson);
-        expect(nonJson.prettyPrinted(), 'Not json');
-        expect(nullDetails.prettyPrinted(), null);
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_v2/logging_model_test.dart b/packages/devtools_app/test/logging/logging_screen_v2/logging_model_test.dart
deleted file mode 100644
index fcfff09..0000000
--- a/packages/devtools_app/test/logging/logging_screen_v2/logging_model_test.dart
+++ /dev/null
@@ -1,543 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_controller_v2.dart';
-import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_model.dart';
-import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_table_row.dart';
-import 'package:devtools_app/src/service/service_manager.dart';
-import 'package:devtools_app/src/shared/globals.dart';
-import 'package:devtools_app/src/shared/preferences/preferences.dart';
-import 'package:devtools_app/src/shared/primitives/message_bus.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  late LoggingTableModel loggingTableModel;
-  final log1 = LogDataV2('test', 'The details', 464564);
-
-  Future<void> pumpForContext(WidgetTester tester) async {
-    // A barebones widget is pumped to ensure that a style is available
-    // for the LogTableModel to approximate widget sizes with
-    await tester.pumpWidget(wrap(const Placeholder()));
-  }
-
-  Finder findLogRow(String details) => find.ancestor(
-        of: find.richText(details),
-        matching: find.byType(LoggingTableRow),
-      );
-
-  setUp(() {
-    final fakeServiceConnection = FakeServiceConnectionManager();
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(IdeTheme, getIdeTheme());
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    setGlobal(GlobalKey<NavigatorState>, GlobalKey<NavigatorState>());
-
-    TestWidgetsFlutterBinding.ensureInitialized();
-    loggingTableModel = LoggingTableModel();
-
-    // Set a generic width for all tests
-    loggingTableModel.tableWidth = 500;
-  });
-
-  tearDown(() {
-    loggingTableModel.dispose();
-  });
-
-  group('LoggingModel', () {
-    testWidgets('can add logs', (WidgetTester tester) async {
-      await pumpForContext(tester);
-      expect(loggingTableModel.logCount, 0);
-      expect(loggingTableModel.filteredLogCount, 0);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      loggingTableModel.add(log1);
-
-      expect(loggingTableModel.logCount, 1);
-      expect(loggingTableModel.filteredLogCount, 1);
-      expect(loggingTableModel.selectedLogCount, 0);
-    });
-
-    for (var windowWidth = 300.0; windowWidth <= 550.0; windowWidth += 50.0) {
-      testWidgetsWithWindowSize(
-        'calculate heights correctly for window of width: $windowWidth',
-        Size(windowWidth, 3000),
-        (WidgetTester tester) async {
-          await pumpForContext(tester);
-          final shortLog = LogDataV2('test', 'Some short details', 464564);
-          final longLog = LogDataV2(
-            'test',
-            'A long log, A long log, A long log, A long log, A long log, A long log, A long log, ',
-            464564,
-          );
-          final frameElapsedLog =
-              LogDataV2('frameLog', '{"elapsed": 1000000}', 4684506);
-          double? columnWidth;
-          final frameElapsedKey = GlobalKey();
-
-          await tester.pumpWidget(
-            wrap(
-              LayoutBuilder(
-                builder: (context, constraints) {
-                  columnWidth = constraints.maxWidth;
-                  return Column(
-                    mainAxisSize: MainAxisSize.min,
-                    children: [
-                      LoggingTableRow(
-                        index: 0,
-                        data: shortLog,
-                        isSelected: false,
-                      ),
-                      LoggingTableRow(
-                        index: 1,
-                        data: longLog,
-                        isSelected: false,
-                      ),
-                      LoggingTableRow(
-                        key: frameElapsedKey,
-                        index: 2,
-                        data: frameElapsedLog,
-                        isSelected: false,
-                      ),
-                    ],
-                  );
-                },
-              ),
-            ),
-          );
-
-          await tester.runAsync(() async {
-            // tester.runAsync is needed here to prevent the ChunkWorker
-            // in set tableWidth from blocking in the test. The Future.delayed
-            // in it would hang otherwise.
-            loggingTableModel.tableWidth = columnWidth!;
-          });
-
-          loggingTableModel
-            ..add(shortLog)
-            ..add(longLog)
-            ..add(frameElapsedLog);
-
-          final shortWidgetFinder = findLogRow(shortLog.details!);
-          final longWidgetFinder = findLogRow(longLog.details!);
-          final frameElapsedFinder = find.byKey(frameElapsedKey);
-
-          await tester.runAsync(() async {
-            // tester.runAsync is needed here to prevent the ChunkWorker
-            // in set tableWidth from blocking in the test. The Future.delayed
-            // in it would hang otherwise.
-            loggingTableModel.tableWidth = windowWidth;
-          });
-
-          expect(
-            loggingTableModel.getFilteredLogHeight(0),
-            tester.getSize(shortWidgetFinder).height,
-          );
-          expect(
-            loggingTableModel.getFilteredLogHeight(1),
-            tester.getSize(longWidgetFinder).height,
-          );
-          expect(
-            loggingTableModel.getFilteredLogHeight(2),
-            tester.getSize(frameElapsedFinder).height,
-          );
-        },
-      );
-    }
-
-    testWidgets('Handles log retention', (WidgetTester tester) async {
-      final log1 = LogDataV2('test', 'The details 1', 464564);
-      final log2 = LogDataV2('test', 'The details 2', 464564);
-      final log3 = LogDataV2('test', 'The details 3', 464564);
-
-      await pumpForContext(tester);
-
-      preferences.logging.retentionLimit.value = 2;
-      await tester.pump();
-
-      loggingTableModel
-        ..add(log1)
-        ..add(log2)
-        ..add(log3);
-
-      expect(loggingTableModel.logCount, 2);
-      expect(loggingTableModel.filteredLogCount, 2);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      final completer = Completer<void>();
-      loggingTableModel.addListener(() => completer.complete());
-      preferences.logging.retentionLimit.value = 1;
-      await completer.future;
-
-      expect(loggingTableModel.logCount, 1);
-      expect(loggingTableModel.filteredLogCount, 1);
-      expect(loggingTableModel.selectedLogCount, 0);
-    });
-
-    testWidgets('filters logs', (WidgetTester tester) async {
-      final log1 = LogDataV2('test', 'The details 123', 464564);
-      final log2 = LogDataV2('test', 'The details 456', 464564);
-      final log3 = LogDataV2('test', 'The details 476', 464564);
-
-      await pumpForContext(tester);
-
-      preferences.logging.retentionLimit.value = 20;
-
-      loggingTableModel
-        ..add(log1)
-        ..add(log2)
-        ..add(log3);
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 3);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      // Set the filter
-      loggingTableModel.setActiveFilter(
-        query: '6',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 2);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      // Add a log that does not match to ensure it is filtered out
-      loggingTableModel
-          .add(LogDataV2('test', 'some non-matching details', 45654));
-
-      expect(loggingTableModel.logCount, 4);
-      expect(loggingTableModel.filteredLogCount, 2);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      // Add a log that matches to ensure that it is included
-      loggingTableModel
-          .add(LogDataV2('test', 'some matching details: 6', 45654));
-
-      expect(loggingTableModel.logCount, 5);
-      expect(loggingTableModel.filteredLogCount, 3);
-      expect(loggingTableModel.selectedLogCount, 0);
-    });
-
-    testWidgets('filters by all relevant fields', (WidgetTester tester) async {
-      final log1 = LogDataV2(
-        'test1',
-        'The details 4',
-        464564,
-        summary: 'Summary 7',
-      );
-      final log2 = LogDataV2(
-        'test2',
-        'The details 5',
-        464564,
-        summary: 'Summary 8',
-      );
-      final log3 = LogDataV2(
-        'test3',
-        'The details 6',
-        464564,
-        summary: 'Summary 9',
-      );
-
-      await pumpForContext(tester);
-
-      preferences.logging.retentionLimit.value = 20;
-
-      loggingTableModel
-        ..add(log1)
-        ..add(log2)
-        ..add(log3);
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 3);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      // Check against kind
-      loggingTableModel.setActiveFilter(
-        query: '1',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 1);
-      expect(loggingTableModel.selectedLogCount, 0);
-      expect(loggingTableModel.filteredLogAt(0), equals(log1));
-
-      // Check against details
-      loggingTableModel.setActiveFilter(
-        query: '5',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 1);
-      expect(loggingTableModel.selectedLogCount, 0);
-      expect(loggingTableModel.filteredLogAt(0), equals(log2));
-
-      // Check against summary
-      loggingTableModel.setActiveFilter(
-        query: '9',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 1);
-      expect(loggingTableModel.selectedLogCount, 0);
-      expect(loggingTableModel.filteredLogAt(0), equals(log3));
-    });
-
-    testWidgets('works with regexp', (WidgetTester tester) async {
-      final log1 = LogDataV2(
-        'test1',
-        '456',
-        464564,
-        summary: 'Summary 7',
-      );
-      final log2 = LogDataV2(
-        'test2',
-        '789',
-        464564,
-        summary: 'Summary 8',
-      );
-      final log3 = LogDataV2(
-        'test3',
-        '476',
-        464564,
-        summary: 'Summary 9',
-      );
-
-      await pumpForContext(tester);
-
-      preferences.logging.retentionLimit.value = 20;
-
-      loggingTableModel
-        ..add(log1)
-        ..add(log2)
-        ..add(log3);
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 3);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      loggingTableModel.setActiveFilter(
-        query: '4.6',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 0);
-      expect(loggingTableModel.selectedLogCount, 0);
-
-      loggingTableModel.useRegExp.value = true;
-      loggingTableModel.setActiveFilter(
-        query: '4.6',
-      );
-
-      expect(loggingTableModel.logCount, 3);
-      expect(loggingTableModel.filteredLogCount, 2);
-      expect(loggingTableModel.selectedLogCount, 0);
-      expect(loggingTableModel.filteredLogAt(0), equals(log1));
-      expect(loggingTableModel.filteredLogAt(1), equals(log3));
-    });
-  });
-
-  group('Original LoggingController tests', () {
-    setGlobal(MessageBus, MessageBus());
-
-    void addStdoutData(String message) {
-      loggingTableModel.add(
-        LogDataV2(
-          'stdout',
-          jsonEncode({'kind': 'stdout', 'message': message}),
-          0,
-          summary: message,
-        ),
-      );
-    }
-
-    void addGcData(String message) {
-      loggingTableModel.add(
-        LogDataV2(
-          'gc',
-          jsonEncode({'kind': 'gc', 'message': message}),
-          0,
-          summary: message,
-        ),
-      );
-    }
-
-    void addLogWithKind(String kind) {
-      loggingTableModel
-          .add(LogDataV2(kind, jsonEncode({'foo': 'test_data'}), 0));
-    }
-
-    setUp(() {
-      setGlobal(
-        ServiceConnectionManager,
-        FakeServiceConnectionManager(),
-      );
-    });
-
-    test('initial state', () {
-      expect(loggingTableModel.logCount, 0);
-      expect(loggingTableModel.filteredLogCount, 0);
-      expect(loggingTableModel.activeFilter.value.isEmpty, isFalse);
-    });
-
-    testWidgets('receives data', (WidgetTester tester) async {
-      await pumpForContext(tester);
-
-      expect(loggingTableModel.logCount, 0);
-
-      addStdoutData('Abc.');
-
-      expect(loggingTableModel.logCount, greaterThan(0));
-      expect(loggingTableModel.filteredLogCount, greaterThan(0));
-
-      expect(loggingTableModel.filteredLogAt(0).summary, contains('Abc'));
-    });
-
-    testWidgets('clear', (WidgetTester tester) async {
-      await pumpForContext(tester);
-      addStdoutData('Abc.');
-
-      expect(loggingTableModel.logCount, greaterThan(0));
-      expect(loggingTableModel.filteredLogCount, greaterThan(0));
-
-      loggingTableModel.clear();
-
-      expect(loggingTableModel.logCount, 0);
-      expect(loggingTableModel.filteredLogCount, 0);
-    });
-
-    // test('matchesForSearch', () {
-    //   addStdoutData('abc');
-    //   addStdoutData('def');
-    //   addStdoutData('abc ghi');
-    //   addLogWithKind('Flutter.Navigation');
-    //   addLogWithKind('Flutter.Error');
-    //   addGcData('gc1');
-    //   addGcData('gc2');
-    //
-    //   expect(loggingTableModel.filteredData.value, 5);
-    //   expect(loggingTableModel.matchesForSearch('abc').length, equals(2));
-    //   expect(loggingTableModel.matchesForSearch('ghi').length, equals(1));
-    //   expect(loggingTableModel.matchesForSearch('abcd').length, equals(0));
-    //   expect(loggingTableModel.matchesForSearch('Flutter*').length, equals(2));
-    //   expect(loggingTableModel.matchesForSearch('').length, equals(0));
-    //
-    //   // Search by event kind.
-    //   expect(loggingTableModel.matchesForSearch('stdout').length, equals(3));
-    //   expect(loggingTableModel.matchesForSearch('flutter.*').length, equals(2));
-    //
-    //   // Search with incorrect case.
-    //   expect(loggingTableModel.matchesForSearch('STDOUT').length, equals(3));
-    // });
-    //
-    // test('matchesForSearch sets isSearchMatch property', () {
-    //   addStdoutData('abc');
-    //   addStdoutData('def');
-    //   addStdoutData('abc ghi');
-    //   addLogWithKind('Flutter.Navigation');
-    //   addLogWithKind('Flutter.Error');
-    //   addGcData('gc1');
-    //   addGcData('gc2');
-    //
-    //   expect(loggingTableModel.filteredData.value, 5);
-    //   loggingTableModel.search = 'abc';
-    //   var matches = loggingTableModel.searchMatches.value;
-    //   expect(matches.length, equals(2));
-    //   verifyIsSearchMatch(loggingTableModel.filteredData.value, matches);
-    //
-    //   loggingTableModel.search = 'Flutter.';
-    //   matches = loggingTableModel.searchMatches.value;
-    //   expect(matches.length, equals(2));
-    //   verifyIsSearchMatch(loggingTableModel.filteredData.value, matches);
-    // });
-
-    testWidgets('filterData', (WidgetTester tester) async {
-      await pumpForContext(tester);
-
-      addStdoutData('abc');
-      addStdoutData('def');
-      addStdoutData('abc ghi');
-      addLogWithKind('Flutter.Navigation');
-      addLogWithKind('Flutter.Error');
-
-      // The following logs should all be filtered by default.
-      addGcData('gc1');
-      addGcData('gc2');
-      addLogWithKind('Flutter.FirstFrame');
-      addLogWithKind('Flutter.FrameworkInitialization');
-      addLogWithKind('Flutter.Frame');
-      addLogWithKind('Flutter.ImageSizesForFrame');
-      addLogWithKind('Flutter.ServiceExtensionStateChanged');
-
-      // At this point data is filtered by the default toggle filter values.
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 5);
-
-      // Test query filters assuming default toggle filters are all enabled.
-      for (final filter in loggingTableModel.activeFilter.value.toggleFilters) {
-        filter.enabled.value = true;
-      }
-
-      loggingTableModel.setActiveFilter(query: 'abc');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 2);
-
-      loggingTableModel.setActiveFilter(query: 'def');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 1);
-
-      loggingTableModel.setActiveFilter(query: 'abc def');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 3);
-
-      loggingTableModel.setActiveFilter(query: 'k:stdout');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 3);
-
-      loggingTableModel.setActiveFilter(query: '-k:stdout');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 2);
-
-      loggingTableModel.setActiveFilter(query: 'k:stdout abc');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 2);
-
-      loggingTableModel.setActiveFilter(query: 'k:stdout,flutter.navigation');
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 4);
-
-      loggingTableModel.setActiveFilter();
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 5);
-
-      // Test toggle filters.
-      final verboseFlutterFrameworkFilter =
-          loggingTableModel.activeFilter.value.toggleFilters[0];
-      final verboseFlutterServiceFilter =
-          loggingTableModel.activeFilter.value.toggleFilters[1];
-      final gcFilter = loggingTableModel.activeFilter.value.toggleFilters[2];
-
-      verboseFlutterFrameworkFilter.enabled.value = false;
-      loggingTableModel.setActiveFilter();
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 9);
-
-      verboseFlutterServiceFilter.enabled.value = false;
-      loggingTableModel.setActiveFilter();
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 10);
-
-      gcFilter.enabled.value = false;
-      loggingTableModel.setActiveFilter();
-      expect(loggingTableModel.logCount, 12);
-      expect(loggingTableModel.filteredLogCount, 12);
-    });
-  });
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_v2/logging_screen_v2_test.dart b/packages/devtools_app/test/logging/logging_screen_v2/logging_screen_v2_test.dart
deleted file mode 100644
index f1a29af..0000000
--- a/packages/devtools_app/test/logging/logging_screen_v2/logging_screen_v2_test.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm')
-library;
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_table_v2.dart';
-import 'package:devtools_app/src/shared/primitives/message_bus.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-void main() {
-  late LoggingScreenV2 screen;
-  late MockLoggingControllerV2 mockLoggingController;
-  const windowSize = Size(1000.0, 1000.0);
-  TestWidgetsFlutterBinding.ensureInitialized();
-
-  group('Logging Screen', () {
-    Future<void> pumpLoggingScreen(WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          const LoggingScreenBodyV2(),
-          loggingV2: mockLoggingController,
-        ),
-      );
-    }
-
-    setUp(() {
-      final fakeServiceConnection = FakeServiceConnectionManager();
-      when(
-        fakeServiceConnection.serviceManager.connectedApp!.isFlutterWebAppNow,
-      ).thenReturn(false);
-      when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-          .thenReturn(false);
-      when(
-        fakeServiceConnection.errorBadgeManager.errorCountNotifier('logging'),
-      ).thenReturn(ValueNotifier<int>(0));
-      setGlobal(GlobalKey<NavigatorState>, GlobalKey<NavigatorState>());
-      setGlobal(NotificationService, NotificationService());
-      setGlobal(
-        DevToolsEnvironmentParameters,
-        ExternalDevToolsEnvironmentParameters(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(ServiceConnectionManager, fakeServiceConnection);
-      setGlobal(IdeTheme, IdeTheme());
-      setGlobal(MessageBus, MessageBus());
-
-      mockLoggingController = createMockLoggingControllerV2WithDefaults();
-
-      screen = LoggingScreenV2();
-    });
-
-    testWidgets('builds its tab', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(Builder(builder: screen.buildTab)));
-      expect(find.text('Logging'), findsOneWidget);
-    });
-
-    testWidgetsWithWindowSize(
-      'builds with no data',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpLoggingScreen(tester);
-        expect(find.byType(LoggingScreenBodyV2), findsOneWidget);
-        expect(find.byType(LoggingTableV2), findsOneWidget);
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/logging/logging_screen_v2/logging_table_row_test.dart b/packages/devtools_app/test/logging/logging_screen_v2/logging_table_row_test.dart
deleted file mode 100644
index b21d7e8..0000000
--- a/packages/devtools_app/test/logging/logging_screen_v2/logging_table_row_test.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_table_row.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
-  const windowSize = Size(1000, 1000);
-
-  setUp(() {
-    setGlobal(IdeTheme, getIdeTheme());
-    setGlobal(GlobalKey<NavigatorState>, GlobalKey<NavigatorState>());
-    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-  });
-
-  group('logging_table_row', () {
-    for (double windowWidth = 208.0; windowWidth < 600.0; windowWidth += 15.0) {
-      const numberOfChips = 3;
-      final data = LogDataV2(
-        'someKind',
-        '{"elapsed": 374}',
-        213567823783,
-      );
-      final windowSize = Size(windowWidth, 500);
-
-      testWidgetsWithWindowSize(
-        'Estimates the height of a row correctly for windowWidth: $windowWidth',
-        windowSize,
-        (WidgetTester tester) async {
-          await tester.pumpWidget(
-            Column(
-              mainAxisSize: MainAxisSize.min,
-              children: [
-                wrap(
-                  LoggingTableRow(
-                    index: 0,
-                    data: data,
-                    isSelected: false,
-                  ),
-                ),
-              ],
-            ),
-          );
-          expect(
-            LoggingTableRow.estimateRowHeight(data, windowWidth),
-            tester.getSize(find.byType(LoggingTableRow)).height,
-          );
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'Estimates the height of the wrapped chips correctly for windowWidth: $windowWidth',
-        windowSize,
-        (WidgetTester tester) async {
-          final chips = LoggingTableRow.metadataChips(data, windowSize.width);
-          final wrapKey = GlobalKey();
-
-          expect(chips.length, numberOfChips);
-          await tester.pumpWidget(
-            wrap(
-              Column(
-                mainAxisSize: MainAxisSize.min,
-                children: [
-                  Wrap(
-                    key: wrapKey,
-                    children: chips,
-                  ),
-                ],
-              ),
-            ),
-          );
-          expect(
-            LoggingTableRow.estimateMetaDataWrapHeight(
-              data,
-              windowWidth,
-            ),
-            tester.getSize(find.byKey(wrapKey)).height,
-          );
-        },
-      );
-    }
-
-    testWidgetsWithWindowSize(
-      'estimates MetadataChip sizes correctly',
-      windowSize,
-      (WidgetTester tester) async {
-        const numberOfChips = 3;
-        final data =
-            LogDataV2('someKind', '{"elapsed": 378564654}', 213567823783);
-        final chips = LoggingTableRow.metadataChips(data, windowSize.width);
-        final wrapKey = GlobalKey();
-
-        await tester.pumpWidget(
-          wrap(
-            Wrap(
-              key: wrapKey,
-              children: chips,
-            ),
-          ),
-        );
-
-        final chipFinder = find.bySubtype<MetadataChip>();
-
-        expect(chipFinder, findsExactly(numberOfChips));
-        final chipElements = chipFinder.evaluate();
-        for (var i = 0; i < numberOfChips; i++) {
-          final chip = chips[i];
-          final chipElement = chipElements.elementAt(i);
-          expect(chip.estimateSize(), chipElement.size);
-        }
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/memory/chart/chart_test.dart b/packages/devtools_app/test/memory/chart/chart_test.dart
deleted file mode 100644
index 10a44b8..0000000
--- a/packages/devtools_app/test/memory/chart/chart_test.dart
+++ /dev/null
@@ -1,839 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/screens/memory/panes/chart/data/charts.dart';
-import 'package:devtools_app/src/service/service_manager.dart';
-import 'package:devtools_app/src/shared/charts/chart.dart';
-import 'package:devtools_app/src/shared/charts/chart_controller.dart';
-import 'package:devtools_app/src/shared/charts/chart_trace.dart';
-import 'package:devtools_app/src/shared/primitives/utils.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_shared.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../test_infra/matchers/matchers.dart';
-import '../../test_infra/test_data/memory.dart';
-
-void main() {
-  const windowSize = Size(2225.0, 1000.0);
-
-  setUp(() {
-    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
-    setGlobal(IdeTheme, IdeTheme());
-  });
-
-  group(
-    'Chart Timeseries',
-    () {
-      late MemoryJson<HeapSample> memoryJson;
-      bool memoryJasonInitialized = false;
-
-      void loadData() {
-        // Load canned data testHeapSampleData.
-        if (!memoryJasonInitialized) {
-          memoryJson =
-              SamplesMemoryJson.decode(argJsonString: testHeapSampleData);
-          memoryJasonInitialized = true;
-        }
-
-        expect(memoryJson.data.length, equals(104));
-      }
-
-      ///////////////////////////////////////////////////////////////////////////
-      // Scaled Y-axis chart.                                                                     //
-      ///////////////////////////////////////////////////////////////////////////
-
-      // Used for test.
-      final rawExternal = <Data>[];
-      final rawUsed = <Data>[];
-      final rawCapacity = <Data>[];
-      final rawRSS = <Data>[];
-
-      late int externalTraceIndex;
-      late int usedTraceIndex;
-      late int capacityTraceIndex;
-      late int rssTraceIndex;
-
-      void setupTraces(ChartController controller) {
-        // External Heap
-        externalTraceIndex = controller.createTrace(
-          ChartType.line,
-          PaintCharacteristics(
-            color: Colors.lightGreen,
-            symbol: ChartSymbol.disc,
-            diameter: 1.5,
-          ),
-          name: externalDisplay,
-        );
-
-        // Used Heap
-        usedTraceIndex = controller.createTrace(
-          ChartType.line,
-          PaintCharacteristics(
-            color: Colors.blue[200]!,
-            symbol: ChartSymbol.disc,
-            diameter: 1.5,
-          ),
-          name: usedDisplay,
-        );
-
-        // Heap Capacity
-        capacityTraceIndex = controller.createTrace(
-          ChartType.line,
-          PaintCharacteristics(
-            color: Colors.grey[400]!,
-            diameter: 0.0,
-            symbol: ChartSymbol.dashedLine,
-          ),
-          name: allocatedDisplay,
-        );
-
-        // RSS
-        rssTraceIndex = controller.createTrace(
-          ChartType.line,
-          PaintCharacteristics(
-            color: Colors.yellow,
-            symbol: ChartSymbol.dashedLine,
-            strokeWidth: 2,
-          ),
-          name: rssDisplay,
-        );
-
-        expect(controller.traces.length, equals(4));
-        for (var index = 0; index < controller.traces.length; index++) {
-          switch (index) {
-            case 0:
-              expect(externalTraceIndex, equals(0));
-              expect(controller.traces[index].name, externalDisplay);
-              break;
-            case 1:
-              expect(usedTraceIndex, equals(1));
-              expect(controller.traces[index].name, usedDisplay);
-              break;
-            case 2:
-              expect(capacityTraceIndex, equals(2));
-              expect(controller.traces[index].name, allocatedDisplay);
-              break;
-            case 3:
-              expect(rssTraceIndex, equals(3));
-              expect(controller.traces[index].name, rssDisplay);
-              break;
-          }
-        }
-      }
-
-      void addDataToTrace(
-        ChartController controller,
-        int traceIndex,
-        Data data,
-      ) {
-        controller.trace(traceIndex).addDatum(data);
-      }
-
-      Future<void> pumpChart(
-        WidgetTester tester,
-        Key theKey,
-        Chart theChart,
-        double chartHeight,
-      ) async {
-        await tester.pumpWidget(
-          wrap(
-            LayoutBuilder(
-              key: theKey,
-              builder: (context, constraints) {
-                return Column(
-                  mainAxisSize: MainAxisSize.min,
-                  children: [
-                    SizedBox(
-                      height: chartHeight,
-                      child: Padding(
-                        padding: const EdgeInsets.all(0.0),
-                        child: theChart,
-                      ),
-                    ),
-                  ],
-                );
-              },
-            ),
-          ),
-        );
-
-        await tester.pumpAndSettle();
-      }
-
-      void chartAllData(ChartController controller) {
-        for (var datumIndex = 0;
-            datumIndex < memoryJson.data.length;
-            datumIndex++) {
-          final datum = memoryJson.data[datumIndex];
-
-          final external = datum.external.toDouble();
-          final used = datum.used.toDouble();
-          final capacity = datum.capacity.toDouble();
-          final rss = datum.rss.toDouble();
-
-          controller.addTimestamp(datum.timestamp);
-
-          rawExternal.add(Data(datum.timestamp, external));
-
-          addDataToTrace(controller, externalTraceIndex, rawExternal.last);
-
-          rawUsed.add(Data(datum.timestamp, external + used));
-          addDataToTrace(controller, usedTraceIndex, rawUsed.last);
-
-          rawCapacity.add(Data(datum.timestamp, capacity));
-          addDataToTrace(controller, capacityTraceIndex, rawCapacity.last);
-
-          rawRSS.add(Data(datum.timestamp, rss));
-          addDataToTrace(controller, rssTraceIndex, rawRSS.last);
-        }
-      }
-
-      Future<void> setupScaledChart(
-        WidgetTester tester,
-        ChartController controller,
-        Key chartKey,
-      ) async {
-        final theChart = Chart(controller, title: 'Scaled Chart');
-
-        setupTraces(controller);
-
-        loadData();
-
-        await pumpChart(tester, chartKey, theChart, 250);
-
-        expect(find.byWidget(theChart), findsOneWidget);
-
-        // Validate the X axis before data added.
-        expect(controller.visibleXAxisTicks, equals(215));
-        expect(controller.xCanvasChart, equals(50.0));
-        expect(controller.xPaddingRight, equals(0.0));
-        expect(controller.displayXLabels, true);
-        expect(controller.canvasChartWidth, equals(2150.0));
-
-        // Validate the Y axis before data added.
-        expect(controller.yScale.labelTicks, equals(0.0));
-        expect(controller.yScale.labelUnitExponent, 0.0);
-        expect(controller.yScale.tickSpacing, equals(1.0));
-        expect(controller.yScale.maxPoint, equals(0.0));
-        expect(controller.yScale.maxTicks, equals(185.0));
-
-        chartAllData(controller);
-      }
-
-      /// Validate the labels displayed on the y-axis.
-      void validateScaledYLabels(ChartController controller) {
-        // Validate the labels displayed on the y-axis.
-        final yScale = controller.yScale;
-        expect(yScale.labelTicks, equals(10));
-        for (var labelIndex = yScale.labelTicks;
-            labelIndex >= 0;
-            labelIndex--) {
-          final labelName = ChartPainter.constructLabel(
-            labelIndex.toInt(),
-            yScale.labelUnitExponent.toInt(),
-          );
-
-          // Ensure Y axis labels match.
-          final expectedLabels = [
-            '0',
-            '100M',
-            '200M',
-            '300M',
-            '400M',
-            '500M',
-            '600M',
-            '700M',
-            '800M',
-            '900M',
-            '1000M',
-          ];
-          expect(labelName, expectedLabels[labelIndex.toInt()]);
-        }
-      }
-
-      testWidgetsWithWindowSize(
-        'Scaled Y-axis live',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupScaledChart(tester, controller, chartKey);
-
-          // Check live view zoom.
-          controller.zoomDuration = const Duration();
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_scaled_live.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(215));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.0));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2150.0));
-
-          // Validate the Y axis after data added to all traces.
-          expect(controller.yScale.labelTicks, equals(10.0));
-          expect(controller.yScale.labelUnitExponent, 8.0);
-          expect(controller.yScale.tickSpacing, equals(5291005.291005291));
-          expect(controller.yScale.maxPoint, equals(717799424.0));
-          expect(controller.yScale.maxTicks, equals(190.0));
-
-          final externalTrace = controller.trace(externalTraceIndex);
-          expect(externalTrace.dataYMax, equals(357446512.0));
-          expect(externalTrace.data.length, equals(rawExternal.length));
-
-          final usedTrace = controller.trace(usedTraceIndex);
-          expect(usedTrace.dataYMax, equals(409913568.0));
-          expect(usedTrace.data.length, equals(rawUsed.length));
-
-          final capacityTrace = controller.trace(capacityTraceIndex);
-          expect(capacityTrace.dataYMax, equals(422794096.0));
-          expect(capacityTrace.data.length, equals(rawCapacity.length));
-
-          final rssTrace = controller.trace(rssTraceIndex);
-          expect(rssTrace.dataYMax, equals(717799424.0));
-          expect(rssTrace.data.length, equals(rawRSS.length));
-
-          expect(controller.timestampsLength, equals(104));
-
-          validateScaledYLabels(controller);
-
-          // Validate the x-axis labels.
-          expect(controller.labelTimestamps.length, equals(2));
-          expect(controller.labelTimestamps[0], equals(1611247510202));
-          expect(controller.labelTimestamps[1], equals(1611247530281));
-
-          // Validate using UTC timezone.
-          expect(
-            prettyTimestamp(controller.labelTimestamps[0], isUtc: true),
-            equals('16:45:10'),
-          );
-          expect(
-            prettyTimestamp(controller.labelTimestamps[1], isUtc: true),
-            equals('16:45:30'),
-          );
-        },
-      );
-
-      void checkScaledXAxis2Labels(ChartController controller) {
-        // Validate the x-axis labels.
-        expect(controller.labelTimestamps.length, equals(1));
-        expect(controller.labelTimestamps[0], equals(1611247510202));
-
-        // Validate using UTC timezone.
-        expect(
-          prettyTimestamp(controller.labelTimestamps[0], isUtc: true),
-          equals('16:45:10'),
-        );
-      }
-
-      testWidgetsWithWindowSize(
-        'Scaled Y-axis all',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupScaledChart(tester, controller, chartKey);
-
-          // Check A=all data view zoom.
-          controller.zoomDuration = null;
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_scaled_all.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(104));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.0));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2150.0));
-
-          // Validate the Y axis after data added to all traces.
-          expect(controller.yScale.labelTicks, equals(10.0));
-          expect(controller.yScale.labelUnitExponent, 8.0);
-          expect(controller.yScale.tickSpacing, equals(5291005.291005291));
-          expect(controller.yScale.maxPoint, equals(717799424.0));
-          expect(controller.yScale.maxTicks, equals(190.0));
-
-          validateScaledYLabels(controller);
-
-          checkScaledXAxis2Labels(controller);
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'Scaled Y-axis Five Minutes',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupScaledChart(tester, controller, chartKey);
-
-          // Check 5 minute data view zoom.
-          controller.zoomDuration = const Duration(minutes: 5);
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_scaled_five_minute.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(1704));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.6880000000001019));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2149.312));
-
-          // Validate the Y axis after data added to all traces.
-          expect(controller.yScale.labelTicks, equals(10.0));
-          expect(controller.yScale.labelUnitExponent, 8.0);
-          expect(controller.yScale.tickSpacing, equals(5291005.291005291));
-          expect(controller.yScale.maxPoint, equals(717799424.0));
-          expect(controller.yScale.maxTicks, equals(190.0));
-
-          validateScaledYLabels(controller);
-
-          checkScaledXAxis2Labels(controller);
-        },
-      );
-
-      ///////////////////////////////////////////////////////////////////////////
-      // Fixed Y-axis chart.                                                                     //
-      ///////////////////////////////////////////////////////////////////////////
-
-      final rawGcEvents = <Data>[];
-      final rawSnapshotEvents = <Data>[];
-      final rawAutoSnapshotEvents = <Data>[];
-
-      late int snapshotTraceIndex;
-      late int autoSnapshotTraceIndex;
-      late int manualGCTraceIndex;
-      late int monitorTraceIndex;
-      late int monitorResetTraceIndex;
-      late int gcTraceIndex;
-
-      void setupFixedTraces(ChartController controller) {
-        // Snapshot
-        snapshotTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics(
-            color: Colors.green,
-            strokeWidth: 4,
-            diameter: 6,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-          ),
-          name: 'Snapshot',
-        );
-
-        // Auto-snapshot
-        autoSnapshotTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics(
-            color: Colors.red,
-            strokeWidth: 4,
-            diameter: 6,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-          ),
-          name: 'Auto-Snapshot',
-        );
-
-        // Manual GC
-        manualGCTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics(
-            color: Colors.blue,
-            strokeWidth: 4,
-            diameter: 6,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-          ),
-          name: 'Manual GC',
-        );
-
-        // Monitor
-        monitorTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics(
-            color: Colors.yellow,
-            strokeWidth: 4,
-            diameter: 6,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-          ),
-          name: 'Monitor',
-        );
-
-        monitorResetTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics.concentric(
-            color: Colors.grey[600]!,
-            strokeWidth: 4,
-            diameter: 6,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-            concentricCenterColor: Colors.yellowAccent,
-            concentricCenterDiameter: 4,
-          ),
-          name: 'Monitor Reset',
-        );
-
-        // VM GC
-        gcTraceIndex = controller.createTrace(
-          ChartType.symbol,
-          PaintCharacteristics(
-            color: Colors.blue,
-            symbol: ChartSymbol.disc,
-            diameter: 4,
-            fixedMinY: 0.4,
-            fixedMaxY: 2.4,
-          ),
-          name: 'VM GC',
-        );
-      }
-
-      /// Event to display in the event pane (User initiated GC, snapshot,
-      /// automatic snapshot, etc.)
-      const visibleEvent = 2.4;
-
-      /// Monitor events Y axis.
-      const visibleMonitorEvent = 1.4;
-
-      /// VM's GCs are displayed in a smaller glyph and closer to the heap graph.
-      const visibleVmEvent = 0.4;
-
-      // Load all data into the chart's traces.
-      void chartAllFixedData(ChartController controller) {
-        for (var datumIndex = 0;
-            datumIndex < memoryJson.data.length;
-            datumIndex++) {
-          final datum = memoryJson.data[datumIndex];
-
-          controller.addTimestamp(datum.timestamp);
-
-          final event = datum.memoryEventInfo;
-          if (datum.isGC) {
-            // VM GC
-            rawGcEvents.add(Data(datum.timestamp, visibleVmEvent));
-            addDataToTrace(controller, gcTraceIndex, rawGcEvents.last);
-          } else if (event.isEventGC) {
-            // Manual GC
-            final rawData = Data(datum.timestamp, visibleVmEvent);
-            addDataToTrace(controller, manualGCTraceIndex, rawData);
-          } else if (event.isEventSnapshot) {
-            rawSnapshotEvents.add(Data(datum.timestamp, visibleEvent));
-            addDataToTrace(
-              controller,
-              snapshotTraceIndex,
-              rawSnapshotEvents.last,
-            );
-          } else if (event.isEventSnapshotAuto) {
-            rawAutoSnapshotEvents.add(Data(datum.timestamp, visibleEvent));
-            addDataToTrace(
-              controller,
-              autoSnapshotTraceIndex,
-              rawAutoSnapshotEvents.last,
-            );
-          } else if (event.isEventAllocationAccumulator) {
-            final monitorType = event.allocationAccumulator;
-            final rawData = Data(datum.timestamp, visibleMonitorEvent);
-            if (monitorType!.isEmpty) continue;
-            if (monitorType.isStart) {
-              addDataToTrace(controller, monitorTraceIndex, rawData);
-            } else if (monitorType.isReset) {
-              addDataToTrace(controller, monitorResetTraceIndex, rawData);
-            } else {
-              assert(false, 'Unknown monitor type');
-            }
-          } else if (event.isEmpty) {
-            assert(false, 'Unexpected EventSample of isEmpty.');
-          }
-        }
-      }
-
-      Future<void> setupFixedChart(
-        WidgetTester tester,
-        ChartController controller,
-        Key chartKey,
-      ) async {
-        controller.setFixedYRange(0.4, 2.4);
-
-        final theChart = Chart(controller, title: 'Fixed Chart');
-
-        await pumpChart(tester, chartKey, theChart, 150);
-
-        expect(find.byWidget(theChart), findsOneWidget);
-
-        setupFixedTraces(controller);
-
-        loadData();
-
-        // Validate the X axis before any data.
-        expect(controller.visibleXAxisTicks, equals(215));
-        expect(controller.xCanvasChart, equals(50.0));
-        expect(controller.xPaddingRight, equals(0.0));
-        expect(controller.displayXLabels, true);
-        expect(controller.canvasChartWidth, equals(2150.0));
-
-        // Validate the Y axis before any data.
-        expect(controller.yScale.labelTicks, equals(3.0));
-        expect(controller.yScale.labelUnitExponent, 0.0);
-        expect(controller.yScale.tickSpacing, equals(0.033707865168539325));
-        expect(controller.yScale.maxPoint, equals(2.4));
-        expect(controller.yScale.maxTicks, equals(90.0));
-
-        // Load all data in the chart.
-        chartAllFixedData(controller);
-      }
-
-      testWidgetsWithWindowSize(
-        'Fixed Y-axis',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupFixedChart(tester, controller, chartKey);
-
-          // Check live view zoom.
-          controller.zoomDuration = const Duration();
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_fixed_live.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(215));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.0));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2150.0));
-
-          // Rest of data is out of view because we're live view max is now 1.4
-          // and only 2 labels visible.
-          expect(controller.yScale.labelTicks, equals(3.0));
-
-          expect(controller.yScale.labelUnitExponent, 0.0);
-          expect(controller.yScale.tickSpacing, equals(0.033707865168539325));
-          // Max live view max is 1.4 other data is not in the visible view.
-          expect(controller.yScale.maxPoint, equals(2.4));
-          expect(controller.yScale.maxTicks, equals(90.0));
-
-          final snapshotTrace = controller.trace(snapshotTraceIndex);
-          expect(snapshotTrace.dataYMax, equals(0.0));
-          expect(snapshotTrace.data.length, equals(1));
-
-          final autoSnapshotTrace = controller.trace(autoSnapshotTraceIndex);
-          expect(autoSnapshotTrace.dataYMax, equals(0.0));
-          expect(autoSnapshotTrace.data.length, equals(0));
-
-          final manualGCTrace = controller.trace(manualGCTraceIndex);
-          expect(manualGCTrace.dataYMax, equals(0.0));
-          expect(manualGCTrace.data.length, equals(0));
-
-          final monitorTrace = controller.trace(monitorTraceIndex);
-          expect(monitorTrace.dataYMax, equals(0.0));
-          expect(monitorTrace.data.length, equals(2));
-
-          final monitorResetTrace = controller.trace(monitorResetTraceIndex);
-          expect(monitorResetTrace.dataYMax, equals(0.0));
-          expect(monitorResetTrace.data.length, equals(1));
-
-          final gcTrace = controller.trace(gcTraceIndex);
-          expect(gcTrace.dataYMax, equals(0.0));
-          expect(gcTrace.data.length, equals(46));
-
-          expect(controller.timestampsLength, equals(104));
-
-          // Validate the labels displayed on the y-axis.
-          final yScale = controller.yScale;
-          expect(yScale.labelTicks, equals(3.0));
-          for (var labelIndex = yScale.labelTicks;
-              labelIndex >= 0;
-              labelIndex--) {
-            final labelName = ChartPainter.constructLabel(
-              labelIndex.toInt(),
-              yScale.labelUnitExponent.toInt(),
-            );
-
-            final expectedLabels = ['0', '1', '2', '3'];
-            expect(labelName, expectedLabels[labelIndex.toInt()]);
-          }
-
-          // Validate the x-axis labels.
-          expect(controller.labelTimestamps.length, equals(2));
-          expect(controller.labelTimestamps[0], equals(1611247510202));
-          expect(controller.labelTimestamps[1], equals(1611247530281));
-
-          // Validate using UTC timezone.
-          expect(
-            prettyTimestamp(controller.labelTimestamps[0], isUtc: true),
-            equals('16:45:10'),
-          );
-          expect(
-            prettyTimestamp(controller.labelTimestamps[1], isUtc: true),
-            equals('16:45:30'),
-          );
-        },
-      );
-
-      void checkFixedXAxis2Labels(ChartController controller) {
-        // Validate the x-axis labels.
-        expect(controller.labelTimestamps.length, equals(1));
-        expect(controller.labelTimestamps[0], equals(1611247510202));
-
-        // Validate using UTC timezone.
-        expect(
-          prettyTimestamp(controller.labelTimestamps[0], isUtc: true),
-          equals('16:45:10'),
-        );
-      }
-
-      testWidgetsWithWindowSize(
-        'Fixed Y-axis all',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupFixedChart(tester, controller, chartKey);
-
-          // Check all data view zoom.
-          controller.zoomDuration = null;
-          await tester.pumpAndSettle(const Duration(seconds: 15));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_fixed_all.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(104));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.0));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2150.0));
-
-          // Validate the Y axis after data added to all traces.
-          expect(controller.yScale.labelTicks, equals(3.0));
-          expect(controller.yScale.labelUnitExponent, 0.0);
-          expect(controller.yScale.tickSpacing, equals(0.033707865168539325));
-          expect(controller.yScale.maxPoint, equals(2.4));
-          expect(controller.yScale.maxTicks, equals(90.0));
-
-          // Validate the labels displayed on the y-axis.
-          final yScale = controller.yScale;
-          expect(yScale.labelTicks, equals(3));
-          for (var labelIndex = yScale.labelTicks;
-              labelIndex >= 0;
-              labelIndex--) {
-            final labelName = ChartPainter.constructLabel(
-              labelIndex.toInt(),
-              yScale.labelUnitExponent.toInt(),
-            );
-
-            final expectedLabels = ['0', '1', '2', '3'];
-            expect(labelName, expectedLabels[labelIndex.toInt()]);
-          }
-
-          checkFixedXAxis2Labels(controller);
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'Fixed Y-axis 5 Minutes',
-        windowSize,
-        (WidgetTester tester) async {
-          const chartKey = Key('Chart');
-          final controller = ChartController();
-
-          await setupFixedChart(tester, controller, chartKey);
-
-          // Check all data view zoom.
-          controller.zoomDuration = const Duration(minutes: 5);
-          await tester.pumpAndSettle(const Duration(seconds: 15));
-
-          await expectLater(
-            find.byKey(chartKey),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory_chart_fixed_five_minutes.png',
-            ),
-          );
-          // Await delay for golden comparison.
-          await tester.pumpAndSettle(const Duration(seconds: 2));
-
-          // Validate the X axis after data added to all traces.
-          expect(controller.visibleXAxisTicks, equals(1704));
-          expect(controller.xCanvasChart, equals(50.0));
-          expect(controller.xPaddingRight, equals(0.6880000000001019));
-          expect(controller.displayXLabels, true);
-          expect(controller.canvasChartWidth, equals(2149.312));
-
-          // Validate the Y axis after data added to all traces.
-          expect(controller.yScale.labelTicks, equals(3.0));
-          expect(controller.yScale.labelUnitExponent, 0.0);
-          expect(controller.yScale.tickSpacing, equals(0.033707865168539325));
-          expect(controller.yScale.maxPoint, equals(2.4));
-          expect(controller.yScale.maxTicks, equals(90.0));
-
-          // Validate the labels displayed on the y-axis.
-          final yScale = controller.yScale;
-          expect(yScale.labelTicks, equals(3));
-          for (var labelIndex = yScale.labelTicks;
-              labelIndex >= 0;
-              labelIndex--) {
-            final labelName = ChartPainter.constructLabel(
-              labelIndex.toInt(),
-              yScale.labelUnitExponent.toInt(),
-            );
-
-            final expectedLabels = ['0', '1', '2', '3'];
-            expect(labelName, expectedLabels[labelIndex.toInt()]);
-          }
-
-          checkFixedXAxis2Labels(controller);
-        },
-      );
-    },
-  );
-}
diff --git a/packages/devtools_app/test/memory/control/settings_dialog_test.dart b/packages/devtools_app/test/memory/control/settings_dialog_test.dart
deleted file mode 100644
index 65b6164..0000000
--- a/packages/devtools_app/test/memory/control/settings_dialog_test.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/screens/memory/panes/control/widgets/settings_dialog.dart';
-import 'package:devtools_app/src/shared/common_widgets.dart';
-import 'package:devtools_app/src/shared/globals.dart';
-import 'package:devtools_app_shared/src/ui/dialogs.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../test_infra/matchers/matchers.dart';
-import '../../test_infra/scenes/memory/default.dart';
-
-void main() {
-  late MemoryDefaultScene scene;
-
-  // Set a wide enough screen width that we do not run into overflow.
-  const windowSize = Size(2225.0, 1000.0);
-
-  setUp(() async {
-    scene = MemoryDefaultScene();
-    await scene.setUp();
-  });
-
-  tearDown(() {
-    scene.tearDown();
-  });
-
-  testWidgetsWithWindowSize(
-    'settings update preferences',
-    windowSize,
-    (WidgetTester tester) async {
-      await scene.pump(tester);
-
-      // Open the dialog.
-      await tester.tap(find.byType(SettingsOutlinedButton));
-      await tester.pumpAndSettle();
-      await expectLater(
-        find.byType(MemorySettingsDialog),
-        matchesDevToolsGolden(
-          '../../test_infra/goldens/settings_dialog_default.png',
-        ),
-      );
-
-      // Modify settings and check the changes are reflected in the controller.
-      expect(
-        preferences.memory.androidCollectionEnabled.value,
-        isFalse,
-      );
-      await tester
-          .tap(find.byKey(MemorySettingDialogKeys.showAndroidChartCheckBox));
-      await tester.pumpAndSettle();
-      await expectLater(
-        find.byType(MemorySettingsDialog),
-        matchesDevToolsGolden(
-          '../../test_infra/goldens/settings_dialog_modified.png',
-        ),
-      );
-      expect(
-        preferences.memory.androidCollectionEnabled.value,
-        isTrue,
-      );
-
-      // Reopen the dialog and check the settings are not changed.
-      await tester.tap(find.byType(DialogCloseButton));
-      await tester.pumpAndSettle();
-      await tester.tap(find.byType(SettingsOutlinedButton));
-      await tester.pumpAndSettle();
-      await expectLater(
-        find.byType(MemorySettingsDialog),
-        matchesDevToolsGolden(
-          '../../test_infra/goldens/settings_dialog_modified.png',
-        ),
-      );
-    },
-  );
-}
diff --git a/packages/devtools_app/test/memory/diff/controller/snapshot_item_test.dart b/packages/devtools_app/test/memory/diff/controller/snapshot_item_test.dart
deleted file mode 100644
index 846620a..0000000
--- a/packages/devtools_app/test/memory/diff/controller/snapshot_item_test.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2024 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:convert';
-
-import 'package:devtools_app/src/screens/memory/panes/diff/controller/snapshot_item.dart';
-import 'package:devtools_app/src/shared/primitives/encoding.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../../test_infra/test_data/memory/heap/heap_data.dart';
-
-void main() {
-  for (final encode in [false, true]) {
-    test(
-      '$SnapshotDataItem serializes and deserializes correctly',
-      () async {
-        final item = SnapshotDataItem(
-          defaultName: 'defaultName',
-          displayNumber: 5,
-          nameOverride: 'nameOverride',
-        );
-        await item.loadHeap(HeapGraphLoaderGoldens());
-
-        Map<String, dynamic> json = item.toJson();
-
-        if (encode) {
-          final encoded = jsonEncode(json, toEncodable: toEncodable);
-          json = jsonDecode(encoded);
-        }
-
-        expect(
-          json.keys.toSet(),
-          equals(Json.values.map((e) => e.name).toSet()),
-        );
-        final fromJson = SnapshotDataItem.fromJson(json);
-
-        expect(fromJson.defaultName, item.defaultName);
-        expect(fromJson.displayNumber, item.displayNumber);
-        expect(fromJson.nameOverride, item.nameOverride);
-
-        await fromJson.process;
-
-        expect(
-          fromJson.heap!.graph.objects.length,
-          item.heap!.graph.objects.length,
-        );
-
-        expect(fromJson.heap!.created, item.heap!.created);
-      },
-    );
-  }
-}
diff --git a/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart b/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart
deleted file mode 100644
index 802da71..0000000
--- a/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/memory/framework/memory_tabs.dart';
-import 'package:devtools_app/src/screens/memory/panes/diff/diff_pane.dart';
-import 'package:devtools_app/src/screens/memory/panes/diff/widgets/snapshot_list.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../../test_infra/matchers/matchers.dart';
-import '../../../test_infra/scenes/memory/default.dart';
-
-Future<void> pumpScene(WidgetTester tester, MemoryDefaultScene scene) async {
-  await scene.pump(tester);
-  await tester.tap(
-    find.byKey(MemoryScreenKeys.diffTab),
-  );
-  await tester.pumpAndSettle();
-}
-
-Future<void> takeSnapshot(WidgetTester tester, MemoryDefaultScene scene) async {
-  final snapshots = scene.controller.diff.core.snapshots;
-  final length = snapshots.value.length;
-  await tester.tap(find.byIcon(Icons.fiber_manual_record).first);
-  await tester.pumpAndSettle();
-  expect(snapshots.value.length, equals(length + 1));
-}
-
-// Set a wide enough screen width that we do not run into overflow.
-const windowSize = Size(2225.0, 1000.0);
-
-void main() {
-  late MemoryDefaultScene scene;
-  setUp(() {
-    scene = MemoryDefaultScene();
-  });
-
-  tearDown(() {
-    scene.tearDown();
-  });
-
-  group('Diff pane', () {
-    setUp(() async {
-      await scene.setUp(heapProviders: MemoryDefaultSceneHeaps.forDiffTesting);
-    });
-
-    testWidgetsWithWindowSize(
-      'records and deletes snapshots',
-      windowSize,
-      (WidgetTester tester) async {
-        final snapshots = scene.controller.diff.core.snapshots;
-        // Check the list contains only documentation item.
-        expect(snapshots.value.length, equals(1));
-        await pumpScene(tester, scene);
-
-        // Check initial golden.
-        await expectLater(
-          find.byType(DiffPane),
-          matchesDevToolsGolden(
-            '../../../test_infra/goldens/memory_diff_empty1.png',
-          ),
-        );
-
-        // Record three snapshots.
-        for (final i in Iterable<int>.generate(3)) {
-          await takeSnapshot(tester, scene);
-          expect(find.text('selected-isolate-${i + 1}'), findsOneWidget);
-        }
-
-        await expectLater(
-          find.byType(DiffPane),
-          matchesDevToolsGolden(
-            '../../../test_infra/goldens/memory_diff_three_snapshots1.png',
-          ),
-        );
-        expect(snapshots.value.length, equals(1 + 3));
-
-        await expectLater(
-          find.byType(DiffPane),
-          matchesDevToolsGolden(
-            '../../../test_infra/goldens/memory_diff_selected_class.png',
-          ),
-        );
-
-        // Delete a snapshot.
-        await tester.tap(
-          find.descendant(
-            of: find.byType(SnapshotListTitle),
-            matching: find.byType(ContextMenuButton),
-          ),
-        );
-        await tester.pumpAndSettle();
-        await tester.tap(
-          find.descendant(
-            of: find.byType(MenuItemButton),
-            matching: find.text('Delete'),
-          ),
-        );
-        await tester.pumpAndSettle();
-        expect(snapshots.value.length, equals(1 + 3 - 1));
-
-        // Record snapshot
-        await takeSnapshot(tester, scene);
-        await expectLater(
-          find.byType(DiffPane),
-          matchesDevToolsGolden(
-            '../../../test_infra/goldens/memory_diff_three_snapshots2.png',
-          ),
-        );
-        expect(snapshots.value.length, equals(1 + 3 - 1 + 1));
-
-        // Clear all
-        await tester.tap(find.byTooltip('Delete all snapshots'));
-        await tester.pumpAndSettle();
-        await expectLater(
-          find.byType(DiffPane),
-          matchesDevToolsGolden(
-            '../../../test_infra/goldens/memory_diff_empty2.png',
-          ),
-        );
-        expect(snapshots.value.length, equals(1));
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/memory/framework/memory_screen_test.dart b/packages/devtools_app/test/memory/framework/memory_screen_test.dart
deleted file mode 100644
index 81ed348..0000000
--- a/packages/devtools_app/test/memory/framework/memory_screen_test.dart
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/memory/framework/memory_tabs.dart';
-import 'package:devtools_app/src/screens/memory/panes/chart/widgets/chart_control_pane.dart';
-import 'package:devtools_app/src/screens/memory/panes/chart/widgets/chart_pane.dart';
-import 'package:devtools_app/src/screens/memory/panes/profile/profile_view.dart';
-import 'package:devtools_app/src/screens/memory/panes/tracing/tracing_view.dart';
-import 'package:devtools_app/src/screens/memory/shared/widgets/shared_memory_widgets.dart';
-import 'package:devtools_app_shared/service.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_shared/devtools_shared.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-import '../../test_infra/matchers/matchers.dart';
-import '../../test_infra/test_data/memory.dart';
-import '../../test_infra/test_data/memory/offline/memory_offline_data.dart'
-    as memory_offline;
-import '../../test_infra/test_data/memory_allocation.dart';
-
-void main() {
-  late MemoryController controller;
-  late FakeServiceConnectionManager fakeServiceConnection;
-
-  // Load canned data testHeapSampleData.
-  final memoryJson =
-      SamplesMemoryJson.decode(argJsonString: testHeapSampleData);
-  final allocationJson =
-      AllocationMemoryJson.decode(argJsonString: testAllocationData);
-
-  void setUpServiceManagerForMemory() {
-    fakeServiceConnection = FakeServiceConnectionManager(
-      service: FakeServiceManager.createFakeService(
-        memoryData: memoryJson,
-        allocationData: allocationJson,
-      ),
-    );
-    when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow)
-        .thenReturn(false);
-    when(fakeServiceConnection.serviceManager.connectedApp!.isFlutterAppNow)
-        .thenReturn(true);
-    when(fakeServiceConnection.serviceManager.connectedApp!.isDartCliAppNow)
-        .thenReturn(false);
-    when(
-      fakeServiceConnection.serviceManager.connectedApp!.isDebugFlutterAppNow,
-    ).thenReturn(false);
-    when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebApp)
-        .thenAnswer((_) => Future.value(false));
-    setGlobal(ServiceConnectionManager, fakeServiceConnection);
-  }
-
-  Future<void> pumpMemoryScreen(WidgetTester tester) async {
-    await tester.pumpWidget(
-      wrapWithControllers(
-        const MemoryScreenBody(),
-        memory: controller,
-      ),
-    );
-
-    // Delay to ensure the memory profiler has collected data.
-    await tester.runAsync(
-      () async => await tester.pumpAndSettle(const Duration(seconds: 2)),
-    );
-    expect(find.byType(MemoryScreenBody), findsOneWidget);
-  }
-
-  // Set a wide enough screen width that we do not run into overflow.
-  const windowSize = Size(2225.0, 1000.0);
-
-  group('MemoryScreen', () {
-    setUp(() {
-      setGlobal(
-        DevToolsEnvironmentParameters,
-        ExternalDevToolsEnvironmentParameters(),
-      );
-      setGlobal(PreferencesController, PreferencesController());
-      setGlobal(OfflineDataController, OfflineDataController());
-      setGlobal(IdeTheme, IdeTheme());
-      setGlobal(NotificationService, NotificationService());
-      setGlobal(BannerMessagesController, BannerMessagesController());
-      setGlobal(DTDManager, MockDTDManager());
-      setGlobal(ScriptManager, MockScriptManager());
-      setUpServiceManagerForMemory();
-    });
-
-    testWidgets('builds its tab', (WidgetTester tester) async {
-      await tester.pumpWidget(wrap(Builder(builder: MemoryScreen().buildTab)));
-      expect(find.text('Memory'), findsOneWidget);
-    });
-
-    group('with connected app', () {
-      setUp(() async {
-        controller = MemoryController();
-        await controller.initialized;
-      });
-
-      testWidgetsWithWindowSize(
-        'builds proper content for state',
-        windowSize,
-        (WidgetTester tester) async {
-          await pumpMemoryScreen(tester);
-
-          // Verify chart is visible.
-          expect(
-            find.byTooltip(ChartPaneTooltips.pauseTooltip),
-            findsOneWidget,
-          );
-          expect(
-            find.byTooltip(ChartPaneTooltips.resumeTooltip),
-            findsOneWidget,
-          );
-
-          expect(find.text('GC'), findsOneWidget);
-
-          expect(find.byType(MemoryChartPane), findsOneWidget);
-        },
-      );
-    });
-
-    group('with offline data', () {
-      setUp(() async {
-        offlineDataController
-          ..offlineDataJson = memory_offline.data
-          ..startShowingOfflineData(
-            offlineApp: serviceConnection.serviceManager.connectedApp!,
-          );
-
-        controller = MemoryController();
-        await controller.initialized;
-      });
-
-      testWidgetsWithWindowSize(
-        'loads successfully',
-        windowSize,
-        (WidgetTester tester) async {
-          await pumpMemoryScreen(tester);
-
-          // Initial load on the profile tab.
-          expect(find.byType(AllocationProfileTableView), findsOneWidget);
-          expect(find.byType(HeapClassView), findsNWidgets(3));
-          await expectLater(
-            find.byType(MemoryScreenBody),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory/load_offline_data_profile_tab.png',
-            ),
-          );
-
-          // TODO(kenz): investigate why pump and settle times out when
-          // switching to the diff tab. This works when loading offline data
-          // locally, so it is something related to running async code in the
-          // test environment.
-          // Switch to the diff tab.
-          // await tester.runAsync(() async {
-          //   await tester.tap(find.byKey(MemoryScreenKeys.diffTab));
-          //   await tester.pumpAndSettle();
-          // });
-          // await expectLater(
-          //   find.byType(MemoryScreenBody),
-          //   matchesDevToolsGolden(
-          //     '../../test_infra/goldens/memory/load_offline_data_diff_tab.png',
-          //   ),
-          // );
-          // Select a snapshot.
-          // await tester.runAsync(() async {
-          //   expect(find.byType(SnapshotListTitle), findsNWidgets(3));
-          //   await tester.tap(find.byType(SnapshotListTitle).last);
-          //   await tester.pumpAndSettle();
-          // });
-          // await expectLater(
-          //   find.byType(MemoryScreenBody),
-          //   matchesDevToolsGolden(
-          //     '../../test_infra/goldens/memory/load_offline_data_diff_tab_snapshot_selected.png',
-          //   ),
-          // );
-
-          // Switch to the trace tab.
-          await tester.runAsync(() async {
-            await tester.tap(find.byKey(MemoryScreenKeys.traceTab));
-            await tester.pumpAndSettle();
-            await controller.trace!.initialized;
-            await tester.pumpAndSettle();
-
-            // TODO(kenz): remove this selection when the selected class is
-            // included with the offline data for the Trace tab.
-            // Select the first class in the trace tab.
-            await tester.tap(find.byType(HeapClassView).first);
-            await tester.pumpAndSettle();
-
-            // Expand the selected allocation trace.
-            await tester.tap(find.byType(ExpandAllButton));
-            await tester.pumpAndSettle();
-          });
-          expect(find.byType(TracingPane), findsOneWidget);
-          await expectLater(
-            find.byType(MemoryScreenBody),
-            matchesDevToolsGolden(
-              '../../test_infra/goldens/memory/load_offline_data_trace_tab.png',
-            ),
-          );
-        },
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/memory/profile/allocation_profile_table_view_test.dart b/packages/devtools_app/test/memory/profile/allocation_profile_table_view_test.dart
deleted file mode 100644
index bc12158..0000000
--- a/packages/devtools_app/test/memory/profile/allocation_profile_table_view_test.dart
+++ /dev/null
@@ -1,345 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/screens/memory/framework/memory_tabs.dart';
-import 'package:devtools_app/src/screens/memory/panes/profile/model.dart';
-import 'package:devtools_app/src/screens/memory/panes/profile/profile_pane_controller.dart';
-import 'package:devtools_app/src/shared/globals.dart';
-import 'package:devtools_app/src/shared/memory/gc_stats.dart';
-import 'package:devtools_app/src/shared/primitives/byte_utils.dart';
-import 'package:devtools_app/src/shared/primitives/utils.dart';
-import 'package:devtools_app/src/shared/table/table.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../test_infra/scenes/memory/default.dart';
-
-void main() {
-  late MemoryDefaultScene scene;
-
-  setUp(() async {
-    scene = MemoryDefaultScene();
-    await scene.setUp();
-  });
-
-  // Set a wide enough screen width that we do not run into overflow.
-  const windowSize = Size(2225.0, 1200.0);
-  //setGlobal(NotificationService, NotificationService());
-
-  group('Allocation Profile Table', () {
-    Future<void> navigateToAllocationProfile(
-      WidgetTester tester,
-      ProfilePaneController allocationProfileController,
-    ) async {
-      await tester.tap(find.byKey(MemoryScreenKeys.profileTab));
-      await tester.pumpAndSettle();
-
-      // We should have requested an allocation profile by navigating to the tab.
-      expect(
-        allocationProfileController.currentAllocationProfile.value,
-        isNotNull,
-      );
-    }
-
-    testWidgetsWithWindowSize(
-      'respects VM Developer Mode setting',
-      windowSize,
-      (WidgetTester tester) async {
-        await scene.pump(tester);
-
-        final allocationProfileController = scene.controller.profile!;
-
-        preferences.toggleVmDeveloperMode(false);
-        await navigateToAllocationProfile(tester, allocationProfileController);
-
-        // Only "total" statistics are shown when VM Developer Mode is disabled.
-        expect(preferences.vmDeveloperModeEnabled.value, isFalse);
-        expect(find.text('Class'), findsOneWidget);
-        expect(find.text('Instances'), findsOneWidget);
-        expect(find.text('Total Size'), findsOneWidget);
-        expect(find.text('Dart Heap'), findsOneWidget);
-        expect(find.text('External'), findsNothing);
-        expect(find.text('Old Space'), findsNothing);
-        expect(find.text('New Space'), findsNothing);
-        expect(find.text('Usage'), findsNothing);
-        expect(find.text('Capacity'), findsNothing);
-        expect(find.text('Collections'), findsNothing);
-        expect(find.text('Latency'), findsNothing);
-
-        // Enable VM Developer Mode to display new/old space column groups as
-        // well as GC statistics.
-        preferences.toggleVmDeveloperMode(true);
-        await tester.pumpAndSettle();
-
-        expect(preferences.vmDeveloperModeEnabled.value, isTrue);
-        expect(find.text('Class'), findsOneWidget);
-        expect(find.text('Instances'), findsNWidgets(3));
-        expect(find.text('Total Size'), findsNWidgets(3));
-        expect(find.text('Dart Heap'), findsNWidgets(3));
-        expect(find.text('External'), findsNWidgets(3));
-        expect(find.text('Total'), findsOneWidget);
-        expect(find.text('Old Space'), findsOneWidget);
-        expect(find.text('New Space'), findsOneWidget);
-        expect(find.text('Usage'), findsNWidgets(3));
-        expect(find.text('Capacity'), findsNWidgets(3));
-        expect(find.text('Collections'), findsNWidgets(3));
-        expect(find.text('Latency'), findsNWidgets(3));
-        final currentProfile =
-            allocationProfileController.currentAllocationProfile.value!;
-
-        void checkGCStats(GCStats stats) {
-          // Usage
-          expect(
-            find.text(
-              prettyPrintBytes(
-                stats.usage,
-                includeUnit: true,
-              )!,
-              findRichText: true,
-            ),
-            findsWidgets,
-          );
-
-          // Capacity
-          expect(
-            find.text(
-              prettyPrintBytes(
-                stats.capacity,
-                includeUnit: true,
-              )!,
-              findRichText: true,
-            ),
-            findsWidgets,
-          );
-
-          // Average collection time
-          expect(
-            find.text(
-              durationText(
-                Duration(milliseconds: stats.averageCollectionTime.toInt()),
-                fractionDigits: 2,
-              ),
-              findRichText: true,
-            ),
-            findsWidgets,
-          );
-
-          // # of collections
-          expect(
-            find.text(
-              stats.collections.toString(),
-              findRichText: true,
-            ),
-            findsWidgets,
-          );
-        }
-
-        checkGCStats(currentProfile.newSpaceGCStats);
-        checkGCStats(currentProfile.oldSpaceGCStats);
-        checkGCStats(currentProfile.totalGCStats);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'manually refreshes',
-      windowSize,
-      (WidgetTester tester) async {
-        await scene.pump(tester);
-
-        final allocationProfileController = scene.controller.profile!;
-        await navigateToAllocationProfile(tester, allocationProfileController);
-
-        // We'll clear it for now so we can tell when it's refreshed.
-        allocationProfileController.clearCurrentProfile();
-        await tester.pump();
-
-        // Refresh the profile.
-        await tester.tap(
-          find.byIcon(Icons.refresh).first,
-        );
-        await tester.pumpAndSettle();
-
-        // Ensure that we have populated the current allocation profile.
-        expect(
-          allocationProfileController.currentAllocationProfile.value,
-          isNotNull,
-        );
-
-        expect(find.text('Class'), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'refreshes on GC',
-      windowSize,
-      (WidgetTester tester) async {
-        await scene.pump(tester);
-
-        final allocationProfileController = scene.controller.profile!;
-
-        await navigateToAllocationProfile(tester, allocationProfileController);
-
-        // We'll clear it for now so we can tell when it's refreshed.
-        allocationProfileController.clearCurrentProfile();
-        await tester.pump();
-
-        // Emit a GC event and confirm we don't perform a refresh.
-        final fakeService = scene.fakeServiceConnection.serviceManager.service
-            as FakeVmServiceWrapper;
-        fakeService.emitGCEvent();
-        expect(
-          allocationProfileController.currentAllocationProfile.value,
-          isNull,
-        );
-
-        // Enable "Refresh on GC" functionality.
-        await tester.tap(
-          find.text('Refresh on GC').first,
-        );
-        await tester.pump();
-
-        // Emit a GC event to trigger a refresh.
-        fakeService.emitGCEvent();
-        // Time to refresh.
-        await tester.runAsync(() => Future.delayed(const Duration(seconds: 1)));
-        await tester.pumpAndSettle();
-
-        // Ensure that we have populated the current allocation profile.
-        expect(
-          allocationProfileController.currentAllocationProfile.value,
-          isNotNull,
-        );
-      },
-    );
-
-    // Regression test for https://github.com/flutter/devtools/issues/4484.
-    testWidgetsWithWindowSize(
-      'sorts correctly',
-      windowSize,
-      (WidgetTester tester) async {
-        await scene.pump(tester);
-
-        final table = find.byType(FlatTable<ProfileRecord>);
-        expect(table, findsOneWidget);
-
-        final cls = find.text('Class');
-        final instances = find.text('Instances');
-        final size = find.text('Total Size');
-        final dartHeap = find.text('Dart Heap');
-
-        final columns = <Finder>[
-          cls,
-          instances,
-          size,
-          dartHeap,
-        ];
-
-        for (final columnFinder in columns) {
-          expect(columnFinder, findsOneWidget);
-        }
-
-        final state = tester.state<FlatTableState<ProfileRecord>>(table.first);
-        var data = state.tableController.tableData.value.data;
-
-        // Initial state should be sorted by size, largest to smallest.
-        int lastValue = data.first.totalDartHeapSize;
-        for (final element in data) {
-          expect(element.totalDartHeapSize <= lastValue, isTrue);
-          lastValue = element.totalDartHeapSize;
-        }
-
-        // Sort by size, smallest to largest.
-        await tester.tap(size);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastValue = data.first.totalDartHeapSize;
-        for (final element in data) {
-          expect(element.totalDartHeapSize >= lastValue, isTrue);
-          lastValue = element.totalDartHeapSize;
-        }
-
-        // Sort by class name, alphabetically
-        await tester.tap(cls);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        String lastClassName = data.first.heapClass.className;
-        for (final element in data) {
-          final name = element.heapClass.className;
-          expect(name.compareTo(lastClassName) >= 0, isTrue);
-          lastClassName = name;
-        }
-
-        // Sort by class name, reverse alphabetical order
-        await tester.tap(cls);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastClassName = data.first.heapClass.className;
-        for (final element in data) {
-          final name = element.heapClass.className;
-          expect(name.compareTo(lastClassName) <= 0, isTrue);
-          lastClassName = name;
-        }
-
-        // Sort by instance count, largest to smallest.
-        await tester.tap(instances);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastValue = data.first.totalInstances!;
-        for (final element in data) {
-          if (element.isTotal) continue;
-          expect(element.totalInstances! <= lastValue, isTrue);
-          lastValue = element.totalInstances!;
-        }
-
-        // Sort by instance count, smallest to largest.
-        await tester.tap(instances);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastValue = data.first.totalInstances!;
-        for (final element in data) {
-          expect(element.totalInstances! >= lastValue, isTrue);
-          lastValue = element.totalInstances!;
-        }
-
-        // Sort by dart heap size, largest to smallest.
-        await tester.tap(dartHeap);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastValue = data.first.totalDartHeapSize;
-        for (final element in data) {
-          final internalSize = element.totalDartHeapSize;
-          expect(internalSize <= lastValue, isTrue);
-          lastValue = internalSize;
-        }
-
-        // Sort by dart heap size, smallest to largest.
-        await tester.tap(dartHeap);
-        await tester.pumpAndSettle();
-
-        data = state.tableController.tableData.value.data;
-
-        lastValue = data.first.totalDartHeapSize;
-        for (final element in data) {
-          final internalSize = element.totalDartHeapSize;
-          expect(internalSize >= lastValue, isTrue);
-          lastValue = internalSize;
-        }
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/memory/shared/heap/heap_test.dart b/packages/devtools_app/test/memory/shared/heap/heap_test.dart
deleted file mode 100644
index 4a10faa..0000000
--- a/packages/devtools_app/test/memory/shared/heap/heap_test.dart
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/src/shared/memory/class_name.dart';
-import 'package:devtools_app/src/shared/memory/classes.dart';
-import 'package:devtools_app/src/shared/memory/heap_data.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../../../test_infra/test_data/memory/heap/heap_graph_fakes.dart';
-
-class _ClassSizeTest {
-  _ClassSizeTest({
-    required this.name,
-    required this.heap,
-    required this.expectedClassARetainedSize,
-  });
-
-  final FakeHeapSnapshotGraph heap;
-  final String name;
-  final int expectedClassARetainedSize;
-}
-
-final _root = HeapClassName.fromPath(className: 'Root', library: 'l');
-final _classA = HeapClassName.fromPath(className: 'A', library: 'l');
-final _classB = HeapClassName.fromPath(className: 'B', library: 'l');
-
-final _classSizeTests = <_ClassSizeTest>[
-  _ClassSizeTest(
-    name: 'separate',
-    heap: FakeHeapSnapshotGraph()
-      ..setObjects(
-        {
-          1: [2, 3, 4],
-          2: [],
-          3: [],
-          4: [],
-        },
-        classes: {
-          1: _root,
-          2: _classA,
-          3: _classA,
-          4: _classA,
-        },
-      ),
-    expectedClassARetainedSize: 3,
-  ),
-  _ClassSizeTest(
-    name: 'linked',
-    heap: FakeHeapSnapshotGraph()
-      ..setObjects(
-        {
-          1: [2],
-          2: [3],
-          3: [4],
-          4: [],
-        },
-        classes: {
-          1: _root,
-          2: _classA,
-          3: _classA,
-          4: _classA,
-        },
-      ),
-    expectedClassARetainedSize: 3,
-  ),
-  _ClassSizeTest(
-    name: 'full graph',
-    heap: FakeHeapSnapshotGraph()
-      ..setObjects(
-        {
-          1: [2],
-          2: [3, 4],
-          3: [2, 4],
-          4: [2, 3],
-        },
-        classes: {
-          1: _root,
-          2: _classA,
-          3: _classA,
-          4: _classA,
-        },
-      ),
-    expectedClassARetainedSize: 3,
-  ),
-  _ClassSizeTest(
-    name: 'with global B',
-    heap: FakeHeapSnapshotGraph()
-      ..setObjects(
-        {
-          1: [2],
-          2: [3, 5],
-          3: [4, 5],
-          4: [2, 3],
-          5: [],
-        },
-        classes: {
-          1: _root,
-          2: _classA,
-          3: _classA,
-          4: _classA,
-          5: _classB,
-        },
-      ),
-    expectedClassARetainedSize: 4,
-  ),
-];
-
-void main() {
-  for (final t in _classSizeTests) {
-    test(
-      '$SingleClassData does not double-count self-referenced classes, ${t.name}.',
-      () async {
-        final heapData = HeapData(
-          t.heap,
-          created: DateTime.now(),
-        );
-        await heapData.calculate;
-
-        final classes = heapData.classes!;
-        final classData = classes.byName(_classA)!;
-
-        expect(
-          classData.objects.retainedSize,
-          t.expectedClassARetainedSize,
-          reason: t.name,
-        );
-      },
-    );
-  }
-}
diff --git a/packages/devtools_app/test/memory/tracing/tracing_view_test.dart b/packages/devtools_app/test/memory/tracing/tracing_view_test.dart
deleted file mode 100644
index 93aacf1..0000000
--- a/packages/devtools_app/test/memory/tracing/tracing_view_test.dart
+++ /dev/null
@@ -1,479 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/memory/framework/memory_tabs.dart';
-import 'package:devtools_app/src/screens/memory/panes/tracing/tracing_pane_controller.dart';
-import 'package:devtools_app/src/screens/memory/panes/tracing/tracing_tree.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../test_infra/scenes/memory/default.dart';
-import '../../test_infra/utils/test_utils.dart';
-
-// TODO(bkonyi): add tests for multi-isolate support.
-// See https://github.com/flutter/devtools/issues/4537.
-
-void main() {
-  final classList = ClassList(
-    classes: [
-      ClassRef(id: 'cls/1', name: 'ClassA'),
-      ClassRef(id: 'cls/2', name: 'ClassB'),
-      ClassRef(id: 'cls/3', name: 'ClassC'),
-      ClassRef(id: 'cls/4', name: 'Foo'),
-    ],
-  );
-
-  /// Clears the class filter text field.
-  Future<void> clearFilter(
-    WidgetTester tester,
-    TracePaneController controller,
-  ) async {
-    final originalClassCount = classList.classes!.length;
-    final clearFilterButton = find.byIcon(Icons.clear);
-    expect(clearFilterButton, findsOneWidget);
-    await tester.tap(clearFilterButton);
-    await tester.pumpAndSettle();
-    expect(
-      controller.selection.value.filteredClassList.value.length,
-      originalClassCount,
-    );
-  }
-
-  // Set a wide enough screen width that we do not run into overflow.
-  const windowSize = Size(2225.0, 1000.0);
-
-  group('Allocation Tracing', () {
-    late MemoryDefaultScene scene;
-
-    late final CpuSamples allocationTracingProfile;
-
-    Future<void> pumpScene(WidgetTester tester) async {
-      await scene.pump(tester);
-      await tester.tap(
-        find.byKey(MemoryScreenKeys.traceTab),
-      );
-      await tester.pumpAndSettle();
-    }
-
-    setUpAll(() {
-      final rawProfile = File(
-        'test/test_infra/test_data/memory/allocation_tracing/allocation_trace.json',
-      ).readAsStringSync();
-      allocationTracingProfile = CpuSamples.parse(jsonDecode(rawProfile))!;
-    });
-
-    setUp(() async {
-      setCharacterWidthForTables();
-
-      scene = MemoryDefaultScene();
-      await scene.setUp(classList: classList);
-      mockConnectedApp(
-        scene.fakeServiceConnection.serviceManager.connectedApp!,
-        isFlutterApp: true,
-        isProfileBuild: false,
-        isWebApp: false,
-      );
-
-      final mockScriptManager = MockScriptManager();
-      when(mockScriptManager.sortedScripts).thenReturn(
-        ValueNotifier<List<ScriptRef>>([]),
-      );
-      when(mockScriptManager.scriptRefForUri(any)).thenReturn(
-        ScriptRef(
-          uri: 'package:test/script.dart',
-          id: 'script.dart',
-        ),
-      );
-      setGlobal(ScriptManager, mockScriptManager);
-    });
-
-    tearDown(() {
-      scene.tearDown();
-    });
-
-    testWidgetsWithWindowSize(
-      'basic tracing flow',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpScene(tester);
-
-        final controller = scene.controller.trace!;
-        final state = controller.selection.value;
-        expect(state.filteredClassList.value.isNotEmpty, isTrue);
-        expect(controller.refreshing.value, isFalse);
-        expect(state.selectedClass.value, isNull);
-        expect(state.selectedClassProfile, isNull);
-
-        final refresh = find.text('Refresh');
-        expect(refresh, findsOneWidget);
-
-        expect(find.text('Trace'), findsOneWidget);
-        expect(find.text('Class'), findsOneWidget);
-        expect(find.text('Delta'), findsOneWidget);
-
-        // There should be classes in the example class list.
-        expect(find.byType(Checkbox), findsNWidgets(classList.classes!.length));
-        for (final cls in state.filteredClassList.value) {
-          expect(find.byKey(Key(cls.clazz.id!)), findsOneWidget);
-        }
-
-        // Enable allocation tracing for one of them.
-        await tester.tap(find.byType(Checkbox).first);
-        await tester.pumpAndSettle();
-
-        expect(
-          state.filteredClassList.value
-              .map((e) => e.traceAllocations)
-              .where((e) => e)
-              .length,
-          1,
-        );
-
-        final selectedTrace = state.filteredClassList.value.firstWhere(
-          (e) => e.traceAllocations,
-        );
-
-        expect(find.byType(TracingTable), findsNothing);
-        final traceElement = find.byKey(Key(selectedTrace.clazz.id!));
-        expect(traceElement, findsOneWidget);
-
-        // Select the list item for the traced class and refresh to fetch data.
-        await tester.tap(traceElement);
-        await tester.pumpAndSettle();
-        await tester.tap(refresh);
-        await tester.pumpAndSettle();
-
-        // No allocations have occurred, so the trace viewer shows an error message.
-        expect(state.selectedClass.value, selectedTrace);
-        expect(state.selectedClassProfile, isNotNull);
-        expect(
-          find.text(
-            'No allocation samples have been collected for class ${selectedTrace.clazz.name}.\n',
-          ),
-          findsOneWidget,
-        );
-
-        // Set fake sample data and refresh to populate the trace view.
-        final fakeService =
-            serviceConnection.serviceManager.service as FakeVmServiceWrapper;
-        fakeService.allocationSamples = allocationTracingProfile;
-
-        await tester.tap(refresh);
-        await tester.pumpAndSettle();
-        expect(
-          find.byType(TracingTable),
-          findsOneWidget,
-        );
-
-        // Verify the expected widget components are present.
-        expect(find.textContaining('Traced allocations for: '), findsOneWidget);
-        expect(find.text('Bottom Up'), findsOneWidget);
-        expect(find.text('Call Tree'), findsOneWidget);
-        expect(find.text('Expand All'), findsOneWidget);
-        expect(find.text('Collapse All'), findsOneWidget);
-        expect(find.text('Inclusive'), findsOneWidget);
-        expect(find.text('Exclusive'), findsOneWidget);
-        expect(find.text('Method'), findsOneWidget);
-
-        final bottomUpRoots = state.selectedClassProfile!.bottomUpRoots;
-        final callTreeRoots = state.selectedClassProfile!.callTreeRoots;
-        for (final root in bottomUpRoots) {
-          expect(root.isExpanded, false);
-        }
-        for (final root in callTreeRoots) {
-          expect(root.isExpanded, false);
-        }
-
-        await tester.tap(find.text('Expand All'));
-        await tester.pumpAndSettle();
-
-        // Check all nodes in the bottom up tree have been expanded.
-        for (final root in bottomUpRoots) {
-          breadthFirstTraversal<CpuStackFrame>(
-            root,
-            action: (e) {
-              expect(e.isExpanded, true);
-            },
-          );
-        }
-
-        // But also make sure that the call tree nodes haven't been expanded.
-        for (final root in callTreeRoots) {
-          expect(root.isExpanded, false);
-        }
-
-        await tester.tap(find.text('Collapse All'));
-        await tester.pumpAndSettle();
-
-        // Check all nodes have been collapsed.
-        for (final root in bottomUpRoots) {
-          breadthFirstTraversal<CpuStackFrame>(
-            root,
-            action: (e) {
-              expect(e.isExpanded, false);
-            },
-          );
-        }
-
-        // Switch from bottom up view to call tree view.
-        await tester.tap(find.text('Call Tree'));
-        await tester.pumpAndSettle();
-
-        // Expand the call tree.
-        await tester.tap(find.text('Expand All'));
-        await tester.pumpAndSettle();
-
-        // Check all nodes in the call tree have been expanded.
-        for (final root in callTreeRoots) {
-          breadthFirstTraversal<CpuStackFrame>(
-            root,
-            action: (e) {
-              expect(e.isExpanded, true);
-            },
-          );
-        }
-
-        // But also make sure that the bottom up tree nodes haven't been expanded.
-        for (final root in bottomUpRoots) {
-          expect(root.isExpanded, false);
-        }
-
-        await tester.tap(find.text('Collapse All'));
-        await tester.pumpAndSettle();
-
-        // Check all nodes have been collapsed.
-        for (final root in callTreeRoots) {
-          breadthFirstTraversal<CpuStackFrame>(
-            root,
-            action: (e) {
-              expect(e.isExpanded, false);
-            },
-          );
-        }
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'clear state',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpScene(tester);
-
-        final controller = scene.controller.trace!;
-        final state = controller.selection.value;
-        expect(state.filteredClassList.value.isNotEmpty, isTrue);
-        expect(controller.refreshing.value, isFalse);
-        expect(state.selectedClass.value, isNull);
-        expect(state.selectedClassProfile, isNull);
-
-        final refresh = find.text('Refresh');
-        expect(refresh, findsOneWidget);
-
-        expect(find.text('Trace'), findsOneWidget);
-        expect(find.text('Class'), findsOneWidget);
-        expect(find.text('Delta'), findsOneWidget);
-
-        // There should be classes in the example class list.
-        expect(find.byType(Checkbox), findsNWidgets(classList.classes!.length));
-        for (final cls in state.filteredClassList.value) {
-          expect(find.byKey(Key(cls.clazz.id!)), findsOneWidget);
-        }
-
-        // Enable allocation tracing for one of them.
-        await tester.tap(find.byType(Checkbox).first);
-        await tester.pumpAndSettle();
-
-        expect(
-          state.filteredClassList.value
-              .map((e) => e.traceAllocations)
-              .where((e) => e)
-              .length,
-          1,
-        );
-
-        final selectedTrace = state.filteredClassList.value.firstWhere(
-          (e) => e.traceAllocations,
-        );
-
-        expect(find.byType(TracingTable), findsNothing);
-        final traceElement = find.byKey(Key(selectedTrace.clazz.id!));
-        expect(traceElement, findsOneWidget);
-
-        // Select the list item for the traced class and refresh to fetch data.
-        await tester.tap(traceElement);
-        await tester.pumpAndSettle();
-        await tester.tap(refresh);
-        await tester.pumpAndSettle();
-
-        // No allocations have occurred, so the trace viewer shows an error message.
-        expect(state.selectedClass.value, selectedTrace);
-        expect(state.selectedClassProfile, isNotNull);
-        expect(
-          find.text(
-            'No allocation samples have been collected for class ${selectedTrace.clazz.name}.\n',
-          ),
-          findsOneWidget,
-        );
-
-        // Set fake sample data and refresh to populate the trace view.
-        final fakeService =
-            serviceConnection.serviceManager.service as FakeVmServiceWrapper;
-        fakeService.allocationSamples = allocationTracingProfile;
-
-        await tester.tap(refresh);
-        await tester.pumpAndSettle();
-        expect(
-          find.byType(TracingTable),
-          findsOneWidget,
-        );
-
-        final clearButton = find.byType(ClearButton);
-        expect(clearButton, findsOneWidget);
-        await tester.tap(clearButton);
-        await tester.pumpAndSettle();
-
-        // Clearing should zero out all the instance counts.
-        expect(state.selectedClass.value, isNotNull);
-        for (final cls in state.filteredClassList.value) {
-          expect(cls.instances, 0);
-        }
-
-        // Clear the fake sample data to emulate no additional samples collected
-        // after a clear.
-        fakeService.allocationSamples = CpuSamples(
-          functions: [],
-          samples: [],
-          sampleCount: 0,
-          timeOriginMicros: 0,
-          timeExtentMicros: 0,
-        );
-        await tester.tap(refresh);
-        await tester.pumpAndSettle();
-
-        // Expect no new samples.
-        expect(state.selectedClass.value, isNotNull);
-        for (final cls in state.filteredClassList.value) {
-          expect(cls.instances, 0);
-        }
-      },
-    );
-
-    group('filtering', () {
-      testWidgetsWithWindowSize('simple', windowSize, (tester) async {
-        await pumpScene(tester);
-
-        final controller = scene.controller.trace!;
-        final state = controller.selection.value;
-
-        final filterTextField = find.byType(DevToolsClearableTextField);
-        expect(filterTextField, findsOneWidget);
-
-        // Filter for 'F'
-        await tester.enterText(filterTextField, 'F');
-        await tester.pumpAndSettle();
-        expect(state.filteredClassList.value.length, 1);
-        expect(state.filteredClassList.value.first.clazz.name, 'Foo');
-
-        // Filter for 'Fooo'
-        await tester.enterText(filterTextField, 'Fooo');
-        await tester.pumpAndSettle();
-        expect(state.filteredClassList.value.isEmpty, true);
-
-        // Clear filter
-        await clearFilter(tester, controller);
-      });
-
-      testWidgetsWithWindowSize(
-        'persisted tracing state',
-        windowSize,
-        (tester) async {
-          await pumpScene(tester);
-
-          final controller = scene.controller.trace!;
-          final state = controller.selection.value;
-
-          final checkboxes = find.byType(Checkbox);
-          expect(checkboxes, findsNWidgets(classList.classes!.length));
-
-          // Enable allocation tracing for one of them
-          await tester.tap(checkboxes.first);
-          await tester.pumpAndSettle();
-
-          final tracedClassList = state.filteredClassList.value
-              .where((e) => e.traceAllocations)
-              .toList();
-          expect(tracedClassList.length, 1);
-          expect(tracedClassList.first.clazz, classList.classes!.first);
-
-          // Filter out all classes and then clear the filter
-          final filterTextField = find.byType(DevToolsClearableTextField);
-          expect(filterTextField, findsOneWidget);
-
-          await tester.enterText(filterTextField, 'Garbage');
-          await tester.pumpAndSettle();
-          expect(state.filteredClassList.value.isEmpty, true);
-
-          await clearFilter(tester, controller);
-
-          // Check tracing state wasn't corrupted
-          final updatedTracedClassList = state.filteredClassList.value
-              .where((e) => e.traceAllocations)
-              .toList();
-          expect(updatedTracedClassList, containsAll(tracedClassList));
-          expect(updatedTracedClassList.first.traceAllocations, true);
-        },
-      );
-
-      testWidgetsWithWindowSize(
-        'persisted selection state',
-        windowSize,
-        (tester) async {
-          await pumpScene(tester);
-
-          final controller = scene.controller.trace!;
-          final state = controller.selection.value;
-
-          expect(state.selectedClass.value, isNull);
-
-          // Select one of the class entries.
-          final selection = find.richTextContaining(
-            classList.classes!.last.name!,
-          );
-          expect(selection, findsOneWidget);
-
-          await tester.tap(selection);
-          await tester.pumpAndSettle();
-
-          expect(state.selectedClass.value, isNotNull);
-          final originalSelection = state.selectedClass.value;
-
-          // Filter out all classes, ensure the selection is still valid, then
-          // clear the filter and check again.
-          final filterTextField = find.byType(DevToolsClearableTextField);
-          expect(filterTextField, findsOneWidget);
-
-          await tester.enterText(filterTextField, 'Garbage');
-          await tester.pumpAndSettle();
-          expect(state.filteredClassList.value.isEmpty, true);
-
-          expect(state.selectedClass.value, originalSelection);
-
-          await clearFilter(tester, controller);
-
-          expect(state.selectedClass.value, originalSelection);
-        },
-      );
-    });
-  });
-}
diff --git a/packages/devtools_app/test/network/network_profiler_test.dart b/packages/devtools_app/test/network/network_profiler_test.dart
deleted file mode 100644
index 63d1e5f..0000000
--- a/packages/devtools_app/test/network/network_profiler_test.dart
+++ /dev/null
@@ -1,559 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm')
-library;
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/network/network_request_inspector.dart';
-import 'package:devtools_app/src/screens/network/network_request_inspector_views.dart';
-import 'package:devtools_app/src/shared/http/http.dart';
-import 'package:devtools_app/src/shared/ui/tab.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../test_infra/test_data/network.dart';
-import 'utils/network_test_utils.dart';
-
-NetworkController controller = NetworkController();
-DebuggerController debugController = DebuggerController();
-
-Future<void> pumpNetworkScreen(WidgetTester tester) async {
-  await tester.pumpWidget(
-    wrapWithControllers(
-      const NetworkScreenBody(),
-      network: controller,
-      debugger: debugController,
-    ),
-  );
-  final finder = find.byType(NetworkScreenBody);
-  expect(finder, findsOneWidget);
-}
-
-/// Clears the timeouts created when calling getHttpTimelineLogging and
-/// setHttpTimelineLogging RPCs.
-Future<void> clearTimeouts(WidgetTester tester) async {
-  await tester.pumpAndSettle(const Duration(seconds: 1));
-}
-
-void main() {
-  late FakeServiceConnectionManager fakeServiceConnection;
-  late SocketProfile socketProfile;
-  late HttpProfile httpProfile;
-
-  const windowSize = Size(1599.0, 1000.0);
-
-  setUpAll(() {
-    socketProfile = loadSocketProfile();
-    httpProfile = loadHttpProfile();
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(NotificationService, NotificationService());
-    setGlobal(BreakpointManager, BreakpointManager());
-  });
-
-  group('Network Profiler', () {
-    setUp(() {
-      fakeServiceConnection = FakeServiceConnectionManager(
-        service: FakeServiceManager.createFakeService(
-          socketProfile: socketProfile,
-          httpProfile: httpProfile,
-        ),
-      );
-      setGlobal(ServiceConnectionManager, fakeServiceConnection);
-    });
-
-    testWidgetsWithWindowSize('starts and stops', windowSize, (
-      WidgetTester tester,
-    ) async {
-      controller = NetworkController();
-
-      // Ensure we're not recording initially.
-      expect(controller.isPolling, false);
-      expect(controller.recordingNotifier.value, false);
-
-      await pumpNetworkScreen(tester);
-      await tester.pumpAndSettle();
-
-      // Check that we're polling.
-      expect(controller.isPolling, true);
-      expect(controller.recordingNotifier.value, true);
-
-      // Pause recording.
-      expect(find.byType(StartStopRecordingButton), findsOneWidget);
-      await tester.tap(find.byType(StartStopRecordingButton));
-      await tester.pumpAndSettle();
-
-      // Check that we've stopped polling.
-      expect(controller.isPolling, false);
-      expect(controller.recordingNotifier.value, false);
-
-      await clearTimeouts(tester);
-    });
-
-    Future<void> loadRequestsAndCheck(WidgetTester tester) async {
-      expect(find.byType(StartStopRecordingButton), findsOneWidget);
-      expect(find.byType(ClearButton), findsOneWidget);
-      expect(find.byType(SplitPane), findsOneWidget);
-
-      // Advance the clock to populate the network requests table.
-      await tester.pump(const Duration(seconds: 2));
-      expect(find.byType(CircularProgressIndicator), findsNothing);
-
-      expect(controller.requests.value, isNotEmpty);
-    }
-
-    // We should see the list of requests and the inspector, but have no
-    // selected request.
-    void expectNoSelection() {
-      expect(find.byType(NetworkRequestsTable), findsOneWidget);
-      expect(find.byType(NetworkRequestInspector), findsOneWidget);
-      expect(find.text('No request selected'), findsOneWidget);
-      expect(controller.selectedRequest.value, isNull);
-    }
-
-    testWidgetsWithWindowSize(
-      'builds proper content for state',
-      windowSize,
-      (WidgetTester tester) async {
-        controller = NetworkController();
-        await pumpNetworkScreen(tester);
-
-        await loadRequestsAndCheck(tester);
-
-        expectNoSelection();
-
-        Future<void> validateHeadersTab(DartIOHttpRequestData data) async {
-          // Switch to headers tab.
-          await tester.tap(
-            find.descendant(
-              of: find.byType(DevToolsTab),
-              matching: find.text('Headers'),
-            ),
-          );
-          await tester.pumpAndSettle();
-
-          expect(find.byType(NetworkRequestOverviewView), findsNothing);
-          expect(find.byType(HttpRequestHeadersView), findsOneWidget);
-          expect(find.byType(HttpResponseView), findsNothing);
-          expect(find.byType(HttpRequestCookiesView), findsNothing);
-
-          // TODO(kenz): move the headers tab validation into its own testing
-          // group (see NetworkRequestOverviewView test group).
-
-          // There should be three tiles: general, response headers, and request
-          // headers.
-          expect(find.byType(ExpansionTile), findsNWidgets(3));
-
-          // Check contents of general.
-          final ExpansionTile generalTile =
-              tester.widget(find.byKey(HttpRequestHeadersView.generalKey));
-
-          final numGeneralEntries = data.general.length;
-          expect(generalTile.children.length, numGeneralEntries);
-
-          // Check contents of request headers.
-          final ExpansionTile requestsTile = tester
-              .widget(find.byKey(HttpRequestHeadersView.requestHeadersKey));
-          final numRequestHeaders = data.requestHeaders?.length ?? 0;
-          expect(requestsTile.children.length, numRequestHeaders);
-
-          // Check contents of response headers.
-          final ExpansionTile responsesTile = tester
-              .widget(find.byKey(HttpRequestHeadersView.responseHeadersKey));
-          final numResponseHeaders = data.responseHeaders?.length ?? 0;
-          expect(responsesTile.children.length, numResponseHeaders);
-        }
-
-        Future<void> validateResponseTab(DartIOHttpRequestData data) async {
-          if (data.responseBody != null) {
-            // Switch to response tab.
-            await tester.tap(
-              find.descendant(
-                of: find.byType(DevToolsTab),
-                matching: find.text('Response'),
-              ),
-            );
-            await tester.pumpAndSettle();
-
-            expect(find.byType(HttpResponseTrailingDropDown), findsOneWidget);
-            expect(find.byType(HttpViewTrailingCopyButton), findsOneWidget);
-            expect(find.byType(NetworkRequestOverviewView), findsNothing);
-            expect(find.byType(HttpRequestHeadersView), findsNothing);
-            expect(find.byType(HttpResponseView), findsOneWidget);
-            expect(find.byType(HttpRequestCookiesView), findsNothing);
-          }
-        }
-
-        Future<void> validateOverviewTab() async {
-          // Switch to overview tab.
-          await tester.tap(
-            find.descendant(
-              of: find.byType(DevToolsTab),
-              matching: find.text('Overview'),
-            ),
-          );
-          await tester.pumpAndSettle();
-
-          expect(find.byType(NetworkRequestOverviewView), findsOneWidget);
-          expect(find.byType(HttpRequestHeadersView), findsNothing);
-          expect(find.byType(HttpResponseView), findsNothing);
-          expect(find.byType(HttpRequestCookiesView), findsNothing);
-        }
-
-        Future<void> validateCookiesTab(DartIOHttpRequestData data) async {
-          final httpRequest =
-              controller.selectedRequest.value as DartIOHttpRequestData;
-          final hasCookies = httpRequest.hasCookies;
-
-          if (hasCookies) {
-            // Switch to cookies tab.
-            await tester.tap(
-              find.descendant(
-                of: find.byType(DevToolsTab),
-                matching: find.text('Cookies'),
-              ),
-            );
-            await tester.pumpAndSettle();
-
-            expect(find.byType(NetworkRequestOverviewView), findsNothing);
-            expect(find.byType(HttpRequestHeadersView), findsNothing);
-            expect(find.byType(HttpResponseView), findsNothing);
-            expect(find.byType(HttpRequestCookiesView), findsOneWidget);
-
-            // TODO(kenz): move the cookie tab validation into its own testing
-            // group (see NetworkRequestOverviewView test group).
-
-            // Checks the contents of a cookies table to ensure it's well formed.
-            void validateCookieTable(List<Cookie> cookies, Key key) {
-              expect(
-                find.byKey(key),
-                findsOneWidget,
-              );
-              final cookieCount = cookies.length;
-              final DataTable cookiesTable = tester.widget(
-                find.byKey(key),
-              );
-              expect(cookiesTable.rows.length, cookieCount);
-            }
-
-            // Check the request cookies table.
-            if (data.requestCookies.isNotEmpty) {
-              validateCookieTable(
-                data.requestCookies,
-                HttpRequestCookiesView.requestCookiesKey,
-              );
-            }
-
-            // Check the response cookies table.
-            if (data.responseCookies.isNotEmpty) {
-              validateCookieTable(
-                data.responseCookies,
-                HttpRequestCookiesView.responseCookiesKey,
-              );
-            }
-          } else {
-            // The cookies tab shouldn't be displayed if there are no cookies
-            // associated with the request.
-            expect(
-              find.descendant(
-                of: find.byType(DevToolsTab),
-                matching: find.text('Cookies'),
-              ),
-              findsNothing,
-            );
-          }
-        }
-
-        for (final request in controller.requests.value) {
-          controller.selectedRequest.value = request;
-          await tester.pumpAndSettle();
-          expect(find.text('No request selected'), findsNothing);
-
-          final selection = controller.selectedRequest.value!;
-          if (selection is DartIOHttpRequestData) {
-            await validateHeadersTab(selection);
-            await validateResponseTab(selection);
-            await validateCookiesTab(selection);
-          }
-          await validateOverviewTab();
-        }
-
-        // Pause recording.
-        await tester.tap(find.byType(StartStopRecordingButton));
-        await tester.pump();
-
-        await clearTimeouts(tester);
-      },
-    );
-
-    // Regression test for https://github.com/flutter/devtools/issues/3286.
-    testWidgetsWithWindowSize(
-      'can select by clicking on url',
-      windowSize,
-      (WidgetTester tester) async {
-        // Load the network profiler screen.
-        controller = NetworkController();
-        await pumpNetworkScreen(tester);
-
-        // Populate the screen with requests.
-        await loadRequestsAndCheck(tester);
-
-        expectNoSelection();
-
-        final textElement = tester.element(
-          find
-              .text(
-                'https://jsonplaceholder.typicode.com/albums/1?userId=1&title=myalbum',
-              )
-              .first,
-        );
-        final selectableTextWidget =
-            textElement.findAncestorWidgetOfExactType<SelectableText>()!;
-        await tester.tap(find.byWidget(selectableTextWidget));
-        await tester.pumpAndSettle();
-
-        expect(controller.selectedRequest.value, isNotNull);
-        expect(find.text('No request selected'), findsNothing);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'clear results',
-      windowSize,
-      (WidgetTester tester) async {
-        // Load the network profiler screen.
-        controller = NetworkController();
-        await pumpNetworkScreen(tester);
-
-        // Populate the screen with requests.
-        await loadRequestsAndCheck(tester);
-
-        // Pause the profiler.
-        await tester.tap(find.byType(StartStopRecordingButton));
-        await tester.pumpAndSettle();
-
-        // Clear the results.
-        await tester.tap(find.byType(ClearButton));
-        // Wait to ensure all the timers have been cancelled.
-        await tester.pumpAndSettle(const Duration(seconds: 2));
-      },
-    );
-  });
-
-  group('NetworkRequestOverviewView', () {
-    Future<void> pumpView(WidgetTester tester, NetworkRequest data) async {
-      final widget = wrap(NetworkRequestOverviewView(data));
-      await tester.pumpWidget(widget);
-      await tester.pumpAndSettle();
-      expect(find.byType(NetworkRequestOverviewView), findsOneWidget);
-    }
-
-    testWidgetsWithWindowSize(
-      'displays for http request',
-      windowSize,
-      (tester) async {
-        final data = httpGet;
-        await pumpView(tester, data);
-
-        // Verify general information.
-        expect(find.text('Request uri: '), findsOneWidget);
-        expect(
-          find.text(
-            'https://jsonplaceholder.typicode.com/albums/1?userId=1&title=myalbum',
-          ),
-          findsOneWidget,
-        );
-        expect(find.text('Method: '), findsOneWidget);
-        expect(find.text('GET'), findsOneWidget);
-        expect(find.text('Status: '), findsOneWidget);
-        expect(find.text('200'), findsOneWidget);
-        expect(find.text('Port: '), findsOneWidget);
-        expect(find.text('45648'), findsOneWidget);
-        expect(find.text('Content type: '), findsOneWidget);
-        expect(find.text('[application/json; charset=utf-8]'), findsOneWidget);
-
-        // Verify timing information.
-        expect(find.text('Timing: '), findsOneWidget);
-        expect(find.text('Start time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.startTimestamp)), findsOneWidget);
-        expect(find.text('End time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.endTimestamp!)), findsOneWidget);
-        expect(
-          find.byKey(NetworkRequestOverviewView.httpTimingGraphKey),
-          findsOneWidget,
-        );
-        expect(find.text('Connection established: '), findsOneWidget);
-        expect(
-          find.text('[0.0 ms - 529.0 ms] → 529.0 ms total'),
-          findsOneWidget,
-        );
-        expect(find.text('Request sent: '), findsOneWidget);
-        expect(
-          find.text('[529.0 ms - 529.0 ms] → 0.0 ms total'),
-          findsOneWidget,
-        );
-        expect(find.text('Waiting (TTFB): '), findsOneWidget);
-        expect(
-          find.text('[529.0 ms - 810.7 ms] → 281.7 ms total'),
-          findsOneWidget,
-        );
-        expect(find.text('Content Download: '), findsOneWidget);
-        expect(
-          find.text('[810.7 ms - 811.7 ms] → 1.0 ms total'),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'displays for http request with error',
-      windowSize,
-      (tester) async {
-        final data = httpGetWithError;
-        await pumpView(tester, data);
-
-        // Verify general information.
-        expect(find.text('Request uri: '), findsOneWidget);
-        expect(find.text('https://www.examplez.com/1'), findsOneWidget);
-        expect(find.text('Method: '), findsOneWidget);
-        expect(find.text('GET'), findsOneWidget);
-        expect(find.text('Status: '), findsOneWidget);
-        expect(find.text('Error'), findsOneWidget);
-        expect(find.text('Port: '), findsNothing);
-        expect(find.text('Content type: '), findsNothing);
-
-        // Verify timing information.
-        expect(find.text('Timing: '), findsOneWidget);
-        expect(find.text('Start time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.startTimestamp)), findsOneWidget);
-        expect(find.text('End time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.endTimestamp!)), findsOneWidget);
-        expect(
-          find.byKey(NetworkRequestOverviewView.httpTimingGraphKey),
-          findsOneWidget,
-        );
-        expect(find.text('Connection established: '), findsNothing);
-        expect(find.text('Request sent: '), findsNothing);
-        expect(find.text('Waiting (TTFB): '), findsNothing);
-        expect(find.text('Content Download: '), findsNothing);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'displays for closed web socket request',
-      windowSize,
-      (tester) async {
-        final data = testSocket1;
-        await pumpView(tester, data);
-
-        // Verify general information.
-        expect(find.text('Request uri: '), findsOneWidget);
-        expect(
-          find.text('[2606:4700:3037::ac43:bd8f]:443'),
-          findsOneWidget,
-        );
-        expect(find.text('Method: '), findsOneWidget);
-        expect(find.text('SOCKET'), findsOneWidget);
-        expect(find.text('Status: '), findsOneWidget);
-        expect(find.text('Closed'), findsOneWidget);
-        expect(find.text('Port: '), findsOneWidget);
-        expect(find.text('443'), findsOneWidget);
-        expect(find.text('Content type: '), findsOneWidget);
-        expect(find.text('socket'), findsOneWidget);
-        expect(find.text('Socket id: '), findsOneWidget);
-        expect(find.text('10000'), findsOneWidget);
-        expect(find.text('Socket type: '), findsOneWidget);
-        expect(find.text('tcp'), findsOneWidget);
-        expect(find.text('Read bytes: '), findsOneWidget);
-        expect(find.text('10'), findsOneWidget);
-        expect(find.text('Write bytes: '), findsOneWidget);
-        expect(find.text('15'), findsOneWidget);
-
-        // Verify timing information.
-        expect(find.text('Timing: '), findsOneWidget);
-        expect(find.text('Start time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.startTimestamp)), findsOneWidget);
-        expect(find.text('End time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.endTimestamp!)), findsOneWidget);
-        expect(
-          find.byKey(NetworkRequestOverviewView.socketTimingGraphKey),
-          findsOneWidget,
-        );
-        expect(find.text('Last read time: '), findsOneWidget);
-        expect(
-          find.text(formatDateTime(data.lastReadTimestamp!)),
-          findsOneWidget,
-        );
-        expect(find.text('Last write time: '), findsOneWidget);
-        expect(
-          find.text(formatDateTime(data.lastWriteTimestamp!)),
-          findsOneWidget,
-        );
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'displays for open web socket request',
-      windowSize,
-      (tester) async {
-        final data = testSocket2;
-        await pumpView(tester, data);
-
-        // Verify general information.
-        expect(find.text('Request uri: '), findsOneWidget);
-        expect(
-          find.text('[2606:4700:3037::ac43:0000]:80'),
-          findsOneWidget,
-        );
-        expect(find.text('Method: '), findsOneWidget);
-        expect(find.text('SOCKET'), findsOneWidget);
-        expect(find.text('Status: '), findsOneWidget);
-        expect(find.text('Open'), findsOneWidget);
-        expect(find.text('Port: '), findsOneWidget);
-        expect(find.text('80'), findsOneWidget);
-        expect(find.text('Content type: '), findsOneWidget);
-        expect(find.text('socket'), findsOneWidget);
-        expect(find.text('Socket id: '), findsOneWidget);
-        expect(find.text('11111'), findsOneWidget);
-        expect(find.text('Socket type: '), findsOneWidget);
-        expect(find.text('tcp'), findsOneWidget);
-        expect(find.text('Read bytes: '), findsOneWidget);
-        expect(find.text('20'), findsOneWidget);
-        expect(find.text('Write bytes: '), findsOneWidget);
-        expect(find.text('25'), findsOneWidget);
-
-        // Verify timing information.
-        expect(find.text('Timing: '), findsOneWidget);
-        expect(find.text('Start time: '), findsOneWidget);
-        expect(find.text(formatDateTime(data.startTimestamp)), findsOneWidget);
-        expect(find.text('End time: '), findsOneWidget);
-        expect(find.text('Pending'), findsOneWidget);
-        expect(
-          find.byKey(NetworkRequestOverviewView.socketTimingGraphKey),
-          findsOneWidget,
-        );
-        expect(find.text('Last read time: '), findsOneWidget);
-        expect(
-          find.text(formatDateTime(data.lastReadTimestamp!)),
-          findsOneWidget,
-        );
-        expect(find.text('Last write time: '), findsOneWidget);
-        expect(
-          find.text(formatDateTime(data.lastWriteTimestamp!)),
-          findsOneWidget,
-        );
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/performance/controls/performance_controls_test.dart b/packages/devtools_app/test/performance/controls/performance_controls_test.dart
deleted file mode 100644
index e1c6645..0000000
--- a/packages/devtools_app/test/performance/controls/performance_controls_test.dart
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/performance/panes/controls/performance_controls.dart';
-import 'package:devtools_app/src/shared/file_import.dart';
-import 'package:devtools_app_shared/ui.dart';
-import 'package:devtools_app_shared/utils.dart';
-import 'package:devtools_test/devtools_test.dart';
-import 'package:devtools_test/helpers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
-
-void main() {
-  const windowSize = Size(3000.0, 1000.0);
-
-  setUp(() {
-    setGlobal(
-      DevToolsEnvironmentParameters,
-      ExternalDevToolsEnvironmentParameters(),
-    );
-    setGlobal(IdeTheme, IdeTheme());
-    setGlobal(PreferencesController, PreferencesController());
-    setGlobal(OfflineDataController, OfflineDataController());
-    setGlobal(NotificationService, NotificationService());
-  });
-
-  group('$PerformanceControls', () {
-    late MockServiceConnectionManager mockServiceConnection;
-    late MockServiceManager mockServiceManager;
-    late MockPerformanceController mockPerformanceController;
-
-    setUp(() {
-      mockServiceConnection = createMockServiceConnectionWithDefaults();
-      mockServiceManager =
-          mockServiceConnection.serviceManager as MockServiceManager;
-      when(mockServiceManager.serviceExtensionManager)
-          .thenReturn(FakeServiceExtensionManager());
-      final connectedApp = MockConnectedApp();
-      mockConnectedApp(
-        connectedApp,
-        isFlutterApp: true,
-        isProfileBuild: false,
-        isWebApp: false,
-      );
-      when(mockServiceManager.connectedApp).thenReturn(connectedApp);
-      setGlobal(ServiceConnectionManager, mockServiceConnection);
-      mockPerformanceController = createMockPerformanceControllerWithDefaults();
-    });
-
-    tearDown(() {
-      offlineDataController.stopShowingOfflineData();
-    });
-
-    Future<void> pumpControls(WidgetTester tester) async {
-      await tester.pumpWidget(
-        wrapWithControllers(
-          PerformanceControls(
-            controller: mockPerformanceController,
-            onClear: () {},
-          ),
-          performance: mockPerformanceController,
-        ),
-      );
-      await tester.pumpAndSettle();
-    }
-
-    testWidgetsWithWindowSize(
-      'builds for Flutter app',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpControls(tester);
-        expect(find.byType(ExitOfflineButton), findsNothing);
-        expect(find.byType(VisibilityButton), findsOneWidget);
-        expect(find.byIcon(Icons.block), findsOneWidget);
-        expect(find.text('Performance Overlay'), findsOneWidget);
-        expect(find.text('Enhance Tracing'), findsOneWidget);
-        expect(find.text('More debugging options'), findsOneWidget);
-        expect(find.byType(OpenSaveButtonGroup), findsOneWidget);
-        expect(find.byIcon(Icons.settings_outlined), findsOneWidget);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds for non flutter app',
-      windowSize,
-      (WidgetTester tester) async {
-        mockConnectedApp(
-          mockServiceManager.connectedApp!,
-          isFlutterApp: false,
-          isProfileBuild: false,
-          isWebApp: false,
-        );
-        await pumpControls(tester);
-
-        expect(find.byType(ExitOfflineButton), findsNothing);
-        expect(find.byType(VisibilityButton), findsNothing);
-        expect(find.byIcon(Icons.block), findsOneWidget);
-        expect(find.text('Performance Overlay'), findsNothing);
-        expect(find.text('Enhance Tracing'), findsNothing);
-        expect(find.text('More debugging options'), findsNothing);
-        expect(find.byType(OpenSaveButtonGroup), findsOneWidget);
-        expect(find.byIcon(Icons.settings_outlined), findsNothing);
-      },
-    );
-
-    testWidgetsWithWindowSize(
-      'builds for offline mode',
-      windowSize,
-      (WidgetTester tester) async {
-        offlineDataController.startShowingOfflineData(
-          offlineApp: serviceConnection.serviceManager.connectedApp!,
-        );
-        await pumpControls(tester);
-        expect(find.byType(ExitOfflineButton), findsOneWidget);
-        expect(find.byType(VisibilityButton), findsOneWidget);
-        expect(find.byIcon(Icons.block), findsNothing);
-        expect(find.text('Performance Overlay'), findsNothing);
-        expect(find.text('Enhance Tracing'), findsNothing);
-        expect(find.text('More debugging options'), findsNothing);
-        expect(find.byType(OpenSaveButtonGroup), findsNothing);
-        expect(find.byIcon(Icons.settings_outlined), findsNothing);
-        offlineDataController.stopShowingOfflineData();
-      },
-    );
-  });
-}
diff --git a/packages/devtools_app/test/screens/app_size/app_size_screen_test.dart b/packages/devtools_app/test/screens/app_size/app_size_screen_test.dart
new file mode 100644
index 0000000..031f21a
--- /dev/null
+++ b/packages/devtools_app/test/screens/app_size/app_size_screen_test.dart
@@ -0,0 +1,706 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/app_size/app_size_table.dart';
+import 'package:devtools_app/src/shared/ui/file_import.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_shared/devtools_test_utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+import '../../test_infra/test_data/app_size/deferred_app.dart';
+import '../../test_infra/test_data/app_size/diff_deferred_app.dart';
+import '../../test_infra/test_data/app_size/diff_no_deferred_app.dart';
+import '../../test_infra/test_data/app_size/new_v8.dart';
+import '../../test_infra/test_data/app_size/old_v8.dart';
+import '../../test_infra/test_data/app_size/sizes.dart';
+import '../../test_infra/test_data/app_size/unsupported_file.dart';
+
+void main() {
+  setUp(() {
+    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(NotificationService, NotificationService());
+  });
+
+  final lastModifiedTime = DateTime.parse('2020-07-28 13:29:00');
+
+  final oldV8JsonFile = DevToolsJsonFile(
+    name: 'lib/src/app_size/stub_data/old_v8.dart',
+    lastModifiedTime: lastModifiedTime,
+    data: json.decode(oldV8),
+  );
+
+  final newV8JsonFile = DevToolsJsonFile(
+    name: 'lib/src/app_size/stub_data/new_v8.dart',
+    lastModifiedTime: lastModifiedTime,
+    data: json.decode(newV8),
+  );
+
+  final deferredAppFile = DevToolsJsonFile(
+    name: 'lib/src/app_size/stub_data/deferred_app.dart',
+    lastModifiedTime: lastModifiedTime,
+    data: json.decode(deferredApp),
+  );
+
+  DevToolsJsonFile(
+    name: 'lib/src/app_size/stub_data/diff_deferred_app.dart',
+    lastModifiedTime: lastModifiedTime,
+    data: json.decode(diffDeferredApp),
+  );
+
+  DevToolsJsonFile(
+    name: 'lib/src/app_size/stub_data/diff_no_deferred_app.dart',
+    lastModifiedTime: lastModifiedTime,
+    data: json.decode(diffNonDeferredApp),
+  );
+
+  late AppSizeScreen screen;
+  late AppSizeTestController appSizeController;
+  FakeServiceConnectionManager fakeServiceConnection;
+
+  const windowSize = Size(2560.0, 1338.0);
+
+  Future<void> pumpAppSizeScreen(
+    WidgetTester tester, {
+    required AppSizeTestController controller,
+  }) async {
+    await tester.pumpWidget(
+      wrapWithControllers(const AppSizeBody(), appSize: controller),
+    );
+    deferredLoadingSupportEnabled = true;
+    await tester.pumpAndSettle(const Duration(seconds: 1));
+    expect(find.byType(AppSizeBody), findsOneWidget);
+  }
+
+  Future<void> loadDataAndPump(
+    WidgetTester tester, {
+    DevToolsJsonFile? data,
+  }) async {
+    data ??= newV8JsonFile;
+    appSizeController.loadTreeFromJsonFile(
+      jsonFile: data,
+      onError: (error) => {},
+    );
+    await tester.pumpAndSettle();
+  }
+
+  group('AppSizeScreen', () {
+    setUp(() {
+      screen = AppSizeScreen();
+      appSizeController = AppSizeTestController();
+      fakeServiceConnection = FakeServiceConnectionManager();
+      setGlobal(ServiceConnectionManager, fakeServiceConnection);
+      when(
+        fakeServiceConnection.errorBadgeManager.errorCountNotifier('app-size'),
+      ).thenReturn(ValueNotifier<int>(0));
+    });
+
+    testWidgets('builds its tab', (WidgetTester tester) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          Builder(builder: screen.buildTab),
+          appSize: appSizeController,
+        ),
+      );
+      expect(find.text('App Size'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('builds initial content', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+
+      expect(find.byType(AppSizeBody), findsOneWidget);
+      expect(find.byType(TabBar), findsOneWidget);
+
+      expect(find.byKey(AppSizeScreen.analysisTabKey), findsOneWidget);
+      expect(find.byKey(AppSizeScreen.diffTabKey), findsOneWidget);
+
+      await loadDataAndPump(tester);
+
+      // Verify the state of the splitter.
+      final splitFinder = find.byType(SplitPane);
+      expect(splitFinder, findsOneWidget);
+      final SplitPane splitter = tester.widget(splitFinder);
+      expect(splitter.initialFractions[0], equals(0.67));
+      expect(splitter.initialFractions[1], equals(0.33));
+    });
+
+    testWidgetsWithWindowSize('builds deferred content', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+      await loadDataAndPump(tester, data: deferredAppFile);
+
+      // Verify the dropdown for selecting app units exists.
+      final appUnitDropdownFinder = _findDropdownButton<AppUnit>();
+      expect(appUnitDropdownFinder, findsOneWidget);
+
+      // Verify the entire app is shown.
+      final breadcrumbs = _fetchBreadcrumbs(tester);
+      expect(breadcrumbs.length, 1);
+      expect(breadcrumbs.first.text, equals('Entire App [39.8 MB]'));
+      expect(find.richText('Main [39.5 MB]'), findsOneWidget);
+
+      // Open the dropdown.
+      await tester.tap(appUnitDropdownFinder);
+      await tester.pumpAndSettle();
+
+      // Verify the menu items in the dropdown are expected.
+      final entireAppMenuItemFinder = _findMenuItemWithText<AppUnit>(
+        'Entire App',
+      );
+      expect(entireAppMenuItemFinder, findsOneWidget);
+      final mainMenuItemFinder = _findMenuItemWithText<AppUnit>('Main');
+      expect(mainMenuItemFinder, findsOneWidget);
+      final deferredMenuItemFinder = _findMenuItemWithText<AppUnit>('Deferred');
+      expect(deferredMenuItemFinder, findsOneWidget);
+
+      // Select the main unit.
+      await tester.tap(find.text('Main').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the main unit is shown.
+      final mainBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(mainBreadcrumbs.length, 1);
+      expect(mainBreadcrumbs.first.text, equals('Main [39.5 MB]'));
+      expect(find.richText('appsize_app.app [39.5 MB]'), findsOneWidget);
+
+      // Open the dropdown.
+      await tester.tap(appUnitDropdownFinder);
+      await tester.pumpAndSettle();
+
+      // Select the deferred units.
+      await tester.tap(find.text('Deferred').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the deferred units are shown.
+      final deferredBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(deferredBreadcrumbs.length, 1);
+      expect(deferredBreadcrumbs.first.text, equals('Deferred [344.3 KB]'));
+      expect(
+        find.richText('flutter_assets [344.3 KB] (Deferred)'),
+        findsOneWidget,
+      );
+    });
+  });
+
+  group('SnapshotView', () {
+    setUp(() {
+      screen = AppSizeScreen();
+      appSizeController = AppSizeTestController();
+    });
+
+    testWidgetsWithWindowSize('imports file and loads data', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+
+      expect(find.byKey(AppSizeScreen.diffTypeDropdownKey), findsNothing);
+      expect(find.byType(ClearButton), findsOneWidget);
+
+      expect(find.byType(FileImportContainer), findsOneWidget);
+      expect(find.text(AnalysisView.importInstructions), findsOneWidget);
+      expect(find.text('No File Selected'), findsOneWidget);
+
+      appSizeController.loadTreeFromJsonFile(
+        jsonFile: newV8JsonFile,
+        onError: (error) => {},
+        delayed: true,
+      );
+      await tester.pump(const Duration(milliseconds: 500));
+      expect(find.text(AppSizeScreen.loadingMessage), findsOneWidget);
+      await tester.pumpAndSettle();
+
+      expect(find.byType(FileImportContainer), findsNothing);
+      expect(find.text(AnalysisView.importInstructions), findsNothing);
+      expect(find.text('No File Selected'), findsNothing);
+      expect(find.byType(AnalysisView), findsOneWidget);
+      expect(
+        find.text(
+          'Dart AOT snapshot: lib/src/app_size/stub_data/new_v8.dart - 7/28/2020 1:29 PM',
+        ),
+        findsOneWidget,
+      );
+      expect(find.byKey(AppSizeScreen.analysisViewTreemapKey), findsOneWidget);
+
+      final breadcrumbs = _fetchBreadcrumbs(tester);
+      expect(breadcrumbs.length, 1);
+      expect(breadcrumbs.first.text, equals('Root [6.0 MB]'));
+      expect(find.byType(BreadcrumbNavigator), findsOneWidget);
+
+      expect(find.richText('package:flutter'), findsOneWidget);
+      expect(find.richText('dart:core'), findsOneWidget);
+
+      expect(find.byType(AppSizeAnalysisTable), findsOneWidget);
+      expect(find.byType(AppSizeDiffTable), findsNothing);
+    });
+
+    testWidgetsWithWindowSize('clears data', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+
+      await loadDataAndPump(tester);
+
+      await tester.tap(find.byType(ClearButton));
+      await tester.pumpAndSettle();
+
+      expect(find.byType(FileImportContainer), findsOneWidget);
+      expect(find.text(AnalysisView.importInstructions), findsOneWidget);
+      expect(find.text('No File Selected'), findsOneWidget);
+    });
+  });
+
+  group('DiffView', () {
+    setUp(() {
+      screen = AppSizeScreen();
+      appSizeController = AppSizeTestController();
+    });
+
+    Future<void> loadDiffTabAndSettle(WidgetTester tester) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+      await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
+      await tester.pumpAndSettle();
+    }
+
+    Future<void> loadDiffDataAndPump(
+      WidgetTester tester,
+      DevToolsJsonFile oldJsonFile,
+      DevToolsJsonFile newJsonFile,
+    ) async {
+      appSizeController.loadDiffTreeFromJsonFiles(
+        oldFile: oldJsonFile,
+        newFile: newJsonFile,
+        onError: (error) => {},
+      );
+      await tester.pumpAndSettle();
+    }
+
+    testWidgetsWithWindowSize('builds initial content', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await loadDiffTabAndSettle(tester);
+
+      expect(find.byKey(AppSizeScreen.diffTypeDropdownKey), findsOneWidget);
+      expect(find.byKey(AppSizeScreen.appUnitDropdownKey), findsNothing);
+      expect(find.byType(ClearButton), findsOneWidget);
+
+      expect(find.byType(DualFileImportContainer), findsOneWidget);
+      expect(find.byType(FileImportContainer), findsNWidgets(2));
+      expect(find.text(DiffView.importOldInstructions), findsOneWidget);
+      expect(find.text(DiffView.importNewInstructions), findsOneWidget);
+      expect(find.text('No File Selected'), findsNWidgets(2));
+    });
+
+    testWidgetsWithWindowSize('imports files and loads data', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await loadDiffTabAndSettle(tester);
+
+      appSizeController.loadDiffTreeFromJsonFiles(
+        oldFile: oldV8JsonFile,
+        newFile: newV8JsonFile,
+        onError: (error) => {},
+        delayed: true,
+      );
+      await tester.pump(const Duration(milliseconds: 500));
+      expect(find.text(AppSizeScreen.loadingMessage), findsOneWidget);
+      await tester.pumpAndSettle();
+
+      expect(find.byType(FileImportContainer), findsNothing);
+      expect(find.text(DiffView.importOldInstructions), findsNothing);
+      expect(find.text(DiffView.importNewInstructions), findsNothing);
+      expect(find.text('No File Selected'), findsNothing);
+
+      expect(find.byType(DiffView), findsOneWidget);
+
+      expect(
+        find.text(
+          'Diffing Dart AOT snapshots: lib/src/app_size/stub_data/old_v8.dart - 7/28/2020 1:29 PM (OLD)    vs    (NEW) lib/src/app_size/stub_data/new_v8.dart - 7/28/2020 1:29 PM',
+        ),
+        findsOneWidget,
+      );
+      expect(find.byKey(AppSizeScreen.diffViewTreemapKey), findsOneWidget);
+      expect(find.byKey(AppSizeScreen.analysisViewTreemapKey), findsNothing);
+
+      final breadcrumbs = _fetchBreadcrumbs(tester);
+      expect(breadcrumbs.length, 1);
+      expect(breadcrumbs.first.text, equals('Root [+1.5 MB]'));
+      expect(find.richText('package:pointycastle'), findsOneWidget);
+      expect(find.richText('package:flutter'), findsOneWidget);
+
+      expect(find.byType(AppSizeAnalysisTable), findsNothing);
+      expect(find.byType(AppSizeDiffTable), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize(
+      'loads data and shows different tree types',
+      windowSize,
+      (WidgetTester tester) async {
+        await loadDiffTabAndSettle(tester);
+
+        await loadDiffDataAndPump(tester, oldV8JsonFile, newV8JsonFile);
+
+        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+        await tester.pumpAndSettle();
+
+        await tester.tap(find.text('Increase Only').hitTestable());
+        await tester.pumpAndSettle();
+
+        final breadcrumbs = _fetchBreadcrumbs(tester);
+        expect(breadcrumbs.length, 1);
+        expect(breadcrumbs.first.text, equals('Root [+1.6 MB]'));
+
+        expect(find.richText('package:pointycastle'), findsOneWidget);
+        expect(find.richText('package:flutter'), findsOneWidget);
+
+        await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+        await tester.pumpAndSettle();
+
+        await tester.tap(find.text('Decrease Only').hitTestable());
+        await tester.pumpAndSettle();
+
+        expect(find.richText('Root'), findsOneWidget);
+        expect(find.richText('package:memory'), findsOneWidget);
+        expect(find.richText('package:flutter'), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize('clears data', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await loadDiffTabAndSettle(tester);
+
+      await loadDiffDataAndPump(tester, oldV8JsonFile, newV8JsonFile);
+
+      await tester.tap(find.byType(ClearButton));
+      await tester.pumpAndSettle();
+
+      expect(find.byType(DualFileImportContainer), findsOneWidget);
+      expect(find.byType(FileImportContainer), findsNWidgets(2));
+      expect(find.text(DiffView.importOldInstructions), findsOneWidget);
+      expect(find.text(DiffView.importNewInstructions), findsOneWidget);
+      expect(find.text('No File Selected'), findsNWidgets(2));
+    });
+  });
+
+  group('AppSizeController', () {
+    setUp(() {
+      screen = AppSizeScreen();
+      appSizeController = AppSizeTestController();
+    });
+
+    Future<void> pumpAppSizeScreenWithContext(
+      WidgetTester tester, {
+      required AppSizeTestController controller,
+    }) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          MaterialApp(
+            builder: (context, child) => child!,
+            home: Builder(
+              builder: (context) {
+                return const AppSizeBody();
+              },
+            ),
+          ),
+          appSize: controller,
+        ),
+      );
+      deferredLoadingSupportEnabled = true;
+      await tester.pumpAndSettle(const Duration(seconds: 1));
+      expect(find.byType(AppSizeBody), findsOneWidget);
+    }
+
+    Future<void> loadDiffTreeAndPump(
+      WidgetTester tester,
+      String firstFile,
+      String secondFile,
+    ) async {
+      appSizeController.loadDiffTreeFromJsonFiles(
+        oldFile: DevToolsJsonFile(
+          name: '',
+          lastModifiedTime: lastModifiedTime,
+          data: json.decode(firstFile),
+        ),
+        newFile: DevToolsJsonFile(
+          name: '',
+          lastModifiedTime: lastModifiedTime,
+          data: json.decode(secondFile),
+        ),
+        onError: (error) => notificationService.push(error),
+      );
+      await tester.pumpAndSettle();
+    }
+
+    testWidgetsWithWindowSize(
+      'outputs error notifications for invalid input on the snapshot tab',
+      windowSize,
+      (WidgetTester tester) async {
+        await pumpAppSizeScreenWithContext(
+          tester,
+          controller: appSizeController,
+        );
+
+        appSizeController.loadTreeFromJsonFile(
+          jsonFile: DevToolsJsonFile(
+            name: 'unsupported_file.json',
+            lastModifiedTime: lastModifiedTime,
+            data: unsupportedFile,
+          ),
+          onError: (error) => notificationService.push(error),
+        );
+        await tester.pumpAndSettle();
+        expect(
+          find.text(AppSizeController.unsupportedFileTypeError),
+          findsOneWidget,
+        );
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'outputs error notifications for invalid input on the diff tab',
+      windowSize,
+      (WidgetTester tester) async {
+        await pumpAppSizeScreenWithContext(
+          tester,
+          controller: appSizeController,
+        );
+        await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
+        await tester.pumpAndSettle();
+
+        await loadDiffTreeAndPump(tester, newV8, newV8);
+        expect(
+          find.text(AppSizeController.identicalFilesError),
+          findsOneWidget,
+        );
+
+        await loadDiffTreeAndPump(tester, instructionSizes, newV8);
+        expect(
+          find.text(AppSizeController.differentTypesError),
+          findsOneWidget,
+        );
+
+        await loadDiffTreeAndPump(tester, unsupportedFile, unsupportedFile);
+        expect(
+          find.text(AppSizeController.unsupportedFileTypeError),
+          findsOneWidget,
+        );
+      },
+    );
+
+    testWidgetsWithWindowSize('builds deferred content for diff table', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpAppSizeScreen(tester, controller: appSizeController);
+      await tester.tap(find.byKey(AppSizeScreen.diffTabKey));
+      await tester.pumpAndSettle();
+
+      await loadDiffTreeAndPump(tester, diffNonDeferredApp, diffDeferredApp);
+
+      // Verify the dropdown for selecting app units exists.
+      final appUnitDropdownFinder = _findDropdownButton<AppUnit>();
+      expect(appUnitDropdownFinder, findsOneWidget);
+
+      // Open the app unit dropdown.
+      await tester.tap(appUnitDropdownFinder);
+      await tester.pumpAndSettle();
+
+      // Verify the menu items in the dropdown are expected.
+      final entireAppMenuItemFinder = _findMenuItemWithText<AppUnit>(
+        'Entire App',
+      );
+      expect(entireAppMenuItemFinder, findsOneWidget);
+      final mainMenuItemFinder = _findMenuItemWithText<AppUnit>('Main');
+      expect(mainMenuItemFinder, findsOneWidget);
+      final deferredMenuItemFinder = _findMenuItemWithText<AppUnit>('Deferred');
+      expect(deferredMenuItemFinder, findsOneWidget);
+
+      // Select the main unit.
+      await tester.tap(find.richText('Main').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the main unit is shown for entire app.
+      final mainBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(mainBreadcrumbs.length, 1);
+      expect(
+        mainBreadcrumbs.first.text,
+        equals(
+          '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [-344.3 KB]',
+        ),
+      );
+      expect(
+        find.richText('packages/cupertino_icons/assets [-276.8 KB]'),
+        findsOneWidget,
+      );
+
+      // Open the diffType dropdown.
+      await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+      await tester.pumpAndSettle();
+
+      // Select increase only.
+      await tester.tap(find.text('Increase Only').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the main unit is shown for increase only.
+      final mainIncreaseBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(mainIncreaseBreadcrumbs.length, 1);
+      expect(
+        mainIncreaseBreadcrumbs.first.text,
+        equals(
+          '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [0 B]',
+        ),
+      );
+
+      // Open the diffType dropdown.
+      await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+      await tester.pumpAndSettle();
+
+      // Select decrease only.
+      await tester.tap(find.text('Decrease Only').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the main unit is shown for decrease only.
+      final mainDecreaseBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(mainDecreaseBreadcrumbs.length, 1);
+      expect(
+        mainDecreaseBreadcrumbs.first.text,
+        equals(
+          '/Main/appsize_app.app/Contents/Frameworks/App.framework/Resources/flutter_assets [-344.3 KB]',
+        ),
+      );
+      expect(
+        find.richText('packages/cupertino_icons/assets [-276.8 KB]'),
+        findsOneWidget,
+      );
+
+      // Open the diffType dropdown.
+      await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+      await tester.pumpAndSettle();
+
+      // Select entire app.
+      await tester.tap(find.text('Combined').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Open the app unit dropdown.
+      await tester.tap(appUnitDropdownFinder);
+      await tester.pumpAndSettle();
+
+      // Select the deferred units.
+      await tester.tap(find.text('Deferred').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the deferred units are shown for entire app.
+      final deferredBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(deferredBreadcrumbs.length, 1);
+      expect(
+        deferredBreadcrumbs.first.text,
+        equals('/Deferred/flutter_assets [+344.3 KB]'),
+      );
+      expect(
+        find.richText('packages/cupertino_icons/assets [+276.8 KB]'),
+        findsOneWidget,
+      );
+
+      // Open the diffType dropdown.
+      await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+      await tester.pumpAndSettle();
+
+      // Select increase only.
+      await tester.tap(find.text('Increase Only').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the deferred unit is shown for increase only.
+      final deferredIncreaseBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(deferredIncreaseBreadcrumbs.length, 1);
+      expect(
+        deferredIncreaseBreadcrumbs.first.text,
+        equals('/Deferred/flutter_assets [+344.3 KB]'),
+      );
+      expect(
+        find.richText('packages/cupertino_icons/assets [+276.8 KB]'),
+        findsOneWidget,
+      );
+
+      // Open the diffType dropdown.
+      await tester.tap(find.byKey(AppSizeScreen.diffTypeDropdownKey));
+      await tester.pumpAndSettle();
+
+      // Select decrease only.
+      await tester.tap(find.text('Decrease Only').hitTestable());
+      await tester.pumpAndSettle();
+
+      // Verify the main unit is shown for decrease only.
+      final deferredDecreaseBreadcrumbs = _fetchBreadcrumbs(tester);
+      expect(deferredDecreaseBreadcrumbs.length, 1);
+      expect(
+        deferredDecreaseBreadcrumbs.first.text,
+        equals('/Deferred/flutter_assets [0 B]'),
+      );
+    });
+  });
+}
+
+class AppSizeTestController extends AppSizeController {
+  @override
+  void loadTreeFromJsonFile({
+    required DevToolsJsonFile jsonFile,
+    required void Function(String error) onError,
+    bool delayed = false,
+  }) async {
+    if (delayed) {
+      await delay();
+    }
+    super.loadTreeFromJsonFile(jsonFile: jsonFile, onError: onError);
+  }
+
+  @override
+  void loadDiffTreeFromJsonFiles({
+    required DevToolsJsonFile oldFile,
+    required DevToolsJsonFile newFile,
+    required void Function(String error) onError,
+    bool delayed = false,
+  }) async {
+    if (delayed) {
+      await delay();
+    }
+    super.loadDiffTreeFromJsonFiles(
+      oldFile: oldFile,
+      newFile: newFile,
+      onError: onError,
+    );
+  }
+}
+
+List<Breadcrumb> _fetchBreadcrumbs(WidgetTester tester) {
+  return tester
+      .widgetList(find.byType(Breadcrumb))
+      .map((widget) => widget as Breadcrumb)
+      .toList();
+}
+
+Finder _findDropdownButton<T>() {
+  return find.byType(DropdownButton<T>);
+}
+
+Finder _findMenuItemWithText<T>(String text) {
+  return find.descendant(
+    of: find.byType(DropdownMenuItem<T>),
+    matching: find.richText(text).hitTestable(),
+  );
+}
diff --git a/packages/devtools_app/test/app_size/code_size_attribution_test.dart b/packages/devtools_app/test/screens/app_size/code_size_attribution_test.dart
similarity index 76%
rename from packages/devtools_app/test/app_size/code_size_attribution_test.dart
rename to packages/devtools_app/test/screens/app_size/code_size_attribution_test.dart
index fa4f96e..ab96db9 100644
--- a/packages/devtools_app/test/app_size/code_size_attribution_test.dart
+++ b/packages/devtools_app/test/screens/app_size/code_size_attribution_test.dart
@@ -14,7 +14,7 @@
 import 'package:vm_snapshot_analysis/precompiler_trace.dart';
 import 'package:vm_snapshot_analysis/program_info.dart';
 
-import '../test_infra/test_data/app_size/precompiler_trace.dart';
+import '../../test_infra/test_data/app_size/precompiler_trace.dart';
 
 void main() {
   late CallGraph callGraph;
@@ -41,16 +41,15 @@
       );
     });
 
-    testWidgets(
-      'builds dominator tree by default',
-      (WidgetTester tester) async {
-        await tester.pumpWidget(wrap(callGraphWithDominators));
-        expect(find.text('Dominator Tree'), findsOneWidget);
-        expect(find.text('Call Graph'), findsNothing);
-        expect(find.byType(DominatorTree), findsOneWidget);
-        expect(find.byType(CallGraphView), findsNothing);
-      },
-    );
+    testWidgets('builds dominator tree by default', (
+      WidgetTester tester,
+    ) async {
+      await tester.pumpWidget(wrap(callGraphWithDominators));
+      expect(find.text('Dominator Tree'), findsOneWidget);
+      expect(find.text('Call Graph'), findsNothing);
+      expect(find.byType(DominatorTree), findsOneWidget);
+      expect(find.byType(CallGraphView), findsNothing);
+    });
 
     testWidgets('builds call graph', (WidgetTester tester) async {
       await tester.pumpWidget(wrap(callGraphWithDominators));
@@ -92,18 +91,14 @@
       expect(find.text('From'), findsOneWidget);
       expect(find.text('To'), findsOneWidget);
 
-      final fromTable = find
-          .byType(FlatTable<CallGraphNode>)
-          .evaluate()
-          .first
-          .widget as FlatTable;
+      final fromTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().first.widget
+              as FlatTable;
       expect(fromTable.data, isEmpty);
 
-      final toTable = find
-          .byType(FlatTable<CallGraphNode>)
-          .evaluate()
-          .last
-          .widget as FlatTable;
+      final toTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().last.widget
+              as FlatTable;
       expect(toTable.data.length, equals(17));
     });
 
@@ -114,27 +109,28 @@
       expect(find.text('From'), findsOneWidget);
       expect(find.text('To'), findsOneWidget);
 
-      var fromTable = find
-          .byType(FlatTable<CallGraphNode>)
-          .evaluate()
-          .first
-          .widget as FlatTable;
+      var fromTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().first.widget
+              as FlatTable;
       expect(fromTable.data, isEmpty);
 
-      var toTable = find.byType(FlatTable<CallGraphNode>).evaluate().last.widget
-          as FlatTable;
+      var toTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().last.widget
+              as FlatTable;
       expect(toTable.data.length, equals(17));
 
       // Tap to re-root call graph.
       await tester.tap(find.richText('dart:math'));
       await tester.pumpAndSettle();
 
-      fromTable = find.byType(FlatTable<CallGraphNode>).evaluate().first.widget
-          as FlatTable;
+      fromTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().first.widget
+              as FlatTable;
       expect(fromTable.data.length, equals(3));
 
-      toTable = find.byType(FlatTable<CallGraphNode>).evaluate().last.widget
-          as FlatTable;
+      toTable =
+          find.byType(FlatTable<CallGraphNode>).evaluate().last.widget
+              as FlatTable;
       expect(toTable.data.length, equals(1));
     });
   });
@@ -155,11 +151,13 @@
       expect(find.byKey(DominatorTree.dominatorTreeTableKey), findsOneWidget);
       expect(find.text('Package'), findsOneWidget);
 
-      final treeTable = find
-          .byKey(DominatorTree.dominatorTreeTableKey)
-          .evaluate()
-          .first
-          .widget as TreeTable;
+      final treeTable =
+          find
+                  .byKey(DominatorTree.dominatorTreeTableKey)
+                  .evaluate()
+                  .first
+                  .widget
+              as TreeTable;
       expect(treeTable.dataRoots.length, equals(1));
 
       final root = treeTable.dataRoots.first;
@@ -174,15 +172,18 @@
     testWidgets('expands tree to selected node', (WidgetTester tester) async {
       dominatorTree = DominatorTree(
         dominatorTreeRoot: DominatorTreeNode.from(callGraph.root.dominatorRoot),
-        selectedNode: callGraph.root.dominated
-            .firstWhere((node) => node.display == 'package:code_size_package'),
+        selectedNode: callGraph.root.dominated.firstWhere(
+          (node) => node.display == 'package:code_size_package',
+        ),
       );
       await tester.pumpWidget(wrap(dominatorTree));
-      final treeTable = find
-          .byKey(DominatorTree.dominatorTreeTableKey)
-          .evaluate()
-          .first
-          .widget as TreeTable;
+      final treeTable =
+          find
+                  .byKey(DominatorTree.dominatorTreeTableKey)
+                  .evaluate()
+                  .first
+                  .widget
+              as TreeTable;
 
       final root = treeTable.dataRoots.first;
       expect(root.isExpanded, isTrue);
diff --git a/packages/devtools_app/test/cpu_profiler/cpu_profile_model_test.dart b/packages/devtools_app/test/screens/cpu_profiler/cpu_profile_model_test.dart
similarity index 80%
rename from packages/devtools_app/test/cpu_profiler/cpu_profile_model_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/cpu_profile_model_test.dart
index ec3a251..0f77190 100644
--- a/packages/devtools_app/test/cpu_profiler/cpu_profile_model_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/cpu_profile_model_test.dart
@@ -10,7 +10,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../test_infra/test_data/cpu_profiler/cpu_profile.dart';
+import '../../test_infra/test_data/cpu_profiler/cpu_profile.dart';
 
 void main() {
   group('CpuProfileData', () {
@@ -28,20 +28,20 @@
       );
     });
 
-    test(
-      'empty frame regression test',
-      () {
-        final cpuProfileEmptyData =
-            CpuProfileData.fromJson(cpuProfileResponseEmptyJson);
-        expect(
-          cpuProfileEmptyData.profileMetaData.time!.end!.inMilliseconds,
-          47377796,
-        );
-        final filtered =
-            CpuProfileData.filterFrom(cpuProfileEmptyData, (_) => true);
-        expect(filtered.profileMetaData.time!.end!.inMilliseconds, 0);
-      },
-    );
+    test('empty frame regression test', () {
+      final cpuProfileEmptyData = CpuProfileData.fromJson(
+        cpuProfileResponseEmptyJson,
+      );
+      expect(
+        cpuProfileEmptyData.profileMetaData.time!.end!.inMilliseconds,
+        47377796,
+      );
+      final filtered = CpuProfileData.filterFrom(
+        cpuProfileEmptyData,
+        (_) => true,
+      );
+      expect(filtered.profileMetaData.time!.end!.inMilliseconds, 0);
+    });
 
     test('init from parse', () {
       expect(
@@ -72,10 +72,7 @@
           ..end = const Duration(microseconds: 47377799063),
       );
 
-      expect(
-        subProfile.stackFramesJson,
-        equals(subProfileStackFrames),
-      );
+      expect(subProfile.stackFramesJson, equals(subProfileStackFrames));
       expect(
         subProfile.cpuSamples.map((sample) => sample.json),
         equals(subProfileTraceEvents),
@@ -92,10 +89,7 @@
         cpuProfileData,
         (stackFrame) => !stackFrame.packageUri.startsWith('dart:'),
       );
-      expect(
-        filteredProfile.stackFramesJson,
-        equals(filteredStackFrames),
-      );
+      expect(filteredProfile.stackFramesJson, equals(filteredStackFrames));
       expect(
         filteredProfile.cpuSamples.map((sample) => sample.toJson),
         equals(filteredCpuSampleTraceEvents),
@@ -115,39 +109,33 @@
       expect(cpuProfileData.toJson(), equals(goldenCpuProfileDataJson));
     });
 
-    test(
-      'converts golden samples to golden cpu profile data',
-      () async {
-        final generatedCpuProfileData =
-            await CpuProfileData.generateFromCpuSamples(
-          isolateId: goldenSamplesIsolate,
-          cpuSamples: CpuSamples.parse(goldenCpuSamplesJson)!,
-        );
+    test('converts golden samples to golden cpu profile data', () async {
+      final generatedCpuProfileData =
+          await CpuProfileData.generateFromCpuSamples(
+            isolateId: goldenSamplesIsolate,
+            cpuSamples: CpuSamples.parse(goldenCpuSamplesJson)!,
+          );
 
-        expect(
-          generatedCpuProfileData.toJson(),
-          equals(goldenCpuProfileDataJson),
-        );
-      },
-    );
+      expect(
+        generatedCpuProfileData.toJson(),
+        equals(goldenCpuProfileDataJson),
+      );
+    });
 
-    test(
-      'to json defaults packageUri to resolvedUrl',
-      () {
-        const id = '140357727781376-12';
+    test('to json defaults packageUri to resolvedUrl', () {
+      const id = '140357727781376-12';
 
-        final profileData = Map.of(goldenCpuProfileDataJson);
-        final stackFrame = goldenCpuProfileStackFrames[id] as Map;
-        final stackFrameData = {id: stackFrame};
-        profileData['stackFrames'] = stackFrameData;
-        stackFrameData[id]!.remove(CpuProfileData.resolvedPackageUriKey);
+      final profileData = Map.of(goldenCpuProfileDataJson);
+      final stackFrame = goldenCpuProfileStackFrames[id] as Map;
+      final stackFrameData = {id: stackFrame};
+      profileData['stackFrames'] = stackFrameData;
+      stackFrameData[id]!.remove(CpuProfileData.resolvedPackageUriKey);
 
-        final parsedProfileData = CpuProfileData.fromJson(profileData);
+      final parsedProfileData = CpuProfileData.fromJson(profileData);
 
-        final jsonPackageUri = parsedProfileData.stackFrames[id]!.packageUri;
-        expect(jsonPackageUri, stackFrame['resolvedUrl']);
-      },
-    );
+      final jsonPackageUri = parsedProfileData.stackFrames[id]!.packageUri;
+      expect(jsonPackageUri, stackFrame['resolvedUrl']);
+    });
 
     test('generateFromCpuSamples handles duplicate resolvedUrls', () async {
       const resolvedUrl = 'the/resolved/Url';
@@ -274,34 +262,50 @@
       expect(testStackFrame.ancestorIds.toList(), ['cpuProfileRoot']);
       expect(stackFrameA.ancestorIds.toList(), ['cpuProfileRoot']);
       expect(stackFrameB.ancestorIds.toList(), ['id_0', 'cpuProfileRoot']);
-      expect(
-        stackFrameC.ancestorIds.toList(),
-        ['id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameD.ancestorIds.toList(),
-        ['id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameE.ancestorIds.toList(),
-        ['id_3', 'id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameF.ancestorIds.toList(),
-        ['id_4', 'id_3', 'id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameF2.ancestorIds.toList(),
-        ['id_3', 'id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameC2.ancestorIds.toList(),
-        ['id_5', 'id_4', 'id_3', 'id_1', 'id_0', 'cpuProfileRoot'],
-      );
-      expect(
-        stackFrameC3.ancestorIds.toList(),
-        ['id_6', 'id_3', 'id_1', 'id_0', 'cpuProfileRoot'],
-      );
+      expect(stackFrameC.ancestorIds.toList(), [
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameD.ancestorIds.toList(), [
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameE.ancestorIds.toList(), [
+        'id_3',
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameF.ancestorIds.toList(), [
+        'id_4',
+        'id_3',
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameF2.ancestorIds.toList(), [
+        'id_3',
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameC2.ancestorIds.toList(), [
+        'id_5',
+        'id_4',
+        'id_3',
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
+      expect(stackFrameC3.ancestorIds.toList(), [
+        'id_6',
+        'id_3',
+        'id_1',
+        'id_0',
+        'cpuProfileRoot',
+      ]);
     });
 
     test('shallowCopy', () {
@@ -390,14 +394,8 @@
     });
 
     test('tooltip', () {
-      expect(
-        testTagRootedStackFrame.tooltip,
-        equals('[Tag] TagA - 10.0 ms'),
-      );
-      expect(
-        stackFrameA.tooltip,
-        equals('[Native] A - 10.0 ms'),
-      );
+      expect(testTagRootedStackFrame.tooltip, equals('[Tag] TagA - 10.0 ms'));
+      expect(stackFrameA.tooltip, equals('[Native] A - 10.0 ms'));
       expect(
         stackFrameB.tooltip,
         equals('[Dart] B - 10.0 ms - dart:async/zone.dart:2222'),
diff --git a/packages/devtools_app/test/cpu_profiler/cpu_profile_transformer_test.dart b/packages/devtools_app/test/screens/cpu_profiler/cpu_profile_transformer_test.dart
similarity index 92%
rename from packages/devtools_app/test/cpu_profiler/cpu_profile_transformer_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/cpu_profile_transformer_test.dart
index e50e122..23e697f 100644
--- a/packages/devtools_app/test/cpu_profiler/cpu_profile_transformer_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/cpu_profile_transformer_test.dart
@@ -4,10 +4,10 @@
 
 import 'package:devtools_app/src/screens/profiler/cpu_profile_model.dart';
 import 'package:devtools_app/src/screens/profiler/cpu_profile_transformer.dart';
-import 'package:devtools_app/src/shared/profiler_utils.dart';
+import 'package:devtools_app/src/shared/utils/profiler_utils.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import '../test_infra/test_data/cpu_profiler/cpu_profile.dart';
+import '../../test_infra/test_data/cpu_profiler/cpu_profile.dart';
 
 void main() {
   group('CpuProfileTransformer', () {
@@ -34,17 +34,15 @@
 
     test('process response with missing leaf frame', () {
       bool runTest() {
-        final cpuProfileDataWithMissingLeaf =
-            CpuProfileData.fromJson(responseWithMissingLeafFrame);
-        expect(
-          () async {
-            await cpuProfileTransformer.processData(
-              cpuProfileDataWithMissingLeaf,
-              processId: 'test',
-            );
-          },
-          throwsA(const TypeMatcher<AssertionError>()),
+        final cpuProfileDataWithMissingLeaf = CpuProfileData.fromJson(
+          responseWithMissingLeafFrame,
         );
+        expect(() async {
+          await cpuProfileTransformer.processData(
+            cpuProfileDataWithMissingLeaf,
+            processId: 'test',
+          );
+        }, throwsA(const TypeMatcher<AssertionError>()));
         return true;
       }
 
diff --git a/packages/devtools_app/test/cpu_profiler/cpu_profiler_controller_test.dart b/packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_controller_test.dart
similarity index 88%
rename from packages/devtools_app/test/cpu_profiler/cpu_profiler_controller_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_controller_test.dart
index d1766df..c57f244 100644
--- a/packages/devtools_app/test/cpu_profiler/cpu_profiler_controller_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_controller_test.dart
@@ -12,7 +12,7 @@
 import 'package:mockito/mockito.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../test_infra/test_data/cpu_profiler/cpu_profile.dart';
+import '../../test_infra/test_data/cpu_profiler/cpu_profile.dart';
 
 void main() {
   late ServiceConnectionManager fakeServiceManager;
@@ -32,8 +32,8 @@
     late CpuProfilerController controller;
 
     Future<void> disableAllFiltering() async {
-      for (final filter in controller.activeFilter.value.toggleFilters) {
-        filter.enabled.value = false;
+      for (final filter in controller.activeFilter.value.settingFilters) {
+        filter.setting.value = false;
       }
       controller.setActiveFilter();
       // [CpuProfilerController.filterData], which is triggered by the call to
@@ -105,9 +105,10 @@
         extentMicros: 100,
         processId: 'test',
       );
-      final originalData = controller.cpuProfileStore.lookupProfile(
-        label: CpuProfilerController.userTagNone,
-      )!;
+      final originalData =
+          controller.cpuProfileStore.lookupProfile(
+            label: CpuProfilerController.userTagNone,
+          )!;
       expect(
         originalData.functionProfile.stackFrames.values.length,
         equals(17),
@@ -117,10 +118,10 @@
       expect(filteredData.stackFrames.values.length, equals(12));
 
       // The native frame filter is applied by default.
-      final originalNativeFrames = originalData
-          .functionProfile.stackFrames.values
-          .where((sf) => sf.isNative)
-          .toList();
+      final originalNativeFrames =
+          originalData.functionProfile.stackFrames.values
+              .where((sf) => sf.isNative)
+              .toList();
       final filteredNativeFrames =
           filteredData.stackFrames.values.where((sf) => sf.isNative).toList();
       expect(originalNativeFrames.length, equals(5));
@@ -134,9 +135,10 @@
         extentMicros: 100,
         processId: 'test',
       );
-      final originalData = controller.cpuProfileStore.lookupProfile(
-        label: CpuProfilerController.userTagNone,
-      )!;
+      final originalData =
+          controller.cpuProfileStore.lookupProfile(
+            label: CpuProfilerController.userTagNone,
+          )!;
       expect(
         originalData.functionProfile.stackFrames.values.length,
         equals(17),
@@ -172,8 +174,8 @@
       filteredData = controller.dataNotifier.value!;
       expect(filteredData.stackFrames.values.length, equals(7));
 
-      for (final filter in controller.activeFilter.value.toggleFilters) {
-        filter.enabled.value = false;
+      for (final filter in controller.activeFilter.value.settingFilters) {
+        filter.setting.value = false;
       }
       controller.setActiveFilter(query: 'paint thread');
       await shortDelay();
@@ -188,9 +190,10 @@
         extentMicros: 100,
         processId: 'test',
       );
-      final originalData = controller.cpuProfileStore.lookupProfile(
-        label: CpuProfilerController.userTagNone,
-      )!;
+      final originalData =
+          controller.cpuProfileStore.lookupProfile(
+            label: CpuProfilerController.userTagNone,
+          )!;
       expect(
         originalData.functionProfile.stackFrames.values.length,
         equals(17),
@@ -242,16 +245,10 @@
     test('selectCpuStackFrame', () async {
       final dataNotifierValue = controller.dataNotifier.value!;
 
-      expect(
-        dataNotifierValue.selectedStackFrame,
-        isNull,
-      );
+      expect(dataNotifierValue.selectedStackFrame, isNull);
       expect(controller.selectedCpuStackFrameNotifier.value, isNull);
       controller.selectCpuStackFrame(testStackFrame);
-      expect(
-        dataNotifierValue.selectedStackFrame,
-        equals(testStackFrame),
-      );
+      expect(dataNotifierValue.selectedStackFrame, equals(testStackFrame));
       expect(
         controller.selectedCpuStackFrameNotifier.value,
         equals(testStackFrame),
@@ -327,8 +324,9 @@
     test('processDataForTag', () async {
       await disableAllFiltering();
 
-      final cpuProfileDataWithTags =
-          CpuProfileData.fromJson(cpuProfileDataWithUserTagsJson);
+      final cpuProfileDataWithTags = CpuProfileData.fromJson(
+        cpuProfileDataWithUserTagsJson,
+      );
       await controller.transformer.processData(
         cpuProfileDataWithTags,
         processId: 'test',
@@ -345,13 +343,16 @@
 
       expect(
         dataNotifierValue
-            .cpuProfileRoot.profileMetaData.time!.duration.inMicroseconds,
+            .cpuProfileRoot
+            .profileMetaData
+            .time!
+            .duration
+            .inMicroseconds,
         equals(250),
       );
       expect(
         dataNotifierValue.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 5
     Frame1 - children: 2 - excl: 0 - incl: 5
       Frame2 - children: 2 - excl: 0 - incl: 2
@@ -359,48 +360,41 @@
         Frame4 - children: 0 - excl: 1 - incl: 1
       Frame5 - children: 1 - excl: 2 - incl: 3
         Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagA');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 2
     Frame1 - children: 2 - excl: 0 - incl: 2
       Frame2 - children: 1 - excl: 0 - incl: 1
         Frame3 - children: 0 - excl: 1 - incl: 1
       Frame5 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagB');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 1
     Frame1 - children: 1 - excl: 0 - incl: 1
       Frame2 - children: 1 - excl: 0 - incl: 1
         Frame4 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagC');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 2
     Frame1 - children: 1 - excl: 0 - incl: 2
       Frame5 - children: 1 - excl: 1 - incl: 2
         Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag(CpuProfilerController.groupByUserTag);
@@ -418,11 +412,12 @@
 
     test('processDataForTag applies toggle filters by default', () async {
       expect(
-        controller.activeFilter.value.toggleFilters[0].enabled.value,
+        controller.activeFilter.value.settingFilters[0].setting.value,
         isTrue,
       );
-      final cpuProfileDataWithTags =
-          CpuProfileData.fromJson(cpuProfileDataWithUserTagsJson);
+      final cpuProfileDataWithTags = CpuProfileData.fromJson(
+        cpuProfileDataWithUserTagsJson,
+      );
       await controller.transformer.processData(
         cpuProfileDataWithTags,
         processId: 'test',
@@ -442,8 +437,7 @@
       );
       expect(
         cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 5
     Frame1 - children: 2 - excl: 0 - incl: 5
       Frame2 - children: 2 - excl: 0 - incl: 2
@@ -451,50 +445,42 @@
         Frame4 - children: 0 - excl: 1 - incl: 1
       Frame5 - children: 1 - excl: 2 - incl: 3
         Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagA');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 2 - excl: 0 - incl: 2
     Frame2 - children: 0 - excl: 1 - incl: 1
     Frame5 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagB');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 1
     Frame2 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag('userTagC');
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 1 - excl: 0 - incl: 2
     Frame5 - children: 1 - excl: 1 - incl: 2
       Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag(CpuProfilerController.groupByUserTag);
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 3 - excl: 0 - incl: 5
     userTagA - children: 2 - excl: 0 - incl: 2
       Frame2 - children: 0 - excl: 1 - incl: 1
@@ -504,15 +490,13 @@
     userTagC - children: 1 - excl: 0 - incl: 2
       Frame5 - children: 1 - excl: 1 - incl: 2
         Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
 
       await controller.loadDataWithTag(CpuProfilerController.groupByVmTag);
       expect(
         controller.dataNotifier.value!.cpuProfileRoot.profileAsString(),
-        equals(
-          '''
+        equals('''
   all - children: 3 - excl: 0 - incl: 5
     vmTagA - children: 2 - excl: 0 - incl: 2
       Frame2 - children: 0 - excl: 1 - incl: 1
@@ -522,8 +506,7 @@
     vmTagC - children: 1 - excl: 0 - incl: 2
       Frame5 - children: 1 - excl: 1 - incl: 2
         Frame6 - children: 0 - excl: 1 - incl: 1
-''',
-        ),
+'''),
       );
     });
 
@@ -540,24 +523,15 @@
 
     test('disposes', () {
       controller.dispose();
-      expect(
-        () {
-          controller.dataNotifier.addListener(() {});
-        },
-        throwsA(anything),
-      );
-      expect(
-        () {
-          controller.selectedCpuStackFrameNotifier.addListener(() {});
-        },
-        throwsA(anything),
-      );
-      expect(
-        () {
-          controller.profilerBusyStatus.addListener(() {});
-        },
-        throwsA(anything),
-      );
+      expect(() {
+        controller.dataNotifier.addListener(() {});
+      }, throwsA(anything));
+      expect(() {
+        controller.selectedCpuStackFrameNotifier.addListener(() {});
+      }, throwsA(anything));
+      expect(() {
+        controller.profilerBusyStatus.addListener(() {});
+      }, throwsA(anything));
     });
   });
 }
diff --git a/packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_test.dart b/packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_test.dart
new file mode 100644
index 0000000..338b143
--- /dev/null
+++ b/packages/devtools_app/test/screens/cpu_profiler/cpu_profiler_test.dart
@@ -0,0 +1,734 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/profiler/common.dart';
+import 'package:devtools_app/src/screens/profiler/cpu_profile_transformer.dart';
+import 'package:devtools_app/src/screens/profiler/cpu_profiler.dart';
+import 'package:devtools_app/src/screens/profiler/cpu_profiler_controller.dart';
+import 'package:devtools_app/src/screens/profiler/panes/bottom_up.dart';
+import 'package:devtools_app/src/screens/profiler/panes/call_tree.dart';
+import 'package:devtools_app/src/screens/profiler/panes/controls/cpu_profiler_controls.dart';
+import 'package:devtools_app/src/screens/profiler/panes/cpu_flame_chart.dart';
+import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table.dart';
+import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_controller.dart';
+import 'package:devtools_app/src/shared/charts/flame_chart.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../../test_infra/test_data/cpu_profiler/cpu_profile.dart';
+import '../../test_infra/utils/test_utils.dart';
+
+void main() {
+  late CpuProfiler cpuProfiler;
+  late CpuProfileData cpuProfileData;
+  late CpuProfilerController controller;
+
+  final fakeServiceManager = FakeServiceConnectionManager();
+  final app = fakeServiceManager.serviceManager.connectedApp!;
+  when(app.isFlutterNativeAppNow).thenReturn(false);
+  when(app.isFlutterAppNow).thenReturn(false);
+  when(app.isDebugFlutterAppNow).thenReturn(false);
+
+  setUp(() async {
+    setCharacterWidthForTables();
+    setGlobal(ServiceConnectionManager, fakeServiceManager);
+    setGlobal(PreferencesController, PreferencesController());
+
+    final transformer = CpuProfileTransformer();
+    controller = CpuProfilerController();
+    cpuProfileData = CpuProfileData.fromJson(goldenCpuProfileDataJson);
+    await transformer.processData(cpuProfileData, processId: 'test');
+
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(OfflineDataController, OfflineDataController());
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BannerMessagesController, BannerMessagesController());
+    setGlobal(PreferencesController, PreferencesController());
+    setGlobal(IdeTheme, IdeTheme());
+    final mockScriptManager = MockScriptManager();
+    when(
+      mockScriptManager.sortedScripts,
+    ).thenReturn(ValueNotifier<List<ScriptRef>>([]));
+    when(
+      mockScriptManager.scriptRefForUri(any),
+    ).thenReturn(ScriptRef(uri: 'package:test/script.dart', id: 'script.dart'));
+    setGlobal(ScriptManager, mockScriptManager);
+  });
+
+  group('CpuProfiler', () {
+    const windowSize = Size(2000.0, 1000.0);
+
+    testWidgetsWithWindowSize('builds for empty cpuProfileData', windowSize, (
+      WidgetTester tester,
+    ) async {
+      cpuProfileData = CpuProfileData.fromJson(emptyCpuProfileDataJson);
+      cpuProfiler = CpuProfiler(data: cpuProfileData, controller: controller);
+      await tester.pumpWidget(wrap(cpuProfiler));
+      expect(find.byType(TabBar), findsOneWidget);
+      expect(find.byType(CpuBottomUpTable), findsOneWidget);
+      expect(find.byType(CpuCallTreeTable), findsNothing);
+      expect(find.byType(CpuMethodTable), findsNothing);
+      expect(find.byType(CpuProfileFlameChart), findsNothing);
+      expect(find.byType(CpuProfileStats), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsOneWidget);
+      expect(find.byType(CollapseAllButton), findsOneWidget);
+      expect(find.byType(FlameChartHelpButton), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsNothing);
+      expect(find.byType(SearchField<CpuProfilerController>), findsNothing);
+      expect(find.byKey(ProfilerTab.bottomUp.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.callTree.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.methodTable.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.cpuFlameChart.key), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('builds for valid cpuProfileData', windowSize, (
+      WidgetTester tester,
+    ) async {
+      cpuProfiler = CpuProfiler(data: cpuProfileData, controller: controller);
+      await tester.pumpWidget(wrap(cpuProfiler));
+      expect(find.byType(TabBar), findsOneWidget);
+      expect(find.byType(CpuBottomUpTable), findsOneWidget);
+      expect(find.byType(CpuProfileStats), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsOneWidget);
+      expect(find.byType(CollapseAllButton), findsOneWidget);
+      expect(find.byType(FlameChartHelpButton), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsNothing);
+      expect(find.byType(SearchField<CpuProfilerController>), findsNothing);
+      expect(find.byKey(ProfilerTab.bottomUp.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.callTree.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.methodTable.key), findsOneWidget);
+      expect(find.byKey(ProfilerTab.cpuFlameChart.key), findsOneWidget);
+    });
+
+    Future<void> loadData(ProfilerScreenController controller) async {
+      for (final filter
+          in controller
+              .cpuProfilerController
+              .activeFilter
+              .value
+              .settingFilters) {
+        filter.setting.value = false;
+      }
+      final data = CpuProfilePair(
+        functionProfile: cpuProfileData,
+        // Function and code profiles have the same structure, so just use
+        // the function profile in place of a dedicated code profile for
+        // testing since we don't care about the contents as much as we
+        // care about testing the ability to switch between function and
+        // code profile views.
+        codeProfile: cpuProfileData,
+      );
+      await controller.cpuProfilerController.processAndSetData(
+        data,
+        processId: 'test-load-data',
+        storeAsUserTagNone: true,
+        shouldApplyFilters: true,
+        shouldRefreshSearchMatches: false,
+      );
+    }
+
+    group('profile views', () {
+      late ProfilerScreenController controller;
+
+      setUp(() async {
+        preferences.toggleVmDeveloperMode(false);
+
+        controller = ProfilerScreenController();
+
+        // Await a small delay to allow the ProfilerScreenController to complete
+        // initialization.
+        await Future.delayed(const Duration(seconds: 1));
+
+        cpuProfileData = CpuProfileData.fromJson(
+          cpuProfileDataWithUserTagsJson,
+        );
+      });
+
+      testWidgetsWithWindowSize(
+        'shows function / code view selector when in VM developer mode',
+        windowSize,
+        (WidgetTester tester) async {
+          await tester.runAsync(() async {
+            // We need to pump the entire `ProfilerScreenBody` widget because the
+            // CpuProfiler widget has `cpuProfileData` passed in from there, and
+            // CpuProfiler needs to be rebuilt on data updates.
+            await tester.pumpWidget(
+              wrapWithControllers(
+                const ProfilerScreenBody(),
+                profiler: controller,
+              ),
+            );
+            await tester.pump();
+
+            // Verify the profile view dropdown is not visible.
+            expect(find.byType(ModeDropdown), findsNothing);
+
+            // Enabling VM developer mode will clear the current profile as it's
+            // possible there's no code profile associated with it.
+            preferences.toggleVmDeveloperMode(true);
+            await tester.pumpAndSettle();
+            expect(find.byType(CpuProfiler), findsNothing);
+
+            // Verify the profile view dropdown appears when toggling VM developer
+            // mode and data is present.
+            await loadData(controller);
+            await tester.pumpAndSettle();
+            expect(find.byType(ModeDropdown), findsOneWidget);
+
+            // Verify the profile view dropdown is no longer visible.
+            preferences.toggleVmDeveloperMode(false);
+            await tester.pumpAndSettle();
+            expect(find.byType(ModeDropdown), findsNothing);
+          });
+        },
+      );
+
+      testWidgetsWithWindowSize(
+        'resets view to function when leaving VM developer mode',
+        windowSize,
+        (WidgetTester tester) async {
+          await tester.runAsync(() async {
+            // We need to pump the entire `ProfilerScreenBody` widget because the
+            // CpuProfiler widget has `cpuProfileData` passed in from there, and
+            // CpuProfiler needs to be rebuilt on data updates.
+            await tester.pumpWidget(
+              wrapWithControllers(
+                const ProfilerScreenBody(),
+                profiler: controller,
+              ),
+            );
+            await tester.pump();
+
+            // Verify the profile view dropdown is not visible.
+            expect(find.byType(ModeDropdown), findsNothing);
+
+            // The default view is the function profile, even when the profile view
+            // selector isn't visible.
+            expect(
+              controller.cpuProfilerController.viewType.value,
+              CpuProfilerViewType.function,
+            );
+
+            // Enable VM developer mode and reset the profile data.
+            preferences.toggleVmDeveloperMode(true);
+            await tester.pumpAndSettle();
+            expect(find.byType(CpuProfiler), findsNothing);
+            await loadData(controller);
+            await tester.pumpAndSettle();
+
+            // Verify the function profile view is still selected.
+            expect(
+              controller.cpuProfilerController.viewType.value,
+              CpuProfilerViewType.function,
+            );
+            expect(find.text('View: Function'), findsOneWidget);
+
+            // Switch to the code profile view.
+            await tester.tap(find.byType(ModeDropdown));
+            await tester.pumpAndSettle();
+            expect(find.text('View: Function'), findsWidgets);
+            expect(find.text('View: Code'), findsWidgets);
+            await tester.tap(find.text('View: Code').last);
+            await tester.pumpAndSettle();
+            expect(
+              controller.cpuProfilerController.viewType.value,
+              CpuProfilerViewType.code,
+            );
+            expect(find.byType(ModeDropdown), findsOneWidget);
+            expect(find.text('View: Code'), findsOneWidget);
+
+            // Disabling VM developer mode will reset the view to the function
+            // profile as the dropdown will no longer be visible.
+            preferences.toggleVmDeveloperMode(false);
+            await tester.pumpAndSettle();
+            expect(
+              controller.cpuProfilerController.viewType.value,
+              CpuProfilerViewType.function,
+            );
+          });
+        },
+      );
+    });
+
+    testWidgetsWithWindowSize('switches tabs', windowSize, (
+      WidgetTester tester,
+    ) async {
+      cpuProfiler = CpuProfiler(data: cpuProfileData, controller: controller);
+      await tester.pumpWidget(wrap(cpuProfiler));
+      expect(find.byType(TabBar), findsOneWidget);
+      expect(find.byType(CpuBottomUpTable), findsOneWidget);
+      expect(find.byType(CpuCallTreeTable), findsNothing);
+      expect(find.byType(CpuMethodTable), findsNothing);
+      expect(find.byType(CpuProfileFlameChart), findsNothing);
+      expect(find.byType(DevToolsFilterButton), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsOneWidget);
+      expect(find.byType(CollapseAllButton), findsOneWidget);
+      expect(find.byType(FlameChartHelpButton), findsNothing);
+      expect(find.byType(ModeDropdown), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsNothing);
+      expect(find.byType(SearchField<CpuProfilerController>), findsNothing);
+
+      await tester.tap(find.text('Call Tree'));
+      await tester.pumpAndSettle();
+      expect(find.byType(CpuBottomUpTable), findsNothing);
+      expect(find.byType(CpuCallTreeTable), findsOneWidget);
+      expect(find.byType(CpuMethodTable), findsNothing);
+      expect(find.byType(CpuProfileFlameChart), findsNothing);
+      expect(find.byType(DevToolsFilterButton), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsOneWidget);
+      expect(find.byType(CollapseAllButton), findsOneWidget);
+      expect(find.byType(FlameChartHelpButton), findsNothing);
+      expect(find.byType(ModeDropdown), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsNothing);
+      expect(find.byType(SearchField<CpuProfilerController>), findsNothing);
+
+      await tester.tap(find.text('Method Table'));
+      await tester.pumpAndSettle();
+      expect(find.byType(CpuBottomUpTable), findsNothing);
+      expect(find.byType(CpuCallTreeTable), findsNothing);
+      expect(find.byType(CpuMethodTable), findsOneWidget);
+      expect(find.byType(CpuProfileFlameChart), findsNothing);
+      expect(find.byType(DevToolsFilterButton), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsNothing);
+      expect(find.byType(CollapseAllButton), findsNothing);
+      expect(find.byType(FlameChartHelpButton), findsNothing);
+      expect(find.byType(ModeDropdown), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsOneWidget);
+      expect(find.byType(SearchField<CpuProfilerController>), findsNothing);
+
+      await tester.tap(find.text('CPU Flame Chart'));
+      await tester.pumpAndSettle();
+      expect(find.byType(CpuBottomUpTable), findsNothing);
+      expect(find.byType(CpuCallTreeTable), findsNothing);
+      expect(find.byType(CpuMethodTable), findsNothing);
+      expect(find.byType(CpuProfileFlameChart), findsOneWidget);
+      expect(find.byType(DevToolsFilterButton), findsOneWidget);
+      expect(find.byType(UserTagDropdown), findsOneWidget);
+      expect(find.byType(ExpandAllButton), findsNothing);
+      expect(find.byType(CollapseAllButton), findsNothing);
+      expect(find.byType(FlameChartHelpButton), findsOneWidget);
+      expect(find.byType(ModeDropdown), findsNothing);
+      expect(find.byType(SearchField<MethodTableController>), findsNothing);
+      expect(find.byType(SearchField<CpuProfilerController>), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('can expand and collapse data', windowSize, (
+      WidgetTester tester,
+    ) async {
+      cpuProfiler = CpuProfiler(data: cpuProfileData, controller: controller);
+      await tester.pumpWidget(wrap(cpuProfiler));
+      await tester.tap(find.text('Call Tree'));
+      await tester.pumpAndSettle();
+
+      expect(cpuProfileData.cpuProfileRoot.isExpanded, isFalse);
+      await tester.tap(find.byType(ExpandAllButton));
+      expect(cpuProfiler.callTreeRoots.first.isExpanded, isTrue);
+      await tester.tap(find.byType(CollapseAllButton));
+      expect(cpuProfiler.callTreeRoots.first.isExpanded, isFalse);
+
+      await tester.tap(find.text('Bottom Up'));
+      await tester.pumpAndSettle();
+      for (final root in cpuProfiler.bottomUpRoots) {
+        expect(root.isExpanded, isFalse);
+      }
+      await tester.tap(find.byType(ExpandAllButton));
+      for (final root in cpuProfiler.bottomUpRoots) {
+        expect(root.isExpanded, isTrue);
+      }
+      await tester.tap(find.byType(CollapseAllButton));
+      for (final root in cpuProfiler.bottomUpRoots) {
+        expect(root.isExpanded, isFalse);
+      }
+    });
+
+    group('UserTag filters', () {
+      late ProfilerScreenController controller;
+
+      setUp(() async {
+        controller = ProfilerScreenController();
+
+        // Await a small delay to allow the ProfilerScreenController to complete
+        // initialization.
+        await Future.delayed(const Duration(seconds: 1));
+
+        cpuProfileData = CpuProfileData.fromJson(
+          cpuProfileDataWithUserTagsJson,
+        );
+        await controller.cpuProfilerController.transformer.processData(
+          cpuProfileData,
+          processId: 'test',
+        );
+        // Call this to force the value of `_dataByTag[userTagNone]` to be set.
+        controller.cpuProfilerController.loadProcessedData(
+          CpuProfilePair(
+            functionProfile: cpuProfileData,
+            // Function and code profiles have the same structure, so just use
+            // the function profile in place of a dedicated code profile for
+            // testing since we don't care about the contents as much as we
+            // care about testing the ability to switch between function and
+            // code profile views.
+            codeProfile: cpuProfileData,
+          ),
+          storeAsUserTagNone: true,
+        );
+      });
+
+      testWidgetsWithWindowSize('can filter data by user tag', windowSize, (
+        WidgetTester tester,
+      ) async {
+        await tester.runAsync(() async {
+          // We need to pump the entire `ProfilerScreenBody` widget because the
+          // CpuProfiler widget has `cpuProfileData` passed in from there, and
+          // CpuProfiler needs to be rebuilt on data updates.
+          await tester.pumpWidget(
+            wrapWithControllers(
+              const ProfilerScreenBody(),
+              profiler: controller,
+            ),
+          );
+          await tester.pump();
+
+          expect(controller.cpuProfilerController.userTags.length, equals(3));
+
+          expect(find.byType(UserTagDropdown), findsOneWidget);
+          // There is a Text widget and a RichText widget.
+          expect(
+            find.text('Filter by tag: userTagA', skipOffstage: false),
+            findsWidgets,
+          );
+          expect(
+            find.text('Filter by tag: userTagB', skipOffstage: false),
+            findsWidgets,
+          );
+          expect(
+            find.text('Filter by tag: userTagC', skipOffstage: false),
+            findsWidgets,
+          );
+          expect(
+            find.text('Group by: User Tag', skipOffstage: false),
+            findsWidgets,
+          );
+
+          await tester.tap(find.text('Call Tree'));
+          await tester.pumpAndSettle();
+          expect(find.byType(CpuCallTreeTable), findsOneWidget);
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+
+          expect(
+            controller
+                .cpuProfileData!
+                .profileMetaData
+                .time!
+                .duration
+                .inMicroseconds,
+            equals(250),
+          );
+
+          expect(find.richTextContaining('Frame1'), findsOneWidget);
+          expect(find.richTextContaining('Frame2'), findsOneWidget);
+          expect(find.richTextContaining('Frame3'), findsOneWidget);
+          expect(find.richTextContaining('Frame4'), findsOneWidget);
+          expect(find.richTextContaining('Frame5'), findsOneWidget);
+          expect(find.richTextContaining('Frame6'), findsOneWidget);
+          expect(find.text('userTagA'), findsNothing);
+          expect(find.text('userTagB'), findsNothing);
+          expect(find.text('userTagC'), findsNothing);
+
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          await tester.tap(find.text('Filter by tag: userTagA').last);
+
+          // Await a small delay to allow the CpuProfilerController to finish
+          // processing data for the new user tag.
+          await Future.delayed(const Duration(seconds: 1));
+          await tester.pumpAndSettle();
+
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+          expect(
+            controller
+                .cpuProfileData!
+                .profileMetaData
+                .time!
+                .duration
+                .inMicroseconds,
+            equals(100),
+          );
+          expect(find.richTextContaining('Frame1'), findsNothing);
+          expect(find.richTextContaining('Frame2'), findsOneWidget);
+          expect(find.richTextContaining('Frame3'), findsNothing);
+          expect(find.richTextContaining('Frame4'), findsNothing);
+          expect(find.richTextContaining('Frame5'), findsOneWidget);
+          expect(find.richTextContaining('Frame6'), findsNothing);
+          expect(find.text('userTagA'), findsNothing);
+          expect(find.text('userTagB'), findsNothing);
+          expect(find.text('userTagC'), findsNothing);
+
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          await tester.tap(find.text('Filter by tag: userTagB').last);
+
+          // Await a small delay to allow the CpuProfilerController to finish
+          // processing data for the new user tag.
+          await Future.delayed(const Duration(seconds: 1));
+          await tester.pumpAndSettle();
+
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+          expect(
+            controller
+                .cpuProfileData!
+                .profileMetaData
+                .time!
+                .duration
+                .inMicroseconds,
+            equals(50),
+          );
+          expect(find.richTextContaining('Frame1'), findsNothing);
+          expect(find.richTextContaining('Frame2'), findsOneWidget);
+          expect(find.richTextContaining('Frame3'), findsNothing);
+          expect(find.richTextContaining('Frame4'), findsNothing);
+          expect(find.richTextContaining('Frame5'), findsNothing);
+          expect(find.richTextContaining('Frame6'), findsNothing);
+          expect(find.text('userTagA'), findsNothing);
+          expect(find.text('userTagB'), findsNothing);
+          expect(find.text('userTagC'), findsNothing);
+
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          await tester.tap(find.text('Filter by tag: userTagC').last);
+
+          // Await a small delay to allow the CpuProfilerController to finish
+          // processing data for the new user tag.
+          await Future.delayed(const Duration(seconds: 1));
+          await tester.pumpAndSettle();
+
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+          expect(
+            controller
+                .cpuProfileData!
+                .profileMetaData
+                .time!
+                .duration
+                .inMicroseconds,
+            equals(100),
+          );
+          expect(find.richTextContaining('Frame1'), findsNothing);
+          expect(find.richTextContaining('Frame2'), findsNothing);
+          expect(find.richTextContaining('Frame3'), findsNothing);
+          expect(find.richTextContaining('Frame4'), findsNothing);
+          expect(find.richTextContaining('Frame5'), findsOneWidget);
+          expect(find.richTextContaining('Frame6'), findsOneWidget);
+          expect(find.text('userTagA'), findsNothing);
+          expect(find.text('userTagB'), findsNothing);
+          expect(find.text('userTagC'), findsNothing);
+        });
+      });
+    });
+
+    group('Group by', () {
+      late ProfilerScreenController controller;
+
+      setUp(() async {
+        controller = ProfilerScreenController();
+
+        // Await a small delay to allow the ProfilerScreenController to complete
+        // initialization.
+        await Future.delayed(const Duration(seconds: 1));
+
+        preferences.toggleVmDeveloperMode(true);
+        cpuProfileData = CpuProfileData.fromJson(
+          cpuProfileDataWithUserTagsJson,
+        );
+        await loadData(controller);
+      });
+
+      testWidgetsWithWindowSize('user tags', windowSize, (tester) async {
+        await tester.runAsync(() async {
+          // We need to pump the entire `ProfilerScreenBody` widget because the
+          // CpuProfiler widget has `cpuProfileData` passed in from there, and
+          // CpuProfiler needs to be rebuilt on data updates.
+          await tester.pumpWidget(
+            wrapWithControllers(
+              const ProfilerScreenBody(),
+              profiler: controller,
+            ),
+          );
+          await tester.pump();
+
+          await tester.tap(find.text('Call Tree'));
+          await tester.pumpAndSettle();
+          expect(find.byType(CpuCallTreeTable), findsOneWidget);
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          await tester.tap(find.text('Group by: User Tag').last);
+
+          // Await a small delay to allow the CpuProfilerController to finish
+          // processing data for the new user tag.
+          await Future.delayed(const Duration(seconds: 1));
+          await tester.pumpAndSettle();
+
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+
+          expect(find.richTextContaining('Frame1'), findsNWidgets(3));
+          expect(find.richTextContaining('Frame2'), findsNWidgets(2));
+          expect(find.richTextContaining('Frame3'), findsNWidgets(1));
+          expect(find.richTextContaining('Frame4'), findsNWidgets(1));
+          expect(find.richTextContaining('Frame5'), findsNWidgets(2));
+          expect(find.richTextContaining('Frame6'), findsNWidgets(1));
+          expect(find.richText('userTagA'), findsOneWidget);
+          expect(find.richText('userTagB'), findsOneWidget);
+          expect(find.richText('userTagC'), findsOneWidget);
+        });
+      });
+
+      testWidgetsWithWindowSize('VM tags', windowSize, (tester) async {
+        await tester.runAsync(() async {
+          // We need to pump the entire `ProfilerScreenBody` widget because the
+          // CpuProfiler widget has `cpuProfileData` passed in from there, and
+          // CpuProfiler needs to be rebuilt on data updates.
+          await tester.pumpWidget(
+            wrapWithControllers(
+              const ProfilerScreenBody(),
+              profiler: controller,
+            ),
+          );
+          await tester.pump();
+
+          await tester.tap(find.text('Call Tree'));
+          await tester.pumpAndSettle();
+          expect(find.byType(CpuCallTreeTable), findsOneWidget);
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          await tester.tap(find.text('Group by: VM Tag').last);
+
+          // Await a small delay to allow the CpuProfilerController to finish
+          // processing data for the new user tag.
+          await Future.delayed(const Duration(seconds: 1));
+          await tester.pumpAndSettle();
+
+          await tester.tap(find.text('Expand All'));
+          await tester.pumpAndSettle();
+
+          expect(find.richTextContaining('Frame1'), findsNWidgets(3));
+          expect(find.richTextContaining('Frame2'), findsNWidgets(2));
+          expect(find.richTextContaining('Frame3'), findsNWidgets(1));
+          expect(find.richTextContaining('Frame4'), findsNWidgets(1));
+          expect(find.richTextContaining('Frame5'), findsNWidgets(2));
+          expect(find.richTextContaining('Frame6'), findsNWidgets(1));
+          expect(find.richText('vmTagA'), findsOneWidget);
+          expect(find.richText('vmTagB'), findsOneWidget);
+          expect(find.richText('vmTagC'), findsOneWidget);
+
+          // Check that disabling VM developer mode when grouping by VM tag
+          // automatically resets the view to 'Filter by tag: none'.
+          preferences.toggleVmDeveloperMode(false);
+          await tester.pumpAndSettle();
+          expect(find.byType(CpuCallTreeTable), findsOneWidget);
+          expect(find.text('Filter by tag: none'), findsOneWidget);
+          await tester.tap(find.byType(UserTagDropdown));
+          await tester.pumpAndSettle();
+          expect(find.text('Group by: VM Tag'), findsNothing);
+        });
+      });
+    });
+  });
+
+  group('$CpuProfileStats', () {
+    testWidgets('displays correctly', (WidgetTester tester) async {
+      final metadata = CpuProfileMetaData(
+        sampleCount: 100,
+        samplePeriod: 100,
+        stackDepth: 128,
+        time:
+            TimeRange()
+              ..start = const Duration()
+              ..end = const Duration(microseconds: 10000),
+      );
+      await tester.pumpWidget(wrap(CpuProfileStats(metadata: metadata)));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byTooltip('The duration of time spanned by the CPU samples'),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip('The number of samples included in the profile'),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip(
+          'The frequency at which samples are collected by the profiler'
+          ' (once every 100 micros)',
+        ),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip('The maximum stack trace depth of a collected sample'),
+        findsOneWidget,
+      );
+      expect(find.text('Duration: 10.0 ms'), findsOneWidget);
+      expect(find.text('Sample count: 100'), findsOneWidget);
+      expect(find.text('Sampling rate: 10000 Hz'), findsOneWidget);
+      expect(find.text('Sampling depth: 128'), findsOneWidget);
+    });
+
+    testWidgets('displays correctly for invalid data', (
+      WidgetTester tester,
+    ) async {
+      final metadata = CpuProfileMetaData(
+        sampleCount: 100,
+        samplePeriod: 0,
+        stackDepth: 128,
+        time:
+            TimeRange()
+              ..start = const Duration()
+              ..end = const Duration(microseconds: 10000),
+      );
+      await tester.pumpWidget(wrap(CpuProfileStats(metadata: metadata)));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byTooltip('The duration of time spanned by the CPU samples'),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip('The number of samples included in the profile'),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip(
+          'The frequency at which samples are collected by the profiler',
+        ),
+        findsOneWidget,
+      );
+      expect(
+        find.byTooltip('The maximum stack trace depth of a collected sample'),
+        findsOneWidget,
+      );
+      expect(find.text('Duration: 10.0 ms'), findsOneWidget);
+      expect(find.text('Sample count: 100'), findsOneWidget);
+      expect(find.text('Sampling rate: -- Hz'), findsOneWidget);
+      expect(find.text('Sampling depth: 128'), findsOneWidget);
+    });
+  });
+}
diff --git a/packages/devtools_app/test/cpu_profiler/method_table/method_table_controller_test.dart b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_controller_test.dart
similarity index 97%
rename from packages/devtools_app/test/cpu_profiler/method_table/method_table_controller_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_controller_test.dart
index 76592c3..eff216c 100644
--- a/packages/devtools_app/test/cpu_profiler/method_table/method_table_controller_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_controller_test.dart
@@ -9,8 +9,8 @@
 import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_model.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import '../../test_infra/test_data/cpu_profiler/simple_profile_1.dart';
-import '../../test_infra/test_data/cpu_profiler/simple_profile_2.dart';
+import '../../../test_infra/test_data/cpu_profiler/simple_profile_1.dart';
+import '../../../test_infra/test_data/cpu_profiler/simple_profile_2.dart';
 
 void main() {
   group('$MethodTableController', () {
diff --git a/packages/devtools_app/test/cpu_profiler/method_table/method_table_model_test.dart b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_model_test.dart
similarity index 85%
rename from packages/devtools_app/test/cpu_profiler/method_table/method_table_model_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_model_test.dart
index d09c158..2868bdd 100644
--- a/packages/devtools_app/test/cpu_profiler/method_table/method_table_model_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_model_test.dart
@@ -4,7 +4,7 @@
 
 import 'package:devtools_app/devtools_app.dart';
 import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_model.dart';
-import 'package:devtools_app/src/shared/profiler_utils.dart';
+import 'package:devtools_app/src/shared/utils/profiler_utils.dart';
 import 'package:flutter_test/flutter_test.dart';
 
 void main() {
@@ -17,9 +17,10 @@
       selfCount: 1,
       profileMetaData: ProfileMetaData(
         sampleCount: 10,
-        time: TimeRange()
-          ..start = Duration.zero
-          ..end = const Duration(seconds: 1),
+        time:
+            TimeRange()
+              ..start = Duration.zero
+              ..end = const Duration(seconds: 1),
       ),
       stackFrameIds: {'1'},
     );
@@ -31,9 +32,10 @@
       selfCount: 4,
       profileMetaData: ProfileMetaData(
         sampleCount: 10,
-        time: TimeRange()
-          ..start = Duration.zero
-          ..end = const Duration(seconds: 1),
+        time:
+            TimeRange()
+              ..start = Duration.zero
+              ..end = const Duration(seconds: 1),
       ),
       stackFrameIds: {'2'},
     );
@@ -45,9 +47,10 @@
       selfCount: 2,
       profileMetaData: ProfileMetaData(
         sampleCount: 10,
-        time: TimeRange()
-          ..start = Duration.zero
-          ..end = const Duration(seconds: 1),
+        time:
+            TimeRange()
+              ..start = Duration.zero
+              ..end = const Duration(seconds: 1),
       ),
       stackFrameIds: {'3'},
     );
@@ -59,9 +62,10 @@
       selfCount: 3,
       profileMetaData: ProfileMetaData(
         sampleCount: 10,
-        time: TimeRange()
-          ..start = Duration.zero
-          ..end = const Duration(seconds: 1),
+        time:
+            TimeRange()
+              ..start = Duration.zero
+              ..end = const Duration(seconds: 1),
       ),
       stackFrameIds: {'4'},
     );
diff --git a/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_test.dart b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_test.dart
new file mode 100644
index 0000000..8595b80
--- /dev/null
+++ b/packages/devtools_app/test/screens/cpu_profiler/method_table/method_table_test.dart
@@ -0,0 +1,128 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/profiler/cpu_profile_transformer.dart';
+import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table.dart';
+import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_controller.dart';
+import 'package:devtools_app/src/screens/profiler/panes/method_table/method_table_model.dart';
+import 'package:devtools_app/src/shared/table/table.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../../../test_infra/matchers/matchers.dart';
+import '../../../test_infra/test_data/cpu_profiler/simple_profile_2.dart';
+import '../../../test_infra/utils/test_utils.dart';
+
+void main() {
+  late MethodTableController methodTableController;
+
+  setUp(() async {
+    setCharacterWidthForTables();
+    setGlobal(
+      ServiceConnectionManager,
+      createMockServiceConnectionWithDefaults(),
+    );
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(OfflineDataController, OfflineDataController());
+    final mockScriptManager = MockScriptManager();
+    when(
+      mockScriptManager.sortedScripts,
+    ).thenReturn(ValueNotifier<List<ScriptRef>>([]));
+    when(
+      mockScriptManager.scriptRefForUri(any),
+    ).thenReturn(ScriptRef(uri: 'package:test/script.dart', id: 'script.dart'));
+    setGlobal(ScriptManager, mockScriptManager);
+    final data = CpuProfileData.fromJson(simpleCpuProfile2);
+    await CpuProfileTransformer().processData(data, processId: 'test');
+    methodTableController = MethodTableController(
+      dataNotifier: FixedValueListenable<CpuProfileData>(data),
+    )..createMethodTableGraph(data);
+  });
+
+  const windowSize = Size(2000.0, 1000.0);
+
+  Future<void> pumpMethodTable(WidgetTester tester) async {
+    await tester.pumpWidget(
+      wrap(CpuMethodTable(methodTableController: methodTableController)),
+    );
+    await tester.pumpAndSettle();
+    expect(find.byType(CpuMethodTable), findsOneWidget);
+  }
+
+  group('$CpuMethodTable', () {
+    testWidgetsWithWindowSize('loads methods with no selection', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpMethodTable(tester);
+      expect(
+        find.byType(SearchableFlatTable<MethodTableGraphNode>),
+        findsOneWidget,
+      );
+      expect(find.text('Method'), findsOneWidget);
+      expect(find.text('Total %'), findsOneWidget);
+      expect(find.text('Self %'), findsOneWidget);
+      expect(find.text('Caller %'), findsNothing);
+      expect(find.text('Callee %'), findsNothing);
+      expect(
+        find.text('Select a method to view its call graph.'),
+        findsOneWidget,
+      );
+      await expectLater(
+        find.byType(CpuMethodTable),
+        matchesDevToolsGolden(
+          '../../../test_infra/goldens/cpu_profiler/method_table_no_selection.png',
+        ),
+      );
+    });
+
+    testWidgetsWithWindowSize('loads methods with selection', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final interestingNode = methodTableController.methods.value.first;
+      expect(interestingNode.name, 'A');
+      expect(interestingNode.predecessors, isNotEmpty);
+      expect(interestingNode.successors, isNotEmpty);
+
+      methodTableController.selectedNode.value = interestingNode;
+      await pumpMethodTable(tester);
+      expect(
+        find.byType(SearchableFlatTable<MethodTableGraphNode>),
+        findsOneWidget,
+      );
+      expect(find.byType(FlatTable<MethodTableGraphNode>), findsNWidgets(2));
+      expect(find.text('Method'), findsNWidgets(3));
+      expect(find.text('Total %'), findsOneWidget);
+      expect(find.text('Self %'), findsOneWidget);
+      expect(find.text('Caller %'), findsOneWidget);
+      expect(find.text('Callee %'), findsOneWidget);
+      expect(
+        find.text('Select a method to view its call graph.'),
+        findsNothing,
+      );
+      await expectLater(
+        find.byType(CpuMethodTable),
+        matchesDevToolsGolden(
+          '../../../test_infra/goldens/cpu_profiler/method_table_with_selection.png',
+        ),
+      );
+
+      methodTableController.selectedNode.value =
+          methodTableController.methods.value.last;
+      await tester.pumpAndSettle();
+      await expectLater(
+        find.byType(CpuMethodTable),
+        matchesDevToolsGolden(
+          '../../../test_infra/goldens/cpu_profiler/method_table_with_selection_2.png',
+        ),
+      );
+    });
+  });
+}
diff --git a/packages/devtools_app/test/cpu_profiler/profiler_screen_controller_test.dart b/packages/devtools_app/test/screens/cpu_profiler/profiler_screen_controller_test.dart
similarity index 70%
rename from packages/devtools_app/test/cpu_profiler/profiler_screen_controller_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/profiler_screen_controller_test.dart
index 71bdff0..9dd1f8a 100644
--- a/packages/devtools_app/test/cpu_profiler/profiler_screen_controller_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/profiler_screen_controller_test.dart
@@ -12,8 +12,9 @@
   group('ProfilerScreenController', () {
     late ProfilerScreenController controller;
     final fakeServiceConnection = FakeServiceConnectionManager();
-    when(fakeServiceConnection.serviceManager.connectedApp!.isFlutterAppNow)
-        .thenReturn(false);
+    when(
+      fakeServiceConnection.serviceManager.connectedApp!.isFlutterAppNow,
+    ).thenReturn(false);
 
     setUp(() {
       setGlobal(
@@ -36,27 +37,18 @@
 
     test('disposes', () {
       controller.dispose();
-      expect(
-        () {
-          controller.recordingNotifier.addListener(() {});
-        },
-        throwsA(anything),
-      );
+      expect(() {
+        controller.recordingNotifier.addListener(() {});
+      }, throwsA(anything));
 
-      expect(
-        () {
-          controller.cpuProfilerController.dataNotifier.addListener(() {});
-        },
-        throwsA(anything),
-      );
+      expect(() {
+        controller.cpuProfilerController.dataNotifier.addListener(() {});
+      }, throwsA(anything));
 
-      expect(
-        () {
-          controller.cpuProfilerController.selectedCpuStackFrameNotifier
-              .addListener(() {});
-        },
-        throwsA(anything),
-      );
+      expect(() {
+        controller.cpuProfilerController.selectedCpuStackFrameNotifier
+            .addListener(() {});
+      }, throwsA(anything));
     });
   });
 }
diff --git a/packages/devtools_app/test/cpu_profiler/profiler_screen_test.dart b/packages/devtools_app/test/screens/cpu_profiler/profiler_screen_test.dart
similarity index 71%
rename from packages/devtools_app/test/cpu_profiler/profiler_screen_test.dart
rename to packages/devtools_app/test/screens/cpu_profiler/profiler_screen_test.dart
index 8476d58..fa6d5b4 100644
--- a/packages/devtools_app/test/cpu_profiler/profiler_screen_test.dart
+++ b/packages/devtools_app/test/screens/cpu_profiler/profiler_screen_test.dart
@@ -7,16 +7,16 @@
 import 'package:devtools_app/src/screens/profiler/panes/controls/cpu_profiler_controls.dart';
 import 'package:devtools_app/src/screens/profiler/profiler_status.dart';
 import 'package:devtools_app/src/service/vm_flags.dart' as vm_flags;
-import 'package:devtools_app/src/shared/file_import.dart';
+import 'package:devtools_app/src/shared/ui/file_import.dart';
 import 'package:devtools_app/src/shared/ui/vm_flag_widgets.dart';
 import 'package:devtools_test/devtools_test.dart';
 import 'package:devtools_test/helpers.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import '../test_infra/scenes/cpu_profiler/default.dart';
-import '../test_infra/scenes/scene_test_extensions.dart';
-import '../test_infra/utils/test_utils.dart';
+import '../../test_infra/scenes/cpu_profiler/default.dart';
+import '../../test_infra/scenes/scene_test_extensions.dart';
+import '../../test_infra/utils/test_utils.dart';
 
 void main() {
   late CpuProfilerDefaultScene scene;
@@ -34,16 +34,16 @@
       expect(find.byType(StartStopRecordingButton), findsOneWidget);
       expect(find.byType(ClearButton), findsOneWidget);
       expect(find.text('Load all CPU samples'), findsOneWidget);
-      if (scene.fakeServiceConnection.serviceManager.connectedApp!
+      if (scene
+          .fakeServiceConnection
+          .serviceManager
+          .connectedApp!
           .isFlutterNativeAppNow) {
         expect(find.text('Profile app start up'), findsOneWidget);
       }
       expect(find.byType(CpuSamplingRateDropdown), findsOneWidget);
       expect(find.byType(OpenSaveButtonGroup), findsOneWidget);
-      expect(
-        find.byType(ProfileRecordingInstructions),
-        findsOneWidget,
-      );
+      expect(find.byType(ProfileRecordingInstructions), findsOneWidget);
       expect(find.byType(RecordingStatus), findsNothing);
       expect(find.byType(CircularProgressIndicator), findsNothing);
       expect(find.byType(CpuProfiler), findsNothing);
@@ -103,10 +103,7 @@
         // Start recording.
         await tester.tap(find.byType(StartStopRecordingButton));
         await tester.pump(const Duration(seconds: 1));
-        expect(
-          find.byType(ProfileRecordingInstructions),
-          findsNothing,
-        );
+        expect(find.byType(ProfileRecordingInstructions), findsNothing);
         expect(find.byType(RecordingStatus), findsOneWidget);
         expect(find.byType(CircularProgressIndicator), findsOneWidget);
         expect(find.byType(CpuProfiler), findsNothing);
@@ -125,37 +122,32 @@
       },
     );
 
-    testWidgetsWithWindowSize(
-      'builds for disabled profiler',
-      windowSize,
-      (WidgetTester tester) async {
-        await tester.runAsync(() async {
-          await scene.fakeServiceConnection.serviceManager.service!.setFlag(
-            vm_flags.profiler,
-            'false',
-          );
-        });
-        await pumpProfilerScreen(tester);
-
-        expect(find.byType(CpuProfilerDisabled), findsOneWidget);
-        expect(
-          find.byType(ProfileRecordingInstructions),
-          findsNothing,
+    testWidgetsWithWindowSize('builds for disabled profiler', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await tester.runAsync(() async {
+        await scene.fakeServiceConnection.serviceManager.service!.setFlag(
+          vm_flags.profiler,
+          'false',
         );
-        expect(find.byType(RecordButton), findsNothing);
-        expect(find.byType(StopRecordingButton), findsNothing);
-        expect(find.byType(ClearButton), findsNothing);
-        expect(find.byType(CpuSamplingRateDropdown), findsNothing);
-        expect(find.byType(OpenSaveButtonGroup), findsNothing);
+      });
+      await pumpProfilerScreen(tester);
 
-        await tester.runAsync(() async {
-          await tester.tap(find.text('Enable profiler'));
-          // Delay to ensure the memory profiler has collected data.
-          await tester.pump(const Duration(seconds: 1));
-        });
-        await tester.pumpAndSettle();
-        verifyBaseState();
-      },
-    );
+      expect(find.byType(CpuProfilerDisabled), findsOneWidget);
+      expect(find.byType(ProfileRecordingInstructions), findsNothing);
+      expect(find.byType(RecordButton), findsNothing);
+      expect(find.byType(StopRecordingButton), findsNothing);
+      expect(find.byType(ClearButton), findsNothing);
+      expect(find.byType(CpuSamplingRateDropdown), findsNothing);
+      expect(find.byType(OpenSaveButtonGroup), findsNothing);
+
+      await tester.runAsync(() async {
+        await tester.tap(find.text('Enable profiler'));
+        // Delay to ensure the memory profiler has collected data.
+        await tester.pump(const Duration(seconds: 1));
+      });
+      await tester.pumpAndSettle();
+      verifyBaseState();
+    });
   });
 }
diff --git a/packages/devtools_app/test/debugger/association_variable_test.dart b/packages/devtools_app/test/screens/debugger/association_variable_test.dart
similarity index 79%
rename from packages/devtools_app/test/debugger/association_variable_test.dart
rename to packages/devtools_app/test/screens/debugger/association_variable_test.dart
index bbf90e2..1587388 100644
--- a/packages/devtools_app/test/debugger/association_variable_test.dart
+++ b/packages/devtools_app/test/screens/debugger/association_variable_test.dart
@@ -59,22 +59,18 @@
         isSystemIsolate: false,
       );
       final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
+        BoundVariable(name: 'test', value: instance),
         isolateRef,
       );
-      when(manager.serviceManager.service!.getObject(isolateId, objectId))
-          .thenAnswer((_) async {
+      when(
+        manager.serviceManager.service!.getObject(isolateId, objectId),
+      ).thenAnswer((_) async {
         return instance;
       });
 
       await buildVariablesTree(variable);
 
-      expect(variable.children, [
-        matchesVariable(name: 'Hey', value: '12.34'),
-      ]);
+      expect(variable.children, [matchesVariable(name: 'Hey', value: '12.34')]);
     },
   );
 
@@ -107,22 +103,18 @@
         ],
       );
       final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
+        BoundVariable(name: 'test', value: instance),
         isolateRef,
       );
-      when(manager.serviceManager.service!.getObject(isolateId, objectId))
-          .thenAnswer((_) async {
+      when(
+        manager.serviceManager.service!.getObject(isolateId, objectId),
+      ).thenAnswer((_) async {
         return instance;
       });
 
       await buildVariablesTree(variable);
 
-      expect(variable.children, [
-        matchesVariable(name: '1', value: '12.34'),
-      ]);
+      expect(variable.children, [matchesVariable(name: '1', value: '12.34')]);
     },
   );
 
@@ -155,14 +147,12 @@
         ],
       );
       final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
+        BoundVariable(name: 'test', value: instance),
         isolateRef,
       );
-      when(manager.serviceManager.service!.getObject(isolateId, objectId))
-          .thenAnswer((_) async {
+      when(
+        manager.serviceManager.service!.getObject(isolateId, objectId),
+      ).thenAnswer((_) async {
         return instance;
       });
 
@@ -179,19 +169,8 @@
   );
 }
 
-Matcher matchesVariable({
-  required String? name,
-  required Object value,
-}) {
+Matcher matchesVariable({required String? name, required Object value}) {
   return const TypeMatcher<DartObjectNode>()
-      .having(
-        (v) => v.displayValue,
-        'displayValue',
-        equals(value),
-      )
-      .having(
-        (v) => v.name,
-        'name',
-        equals(name),
-      );
+      .having((v) => v.displayValue, 'displayValue', equals(value))
+      .having((v) => v.name, 'name', equals(name));
 }
diff --git a/packages/devtools_app/test/debugger/debugger_codeview_statistics_test.dart b/packages/devtools_app/test/screens/debugger/debugger_codeview_statistics_test.dart
similarity index 78%
rename from packages/devtools_app/test/debugger/debugger_codeview_statistics_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_codeview_statistics_test.dart
index 084e191..3f683d9 100644
--- a/packages/devtools_app/test/debugger/debugger_codeview_statistics_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_codeview_statistics_test.dart
@@ -38,10 +38,12 @@
       isProfileBuild: false,
       isWebApp: false,
     );
-    when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-        .thenReturn(false);
-    when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow)
-        .thenReturn(false);
+    when(
+      fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow,
+    ).thenReturn(false);
+    when(
+      fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow,
+    ).thenReturn(false);
     setGlobal(ServiceConnectionManager, fakeServiceConnection);
     setGlobal(IdeTheme, IdeTheme());
     setGlobal(ScriptManager, MockScriptManager());
@@ -54,29 +56,32 @@
     scriptsHistory.pushEntry(mockScript!);
     final mockCodeViewController = debuggerController.codeViewController;
 
-    when(mockCodeViewController.currentScriptRef)
-        .thenReturn(ValueNotifier(mockScriptRef));
-    when(mockCodeViewController.currentParsedScript)
-        .thenReturn(ValueNotifier(mockParsedScript));
+    when(
+      mockCodeViewController.currentScriptRef,
+    ).thenReturn(ValueNotifier(mockScriptRef));
+    when(
+      mockCodeViewController.currentParsedScript,
+    ).thenReturn(ValueNotifier(mockParsedScript));
     when(mockCodeViewController.scriptsHistory).thenReturn(scriptsHistory);
 
     showCodeCoverage = ValueNotifier<bool>(false);
     showProfileHits = ValueNotifier<bool>(false);
-    when(mockCodeViewController.toggleShowCodeCoverage()).thenAnswer(
-      (_) => showCodeCoverage.value = !showCodeCoverage.value,
-    );
-    when(mockCodeViewController.toggleShowProfileInformation()).thenAnswer(
-      (_) => showProfileHits.value = !showProfileHits.value,
-    );
+    when(
+      mockCodeViewController.toggleShowCodeCoverage(),
+    ).thenAnswer((_) => showCodeCoverage.value = !showCodeCoverage.value);
+    when(
+      mockCodeViewController.toggleShowProfileInformation(),
+    ).thenAnswer((_) => showProfileHits.value = !showProfileHits.value);
     when(mockCodeViewController.showCodeCoverage).thenReturn(showCodeCoverage);
-    when(mockCodeViewController.showProfileInformation)
-        .thenReturn(showProfileHits);
+    when(
+      mockCodeViewController.showProfileInformation,
+    ).thenReturn(showProfileHits);
     refreshCodeCoverageInvoked = false;
-    // TODO(jacobr): is there a better way to clean this up?
-    // ignore: discarded_futures
-    when(mockCodeViewController.refreshCodeStatistics()).thenAnswer(
-      (_) async => refreshCodeCoverageInvoked = true,
-    );
+    when(
+      // TODO(jacobr): is there a better way to clean this up?
+      // ignore: discarded_futures
+      mockCodeViewController.refreshCodeStatistics(),
+    ).thenAnswer((_) async => refreshCodeCoverageInvoked = true);
     when(codeViewController.navigationInProgress).thenReturn(false);
   });
 
@@ -140,10 +145,7 @@
       // Coverage display starts disabled.
       gutterItemCoverageTester(tester, false);
       gutterItemProfileInfoTester(tester, false);
-      expect(
-        tester.widget<DevToolsButton>(findRefresh).onPressed,
-        isNull,
-      );
+      expect(tester.widget<DevToolsButton>(findRefresh).onPressed, isNull);
 
       // Toggle showing coverage and verify the gutter items contain coverage
       // information.
@@ -151,10 +153,7 @@
       await pumpDebuggerScreen(tester, debuggerController);
       gutterItemCoverageTester(tester, true);
       gutterItemProfileInfoTester(tester, false);
-      expect(
-        tester.widget<DevToolsButton>(findRefresh).onPressed,
-        isNotNull,
-      );
+      expect(tester.widget<DevToolsButton>(findRefresh).onPressed, isNotNull);
 
       // Toggle showing profiler information and verify the gutter items contain
       // profiling information.
@@ -162,10 +161,7 @@
       await pumpDebuggerScreen(tester, debuggerController);
       gutterItemCoverageTester(tester, true);
       gutterItemProfileInfoTester(tester, true);
-      expect(
-        tester.widget<DevToolsButton>(findRefresh).onPressed,
-        isNotNull,
-      );
+      expect(tester.widget<DevToolsButton>(findRefresh).onPressed, isNotNull);
 
       // Test the refresh coverage button.
       await tester.tap(findRefresh);
@@ -177,20 +173,14 @@
       await pumpDebuggerScreen(tester, debuggerController);
       gutterItemCoverageTester(tester, false);
       gutterItemProfileInfoTester(tester, true);
-      expect(
-        tester.widget<DevToolsButton>(findRefresh).onPressed,
-        isNotNull,
-      );
+      expect(tester.widget<DevToolsButton>(findRefresh).onPressed, isNotNull);
 
       // Toggle again and verify the profiling information is no longer present.
       await tester.tap(findProfileToggle);
       await pumpDebuggerScreen(tester, debuggerController);
       gutterItemCoverageTester(tester, false);
       gutterItemProfileInfoTester(tester, false);
-      expect(
-        tester.widget<DevToolsButton>(findRefresh).onPressed,
-        isNull,
-      );
+      expect(tester.widget<DevToolsButton>(findRefresh).onPressed, isNull);
     },
   );
 }
diff --git a/packages/devtools_app/test/debugger/debugger_codeview_test.dart b/packages/devtools_app/test/screens/debugger/debugger_codeview_test.dart
similarity index 64%
rename from packages/devtools_app/test/debugger/debugger_codeview_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_codeview_test.dart
index 3b54f17..814d526 100644
--- a/packages/devtools_app/test/debugger/debugger_codeview_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_codeview_test.dart
@@ -15,7 +15,7 @@
 import 'package:mockito/mockito.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../test_infra/matchers/matchers.dart';
+import '../../test_infra/matchers/matchers.dart';
 
 void main() {
   const mockScriptRefFileUri = 'the/path/mapped/to/script/ref/uri';
@@ -54,15 +54,18 @@
     );
     setGlobal(PreferencesController, PreferencesController());
     fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
 
     scriptsHistory.pushEntry(mockScript!);
     final mockCodeViewController = debuggerController.codeViewController;
-    when(mockCodeViewController.currentScriptRef)
-        .thenReturn(ValueNotifier(mockScriptRef));
-    when(mockCodeViewController.currentParsedScript)
-        .thenReturn(ValueNotifier(mockParsedScript));
+    when(
+      mockCodeViewController.currentScriptRef,
+    ).thenReturn(ValueNotifier(mockScriptRef));
+    when(
+      mockCodeViewController.currentParsedScript,
+    ).thenReturn(ValueNotifier(mockParsedScript));
     when(mockCodeViewController.scriptsHistory).thenReturn(scriptsHistory);
   });
 
@@ -107,7 +110,7 @@
       await expectLater(
         find.byType(CodeView),
         matchesDevToolsGolden(
-          '../test_infra/goldens/codeview_scrollbars.png',
+          '../../test_infra/goldens/codeview_scrollbars.png',
         ),
       );
     },
@@ -117,30 +120,27 @@
     'search in file field is visible',
     smallWindowSize,
     (WidgetTester tester) async {
-      when(codeViewController.showSearchInFileField)
-          .thenReturn(ValueNotifier(true));
+      when(
+        codeViewController.showSearchInFileField,
+      ).thenReturn(ValueNotifier(true));
       when(codeViewController.searchFieldFocusNode).thenReturn(FocusNode());
-      when(codeViewController.searchTextFieldController)
-          .thenReturn(SearchTextEditingController());
+      when(
+        codeViewController.searchTextFieldController,
+      ).thenReturn(SearchTextEditingController());
 
       await pumpDebuggerScreen(tester, debuggerController);
       expect(find.byType(SearchField<CodeViewController>), findsOneWidget);
     },
   );
 
-  testWidgetsWithWindowSize(
-    'file opener is visible',
-    smallWindowSize,
-    (WidgetTester tester) async {
-      when(codeViewController.showFileOpener).thenReturn(ValueNotifier(true));
-      when(scriptManager.sortedScripts).thenReturn(ValueNotifier([]));
-      await pumpDebuggerScreen(tester, debuggerController);
-      expect(
-        find.byKey(debuggerCodeViewFileOpenerKey),
-        findsOneWidget,
-      );
-    },
-  );
+  testWidgetsWithWindowSize('file opener is visible', smallWindowSize, (
+    WidgetTester tester,
+  ) async {
+    when(codeViewController.showFileOpener).thenReturn(ValueNotifier(true));
+    when(scriptManager.sortedScripts).thenReturn(ValueNotifier([]));
+    await pumpDebuggerScreen(tester, debuggerController);
+    expect(find.byKey(debuggerCodeViewFileOpenerKey), findsOneWidget);
+  });
 
   group('fetchScriptLocationFullFilePath', () {
     testWidgets('gets the full path', (WidgetTester tester) async {
@@ -153,57 +153,62 @@
         ),
       );
 
-      final filePath =
-          await fetchScriptLocationFullFilePath(codeViewController);
+      final filePath = await fetchScriptLocationFullFilePath(
+        codeViewController,
+      );
 
       expect(filePath, equals(mockScriptRefFileUri));
     });
 
-    testWidgets(
-      'gets the path if immediately available',
-      (WidgetTester tester) async {
-        when(codeViewController.scriptLocation).thenReturn(
-          ValueNotifier(
-            ScriptLocation(
-              mockScriptRef,
-              location: const SourcePosition(line: 50, column: 50),
-            ),
+    testWidgets('gets the path if immediately available', (
+      WidgetTester tester,
+    ) async {
+      when(codeViewController.scriptLocation).thenReturn(
+        ValueNotifier(
+          ScriptLocation(
+            mockScriptRef,
+            location: const SourcePosition(line: 50, column: 50),
           ),
-        );
-        // Prefetch File Uris
-        await serviceConnection.serviceManager.resolvedUriManager.fetchFileUris(
-          serviceConnection
-              .serviceManager.isolateManager.selectedIsolate.value!.id!,
-          [mockScriptRef.uri!],
-        );
+        ),
+      );
+      // Prefetch File Uris
+      await serviceConnection.serviceManager.resolvedUriManager.fetchFileUris(
+        serviceConnection
+            .serviceManager
+            .isolateManager
+            .selectedIsolate
+            .value!
+            .id!,
+        [mockScriptRef.uri!],
+      );
 
-        final filePath =
-            await fetchScriptLocationFullFilePath(codeViewController);
+      final filePath = await fetchScriptLocationFullFilePath(
+        codeViewController,
+      );
 
-        expect(filePath, equals(mockScriptRefFileUri));
-      },
-    );
+      expect(filePath, equals(mockScriptRefFileUri));
+    });
 
-    testWidgets(
-      'returns null if package not found',
-      (WidgetTester tester) async {
-        when(codeViewController.scriptLocation).thenReturn(
-          ValueNotifier(
-            ScriptLocation(
-              ScriptRef(
-                uri: 'some/unknown/file',
-                id: 'unknown-script-ref-for-test',
-              ),
-              location: const SourcePosition(line: 123, column: 456),
+    testWidgets('returns null if package not found', (
+      WidgetTester tester,
+    ) async {
+      when(codeViewController.scriptLocation).thenReturn(
+        ValueNotifier(
+          ScriptLocation(
+            ScriptRef(
+              uri: 'some/unknown/file',
+              id: 'unknown-script-ref-for-test',
             ),
+            location: const SourcePosition(line: 123, column: 456),
           ),
-        );
+        ),
+      );
 
-        final filePath =
-            await fetchScriptLocationFullFilePath(codeViewController);
+      final filePath = await fetchScriptLocationFullFilePath(
+        codeViewController,
+      );
 
-        expect(filePath, isNull);
-      },
-    );
+      expect(filePath, isNull);
+    });
   });
 }
diff --git a/packages/devtools_app/test/debugger/debugger_console_test.dart b/packages/devtools_app/test/screens/debugger/debugger_console_test.dart
similarity index 90%
rename from packages/devtools_app/test/debugger/debugger_console_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_console_test.dart
index fd45330..ed5501b 100644
--- a/packages/devtools_app/test/debugger/debugger_console_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_console_test.dart
@@ -14,7 +14,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/mockito.dart';
 
-import '../test_infra/utils/test_utils.dart';
+import '../../test_infra/utils/test_utils.dart';
 
 void main() {
   final fakeServiceConnection = FakeServiceConnectionManager();
@@ -22,10 +22,12 @@
 
   const windowSize = Size(4000.0, 4000.0);
 
-  when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-      .thenReturn(false);
-  when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow)
-      .thenReturn(false);
+  when(
+    fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow,
+  ).thenReturn(false);
+  when(
+    fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow,
+  ).thenReturn(false);
   setGlobal(ServiceConnectionManager, fakeServiceConnection);
   setGlobal(IdeTheme, IdeTheme());
   setGlobal(ScriptManager, MockScriptManager());
@@ -37,8 +39,9 @@
   );
   setGlobal(PreferencesController, PreferencesController());
   fakeServiceConnection.consoleService.ensureServiceInitialized();
-  when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-      .thenReturn(ValueNotifier<int>(0));
+  when(
+    fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+  ).thenReturn(ValueNotifier<int>(0));
 
   Future<void> pumpConsole(
     WidgetTester tester,
diff --git a/packages/devtools_app/test/debugger/debugger_controller_stdio_test.dart b/packages/devtools_app/test/screens/debugger/debugger_controller_stdio_test.dart
similarity index 100%
rename from packages/devtools_app/test/debugger/debugger_controller_stdio_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_controller_stdio_test.dart
diff --git a/packages/devtools_app/test/debugger/debugger_controller_test.dart b/packages/devtools_app/test/screens/debugger/debugger_controller_test.dart
similarity index 98%
rename from packages/devtools_app/test/debugger/debugger_controller_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_controller_test.dart
index 925d036..a0b6cdb 100644
--- a/packages/devtools_app/test/debugger/debugger_controller_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_controller_test.dart
@@ -239,10 +239,7 @@
         debuggerController.matchesForSearch('hello world').toString(),
         equals('[5:28-39, 6:9-20]'),
       );
-      expect(
-        debuggerController.matchesForSearch('').toString(),
-        equals('[]'),
-      );
+      expect(debuggerController.matchesForSearch('').toString(), equals('[]'));
     });
   });
 }
diff --git a/packages/devtools_app/test/screens/debugger/debugger_evaluation_test.dart b/packages/devtools_app/test/screens/debugger/debugger_evaluation_test.dart
new file mode 100644
index 0000000..0351e35
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_evaluation_test.dart
@@ -0,0 +1,316 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/shared/console/eval/auto_complete.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../../test_infra/flutter_test_driver.dart';
+import '../../test_infra/flutter_test_environment.dart';
+import '../../test_infra/flutter_test_storage.dart';
+
+void main() {
+  setGlobal(Storage, FlutterTestStorage());
+
+  final env = FlutterTestEnvironment(
+    const FlutterRunConfiguration(withDebugger: true),
+  );
+
+  late Disposable isAlive;
+  late DebuggerController debuggerController;
+  late EvalOnDartLibrary eval;
+
+  setUp(() async {
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(BreakpointManager, BreakpointManager());
+    setGlobal(EvalService, EvalService());
+    setGlobal(NotificationService, NotificationService());
+    isAlive = Disposable();
+    await env.setupEnvironment();
+    debuggerController = DebuggerController();
+
+    eval = EvalOnDartLibrary(
+      'package:flutter_app/src/autocomplete.dart',
+      serviceConnection.serviceManager.service!,
+      serviceManager: serviceConnection.serviceManager,
+      disableBreakpoints: false,
+    );
+  });
+
+  tearDown(() async {
+    await debuggerController.resume();
+    isAlive.dispose();
+    debuggerController.dispose();
+    await env.tearDownEnvironment();
+  });
+
+  tearDownAll(() async {
+    await env.tearDownEnvironment(force: true);
+  });
+
+  Future<void> runMethodAndWaitForPause(String method) async {
+    unawaited(eval.eval(method, isAlive: isAlive));
+
+    await whenMatches(debuggerController.selectedStackFrame, (f) => f != null);
+  }
+
+  group('EvalOnDartLibrary', () {
+    test(
+      'returns scoped variables when EditingParts is not a field',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'foo', leftSide: '', rightSide: ''),
+            evalService,
+          ),
+          equals(['foo', 'foobar']),
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'b', leftSide: '', rightSide: ''),
+            evalService,
+          ),
+          equals(['bar', 'baz']),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+
+    test(
+      'returns filtered members when EditingParts is a field ',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'f', leftSide: 'foo.', rightSide: ''),
+            evalService,
+          ),
+          equals(['field1', 'field2', 'func1', 'func2']),
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'fu', leftSide: 'foo.', rightSide: ''),
+            evalService,
+          ),
+          equals(['func1', 'func2']),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+
+    test(
+      'returns filtered members when EditingParts is a class name ',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(leftSide: 'FooClass.', activeWord: '', rightSide: ''),
+            evalService,
+          ),
+          equals([
+            'staticField1',
+            'staticField2',
+            'namedConstructor',
+            'factory1',
+            'staticMethod',
+          ]),
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(
+              activeWord: 'fa',
+              leftSide: 'FooClass.',
+              rightSide: '',
+            ),
+            evalService,
+          ),
+          equals(['factory1']),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+    test(
+      'returns privates only from library',
+      // TODO(https://github.com/flutter/devtools/issues/7099): unskip once
+      // this test flake is fixed.
+      skip: true,
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        await expectLater(
+          autoCompleteResultsFor(
+            EditingParts(activeWord: '_', leftSide: '', rightSide: ''),
+            evalService,
+          ),
+          completion(
+            unorderedEquals([
+              '_privateField2',
+              '_privateField1',
+              '_PrivateClass',
+            ]),
+          ),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+    test(
+      'returns exported members from import',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(
+              activeWord: 'exportedField',
+              leftSide: '',
+              rightSide: '',
+            ),
+            evalService,
+          ),
+          equals(['exportedField']),
+        );
+
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(
+              activeWord: 'ExportedClass',
+              leftSide: '',
+              rightSide: '',
+            ),
+            evalService,
+          ),
+          equals(['ExportedClass']),
+        );
+
+        // Privates are not exported
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(
+              activeWord: '_privateExportedField',
+              leftSide: '',
+              rightSide: '',
+            ),
+            evalService,
+          ),
+          equals([]),
+        );
+
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(
+              activeWord: '_PrivateExportedClass',
+              leftSide: '',
+              rightSide: '',
+            ),
+            evalService,
+          ),
+          equals([]),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+
+    test(
+      'returns prefixes of libraries imported',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'developer', leftSide: '', rightSide: ''),
+            evalService,
+          ),
+          equals(['developer']),
+        );
+
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(activeWord: 'math', leftSide: '', rightSide: ''),
+            evalService,
+          ),
+          equals(['math']),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+
+    test(
+      'returns no operators for int',
+      () async {
+        await runMethodAndWaitForPause(
+          'AnotherClass().pauseWithScopedVariablesMethod()',
+        );
+        expect(
+          await autoCompleteResultsFor(
+            EditingParts(leftSide: '7.', activeWord: '', rightSide: ''),
+            evalService,
+          ),
+          equals([
+            'hashCode',
+            'bitLength',
+            'toString',
+            'remainder',
+            'abs',
+            'sign',
+            'isEven',
+            'isOdd',
+            'isNaN',
+            'isNegative',
+            'isInfinite',
+            'isFinite',
+            'toUnsigned',
+            'toSigned',
+            'compareTo',
+            'round',
+            'floor',
+            'ceil',
+            'truncate',
+            'roundToDouble',
+            'floorToDouble',
+            'ceilToDouble',
+            'truncateToDouble',
+            'clamp',
+            'toInt',
+            'toDouble',
+            'toStringAsFixed',
+            'toStringAsExponential',
+            'toStringAsPrecision',
+            'toRadixString',
+            'modPow',
+            'modInverse',
+            'gcd',
+            'noSuchMethod',
+            'runtimeType',
+          ]),
+        );
+      },
+      timeout: const Timeout.factor(8),
+      tags: includeForCustomerTestsTag,
+    );
+  });
+}
diff --git a/packages/devtools_app/test/debugger/debugger_floating_test.dart b/packages/devtools_app/test/screens/debugger/debugger_floating_test.dart
similarity index 91%
rename from packages/devtools_app/test/debugger/debugger_floating_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_floating_test.dart
index ceb06ea..23b2c61 100644
--- a/packages/devtools_app/test/debugger/debugger_floating_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_floating_test.dart
@@ -17,10 +17,12 @@
   final debuggerController = createMockDebuggerControllerWithDefaults();
   final scriptManager = MockScriptManager();
 
-  when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-      .thenReturn(false);
-  when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow)
-      .thenReturn(false);
+  when(
+    fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow,
+  ).thenReturn(false);
+  when(
+    fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow,
+  ).thenReturn(false);
   setGlobal(ServiceConnectionManager, fakeServiceConnection);
   setGlobal(IdeTheme, IdeTheme());
   setGlobal(ScriptManager, scriptManager);
@@ -51,10 +53,7 @@
     final animatedOpacity =
         animatedOpacityFinder.evaluate().first.widget as AnimatedOpacity;
     expect(animatedOpacity.opacity, equals(1.0));
-    expect(
-      find.text('Main isolate is paused in the debugger'),
-      findsOneWidget,
-    );
+    expect(find.text('Main isolate is paused in the debugger'), findsOneWidget);
     expect(find.byTooltip('Resume'), findsOneWidget);
     expect(find.byTooltip('Step over'), findsOneWidget);
   });
diff --git a/packages/devtools_app/test/debugger/debugger_screen_breakpoints_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_breakpoints_test.dart
similarity index 73%
rename from packages/devtools_app/test/debugger/debugger_screen_breakpoints_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_screen_breakpoints_test.dart
index c849a02..849c2b0 100644
--- a/packages/devtools_app/test/debugger/debugger_screen_breakpoints_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_breakpoints_test.dart
@@ -36,8 +36,9 @@
   );
   setGlobal(PreferencesController, PreferencesController());
   fakeServiceConnection.consoleService.ensureServiceInitialized();
-  when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-      .thenReturn(ValueNotifier<int>(0));
+  when(
+    fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+  ).thenReturn(ValueNotifier<int>(0));
   final debuggerController = createMockDebuggerControllerWithDefaults();
 
   final breakpoints = [
@@ -60,10 +61,12 @@
     ),
   ];
   final codeViewController = debuggerController.codeViewController;
-  when(mockBreakpointManager.breakpoints)
-      .thenReturn(ValueNotifier(breakpoints));
-  when(mockBreakpointManager.breakpointsWithLocation)
-      .thenReturn(ValueNotifier(breakpointsWithLocation));
+  when(
+    mockBreakpointManager.breakpoints,
+  ).thenReturn(ValueNotifier(breakpoints));
+  when(
+    mockBreakpointManager.breakpointsWithLocation,
+  ).thenReturn(ValueNotifier(breakpointsWithLocation));
 
   when(scriptManager.sortedScripts).thenReturn(ValueNotifier([]));
   when(codeViewController.scriptLocation).thenReturn(ValueNotifier(null));
@@ -74,30 +77,25 @@
     DebuggerController controller,
   ) async {
     await tester.pumpWidget(
-      wrapWithControllers(
-        const DebuggerWindows(),
-        debugger: controller,
-      ),
+      wrapWithControllers(const DebuggerWindows(), debugger: controller),
     );
   }
 
-  testWidgetsWithWindowSize(
-    'Breakpoints show items',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpDebuggerScreen(tester, debuggerController);
+  testWidgetsWithWindowSize('Breakpoints show items', windowSize, (
+    WidgetTester tester,
+  ) async {
+    await pumpDebuggerScreen(tester, debuggerController);
 
-      expect(find.text('Breakpoints'), findsOneWidget);
+    expect(find.text('Breakpoints'), findsOneWidget);
 
-      // test for items in the breakpoint list
-      expect(
-        find.byWidgetPredicate(
-          (Widget widget) =>
-              widget is RichText &&
-              widget.text.toPlainText().contains('script.dart:10'),
-        ),
-        findsOneWidget,
-      );
-    },
-  );
+    // test for items in the breakpoint list
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains('script.dart:10'),
+      ),
+      findsOneWidget,
+    );
+  });
 }
diff --git a/packages/devtools_app/test/screens/debugger/debugger_screen_call_stack_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_call_stack_test.dart
new file mode 100644
index 0000000..d2b2e95
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_call_stack_test.dart
@@ -0,0 +1,224 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
+import 'package:devtools_app/src/shared/diagnostics/primitives/source_location.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+void main() {
+  late FakeServiceConnectionManager fakeServiceConnection;
+  late MockDebuggerController debuggerController;
+  late MockScriptManager scriptManager;
+
+  const windowSize = Size(4000.0, 4000.0);
+
+  setUp(() {
+    fakeServiceConnection = FakeServiceConnectionManager();
+    scriptManager = MockScriptManager();
+    mockConnectedApp(
+      fakeServiceConnection.serviceManager.connectedApp!,
+      isProfileBuild: false,
+      isFlutterApp: true,
+      isWebApp: false,
+    );
+    setGlobal(ServiceConnectionManager, fakeServiceConnection);
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BreakpointManager, BreakpointManager());
+    setGlobal(ScriptManager, scriptManager);
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+    fakeServiceConnection.consoleService.ensureServiceInitialized();
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
+    debuggerController = createMockDebuggerControllerWithDefaults();
+  });
+
+  Future<void> pumpDebuggerScreen(
+    WidgetTester tester,
+    DebuggerController controller,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(const DebuggerWindows(), debugger: controller),
+    );
+  }
+
+  testWidgetsWithWindowSize('Call Stack shows items', windowSize, (
+    WidgetTester tester,
+  ) async {
+    final stackFrames = [
+      Frame(
+        index: 0,
+        code: CodeRef(
+          name: 'testCodeRef',
+          id: 'testCodeRef',
+          kind: CodeKind.kDart,
+        ),
+        location: SourceLocation(
+          script: ScriptRef(uri: 'package:test/script.dart', id: 'script.dart'),
+          tokenPos: 10,
+        ),
+        kind: FrameKind.kRegular,
+      ),
+      Frame(
+        index: 1,
+        location: SourceLocation(
+          script: ScriptRef(
+            uri: 'package:test/script1.dart',
+            id: 'script1.dart',
+          ),
+          tokenPos: 10,
+        ),
+        kind: FrameKind.kRegular,
+      ),
+      Frame(
+        index: 2,
+        code: CodeRef(
+          name: '[Unoptimized] testCodeRef2',
+          id: 'testCodeRef2',
+          kind: CodeKind.kDart,
+        ),
+        location: SourceLocation(
+          script: ScriptRef(
+            uri: 'package:test/script2.dart',
+            id: 'script2.dart',
+          ),
+          tokenPos: 10,
+        ),
+        kind: FrameKind.kRegular,
+      ),
+      Frame(
+        index: 3,
+        code: CodeRef(
+          name: 'testCodeRef3.<anonymous closure>',
+          id: 'testCodeRef3.closure',
+          kind: CodeKind.kDart,
+        ),
+        location: SourceLocation(
+          script: ScriptRef(
+            uri: 'package:test/script3.dart',
+            id: 'script3.dart',
+          ),
+          tokenPos: 10,
+        ),
+        kind: FrameKind.kRegular,
+      ),
+      Frame(
+        index: 4,
+        location: SourceLocation(
+          script: ScriptRef(
+            uri: 'package:test/script4.dart',
+            id: 'script4.dart',
+          ),
+          tokenPos: 10,
+        ),
+        kind: FrameKind.kAsyncSuspensionMarker,
+      ),
+      Frame(
+        index: 5,
+        code: CodeRef(
+          name: '_createTimer',
+          id: 'testJsCodeRefId',
+          kind: CodeKind.kNative,
+        ),
+        location: SourceLocation(
+          script: ScriptRef(
+            uri: 'http://localhost:63691/dwds/src/injected/client.js',
+            id: 'jsScriptId',
+          ),
+          tokenPos: 12,
+        ),
+        kind: FrameKind.kRegular,
+      ),
+    ];
+
+    final stackFramesWithLocation =
+        stackFrames.map<StackFrameAndSourcePosition>((frame) {
+          return StackFrameAndSourcePosition(
+            frame,
+            position: SourcePosition(
+              line: stackFrames.indexOf(frame),
+              column: 10,
+            ),
+          );
+        }).toList();
+
+    when(
+      debuggerController.stackFramesWithLocation,
+    ).thenReturn(ValueNotifier(stackFramesWithLocation));
+    final codeViewController = debuggerController.codeViewController;
+    when(codeViewController.showFileOpener).thenReturn(ValueNotifier(false));
+    await pumpDebuggerScreen(tester, debuggerController);
+
+    expect(find.text('Call Stack'), findsOneWidget);
+
+    // Stack frame 0
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains('testCodeRef script.dart:0'),
+      ),
+      findsOneWidget,
+    );
+
+    // verify that the frame has a tooltip
+    expect(find.byTooltip('testCodeRef script.dart:0'), findsOneWidget);
+
+    // Stack frame 1
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains('<none> script1.dart:1'),
+      ),
+      findsOneWidget,
+    );
+    // Stack frame 2
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains('testCodeRef2 script2.dart:2'),
+      ),
+      findsOneWidget,
+    );
+    // Stack frame 3
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains(
+              'testCodeRef3.<closure> script3.dart:3',
+            ),
+      ),
+      findsOneWidget,
+    );
+    // Stack frame 4
+    expect(find.text('<async break>'), findsOneWidget);
+    // Stack frame 5
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is RichText &&
+            widget.text.toPlainText().contains(
+              '<native code: _createTimer> client.js',
+            ),
+      ),
+      findsOneWidget,
+    );
+  });
+}
diff --git a/packages/devtools_app/test/screens/debugger/debugger_screen_dap_variables_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_dap_variables_test.dart
new file mode 100644
index 0000000..389c77f
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_dap_variables_test.dart
@@ -0,0 +1,134 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:dap/dap.dart' as dap;
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/shared/diagnostics/dap_object_node.dart';
+import 'package:devtools_app/src/shared/feature_flags.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+  late FakeServiceConnectionManager fakeServiceConnection;
+  late MockDebuggerController debuggerController;
+  late MockScriptManager scriptManager;
+  late MockVmServiceWrapper vmService;
+
+  const windowSize = Size(2500, 1500);
+
+  setUp(() {
+    FeatureFlags.dapDebugging = true;
+    vmService = createMockVmServiceWrapperWithDefaults();
+    fakeServiceConnection = FakeServiceConnectionManager(service: vmService);
+    scriptManager = MockScriptManager();
+
+    mockConnectedApp(
+      fakeServiceConnection.serviceManager.connectedApp!,
+      isProfileBuild: false,
+      isFlutterApp: true,
+      isWebApp: false,
+    );
+    setGlobal(ServiceConnectionManager, fakeServiceConnection);
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(ScriptManager, scriptManager);
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BreakpointManager, BreakpointManager());
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+    fakeServiceConnection.consoleService.ensureServiceInitialized();
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
+    debuggerController = createMockDebuggerControllerWithDefaults();
+  });
+
+  tearDown(() {
+    fakeServiceConnection.appState.setDapVariables([]);
+  });
+
+  Future<void> pumpDebuggerScreen(
+    WidgetTester tester,
+    DebuggerController controller,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(const DebuggerWindows(), debugger: controller),
+    );
+  }
+
+  testWidgetsWithWindowSize('Shows non-expandable variables', windowSize, (
+    WidgetTester tester,
+  ) async {
+    final node = DapObjectNode(
+      service: vmService,
+      variable: dap.Variable(name: 'myInt', value: '10', variablesReference: 0),
+    );
+
+    fakeServiceConnection.appState.setDapVariables([node]);
+    await pumpDebuggerScreen(tester, debuggerController);
+    expect(find.text('Variables'), findsOneWidget);
+
+    // Variables should include the int.
+    final intFinder = find.text('myInt: 10');
+    expect(intFinder, findsOneWidget);
+
+    // The int is not expandable.
+    final expandArrowFinder = find.byIcon(Icons.keyboard_arrow_down);
+    expect(expandArrowFinder, findsNothing);
+  });
+
+  testWidgetsWithWindowSize('Shows expandable variables', windowSize, (
+    WidgetTester tester,
+  ) async {
+    when(vmService.dapVariablesRequest(any)).thenAnswer((_) async {
+      return dap.VariablesResponseBody(
+        variables: [
+          dap.Variable(
+            name: 'myString',
+            value: '"myString"',
+            variablesReference: 0,
+          ),
+        ],
+      );
+    });
+
+    final node = DapObjectNode(
+      service: vmService,
+      variable: dap.Variable(
+        name: 'myList',
+        value: 'List (1 item)',
+        variablesReference: 1,
+      ),
+    );
+    await node.fetchChildren();
+
+    fakeServiceConnection.appState.setDapVariables([node]);
+    await pumpDebuggerScreen(tester, debuggerController);
+    expect(find.text('Variables'), findsOneWidget);
+
+    // Variables should include the list.
+    final listFinder = find.text('myList: List (1 item)');
+    expect(listFinder, findsOneWidget);
+
+    // Initially the string is not visible.
+    final stringFinder = find.text('myString: "myString"');
+    expect(stringFinder, findsNothing);
+
+    // Expand the list.
+    final expandArrowFinder = find.byIcon(Icons.keyboard_arrow_down);
+    expect(expandArrowFinder, findsOneWidget);
+    await tester.tap(expandArrowFinder.first);
+    await tester.pump();
+
+    // String is now visible.
+    expect(stringFinder, findsOneWidget);
+  });
+}
diff --git a/packages/devtools_app/test/screens/debugger/debugger_screen_explorer_visibility_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_explorer_visibility_test.dart
new file mode 100644
index 0000000..83a7283
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_explorer_visibility_test.dart
@@ -0,0 +1,116 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+void main() {
+  const windowSize = Size(4000.0, 4000.0);
+
+  late FakeServiceConnectionManager fakeServiceConnection;
+  late MockScriptManager scriptManager;
+  late MockDebuggerController debuggerController;
+  late CodeViewController mockCodeViewController;
+  final scripts = [
+    ScriptRef(uri: 'package:test/script.dart', id: 'test-script'),
+  ];
+
+  group('FileExplorer', () {
+    setUp(() {
+      fakeServiceConnection = FakeServiceConnectionManager();
+      scriptManager = MockScriptManager();
+      mockConnectedApp(
+        fakeServiceConnection.serviceManager.connectedApp!,
+        isFlutterApp: true,
+        isProfileBuild: false,
+        isWebApp: false,
+      );
+      setGlobal(ServiceConnectionManager, fakeServiceConnection);
+      setGlobal(IdeTheme, IdeTheme());
+      setGlobal(NotificationService, NotificationService());
+      setGlobal(ScriptManager, scriptManager);
+      setGlobal(BreakpointManager, BreakpointManager());
+      setGlobal(
+        DevToolsEnvironmentParameters,
+        ExternalDevToolsEnvironmentParameters(),
+      );
+      setGlobal(PreferencesController, PreferencesController());
+      fakeServiceConnection.consoleService.ensureServiceInitialized();
+      when(
+        fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+      ).thenReturn(ValueNotifier<int>(0));
+      final mockProgramExplorerController =
+          createMockProgramExplorerControllerWithDefaults();
+      mockCodeViewController = createMockCodeViewControllerWithDefaults(
+        programExplorerController: mockProgramExplorerController,
+      );
+      debuggerController = createMockDebuggerControllerWithDefaults(
+        codeViewController: mockCodeViewController,
+      );
+
+      when(scriptManager.sortedScripts).thenReturn(ValueNotifier(scripts));
+
+      when(mockProgramExplorerController.rootObjectNodes).thenReturn(
+        ValueNotifier([
+          VMServiceObjectNode(
+            mockCodeViewController.programExplorerController,
+            'package:test',
+            null,
+          ),
+        ]),
+      );
+      when(
+        mockCodeViewController.showFileOpener,
+      ).thenReturn(ValueNotifier(false));
+    });
+
+    Future<void> pumpDebuggerScreen(
+      WidgetTester tester,
+      DebuggerController controller,
+    ) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          DebuggerSourceAndControls(
+            shownFirstScript: () => true,
+            setShownFirstScript: (_) {},
+          ),
+          debugger: controller,
+        ),
+      );
+    }
+
+    testWidgetsWithWindowSize('visible', windowSize, (
+      WidgetTester tester,
+    ) async {
+      // File Explorer view is shown
+      when(
+        mockCodeViewController.fileExplorerVisible,
+      ).thenReturn(ValueNotifier(true));
+      await pumpDebuggerScreen(tester, debuggerController);
+      // One for the button and one for the title of the File Explorer view.
+      expect(find.text('File Explorer'), findsNWidgets(2));
+
+      // test for items in the libraries tree
+      expect(find.text(scripts.first.uri!.split('/').first), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('hidden', windowSize, (
+      WidgetTester tester,
+    ) async {
+      // File Explorer view is hidden
+      when(
+        mockCodeViewController.fileExplorerVisible,
+      ).thenReturn(ValueNotifier(false));
+      await pumpDebuggerScreen(tester, debuggerController);
+      expect(find.text('File Explorer'), findsOneWidget);
+    });
+  });
+}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_paused_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_paused_test.dart
similarity index 69%
rename from packages/devtools_app/test/debugger/debugger_screen_paused_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_screen_paused_test.dart
index 55f65f6..18a327f 100644
--- a/packages/devtools_app/test/debugger/debugger_screen_paused_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_paused_test.dart
@@ -16,8 +16,8 @@
 import 'package:mockito/mockito.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../test_infra/utils/debugger_utils.dart';
-import '../test_infra/utils/test_utils.dart';
+import '../../test_infra/utils/debugger_utils.dart';
+import '../../test_infra/utils/test_utils.dart';
 
 void main() {
   const windowSize = Size(2500.0, 1200.0);
@@ -47,23 +47,23 @@
     );
     setGlobal(PreferencesController, PreferencesController());
     fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
     debuggerController = createMockDebuggerControllerWithDefaults();
     final codeViewController = debuggerController.codeViewController;
     final scriptsHistory = ScriptsHistory();
     scriptsHistory.pushEntry(mockScript!);
     when(codeViewController.scriptsHistory).thenReturn(scriptsHistory);
-    when(debuggerController.stackFramesWithLocation).thenReturn(
-      ValueNotifier([
-        _stackFrame1,
-        _stackFrame2,
-      ]),
-    );
-    when(codeViewController.currentScriptRef)
-        .thenReturn(ValueNotifier(mockScriptRef));
-    when(codeViewController.currentParsedScript)
-        .thenReturn(ValueNotifier(mockParsedScript));
+    when(
+      debuggerController.stackFramesWithLocation,
+    ).thenReturn(ValueNotifier([_stackFrame1, _stackFrame2]));
+    when(
+      codeViewController.currentScriptRef,
+    ).thenReturn(ValueNotifier(mockScriptRef));
+    when(
+      codeViewController.currentParsedScript,
+    ).thenReturn(ValueNotifier(mockParsedScript));
     when(codeViewController.navigationInProgress).thenReturn(false);
   });
 
@@ -79,55 +79,50 @@
     );
   }
 
-  testWidgetsWithWindowSize(
-    'debugger controls paused',
-    windowSize,
-    (WidgetTester tester) async {
-      await pumpDebuggerScreen(tester);
+  testWidgetsWithWindowSize('debugger controls paused', windowSize, (
+    WidgetTester tester,
+  ) async {
+    await pumpDebuggerScreen(tester);
 
-      (serviceConnection.serviceManager.isolateManager as FakeIsolateManager)
-          .setMainIsolatePausedState(true);
-      await tester.pump();
+    (serviceConnection.serviceManager.isolateManager as FakeIsolateManager)
+        .setMainIsolatePausedState(true);
+    await tester.pump();
 
-      expect(
-        findDebuggerButtonWithIcon(Codicons.debugPause),
-        findsOneWidget,
-      );
-      final pause = getWidgetFromFinder<OutlinedButton>(
-        findDebuggerButtonWithIcon(Codicons.debugPause),
-      );
-      expect(pause.onPressed, isNull);
+    expect(findDebuggerButtonWithIcon(Codicons.debugPause), findsOneWidget);
+    final pause = getWidgetFromFinder<OutlinedButton>(
+      findDebuggerButtonWithIcon(Codicons.debugPause),
+    );
+    expect(pause.onPressed, isNull);
 
-      expect(
-        findDebuggerButtonWithIcon(Codicons.debugContinue),
-        findsOneWidget,
-      );
-      final resume = getWidgetFromFinder<OutlinedButton>(
-        findDebuggerButtonWithIcon(Codicons.debugContinue),
-      );
-      expect(resume.onPressed, isNotNull);
-    },
-  );
+    expect(findDebuggerButtonWithIcon(Codicons.debugContinue), findsOneWidget);
+    final resume = getWidgetFromFinder<OutlinedButton>(
+      findDebuggerButtonWithIcon(Codicons.debugContinue),
+    );
+    expect(resume.onPressed, isNotNull);
+  });
 
   testWidgetsWithWindowSize(
     'selecting stackframe scrolls the frame location into view',
     windowSize,
     (WidgetTester tester) async {
       final stackFrameNotifier = ValueNotifier(_stackFrame1);
-      when(debuggerController.selectedStackFrame)
-          .thenReturn(stackFrameNotifier);
+      when(
+        debuggerController.selectedStackFrame,
+      ).thenReturn(stackFrameNotifier);
 
       await pumpDebuggerScreen(tester);
       await tester.pumpAndSettle();
 
       // The first stack frame is visible:
-      final firstStackFrame =
-          findStackFrameWithText('firstCodeRef main.dart:1');
+      final firstStackFrame = findStackFrameWithText(
+        'firstCodeRef main.dart:1',
+      );
       expect(firstStackFrame, findsOneWidget);
 
       // The second stack frame is visible:
-      final secondStackFrame =
-          findStackFrameWithText('secondCodeRef main.dart:85');
+      final secondStackFrame = findStackFrameWithText(
+        'secondCodeRef main.dart:85',
+      );
       expect(secondStackFrame, findsOneWidget);
 
       // The first stack frame's line is visible:
@@ -159,9 +154,7 @@
       id: 'firstCodeRef',
       kind: CodeKind.kDart,
     ),
-    location: SourceLocation(
-      script: mockScriptRef,
-    ),
+    location: SourceLocation(script: mockScriptRef),
     kind: FrameKind.kRegular,
   ),
   position: const SourcePosition(line: _stackFrame1Line, column: 1),
@@ -177,25 +170,25 @@
       id: 'secondCodeRef',
       kind: CodeKind.kDart,
     ),
-    location: SourceLocation(
-      script: mockScriptRef,
-    ),
+    location: SourceLocation(script: mockScriptRef),
     kind: FrameKind.kRegular,
   ),
   position: const SourcePosition(line: _stackFrame2Line, column: 1),
 );
 
 Finder findStackFrameWithText(String text) => find.byWidgetPredicate(
-      (Widget widget) =>
-          widget is RichText && widget.text.toPlainText().contains(text),
-    );
+  (Widget widget) =>
+      widget is RichText && widget.text.toPlainText().contains(text),
+);
 
 bool gutterItemForLineIsVisible(int lineNumber) {
   final gutterItems = find.byType(GutterItem);
   final firstGutterItem = getWidgetFromFinder<GutterItem>(gutterItems.first);
   final lastGutterItem = getWidgetFromFinder<GutterItem>(gutterItems.last);
-  final lineRange =
-      Range(firstGutterItem.lineNumber, lastGutterItem.lineNumber);
+  final lineRange = Range(
+    firstGutterItem.lineNumber,
+    lastGutterItem.lineNumber,
+  );
 
   return lineRange.contains(lineNumber);
 }
diff --git a/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart
new file mode 100644
index 0000000..7664671
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_test.dart
@@ -0,0 +1,237 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:codicon/codicon.dart';
+import 'package:collection/collection.dart';
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
+import 'package:devtools_app/src/shared/console/widgets/console_pane.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../../test_infra/test_data/debugger/vm_service_object_tree.dart';
+import '../../test_infra/utils/debugger_utils.dart';
+import '../../test_infra/utils/test_utils.dart';
+import '../../test_infra/utils/tree_utils.dart';
+
+void main() {
+  final screen = DebuggerScreen();
+
+  const windowSize = Size(4000.0, 4000.0);
+  const smallWindowSize = Size(1200.0, 1100.0);
+
+  late MockDebuggerController debuggerController;
+  late TestProgramExplorerController programExplorerController;
+  late VMServiceObjectNode libraryNode;
+
+  setUp(() async {
+    final fakeServiceConnection = FakeServiceConnectionManager();
+    final scriptManager = MockScriptManager();
+    when(
+      scriptManager.getScript(any),
+    ).thenAnswer((_) => Future<Script>.value(testScript));
+    mockConnectedApp(
+      fakeServiceConnection.serviceManager.connectedApp!,
+      isFlutterApp: true,
+      isProfileBuild: false,
+      isWebApp: false,
+    );
+    setGlobal(ServiceConnectionManager, fakeServiceConnection);
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(ScriptManager, scriptManager);
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(BreakpointManager, BreakpointManager());
+    setGlobal(EvalService, MockEvalService());
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+    setGlobal(BannerMessagesController, BannerMessagesController());
+    fakeServiceConnection.consoleService.ensureServiceInitialized();
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
+
+    programExplorerController = TestProgramExplorerController(
+      initializer: (controller) {
+        libraryNode = VMServiceObjectNode(controller, 'fooLib', testLib);
+        libraryNode.script = testScript;
+        libraryNode.location = ScriptLocation(testScript);
+        controller.rootObjectNodesInternal.add(libraryNode);
+      },
+    );
+    await programExplorerController.initialize();
+    await programExplorerController.selectNode(libraryNode);
+
+    final codeViewController = createMockCodeViewControllerWithDefaults(
+      programExplorerController: programExplorerController,
+    );
+    when(codeViewController.showFileOpener).thenReturn(ValueNotifier(false));
+    when(
+      codeViewController.fileExplorerVisible,
+    ).thenReturn(ValueNotifier(true));
+    debuggerController = createMockDebuggerControllerWithDefaults(
+      codeViewController: codeViewController,
+    );
+  });
+
+  Future<void> pumpConsole(
+    WidgetTester tester,
+    DebuggerController controller,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(
+        Row(
+          children: [
+            Flexible(child: ConsolePaneHeader()),
+            const Expanded(child: ConsolePane()),
+          ],
+        ),
+        debugger: controller,
+      ),
+    );
+  }
+
+  testWidgets('builds its tab', (WidgetTester tester) async {
+    await tester.pumpWidget(wrap(Builder(builder: screen.buildTab)));
+    expect(find.text('Debugger'), findsOneWidget);
+  });
+
+  testWidgetsWithWindowSize('has Console / stdio area', windowSize, (
+    WidgetTester tester,
+  ) async {
+    serviceConnection.consoleService.appendStdio('test stdio');
+
+    await pumpConsole(tester, debuggerController);
+
+    expect(find.text('Console'), findsOneWidget);
+
+    // test for stdio output.
+    expect(find.text('test stdio'), findsOneWidget);
+  });
+
+  testWidgetsWithWindowSize('debugger controls running', windowSize, (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(
+        Builder(builder: screen.build),
+        debugger: debuggerController,
+      ),
+    );
+
+    expect(findDebuggerButtonWithIcon(Codicons.debugPause), findsOneWidget);
+    final pause = getWidgetFromFinder<OutlinedButton>(
+      findDebuggerButtonWithIcon(Codicons.debugPause),
+    );
+    expect(pause.onPressed, isNotNull);
+
+    expect(findDebuggerButtonWithIcon(Codicons.debugContinue), findsOneWidget);
+    final resume = getWidgetFromFinder<OutlinedButton>(
+      findDebuggerButtonWithIcon(Codicons.debugContinue),
+    );
+    expect(resume.onPressed, isNull);
+  });
+
+  testWidgetsWithWindowSize(
+    'debugger controls break on exceptions',
+    windowSize,
+    (WidgetTester tester) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          Builder(builder: screen.build),
+          debugger: debuggerController,
+        ),
+      );
+      expect(
+        find.text("Don't stop on exceptions", skipOffstage: false),
+        findsOneWidget,
+      );
+    },
+  );
+
+  testWidgetsWithWindowSize(
+    'debugger controls break on exceptions abbreviated on small window',
+    smallWindowSize,
+    (WidgetTester tester) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          Builder(builder: screen.build),
+          debugger: debuggerController,
+        ),
+      );
+      expect(
+        find.text('Ignore exceptions', skipOffstage: false),
+        findsOneWidget,
+      );
+    },
+  );
+
+  testWidgetsWithWindowSize('node selection state', windowSize, (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(
+        Builder(builder: screen.build),
+        debugger: debuggerController,
+      ),
+    );
+    final delegate =
+        tester.firstWidget<MaterialApp>(find.byType(MaterialApp)).routerDelegate
+            as DevToolsRouterDelegate;
+    final libNode = programExplorerController.rootObjectNodes.value.first;
+    final libScript = libraryNode.script!;
+    final libScriptRef = ScriptRef(id: libScript.id!, uri: libScript.uri);
+
+    // Select the library node and ensure the outline is populated.
+    final libNodeFinder = find.text(libNode.name);
+    expect(libNodeFinder, findsOneWidget);
+    await tester.tap(libNodeFinder);
+    await tester.pump();
+
+    expect(programExplorerController.scriptSelection, libNode);
+
+    CodeViewSourceLocationNavigationState? state =
+        CodeViewSourceLocationNavigationState.fromState(
+          delegate.currentConfiguration!.state,
+        );
+
+    expect(state, isNotNull);
+    expect(state!.script, libScriptRef);
+    expect(state.line, 0);
+
+    // There should be three children total, one root with two children.
+    expect(programExplorerController.outlineNodes.value.length, 1);
+    expect(programExplorerController.outlineNodes.value.numNodes, 3);
+
+    // Select one of them and check that the outline selection has been
+    // updated.
+    final outlineNode = programExplorerController.outlineNodes.value.first;
+    final outlineNodeFinder = find.text(outlineNode.name);
+    expect(outlineNodeFinder, findsOneWidget);
+    await tester.tap(outlineNodeFinder);
+    await tester.pump();
+
+    expect(programExplorerController.scriptSelection, libNode);
+    expect(
+      programExplorerController.outlineNodes.value.singleWhereOrNull(
+        (e) => e.isSelected,
+      ),
+      outlineNode,
+    );
+    state = CodeViewSourceLocationNavigationState.fromState(
+      delegate.currentConfiguration!.state,
+    );
+    expect(state, isNotNull);
+    expect(state!.script, libScriptRef);
+    expect(state.line, testClassRef.location!.line);
+  });
+}
diff --git a/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart b/packages/devtools_app/test/screens/debugger/debugger_screen_variables_test.dart
similarity index 66%
rename from packages/devtools_app/test/debugger/debugger_screen_variables_test.dart
rename to packages/devtools_app/test/screens/debugger/debugger_screen_variables_test.dart
index f1b178b..74838ed 100644
--- a/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart
+++ b/packages/devtools_app/test/screens/debugger/debugger_screen_variables_test.dart
@@ -12,7 +12,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/mockito.dart';
 
-import '../test_infra/utils/variable_utils.dart';
+import '../../test_infra/utils/variable_utils.dart';
 
 void main() {
   late FakeServiceConnectionManager fakeServiceConnection;
@@ -42,8 +42,9 @@
     );
     setGlobal(PreferencesController, PreferencesController());
     fakeServiceConnection.consoleService.ensureServiceInitialized();
-    when(fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'))
-        .thenReturn(ValueNotifier<int>(0));
+    when(
+      fakeServiceConnection.errorBadgeManager.errorCountNotifier('debugger'),
+    ).thenReturn(ValueNotifier<int>(0));
     debuggerController = createMockDebuggerControllerWithDefaults();
 
     resetRef();
@@ -55,10 +56,7 @@
     DebuggerController controller,
   ) async {
     await tester.pumpWidget(
-      wrapWithControllers(
-        const DebuggerWindows(),
-        debugger: controller,
-      ),
+      wrapWithControllers(const DebuggerWindows(), debugger: controller),
     );
   }
 
@@ -103,78 +101,66 @@
     expect(group200To299Finder, findsOneWidget);
   }
 
-  testWidgetsWithWindowSize(
-    'Variables shows items',
-    windowSize,
-    (WidgetTester tester) async {
-      fakeServiceConnection.appState.setVariables(
-        [
-          buildListVariable(),
-          buildMapVariable(),
-          buildStringVariable('test str'),
-          buildBooleanVariable(true),
-          buildSetVariable(),
-        ],
-      );
-      await pumpDebuggerScreen(tester, debuggerController);
-      expect(find.text('Variables'), findsOneWidget);
+  testWidgetsWithWindowSize('Variables shows items', windowSize, (
+    WidgetTester tester,
+  ) async {
+    fakeServiceConnection.appState.setVariables([
+      buildListVariable(),
+      buildMapVariable(),
+      buildStringVariable('test str'),
+      buildBooleanVariable(true),
+      buildSetVariable(),
+    ]);
+    await pumpDebuggerScreen(tester, debuggerController);
+    expect(find.text('Variables'), findsOneWidget);
 
-      final listFinder = find.text('Root 1: List (2 items)');
+    final listFinder = find.text('Root 1: List (2 items)');
 
-      expect(listFinder, findsOneWidget);
+    expect(listFinder, findsOneWidget);
 
-      final mapFinder = find.textContaining(
-        'Root 2: Map (2 items)',
-      );
-      final mapElement1Finder = find.textContaining("['key1']: 1.0");
-      final mapElement2Finder = find.textContaining("['key2']: 2.0");
+    final mapFinder = find.textContaining('Root 2: Map (2 items)');
+    final mapElement1Finder = find.textContaining("['key1']: 1.0");
+    final mapElement2Finder = find.textContaining("['key2']: 2.0");
 
-      expect(listFinder, findsOneWidget);
-      expect(mapFinder, findsOneWidget);
-      expect(
-        find.textContaining("Root 3: 'test str...'"),
-        findsOneWidget,
-      );
-      expect(
-        find.textContaining('Root 4: true'),
-        findsOneWidget,
-      );
+    expect(listFinder, findsOneWidget);
+    expect(mapFinder, findsOneWidget);
+    expect(find.textContaining("Root 3: 'test str...'"), findsOneWidget);
+    expect(find.textContaining('Root 4: true'), findsOneWidget);
 
-      // Initially list is not expanded.
-      expect(find.textContaining('0: 3'), findsNothing);
-      expect(find.textContaining('1: 4'), findsNothing);
+    // Initially list is not expanded.
+    expect(find.textContaining('0: 3'), findsNothing);
+    expect(find.textContaining('1: 4'), findsNothing);
 
-      // Expand list.
-      await tester.tap(listFinder);
-      await tester.pump();
-      expect(find.textContaining('0: 0'), findsOneWidget);
-      expect(find.textContaining('1: 1'), findsOneWidget);
+    // Expand list.
+    await tester.tap(listFinder);
+    await tester.pump();
+    expect(find.textContaining('0: 0'), findsOneWidget);
+    expect(find.textContaining('1: 1'), findsOneWidget);
 
-      // Initially map is not expanded.
-      expect(mapElement1Finder, findsNothing);
-      expect(mapElement2Finder, findsNothing);
+    // Initially map is not expanded.
+    expect(mapElement1Finder, findsNothing);
+    expect(mapElement2Finder, findsNothing);
 
-      // Expand map.
-      await tester.tap(mapFinder);
-      await tester.pump();
-      expect(mapElement1Finder, findsOneWidget);
-      expect(mapElement2Finder, findsOneWidget);
+    // Expand map.
+    await tester.tap(mapFinder);
+    await tester.pump();
+    expect(mapElement1Finder, findsOneWidget);
+    expect(mapElement2Finder, findsOneWidget);
 
-      // Expect a tooltip for the set instance.
-      final setFinder = find.text('Root 5: Set (2 items)');
-      expect(setFinder, findsOneWidget);
+    // Expect a tooltip for the set instance.
+    final setFinder = find.text('Root 5: Set (2 items)');
+    expect(setFinder, findsOneWidget);
 
-      // Initially set is not expanded.
-      expect(find.textContaining('set value 0'), findsNothing);
-      expect(find.textContaining('set value 1'), findsNothing);
+    // Initially set is not expanded.
+    expect(find.textContaining('set value 0'), findsNothing);
+    expect(find.textContaining('set value 1'), findsNothing);
 
-      // Expand set
-      await tester.tap(setFinder);
-      await tester.pump();
-      expect(find.textContaining('set value 0'), findsOneWidget);
-      expect(find.textContaining('set value 1'), findsOneWidget);
-    },
-  );
+    // Expand set
+    await tester.tap(setFinder);
+    await tester.pump();
+    expect(find.textContaining('set value 0'), findsOneWidget);
+    expect(find.textContaining('set value 1'), findsOneWidget);
+  });
 
   testWidgetsWithWindowSize(
     'Children in large list variables are grouped',
diff --git a/packages/devtools_app/test/screens/debugger/debugger_scripts_test.dart b/packages/devtools_app/test/screens/debugger/debugger_scripts_test.dart
new file mode 100644
index 0000000..fa968d2
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/debugger_scripts_test.dart
@@ -0,0 +1,262 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/debugger/codeview.dart';
+import 'package:devtools_app/src/screens/debugger/debugger_model.dart';
+import 'package:devtools_app/src/shared/diagnostics/primitives/source_location.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../../test_infra/utils/test_utils.dart';
+
+void main() {
+  late CodeViewController codeViewController;
+  late MockDebuggerController mockDebuggerController;
+
+  const smallWindowSize = Size(1200.0, 1000.0);
+
+  void initializeGlobalsAndMockApp() {
+    final fakeServiceConnection = FakeServiceConnectionManager();
+    setGlobal(ServiceConnectionManager, fakeServiceConnection);
+    setGlobal(BreakpointManager, BreakpointManager());
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(ScriptManager, MockScriptManager());
+    setGlobal(NotificationService, NotificationService());
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+
+    mockConnectedApp(
+      fakeServiceConnection.serviceManager.connectedApp!,
+      isProfileBuild: false,
+      isFlutterApp: true,
+      isWebApp: false,
+    );
+  }
+
+  Future<void> pumpDebuggerScreen(
+    WidgetTester tester,
+    DebuggerController controller,
+  ) async {
+    await tester.pumpWidget(
+      wrapWithControllers(
+        DebuggerSourceAndControls(
+          shownFirstScript: () => true,
+          setShownFirstScript: (_) {},
+        ),
+        debugger: controller,
+      ),
+    );
+  }
+
+  Future<void> showScript(ScriptRef scriptRef) async {
+    await codeViewController.showScriptLocation(
+      ScriptLocation(
+        scriptRef,
+        location: const SourcePosition(line: 1, column: 1),
+      ),
+    );
+  }
+
+  setUpAll(() async {
+    initializeGlobalsAndMockApp();
+    await SyntaxHighlighter.initialize();
+    codeViewController = CodeViewController();
+    mockDebuggerController = createMockDebuggerControllerWithDefaults(
+      codeViewController: codeViewController,
+    );
+  });
+
+  group('for a script with < 100000 lines', () {
+    setUpAll(() {
+      when(scriptManager.getScriptCached(mockScriptRef)).thenReturn(mockScript);
+    });
+
+    testWidgetsWithWindowSize(
+      'lines of the script are visible',
+      smallWindowSize,
+      (WidgetTester tester) async {
+        await pumpDebuggerScreen(tester, mockDebuggerController);
+        await showScript(mockScriptRef);
+        await tester.pumpAndSettle();
+
+        expectFirstNLinesContain([
+          '// Copyright 2019 The Flutter team. All rights reserved',
+          '// Use of this source code is governed by a BSD-style license that can be',
+          '// found in the LICENSE file.',
+        ]);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'lines of the script are highlighted',
+      smallWindowSize,
+      (WidgetTester tester) async {
+        await pumpDebuggerScreen(tester, mockDebuggerController);
+        await showScript(mockScriptRef);
+        await tester.pumpAndSettle();
+
+        expect(firstNLinesAreHighlighted(10), isTrue);
+      },
+    );
+
+    testWidgetsWithWindowSize('script name is visible', smallWindowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      await showScript(mockScriptRef);
+      await tester.pumpAndSettle();
+
+      expect(find.text('package:gallery/main.dart'), findsOneWidget);
+    });
+  });
+
+  group('for a script with > 100000 lines', () {
+    setUpAll(() {
+      when(
+        scriptManager.getScriptCached(mockLargeScriptRef),
+      ).thenReturn(mockLargeScript);
+    });
+
+    testWidgetsWithWindowSize('script name is visible', smallWindowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      await showScript(mockLargeScriptRef);
+      await tester.pumpAndSettle();
+
+      expect(
+        find.text('package:front_end/src/fasta/kernel/body_builder.dart'),
+        findsOneWidget,
+      );
+    });
+
+    testWidgetsWithWindowSize('lines of the script are visible', smallWindowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      await showScript(mockLargeScriptRef);
+      await tester.pumpAndSettle();
+
+      expectFirstNLinesContain([
+        '// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file',
+        '// for details. All rights reserved. Use of this source code is governed by a',
+        '// BSD-style license that can be found in the LICENSE file.',
+      ]);
+    });
+
+    testWidgetsWithWindowSize(
+      'lines of the script are not highlighted',
+      smallWindowSize,
+      (WidgetTester tester) async {
+        await pumpDebuggerScreen(tester, mockDebuggerController);
+        await showScript(mockLargeScriptRef);
+        await tester.pumpAndSettle();
+
+        expect(firstNLinesAreHighlighted(10), isFalse);
+      },
+    );
+  });
+
+  group('for a script with no source', () {
+    setUpAll(() {
+      when(scriptManager.getScriptCached(mockScriptRef)).thenReturn(mockScript);
+      when(
+        scriptManager.getScriptCached(mockEmptyScriptRef),
+      ).thenReturn(mockEmptyScript);
+    });
+
+    testWidgetsWithWindowSize('script name does not update', smallWindowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      await showScript(mockScriptRef);
+      await tester.pumpAndSettle();
+
+      expect(find.text('package:gallery/main.dart'), findsOneWidget);
+
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      await showScript(mockEmptyScriptRef);
+      await tester.pumpAndSettle();
+
+      expect(find.text('package:gallery/main.dart'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize(
+      'lines of the script do not update',
+      smallWindowSize,
+      (WidgetTester tester) async {
+        await pumpDebuggerScreen(tester, mockDebuggerController);
+        await showScript(mockScriptRef);
+        await tester.pumpAndSettle();
+
+        expectFirstNLinesContain([
+          '// Copyright 2019 The Flutter team. All rights reserved',
+          '// Use of this source code is governed by a BSD-style license that can be',
+          '// found in the LICENSE file.',
+        ]);
+
+        await pumpDebuggerScreen(tester, mockDebuggerController);
+        await showScript(mockEmptyScriptRef);
+        await tester.pumpAndSettle();
+
+        expectFirstNLinesContain([
+          '// Copyright 2019 The Flutter team. All rights reserved',
+          '// Use of this source code is governed by a BSD-style license that can be',
+          '// found in the LICENSE file.',
+        ]);
+      },
+    );
+
+    testWidgetsWithWindowSize('an error message is shown', smallWindowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDebuggerScreen(tester, mockDebuggerController);
+      // Dismiss any previous notifications:
+      notificationService.dismiss(
+        'Failed to parse package:gallery/src/unknown.dart.',
+      );
+      await tester.pumpAndSettle();
+
+      await showScript(mockEmptyScriptRef);
+      await tester.pumpAndSettle();
+
+      expect(
+        notificationService.activeMessages.first.text,
+        equals('Failed to parse package:gallery/src/unknown.dart.'),
+      );
+    });
+  });
+}
+
+bool firstNLinesAreHighlighted(int n) {
+  bool containsNonHighlightedLine = false;
+  final lines = find.byType(LineItem);
+  for (int i = 0; i < n; i++) {
+    final line = getWidgetFromFinder<LineItem>(lines.at(i));
+    if (line.lineContents.children == null) {
+      containsNonHighlightedLine = true;
+    }
+  }
+  return !containsNonHighlightedLine;
+}
+
+void expectFirstNLinesContain(List<String> stringMatches) {
+  final lines = find.byType(LineItem);
+  expect(lines, findsAtLeastNWidgets(stringMatches.length));
+  for (int i = 0; i < stringMatches.length; i++) {
+    final stringMatch = stringMatches[i];
+    final line = getWidgetFromFinder<LineItem>(lines.at(i));
+    expect(line.lineContents.toPlainText(), contains(stringMatch));
+  }
+}
diff --git a/packages/devtools_app/test/debugger/eval_field_test.dart b/packages/devtools_app/test/screens/debugger/eval_field_test.dart
similarity index 70%
rename from packages/devtools_app/test/debugger/eval_field_test.dart
rename to packages/devtools_app/test/screens/debugger/eval_field_test.dart
index ba71218..89dafcd 100644
--- a/packages/devtools_app/test/debugger/eval_field_test.dart
+++ b/packages/devtools_app/test/screens/debugger/eval_field_test.dart
@@ -29,57 +29,51 @@
     });
 
     group('suggestions', () {
-      testWidgets(
-        'shows no suggestion for empty text',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('shows no suggestion for empty text', (tester) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, '');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, '');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, '');
-          expect(objects.searchTextEditingController.suggestionText, null);
-        },
-      );
+        expect(objects.searchTextEditingController.text, '');
+        expect(objects.searchTextEditingController.suggestionText, null);
+      });
 
-      testWidgets(
-        'shows "bar" item as suggestion for string "b"',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('shows "bar" item as suggestion for string "b"', (
+        tester,
+      ) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, 'b');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, 'b');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, 'b');
-          expect(objects.searchTextEditingController.suggestionText, 'ar');
-        },
-      );
+        expect(objects.searchTextEditingController.text, 'b');
+        expect(objects.searchTextEditingController.suggestionText, 'ar');
+      });
 
-      testWidgets(
-        'shows "bazz" item as suggestion for string "baz"',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('shows "bazz" item as suggestion for string "baz"', (
+        tester,
+      ) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, 'baz');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, 'baz');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, 'baz');
-          expect(objects.searchTextEditingController.suggestionText, 'z');
-        },
-      );
+        expect(objects.searchTextEditingController.text, 'baz');
+        expect(objects.searchTextEditingController.suggestionText, 'z');
+      });
 
-      testWidgets(
-        'shows "foo" (first item) item as suggestion for field',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('shows "foo" (first item) item as suggestion for field', (
+        tester,
+      ) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, 'someValue.');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, 'someValue.');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, 'someValue.');
-          expect(objects.searchTextEditingController.suggestionText, 'foo');
-        },
-      );
+        expect(objects.searchTextEditingController.text, 'someValue.');
+        expect(objects.searchTextEditingController.suggestionText, 'foo');
+      });
 
       testWidgets(
         'pressing "arrowDown" shows the "bar" item as suggestion for field',
@@ -142,19 +136,18 @@
         },
       );
 
-      testWidgets(
-        'removing the dot after a word removes the suggestion',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('removing the dot after a word removes the suggestion', (
+        tester,
+      ) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, 'someValue.');
-          await tester.enterText(objects.textField, 'someValue');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, 'someValue.');
+        await tester.enterText(objects.textField, 'someValue');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, 'someValue');
-          expect(objects.searchTextEditingController.suggestionText, null);
-        },
-      );
+        expect(objects.searchTextEditingController.text, 'someValue');
+        expect(objects.searchTextEditingController.suggestionText, null);
+      });
 
       testWidgets(
         'when the cursor is not at the end of the text, don\'t show suggestion',
@@ -162,8 +155,9 @@
           final objects = await _setupEvalFieldObjects(tester);
 
           await tester.enterText(objects.textField, 'someValue.');
-          objects.searchTextEditingController.selection =
-              const TextSelection.collapsed(offset: 0);
+          objects
+              .searchTextEditingController
+              .selection = const TextSelection.collapsed(offset: 0);
           await tester.pumpAndSettle();
 
           expect(objects.searchTextEditingController.text, 'someValue.');
@@ -184,18 +178,17 @@
         },
       );
 
-      testWidgets(
-        'when there is no match, don\'t show suggestion text',
-        (tester) async {
-          final objects = await _setupEvalFieldObjects(tester);
+      testWidgets('when there is no match, don\'t show suggestion text', (
+        tester,
+      ) async {
+        final objects = await _setupEvalFieldObjects(tester);
 
-          await tester.enterText(objects.textField, 'someValue.bazzz');
-          await tester.pumpAndSettle();
+        await tester.enterText(objects.textField, 'someValue.bazzz');
+        await tester.pumpAndSettle();
 
-          expect(objects.searchTextEditingController.text, 'someValue.bazzz');
-          expect(objects.searchTextEditingController.suggestionText, null);
-        },
-      );
+        expect(objects.searchTextEditingController.text, 'someValue.bazzz');
+        expect(objects.searchTextEditingController.suggestionText, null);
+      });
 
       testWidgets(
         'when there is a exact match ("bar") and another match ("barz"), the exact won\'t show suggestion text',
@@ -231,43 +224,41 @@
       LogicalKeyboardKey.arrowRight,
     ]) {
       group('selection with ${selectionKey.keyLabel} key', () {
-        testWidgets(
-          'selecting "ar" autocompletes "b" to "bar"',
-          (tester) async {
-            final objects = await _setupEvalFieldObjects(tester);
+        testWidgets('selecting "ar" autocompletes "b" to "bar"', (
+          tester,
+        ) async {
+          final objects = await _setupEvalFieldObjects(tester);
 
-            await tester.enterText(objects.textField, 'b');
-            await tester.pumpAndSettle();
+          await tester.enterText(objects.textField, 'b');
+          await tester.pumpAndSettle();
 
-            expect(objects.searchTextEditingController.text, 'b');
-            expect(objects.searchTextEditingController.suggestionText, 'ar');
+          expect(objects.searchTextEditingController.text, 'b');
+          expect(objects.searchTextEditingController.suggestionText, 'ar');
 
-            // Select the suggestion.
-            await tester.sendKeyEvent(selectionKey);
-            await tester.pumpAndSettle();
+          // Select the suggestion.
+          await tester.sendKeyEvent(selectionKey);
+          await tester.pumpAndSettle();
 
-            expect(objects.textFieldValue, equals('bar'));
-          },
-        );
+          expect(objects.textFieldValue, equals('bar'));
+        });
 
-        testWidgets(
-          'selecting "r" autocompletes "ba" to "bar"',
-          (tester) async {
-            final objects = await _setupEvalFieldObjects(tester);
+        testWidgets('selecting "r" autocompletes "ba" to "bar"', (
+          tester,
+        ) async {
+          final objects = await _setupEvalFieldObjects(tester);
 
-            await tester.enterText(objects.textField, 'ba');
-            await tester.pumpAndSettle();
+          await tester.enterText(objects.textField, 'ba');
+          await tester.pumpAndSettle();
 
-            expect(objects.searchTextEditingController.text, 'ba');
-            expect(objects.searchTextEditingController.suggestionText, 'r');
+          expect(objects.searchTextEditingController.text, 'ba');
+          expect(objects.searchTextEditingController.suggestionText, 'r');
 
-            // Select the suggestion.
-            await tester.sendKeyEvent(selectionKey);
-            await tester.pumpAndSettle();
+          // Select the suggestion.
+          await tester.sendKeyEvent(selectionKey);
+          await tester.pumpAndSettle();
 
-            expect(objects.textFieldValue, equals('bar'));
-          },
-        );
+          expect(objects.textFieldValue, equals('bar'));
+        });
 
         testWidgets(
           'selecting "foo" autocompletes "someValue." to "someValue.foo"',
@@ -339,18 +330,13 @@
     },
   );
 
-  await tester.pumpWidget(
-    MaterialApp(
-      home: Scaffold(
-        body: evalField,
-      ),
-    ),
-  );
+  await tester.pumpWidget(MaterialApp(home: Scaffold(body: evalField)));
 
   final textField = find.byType(TextField).first;
 
-  final state =
-      tester.state<ExpressionEvalFieldState>(find.byWidget(evalField));
+  final state = tester.state<ExpressionEvalFieldState>(
+    find.byWidget(evalField),
+  );
 
   return _EvalFieldTestObjects(
     state.searchTextFieldController,
diff --git a/packages/devtools_app/test/screens/debugger/file_search_test.dart b/packages/devtools_app/test/screens/debugger/file_search_test.dart
new file mode 100644
index 0000000..d81872f
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/file_search_test.dart
@@ -0,0 +1,399 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/debugger/file_search.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+void main() {
+  final codeViewController = createMockCodeViewControllerWithDefaults();
+  final scriptManager = MockScriptManager();
+
+  Widget buildFileSearch() {
+    return MaterialApp(
+      home: Scaffold(
+        body: Card(
+          child: FileSearchField(codeViewController: codeViewController),
+        ),
+      ),
+    );
+  }
+
+  when(scriptManager.sortedScripts).thenReturn(ValueNotifier(mockScriptRefs));
+  setGlobal(IdeTheme, IdeTheme());
+  setGlobal(ScriptManager, scriptManager);
+
+  testWidgetsWithWindowSize(
+    'Selecting search sets current file',
+    const Size(1000.0, 4000.0),
+    (WidgetTester tester) async {
+      await tester.pumpWidget(buildFileSearch());
+      final FileSearchFieldState state = tester.state(
+        find.byType(FileSearchField),
+      );
+      final autoCompleteController = state.autoCompleteController;
+
+      autoCompleteController.search = 'cat';
+
+      await tester.pumpAndSettle();
+
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATerpillar.dart',
+          // Exact full path matches:
+          'zoo:animals/CATs/meow.dart',
+          'zoo:animals/CATs/purr.dart',
+          'kitchen:food/CATering/party.dart',
+          // Fuzzy matches:
+          'zoo:animals/insects/CicAda.darT',
+          'kitchen:food/milk/CArTon.dart',
+          'travel:adventure/CAve_Tours_europe.dart',
+        ]),
+      );
+
+      final tileFinder = find.byType(AutoCompleteTile);
+      expect(tileFinder, findsNWidgets(7));
+
+      await tester.tap(tileFinder.at(3));
+
+      expect(
+        autoCompleteController.search,
+        equals('kitchen:food/catering/party.dart'),
+      );
+    },
+  );
+
+  testWidgetsWithWindowSize(
+    'Search returns expected files',
+    const Size(1000.0, 4000.0),
+    (WidgetTester tester) async {
+      await tester.pumpWidget(buildFileSearch());
+      final FileSearchFieldState state = tester.state(
+        find.byType(FileSearchField),
+      );
+      final autoCompleteController = state.autoCompleteController;
+
+      autoCompleteController.search = '';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Show all results (truncated to 10):
+          'zoo:animals/cats/meow.dart',
+          'zoo:animals/cats/purr.dart',
+          'zoo:animals/dogs/bark.dart',
+          'zoo:animals/dogs/growl.dart',
+          'zoo:animals/insects/caterpillar.dart',
+          'zoo:animals/insects/cicada.dart',
+          'kitchen:food/catering/party.dart',
+          'kitchen:food/carton/milk.dart',
+          'kitchen:food/milk/carton.dart',
+          'travel:adventure/cave_tours_europe.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'c';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/Caterpillar.dart',
+          'zoo:animals/insects/Cicada.dart',
+          'kitchen:food/milk/Carton.dart',
+          'travel:adventure/Cave_tours_europe.dart',
+          // Exact full path matches:
+          'zoo:animals/Cats/meow.dart',
+          'zoo:animals/Cats/purr.dart',
+          'kitChen:food/catering/party.dart',
+          'kitChen:food/carton/milk.dart',
+          'travel:Canada/banff.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'ca';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CAterpillar.dart',
+          'zoo:animals/insects/ciCAda.dart',
+          'kitchen:food/milk/CArton.dart',
+          'travel:adventure/CAve_tours_europe.dart',
+          // Exact full path matches:
+          'zoo:animals/CAts/meow.dart',
+          'zoo:animals/CAts/purr.dart',
+          'kitchen:food/CAtering/party.dart',
+          'kitchen:food/CArton/milk.dart',
+          'travel:CAnada/banff.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'cat';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATerpillar.dart',
+          // Exact full path matches:
+          'zoo:animals/CATs/meow.dart',
+          'zoo:animals/CATs/purr.dart',
+          'kitchen:food/CATering/party.dart',
+          // Fuzzy matches:
+          'zoo:animals/insects/CicAda.darT',
+          'kitchen:food/milk/CArTon.dart',
+          'travel:adventure/CAve_Tours_europe.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'cate';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATErpillar.dart',
+          // Exact full path matches:
+          'kitchen:food/CATEring/party.dart',
+          // Fuzzy matches:
+          'travel:adventure/CAve_Tours_Europe.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'cater';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATERpillar.dart',
+          // Exact full path matches:
+          'kitchen:food/CATERing/party.dart',
+          // Fuzzy matches:
+          'travel:adventure/CAve_Tours_EuRope.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'caterp';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATERPillar.dart',
+          // Fuzzy matches:
+          'travel:adventure/CAve_Tours_EuRoPe.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'caterpi';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'zoo:animals/insects/CATERPIllar.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'caterpie';
+      expect(
+        getAutoCompleteMatch(
+          autoCompleteController.searchAutoComplete.value,
+          preserveCases: true,
+        ),
+        equals(['No files found.']),
+      );
+    },
+  );
+
+  testWidgetsWithWindowSize(
+    'Multi token search returns expected files',
+    const Size(1000.0, 4000.0),
+    (WidgetTester tester) async {
+      await tester.pumpWidget(buildFileSearch());
+      final FileSearchFieldState state = tester.state(
+        find.byType(FileSearchField),
+      );
+      final autoCompleteController = state.autoCompleteController;
+
+      autoCompleteController.search = '';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Show all results (truncated to 10):
+          'zoo:animals/cats/meow.dart',
+          'zoo:animals/cats/purr.dart',
+          'zoo:animals/dogs/bark.dart',
+          'zoo:animals/dogs/growl.dart',
+          'zoo:animals/insects/caterpillar.dart',
+          'zoo:animals/insects/cicada.dart',
+          'kitchen:food/catering/party.dart',
+          'kitchen:food/carton/milk.dart',
+          'kitchen:food/milk/carton.dart',
+          'travel:adventure/cave_tours_europe.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'f';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact file name matches:
+          'travel:canada/banFf.dart',
+          // Exact full path matches:
+          'kitchen:Food/catering/party.dart',
+          'kitchen:Food/carton/milk.dart',
+          'kitchen:Food/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'fo';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOod/catering/party.dart',
+          'kitchen:FOod/carton/milk.dart',
+          'kitchen:FOod/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'foo';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOd/catering/party.dart',
+          'kitchen:FOOd/carton/milk.dart',
+          'kitchen:FOOd/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/catering/party.dart',
+          'kitchen:FOOD/carton/milk.dart',
+          'kitchen:FOOD/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food ';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/catering/party.dart',
+          'kitchen:FOOD/carton/milk.dart',
+          'kitchen:FOOD/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food c';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitChen:FOOD/catering/party.dart',
+          'kitChen:FOOD/carton/milk.dart',
+          'kitChen:FOOD/milk/carton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food ca';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/CAtering/party.dart',
+          'kitchen:FOOD/CArton/milk.dart',
+          'kitchen:FOOD/milk/CArton.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food car';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/CARton/milk.dart',
+          'kitchen:FOOD/milk/CARton.dart',
+          // Fuzzy matches:
+          'kitchen:FOOD/CAteRing/party.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food cart';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/CARTon/milk.dart',
+          'kitchen:FOOD/milk/CARTon.dart',
+          // Fuzzy matches:
+          'kitchen:FOOD/CAteRing/parTy.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'food carto';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:FOOD/CARTOn/milk.dart',
+          'kitchen:FOOD/milk/CARTOn.dart',
+        ]),
+      );
+
+      autoCompleteController.search = 'carton.dar';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:food/milk/CARTON.DARt',
+        ]),
+      );
+
+      autoCompleteController.search = 'carton.dart';
+      expect(
+        getAutoCompleteMatch(autoCompleteController.searchAutoComplete.value),
+        equals([
+          // Exact full path matches:
+          'kitchen:food/milk/CARTON.DART',
+        ]),
+      );
+
+      autoCompleteController.search = 'food cartwheel';
+      expect(
+        getAutoCompleteMatch(
+          autoCompleteController.searchAutoComplete.value,
+          preserveCases: true,
+        ),
+        equals(['No files found.']),
+      );
+    },
+  );
+}
+
+List<String> getAutoCompleteMatch(
+  List<AutoCompleteMatch> matches, {
+  bool preserveCases = false,
+}) {
+  return matches
+      .map(
+        (match) => match.transformAutoCompleteMatch<String>(
+          transformMatchedSegment:
+              (segment) => preserveCases ? segment : segment.toUpperCase(),
+          transformUnmatchedSegment:
+              (segment) => preserveCases ? segment : segment.toLowerCase(),
+          combineSegments: (segments) => segments.join(),
+        ),
+      )
+      .toList();
+}
diff --git a/packages/devtools_app/test/debugger/program_explorer_test.dart b/packages/devtools_app/test/screens/debugger/program_explorer_test.dart
similarity index 60%
rename from packages/devtools_app/test/debugger/program_explorer_test.dart
rename to packages/devtools_app/test/screens/debugger/program_explorer_test.dart
index ea87b56..5212998 100644
--- a/packages/devtools_app/test/debugger/program_explorer_test.dart
+++ b/packages/devtools_app/test/screens/debugger/program_explorer_test.dart
@@ -15,8 +15,8 @@
 import 'package:mockito/mockito.dart';
 import 'package:vm_service/vm_service.dart';
 
-import '../test_infra/test_data/debugger/vm_service_object_tree.dart';
-import '../test_infra/utils/tree_utils.dart';
+import '../../test_infra/test_data/debugger/vm_service_object_tree.dart';
+import '../../test_infra/utils/tree_utils.dart';
 
 void main() {
   group('Mock ProgramExplorer', () {
@@ -42,21 +42,18 @@
     });
 
     testWidgets('builds when not initialized', (WidgetTester tester) async {
-      when(mockProgramExplorerController.initialized)
-          .thenReturn(const FixedValueListenable(false));
+      when(
+        mockProgramExplorerController.initialized,
+      ).thenReturn(const FixedValueListenable(false));
       await tester.pumpWidget(
-        wrap(
-          ProgramExplorer(controller: mockProgramExplorerController),
-        ),
+        wrap(ProgramExplorer(controller: mockProgramExplorerController)),
       );
       expect(find.byType(CenteredCircularProgressIndicator), findsOneWidget);
     });
 
     testWidgets('builds when initialized', (WidgetTester tester) async {
       await tester.pumpWidget(
-        wrap(
-          ProgramExplorer(controller: mockProgramExplorerController),
-        ),
+        wrap(ProgramExplorer(controller: mockProgramExplorerController)),
       );
       expect(find.byType(AreaPaneHeader), findsNWidgets(2));
       expect(find.text('File Explorer'), findsOneWidget);
@@ -74,17 +71,19 @@
     setUpAll(() {
       fakeServiceConnection = FakeServiceConnectionManager();
 
-      when(fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow)
-          .thenReturn(false);
-      when(fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow)
-          .thenReturn(false);
+      when(
+        fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow,
+      ).thenReturn(false);
+      when(
+        fakeServiceConnection.serviceManager.connectedApp!.isDartWebAppNow,
+      ).thenReturn(false);
 
       final mockScriptManager = MockScriptManager();
-      //`then` is used
-      // ignore: discarded_futures
-      when(mockScriptManager.getScript(any)).thenAnswer(
-        (_) => Future<Script>.value(testScript),
-      );
+      when(
+        //`then` is used
+        // ignore: discarded_futures
+        mockScriptManager.getScript(any),
+      ).thenAnswer((_) => Future<Script>.value(testScript));
 
       setGlobal(ScriptManager, mockScriptManager);
       setGlobal(ServiceConnectionManager, fakeServiceConnection);
@@ -97,8 +96,11 @@
     ) async {
       final programExplorerController = TestProgramExplorerController(
         initializer: (controller) {
-          final libraryNode =
-              VMServiceObjectNode(controller, 'fooLib', testLib);
+          final libraryNode = VMServiceObjectNode(
+            controller,
+            'fooLib',
+            testLib,
+          );
           libraryNode.script = testScript;
           libraryNode.location = ScriptLocation(testScript);
           controller.rootObjectNodesInternal.add(libraryNode);
@@ -106,13 +108,7 @@
       );
       final explorer = ProgramExplorer(controller: programExplorerController);
       await programExplorerController.initialize();
-      await tester.pumpWidget(
-        wrap(
-          Builder(
-            builder: explorer.build,
-          ),
-        ),
-      );
+      await tester.pumpWidget(wrap(Builder(builder: explorer.build)));
       expect(programExplorerController.initialized.value, true);
       expect(programExplorerController.rootObjectNodes.value.numNodes, 1);
       return programExplorerController;
@@ -142,54 +138,49 @@
       }
     });
 
-    testWidgets(
-      'selection',
-      (WidgetTester tester) async {
-        final programExplorerController =
-            await initializeProgramExplorer(tester);
-        final libNode = programExplorerController.rootObjectNodes.value.first;
+    testWidgets('selection', (WidgetTester tester) async {
+      final programExplorerController = await initializeProgramExplorer(tester);
+      final libNode = programExplorerController.rootObjectNodes.value.first;
 
-        // No node has been selected yet, so the outline should be empty.
-        expect(programExplorerController.outlineNodes.value.isEmpty, true);
-        expect(programExplorerController.scriptSelection, isNull);
-        expect(
-          programExplorerController.outlineNodes.value
-              .where((e) => e.isSelected),
-          isEmpty,
-        );
+      // No node has been selected yet, so the outline should be empty.
+      expect(programExplorerController.outlineNodes.value.isEmpty, true);
+      expect(programExplorerController.scriptSelection, isNull);
+      expect(
+        programExplorerController.outlineNodes.value.where((e) => e.isSelected),
+        isEmpty,
+      );
 
-        // Select the library node and ensure the outline is populated.
-        final libNodeFinder = find.text(libNode.name);
-        expect(libNodeFinder, findsOneWidget);
-        await tester.tap(libNodeFinder);
-        await tester.pumpAndSettle();
+      // Select the library node and ensure the outline is populated.
+      final libNodeFinder = find.text(libNode.name);
+      expect(libNodeFinder, findsOneWidget);
+      await tester.tap(libNodeFinder);
+      await tester.pumpAndSettle();
 
-        expect(programExplorerController.scriptSelection, libNode);
-        expect(
-          programExplorerController.outlineNodes.value
-              .where((e) => e.isSelected),
-          isEmpty,
-        );
+      expect(programExplorerController.scriptSelection, libNode);
+      expect(
+        programExplorerController.outlineNodes.value.where((e) => e.isSelected),
+        isEmpty,
+      );
 
-        // There should be three children total, one root with two children.
-        expect(programExplorerController.outlineNodes.value.length, 1);
-        expect(programExplorerController.outlineNodes.value.numNodes, 3);
+      // There should be three children total, one root with two children.
+      expect(programExplorerController.outlineNodes.value.length, 1);
+      expect(programExplorerController.outlineNodes.value.numNodes, 3);
 
-        // Select one of them and check that the outline selection has been
-        // updated.
-        final outlineNode = programExplorerController.outlineNodes.value.first;
-        final outlineNodeFinder = find.text(outlineNode.name);
-        expect(outlineNodeFinder, findsOneWidget);
-        await tester.tap(outlineNodeFinder);
-        await tester.pumpAndSettle();
+      // Select one of them and check that the outline selection has been
+      // updated.
+      final outlineNode = programExplorerController.outlineNodes.value.first;
+      final outlineNodeFinder = find.text(outlineNode.name);
+      expect(outlineNodeFinder, findsOneWidget);
+      await tester.tap(outlineNodeFinder);
+      await tester.pumpAndSettle();
 
-        expect(programExplorerController.scriptSelection, libNode);
-        expect(
-          programExplorerController.outlineNodes.value
-              .singleWhereOrNull((e) => e.isSelected),
-          outlineNode,
-        );
-      },
-    );
+      expect(programExplorerController.scriptSelection, libNode);
+      expect(
+        programExplorerController.outlineNodes.value.singleWhereOrNull(
+          (e) => e.isSelected,
+        ),
+        outlineNode,
+      );
+    });
   });
 }
diff --git a/packages/devtools_app/test/debugger/records_variable_test.dart b/packages/devtools_app/test/screens/debugger/records_variable_test.dart
similarity index 83%
rename from packages/devtools_app/test/debugger/records_variable_test.dart
rename to packages/devtools_app/test/screens/debugger/records_variable_test.dart
index bf4ccb7..c390733 100644
--- a/packages/devtools_app/test/debugger/records_variable_test.dart
+++ b/packages/devtools_app/test/screens/debugger/records_variable_test.dart
@@ -65,18 +65,12 @@
         isSystemIsolate: false,
       );
       final recordVar = DartObjectNode.create(
-        BoundVariable(
-          name: 'myRecord',
-          value: instance,
-        ),
+        BoundVariable(name: 'myRecord', value: instance),
         isolateRef,
       );
 
       when(
-        manager.serviceManager.service!.getObject(
-          isolateId,
-          objectId,
-        ),
+        manager.serviceManager.service!.getObject(isolateId, objectId),
       ).thenAnswer((_) async {
         return instance;
       });
@@ -92,19 +86,8 @@
   );
 }
 
-Matcher matchesVariable({
-  required String? name,
-  required Object value,
-}) {
+Matcher matchesVariable({required String? name, required Object value}) {
   return const TypeMatcher<DartObjectNode>()
-      .having(
-        (v) => v.displayValue,
-        'displayValue',
-        equals(value),
-      )
-      .having(
-        (v) => v.name,
-        'name',
-        equals(name),
-      );
+      .having((v) => v.displayValue, 'displayValue', equals(value))
+      .having((v) => v.name, 'name', equals(name));
 }
diff --git a/packages/devtools_app/test/shared/span_parser_test.dart b/packages/devtools_app/test/screens/debugger/span_parser_test.dart
similarity index 92%
rename from packages/devtools_app/test/shared/span_parser_test.dart
rename to packages/devtools_app/test/screens/debugger/span_parser_test.dart
index 684c8e2..1172f2a 100644
--- a/packages/devtools_app/test/shared/span_parser_test.dart
+++ b/packages/devtools_app/test/screens/debugger/span_parser_test.dart
@@ -32,12 +32,14 @@
   final grammarFile = File(path.join('assets', 'dart_syntax.json')).absolute;
   late Grammar grammar;
 
-  final testDataDirectory = Directory(
-    path.join('test', 'test_infra', 'test_data', 'syntax_highlighting'),
-  ).absolute;
-  final goldenDirectory = Directory(
-    path.join('test', 'test_infra', 'goldens', 'syntax_highlighting'),
-  ).absolute;
+  final testDataDirectory =
+      Directory(
+        path.join('test', 'test_infra', 'test_data', 'syntax_highlighting'),
+      ).absolute;
+  final goldenDirectory =
+      Directory(
+        path.join('test', 'test_infra', 'goldens', 'syntax_highlighting'),
+      ).absolute;
 
   setUpAll(() async {
     expect(grammarFile.existsSync(), true);
@@ -49,10 +51,7 @@
     final updateGoldens = autoUpdateGoldenFiles;
 
     /// Expects parsing [content] using produces the output in [goldenFile].
-    void expectSpansMatchGolden(
-      String content,
-      File goldenFile,
-    ) {
+    void expectSpansMatchGolden(String content, File goldenFile) {
       if (updateGoldens) {
         final spans = SpanParser.parse(grammar, content);
         final actual = _buildGoldenText(content, spans);
@@ -112,10 +111,9 @@
       // flag:
       //
       //     flutter test test/shared/span_parser_test.dart --update-goldens
-      final testFiles = testDataDirectory
-          .listSync()
-          .whereType<File>()
-          .where((file) => path.extension(file.path) == '.dart');
+      final testFiles = testDataDirectory.listSync().whereType<File>().where(
+        (file) => path.extension(file.path) == '.dart',
+      );
 
       for (final testFile in testFiles) {
         final goldenFile = goldenFileFor(path.basename(testFile.path));
diff --git a/packages/devtools_app/test/screens/debugger/syntax_highlighter_test.dart b/packages/devtools_app/test/screens/debugger/syntax_highlighter_test.dart
new file mode 100644
index 0000000..1ffdb7d
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/syntax_highlighter_test.dart
@@ -0,0 +1,540 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@TestOn('vm')
+library;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:devtools_app/src/screens/debugger/span_parser.dart';
+import 'package:devtools_app/src/screens/debugger/syntax_highlighter.dart';
+import 'package:devtools_app/src/shared/framework/routing.dart';
+import 'package:devtools_app/src/shared/ui/colors.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+const modifierSpans = [
+  // Start multi-capture spans
+  'import "foo";',
+  'export "foo";',
+  'part of "baz";',
+  'part "foo";',
+  'export "foo";',
+  // End multi-capture spans
+  '@annotation',
+  'true',
+  'false',
+  'null',
+  'as',
+  'abstract',
+  'class',
+  'enum',
+  'extends',
+  'extension',
+  'external',
+  'factory',
+  'implements',
+  'get',
+  'mixin',
+  'native',
+  'operator',
+  'set',
+  'typedef',
+  'with',
+  'covariant',
+  'static',
+  'final',
+  'const',
+  'required',
+  'late',
+];
+
+const controlFlowSpans = [
+  'try',
+  'on',
+  'catch',
+  'finally',
+  'throw',
+  'rethrow',
+  'break',
+  'case',
+  'continue',
+  'default',
+  'do',
+  'else',
+  'for',
+  'if',
+  'in',
+  'return',
+  'switch',
+  'while',
+  'sync',
+  'async',
+  'await',
+  'yield',
+  'assert',
+  'new',
+];
+
+const declarationSpans = [
+  'this',
+  'super',
+  'bool',
+  'num',
+  'int',
+  'double',
+  'dynamic',
+  '_PrivateDeclaration',
+  'PublicDeclaration',
+];
+
+const functionSpans = ['foo()', '_foo()', 'foo(bar)'];
+
+const numericSpans = ['1', '1.1', '0xFF', '0xff', '1.3e5', '1.3E5'];
+
+const helloWorld = '''
+Future<void> main() async {
+  print('hello world!');
+}
+''';
+
+const multilineDoc = '''
+/**
+ * Multiline
+ */
+''';
+
+const docCodeReference = '''
+/// This is a code reference for [Foo]
+''';
+
+const variableReferenceInString = '''
+'\$i: \${foo[i] == bar[i]}'
+''';
+
+void main() {
+  late Grammar grammar;
+  setUp(() async {
+    final grammarFile = File('assets/dart_syntax.json');
+    expect(grammarFile.existsSync(), true);
+
+    final grammarJson = json.decode(await grammarFile.readAsString());
+    grammar = Grammar.fromJson(grammarJson);
+    setGlobal(IdeTheme, IdeTheme());
+  });
+
+  Color? defaultTextColor(_) => const TextStyle().color;
+  Color commentSyntaxColor(ColorScheme scheme) => scheme.commentSyntaxColor;
+  Color controlFlowSyntaxColor(ColorScheme scheme) =>
+      scheme.controlFlowSyntaxColor;
+  Color declarationSyntaxColor(ColorScheme scheme) =>
+      scheme.declarationsSyntaxColor;
+  Color functionSyntaxColor(ColorScheme scheme) => scheme.functionSyntaxColor;
+  Color modifierSyntaxColor(ColorScheme scheme) => scheme.modifierSyntaxColor;
+  Color numericConstantSyntaxColor(ColorScheme scheme) =>
+      scheme.numericConstantSyntaxColor;
+  Color stringSyntaxColor(ColorScheme scheme) => scheme.stringSyntaxColor;
+  Color variableSyntaxColor(ColorScheme scheme) => scheme.variableSyntaxColor;
+
+  void spanTester(
+    BuildContext context,
+    TextSpan span,
+    String expectedText,
+    Color? Function(ColorScheme) expectedColor,
+  ) {
+    expect(span.text, expectedText);
+    expect(
+      span.style,
+      TextStyle(color: expectedColor(Theme.of(context).colorScheme)),
+    );
+  }
+
+  void runTestsWithTheme({required bool useDarkTheme}) {
+    group('Syntax Highlighting (${useDarkTheme ? 'Dark' : 'Light'} Theme)', () {
+      Widget buildSyntaxHighlightingTestContext(
+        void Function(BuildContext) callback,
+      ) {
+        return MaterialApp.router(
+          theme: themeFor(
+            isDarkTheme: useDarkTheme,
+            ideTheme: getIdeTheme(),
+            theme: ThemeData(
+              useMaterial3: true,
+              colorScheme: useDarkTheme ? darkColorScheme : lightColorScheme,
+            ),
+          ),
+          routerDelegate: DevToolsRouterDelegate(
+            (a, b, c, d) => const CupertinoPage(child: SizedBox.shrink()),
+          ),
+          routeInformationParser: DevToolsRouteInformationParser(),
+          builder: (context, _) {
+            callback(context);
+            return Container();
+          },
+        );
+      }
+
+      testWidgetsWithContext('hello world smoke', (WidgetTester tester) async {
+        final highlighter = SyntaxHighlighter.withGrammar(
+          grammar: grammar,
+          source: helloWorld,
+        );
+
+        await tester.pumpWidget(
+          buildSyntaxHighlightingTestContext((context) {
+            final highlighted = highlighter.highlight(context);
+            final children = highlighted.children!;
+
+            spanTester(
+              context,
+              children[0] as TextSpan,
+              'Future',
+              declarationSyntaxColor,
+            );
+
+            spanTester(context, children[1] as TextSpan, '<', defaultTextColor);
+
+            spanTester(
+              context,
+              children[2] as TextSpan,
+              'void',
+              modifierSyntaxColor,
+            );
+
+            spanTester(context, children[3] as TextSpan, '>', defaultTextColor);
+
+            spanTester(context, children[4] as TextSpan, ' ', defaultTextColor);
+
+            spanTester(
+              context,
+              children[5] as TextSpan,
+              'main',
+              functionSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[6] as TextSpan,
+              '() ',
+              defaultTextColor,
+            );
+
+            spanTester(
+              context,
+              children[7] as TextSpan,
+              'async',
+              controlFlowSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[8] as TextSpan,
+              ' {',
+              defaultTextColor,
+            );
+
+            expect(children[9].toPlainText(), '\n');
+
+            spanTester(
+              context,
+              children[10] as TextSpan,
+              '  ',
+              defaultTextColor,
+            );
+
+            spanTester(
+              context,
+              children[11] as TextSpan,
+              'print',
+              functionSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[12] as TextSpan,
+              '(',
+              defaultTextColor,
+            );
+
+            spanTester(
+              context,
+              children[13] as TextSpan,
+              "'hello world!'",
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[14] as TextSpan,
+              ')',
+              defaultTextColor,
+            );
+
+            spanTester(
+              context,
+              children[15] as TextSpan,
+              ';',
+              defaultTextColor,
+            );
+
+            expect(children[16].toPlainText(), '\n');
+
+            spanTester(
+              context,
+              children[17] as TextSpan,
+              '}',
+              defaultTextColor,
+            );
+
+            expect(children[18].toPlainText(), '\n');
+          }),
+        );
+      });
+
+      testWidgetsWithContext('multiline documentation', (
+        WidgetTester tester,
+      ) async {
+        final highlighter = SyntaxHighlighter.withGrammar(
+          grammar: grammar,
+          source: multilineDoc,
+        );
+
+        await tester.pumpWidget(
+          buildSyntaxHighlightingTestContext((context) {
+            final highlighted = highlighter.highlight(context);
+            final children = highlighted.children!;
+
+            spanTester(
+              context,
+              children[0] as TextSpan,
+              '/**',
+              commentSyntaxColor,
+            );
+
+            expect(children[1].toPlainText(), '\n');
+
+            spanTester(
+              context,
+              children[2] as TextSpan,
+              ' * Multiline',
+              commentSyntaxColor,
+            );
+
+            expect(children[3].toPlainText(), '\n');
+
+            spanTester(
+              context,
+              children[4] as TextSpan,
+              ' */',
+              commentSyntaxColor,
+            );
+          }),
+        );
+      });
+
+      testWidgetsWithContext('documentation code reference', (
+        WidgetTester tester,
+      ) async {
+        final highlighter = SyntaxHighlighter.withGrammar(
+          grammar: grammar,
+          source: docCodeReference,
+        );
+
+        await tester.pumpWidget(
+          buildSyntaxHighlightingTestContext((context) {
+            final highlighted = highlighter.highlight(context);
+            final children = highlighted.children!;
+
+            spanTester(
+              context,
+              children[0] as TextSpan,
+              '/// This is a code reference for ',
+              commentSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[1] as TextSpan,
+              '[Foo]',
+              variableSyntaxColor,
+            );
+
+            expect(children[2].toPlainText(), '\n');
+          }),
+        );
+      });
+
+      testWidgetsWithContext('variable reference in string', (
+        WidgetTester tester,
+      ) async {
+        final highlighter = SyntaxHighlighter.withGrammar(
+          grammar: grammar,
+          source: variableReferenceInString,
+        );
+
+        await tester.pumpWidget(
+          buildSyntaxHighlightingTestContext((context) {
+            final highlighted = highlighter.highlight(context);
+            final children = highlighted.children!;
+
+            var i = 0;
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              "'",
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '\$',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              'i',
+              variableSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              ': ',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '\${',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              'foo',
+              variableSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '[',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              'i',
+              variableSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '] == ',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              'bar',
+              variableSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '[',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              'i',
+              variableSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              ']}',
+              stringSyntaxColor,
+            );
+
+            spanTester(
+              context,
+              children[i++] as TextSpan,
+              '\'',
+              stringSyntaxColor,
+            );
+          }),
+        );
+      });
+
+      void testSingleSpan(
+        String name,
+        String spanText,
+        Color Function(ColorScheme) colorCallback,
+      ) {
+        testWidgetsWithContext("$name '$spanText'", (
+          WidgetTester tester,
+        ) async {
+          final highlighter = SyntaxHighlighter.withGrammar(
+            grammar: grammar,
+            source: spanText,
+          );
+
+          await tester.pumpWidget(
+            buildSyntaxHighlightingTestContext((context) {
+              final highlighted = highlighter.highlight(context);
+              expect(
+                highlighted.children!.first.style,
+                TextStyle(color: colorCallback(Theme.of(context).colorScheme)),
+              );
+            }),
+          );
+        });
+      }
+
+      group('single span highlighting:', () {
+        for (final spanText in modifierSpans) {
+          testSingleSpan('modifier', spanText, modifierSyntaxColor);
+        }
+
+        for (final spanText in controlFlowSpans) {
+          testSingleSpan('control flow', spanText, controlFlowSyntaxColor);
+        }
+
+        for (final spanText in declarationSpans) {
+          testSingleSpan('declaration', spanText, declarationSyntaxColor);
+        }
+
+        for (final spanText in functionSpans) {
+          testSingleSpan('function', spanText, functionSyntaxColor);
+        }
+
+        for (final spanText in numericSpans) {
+          testSingleSpan('numeric', spanText, numericConstantSyntaxColor);
+        }
+      });
+    });
+  }
+
+  runTestsWithTheme(useDarkTheme: false);
+  runTestsWithTheme(useDarkTheme: true);
+}
diff --git a/packages/devtools_app/test/debugger/typed_data_variable_test.dart b/packages/devtools_app/test/screens/debugger/typed_data_variable_test.dart
similarity index 68%
rename from packages/devtools_app/test/debugger/typed_data_variable_test.dart
rename to packages/devtools_app/test/screens/debugger/typed_data_variable_test.dart
index 2ca4970..5f70edb 100644
--- a/packages/devtools_app/test/debugger/typed_data_variable_test.dart
+++ b/packages/devtools_app/test/screens/debugger/typed_data_variable_test.dart
@@ -44,15 +44,13 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
 
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -75,14 +73,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -105,14 +101,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -135,14 +129,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -156,40 +148,33 @@
     ]);
   });
 
-  test(
-    'Creates bound variables for Uint64List instance',
-    () async {
-      final bytes =
-          Uint64List.fromList([0, 4294967296, 4294967297, 4294967298]);
-      final instance = Instance(
-        kind: InstanceKind.kUint64List,
-        id: objectId,
-        bytes: base64.encode(bytes.buffer.asUint8List()),
-        length: 4,
-      );
-      final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
-        isolateRef,
-      );
-      when(manager.serviceManager.service!.getObject(isolateId, objectId))
-          .thenAnswer((_) async {
-        return instance;
-      });
+  test('Creates bound variables for Uint64List instance', () async {
+    final bytes = Uint64List.fromList([0, 4294967296, 4294967297, 4294967298]);
+    final instance = Instance(
+      kind: InstanceKind.kUint64List,
+      id: objectId,
+      bytes: base64.encode(bytes.buffer.asUint8List()),
+      length: 4,
+    );
+    final variable = DartObjectNode.create(
+      BoundVariable(name: 'test', value: instance),
+      isolateRef,
+    );
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
+      return instance;
+    });
 
-      await buildVariablesTree(variable);
+    await buildVariablesTree(variable);
 
-      expect(variable.children, [
-        matchesVariable(name: '[0]', value: 0),
-        matchesVariable(name: '[1]', value: 4294967296),
-        matchesVariable(name: '[2]', value: 4294967297),
-        matchesVariable(name: '[3]', value: 4294967298),
-      ]);
-    },
-    skip: kIsWeb,
-  );
+    expect(variable.children, [
+      matchesVariable(name: '[0]', value: 0),
+      matchesVariable(name: '[1]', value: 4294967296),
+      matchesVariable(name: '[2]', value: 4294967297),
+      matchesVariable(name: '[3]', value: 4294967298),
+    ]);
+  }, skip: kIsWeb);
 
   test('Creates bound variables for Int8List instance', () async {
     final bytes = Int8List.fromList([0, 1, -2, 3]);
@@ -200,14 +185,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -230,14 +213,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -260,14 +241,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -281,44 +260,40 @@
     ]);
   });
 
-  test(
-    'Creates bound variables for Int64List instance',
-    () async {
-      final bytes =
-          Int64List.fromList([0, 4294967296, -4294967297, 4294967298]);
-      final instance = Instance(
-        kind: InstanceKind.kInt64List,
-        id: objectId,
-        bytes: base64.encode(bytes.buffer.asUint8List()),
-        length: 4,
-      );
-      final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
-        isolateRef,
-      );
-      when(manager.serviceManager.service!.getObject(isolateId, objectId))
-          .thenAnswer((_) async {
-        return instance;
-      });
+  test('Creates bound variables for Int64List instance', () async {
+    final bytes = Int64List.fromList([0, 4294967296, -4294967297, 4294967298]);
+    final instance = Instance(
+      kind: InstanceKind.kInt64List,
+      id: objectId,
+      bytes: base64.encode(bytes.buffer.asUint8List()),
+      length: 4,
+    );
+    final variable = DartObjectNode.create(
+      BoundVariable(name: 'test', value: instance),
+      isolateRef,
+    );
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
+      return instance;
+    });
 
-      await buildVariablesTree(variable);
+    await buildVariablesTree(variable);
 
-      expect(variable.children, [
-        matchesVariable(name: '[0]', value: 0),
-        matchesVariable(name: '[1]', value: 4294967296),
-        matchesVariable(name: '[2]', value: -4294967297),
-        matchesVariable(name: '[3]', value: 4294967298),
-      ]);
-    },
-    skip: kIsWeb,
-  ); // Int64List cannot be instantiated on the web.
+    expect(variable.children, [
+      matchesVariable(name: '[0]', value: 0),
+      matchesVariable(name: '[1]', value: 4294967296),
+      matchesVariable(name: '[2]', value: -4294967297),
+      matchesVariable(name: '[3]', value: 4294967298),
+    ]);
+  }, skip: kIsWeb); // Int64List cannot be instantiated on the web.
 
   test('Creates bound variables for Float32List instance', () async {
-    final bytes =
-        Float32List.fromList([0, 2.2300031185150146, -4.610400199890137]);
+    final bytes = Float32List.fromList([
+      0,
+      2.2300031185150146,
+      -4.610400199890137,
+    ]);
     final instance = Instance(
       kind: InstanceKind.kFloat32List,
       id: objectId,
@@ -326,14 +301,12 @@
       length: 4,
     );
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -356,14 +329,12 @@
     );
 
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -377,8 +348,9 @@
   });
 
   test('Creates bound variables for Int32x4List instance', () async {
-    final bytes =
-        Int32x4List.fromList([Int32x4.bool(true, false, true, false)]);
+    final bytes = Int32x4List.fromList([
+      Int32x4.bool(true, false, true, false),
+    ]);
     final instance = Instance(
       kind: InstanceKind.kInt32x4List,
       id: objectId,
@@ -387,14 +359,12 @@
     );
 
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
     await buildVariablesTree(variable);
@@ -413,9 +383,9 @@
   });
 
   test('Creates bound variables for Float32x4List instance', () async {
-    final bytes = Float32x4List.fromList(
-      [Float32x4(0.0, -232.1999969482422, 2.3299999237060547, 9.0)],
-    );
+    final bytes = Float32x4List.fromList([
+      Float32x4(0.0, -232.1999969482422, 2.3299999237060547, 9.0),
+    ]);
     final instance = Instance(
       kind: InstanceKind.kFloat32x4List,
       id: objectId,
@@ -424,14 +394,12 @@
     );
 
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -459,14 +427,12 @@
     );
 
     final variable = DartObjectNode.create(
-      BoundVariable(
-        name: 'test',
-        value: instance,
-      ),
+      BoundVariable(name: 'test', value: instance),
       isolateRef,
     );
-    when(manager.serviceManager.service!.getObject(isolateId, objectId))
-        .thenAnswer((_) async {
+    when(
+      manager.serviceManager.service!.getObject(isolateId, objectId),
+    ).thenAnswer((_) async {
       return instance;
     });
 
@@ -493,10 +459,7 @@
         length: 332,
       );
       final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
+        BoundVariable(name: 'test', value: instance),
         isolateRef,
       );
 
@@ -520,10 +483,7 @@
         length: 300,
       );
       final variable = DartObjectNode.create(
-        BoundVariable(
-          name: 'test',
-          value: instance,
-        ),
+        BoundVariable(name: 'test', value: instance),
         isolateRef,
       );
 
@@ -538,10 +498,7 @@
   );
 }
 
-Matcher matchesVariable({
-  required String name,
-  required Object value,
-}) {
+Matcher matchesVariable({required String name, required Object value}) {
   return const TypeMatcher<DartObjectNode>().having(
     (v) => v,
     'boundVar',
@@ -551,14 +508,14 @@
   );
 }
 
-Matcher matchesVariableGroup({
-  required int start,
-  required int end,
-}) {
+Matcher matchesVariableGroup({required int start, required int end}) {
   return const TypeMatcher<DartObjectNode>().having(
     (v) => v,
     'boundVar',
-    const TypeMatcher<DartObjectNode>()
-        .having((v) => v.text, 'text', equals('[$start - $end]')),
+    const TypeMatcher<DartObjectNode>().having(
+      (v) => v.text,
+      'text',
+      equals('[$start - $end]'),
+    ),
   );
 }
diff --git a/packages/devtools_app/test/screens/debugger/variables_test.dart b/packages/devtools_app/test/screens/debugger/variables_test.dart
new file mode 100644
index 0000000..7b02de9
--- /dev/null
+++ b/packages/devtools_app/test/screens/debugger/variables_test.dart
@@ -0,0 +1,61 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/shared/console/widgets/expandable_variable.dart';
+import 'package:devtools_app/src/shared/diagnostics/dart_object_node.dart';
+import 'package:devtools_app/src/shared/ui/tree_view.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  setUp(() {
+    final service = createMockVmServiceWrapperWithDefaults();
+    final manager = FakeServiceConnectionManager(service: service);
+    setGlobal(ServiceConnectionManager, manager);
+  });
+
+  group('debugger variables', () {
+    late DartObjectNode objectNode;
+
+    setUp(() {
+      setGlobal(IdeTheme, IdeTheme());
+      objectNode = DartObjectNode.text('test node');
+    });
+
+    Future<void> pumpExpandableVariable(
+      WidgetTester tester,
+      DartObjectNode? variable,
+    ) async {
+      await tester.pumpWidget(wrap(ExpandableVariable(variable: variable)));
+      await tester.pumpAndSettle();
+      expect(find.byType(ExpandableVariable), findsOneWidget);
+    }
+
+    testWidgets('ExpandableVariable builds without error', (
+      WidgetTester tester,
+    ) async {
+      await pumpExpandableVariable(tester, objectNode);
+      expect(find.byType(TreeView<DartObjectNode>), findsOneWidget);
+      expect(
+        find.byKey(ExpandableVariable.emptyExpandableVariableKey),
+        findsNothing,
+      );
+    });
+
+    testWidgets('ExpandableVariable builds for null variable', (
+      WidgetTester tester,
+    ) async {
+      await pumpExpandableVariable(tester, null);
+      expect(find.byType(TreeView<DartObjectNode>), findsNothing);
+      expect(
+        find.byKey(ExpandableVariable.emptyExpandableVariableKey),
+        findsOneWidget,
+      );
+    });
+  });
+}
diff --git a/packages/devtools_app/test/deep_link_vlidation/app_link_settings_test.dart b/packages/devtools_app/test/screens/deep_link_vlidation/app_link_settings_test.dart
similarity index 100%
rename from packages/devtools_app/test/deep_link_vlidation/app_link_settings_test.dart
rename to packages/devtools_app/test/screens/deep_link_vlidation/app_link_settings_test.dart
diff --git a/packages/devtools_app/test/screens/deep_link_vlidation/deep_links_screen_test.dart b/packages/devtools_app/test/screens/deep_link_vlidation/deep_links_screen_test.dart
new file mode 100644
index 0000000..6853581
--- /dev/null
+++ b/packages/devtools_app/test/screens/deep_link_vlidation/deep_links_screen_test.dart
@@ -0,0 +1,623 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/screens/deep_link_validation/deep_link_list_view.dart';
+import 'package:devtools_app/src/screens/deep_link_validation/deep_links_model.dart';
+import 'package:devtools_app/src/screens/deep_link_validation/project_root_selection/root_selector.dart';
+import 'package:devtools_app/src/screens/deep_link_validation/project_root_selection/select_project_view.dart';
+import 'package:devtools_app/src/screens/deep_link_validation/validation_details_view.dart';
+import 'package:devtools_app/src/shared/feature_flags.dart';
+import 'package:devtools_app_shared/service.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_shared/devtools_deeplink.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:dtd/dtd.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+
+import '../../test_infra/test_data/deep_link/fake_responses.dart';
+import '../../test_infra/utils/deep_links_utils.dart';
+
+final xcodeBuildOptions = XcodeBuildOptions.fromJson(
+  '''{"configurations":["debug", "release"],"targets":["runner","runnerTests"]}''',
+);
+
+void main() {
+  // ignore: avoid-redundant-async, false positive.
+  setUp(() async {
+    setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
+    setGlobal(
+      DevToolsEnvironmentParameters,
+      ExternalDevToolsEnvironmentParameters(),
+    );
+    setGlobal(PreferencesController, PreferencesController());
+    setGlobal(IdeTheme, IdeTheme());
+    setGlobal(NotificationService, NotificationService());
+
+    final mockDtdManager = MockDTDManager();
+    final rootUri1 = Uri.parse('file:///Users/me/package_root_1');
+    final rootUri2 = Uri.parse('file:///Users/me/package_root_2');
+    when(mockDtdManager.projectRoots()).thenAnswer((_) async {
+      return UriList(uris: [rootUri1, rootUri2]);
+    });
+    setGlobal(DTDManager, mockDtdManager);
+    FeatureFlags.deepLinkIosCheck = true;
+  });
+
+  late DeepLinksScreen screen;
+  late TestDeepLinksController deepLinksController;
+
+  const windowSize = Size(2560.0, 1338.0);
+
+  Future<void> pumpDeepLinkScreen(
+    WidgetTester tester, {
+    required DeepLinksController controller,
+  }) async {
+    await tester.pumpWidget(
+      wrapWithControllers(const DeepLinkPage(), deepLink: controller),
+    );
+    await tester.pump(const Duration(seconds: 1));
+    expect(find.byType(DeepLinkPage), findsOneWidget);
+  }
+
+  group('DeepLinkScreen', () {
+    setUp(() {
+      screen = DeepLinksScreen();
+      deepLinksController = TestDeepLinksController();
+    });
+
+    testWidgets('builds its tab', (WidgetTester tester) async {
+      await tester.pumpWidget(
+        wrapWithControllers(
+          Builder(builder: screen.buildTab),
+          deepLink: deepLinksController,
+        ),
+      );
+      expect(find.text('Deep Links'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('builds initial content', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.byType(SelectProjectView), findsOneWidget);
+      expect(find.byType(ProjectRootsDropdown), findsOneWidget);
+      expect(find.byType(ProjectRootTextField), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize(
+      'builds deeplink list page with no links',
+      windowSize,
+      (WidgetTester tester) async {
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = []
+          ..fakeIosDomains = [];
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(deepLinksController.pagePhase.value, PagePhase.noLinks);
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'builds deeplink list page with links',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController();
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+          ..fakeIosDomains = [defaultDomain];
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(deepLinksController.pagePhase.value, PagePhase.linksValidated);
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+        expect(find.byType(ValidationDetailView), findsNothing);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'builds deeplink list page with default ios and android configurations',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController();
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'profile', 'release'],
+            iosBuildOptions: XcodeBuildOptions.fromJson(
+              '''{"configurations":["debug", "release"],"targets":["runner","runnerTests"]}''',
+            ),
+          )
+          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+          ..fakeIosDomains = [defaultDomain];
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(deepLinksController.pagePhase.value, PagePhase.linksValidated);
+        expect(deepLinksController.selectedAndroidVariantIndex.value, 2);
+        expect(deepLinksController.selectedIosConfigurationIndex.value, 1);
+        expect(deepLinksController.selectedIosTargetIndex.value, 0);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'builds deeplink list page with split screen',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController();
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+          ..fakeIosDomains = [defaultDomain];
+
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        deepLinksController.autoSelectLink(TableViewType.domainView);
+        deepLinksController.displayOptionsNotifier.value = DisplayOptions(
+          showSplitScreen: true,
+        );
+
+        await tester.pumpAndSettle();
+
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+        expect(find.byType(ValidationDetailView), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'shows notification cards when there are domain errors',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController(
+          hasAndroidDomainErrors: true,
+          iosValidationResponse: iosValidationResponseWithError,
+        );
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+          ..fakeIosDomains = [defaultDomain];
+
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+        expect(find.byType(NotificationCard), findsOneWidget);
+      },
+    );
+    testWidgetsWithWindowSize(
+      'shows notification cards when there are path errors',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController();
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [
+            androidDeepLinkJson(defaultDomain, hasPathError: true),
+          ]
+          ..fakeIosDomains = [defaultDomain];
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+        expect(find.byType(NotificationCard), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'taps the action button in notification cards to go to the split screen',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController(
+          hasAndroidDomainErrors: true,
+          iosValidationResponse: iosValidationResponseWithError,
+        );
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+          ..fakeIosDomains = [defaultDomain];
+
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        await tester.tap(find.text('Fix domain'));
+        await tester.pumpAndSettle();
+
+        expect(find.byType(DeepLinkPage), findsOneWidget);
+        expect(find.byType(DeepLinkListView), findsOneWidget);
+        expect(find.byType(NotificationCard), findsNothing);
+        expect(find.byType(ValidationDetailView), findsOneWidget);
+        expect(find.text('Digital assets link file'), findsOneWidget);
+        expect(find.text('Apple-App-Site-Association file'), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize(
+      'Don\'t show domain errors when they are just warnings',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController(
+          iosValidationResponse: iosValidationResponseWithWarning,
+        );
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [
+            androidDeepLinkJson('www.domain1.com'),
+            androidDeepLinkJson('www.google.com'),
+          ]
+          ..fakeIosDomains = [defaultDomain];
+
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(find.text('www.domain1.com'), findsOneWidget);
+        expect(find.text('example.com'), findsOneWidget);
+        expect(find.text('www.google.com'), findsOneWidget);
+
+        await tester.tap(find.text('example.com'));
+        await tester.pumpAndSettle(const Duration(milliseconds: 500));
+
+        final domainErrors =
+            deepLinksController.selectedLink.value!.domainErrors;
+        expect(domainErrors.length, 0);
+      },
+    );
+
+    testWidgetsWithWindowSize('search links', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController();
+
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeAndroidDeepLinks = [defaultAndroidDeeplink]
+        ..fakeIosDomains = [
+          'www.domain1.com',
+          'www.domain2.com',
+          'www.google.com',
+        ];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.text('www.domain1.com'), findsOneWidget);
+      expect(find.text('www.domain2.com'), findsOneWidget);
+      expect(find.text('www.google.com'), findsOneWidget);
+
+      deepLinksController.searchContent = 'goo';
+
+      await tester.pumpAndSettle();
+
+      expect(find.text('www.domain1.com'), findsNothing);
+      expect(find.text('www.domain2.com'), findsNothing);
+      expect(find.text('www.google.com'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('filter links with os', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController();
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeAndroidDeepLinks = [
+          androidDeepLinkJson('www.domain1.com'),
+          androidDeepLinkJson('www.google.com'),
+        ]
+        ..fakeIosDomains = ['www.domain2.com', 'www.google.com'];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.text('www.domain1.com'), findsOneWidget);
+      expect(find.text('www.domain2.com'), findsOneWidget);
+      expect(find.text('www.google.com'), findsOneWidget);
+
+      // Only show Android links.
+      deepLinksController.updateDisplayOptions(removedFilter: FilterOption.ios);
+
+      await tester.pumpAndSettle();
+
+      expect(find.text('www.domain1.com'), findsOneWidget);
+      expect(find.text('www.domain2.com'), findsNothing);
+      expect(find.text('www.google.com'), findsOneWidget);
+
+      // Only show iOS links.
+      deepLinksController.updateDisplayOptions(
+        addedFilter: FilterOption.ios,
+        removedFilter: FilterOption.android,
+      );
+
+      await tester.pumpAndSettle();
+
+      expect(find.text('www.domain1.com'), findsNothing);
+      expect(find.text('www.domain2.com'), findsOneWidget);
+      expect(find.text('www.google.com'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize(
+      'filter links with validation result',
+      windowSize,
+      (WidgetTester tester) async {
+        final deepLinksController = TestDeepLinksController(
+          iosValidationResponse: iosValidationResponseWithError,
+        );
+
+        deepLinksController
+          ..selectedProject.value = FlutterProject(
+            path: '/abc',
+            androidVariants: ['debug', 'release'],
+            iosBuildOptions: xcodeBuildOptions,
+          )
+          ..fakeAndroidDeepLinks = [
+            androidDeepLinkJson('www.domain1.com'),
+            androidDeepLinkJson('www.google.com'),
+          ]
+          ..fakeIosDomains = [defaultDomain];
+
+        await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+        expect(find.text('www.domain1.com'), findsOneWidget);
+        expect(find.text('example.com'), findsOneWidget);
+        expect(find.text('www.google.com'), findsOneWidget);
+
+        // Only show links with domain error.
+        deepLinksController.updateDisplayOptions(
+          removedFilter: FilterOption.noIssue,
+        );
+
+        await tester.pumpAndSettle();
+        expect(find.text('www.domain1.com'), findsNothing);
+        expect(find.text('example.com'), findsOneWidget);
+        expect(find.text('www.google.com'), findsNothing);
+
+        // Only show links with no issue.
+        deepLinksController.updateDisplayOptions(
+          removedFilter: FilterOption.failedDomainCheck,
+        );
+        deepLinksController.updateDisplayOptions(
+          addedFilter: FilterOption.noIssue,
+        );
+
+        await tester.pumpAndSettle();
+
+        expect(find.text('www.domain1.com'), findsOneWidget);
+        expect(find.text('example.com'), findsNothing);
+        expect(find.text('www.google.com'), findsOneWidget);
+      },
+    );
+
+    testWidgetsWithWindowSize('domain errors are correct', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController(
+        iosValidationResponse: iosValidationResponseWithError,
+      );
+
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeAndroidDeepLinks = [
+          androidDeepLinkJson('www.domain1.com'),
+          androidDeepLinkJson('www.google.com'),
+        ]
+        ..fakeIosDomains = [defaultDomain];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.text('www.domain1.com'), findsOneWidget);
+      expect(find.text('example.com'), findsOneWidget);
+      expect(find.text('www.google.com'), findsOneWidget);
+
+      await tester.tap(find.text('example.com'));
+      await tester.pumpAndSettle(const Duration(milliseconds: 500));
+
+      final domainErrors = deepLinksController.selectedLink.value!.domainErrors;
+      expect(domainErrors.length, 3);
+      expect(domainErrors[0], IosDomainError.existence);
+      expect(domainErrors[1].title, IosDomainError.appIdentifier.title);
+      expect(
+        (domainErrors[2] as IosDomainError).subcheckErrors.single,
+        AASAfileFormatSubCheck.componentPercentEncodedFormat,
+      );
+    });
+
+    testWidgetsWithWindowSize('sort links', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController(
+        iosValidationResponse: iosValidationResponseWithError,
+      );
+
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeIosDomains = [defaultDomain, 'domain1.com', 'domain2.com'];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.text('domain1.com'), findsOneWidget);
+      expect(find.text('domain2.com'), findsOneWidget);
+      expect(find.text('example.com'), findsOneWidget);
+
+      // Sort with a-z.
+      deepLinksController.updateDisplayOptions(
+        domainSortingOption: SortingOption.aToZ,
+      );
+      await tester.pumpAndSettle();
+
+      var widgetACenter = tester.getCenter(find.text('domain1.com'));
+      var widgetBCenter = tester.getCenter(find.text('domain2.com'));
+      var widgetCCenter = tester.getCenter(find.text('example.com'));
+
+      expect(widgetACenter.dy < widgetBCenter.dy, true);
+      expect(widgetBCenter.dy < widgetCCenter.dy, true);
+
+      // Sort with z-a.
+      deepLinksController.updateDisplayOptions(
+        domainSortingOption: SortingOption.zToA,
+      );
+      await tester.pumpAndSettle();
+
+      widgetACenter = tester.getCenter(find.text('domain1.com'));
+      widgetBCenter = tester.getCenter(find.text('domain2.com'));
+      widgetCCenter = tester.getCenter(find.text('example.com'));
+
+      expect(widgetCCenter.dy < widgetBCenter.dy, true);
+      expect(widgetBCenter.dy < widgetACenter.dy, true);
+
+      // Sort with error on top. `example.com` is the one with error.
+      deepLinksController.updateDisplayOptions(
+        domainSortingOption: SortingOption.errorOnTop,
+      );
+      await tester.pumpAndSettle();
+
+      widgetACenter = tester.getCenter(find.text('domain1.com'));
+      widgetBCenter = tester.getCenter(find.text('domain2.com'));
+      widgetCCenter = tester.getCenter(find.text('example.com'));
+
+      expect(widgetCCenter.dy < widgetACenter.dy, true);
+      expect(widgetCCenter.dy < widgetBCenter.dy, true);
+    });
+
+    testWidgetsWithWindowSize('show scheme or missing scheme', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController();
+
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeAndroidDeepLinks = [
+          androidDeepLinkJson('www.domain1.com'),
+          androidDeepLinkJson('www.domain2.com', scheme: null),
+        ];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      expect(find.text('www.domain1.com'), findsOneWidget);
+      expect(find.text('www.domain2.com'), findsOneWidget);
+
+      expect(find.text('missing scheme'), findsOneWidget);
+      expect(find.text('http'), findsOneWidget);
+    });
+
+    testWidgetsWithWindowSize('path view', windowSize, (
+      WidgetTester tester,
+    ) async {
+      final deepLinksController = TestDeepLinksController();
+
+      deepLinksController
+        ..selectedProject.value = FlutterProject(
+          path: '/abc',
+          androidVariants: ['debug', 'release'],
+          iosBuildOptions: xcodeBuildOptions,
+        )
+        ..fakeAndroidDeepLinks = [
+          androidDeepLinkJson('www.domain1.com', path: '/path1'),
+          androidDeepLinkJson(
+            'www.domain2.com',
+            path: '/path2',
+            hasPathError: true,
+          ),
+          androidDeepLinkJson('www.domain3.com', path: '/path3'),
+        ]
+        ..fakeIosDomains = [defaultDomain];
+
+      await pumpDeepLinkScreen(tester, controller: deepLinksController);
+
+      await tester.tap(find.text('Path view'));
+      await tester.pumpAndSettle(const Duration(milliseconds: 500));
+
+      expect(find.text('/path1'), findsOneWidget);
+      expect(find.text('/path2'), findsOneWidget);
+      expect(find.text('/path3'), findsOneWidget);
+      expect(find.text('/ios-path1'), findsOneWidget);
+      expect(find.text('NOT /ios-path2'), findsOneWidget);
+
+      // Only show links with path error.
+      deepLinksController.updateDisplayOptions(
+        removedFilter: FilterOption.noIssue,
+      );
+
+      await tester.pumpAndSettle();
+
+      expect(find.text('/path1'), findsNothing);
+      expect(find.text('/path2'), findsOneWidget);
+      expect(find.text('/path3'), findsNothing);
+      expect(find.text('/ios-path1'), findsNothing);
+      expect(find.text('NOT /ios-path2'), findsNothing);
+
+      // Only show links with no issue.
+      deepLinksController.updateDisplayOptions(
+        removedFilter: FilterOption.failedPathCheck,
+      );
+      deepLinksController.updateDisplayOptions(
+        addedFilter: FilterOption.noIssue,
+      );
+
+      await tester.pumpAndSettle();
+
+      expect(find.text('/path1'), findsOneWidget);
+      expect(find.text('/path2'), findsNothing);
+      expect(find.text('/path3'), findsOneWidget);
+      expect(find.text('/ios-path1'), findsOneWidget);
+      expect(find.text('NOT /ios-path2'), findsOneWidget);
+    });
+  });
+}
diff --git a/packages/devtools_app/test/deep_link_vlidation/select_project_view_test.dart b/packages/devtools_app/test/screens/deep_link_vlidation/select_project_view_test.dart
similarity index 77%
rename from packages/devtools_app/test/deep_link_vlidation/select_project_view_test.dart
rename to packages/devtools_app/test/screens/deep_link_vlidation/select_project_view_test.dart
index b31b4fa..37d6f25 100644
--- a/packages/devtools_app/test/deep_link_vlidation/select_project_view_test.dart
+++ b/packages/devtools_app/test/screens/deep_link_vlidation/select_project_view_test.dart
@@ -16,7 +16,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/mockito.dart';
 
-import '../test_infra/utils/deep_links_utils.dart';
+import '../../test_infra/utils/deep_links_utils.dart';
 
 void main() {
   // ignore: avoid-redundant-async, false positive.
@@ -59,38 +59,36 @@
       deepLinksController = TestDeepLinksController();
     });
 
-    testWidgetsWithWindowSize(
-      'builds content',
-      windowSize,
-      (WidgetTester tester) async {
-        await pumpSelectProjectView(tester);
-        expect(
-          find.textContaining('Select a local flutter project to check'),
-          findsOneWidget,
-        );
-        expect(find.byType(ProjectRootsDropdown), findsOneWidget);
-        expect(
-          find.textContaining('Don\'t see your project in the list?'),
-          findsOneWidget,
-        );
-        expect(find.byType(ProjectRootTextField), findsOneWidget);
+    testWidgetsWithWindowSize('builds content', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await pumpSelectProjectView(tester);
+      expect(
+        find.textContaining('Select a local flutter project to check'),
+        findsOneWidget,
+      );
+      expect(find.byType(ProjectRootsDropdown), findsOneWidget);
+      expect(
+        find.textContaining('Don\'t see your project in the list?'),
+        findsOneWidget,
+      );
+      expect(find.byType(ProjectRootTextField), findsOneWidget);
 
-        expect(
-          find.descendant(
-            of: find.byType(ProjectRootsDropdown),
-            matching: find.byType(Column),
-          ),
-          findsNothing,
-        );
-        expect(
-          find.descendant(
-            of: find.byType(ProjectRootTextField),
-            matching: find.byType(Column),
-          ),
-          findsNothing,
-        );
-      },
-    );
+      expect(
+        find.descendant(
+          of: find.byType(ProjectRootsDropdown),
+          matching: find.byType(Column),
+        ),
+        findsNothing,
+      );
+      expect(
+        find.descendant(
+          of: find.byType(ProjectRootTextField),
+          matching: find.byType(Column),
+        ),
+        findsNothing,
+      );
+    });
 
     testWidgetsWithWindowSize(
       'builds content for narrow screen',
diff --git a/packages/devtools_app/test/deep_link_vlidation/universal_link_settings_test.dart b/packages/devtools_app/test/screens/deep_link_vlidation/universal_link_settings_test.dart
similarity index 100%
rename from packages/devtools_app/test/deep_link_vlidation/universal_link_settings_test.dart
rename to packages/devtools_app/test/screens/deep_link_vlidation/universal_link_settings_test.dart
diff --git a/packages/devtools_app/test/screens/inspector/diagnostics_test.dart b/packages/devtools_app/test/screens/inspector/diagnostics_test.dart
new file mode 100644
index 0000000..f950279
--- /dev/null
+++ b/packages/devtools_app/test/screens/inspector/diagnostics_test.dart
@@ -0,0 +1,302 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/shared/ui/utils.dart';
+import 'package:devtools_app_shared/ui.dart';
+import 'package:devtools_app_shared/utils.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:devtools_test/helpers.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  group('DiagnosticsNodeDescription', () {
+    final renderObjectJson = jsonDecode('''
+        {
+          "properties": [
+            {
+              "description": "horizontal",
+              "name": "direction"
+            },
+            {
+              "description": "start",
+              "name": "mainAxisAlignment"
+            },
+            {
+              "description": "max",
+              "name": "mainAxisSize"
+            },
+            {
+              "description": "center",
+              "name": "crossAxisAlignment"
+            },
+            {
+              "description": "ltr",
+              "name": "textDirection"
+            },
+            {
+              "description": "down",
+              "name": "verticalDirection"
+            }
+          ]
+        }
+      ''');
+    setUp(() {
+      setGlobal(
+        DevToolsEnvironmentParameters,
+        ExternalDevToolsEnvironmentParameters(),
+      );
+      setGlobal(PreferencesController, PreferencesController());
+      setGlobal(IdeTheme, IdeTheme());
+      setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());
+    });
+
+    group('hover eval', () {
+      final nodeJson = <String, Object?>{
+        'widgetRuntimeType': 'Row',
+        'renderObject': renderObjectJson,
+        'hasChildren': false,
+        'children': [],
+      };
+      final inspectorService = MockInspectorObjectGroupBase();
+      final diagnostic = RemoteDiagnosticsNode(
+        nodeJson,
+        inspectorService,
+        false,
+        null,
+      );
+      late DiagnosticsNodeDescription diagnosticsNodeDescription;
+
+      setUp(() {
+        preferences.inspector.setHoverEvalMode(true);
+        diagnosticsNodeDescription = DiagnosticsNodeDescription(diagnostic);
+      });
+
+      testWidgets('can be enabled from preferences', (
+        WidgetTester tester,
+      ) async {
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final hoverCardTooltip =
+            tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
+        expect(hoverCardTooltip.enabled(), true);
+      });
+
+      testWidgets('can be disabled from preferences', (
+        WidgetTester tester,
+      ) async {
+        preferences.inspector.setHoverEvalMode(false);
+
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final hoverCardTooltip =
+            tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
+        expect(hoverCardTooltip.enabled(), false);
+      });
+
+      testWidgets('disabled when inspector service not set', (
+        WidgetTester tester,
+      ) async {
+        final diagnosticWithoutService = RemoteDiagnosticsNode(
+          nodeJson,
+          null,
+          false,
+          null,
+        );
+        diagnosticsNodeDescription = DiagnosticsNodeDescription(
+          diagnosticWithoutService,
+        );
+
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final hoverCardTooltip =
+            tester.widget(find.byType(HoverCardTooltip)) as HoverCardTooltip;
+        expect(hoverCardTooltip.enabled(), false);
+      });
+    });
+
+    group('approximateNodeWidth', () {
+      const epsilon = 7.0;
+      testWidgets('property diagnostics node with name and description', (
+        WidgetTester tester,
+      ) async {
+        final nodeJson = <String, Object?>{
+          'widgetRuntimeType': 'Row',
+          'renderObject': renderObjectJson,
+          'hasChildren': false,
+          'children': [],
+          'description':
+              'this is a showname description, which will show up after the name',
+          'showName': true,
+          'name': 'THE NAME to be shown',
+        };
+        final diagnosticWithoutService = RemoteDiagnosticsNode(
+          nodeJson,
+          null,
+          true,
+          null,
+        );
+        final diagnosticsNodeDescription = DiagnosticsNodeDescription(
+          diagnosticWithoutService,
+        );
+
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final approximatedWidth =
+            DiagnosticsNodeDescription.approximateNodeWidth(
+              diagnosticWithoutService,
+            );
+
+        final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
+        // There are many rich texts, containg the name, and description.
+        final allRichTexts = find
+            .descendant(
+              of: diagnosticsNodeFind,
+              matching: find.byType(RichText),
+            )
+            .evaluate()
+            .map((e) => e.widget as RichText);
+        final measuredWidthOfAllRichTexts = allRichTexts.fold<double>(
+          0,
+          (previousValue, richText) =>
+              previousValue + calculateTextSpanWidth(richText.text as TextSpan),
+        );
+        expect(
+          approximatedWidth,
+          moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
+        );
+      });
+
+      testWidgets('diagnostics node with icon and description', (
+        WidgetTester tester,
+      ) async {
+        final nodeJson = <String, Object?>{
+          'widgetRuntimeType': 'Row',
+          'renderObject': renderObjectJson,
+          'hasChildren': false,
+          'description': 'This is the description',
+          'children': [],
+          'showName': false,
+        };
+        final diagnosticWithoutService = RemoteDiagnosticsNode(
+          nodeJson,
+          null,
+          false,
+          null,
+        );
+        final diagnosticsNodeDescription = DiagnosticsNodeDescription(
+          diagnosticWithoutService,
+        );
+
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final approximatedTextWidth =
+            DiagnosticsNodeDescription.approximateNodeWidth(
+              diagnosticWithoutService,
+            );
+
+        final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
+        // The icon is part of the clickable width, so we include it.
+        final measuredIconWidth =
+            tester
+                .getSize(
+                  find.descendant(
+                    of: diagnosticsNodeFind,
+                    matching: find.byType(AssetImageIcon),
+                  ),
+                )
+                .width;
+
+        // There is only one rich text widget, containing the description.
+        final richTextWidget =
+            find
+                    .descendant(
+                      of: diagnosticsNodeFind,
+                      matching: find.byType(RichText),
+                    )
+                    .first
+                    .evaluate()
+                    .first
+                    .widget
+                as RichText;
+        final measuredTextWidth = calculateTextSpanWidth(
+          richTextWidget.text as TextSpan,
+        );
+
+        expect(
+          approximatedTextWidth,
+          moreOrLessEquals(
+            measuredTextWidth + measuredIconWidth,
+            epsilon: epsilon,
+          ),
+        );
+      });
+
+      testWidgets('error node with different fontSize', (
+        WidgetTester tester,
+      ) async {
+        // Nodes with normal levels default to using the default fontSize, so
+        // using an error level node allows us to test different font sizes.
+        final nodeJson = <String, Object?>{
+          'widgetRuntimeType': 'Row',
+          'renderObject': renderObjectJson,
+          'hasChildren': false,
+          'children': [],
+          'description':
+              'this is a showname description, which will show up after the name',
+          'showName': true,
+          'name': 'THE NAME to be shown',
+          'level': 'error',
+        };
+        final diagnosticWithoutService = RemoteDiagnosticsNode(
+          nodeJson,
+          null,
+          false,
+          null,
+        );
+
+        //Use a textStyle that is much larger than the normal style
+        const textStyle = TextStyle(fontSize: 24.0, fontFamily: 'Roboto');
+        final diagnosticsNodeDescription = DiagnosticsNodeDescription(
+          diagnosticWithoutService,
+          style: textStyle,
+        );
+
+        await tester.pumpWidget(wrap(diagnosticsNodeDescription));
+
+        final approximatedWidth =
+            DiagnosticsNodeDescription.approximateNodeWidth(
+              diagnosticWithoutService,
+            );
+
+        final diagnosticsNodeFind = find.byType(DiagnosticsNodeDescription);
+        // There are many rich texts, containg the name, and description.
+        final allRichTexts = find
+            .descendant(
+              of: diagnosticsNodeFind,
+              matching: find.byType(RichText),
+            )
+            .evaluate()
+            .map((e) => e.widget as RichText);
+
+        final measuredWidthOfAllRichTexts = allRichTexts.fold<double>(0, (
+          previousValue,
+          richText,
+        ) {
+          final originalTextSpan = richText.text as TextSpan;
+
+          return previousValue + calculateTextSpanWidth(originalTextSpan);
+        });
+
+        expect(
+          approximatedWidth,
+          moreOrLessEquals(measuredWidthOfAllRichTexts, epsilon: epsilon),
+        );
+      });
+    });
+  });
+}
diff --git a/packages/devtools_app/test/inspector/inspector_error_navigator_test.dart b/packages/devtools_app/test/screens/inspector/inspector_error_navigator_test.dart
similarity index 96%
rename from packages/devtools_app/test/inspector/inspector_error_navigator_test.dart
rename to packages/devtools_app/test/screens/inspector/inspector_error_navigator_test.dart
index 420f0d5..557f6c0 100644
--- a/packages/devtools_app/test/inspector/inspector_error_navigator_test.dart
+++ b/packages/devtools_app/test/screens/inspector/inspector_error_navigator_test.dart
@@ -125,10 +125,7 @@
         count,
         (index) => MapEntry(
           'error-$index',
-          InspectableWidgetError(
-            'Error $index',
-            'error-$index',
-          ),
+          InspectableWidgetError('Error $index', 'error-$index'),
         ),
       ),
     );
diff --git a/packages/devtools_app/test/inspector/inspector_integration_test.dart b/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
similarity index 63%
rename from packages/devtools_app/test/inspector/inspector_integration_test.dart
rename to packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
index ba91ce0..e070bc7 100644
--- a/packages/devtools_app/test/inspector/inspector_integration_test.dart
+++ b/packages/devtools_app/test/screens/inspector/inspector_integration_test.dart
@@ -7,9 +7,9 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
-import '../test_infra/flutter_test_driver.dart' show FlutterRunConfiguration;
-import '../test_infra/flutter_test_environment.dart';
-import '../test_infra/matchers/matchers.dart';
+import '../../test_infra/flutter_test_driver.dart' show FlutterRunConfiguration;
+import '../../test_infra/flutter_test_environment.dart';
+import '../../test_infra/matchers/matchers.dart';
 
 // This is a bit conservative to ensure we do not get flakes due to
 // slow interactions with the VM Service. This delay could likely be
@@ -50,106 +50,103 @@
   });
 
   group('screenshot tests', () {
-    testWidgetsWithWindowSize(
-      'navigation',
-      windowSize,
-      (WidgetTester tester) async {
-        await env.setupEnvironment();
-        expect(serviceConnection.serviceManager.service, equals(env.service));
-        expect(serviceConnection.serviceManager.isolateManager, isNotNull);
+    testWidgetsWithWindowSize('navigation', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await env.setupEnvironment();
+      expect(serviceConnection.serviceManager.service, equals(env.service));
+      expect(serviceConnection.serviceManager.isolateManager, isNotNull);
 
-        final screen = InspectorScreen();
-        await tester.pumpWidget(
-          wrapWithInspectorControllers(Builder(builder: screen.build)),
-        );
-        await tester.pump(const Duration(seconds: 1));
-        final InspectorScreenBodyState state =
-            tester.state(find.byType(InspectorScreenBody));
-        final controller = state.controller;
-        while (!controller.flutterAppFrameReady) {
-          await controller.maybeLoadUI();
-          await tester.pumpAndSettle();
-        }
-        // Give time for the initial animation to complete.
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_initial_load.png',
-          ),
-        );
+      final screen = InspectorScreen();
+      await tester.pumpWidget(
+        wrapWithInspectorControllers(Builder(builder: screen.build)),
+      );
+      await tester.pump(const Duration(seconds: 1));
+      final InspectorScreenBodyState state = tester.state(
+        find.byType(InspectorScreenBody),
+      );
+      final controller = state.controller;
+      while (!controller.flutterAppFrameReady) {
+        await controller.maybeLoadUI();
+        await tester.pumpAndSettle();
+      }
+      // Give time for the initial animation to complete.
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_initial_load.png',
+        ),
+      );
 
-        // Click on the Center widget (row index #5)
-        await tester.tap(find.richText('Center'));
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_select_center.png',
-          ),
-        );
+      // Click on the Center widget (row index #5)
+      await tester.tap(find.richText('Center'));
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_select_center.png',
+        ),
+      );
 
-        // Select the details tree.
-        await tester.tap(
-          find.text(
-            InspectorDetailsViewType.widgetDetailsTree.key,
-          ),
-        );
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_select_center_details_tree.png',
-          ),
-        );
+      // Select the details tree.
+      await tester.tap(
+        find.text(InspectorDetailsViewType.widgetDetailsTree.key),
+      );
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_select_center_details_tree.png',
+        ),
+      );
 
-        // Select the RichText row.
-        await tester.tap(find.richText('RichText'));
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_richtext_selected.png',
-          ),
-        );
+      // Select the RichText row.
+      await tester.tap(find.richText('RichText'));
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_richtext_selected.png',
+        ),
+      );
 
-        // Test hovering over the icon shown when a property has its default
-        // value.
-        // TODO(jacobr): support tooltips in the Flutter version of the inspector.
-        // https://github.com/flutter/devtools/issues/2570.
-        // For example, verify that the tooltip hovering over the default value
-        // icons is "Default value".
-        // Test selecting a widget.
+      // Test hovering over the icon shown when a property has its default
+      // value.
+      // TODO(jacobr): support tooltips in the Flutter version of the inspector.
+      // https://github.com/flutter/devtools/issues/2570.
+      // For example, verify that the tooltip hovering over the default value
+      // icons is "Default value".
+      // Test selecting a widget.
 
-        // Two 'Scaffold's: a breadcrumb and an actual tree item
-        expect(find.richText('Scaffold'), findsNWidgets(2));
-        // select Scaffold widget in summary tree.
-        await tester.tap(find.richText('Scaffold').last);
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        // This tree is huge. If there is a change to package:flutter it may
-        // change. If this happens don't panic and rebaseline the golden.
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_scaffold_selected.png',
-          ),
-        );
+      // Two 'Scaffold's: a breadcrumb and an actual tree item
+      expect(find.richText('Scaffold'), findsNWidgets(2));
+      // select Scaffold widget in summary tree.
+      await tester.tap(find.richText('Scaffold').last);
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      // This tree is huge. If there is a change to package:flutter it may
+      // change. If this happens don't panic and rebaseline the golden.
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_scaffold_selected.png',
+        ),
+      );
 
-        // The important thing about this is that the details tree should scroll
-        // instead of re-rooting as the selected row is already visible in the
-        // details tree.
-        await tester.tap(find.richText('AnimatedPhysicalModel'));
-        await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_animated_physical_model_selected.png',
-          ),
-        );
+      // The important thing about this is that the details tree should scroll
+      // instead of re-rooting as the selected row is already visible in the
+      // details tree.
+      await tester.tap(find.richText('AnimatedPhysicalModel'));
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_animated_physical_model_selected.png',
+        ),
+      );
 
-        await env.tearDownEnvironment();
-      },
-    );
+      await env.tearDownEnvironment();
+    });
 
     // TODO(jacobr): convert these tests to screenshot tests like the initial
     // state test.
@@ -328,9 +325,9 @@
       await env.tearDownEnvironment();
     });
     */
-// TODO(jacobr): uncomment out the hotRestart tests once
-// https://github.com/flutter/devtools/issues/337 is fixed.
-/*
+    // TODO(jacobr): uncomment out the hotRestart tests once
+    // https://github.com/flutter/devtools/issues/337 is fixed.
+    /*
     test('hotRestart', () async {
       await env.setupEnvironment();
 
@@ -404,55 +401,54 @@
   });
 
   group('widget errors', () {
-    testWidgetsWithWindowSize(
-      'show navigator and error labels',
-      windowSize,
-      (WidgetTester tester) async {
-        await env.setupEnvironment(
-          config: const FlutterRunConfiguration(
-            withDebugger: true,
-            entryScript: 'lib/overflow_errors.dart',
-          ),
-        );
-        expect(serviceConnection.serviceManager.service, equals(env.service));
-        expect(serviceConnection.serviceManager.isolateManager, isNotNull);
+    testWidgetsWithWindowSize('show navigator and error labels', windowSize, (
+      WidgetTester tester,
+    ) async {
+      await env.setupEnvironment(
+        config: const FlutterRunConfiguration(
+          withDebugger: true,
+          entryScript: 'lib/overflow_errors.dart',
+        ),
+      );
+      expect(serviceConnection.serviceManager.service, equals(env.service));
+      expect(serviceConnection.serviceManager.isolateManager, isNotNull);
 
-        final screen = InspectorScreen();
-        await tester.pumpWidget(
-          wrapWithInspectorControllers(Builder(builder: screen.build)),
-        );
-        await tester.pumpAndSettle(const Duration(seconds: 1));
-        final InspectorScreenBodyState state =
-            tester.state(find.byType(InspectorScreenBody));
-        final controller = state.controller;
-        while (!controller.flutterAppFrameReady) {
-          await controller.maybeLoadUI();
-          await tester.pumpAndSettle();
-        }
-        await env.flutter!.hotReload();
-        // Give time for the initial animation to complete.
+      final screen = InspectorScreen();
+      await tester.pumpWidget(
+        wrapWithInspectorControllers(Builder(builder: screen.build)),
+      );
+      await tester.pumpAndSettle(const Duration(seconds: 1));
+      final InspectorScreenBodyState state = tester.state(
+        find.byType(InspectorScreenBody),
+      );
+      final controller = state.controller;
+      while (!controller.flutterAppFrameReady) {
+        await controller.maybeLoadUI();
+        await tester.pumpAndSettle();
+      }
+      await env.flutter!.hotReload();
+      // Give time for the initial animation to complete.
+      await tester.pumpAndSettle(inspectorChangeSettleTime);
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_errors_1_initial_load.png',
+        ),
+      );
+
+      // Navigate so one of the errors is selected.
+      for (var i = 0; i < 2; i++) {
+        await tester.tap(find.byIcon(Icons.keyboard_arrow_down));
         await tester.pumpAndSettle(inspectorChangeSettleTime);
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_errors_1_initial_load.png',
-          ),
-        );
+      }
+      await expectLater(
+        find.byType(InspectorScreenBody),
+        matchesDevToolsGolden(
+          '../../test_infra/goldens/integration_inspector_errors_2_error_selected.png',
+        ),
+      );
 
-        // Navigate so one of the errors is selected.
-        for (var i = 0; i < 2; i++) {
-          await tester.tap(find.byIcon(Icons.keyboard_arrow_down));
-          await tester.pumpAndSettle(inspectorChangeSettleTime);
-        }
-        await expectLater(
-          find.byType(InspectorScreenBody),
-          matchesDevToolsGolden(
-            '../test_infra/goldens/integration_inspector_errors_2_error_selected.png',
-          ),
-        );
-
-        await env.tearDownEnvironment();
-      },
-    );
+      await env.tearDownEnvironment();
+    });
   });
 }
diff --git a/packages/devtools_app/test/inspector/inspector_screen_test.dart b/packages/devtools_app/test/screens/inspector/inspector_screen_test.dart
similarity index 64%
rename from packages/devtools_app/test/inspector/inspector_screen_test.dart
rename to packages/devtools_app/test/screens/inspector/inspector_screen_test.dart
index f00ca9f..9dcb8d2 100644
--- a/packages/devtools_app/test/inspector/inspector_screen_test.dart
+++ b/packages/devtools_app/test/screens/inspector/inspector_screen_test.dart
@@ -20,7 +20,7 @@
 import 'package:flutter_test/flutter_test.dart' hide Fake;
 import 'package:mockito/mockito.dart';
 
-import '../test_infra/flutter_test_storage.dart';
+import '../../test_infra/flutter_test_storage.dart';
 
 void main() {
   final screen = InspectorScreen();
@@ -75,12 +75,15 @@
     await fakeExtensionManager.fakeAddServiceExtension(
       extensions.toggleOnDeviceWidgetInspector.extension,
     );
-    await fakeExtensionManager
-        .fakeAddServiceExtension(extensions.toggleSelectWidgetMode.extension);
-    await fakeExtensionManager
-        .fakeAddServiceExtension(extensions.enableOnDeviceInspector.extension);
-    await fakeExtensionManager
-        .fakeAddServiceExtension(extensions.debugPaint.extension);
+    await fakeExtensionManager.fakeAddServiceExtension(
+      extensions.toggleSelectWidgetMode.extension,
+    );
+    await fakeExtensionManager.fakeAddServiceExtension(
+      extensions.enableOnDeviceInspector.extension,
+    );
+    await fakeExtensionManager.fakeAddServiceExtension(
+      extensions.debugPaint.extension,
+    );
     await fakeExtensionManager.fakeFrame();
   }
 
@@ -95,15 +98,13 @@
     fakeExtensionManager.fakeFrame();
   }
 
-  testWidgetsWithWindowSize(
-    'builds its tab',
-    windowSize,
-    (WidgetTester tester) async {
-      await tester.pumpWidget(buildInspectorScreen());
-      await tester.pumpAndSettle();
-      expect(find.byType(InspectorScreenBody), findsOneWidget);
-    },
-  );
+  testWidgetsWithWindowSize('builds its tab', windowSize, (
+    WidgetTester tester,
+  ) async {
+    await tester.pumpWidget(buildInspectorScreen());
+    await tester.pumpAndSettle();
+    expect(find.byType(InspectorScreenBody), findsOneWidget);
+  });
 
   group('Widget Errors', () {
     // Display of error navigator/indicators is tested by a golden in
@@ -119,24 +120,22 @@
     );
   });
 
-  testWidgetsWithWindowSize(
-    'builds with no data',
-    windowSize,
-    (WidgetTester tester) async {
-      // Make sure the window is wide enough to display description text.
+  testWidgetsWithWindowSize('builds with no data', windowSize, (
+    WidgetTester tester,
+  ) async {
+    // Make sure the window is wide enough to display description text.
 
-      await tester.pumpWidget(buildInspectorScreen());
-      expect(find.byType(InspectorScreenBody), findsOneWidget);
-      expect(find.byTooltip('Refresh Tree'), findsOneWidget);
-      expect(find.text(extensions.debugPaint.title), findsOneWidget);
-      // Make sure there is not an overflow if the window is narrow.
-      // TODO(jacobr): determine why there are overflows in the test environment
-      // but not on the actual device for this cae.
-      // await setWindowSize(const Size(1000.0, 1200.0));
-      // Verify that description text is no-longer shown.
-      // expect(find.text(extensions.debugPaint.description), findsOneWidget);
-    },
-  );
+    await tester.pumpWidget(buildInspectorScreen());
+    expect(find.byType(InspectorScreenBody), findsOneWidget);
+    expect(find.byTooltip('Refresh Tree'), findsOneWidget);
+    expect(find.text(extensions.debugPaint.title), findsOneWidget);
+    // Make sure there is not an overflow if the window is narrow.
+    // TODO(jacobr): determine why there are overflows in the test environment
+    // but not on the actual device for this cae.
+    // await setWindowSize(const Size(1000.0, 1200.0));
+    // Verify that description text is no-longer shown.
+    // expect(find.text(extensions.debugPaint.description), findsOneWidget);
+  });
 
   testWidgetsWithWindowSize(
     'Test toggling service extension buttons',
@@ -144,21 +143,24 @@
     (WidgetTester tester) async {
       await mockExtensions();
       expect(
-        fakeExtensionManager
-            .extensionValueOnDevice[extensions.debugPaint.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .debugPaint
+            .extension],
         isFalse,
       );
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleOnDeviceWidgetInspector.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleOnDeviceWidgetInspector
+            .extension],
         isTrue,
       );
 
       await tester.pumpWidget(buildInspectorScreen());
 
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleSelectWidgetMode.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleSelectWidgetMode
+            .extension],
         isTrue,
       );
 
@@ -172,28 +174,32 @@
       await tester.pump();
       await tester.tap(find.text(extensions.toggleSelectWidgetMode.title));
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleSelectWidgetMode.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleSelectWidgetMode
+            .extension],
         isFalse,
       );
       // Verify the other service extension's state hasn't changed.
       expect(
-        fakeExtensionManager
-            .extensionValueOnDevice[extensions.debugPaint.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .debugPaint
+            .extension],
         isFalse,
       );
 
       await tester.tap(find.text(extensions.toggleSelectWidgetMode.title));
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleSelectWidgetMode.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleSelectWidgetMode
+            .extension],
         isTrue,
       );
 
       await tester.tap(find.text(extensions.debugPaint.title));
       expect(
-        fakeExtensionManager
-            .extensionValueOnDevice[extensions.debugPaint.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .debugPaint
+            .extension],
         isTrue,
       );
     },
@@ -205,13 +211,15 @@
     (WidgetTester tester) async {
       mockNoExtensionsAvailable();
       expect(
-        fakeExtensionManager
-            .extensionValueOnDevice[extensions.debugPaint.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .debugPaint
+            .extension],
         isFalse,
       );
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleOnDeviceWidgetInspector.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleOnDeviceWidgetInspector
+            .extension],
         isTrue,
       );
 
@@ -225,20 +233,24 @@
       expect(find.text(extensions.debugPaint.title), findsOneWidget);
       await tester.pump();
 
-      await tester
-          .tap(find.text(extensions.toggleOnDeviceWidgetInspector.title));
+      await tester.tap(
+        find.text(extensions.toggleOnDeviceWidgetInspector.title),
+      );
       // Verify the service extension state has not changed.
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleOnDeviceWidgetInspector.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleOnDeviceWidgetInspector
+            .extension],
         isTrue,
       );
-      await tester
-          .tap(find.text(extensions.toggleOnDeviceWidgetInspector.title));
+      await tester.tap(
+        find.text(extensions.toggleOnDeviceWidgetInspector.title),
+      );
       // Verify the service extension state has not changed.
       expect(
-        fakeExtensionManager.extensionValueOnDevice[
-            extensions.toggleOnDeviceWidgetInspector.extension],
+        fakeExtensionManager.extensionValueOnDevice[extensions
+            .toggleOnDeviceWidgetInspector
+            .extension],
         isTrue,
       );
 
@@ -248,8 +260,7 @@
   );
 
   group('LayoutDetailsTab', () {
-    final renderObjectJson = jsonDecode(
-      '''
+    final renderObjectJson = jsonDecode('''
         {
           "properties": [
             {
@@ -278,8 +289,7 @@
             }
           ]
         }
-      ''',
-    );
+      ''');
     final diagnostic = RemoteDiagnosticsNode(
       <String, Object?>{
         'widgetRuntimeType': 'Row',
@@ -299,11 +309,7 @@
         final controller = TestInspectorController()..setSelectedNode(treeNode);
         await tester.pumpWidget(
           MaterialApp(
-            home: Scaffold(
-              body: LayoutExplorerTab(
-                controller: controller,
-              ),
-            ),
+            home: Scaffold(body: LayoutExplorerTab(controller: controller)),
           ),
         );
         expect(find.byType(FlexLayoutExplorerWidget), findsOneWidget);
@@ -317,11 +323,7 @@
         final controller = TestInspectorController();
         await tester.pumpWidget(
           MaterialApp(
-            home: Scaffold(
-              body: LayoutExplorerTab(
-                controller: controller,
-              ),
-            ),
+            home: Scaffold(body: LayoutExplorerTab(controller: controller)),
           ),
         );
         expect(find.byType(FlexLayoutExplorerWidget), findsNothing);
@@ -332,46 +334,40 @@
     );
   });
 
-  group(
-    'FlutterInspectorSettingsDialog',
-    () {
-      const startingHoverEvalModeValue = false;
+  group('FlutterInspectorSettingsDialog', () {
+    const startingHoverEvalModeValue = false;
 
-      setUp(() {
-        preferences.inspector.setHoverEvalMode(startingHoverEvalModeValue);
-      });
+    setUp(() {
+      preferences.inspector.setHoverEvalMode(startingHoverEvalModeValue);
+    });
 
-      testWidgetsWithWindowSize(
-        'can update hover inspection setting',
-        windowSize,
-        (WidgetTester tester) async {
-          await tester.pumpWidget(buildInspectorScreen());
+    testWidgetsWithWindowSize(
+      'can update hover inspection setting',
+      windowSize,
+      (WidgetTester tester) async {
+        await tester.pumpWidget(buildInspectorScreen());
 
-          await tester.tap(find.byType(SettingsOutlinedButton));
-          await tester.pumpAndSettle();
-          expect(
-            find.byType(FlutterInspectorSettingsDialog),
-            findsOneWidget,
-          );
+        await tester.tap(find.byType(SettingsOutlinedButton));
+        await tester.pumpAndSettle();
+        expect(find.byType(FlutterInspectorSettingsDialog), findsOneWidget);
 
-          final hoverCheckBoxSetting = find.ancestor(
-            of: find.richTextContaining('Enable hover inspection'),
-            matching: find.byType(CheckboxSetting),
-          );
-          final hoverModeCheckBox = find.descendant(
-            of: hoverCheckBoxSetting,
-            matching: find.byType(NotifierCheckbox),
-          );
-          await tester.tap(hoverModeCheckBox);
-          await tester.pumpAndSettle();
-          expect(
-            preferences.inspector.hoverEvalModeEnabled.value,
-            !startingHoverEvalModeValue,
-          );
-        },
-      );
-    },
-  );
+        final hoverCheckBoxSetting = find.ancestor(
+          of: find.richTextContaining('Enable hover inspection'),
+          matching: find.byType(CheckboxSetting),
+        );
+        final hoverModeCheckBox = find.descendant(
+          of: hoverCheckBoxSetting,
+          matching: find.byType(NotifierCheckbox),
+        );
+        await tester.tap(hoverModeCheckBox);
+        await tester.pumpAndSettle();
+        expect(
+          preferences.inspector.hoverEvalModeEnabled.value,
+          !startingHoverEvalModeValue,
+        );
+      },
+    );
+  });
 
   // TODO(jacobr): add screenshot tests that connect to a test application
   // in the same way the inspector_controller test does today and take golden
diff --git a/packages/devtools_app/test/inspector/inspector_tree_test.dart b/packages/devtools_app/test/screens/inspector/inspector_tree_test.dart
similarity index 69%
rename from packages/devtools_app/test/inspector/inspector_tree_test.dart
rename to packages/devtools_app/test/screens/inspector/inspector_tree_test.dart
index f6a3f80..d9a6d5c 100644
--- a/packages/devtools_app/test/inspector/inspector_tree_test.dart
+++ b/packages/devtools_app/test/screens/inspector/inspector_tree_test.dart
@@ -70,37 +70,37 @@
   }
 
   group('InspectorTreeController', () {
-    testWidgets(
-      'Row with negative index regression test',
-      (WidgetTester tester) async {
-        final treeController = InspectorTreeController()
-          ..config = InspectorTreeConfig(
-            onNodeAdded: (_, __) {},
-            onClientActiveChange: (_) {},
-          );
-        await pumpInspectorTree(tester, treeController: treeController);
+    testWidgets('Row with negative index regression test', (
+      WidgetTester tester,
+    ) async {
+      final treeController =
+          InspectorTreeController()
+            ..config = InspectorTreeConfig(
+              onNodeAdded: (_, _) {},
+              onClientActiveChange: (_) {},
+            );
+      await pumpInspectorTree(tester, treeController: treeController);
 
-        expect(treeController.getRow(const Offset(0, -100.0)), isNull);
-        expect(treeController.getRowOffset(-1), equals(0));
+      expect(treeController.getRow(const Offset(0, -100.0)), isNull);
+      expect(treeController.getRowOffset(-1), equals(0));
 
-        expect(treeController.getRow(const Offset(0, 0.0)), isNull);
-        expect(treeController.getRowOffset(0), equals(0));
+      expect(treeController.getRow(const Offset(0, 0.0)), isNull);
+      expect(treeController.getRowOffset(0), equals(0));
 
-        treeController.root = InspectorTreeNode()
-          ..appendChild(InspectorTreeNode());
+      treeController.root =
+          InspectorTreeNode()..appendChild(InspectorTreeNode());
 
-        await pumpInspectorTree(tester, treeController: treeController);
+      await pumpInspectorTree(tester, treeController: treeController);
 
-        expect(treeController.getRow(const Offset(0, -20))!.index, 0);
-        expect(treeController.getRowOffset(-1), equals(0));
-        expect(treeController.getRow(const Offset(0, 0.0)), isNotNull);
-        expect(treeController.getRowOffset(0), equals(0));
+      expect(treeController.getRow(const Offset(0, -20))!.index, 0);
+      expect(treeController.getRowOffset(-1), equals(0));
+      expect(treeController.getRow(const Offset(0, 0.0)), isNotNull);
+      expect(treeController.getRowOffset(0), equals(0));
 
-        // This operation would previously throw an exception in debug builds
-        // and infinite loop in release builds.
-        treeController.scrollToRect(const Rect.fromLTWH(0, -20, 100, 100));
-      },
-    );
+      // This operation would previously throw an exception in debug builds
+      // and infinite loop in release builds.
+      treeController.scrollToRect(const Rect.fromLTWH(0, -20, 100, 100));
+    });
   });
 
   group('Inspector tree content preview', () {
@@ -119,12 +119,7 @@
     testWidgets('Shows preview from Text.rich', (WidgetTester tester) async {
       final diagnosticNode = await widgetToInspectorTreeDiagnosticsNode(
         widget: const Text.rich(
-          TextSpan(
-            children: [
-              TextSpan(text: 'Rich '),
-              TextSpan(text: 'text'),
-            ],
-          ),
+          TextSpan(children: [TextSpan(text: 'Rich '), TextSpan(text: 'text')]),
         ),
         tester: tester,
       );
@@ -135,23 +130,19 @@
       expect(find.richText('Text: "Rich text"'), findsOneWidget);
     });
 
-    testWidgets(
-      'Strips new lines from text preview',
-      (WidgetTester tester) async {
-        final diagnosticNode = await widgetToInspectorTreeDiagnosticsNode(
-          widget: const Text('Multiline\ntext\n\ncontent'),
-          tester: tester,
-        );
+    testWidgets('Strips new lines from text preview', (
+      WidgetTester tester,
+    ) async {
+      final diagnosticNode = await widgetToInspectorTreeDiagnosticsNode(
+        widget: const Text('Multiline\ntext\n\ncontent'),
+        tester: tester,
+      );
 
-        final treeController = inspectorTreeControllerFromNode(diagnosticNode);
-        await pumpInspectorTree(tester, treeController: treeController);
+      final treeController = inspectorTreeControllerFromNode(diagnosticNode);
+      await pumpInspectorTree(tester, treeController: treeController);
 
-        expect(
-          find.richText('Text: "Multiline text  content"'),
-          findsOneWidget,
-        );
-      },
-    );
+      expect(find.richText('Text: "Multiline text  content"'), findsOneWidget);
+    });
 
     testWidgets('Shows breadcrumbs in Widget detail tree', (tester) async {
       final diagnosticNode = await widgetToInspectorTreeDiagnosticsNode(
diff --git a/packages/devtools_app/test/screens/inspector/layout_explorer/flex/arrow_test.dart b/packages/devtools_app/test/screens/inspector/layout_explorer/flex/arrow_test.dart
new file mode 100644
index 0000000..a6b61f7
--- /dev/null
+++ b/packages/devtools_app/test/screens/inspector/layout_explorer/flex/arrow_test.dart
@@ -0,0 +1,109 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:devtools_app/src/screens/inspector/layout_explorer/ui/arrow.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../../../../test_infra/matchers/matchers.dart';
+
+void main() {
+  const relativeGoldenPath =
+      '../../../../test_infra/goldens/inspector/layout_explorer/flex';
+
+  group('Arrow Golden Tests', () {
+    group('Unidirectional', () {
+      Widget buildUnidirectionalArrowWrapper(ArrowType type) => Directionality(
+        textDirection: TextDirection.ltr,
+        child: SizedBox(
+          width: 100,
+          height: 100,
+          child: ArrowWrapper.unidirectional(
+            type: type,
+            arrowColor: Colors.black,
+            arrowHeadSize: 8.0,
+            child: Container(width: 10, height