diff --git a/flutter-idea/src/io/flutter/FlutterInitializer.java b/flutter-idea/src/io/flutter/FlutterInitializer.java
index e87e0e2df..6ae608ea8 100644
--- a/flutter-idea/src/io/flutter/FlutterInitializer.java
+++ b/flutter-idea/src/io/flutter/FlutterInitializer.java
@@ -364,7 +364,7 @@ public void actionPerformed(@NotNull AnActionEvent event) {
     });
   }
 
-  private void initializeToolWindows(@NotNull Project project) {
+  private void initializeToolWindows(@NotNull final Project project) {
     // Start watching for Flutter debug active events.
     FlutterViewFactory.init(project);
     RemainingDevToolsViewFactory.init(project);
diff --git a/flutter-idea/src/io/flutter/deeplinks/DeepLinksViewFactory.java b/flutter-idea/src/io/flutter/deeplinks/DeepLinksViewFactory.java
index b7048a08e..d6f84210f 100644
--- a/flutter-idea/src/io/flutter/deeplinks/DeepLinksViewFactory.java
+++ b/flutter-idea/src/io/flutter/deeplinks/DeepLinksViewFactory.java
@@ -6,75 +6,49 @@
 package io.flutter.deeplinks;
 
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowFactory;
-import io.flutter.FlutterUtils;
-import io.flutter.actions.RefreshToolWindowAction;
 import io.flutter.bazel.WorkspaceCache;
+import io.flutter.devtools.AbstractDevToolsViewFactory;
 import io.flutter.devtools.DevToolsIdeFeature;
 import io.flutter.devtools.DevToolsUrl;
-import io.flutter.run.daemon.DevToolsService;
-import io.flutter.sdk.FlutterSdk;
+import io.flutter.run.daemon.DevToolsInstance;
 import io.flutter.sdk.FlutterSdkVersion;
-import io.flutter.utils.AsyncUtils;
-import io.flutter.utils.OpenApiUtils;
-import kotlin.coroutines.Continuation;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.List;
-import java.util.Optional;
+public class DeepLinksViewFactory extends AbstractDevToolsViewFactory {
+  @NotNull public static String TOOL_WINDOW_ID = "Flutter Deep Links";
 
-public class DeepLinksViewFactory implements ToolWindowFactory {
-  @NotNull private static String TOOL_WINDOW_ID = "Flutter Deep Links";
+  @NotNull public static String DEVTOOLS_PAGE_ID = "deep-links";
 
   @Override
-  public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    return sdkVersion != null && sdkVersion.canUseDeepLinksTool();
+  public boolean versionSupportsThisTool(@NotNull final FlutterSdkVersion flutterSdkVersion) {
+    return flutterSdkVersion.canUseDeepLinksTool();
   }
 
   @Override
-  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-
-    AsyncUtils.whenCompleteUiThread(
-      DevToolsService.getInstance(project).getDevToolsInstance(),
-      (instance, error) -> {
-        // Skip displaying if the project has been closed.
-        if (!project.isOpen()) {
-          return;
-        }
-
-        if (error != null) {
-          return;
-        }
-
-        if (instance == null) {
-          return;
-        }
-
-        final DevToolsUrl devToolsUrl = new DevToolsUrl.Builder()
-          .setDevToolsHost(instance.host())
-          .setDevToolsPort(instance.port())
-          .setPage("deep-links")
-          .setEmbed(true)
-          .setFlutterSdkVersion(sdkVersion)
-          .setWorkspaceCache(WorkspaceCache.getInstance(project))
-          .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
-          .build();
+  @NotNull
+  public String getToolWindowId() {
+    return TOOL_WINDOW_ID;
+  }
 
-        OpenApiUtils.safeInvokeLater(() -> {
-          Optional.ofNullable(
-              FlutterUtils.embeddedBrowser(project))
-            .ifPresent(embeddedBrowser -> embeddedBrowser.openPanel(toolWindow, "Deep Links", devToolsUrl, System.out::println));
-        });
-      }
-    );
+  @Override
+  @NotNull
+  public String getToolWindowTitle() {
+    return "Deep Links";
+  }
 
-    // TODO(helin24): It may be better to add this to the gear actions or to attach as a mouse event on individual tabs within a tool
-    //  window, but I wasn't able to get either working immediately.
-    toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(TOOL_WINDOW_ID)));
+  @Override
+  @NotNull
+  public DevToolsUrl getDevToolsUrl(@NotNull final Project project,
+                                    @NotNull final FlutterSdkVersion flutterSdkVersion,
+                                    @NotNull final DevToolsInstance instance) {
+    return new DevToolsUrl.Builder()
+      .setDevToolsHost(instance.host())
+      .setDevToolsPort(instance.port())
+      .setPage(DEVTOOLS_PAGE_ID)
+      .setEmbed(true)
+      .setFlutterSdkVersion(flutterSdkVersion)
+      .setWorkspaceCache(WorkspaceCache.getInstance(project))
+      .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
+      .build();
   }
-}
+}
\ No newline at end of file
diff --git a/flutter-idea/src/io/flutter/devtools/AbstractDevToolsViewFactory.java b/flutter-idea/src/io/flutter/devtools/AbstractDevToolsViewFactory.java
new file mode 100644
index 000000000..e46e52821
--- /dev/null
+++ b/flutter-idea/src/io/flutter/devtools/AbstractDevToolsViewFactory.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2025 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.
+ */
+package io.flutter.devtools;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import io.flutter.FlutterUtils;
+import io.flutter.actions.RefreshToolWindowAction;
+import io.flutter.run.daemon.DevToolsInstance;
+import io.flutter.run.daemon.DevToolsService;
+import io.flutter.sdk.FlutterSdk;
+import io.flutter.sdk.FlutterSdkVersion;
+import io.flutter.utils.AsyncUtils;
+import io.flutter.utils.OpenApiUtils;
+import io.flutter.view.EmbeddedBrowser;
+import io.flutter.view.ViewUtils;
+import kotlin.coroutines.Continuation;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Optional;
+
+public abstract class AbstractDevToolsViewFactory implements ToolWindowFactory {
+
+  @NotNull
+  protected final ViewUtils viewUtils = new ViewUtils();
+
+  public abstract boolean versionSupportsThisTool(@NotNull FlutterSdkVersion flutterSdkVersion);
+
+  @NotNull
+  public abstract String getToolWindowId();
+
+  @NotNull
+  public abstract String getToolWindowTitle();
+
+  @NotNull
+  public abstract DevToolsUrl getDevToolsUrl(@NotNull Project project,
+                                             @NotNull FlutterSdkVersion flutterSdkVersion,
+                                             @NotNull DevToolsInstance instance);
+
+  protected void doAfterBrowserOpened(@NotNull Project project, @NotNull EmbeddedBrowser browser) {}
+
+  @Override
+  public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
+    // Due to https://github.com/flutter/flutter/issues/142521, this always returns true when the
+    // Flutter IJ plugin is installed.
+
+    // The logic which asserts that the Flutter SDK is up to date enough for this particular feature
+    // is captured in the implementation of createToolWindowContent() below.
+
+    return true;
+  }
+
+  @Override
+  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+    final FlutterSdk flutterSdk = FlutterSdk.getFlutterSdk(project);
+    final FlutterSdkVersion flutterSdkVersion = flutterSdk == null ? null : flutterSdk.getVersion();
+
+    // There are four potential states for the Flutter SDK:
+    // 1. The Flutter SDK is null (flutterSdk == null) (an SDK path is invalid or not configured)
+    // 2. The Flutter SDK exists, but version file information is unavailable (flutterSdkVersion == null),
+    //      see https://github.com/flutter/flutter/issues/142521.
+    // 3. The Flutter SDK exists, and the version file information is available, but this tool is not
+    //      available on the version of the SDK that the user has.
+    // 4. The Flutter SDK exists with valid version information.
+
+    // First case:
+    if (flutterSdk == null) {
+      viewUtils.presentLabels(toolWindow, List.of("Set the Flutter SDK path in",
+                                                  "Settings > Languages & Frameworks > Flutter,",
+                                                  "and then restart the IDE."));
+      return;
+    }
+
+    // Second case:
+    if (flutterSdk.getVersion().fullVersion().equals(FlutterSdkVersion.UNKNOWN_VERSION)) {
+      viewUtils.presentLabels(toolWindow, List.of("A Flutter SDK was found at the location",
+                                                  "specified in the settings, however the directory",
+                                                  "is in an incomplete state. To fix, shut down the IDE,",
+                                                  "run `flutter doctor` or `flutter --version`",
+                                                  "and then restart the IDE."));
+      return;
+    }
+
+    // Third case:
+    if (!versionSupportsThisTool(flutterSdkVersion)) {
+      final String versionText = flutterSdkVersion.fullVersion();
+      viewUtils.presentLabels(toolWindow, List.of("The version of your Flutter SDK,",
+                                                  versionText + ",",
+                                                  "is not recent enough to use this tool.",
+                                                  "Update the Flutter SDK, `flutter upgrade`,",
+                                                  "and then restart the IDE."));
+      return;
+    }
+
+    // Final case:
+    AsyncUtils.whenCompleteUiThread(
+      DevToolsService.getInstance(project).getDevToolsInstance(),
+      (instance, error) -> {
+        // Skip displaying if the project has been closed.
+        if (!project.isOpen()) {
+          return;
+        }
+
+        if (error != null) {
+          return;
+        }
+
+        if (instance == null) {
+          return;
+        }
+
+        final DevToolsUrl devToolsUrl = getDevToolsUrl(project, flutterSdkVersion, instance);
+
+        OpenApiUtils.safeInvokeLater(() -> {
+          Optional.ofNullable(
+              FlutterUtils.embeddedBrowser(project))
+            .ifPresent(embeddedBrowser ->
+                       {
+                         embeddedBrowser.openPanel(toolWindow, getToolWindowTitle(), devToolsUrl, System.out::println);
+                         doAfterBrowserOpened(project, embeddedBrowser);
+                       });
+        });
+      }
+    );
+
+    // TODO(helin24): It may be better to add this to the gear actions or to attach as a mouse event on individual tabs within a tool
+    //  window, but I wasn't able to get either working immediately.
+    toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(getToolWindowId())));
+  }
+}
\ No newline at end of file
diff --git a/flutter-idea/src/io/flutter/devtools/DevToolsExtensionsViewFactory.java b/flutter-idea/src/io/flutter/devtools/DevToolsExtensionsViewFactory.java
index 1c65bae26..beb85961c 100644
--- a/flutter-idea/src/io/flutter/devtools/DevToolsExtensionsViewFactory.java
+++ b/flutter-idea/src/io/flutter/devtools/DevToolsExtensionsViewFactory.java
@@ -6,92 +6,64 @@
 package io.flutter.devtools;
 
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowFactory;
-import com.intellij.ui.content.ContentManager;
-import io.flutter.FlutterUtils;
-import io.flutter.actions.RefreshToolWindowAction;
 import io.flutter.bazel.WorkspaceCache;
-import io.flutter.run.daemon.DevToolsService;
-import io.flutter.sdk.FlutterSdk;
+import io.flutter.run.daemon.DevToolsInstance;
 import io.flutter.sdk.FlutterSdkVersion;
-import io.flutter.utils.AsyncUtils;
-import io.flutter.utils.OpenApiUtils;
+import io.flutter.view.EmbeddedBrowser;
 import io.flutter.view.FlutterViewMessages;
-import kotlin.coroutines.Continuation;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
-import java.util.List;
-import java.util.Optional;
+public class DevToolsExtensionsViewFactory extends AbstractDevToolsViewFactory {
+  @NotNull public static String TOOL_WINDOW_ID = "Flutter DevTools Extensions";
 
-public class DevToolsExtensionsViewFactory implements ToolWindowFactory {
-  @NotNull private static String TOOL_WINDOW_ID = "Flutter DevTools Extensions";
-
-  public static void init(Project project) {
+  public static void init(@NotNull final Project project) {
     project.getMessageBus().connect().subscribe(
       FlutterViewMessages.FLUTTER_DEBUG_TOPIC, (FlutterViewMessages.FlutterDebugNotifier)event -> initView(project, event)
     );
   }
 
-  private static void initView(Project project, FlutterViewMessages.FlutterDebugEvent event) {
+  private static void initView(@NotNull final Project project, FlutterViewMessages.FlutterDebugEvent event) {
     DevToolsExtensionsViewService service = project.getService(DevToolsExtensionsViewService.class);
     String vmServiceUri = event.app.getConnector().getBrowserUrl();
-    if (vmServiceUri == null) return;
+    if (service == null || vmServiceUri == null) return;
     service.updateVmServiceUri(vmServiceUri);
   }
 
   @Override
-  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow window) {
-    final ContentManager contentManager = window.getContentManager();
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    DevToolsExtensionsViewService service = project.getService(DevToolsExtensionsViewService.class);
-
-    AsyncUtils.whenCompleteUiThread(
-      DevToolsService.getInstance(project).getDevToolsInstance(),
-      (instance, error) -> {
-        // Skip displaying if the project has been closed.
-        if (!project.isOpen()) {
-          return;
-        }
-
-        if (error != null) {
-          return;
-        }
-
-        if (instance == null) {
-          return;
-        }
+  public boolean versionSupportsThisTool(@NotNull final FlutterSdkVersion flutterSdkVersion) {
+    return flutterSdkVersion.canUseDevToolsMultiEmbed();
+  }
 
-        final DevToolsUrl devToolsUrl = new DevToolsUrl.Builder()
-          .setDevToolsHost(instance.host())
-          .setDevToolsPort(instance.port())
-          .setHide("all-except-extensions")
-          .setEmbed(true).setFlutterSdkVersion(sdkVersion)
-          .setWorkspaceCache(WorkspaceCache.getInstance(project))
-          .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
-          .build();
+  @Override
+  @NotNull
+  public String getToolWindowId() {
+    return TOOL_WINDOW_ID;
+  }
 
-        OpenApiUtils.safeInvokeLater(() -> {
-          Optional.ofNullable(
-              FlutterUtils.embeddedBrowser(project))
-            .ifPresent(embeddedBrowser -> {
-              embeddedBrowser.openPanel(window, "Flutter DevTools", devToolsUrl, System.out::println);
-              service.setEmbeddedBrowser(embeddedBrowser);
-            });
-        });
-      }
-    );
+  @NotNull
+  public String getToolWindowTitle() {
+    return "Flutter DevTools";
+  }
 
-    window.setTitleActions(List.of(new RefreshToolWindowAction(TOOL_WINDOW_ID)));
+  @Override
+  protected void doAfterBrowserOpened(@NotNull final Project project, @NotNull final EmbeddedBrowser embeddedBrowser) {
+    DevToolsExtensionsViewService service = project.getService(DevToolsExtensionsViewService.class);
+    if (service == null) return;
+    service.setEmbeddedBrowser(embeddedBrowser);
   }
 
-  @Nullable
   @Override
-  public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    return sdkVersion != null && sdkVersion.canUseDevToolsMultiEmbed();
+  @NotNull
+  public DevToolsUrl getDevToolsUrl(@NotNull final Project project,
+                                    @NotNull final FlutterSdkVersion flutterSdkVersion,
+                                    @NotNull final DevToolsInstance instance) {
+    return new DevToolsUrl.Builder()
+      .setDevToolsHost(instance.host())
+      .setDevToolsPort(instance.port())
+      .setHide("all-except-extensions")
+      .setEmbed(true).setFlutterSdkVersion(flutterSdkVersion)
+      .setWorkspaceCache(WorkspaceCache.getInstance(project))
+      .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
+      .build();
   }
 }
diff --git a/flutter-idea/src/io/flutter/devtools/DevToolsUrl.java b/flutter-idea/src/io/flutter/devtools/DevToolsUrl.java
index 8646b8346..ce9ed21d4 100644
--- a/flutter-idea/src/io/flutter/devtools/DevToolsUrl.java
+++ b/flutter-idea/src/io/flutter/devtools/DevToolsUrl.java
@@ -56,66 +56,79 @@ public static class Builder {
 
     public Builder() {}
 
+    @NotNull
     public Builder setDevToolsHost(String devToolsHost) {
       this.devToolsHost = devToolsHost;
       return this;
     }
 
+    @NotNull
     public Builder setDevToolsPort(int devToolsPort) {
       this.devToolsPort = devToolsPort;
       return this;
     }
 
+    @NotNull
     public Builder setVmServiceUri(String vmServiceUri) {
       this.vmServiceUri = vmServiceUri;
       return this;
     }
 
+    @NotNull
     public Builder setPage(String page) {
       this.page = page;
       return this;
     }
 
+    @NotNull
     public Builder setEmbed(Boolean embed) {
       this.embed = embed;
       return this;
     }
 
+    @NotNull
     public Builder setWidgetId(String widgetId) {
       this.widgetId = widgetId;
       return this;
     }
 
+    @NotNull
     public Builder setHide(String hide) {
       this.hide = hide;
       return this;
     }
 
+    @NotNull
     public Builder setDevToolsUtils(DevToolsUtils devToolsUtils) {
       this.devToolsUtils = devToolsUtils;
       return this;
     }
 
+    @NotNull
     public Builder setFlutterSdkVersion(FlutterSdkVersion sdkVersion) {
       this.flutterSdkVersion = sdkVersion;
       return this;
     }
 
+    @NotNull
     public Builder setWorkspaceCache(WorkspaceCache workspaceCache) {
       this.workspaceCache = workspaceCache;
       return this;
     }
 
+    @NotNull
     public Builder setIdeFeature(DevToolsIdeFeature ideFeature) {
       this.ideFeature = ideFeature;
       return this;
     }
 
+    @NotNull
     public Builder setFlutterSdkUtil(FlutterSdkUtil flutterSdkUtil) {
       this.flutterSdkUtil = flutterSdkUtil;
       return this;
     }
 
+    @NotNull
     public DevToolsUrl build() {
       if (devToolsUtils == null) {
         devToolsUtils = new DevToolsUtils();
diff --git a/flutter-idea/src/io/flutter/devtools/RemainingDevToolsViewFactory.java b/flutter-idea/src/io/flutter/devtools/RemainingDevToolsViewFactory.java
index 62adf042b..86826e5ff 100644
--- a/flutter-idea/src/io/flutter/devtools/RemainingDevToolsViewFactory.java
+++ b/flutter-idea/src/io/flutter/devtools/RemainingDevToolsViewFactory.java
@@ -6,93 +6,63 @@
 package io.flutter.devtools;
 
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowFactory;
-import com.intellij.ui.content.ContentManager;
-import io.flutter.FlutterUtils;
-import io.flutter.actions.RefreshToolWindowAction;
 import io.flutter.bazel.WorkspaceCache;
-import io.flutter.run.daemon.DevToolsService;
-import io.flutter.sdk.FlutterSdk;
+import io.flutter.run.daemon.DevToolsInstance;
 import io.flutter.sdk.FlutterSdkVersion;
-import io.flutter.utils.AsyncUtils;
-import io.flutter.utils.OpenApiUtils;
+import io.flutter.view.EmbeddedBrowser;
 import io.flutter.view.FlutterViewMessages;
-import kotlin.coroutines.Continuation;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
-import java.util.List;
-import java.util.Optional;
+public class RemainingDevToolsViewFactory extends AbstractDevToolsViewFactory {
+  @NotNull public static String TOOL_WINDOW_ID = "Flutter DevTools";
 
-public class RemainingDevToolsViewFactory implements ToolWindowFactory {
-  @NotNull private static String TOOL_WINDOW_ID = "Flutter DevTools";
-
-  public static void init(Project project) {
-    project.getMessageBus().connect().subscribe(
-      FlutterViewMessages.FLUTTER_DEBUG_TOPIC, (FlutterViewMessages.FlutterDebugNotifier)event -> initView(project, event)
-    );
+  public static void init(@NotNull final Project project) {
+    project.getMessageBus().connect()
+      .subscribe(FlutterViewMessages.FLUTTER_DEBUG_TOPIC, (FlutterViewMessages.FlutterDebugNotifier)event -> initView(project, event));
   }
 
-  private static void initView(Project project, FlutterViewMessages.FlutterDebugEvent event) {
+  private static void initView(@NotNull final Project project, FlutterViewMessages.FlutterDebugEvent event) {
     RemainingDevToolsViewService service = project.getService(RemainingDevToolsViewService.class);
     String vmServiceUri = event.app.getConnector().getBrowserUrl();
-    if (vmServiceUri == null) return;
+    if (service == null || vmServiceUri == null) return;
     service.updateVmServiceUri(vmServiceUri);
   }
 
-
   @Override
-  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow window) {
-    final ContentManager contentManager = window.getContentManager();
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    RemainingDevToolsViewService service = project.getService(RemainingDevToolsViewService.class);
-
-    AsyncUtils.whenCompleteUiThread(
-      DevToolsService.getInstance(project).getDevToolsInstance(),
-      (instance, error) -> {
-        // Skip displaying if the project has been closed.
-        if (!project.isOpen()) {
-          return;
-        }
-
-        if (error != null) {
-          return;
-        }
-
-        if (instance == null) {
-          return;
-        }
+  public boolean versionSupportsThisTool(@NotNull final FlutterSdkVersion flutterSdkVersion) {
+    return flutterSdkVersion.canUseDevToolsMultiEmbed();
+  }
 
-        final DevToolsUrl devToolsUrl = new DevToolsUrl.Builder()
-          .setDevToolsHost(instance.host())
-          .setDevToolsPort(instance.port())
-          .setHide("home,inspector,deep-links,extensions,debugger")
-          .setEmbed(true).setFlutterSdkVersion(sdkVersion)
-          .setWorkspaceCache(WorkspaceCache.getInstance(project))
-          .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
-          .build();
+  @Override
+  @NotNull
+  public String getToolWindowId() {
+    return TOOL_WINDOW_ID;
+  }
 
-        OpenApiUtils.safeInvokeLater(() -> {
-          Optional.ofNullable(
-              FlutterUtils.embeddedBrowser(project))
-            .ifPresent(embeddedBrowser -> {
-              embeddedBrowser.openPanel(window, "Flutter DevTools", devToolsUrl, System.out::println);
-              service.setEmbeddedBrowser(embeddedBrowser);
-            });
-        });
-      }
-    );
+  @NotNull
+  public String getToolWindowTitle() {
+    return "Flutter DevTools";
+  }
 
-    window.setTitleActions(List.of(new RefreshToolWindowAction(TOOL_WINDOW_ID)));
+  @Override
+  protected void doAfterBrowserOpened(@NotNull final Project project, @NotNull final EmbeddedBrowser embeddedBrowser) {
+    RemainingDevToolsViewService service = project.getService(RemainingDevToolsViewService.class);
+    if (service == null) return;
+    service.setEmbeddedBrowser(embeddedBrowser);
   }
 
-  @Nullable
   @Override
-  public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    return sdkVersion != null && sdkVersion.canUseDevToolsMultiEmbed();
+  @NotNull
+  public DevToolsUrl getDevToolsUrl(@NotNull final Project project,
+                                    @NotNull final FlutterSdkVersion flutterSdkVersion,
+                                    @NotNull final DevToolsInstance instance) {
+    return new DevToolsUrl.Builder()
+      .setDevToolsHost(instance.host())
+      .setDevToolsPort(instance.port())
+      .setHide("home,inspector,deep-links,extensions,debugger")
+      .setEmbed(true).setFlutterSdkVersion(flutterSdkVersion)
+      .setWorkspaceCache(WorkspaceCache.getInstance(project))
+      .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
+      .build();
   }
 }
diff --git a/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java b/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java
index 2a0c01737..49f2906cd 100644
--- a/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java
+++ b/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java
@@ -7,84 +7,64 @@
 
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowFactory;
-import io.flutter.FlutterUtils;
-import io.flutter.actions.RefreshToolWindowAction;
 import io.flutter.bazel.WorkspaceCache;
 import io.flutter.dart.DartPlugin;
 import io.flutter.dart.DartPluginVersion;
+import io.flutter.devtools.AbstractDevToolsViewFactory;
 import io.flutter.devtools.DevToolsIdeFeature;
 import io.flutter.devtools.DevToolsUrl;
-import io.flutter.run.daemon.DevToolsService;
-import io.flutter.sdk.FlutterSdk;
+import io.flutter.run.daemon.DevToolsInstance;
 import io.flutter.sdk.FlutterSdkVersion;
-import io.flutter.utils.AsyncUtils;
-import io.flutter.utils.OpenApiUtils;
-import io.flutter.view.ViewUtils;
-import kotlin.coroutines.Continuation;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
-import java.util.Optional;
 
-public class PropertyEditorViewFactory implements ToolWindowFactory {
-  @NotNull private static String TOOL_WINDOW_ID = "Flutter Property Editor";
+public class PropertyEditorViewFactory extends AbstractDevToolsViewFactory {
+  @NotNull public static String TOOL_WINDOW_ID = "Flutter Property Editor";
 
+  @NotNull public static String DEVTOOLS_PAGE_ID = "propertyEditor";
+
+  @Override
+  public boolean versionSupportsThisTool(@NotNull final FlutterSdkVersion flutterSdkVersion) {
+    return flutterSdkVersion.canUsePropertyEditor();
+  }
+
+  @Override
   @NotNull
-  private final ViewUtils viewUtils = new ViewUtils();
+  public String getToolWindowId() {
+    return TOOL_WINDOW_ID;
+  }
 
   @Override
-  public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
-    return sdkVersion != null && sdkVersion.canUsePropertyEditor();
+  @NotNull
+  public String getToolWindowTitle() {
+    return "Property Editor";
   }
 
   @Override
-  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
-    FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
-    FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion();
+  @NotNull
+  public DevToolsUrl getDevToolsUrl(@NotNull final Project project,
+                                    @NotNull final FlutterSdkVersion flutterSdkVersion,
+                                    @NotNull final DevToolsInstance instance) {
+    return new DevToolsUrl.Builder()
+      .setDevToolsHost(instance.host())
+      .setDevToolsPort(instance.port())
+      .setPage(DEVTOOLS_PAGE_ID)
+      .setEmbed(true)
+      .setFlutterSdkVersion(flutterSdkVersion)
+      .setWorkspaceCache(WorkspaceCache.getInstance(project))
+      .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
+      .build();
+  }
 
+  @Override
+  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
     final DartPluginVersion dartPluginVersion = DartPlugin.getDartPluginVersion();
-    if (dartPluginVersion == null || !dartPluginVersion.supportsPropertyEditor()) {
-      viewUtils.presentLabel(toolWindow, "Flutter Property Editor requires a newer version of the Dart plugin.");
+    if (!dartPluginVersion.supportsPropertyEditor()) {
+      super.viewUtils.presentLabels(toolWindow, List.of("The Flutter Property Editor requires a",
+                                                        "newer version of the Dart plugin."));
       return;
     }
-
-    AsyncUtils.whenCompleteUiThread(
-      DevToolsService.getInstance(project).getDevToolsInstance(),
-      (instance, error) -> {
-        // Skip displaying if the project has been closed.
-        if (!project.isOpen()) {
-          return;
-        }
-
-        if (error != null) {
-          return;
-        }
-
-        if (instance == null) {
-          return;
-        }
-
-        final DevToolsUrl devToolsUrl = new DevToolsUrl.Builder()
-          .setDevToolsHost(instance.host())
-          .setDevToolsPort(instance.port())
-          .setPage("propertyEditor")
-          .setEmbed(true)
-          .setFlutterSdkVersion(sdkVersion)
-          .setWorkspaceCache(WorkspaceCache.getInstance(project))
-          .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW)
-          .build();
-
-        OpenApiUtils.safeInvokeLater(() -> {
-          Optional.ofNullable(
-              FlutterUtils.embeddedBrowser(project))
-            .ifPresent(embeddedBrowser -> embeddedBrowser.openPanel(toolWindow, "Property Editor", devToolsUrl, System.out::println));
-        });
-      }
-    );
-
-    toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(TOOL_WINDOW_ID)));
+    super.createToolWindowContent(project, toolWindow);
   }
 }
diff --git a/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java b/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java
index f4ccd9547..ac4f37b43 100644
--- a/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java
+++ b/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java
@@ -49,6 +49,9 @@ public final class FlutterSdkVersion implements Comparable<FlutterSdkVersion> {
   @NotNull
   public static final FlutterSdkVersion MIN_SUPPORTS_PROPERTY_EDITOR = new FlutterSdkVersion("3.32.0-0.1.pre");
 
+  @NotNull
+  public static final String UNKNOWN_VERSION = "unknown version";
+
   @Nullable
   private final Version version;
   @Nullable
@@ -153,7 +156,7 @@ public boolean isValid() {
 
   @NotNull
   public String fullVersion() {
-    return version == null ? "unknown version" : Objects.requireNonNull(version.toString());
+    return version == null ? UNKNOWN_VERSION : Objects.requireNonNull(version.toString());
   }
 
   /**
@@ -167,7 +170,7 @@ public String getVersionText() {
   @Override
   @NotNull
   public String toString() {
-    return version == null ? "unknown version" : Objects.requireNonNull(version.toCompactString());
+    return version == null ? UNKNOWN_VERSION : Objects.requireNonNull(version.toCompactString());
   }
 
   @Override
diff --git a/flutter-idea/src/io/flutter/view/ViewUtils.java b/flutter-idea/src/io/flutter/view/ViewUtils.java
index 8564ace95..a96907b2a 100644
--- a/flutter-idea/src/io/flutter/view/ViewUtils.java
+++ b/flutter-idea/src/io/flutter/view/ViewUtils.java
@@ -15,6 +15,7 @@
 import com.intellij.util.ui.UIUtil;
 import io.flutter.utils.LabelInput;
 import io.flutter.utils.OpenApiUtils;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import java.awt.*;
@@ -27,6 +28,33 @@ public void presentLabel(ToolWindow toolWindow, String text) {
     replacePanelLabel(toolWindow, label);
   }
 
+  /**
+   * Displays multiple labels vertically centered in the tool window.
+   *
+   * @param toolWindow The target tool window.
+   * @param labels     A list of strings to display as labels.
+   */
+  public void presentLabels(@NotNull ToolWindow toolWindow, @NotNull List<String> labels) {
+    final JPanel labelsPanel = new JPanel(new GridLayout(0, 1));
+    labelsPanel.setBorder(JBUI.Borders.empty()); // Use padding on individual labels if needed
+
+    for (String text : labels) {
+      final JBLabel label = new JBLabel(text, SwingConstants.CENTER);
+      label.setForeground(UIUtil.getLabelDisabledForeground());
+      // Add padding to each label for spacing
+      label.setBorder(JBUI.Borders.empty(2, 0));
+      labelsPanel.add(label);
+    }
+
+    // Use VerticalFlowLayout to center the block of labels vertically
+    final JPanel centerPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.CENTER));
+    centerPanel.add(labelsPanel);
+
+    replacePanelLabel(toolWindow, centerPanel);
+  }
+
+
+
   public void presentClickableLabel(ToolWindow toolWindow, List<LabelInput> labels) {
     final JPanel panel = new JPanel(new GridLayout(0, 1));