Skip to content

Fix device labels missing from run tabs on IntelliJ 2025.3+#8796

Merged
pq merged 1 commit intoflutter:mainfrom
lukemmtt:fix/display-name-reflection
Feb 18, 2026
Merged

Fix device labels missing from run tabs on IntelliJ 2025.3+#8796
pq merged 1 commit intoflutter:mainfrom
lukemmtt:fix/display-name-reflection

Conversation

@lukemmtt
Copy link
Contributor

@lukemmtt lukemmtt commented Feb 15, 2026

Summary

  • Fix IllegalAccessException that silently prevents device names from appearing in Run/Debug tab titles on IntelliJ 2025.3 (Android Studio Panda)
  • Use RunContentDescriptor.setDisplayName() API instead of reaching into a private field's internal type

Fixes #8795

Problem

In IntelliJ 2025.3, the myDisplayNameView field on RunContentDescriptor changed from MutableReactiveProperty (a public class) to MutableStateFlow (whose runtime class StateFlowImpl is package-private). The existing reflection code calls Method.invoke() on the setValue method found via getMethod(), which throws IllegalAccessException when the declaring class is not accessible — even though the method itself is public. The exception is caught silently, so run tabs show "main.dart" instead of "main.dart (macOS)".

Regression introduced by JetBrains/intellij-community@aaa88849ba4c (Jul 14, 2025).

Fix

Replace the field-level reflection (getDeclaredField("myDisplayNameView")getMethod("setValue")invoke()) with a call to RunContentDescriptor.setDisplayName(), an existing protected method on RunContentDescriptor. This calls a stable API method on a public class rather than depending on the internal type of a private field.

An alternative minimal fix would be to add setValueMethod.setAccessible(true) before the existing invoke() call, but calling a stable API method on a public class is more resilient than depending on the internal type of a private field.

Before & After

Before fix

Screenshot 2026-02-15 at 2 53 56 PM

After fix:

Screenshot 2026-02-15 at 2 52 38 PM
  • I've reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:
  • See our contributor guide 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 and use dart format.
  • Java and Kotlin contributions should strive to follow Java and Kotlin best practices (discussion).

IntelliJ commit aaa88849ba4c (Jul 2025) changed the myDisplayNameView
field type from MutableReactiveProperty (a public class) to
MutableStateFlow (whose runtime class StateFlowImpl is package-private).
The existing reflection called Method.invoke() on a public setValue
method, which throws IllegalAccessException when the declaring class is
not accessible. The exception was caught silently, so device names never
appeared in run tabs.

Replace the field-level reflection with a call to
RunContentDescriptor.setDisplayName(), a protected method added in
IntelliJ commit b923077ed65d (Sep 2025). This calls a stable API method
on a public class rather than depending on the internal type of a
private field.

Fixes flutter#8795
@pq
Copy link
Collaborator

pq commented Feb 18, 2026

Hey wow. This is really fantastic. Thanks especially for the thoughtful write-up.

I do so wish there was a way around this that didn't lean on reflection but this is a lot safer than what was there so it feels like a win.

I wonder if this could be the root of #8794 too?

@lukemmtt
Copy link
Contributor Author

lukemmtt commented Feb 18, 2026

Glad to help! This feature is pretty helpful and important for my workflow, so when I found those labels to be removed, my first thought was "why on earth did they remove the labels!"; I was dying to know who was behind the decision and how / why that choice was made, which started me on this wild goose chase, scouring the IntelliJ codebase before finally realizing (1) it's not an Android studio feature, and (2) it wasn't an intentional removal, but rather just a regression 😂

As for the reflection approach, yeah not my favorite pattern, & I'm open to others, seems robust enough for me though, now that I understand it.

@pq
Copy link
Collaborator

pq commented Feb 18, 2026

Awesome. This LGTM, so let's land it. We may have missed today's dev-channel build, but it should be in tomorrow's and you can grab it and enjoy the fruits of your labor!

Instructions here if you're game to try that: https://github.com/flutter/flutter-intellij/blob/main/docs/Dev-Channel.md

@pq pq added this to the M90 milestone Feb 18, 2026
Copy link
Collaborator

@pq pq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💎

@pq pq merged commit be8bcce into flutter:main Feb 18, 2026
7 checks passed
@pq
Copy link
Collaborator

pq commented Feb 18, 2026

One thing that occurs to me...

We should confirm (if you haven't already) that setDisplayName is available in 251.0; if not, we should add back the old bits as a fallback.

For extra credit, we might consider adding a test that asserts that we can access something that can set a display name. Maybe introducing a function like:

@visibleForTesting
static @Nullable BiConsumer<RunContentDescriptor, String> getDisplaySetter() { ... }

where the test could be something like

assertNotNull(LaunchState.getDisplaySetter());

@lukemmtt: WDYT?

@lukemmtt
Copy link
Contributor Author

lukemmtt commented Feb 18, 2026

Good thinking; I checked the 251 branch source and can confirm setDisplayName is there; it was added back in February 2024 (~243 ish), well before build 251. No fallback needed.

As for the test, good idea, sorry that slipped my mind! I went ahead and implemented your suggestion with getDisplaySetter(); PR here: #8809

Thank you @pq!

lukemmtt added a commit to lukemmtt/flutter-intellij that referenced this pull request Feb 18, 2026
…liJ build

Extracts the reflection lookup into a @VisibleForTesting getDisplaySetter() method and adds a test asserting it returns non-null. Serves as a regression guard: if JetBrains removes or renames setDisplayName in a future build, the test will fail loudly rather than the feature silently breaking for users.

Follow-up to flutter#8796.
@pq
Copy link
Collaborator

pq commented Feb 18, 2026

That's awesome. Thanks so much! Looking at your PR now...

pq pushed a commit that referenced this pull request Feb 18, 2026
…liJ build (#8809)

Follow-up to #8796.

Extracts the reflection lookup into a `@VisibleForTesting
getDisplaySetter()` method and adds a test asserting it returns
non-null. This serves as a regression guard: if JetBrains removes or
renames `setDisplayName` in a future build, the test will fail loudly
rather than the feature silently breaking for users (which was the
original failure mode in #8795).

Verified locally by running the test against the real SDK (passes), then
temporarily corrupting the method name to `setDisplayName_doesNotExist`
(fails with the expected assertion message).

I've reviewed the contributor guide and applied the relevant portions to
this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Device labels missing from run tabs on IntelliJ 2025.3 / Android Studio Panda

2 participants

Comments