Skip to content

Commit 662daed

Browse files
authored
feat: Add file chooser support for widget preview initialization. (#8888)
When using a multi-module project, the widget previewer sometimes fails to automatically find the correct pubspec.yaml. Adding the possibility to manually choose the file allows for more complex project setups. <img width="462" height="614" alt="Screenshot 2026-04-03 at 22 46 57" src="https://github.com/user-attachments/assets/c0554589-aa4b-4077-b293-f3261db9f6af" /> <img width="882" height="451" alt="Screenshot 2026-04-16 at 16 12 18" src="https://github.com/user-attachments/assets/3b41e615-0b04-45cf-a78e-0450b056a654" /> --- Review the contribution guidelines below: - [x] I’ve reviewed the contributor guide and applied the relevant portions to this PR. - [x] I've included the required information in the description above. - [x] My up-to-date information is in the `AUTHORS` file. - [x] I've updated `CHANGELOG.md` if appropriate. <details> <summary>Contribution guidelines:</summary><br> - See our [contributor guide](../CONTRIBUTING.md) and the [Flutter organization contributor guide]([https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md) for general expectations for PRs. - Larger or significant changes should be discussed in an issue before creating a PR. - Dart contributions to our repos should follow the [Dart style guide](https://dart.dev/guides/language/effective-dart) and use `dart format`. - Java and Kotlin contributions should strive to follow Java and Kotlin best practices ([discussion](#8098)). </details>
1 parent a105e50 commit 662daed

4 files changed

Lines changed: 57 additions & 4 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ Edwin Ludik <edwin.ludik@gmail.com>
2727
Japnit Singh <truejswalia@gmail.com>
2828
Dmitry Kandalov <dmitry.kandalov@gmail.com>
2929
Kazuya Chikamatsu <kazu.chika.shima@gmail.com>
30+
Dustin Feucht <code.nopjar@gmail.com>

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Added
44

5+
- Option to specify the pub root module for Flutter Widget Previewer. (#8888)
6+
57
### Changed
68

79
### Removed

src/io/flutter/FlutterBundle.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,9 @@ widget.preview.starting=Starting Flutter Widget Preview web server...
193193
widget.preview.loading=Loading Flutter Widget Preview at {0}...
194194
widget.preview.error=Error: {0}
195195
widget.preview.sdk.too.old=Flutter SDK version is too old for Flutter Widget Preview. Please update your SDK.
196+
widget.preview.pubroot.not.found=Pub root could not be found to start Flutter Widget Preview.
197+
widget.preview.retry=Reload and try again
198+
widget.preview.choose_pubroot=Choose module and try again
199+
widget.preview.choose_pubroot.title=Select Flutter Project
200+
widget.preview.choose_pubroot.description=Select the Flutter project to use for Flutter Widget Preview.
196201
flutter.sdk.not.found=Flutter SDK not found.

src/io/flutter/widgetpreview/WidgetPreviewPanel.java

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88
import com.intellij.openapi.diagnostic.Logger;
99
import com.intellij.openapi.editor.colors.EditorColorsListener;
1010
import com.intellij.openapi.editor.colors.EditorColorsManager;
11+
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
12+
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
13+
import com.intellij.openapi.fileChooser.FileChooser;
1114
import com.intellij.openapi.project.Project;
15+
import com.intellij.openapi.roots.ProjectFileIndex;
1216
import com.intellij.openapi.ui.SimpleToolWindowPanel;
1317
import com.intellij.openapi.util.Disposer;
18+
import com.intellij.openapi.vfs.VirtualFile;
1419
import com.intellij.openapi.wm.ToolWindow;
1520
import com.intellij.util.messages.MessageBusConnection;
1621
import com.jetbrains.lang.dart.ide.toolingDaemon.DartToolingDaemonService;
@@ -22,12 +27,14 @@
2227
import io.flutter.devtools.DevToolsUtils;
2328
import io.flutter.logging.PluginLogger;
2429
import io.flutter.pub.PubRoot;
30+
import io.flutter.pub.PubRootCache;
2531
import io.flutter.run.daemon.DevToolsInstance;
2632
import io.flutter.run.daemon.DevToolsService;
2733
import io.flutter.sdk.FlutterCommand;
2834
import io.flutter.sdk.FlutterSdk;
2935
import io.flutter.sdk.FlutterSdkVersion;
3036
import io.flutter.settings.FlutterSettings;
37+
import io.flutter.utils.LabelInput;
3138
import io.flutter.utils.MostlySilentColoredProcessHandler;
3239
import io.flutter.utils.OpenApiUtils;
3340
import io.flutter.view.BrowserUrlProvider;
@@ -71,10 +78,10 @@ public WidgetPreviewPanel(@NotNull Project project, @NotNull ToolWindow toolWind
7178
showInfoMessage(FlutterBundle.message("widget.preview.initializing"));
7279

7380
// Start the preview process asynchronously
74-
startWidgetPreview();
81+
startWidgetPreview(null);
7582
}
7683

77-
private void startWidgetPreview() {
84+
private void startWidgetPreview(@Nullable VirtualFile file) {
7885
OpenApiUtils.safeExecuteOnPooledThread(() -> {
7986
try {
8087
// Check versioning of Flutter SDK.
@@ -125,10 +132,10 @@ private void startWidgetPreview() {
125132
});
126133
});
127134

128-
final PubRoot root = PubRoot.forFile(project.getProjectFile());
135+
final PubRoot root = getPubRoot(file);
129136
if (root == null) {
130137
LOG.warn("Pub root not found for project: " + project.getName());
131-
showInfoMessage("Pub root could not be found to start widget preview.");
138+
showRetryMessage(FlutterBundle.message("widget.preview.pubroot.not.found"), file);
132139
return;
133140
}
134141

@@ -150,6 +157,16 @@ private void startWidgetPreview() {
150157
});
151158
}
152159

160+
private @Nullable PubRoot getPubRoot(@Nullable VirtualFile file) {
161+
return OpenApiUtils.safeRunReadAction(() -> {
162+
if (file != null) {
163+
return PubRootCache.getInstance(project).getRoot(file);
164+
} else {
165+
return PubRoot.forFile(project.getProjectFile());
166+
}
167+
});
168+
}
169+
153170
private @Nullable String getDevToolsUri() {
154171
try {
155172
final CompletableFuture<DevToolsInstance> devToolsFuture = DevToolsService.getInstance(project).getDevToolsInstance();
@@ -252,6 +269,34 @@ private void showInfoMessage(@NotNull String message) {
252269
});
253270
}
254271

272+
private void showRetryMessage(@NotNull String message, @Nullable VirtualFile file) {
273+
ApplicationManager.getApplication().invokeLater(() -> {
274+
final List<LabelInput> inputs = List.of(
275+
new LabelInput(message),
276+
new LabelInput(FlutterBundle.message("widget.preview.retry"), (label, data) -> startWidgetPreview(file)),
277+
new LabelInput(FlutterBundle.message("widget.preview.choose_pubroot"), (label, data) -> chooseFile())
278+
);
279+
final JPanel panel = viewUtils.createClickableLabelPanel(inputs);
280+
contentPanel.removeAll();
281+
contentPanel.add(panel, BorderLayout.CENTER);
282+
contentPanel.revalidate();
283+
contentPanel.repaint();
284+
});
285+
}
286+
287+
private void chooseFile() {
288+
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.singleFileOrDir()
289+
.withTitle(FlutterBundle.message("widget.preview.choose_pubroot.title"))
290+
.withDescription(FlutterBundle.message("widget.preview.choose_pubroot.description"))
291+
.withRoots(FlutterUtils.getProjectRoot(project));
292+
293+
final VirtualFile selectedFile = FileChooser.chooseFile(descriptor, project, null);
294+
if (selectedFile != null
295+
&& OpenApiUtils.safeRunReadAction(() -> ProjectFileIndex.getInstance(project).isInContent(selectedFile))) {
296+
startWidgetPreview(selectedFile);
297+
}
298+
}
299+
255300
@Override
256301
public void dispose() {
257302
// Terminate the Flutter process when the tool window is closed

0 commit comments

Comments
 (0)