Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/io/flutter/run/LaunchState.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import org.jetbrains.annotations.VisibleForTesting;

/**
* Launches a flutter app, showing it in the console.
Expand Down Expand Up @@ -168,20 +170,42 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws
// The descriptor shows the run configuration name (e.g., `main.dart`) by default;
// adding the device name will help users identify the instance when trying to operate a specific one.
final String nameWithDeviceName = descriptor.getDisplayName() + " (" + device.deviceName() + ")";

try {
// RunContentDescriptor.setDisplayName() is protected, so we use reflection to call it.
final Method setDisplayName = RunContentDescriptor.class.getDeclaredMethod("setDisplayName", String.class);
setDisplayName.setAccessible(true);
setDisplayName.invoke(descriptor, nameWithDeviceName);
final BiConsumer<RunContentDescriptor, String> setter = getDisplaySetter();
if (setter != null) {
setter.accept(descriptor, nameWithDeviceName);
}
catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
FlutterUtils.info(LOG, "Error setting display name", e, true);
else {
FlutterUtils.info(LOG, "Could not find a way to set run tab display name", null, true);
}

return descriptor;
}

/**
* Returns a function that sets the display name on a {@link RunContentDescriptor}, or null if unavailable.
* Uses reflection to call the protected {@code setDisplayName()} method.
* Exposed for testing to verify that the method is accessible on the current IntelliJ build.
*/
@VisibleForTesting
@Nullable
static BiConsumer<RunContentDescriptor, String> getDisplaySetter() {
try {
final Method m = RunContentDescriptor.class.getDeclaredMethod("setDisplayName", String.class);
m.setAccessible(true);
return (descriptor, name) -> {
try {
m.invoke(descriptor, name);
}
catch (IllegalAccessException | InvocationTargetException e) {
FlutterUtils.info(LOG, "Error setting display name", e, true);
}
};
}
catch (NoSuchMethodException e) {
return null;
}
}

private static Class classForName(String className) {
try {
return Class.forName(className);
Expand Down
21 changes: 21 additions & 0 deletions testSrc/unit/io/flutter/run/LaunchStateTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2026 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.run;

import org.junit.Test;

import static org.junit.Assert.assertNotNull;

public class LaunchStateTest {

@Test
public void displaySetterIsAvailable() {
assertNotNull(
"setDisplayName not found on RunContentDescriptor — JetBrains may have removed or renamed it",
LaunchState.getDisplaySetter()
);
}
}
Loading