Skip to content

Instantly share code, notes, and snippets.

@NicolaVerbeeck
Created August 27, 2025 10:42
Show Gist options
  • Save NicolaVerbeeck/69c0dcf99030ccf6e828901d3e13e998 to your computer and use it in GitHub Desktop.
Save NicolaVerbeeck/69c0dcf99030ccf6e828901d3e13e998 to your computer and use it in GitHub Desktop.
Blend mode fun times
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(
Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: [
RepaintBoundary(child: CustomPaint(painter: _DrawRectPainter())),
Positioned(
left: 100,
top: 100,
width: 253.844,
height: 238.356,
child: BlendModeWidget(
blendMode: BlendMode.difference,
child: CustomPaint(painter: ImagePainter()),
),
),
],
),
),
);
}
class ImagePainter extends CustomPainter {
ImagePainter({super.repaint});
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
const Rect.fromLTRB(10, 10, 100, 100),
Paint()
..style = PaintingStyle.fill
..color = Colors.red,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class _DrawRectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final path = Path();
path.addRect(const Rect.fromLTWH(0, 0, 594, 840));
path.close();
canvas.drawPath(
path,
Paint()
..style = PaintingStyle.fill
..color = const Color(0xFFD9D9D9),
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
class BlendModeWidget extends StatelessWidget {
final BlendMode blendMode;
final Widget child;
const BlendModeWidget({
super.key,
required this.child,
this.blendMode = BlendMode.srcOver,
});
@override
Widget build(BuildContext context) =>
BlendMaskWidget(key: key, blendMode: blendMode, child: child);
}
class BlendMaskWidget extends SingleChildRenderObjectWidget {
final BlendMode blendMode;
const BlendMaskWidget({required this.blendMode, super.key, super.child});
@override
RenderObject createRenderObject(context) {
return RenderBlendMask(blendMode: blendMode);
}
@override
void updateRenderObject(BuildContext context, RenderBlendMask renderObject) {
super.updateRenderObject(context, renderObject);
renderObject.blendMode = blendMode;
}
}
/// Responsible for actually painting the opacity/blendmode.
class RenderBlendMask extends RenderProxyBox {
BlendMode _blendMode;
set blendMode(BlendMode value) {
if (_blendMode != value) {
_blendMode = value;
markNeedsCompositedLayerUpdate();
markNeedsPaint();
}
}
@override
bool get alwaysNeedsCompositing => child != null;
RenderBlendMask({required BlendMode blendMode}) : _blendMode = blendMode;
@override
void paint(PaintingContext context, Offset offset) {
final paint = Paint()..blendMode = _blendMode;
// Create a composition layer, with a blendmode applied.
context.canvas.saveLayer(null, paint);
super.paint(context, offset);
context.canvas.restore();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment