-
Notifications
You must be signed in to change notification settings - Fork 329
Description
- Flutter IntelliJ Plugin Version: 87.0
- IDE: Android Studio 2025.1.2 Narwhal Feature Drop
- Operating System: Windows 11
Problem Description
When running a Flutter application, the "Run" tool window icon on the side stripe breaks. It may appear empty, multiple, unclickable, or fail to update its state. This is caused by an unhandled java.lang.ClassCastException
that occurs when the IDE attempts to apply a status badge (e.g., a green overlay for a successful run) to the tool window icon.
The core issue is that the IDE's UI framework expects the icon to implement com.intellij.openapi.util.ScalableIcon
for proper handling on HiDPI displays, but the ToolWindowBadgeUpdater$BadgeIcon
class provided by the Flutter plugin does not.
Full Stack Trace
The following exception is thrown when the tool window icon attempts to update:
java.lang.ClassCastException: class io.flutter.toolwindow.ToolWindowBadgeUpdater$BadgeIcon cannot be cast to class com.intellij.openapi.util.ScalableIcon (io.flutter.toolwindow.ToolWindowBadgeUpdater$BadgeIcon is in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @4f08d22; com.intellij.openapi.util.ScalableIcon is in unnamed module of loader com.intellij.util.lang.PathClassLoader @4cb2c100)
at com.intellij.openapi.wm.impl.SquareStripeButtonKt.createPresentation$lambda$0(SquareStripeButton.kt:304)
at com.intellij.util.concurrency.SynchronizedClearableLazy._get_value_$lambda$1$lambda$0(SynchronizedClearableLazy.kt:41)
at java.base/java.util.concurrent.atomic.AtomicReference.updateAndGet(Unknown Source)
at com.intellij.util.concurrency.SynchronizedClearableLazy.getValue(SynchronizedClearableLazy.kt:40)
at com.intellij.util.concurrency.SynchronizedClearableLazy.get(SynchronizedClearableLazy.kt:28)
at com.intellij.openapi.actionSystem.Presentation.setIcon(Presentation.java:319)
at com.intellij.openapi.wm.impl.SquareAnActionButton.isSelected(SquareStripeButton.kt:345)
at com.intellij.openapi.actionSystem.ToggleAction.update(ToggleAction.java:85)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareUpdate$lambda$2(ActionUtil.kt:236)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareUpdate(ActionUtil.kt:254)
at com.intellij.openapi.actionSystem.impl.ActionButton.update(ActionButton.java:339)
Proposed Solution and Patch
The issue can be resolved by modifying ToolWindowBadgeUpdater$BadgeIcon
to correctly implement the ScalableIcon
interface, including all methods required by modern platform versions.
Here is the patched version of the class that resolves the problem:
package io.flutter.toolwindow;
import com.intellij.openapi.util.ScalableIcon;
import javax.swing.*;
import java.awt.*;
class ToolWindowBadgeUpdater$BadgeIcon implements Icon, ScalableIcon {
private final Icon baseIcon;
private final Color overlayColor;
public ToolWindowBadgeUpdater$BadgeIcon(Icon baseIcon, Color overlayColor) {
this.baseIcon = baseIcon;
this.overlayColor = overlayColor;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
baseIcon.paintIcon(c, g, x, y);
Graphics2D g2d = (Graphics2D) g.create();
try {
g2d.translate(x, y);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
g2d.setColor(overlayColor);
g2d.fillRect(0, 0, getIconWidth(), getIconHeight());
} finally {
g2d.dispose();
}
}
@Override
public int getIconWidth() {
return baseIcon.getIconWidth();
}
@Override
public int getIconHeight() {
return baseIcon.getIconHeight();
}
// FIX #1: Implement the scale() method from ScalableIcon.
// This implementation is robust: it attempts to delegate scaling to the base
// icon and returns a new BadgeIcon with the result.
@Override
public Icon scale(float scaleFactor) {
if (baseIcon instanceof ScalableIcon) {
Icon scaledBaseIcon = ((ScalableIcon) baseIcon).scale(scaleFactor);
return new ToolWindowBadgeUpdater$BadgeIcon(scaledBaseIcon, this.overlayColor);
}
return this;
}
// FIX #2: Implement the getScale() method, required by modern platform versions.
// This delegates to the base icon if possible, providing a sensible default.
@Override
public float getScale() {
if (baseIcon instanceof ScalableIcon) {
return ((ScalableIcon) baseIcon).getScale();
}
return 1.0f;
}
}
Verification and Testing
I have successfully patched the plugin and verified the fix. The process was as follows:
- Set up a minimal Gradle project using the
org.jetbrains.intellij
plugin. - Configured the project to compile against the exact platform version of my IDE (2025.1.2) to avoid ClassLoader conflicts.
- Compiled the patched
ToolWindowBadgeUpdater$BadgeIcon.class
with the fixes above. It was compiled with JDK 21 to match the original plugin's bytecode version (65.0). - Manually updated the flutter-intellij-87.0.jar by injecting the new .class file using the jar command-line tool.
Result:
With the patched class, the ClassCastException
is resolved. The Run tool window icon now correctly updates and displays its green badge overlay without crashing, restoring the intended functionality.
Hope this detailed report is helpful for incorporating a permanent fix into an upcoming release.




