Skip to content

Commit b9d28de

Browse files
committed
Blend Modes #5
1 parent 59fa73a commit b9d28de

File tree

4 files changed

+110
-139
lines changed

4 files changed

+110
-139
lines changed

lib/codelessly_sdk.dart

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export 'src/model/privacy_base.dart';
2525
export 'src/model/publish_source.dart';
2626
export 'src/model/sdk_publish_model.dart';
2727
export 'src/transformers/transformers.dart';
28+
export 'src/transformers/utils/blend_mask.dart';
2829
export 'src/ui/codelessly_context.dart';
2930
export 'src/ui/codelessly_widget.dart';
3031
export 'src/ui/codelessly_widget_controller.dart';

lib/src/transformers/node_transformers/passive_rectangle_transformer.dart

+4-132
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import 'dart:math' hide log;
2-
import 'dart:ui' as ui;
32

43
import 'package:codelessly_api/codelessly_api.dart';
54
import 'package:collection/collection.dart';
65
import 'package:flutter/material.dart';
7-
import 'package:flutter/rendering.dart';
86
import 'package:vector_math/vector_math_64.dart' as vec_math;
97

108
import '../../../codelessly_sdk.dart';
@@ -580,7 +578,7 @@ List<Widget> buildFills(
580578
for (final model in node.fills.reversed) {
581579
final index = node.fills.indexOf(model);
582580
if (!model.visible) continue;
583-
if (model.blendMode != BlendModeC.srcOver) {
581+
if (settings.withBlendModes && model.blendMode != BlendModeC.srcOver) {
584582
lastBlendTree.add(model);
585583
continue;
586584
}
@@ -608,9 +606,9 @@ List<Widget> buildFills(
608606
for (final paint in lastBlendTree.reversed)
609607
if (paint.visible)
610608
Positioned.fill(
611-
child: PaintBlendMask(
612-
model: paint,
613-
image: null,
609+
child: BlendMask(
610+
key: ObjectKey(paint.blendMode),
611+
blendMode: paint.blendMode.flutterBlendMode,
614612
child: buildFill(
615613
node,
616614
index: node.fills.indexOf(paint),
@@ -827,129 +825,3 @@ class RelativeTransform {
827825
skew: skew ?? this.skew,
828826
);
829827
}
830-
831-
class BlendMask extends SingleChildRenderObjectWidget {
832-
final List<BlendMode> blendModes;
833-
final double opacity;
834-
835-
const BlendMask({
836-
required this.blendModes,
837-
this.opacity = 1.0,
838-
super.key,
839-
required Widget super.child,
840-
});
841-
842-
@override
843-
RenderObject createRenderObject(context) =>
844-
RenderBlendMask(blendModes, opacity);
845-
846-
@override
847-
void updateRenderObject(BuildContext context, RenderBlendMask renderObject) {
848-
renderObject.blendModes = blendModes;
849-
renderObject.opacity = opacity;
850-
}
851-
}
852-
853-
class RenderBlendMask extends RenderProxyBox {
854-
List<BlendMode> blendModes;
855-
double opacity;
856-
857-
RenderBlendMask(this.blendModes, this.opacity);
858-
859-
@override
860-
void paint(context, offset) {
861-
// Complex blend modes can be raster cached incorrectly on the Skia backend.
862-
// context.setWillChangeHint();
863-
for (var blend in blendModes) {
864-
context.canvas.saveLayer(
865-
offset & size,
866-
Paint()
867-
..blendMode = blend
868-
..color = Color.fromARGB((opacity * 255).round(), 255, 255, 255),
869-
);
870-
}
871-
super.paint(context, offset);
872-
context.canvas.restore();
873-
}
874-
}
875-
876-
class PaintBlendMask extends SingleChildRenderObjectWidget {
877-
final PaintModel _model;
878-
final ui.Image? _image;
879-
880-
const PaintBlendMask({
881-
required PaintModel model,
882-
ui.Image? image,
883-
super.key,
884-
super.child,
885-
}) : _model = model,
886-
_image = image;
887-
888-
@override
889-
RenderObject createRenderObject(context) {
890-
return RenderPaintBlendMask(_model, _image);
891-
}
892-
893-
@override
894-
void updateRenderObject(
895-
BuildContext context, RenderPaintBlendMask renderObject) {
896-
renderObject._model = _model;
897-
renderObject._image = _image;
898-
}
899-
}
900-
901-
class RenderPaintBlendMask extends RenderProxyBox {
902-
PaintModel _model;
903-
ui.Image? _image;
904-
905-
RenderPaintBlendMask(PaintModel model, ui.Image? image)
906-
: _model = model,
907-
_image = image;
908-
909-
Paint makePaint(Rect bounds) {
910-
Paint paint = Paint()..blendMode = _model.blendMode.flutterBlendMode;
911-
912-
switch (_model.type) {
913-
case PaintType.solid:
914-
Color? color = _model.toFlutterColor();
915-
if (color != null) {
916-
paint.color = color;
917-
}
918-
case PaintType.gradientLinear:
919-
case PaintType.gradientRadial:
920-
case PaintType.gradientAngular:
921-
case PaintType.gradientDiamond:
922-
paint.shader = retrieveGradient(_model)?.createShader(bounds);
923-
case PaintType.image:
924-
if (_image case ui.Image image) {
925-
paint.shader = ImageShader(
926-
image,
927-
TileMode.clamp,
928-
TileMode.clamp,
929-
Matrix4.identity().storage,
930-
);
931-
}
932-
case PaintType.emoji:
933-
break;
934-
}
935-
936-
return paint;
937-
}
938-
939-
@override
940-
void paint(context, offset) {
941-
// Complex blend modes can be raster cached incorrectly on the Skia backend.
942-
// context.setWillChangeHint();
943-
context.canvas.saveLayer(
944-
offset & size,
945-
// makePaint(offset & size),
946-
Paint()
947-
..blendMode = _model.blendMode.flutterBlendMode
948-
..color = Colors.white,
949-
);
950-
951-
super.paint(context, offset);
952-
953-
context.canvas.restore();
954-
}
955-
}
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter/rendering.dart';
3+
4+
/// A widget that applies a [BlendMode] to its child.
5+
///
6+
/// The [BlendMask] widget uses a [RenderBlendMask] to apply the specified
7+
/// [blendMode] to its child widget during the paint phase. This can be used to
8+
/// blend the child widget with the background or to create various visual
9+
/// effects.
10+
///
11+
/// {@tool snippet}
12+
/// This example shows how to use the [BlendMask] widget to apply a
13+
/// [BlendMode.multiply] effect to an image.
14+
///
15+
/// ```dart
16+
/// BlendMask(
17+
/// blendMode: BlendMode.multiply,
18+
/// child: Image.asset('assets/my_image.png'),
19+
/// );
20+
/// ```
21+
/// {@end-tool}
22+
///
23+
/// See also:
24+
///
25+
/// * [BlendMode], which describes the different ways to blend a source image
26+
/// with the destination image.
27+
class BlendMask extends SingleChildRenderObjectWidget {
28+
/// The blend mode to apply to the child widget.
29+
final BlendMode blendMode;
30+
31+
/// Creates a widget that blends its child using the given [blendMode].
32+
///
33+
/// The [blendMode] argument must not be null.
34+
const BlendMask({
35+
required this.blendMode,
36+
super.key,
37+
super.child,
38+
});
39+
40+
@override
41+
RenderObject createRenderObject(BuildContext context) {
42+
return RenderBlendMask(blendMode);
43+
}
44+
45+
@override
46+
void updateRenderObject(BuildContext context, RenderBlendMask renderObject) {
47+
renderObject.blendMode = blendMode;
48+
}
49+
}
50+
51+
/// A render object that applies a [BlendMode] to its child during painting.
52+
///
53+
/// The [RenderBlendMask] uses a [Paint] object with the specified [blendMode]
54+
/// and applies it to the child by saving a layer with that paint during the
55+
/// paint phase.
56+
///
57+
/// This render object is typically used by the [BlendMask] widget.
58+
class RenderBlendMask extends RenderProxyBox {
59+
BlendMode _blendMode;
60+
61+
/// The blend mode to apply to the child during painting.
62+
///
63+
/// Changing this value will cause the render object to repaint.
64+
BlendMode get blendMode => _blendMode;
65+
66+
set blendMode(BlendMode value) {
67+
if (_blendMode == value) return;
68+
_blendMode = value;
69+
blender.blendMode = _blendMode;
70+
markNeedsPaint();
71+
}
72+
73+
/// The [Paint] object used to blend the child.
74+
///
75+
/// It is initialized with the [_blendMode] and a white color.
76+
final Paint blender;
77+
78+
/// Creates a [RenderBlendMask] with the given [blendMode].
79+
///
80+
/// The [blendMode] must not be null.
81+
RenderBlendMask(BlendMode blendMode)
82+
: _blendMode = blendMode,
83+
blender = Paint()
84+
..blendMode = blendMode
85+
..color = Colors.white;
86+
87+
@override
88+
void paint(PaintingContext context, Offset offset) {
89+
// Complex blend modes can be raster cached incorrectly on the Skia backend.
90+
context.setWillChangeHint();
91+
context.canvas.saveLayer(offset & size, blender);
92+
93+
super.paint(context, offset);
94+
95+
context.canvas.restore();
96+
}
97+
}

lib/src/transformers/widget_node_transformer_manager.dart

+8-7
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@ abstract class WidgetNodeTransformerManager extends NodeTransformerManager<
6767
);
6868
}
6969

70+
/// Applies a blending mask to the widget, which saves the widget's
71+
/// rendered layer to the canvas, allowing other blend masks to be applied
72+
/// on top of it.
7073
Widget applyWidgetBlendMode(
7174
BaseNode node,
72-
Widget widget,
73-
) {
74-
return BlendMask(
75-
blendModes: [BlendMode.srcOver],
76-
child: widget,
77-
);
78-
}
75+
Widget widget,) =>
76+
BlendMask(
77+
blendMode: BlendMode.srcOver,
78+
child: widget,
79+
);
7980

8081
/// Convenience method to handle widget rotation.
8182
///

0 commit comments

Comments
 (0)