Skip to content

Commit 78f85d4

Browse files
authored
Add a flutter pub add action (flutter#7924)
Opening in case we are interesting in actually upstreaming this - I mostly just did it to get familiar with how actions are set up and plumbed through. Using the action opens a dialog like this: <img width="413" alt="Screenshot 2025-02-04 at 1 16 26 PM" src="https://github.com/user-attachments/assets/62e642a1-0a5c-48bd-9350-2d8768a66b75" /> It does do some validation that a non-empty package name was input, but that is it today: <img width="413" alt="Screenshot 2025-02-04 at 1 16 59 PM" src="https://github.com/user-attachments/assets/ba062554-6b3a-4074-aead-2d3f4e3ed101" /> And it does ensure you actually clicked "OK" before actually running any commands.
1 parent 140f16c commit 78f85d4

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.actions;
7+
8+
import com.intellij.openapi.actionSystem.DataContext;
9+
import com.intellij.openapi.project.Project;
10+
import io.flutter.FlutterMessages;
11+
import io.flutter.pub.PubRoot;
12+
import io.flutter.sdk.FlutterSdk;
13+
import org.jetbrains.annotations.NotNull;
14+
import org.jetbrains.annotations.Nullable;
15+
16+
public class FlutterPackagesAddAction extends FlutterSdkAction {
17+
@Override
18+
public void startCommand(@NotNull Project project, @NotNull FlutterSdk sdk, @Nullable PubRoot root, @NotNull DataContext context) {
19+
if (root == null) {
20+
FlutterMessages.showError(
21+
"Cannot Find Pub Root",
22+
"Flutter pub add can only be run within a directory with a pubspec.yaml file",
23+
project);
24+
return;
25+
}
26+
PackageDialogWrapper dialog = new PackageDialogWrapper();
27+
if (dialog.showAndGet()) {
28+
sdk.startPubAdd(root, project, dialog.getPackageName(), dialog.getDevOnly());
29+
}
30+
}
31+
}
32+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter.actions;
7+
8+
import com.intellij.openapi.ui.DialogWrapper;
9+
import com.intellij.openapi.ui.ValidationInfo;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
import javax.swing.*;
14+
import java.awt.*;
15+
16+
public class PackageDialogWrapper extends DialogWrapper {
17+
18+
@NotNull
19+
private JTextField packageInput = new JTextField("", 16);
20+
21+
@NotNull
22+
private JCheckBox devOnlyCheckBox = new JCheckBox();
23+
24+
public PackageDialogWrapper() {
25+
super(true); // use current window as parent
26+
setTitle("Add Package");
27+
init();
28+
}
29+
30+
@Nullable
31+
@Override
32+
protected JComponent createCenterPanel() {
33+
JPanel dialogPanel = new JPanel();
34+
35+
JLabel packageNameLabel = new JLabel("Package name:");
36+
dialogPanel.add(packageNameLabel, BorderLayout.LINE_START);
37+
dialogPanel.add(packageInput, BorderLayout.CENTER);
38+
39+
dialogPanel.add(new JSeparator());
40+
41+
JLabel devOnlyLabel = new JLabel("Dev only:");
42+
dialogPanel.add(devOnlyLabel, BorderLayout.LINE_START);
43+
dialogPanel.add(devOnlyCheckBox, BorderLayout.CENTER);
44+
45+
return dialogPanel;
46+
}
47+
48+
@Nullable
49+
@Override
50+
protected ValidationInfo doValidate() {
51+
if (getPackageName().isEmpty()) {
52+
return new ValidationInfo("A package name is required", packageInput);
53+
}
54+
return null;
55+
}
56+
57+
@Override
58+
public JComponent getPreferredFocusedComponent() {
59+
return packageInput;
60+
}
61+
62+
@NotNull
63+
public String getPackageName() {
64+
String text = packageInput.getText();
65+
return text == null ? "" : text;
66+
}
67+
68+
public boolean getDevOnly() {
69+
Object[] selectedObjects = devOnlyCheckBox.getSelectedObjects();
70+
if (selectedObjects == null) return false;
71+
return selectedObjects.length > 0;
72+
}
73+
}

flutter-idea/src/io/flutter/sdk/FlutterCommand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ enum Type {
272272
CONFIG("Flutter config", "config"),
273273
CREATE("Flutter create", "create"),
274274
DOCTOR("Flutter doctor", "doctor", "--verbose"),
275+
PUB_ADD("Flutter pub add", "pub", "add"),
275276
PUB_GET("Flutter pub get", "pub", "get"),
276277
PUB_UPGRADE("Flutter pub upgrade", "pub", "upgrade"),
277278
PUB_OUTDATED("Flutter pub outdated", "pub", "outdated"),

flutter-idea/src/io/flutter/sdk/FlutterSdk.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ public FlutterCommand flutterCreate(@NotNull VirtualFile appDir, @Nullable Flutt
236236
return new FlutterCommand(this, appDir.getParent(), FlutterCommand.Type.CREATE, vargs);
237237
}
238238

239+
public FlutterCommand flutterPackagesAdd(@NotNull PubRoot root, @NotNull String name, boolean devOnly) {
240+
if (devOnly) name = "dev:" + name;
241+
return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PUB_ADD, name);
242+
}
243+
239244
public FlutterCommand flutterPackagesGet(@NotNull PubRoot root) {
240245
return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PUB_GET);
241246
}
@@ -441,6 +446,22 @@ public PubRoot createFiles(@NotNull VirtualFile baseDir, @Nullable Module module
441446
return PubRoot.forDirectory(baseDir);
442447
}
443448

449+
/**
450+
* Starts running 'flutter pub add' on the given pub root provided it's in one of this project's modules.
451+
* <p>
452+
* Shows output in the console associated with the given module.
453+
* <p>
454+
* Returns the process if successfully started.
455+
*/
456+
public Process startPubAdd(@NotNull PubRoot root, @NotNull Project project, @NotNull String pkg, boolean devOnly) {
457+
final Module module = root.getModule(project);
458+
if (module == null) return null;
459+
// Ensure pubspec is saved.
460+
FileDocumentManager.getInstance().saveAllDocuments();
461+
// Refresh afterwards to ensure Dart Plugin doesn't mistakenly nag to run pub.
462+
return flutterPackagesAdd(root, pkg, devOnly).startInModuleConsole(module, root::refresh, null);
463+
}
464+
444465
/**
445466
* Starts running 'flutter pub get' on the given pub root provided it's in one of this project's modules.
446467
* <p>

resources/META-INF/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@
147147
text="Flutter Doctor"
148148
description="Run 'flutter doctor'"/>
149149
<separator/>
150+
<action id="flutter.pub.add" class="io.flutter.actions.FlutterPackagesAddAction"
151+
text="Flutter Pub Add"
152+
description="Run 'flutter pub add'"/>
150153
<action id="flutter.pub.get" class="io.flutter.actions.FlutterPackagesGetAction"
151154
text="Flutter Pub Get"
152155
description="Run 'flutter pub get'"/>

resources/META-INF/plugin_template.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
text="Flutter Doctor"
8585
description="Run 'flutter doctor'"/>
8686
<separator/>
87+
<action id="flutter.pub.add" class="io.flutter.actions.FlutterPackagesAddAction"
88+
text="Flutter Pub Add"
89+
description="Run 'flutter pub add'"/>
8790
<action id="flutter.pub.get" class="io.flutter.actions.FlutterPackagesGetAction"
8891
text="Flutter Pub Get"
8992
description="Run 'flutter pub get'"/>

0 commit comments

Comments
 (0)