Skip to content

Commit 7037972

Browse files
authored
Wait for the Dart plugin to start the DevTools server with retries (#8177)
Fixes #8144 This PR: * Makes sure we only ever have one connected DevTools server * Tracks the start-up of the DevTools server using the [Progress API](https://plugins.jetbrains.com/docs/intellij/background-processes.html) instead of `executeOnPooledThread` (logic pulled into `DevToolsServerTask.java`). This allows us to display the progress and cancel the server startup on `forceRestart`. * Retries waiting for the Dart plugin initiated DevTools server until a timeout is reached * If the Dart plugin DevTools is not ready by the timeout, switching to a new Flutter panel will re-trigger starting the server ### Server start-up status: <img width="466" alt="Screenshot 2025-05-09 at 12 40 31 PM" src="https://github.com/user-attachments/assets/8f9d7579-426d-4ef9-8f85-bc0789320d52" /> ### Message if waiting for the server timed out: <img width="501" alt="Screenshot 2025-05-09 at 1 26 55 PM" src="https://github.com/user-attachments/assets/25ef03e6-6d5e-44ee-9c29-5276912b3427" /> ### Loaded Property Editor: <img width="904" alt="Screenshot 2025-05-09 at 12 39 37 PM" src="https://github.com/user-attachments/assets/f538f835-2015-4289-ade4-76514088575c" />
1 parent d029a3b commit 7037972

File tree

4 files changed

+482
-265
lines changed

4 files changed

+482
-265
lines changed

flutter-idea/src/io/flutter/devtools/AbstractDevToolsViewFactory.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
package io.flutter.devtools;
77

88
import com.intellij.openapi.project.Project;
9+
import com.intellij.openapi.util.Disposer;
910
import com.intellij.openapi.wm.ToolWindow;
1011
import com.intellij.openapi.wm.ToolWindowFactory;
12+
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
13+
import com.intellij.util.messages.MessageBusConnection;
1114
import io.flutter.FlutterUtils;
1215
import io.flutter.actions.RefreshToolWindowAction;
1316
import io.flutter.run.daemon.DevToolsInstance;
@@ -42,7 +45,10 @@ public abstract DevToolsUrl getDevToolsUrl(@NotNull Project project,
4245
@NotNull FlutterSdkVersion flutterSdkVersion,
4346
@NotNull DevToolsInstance instance);
4447

45-
protected void doAfterBrowserOpened(@NotNull Project project, @NotNull EmbeddedBrowser browser) {}
48+
protected void doAfterBrowserOpened(@NotNull Project project, @NotNull EmbeddedBrowser browser) {
49+
}
50+
51+
private boolean devToolsLoadedInBrowser = false;
4652

4753
@Override
4854
public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<? super Boolean> $completion) {
@@ -98,19 +104,36 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
98104
}
99105

100106
// Final case:
107+
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion);
108+
109+
// Finally, listen for the panel to be reopened and potentially reload DevTools.
110+
maybeReloadDevToolsWhenVisible(project, toolWindow, flutterSdkVersion);
111+
}
112+
113+
private void loadDevToolsInEmbeddedBrowser(@NotNull Project project,
114+
@NotNull ToolWindow toolWindow,
115+
@NotNull FlutterSdkVersion flutterSdkVersion) {
116+
viewUtils.presentLabel(toolWindow, "Loading " + getToolWindowTitle() + "...");
117+
101118
AsyncUtils.whenCompleteUiThread(
102119
DevToolsService.getInstance(project).getDevToolsInstance(),
103120
(instance, error) -> {
104121
// Skip displaying if the project has been closed.
105122
if (!project.isOpen()) {
123+
viewUtils.presentLabel(toolWindow, "Project is closed.");
106124
return;
107125
}
108126

127+
// Show a message if DevTools started with an error.
128+
final String restartDevToolsMessage = "Try switching to another Flutter panel and back again to restart the server.";
109129
if (error != null) {
130+
viewUtils.presentLabels(toolWindow, List.of("Flutter DevTools start-up failed.", restartDevToolsMessage));
110131
return;
111132
}
112133

134+
// Show a message if there is no DevTools yet.
113135
if (instance == null) {
136+
viewUtils.presentLabels(toolWindow, List.of("Flutter DevTools does not exist.", restartDevToolsMessage));
114137
return;
115138
}
116139

@@ -122,14 +145,30 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
122145
.ifPresent(embeddedBrowser ->
123146
{
124147
embeddedBrowser.openPanel(toolWindow, getToolWindowTitle(), devToolsUrl, System.out::println);
148+
devToolsLoadedInBrowser = true;
125149
doAfterBrowserOpened(project, embeddedBrowser);
150+
// The "refresh" action refreshes the embedded browser, not the panel.
151+
// Therefore, we only show it once we have an embedded browser.
152+
toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(getToolWindowId())));
126153
});
127154
});
128155
}
129156
);
157+
}
130158

131-
// 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
132-
// window, but I wasn't able to get either working immediately.
133-
toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(getToolWindowId())));
159+
private void maybeReloadDevToolsWhenVisible(@NotNull Project project,
160+
@NotNull ToolWindow toolWindow, @NotNull FlutterSdkVersion flutterSdkVersion) {
161+
MessageBusConnection connection = project.getMessageBus().connect();
162+
connection.subscribe(ToolWindowManagerListener.TOPIC, new ToolWindowManagerListener() {
163+
@Override
164+
public void toolWindowShown(@NotNull ToolWindow activatedToolWindow) {
165+
if (activatedToolWindow.getId().equals(getToolWindowId())) {
166+
if (!devToolsLoadedInBrowser) {
167+
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion);
168+
}
169+
}
170+
}
171+
});
172+
Disposer.register(toolWindow.getDisposable(), connection);
134173
}
135-
}
174+
}

0 commit comments

Comments
 (0)