flame-engine / flame-engine/flame

TextComponent shadows are not affected by OpacityEffect

Open
#4,013 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
10.8k
Forks
1k
Avg merge
1d 20h
Merged PRs (30d)
21

Description

## Description

When an `OpacityEffect` runs on a `TextComponent` whose `TextPaint` style defines `shadows`, the glyph color fades but the shadows keep their original alpha. The shadows stay at full strength for the whole animation, so a fading text leaves a non-fading shadow or glow behind, which reads as a visual glitch.

There is also a related gotcha in the same code path: the first time the effect updates the paint, the style's fill color is replaced entirely by the component's `HasPaint` paint (white by default), so a colored text visibly snaps to white when the fade begins unless the user remembers to seed `paint.color` with the same color as the style.

## Cause

`TextComponent.onChanged` applies opacity through `TextRenderer.copyWithPaint`, and `TextPaint.copyWithPaint` only swaps the style's `foreground`:

```dart
@override
TextRenderer copyWithPaint(Paint paint) {
return copyWith(
(style) {
return style.copyWith(
foreground: paint,
);
},
);
}
```

`style.shadows` is left untouched, so shadow alpha never follows the component's opacity.

## Steps to reproduce

```dart
final text = TextComponent(
text: '+1 kr',
textRenderer: TextPaint(
style: const TextStyle(
color: Color(0xFF2E9940),
shadows: [Shadow(color: Color(0x99FFFFFF), blurRadius: 4)],
),
),
)..add(OpacityEffect.fadeOut(EffectController(duration: 1)));
```

## Expected behavior

The text and its shadows fade out together, in the text's own color.

## Actual behavior

The text snaps to white when the effect starts (foreground paint replaces the style color) and fades, while the white shadow stays at constant alpha until the component is removed.

## Suggested fix

In `TextPaint.copyWithPaint`, scale each shadow's color alpha by the incoming paint's alpha (and consider multiplying the paint color into the existing style color instead of replacing it, so `OpacityEffect` composes with a styled color the way it does for sprites).

Observed on the `perf/component-set-backing` branch, but the `copyWithPaint` implementation is the same on `main`.

Contributor guide

Open the contributing guide

Research direction

Start with TextComponent.onChanged and TextPaint.copyWithPaint, where OpacityEffect updates the paint. Trace how TextStyle.shadows and the styled foreground color are copied, then add coverage for fading colored text with shadows. Done means the text and its shadows fade together without the initial color snap.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, flutter
Domain
game-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.