Clipping, blendmodi en compositing van lagen
Gebruik clippingpaden en blendmodi om geavanceerde visuele effecten met lagen samen te stellen.
Clipping, blendmodi en compositing van lagen is een gratis Mobiele ontwikkeling met Flutter-les op CoddyKit. Dit is les 3 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Mobiele ontwikkeling met Flutter. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Mobiele ontwikkeling met Flutter bevat in totaal 4 lessen.
Waarom compositing belangrijk is
Geavanceerde beelden in Flutter komen zelden uit één vorm. Gloeiende ringen, matglazen overlays, gemaskeerde miniaturen en badges met "uitsparingen" worden opgebouwd door meerdere tekenbewerkingen over elkaar te leggen en te bepalen hoe elke nieuwe laag wordt gecombineerd met wat al op het canvas staat.
Drie hulpmiddelen sturen dit aan op een Canvas:
- Clipping — beperkt waar er getekend kan worden (
clipPath,clipRect,clipRRect). - Overvloeimodi — bepalen met welke berekening een nieuwe laag wordt samengevoegd met de pixels eronder (
BlendModeop eenPaint). - Lagen —
saveLayermaakt een buffer buiten het scherm, zodat een overvloeimodus op een hele groep wordt toegepast in plaats van op slechts één vorm.
In deze les verbind je alle drie.
Het canvas clippen
Binnen CustomPainter.paint verkleint een clip het tekengebied. Alles wat je na de clip tekent, wordt tot dat gebied beperkt totdat de volgende restore() wordt aangeroepen.
canvas.clipRect(rect)— rechthoekige clip.canvas.clipRRect(rrect)— clip met afgeronde rechthoek (ideaal voor miniaturen van kaarten).canvas.clipPath(path)— clip met een willekeurige vorm.
Combineer clipping altijd met save() / restore(), zodat de clip niet doorwerkt in latere tekenbewerkingen.
class ClipPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = const Color(0xFF2196F3);
canvas.save();
final rrect = RRect.fromRectAndRadius(
Offset.zero & size,
const Radius.circular(24),
);
canvas.clipRRect(rrect);
// Fills the whole canvas, but only the rounded area shows.
canvas.drawRect(Offset.zero & size, paint);
canvas.restore();
}
@override
bool shouldRepaint(covariant ClipPainter oldDelegate) => false;
}Clippen met een willekeurig pad
Voor niet-rechthoekige maskers (tickets, golven en badges) bouw je een Path en geef je die door aan clipPath. De klassieke ticketvorm gebruikt aan beide zijkanten twee holle bogen.
Stel doAntiAlias: true in (de standaardwaarde) voor vloeiende randen. Voor geanimeerde clips die elk frame veranderen, kan doAntiAlias: false iets sneller zijn.
Path ticketPath(Size size, double notch) {
final r = notch;
return Path()
..moveTo(0, 0)
..lineTo(size.width, 0)
..lineTo(size.width, size.height / 2 - r)
..arcToPoint(
Offset(size.width, size.height / 2 + r),
radius: Radius.circular(r),
clockwise: false,
)
..lineTo(size.width, size.height)
..lineTo(0, size.height)
..lineTo(0, size.height / 2 + r)
..arcToPoint(
Offset(0, size.height / 2 - r),
radius: Radius.circular(r),
clockwise: false,
)
..close();
}Wat een overvloeimodus echt doet
Een BlendMode definieert de formule die de bron (wat je nu tekent) combineert met de bestemming (de pixels die er al staan). De standaardwaarde is BlendMode.srcOver — normale alfacompositie.
Nuttige groepen:
- Porter-Duff (geometrie/maskering):
srcIn,dstIn,srcOut,dstATop,clear. - Scheidbare kleurrekenkunde:
multiply,screen,overlay,plus(gloed door optellen),difference.
Je wijst deze toe aan de Paint: paint.blendMode = BlendMode.multiply.
Overvloeimodi hebben een laag nodig
Dit is de belangrijkste valkuil: veel overvloeimodi (srcIn, dstIn, multiply over transparantie enzovoort) werken alleen correct wanneer er een expliciete laag is die als bestemming fungeert.
canvas.saveLayer(bounds, paint) reserveert een buffer buiten het scherm. Tekenbewerkingen tussen saveLayer en restore worden in die buffer verzameld; bij restore wordt de buffer weer samengesteld met de overvloeimodus van de verf van de laag.
Zonder saveLayer zou een tekening met srcIn het volledige scherm als bestemming gebruiken — meestal wordt er dan meer gewist dan je bedoelde.
void paint(Canvas canvas, Size size) {
final bounds = Offset.zero & size;
// 1. Open an offscreen layer.
canvas.saveLayer(bounds, Paint());
// 2. Destination: a circle.
canvas.drawCircle(
size.center(Offset.zero),
size.shortestSide / 2,
Paint()..color = const Color(0xFFFFFFFF),
);
// 3. Source with srcIn keeps only the overlap with the circle.
canvas.drawRect(
bounds,
Paint()
..shader = const LinearGradient(
colors: [Color(0xFFFF0080), Color(0xFF7928CA)],
).createShader(bounds)
..blendMode = BlendMode.srcIn,
);
// 4. Composite the layer back to the canvas.
canvas.restore();
}Een verloop in een vorm maskeren
Het patroon uit de vorige scène is de gebruikelijke aanpak voor pictogrammen met een verloop of tekstmaskers:
- Teken eerst de ondoorzichtige maskervorm (die wordt de bestemming).
- Teken het verloop met
BlendMode.srcIn— het blijft alleen zichtbaar waar het masker ondoorzichtig is.
Draai de rollen om met dstIn: teken eerst het verloop en daarna een vorm met dstIn om alleen het verloop onder die vorm te behouden. Kies op basis van welke laag je als laatste wilt tekenen.
class GradientTextMask extends CustomPainter {
GradientTextMask(this.label);
final String label;
@override
void paint(Canvas canvas, Size size) {
final bounds = Offset.zero & size;
canvas.saveLayer(bounds, Paint());
final tp = TextPainter(
text: TextSpan(
text: label,
style: const TextStyle(
fontSize: 64, fontWeight: FontWeight.bold,
color: Color(0xFFFFFFFF),
),
),
textDirection: TextDirection.ltr,
)..layout();
tp.paint(canvas, Offset.zero); // destination = glyphs
canvas.drawRect(
bounds,
Paint()
..shader = const LinearGradient(
colors: [Color(0xFF00C6FF), Color(0xFF0072FF)],
).createShader(bounds)
..blendMode = BlendMode.srcIn, // gradient fills glyphs
);
canvas.restore();
}
@override
bool shouldRepaint(covariant GradientTextMask old) => old.label != label;
}Gloed door optellen met BlendMode.plus
BlendMode.plus telt de kleurkanalen van de bron en de bestemming bij elkaar op (afgekapt op 1,0). Overlappende heldere vormen worden helderder — perfect voor neonlichten, vonken en lichtgloed.
Combineer dit met een MaskFilter.blur om elke vorm zachter te maken tot een halo. Omdat plus zich opstapelt, leveren meerdere vervaagde cirkels een overtuigende gloed op zonder afbeeldingsbestand.
void drawGlow(Canvas canvas, Offset c, double r, Color color) {
final glow = Paint()
..color = color
..blendMode = BlendMode.plus
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12);
canvas.drawCircle(c, r, glow);
// A second, tighter core makes the center read brighter.
canvas.drawCircle(c, r * 0.5,
Paint()..color = color..blendMode = BlendMode.plus);
}Gaten maken met BlendMode.clear
Als je een transparant gat wilt maken in een al getekende laag, teken je een vorm met BlendMode.clear. Hiermee worden bestemmingspixels gewist (de alfawaarde wordt 0) op alle plaatsen waar de bron ligt.
Dit moet binnen een saveLayer gebeuren — anders zou clear door alles achter het canvas heen wissen, inclusief widgets eronder. Het effect van een spotlight of overlay met uitsparing is het kenmerkende gebruik hiervan.
void paint(Canvas canvas, Size size) {
final bounds = Offset.zero & size;
canvas.saveLayer(bounds, Paint());
// Dim scrim covering everything.
canvas.drawRect(bounds, Paint()..color = const Color(0xCC000000));
// Cut a clear circular "spotlight" through the scrim.
canvas.drawCircle(
size.center(Offset.zero),
size.shortestSide / 4,
Paint()..blendMode = BlendMode.clear,
);
canvas.restore();
}Verf van de laag versus verf per vorm
Er zijn twee verschillende verfobjecten in het spel en die door elkaar halen is een veelgemaakte fout:
- De verf per vorm (doorgegeven aan
drawCircle,drawRect…) bepaalt hoe die ene vorm wordt overvloeid met de inhoud van de huidige laag. - De verf van de laag (doorgegeven aan
saveLayer) bepaalt hoe de volledig opgebouwde laag bijrestore()weer wordt samengevoegd en kanopacityofcolorFilterop een groep toepassen.
Wil je één schuifregelaar gebruiken om een hele samengestelde groep gelijkmatig te laten vervagen? Zet de doorzichtigheid op de verf van de laag, niet op elke vorm — doorzichtigheid per vorm zou ervoor zorgen dat overlappingen door elkaar heen zichtbaar worden.
void paint(Canvas canvas, Size size) {
final bounds = Offset.zero & size;
// Group opacity: whole layer fades together.
final layerPaint = Paint()..color = const Color(0x80FFFFFF);
canvas.saveLayer(bounds, layerPaint);
final p = Paint()..color = const Color(0xFFE91E63);
canvas.drawCircle(const Offset(60, 60), 50, p);
canvas.drawCircle(const Offset(100, 60), 50, p); // overlap stays solid
canvas.restore(); // entire pair composited at 50% alpha
}Clip, overvloeien en laag samen
Een miniatuur met afgeronde hoeken, matglas en een glanzend verloop combineert alle drie de hulpmiddelen in deze volgorde:
- Clip naar een afgeronde rechthoek, zodat niets buiten de kaart komt.
saveLayer, zodat de glans als groep met de foto kan overvloeien.- Teken de foto en daarna een zachte lichtvlek met
BlendMode.softLightofoverlay.
De volgorde is allesbepalend: eerst clippen, de laag openen, de bestemming tekenen, de overvloeiende bron tekenen, herstellen en daarna de clip herstellen.
void paint(Canvas canvas, Size size, ui.Image photo) {
final bounds = Offset.zero & size;
canvas.save();
canvas.clipRRect(
RRect.fromRectAndRadius(bounds, const Radius.circular(20)),
);
canvas.saveLayer(bounds, Paint());
canvas.drawImageRect(
photo,
Offset.zero & Size(photo.width.toDouble(), photo.height.toDouble()),
bounds,
Paint(),
);
canvas.drawRect(
bounds,
Paint()
..shader = const LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Color(0x88FFFFFF), Color(0x11000000)],
).createShader(bounds)
..blendMode = BlendMode.softLight,
);
canvas.restore(); // close layer
canvas.restore(); // close clip
}Prestaties: lagen zijn niet gratis
Elke saveLayer reserveert een textuur buiten het scherm en dwingt een extra compositieronde af — de duurste bewerking die je per frame in een painter kunt uitvoeren. Gebruik dit bewust als hulpmiddel, niet als standaard.
- Geef de kleinst mogelijke grenzen door aan
saveLayer; een laag over het volledige scherm is veel duurder dan een kleine laag. - Geef de voorkeur aan een gewone
clipRRectmet directe tekenbewerkingen wanneer je geen overvloeien van groepen nodig hebt. - Cache statische composities (bijvoorbeeld via
ui.PictureRecordernaar eenui.Image) in plaats van de laag elk frame opnieuw op te bouwen. - Gebruik
Paint-objecten opnieuw; vermijd toewijzingen in het veelgebruiktepaint-pad.
Korte controle: het juiste hulpmiddel kiezen
Je wilt enkele vetgedrukte teksttekens binnen een CustomPainter vullen met een lineair verloop, en niets buiten de tekens mag worden ingekleurd. Welke aanpak is correct?
Samenvatting
Je kunt nu geavanceerde gelaagde effecten samenstellen op een Canvas:
- Clipping (
clipRect/clipRRect/clipPath) beperkt waar tekenbewerkingen terechtkomen — wikkel dit altijd insave()/restore(). - Overvloeimodi bepalen hoe een bron met de bestemming wordt samengevoegd:
srcIn/dstInvoor maskering,plusvoor een optellende gloed,clearvoor gaten,softLight/overlayvoor glans. - saveLayer geeft overvloeimodi een echte bestemmingsbuffer en maakt het mogelijk om groepsdoorzichtigheid of kleurfilters toe te passen via de verf van de laag.
- Maak onderscheid tussen de verf van de laag en de verf per vorm, teken de bestemming vóór de bron en houd lagen klein en schaars voor goede prestaties.
Gebruik lagen bewust, clip alleen wanneer je een masker nodig hebt en cache statische composities.
Leer Dart met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 88
Veelgestelde vragen
Is de les “Clipping, blendmodi en compositing van lagen” gratis?
Ja — je kunt hier op het web alle 3 lessen van het leerpad Mobiele ontwikkeling met Flutter, waaronder “Clipping, blendmodi en compositing van lagen”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Mobiele ontwikkeling met Flutter bevat in totaal 4 lessen.
Wat leer ik in “Clipping, blendmodi en compositing van lagen”?
Gebruik clippingpaden en blendmodi om geavanceerde visuele effecten met lagen samen te stellen. Je oefent met Mobiele ontwikkeling met Flutter door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met Mobiele ontwikkeling met Flutter te beginnen?
Ervaring vooraf is niet nodig. Mobiele ontwikkeling met Flutter op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.
Hoe lang duurt de les “Clipping, blendmodi en compositing van lagen”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over Mobiele ontwikkeling met Flutter?
Ja. Elke les over Mobiele ontwikkeling met Flutter bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- De Canvas-API: paden, paints en shaders
- Een interactieve aangepaste chart-widget bouwen
- Clipping, blendmodi en compositing van lagen
- Hit-testing en optimalisatie van repaints