ShaderMask in Flutter: Painting Gradient Effects on Any Widget
Use ShaderMask to paint gradients, blends, and custom shader effects over any widget — gradient text, fading edges, and tinted icons.
Published on • November 2, 2026
AI Assistant

Gradient text and tinted icons are staples of modern UI, yet Flutter ships no GradientText. ShaderMask fills the gap: it paints the child, then masks the result with a shader you supply (docs.flutter.dev).
How shaderCallback Works
shaderCallback(Rect rect) builds a Shader for the child’s painted bounds — always derive the gradient from rect so the effect tracks resizes; blendMode (default BlendMode.modulate) decides how it composites:
ShaderMask(
shaderCallback: (rect) => const LinearGradient(
colors: [Color(0xFF6D28D9), Color(0xFFEC4899)],
).createShader(rect),
child: const Text('REDLINESOFT',
style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)),
)
Gradient Text with BlendMode.srcIn
The canonical gradient-text pattern keeps source pixels only where the shader paints alpha — BlendMode.srcIn:
ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (b) => const LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Colors.deepPurple, Colors.pinkAccent],
).createShader(b),
child: Text('Pro Plan', style: Theme.of(context).textTheme.headlineMedium),
)
Fading Edges and Gradient Icons
Swap stops for Colors.transparent to fade a child out — wrap a horizontal ListView with the same mask to fade its edges — or tint any monochrome widget (Icon, SvgPicture, a Row of glyphs):
ShaderMask(
shaderCallback: (b) => const LinearGradient(
begin: Alignment.centerLeft, end: Alignment.centerRight,
colors: [Colors.transparent, Colors.black, Colors.transparent],
).createShader(b),
child: const Icon(Icons.star_rounded, size: 56, color: Colors.white),
)
End-to-End: Gradient Badge Widget
class GradientBadge extends StatelessWidget {
const GradientBadge({super.key, required this.label});
final String label;
@override
Widget build(BuildContext context) => ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (b) =>
const LinearGradient(colors: [Colors.amber, Colors.redAccent]).createShader(b),
child: Text(label, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w700)),
);
}
Gotchas and Best Practices
saveLayercost:ShaderMaskrenders the child into an offscreen layer before compositing — a real frame-time hit on complex subtrees. Use aContainer+BoxDecorationgradient when you only need a background.- Dispose your controllers: if an
AnimationControllerdrives the shader (shimmer, sweep), dispose it or the layer outlives the widget. - No layout impact: the mask only affects painting; hit testing and semantics pass through unchanged.
- Accessibility: check contrast at both gradient ends — a light stop can silently fail WCAG against the background.
Takeaway: reach for ShaderMask when you must recolor a widget’s pixels, and for BoxDecoration when you only need a gradient background.