An overview of ShaderMask

Darshan Kawar
Flutter Community
Published in
8 min readMay 1, 2020

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.color
  • BlendMode.colorBurn: This property paints the image with invert effects based on colors provided.
BlendMode.colorBurn
  • BlendMode.colorDodge: This property paints the image with brightness based on the colors provided.
BlendMode.colorDodge
  • BlendMode.clear: This property removes the source and destination and shows a transparent screen.
BlendMode.clear
  • BlendMode.src: Shorthand for source. This property shows the original / source widget on which the image is to be painted. In this case, Container
BlendMode.src

Variants of this blendmode are: srcIn, srcOut, srcOver, srcAtTop

  • BlendMode.dst: Shorthand for destination. This property shows the original destination (image) only.
BlendMode.dst

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)
)
)
)
RadialGradient with screen blendmode
return RadialGradient(
colors: [Colors.green, Colors.blue, Colors.orange],
).createShader(bounds);
},blendMode: BlendMode.hardLight,
RadialGradient with hardlight blend

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,
SweepGradient with softlight blend

There are other properties that Gradient class provides specific to Linear, Radial and Sweep types we saw above, such as tileMode , focal , radius , center , stops and so on. Since these are specific to Gradient and not ShaderMask, 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 and green colors along with exclusion 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.

--

--

Darshan Kawar
Flutter Community

Open Source Support Engineer For Flutter @nevercodeHQ. Android Nanodegree Certified. Previously, Android Automation Test Engineer.