Gradient text in Flutter

Tony Owen
2 min readDec 13, 2018

Edit

Although this worked, it’s not a good implementation. After discussions on Reddit, it was suggested to use ShaderMask instead. I have now implemented this and published a plugin.

Feel free to look through the code for implementation

I work for a company, who have a small (read large) obsession for gradients. Including through text.

So, to Flutter:

A quick Google landed me in StackOverflow, and a straight answer.

final Shader linearGradient = LinearGradient(
colors: <Color>[Colors.pink, Colors.green],
).createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0));
Text('Hello Gradients!',
style: new TextStyle(
fontSize: 60.0,
fontWeight: FontWeight.bold,
foreground: Paint()..shader = linearGradient),
)

The only problem is that it uses magic numbers for the Rect , which will lead to the full gradient not being used (something testers have been known to pick up!)

Here’s my attempt to fix that. It’s not pretty, and maybe somebody can suggest a cleaner way.

First, a method to return the shader:

Shader getTextGradient(RenderBox renderBox) {
if (renderBox == null) return null;
return LinearGradient(
colors: <Color>[Colors.deepOrange, Colors.lightGreenAccent],
).createShader(Rect.fromLTWH(
renderBox.localToGlobal(Offset.zero).dx,
renderBox.localToGlobal(Offset.zero).dy,
renderBox.size.width,
renderBox.size.height));
}

For this method to work, we need x, y, height, and width. The best way I found was to get the RenderBox of the Text widget.

As we don’t have these at build time, we need to do some state work:

// a key to set on our Text widget, so we can measure later
GlobalKey myTextKey = GlobalKey();
// a RenderBox object to use in state
RenderBox myTextRenderBox;
@override
void initState() {
super.initState();
// this will be called after first draw, and then call _recordSize() method
WidgetsBinding.instance.addPostFrameCallback((_) => _recordSize());
}
void _recordSize() {
// now we set the RenderBox and trigger a redraw
setState(() {
myTextRenderBox = myTextKey.currentContext.findRenderObject();
});
}

The last thing we need to do is setup the Text widget to respond to this state:

Text('Hello Gradients!',
key: myTextKey,
style: new TextStyle(
fontSize: 60.0,
fontWeight: FontWeight.bold,
foreground: Paint()..shader = getTextGradient(myTextRenderBox)),
)

Now, we have text with a gradient drawn through it.

One gotcha, if your Text is in an Expanded widget, the RenderBox is going to be the size of the Expanded widget. So be careful there.

Like I said earlier. This doesn’t seem all that optimal. Hopefully there is a better way, and someone can advise. If not, well this works.

--

--