An overview of ShaderMask
According to official documentation, ShaderMask
is a widget that applies a mask generated by a shader to its child.
What does this mean ? What is a Shader
? What does Mask
mean ?
I’ll try to explain its meaning with an example. Let’s say, for instance, to paint a house, you need two basic things:
- Color
- Paint tools (Brush, ladder and so on)
The tools such as brush which is used to apply color on the walls of a house, is nothing but a shader
. The color that is being applied, is a mask
and the house (wall) of course is a widget
. So, in simple terms, ShaderMask
is a widget that is used to apply effects / color on it’s child.
Let’s now see an example of wrapping a widget with ShaderMask
and what all properties and methods are offered by ShaderMask
to achieve the effect we want on it’s child. We will have a simple Container
with bounds along with an image as it’s child, as below:
body: Center(
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover)
)
)
When we wrap Container
with ShaderMask
, we also need to add required parameter named shaderCallback
shaderCallback
as the name indicates, is the callback that accepts bounds
for Rect
(stands for Rectangle) that helps to create the shader
for the given bounds, ie, the process of identifying the area where to start coloring.
Since we need to give effects to the child (or wall), we can make use of Gradients
class to apply colors / effects. This is usually done using:
- LinearGradient
- RadialGradient
- SweepGradient
Let’s take a look at them one by one and see how we can apply effects to the child.
LinearGradient
This accepts begin
and end
as two optional bounds followed by color
that accepts list of colors which will be applied to the child. Note that, begin
and end
accepts only Alignment
objects, so we need to pass regular alignments that indicates how the effect will be aligned, as below:
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red,
Colors.black
]
)
Here, the callback implementation isn’t complete yet, that means, we have dipped the brush in the color and are ready to paint the wall, but, we haven’t received a go-ahead to get started with the task. The last piece of this implementation is another method named createShaders()
that accepts the bounds
on which the colors are to be applied. Let’s make that call:
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red,
Colors.black
]
).createShader(bounds);
This method tells ShaderMask
to start applying / fill the colors on the child in the given rect
bounds. Now, if we run this, we’ll see:
Here, we just applied gradient on an image and ShaderMask
spanned those colors across the Container
. But this is not enough for us to make use of ShaderMask.
We also want to apply some effects on the image to make it more enhance, meaningful and appealing, right ?
How ? Using blendmode
property.
When we take picture from our phone, there’s an option to edit it and add more effects to it, like, increase brightness, add contrast, add shadows and so on. On the same lines, we can apply similar effects to the image using blendmode
property. It offers various options to apply effects on the image. Let’s see few of them.
BlendMode
BlendMode
effects work on the concept of source
and destination
. All types of blend mode effects are applied based on these two terms. You can read about these terms in detail here.
- BlendMode.color: This property mode simply paints the image with the given colors.
- BlendMode.colorBurn: This property paints the image with invert effects based on colors provided.
- BlendMode.colorDodge: This property paints the image with brightness based on the colors provided.
- BlendMode.clear: This property removes the
source
anddestination
and shows a transparent screen.
- BlendMode.src: Shorthand for
source
. This property shows the original / source widget on which the image is to be painted. In this case,Container
Variants of this blendmode are: srcIn, srcOut, srcOver, srcAtTop
- BlendMode.dst: Shorthand for destination. This property shows the original destination (image) only.
Variants of this blendmode are: dstIn, dstOut, dstOver, dstAtTop
There are few more modes that I’ll skip (for sake of length of this article) and will include them in my github repo here.
RadialGradient:
This shows the gradient / color effects in concentric circles. Along with various blend modes
, we can change appearance of the image as we need. Let’s see few examples:
body: Center(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return RadialGradient(
colors: [
Colors.green,
Colors.blue,
Colors.orange
]
).createShader(bounds);
}, blendMode: BlendMode.screen,
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover)
)
)
)
return RadialGradient(
colors: [Colors.green, Colors.blue, Colors.orange],
).createShader(bounds);
},blendMode: BlendMode.hardLight,
SweepGradient:
This shows the gradient / color effects in an arc. When it comes to arc, we think of angles, right ? Similarly, this gradient provides properties such as startAngle
and endAngle
to change appearance as required. Of course, the different blend modes
can be used to enhance the effect. Below is one such example:
return SweepGradient(
colors: [Colors.indigo, Colors.blue, Colors.green,Colors.yellow,Colors.orange,Colors.red],
startAngle: 0.1,
endAngle: 1,
).createShader(bounds);
},blendMode: BlendMode.softLight,
There are other properties that
Gradient
class provides specific toLinear
,Radial
andSweep
types we saw above, such astileMode
,focal
,radius
,center
,stops
and so on. Since these are specific toGradient
and notShaderMask
, I used only the minimum basic properties for demo. I’d suggest you try out these properties to see how they help to enhance and change appearance of the image.
Take a moment to note the impact blendMode
values make on the appearance of the image. You may compare the effect by removing the blendmode
property to see the image being painted and then putting back the blending again to see the difference / impact.
Mimicking photo booth using ShaderMask
As we saw above various gradients along with blend modes to achieve desired effects on an image, let’s put all this together in a demo to mimic an app.
Photo booth is a fun little app that can be used to apply effects on a picture from camera, as below:
Let’s try to recreate some of these effects using Gradient
and ShaderMask’s
blend modes.
- Sepia: This effect can be achieved by providing dark and light brown color and specifying
color
blend mode:
body: Center(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
colors: [Color(0xFF704214), Colors.brown],
).createShader(bounds);
},blendMode: BlendMode.color,
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover)
)
)
)
- Black & White: This effect can be achieved by providing white and black color and specifying
hue
blend mode:
body: Center(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
colors: [Colors.black, Colors.white],
).createShader(bounds);
},blendMode: BlendMode.hue,
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover)
)
)
)
- Thermal Camera: This type of effect is similar to scanning a normal picture through a Infrared radiation device and depending on colors we pass, we will see thermal variation of the picture. This effect can be achieved by providing
red
,yellow
andgreen
colors along withexclusion
blend mode.
body: Center(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
colors: [Colors.yellow, Colors.red, Colors.greenAccent],
).createShader(bounds);
},blendMode: BlendMode.exclusion,
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover)
)
)
)
- X-Ray: This property shows image in stark black and white with inverted effect. We can use combination of color hexa codes along with
difference
blend mode to achieve closest possible version of original x-ray effect:
body: Center(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF191a1b),
Color(0xFFc8dde7),
Color(0xFFa8d3e3),
Color(0xFF394346),
Color(0xFF293135),
],
).createShader(bounds);
},
blendMode: BlendMode.difference,
child: Container(
width: double.infinity,
height: double.infinity,
child: Image.asset('assets/jpg.jpg', fit: BoxFit.cover))))
Although the effects may not be similar to what Photo Booth shows, my idea was to show that similar effects can be achieved using ShaderMask
in combination with gradient and blend modes.
In this article, we saw how ShaderMask
widget can be helpful to apply color effects and change appearance of it’s child. Instead of image
, we can use other widgets too, such as a Text
.
With this, I barely scratched the surface of ShaderMask
and believe that the potential of using widget is huge by using various gradient properties in combination with blend modes.
That’s all I have for today. Thanks for reading and feel free to comment below your thoughts / suggestion / feedback on this topic.
I can be reached via Twitter and LinkedIn.
You may check out my Medium profile below for other articles I wrote on Flutter.