Add a subtree blend-mode widget modifier
- Dominant language
- Dart
- Stars
- 800
- Forks
- 49
- Avg merge
- 3h 39m
- Merged PRs (30d)
- 22
Description
## Use case
Mix supports `BoxDecorationMix.backgroundBlendMode`, which blends a decoration's own background layers, and `ShaderMaskModifier`, which blends a shader with its child. It does not currently have the widget-level equivalent of CSS `mix-blend-mode`: compositing a complete rendered child subtree as one source against content already painted behind it.
This is needed when mapping design systems that use element-level blending. For example, Radix Themes applies `mix-blend-mode: multiply` to disabled Slider roots in light themes and `screen` in dark themes. Applying a blend mode independently to the track, range, thumbs, and shadows produces different output because those elements are not grouped before compositing.
Remix currently needs a bespoke render-object wrapper to preserve this behavior.
## Visual comparison
The same disabled Fortal Slider is rendered against the same backdrop. Only subtree compositing changes.

- **Before:** `BlendMode.srcOver` — standard painting, with no subtree layer.
- **After:** `BlendMode.multiply` — the complete Slider subtree is composited as one layer against the backdrop.
This comparison was rendered by Flutter with the theme, recipe, disabled state, value, dimensions, and backdrop held constant.
## Proposal
Add a Mix widget modifier for subtree blending, tentatively `BlendModeModifier` / `BlendModeModifierMix`, exposed through `WidgetModifierConfig`.
Proposed usage:
```dart
BoxStyler().wrap(.blendMode(BlendMode.multiply))
```
Desired behavior:
- Composite the complete painted child subtree against its backdrop using the selected `BlendMode`.
- Treat `BlendMode.srcOver` as a no-op fast path.
- Preserve layout, hit testing, and semantics.
- Participate in normal Mix property resolution and modifier merging.
- Account for child paint overflow and explicit clips when determining the blend layer bounds.
- Explicitly support or document the behavior for composited descendants such as `RepaintBoundary`.
A canvas implementation can group child painting with `saveLayer`, but retained/composited descendants cannot simply be enclosed by that canvas operation. The API should therefore either support them through a layer-based design or expose the limitation clearly.
This is distinct from both decoration `backgroundBlendMode` and `ShaderMaskModifier` because the source and destination operands are different.
## References
- [Radix Themes Slider uses element-level blend modes](https://github.com/radix-ui/themes/blob/1faff10ac26ae17f09944d418c6949b93fc6b566/packages/radix-ui-themes/src/components/slider.css#L249-L263)
- [Mix `BoxDecorationMix.backgroundBlendMode`](https://github.com/conceptadev/mix/blob/2daca01db3b146ec5bd0a466019b6a7d8dbdb02b/packages/mix/lib/src/properties/painting/decoration_mix.dart#L115-L161)
- [Mix `ShaderMaskModifier`](https://github.com/conceptadev/mix/blob/2daca01db3b146ec5bd0a466019b6a7d8dbdb02b/packages/mix/lib/src/modifiers/shader_mask_modifier.dart#L9-L69)
- [Current Remix subtree compositor](https://github.com/conceptadev/remix/blob/1d9c7ee97268c5e7708ca69a35d5cd09326832ac/packages/remix/lib/src/rendering/remix_blend_mode.dart)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading the existing ShaderMaskModifier and BoxDecorationMix references, then inspect Remix's packages/remix/lib/src/rendering/remix_blend_mode.dart. Determine how a widget modifier could composite a complete subtree while preserving layout, hit testing, semantics, overflow, clips, and composited descendants. Done means the proposed modifier is exposed through WidgetModifierConfig and its supported behavior and limitations are documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter
- Domain
- frontend, mobile-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100