Flutter: how to draw text along an arc
For one of our side projects (highly experimental and written in Flutter for Web, by the way) I needed to implement something like this:
The code is actually the same for mobile and web, so, for the sake of simplicity (and because they look nicer), I’ll share screenshots from the mobile app.
The problem is that Flutter doesn’t support drawing text along a custom path (and it doesn’t look like it will, at least not in the near future). So I decided to implement the functionality on my own. Drawing text along any custom path would be quite complex, but luckily I only had to implement text along an arc. I’d like to share with you one way to do that.
For this implementation, your first geometry course should be more than enough (though, shame on me, I’ve realised that I’ve forgot almost all of mine). It won’t be the best possible implementation, but for our use case it will be good enough.
Let’s start with defining the interface of our widget (let it be
startAngle is the initial angle where the text will start, and
radius is the arc's radius - the text will be drawn along its side. We can use it like this:
For the text rendering we’ll be using
CustomPainter. This is where the magic happens:
Before starting to implement the
_Painter class let's think about how it will work:
The idea here is that we will draw each letter on top of a chord. The radius of the circle the cord belongs to is our defined radius.
d - the chord's length - equals the letter's width. That means that after drawing each letter we can rotate the canvas to a specified angle and move it to a distance
d along the x axis. It's easier to transform the canvas than to calculate new coordinates.
Let’s start implementing the
For rendering we will need: the radius, the text itself, the text style (so that we can get a width of each letter) and the initial angle. The
shouldRepaint method defines whether the
paint method needs to be called and, in the simplest case (when we don't have any complex calculations there), it can always return
Now we can continue implementing
In this code snippet we’re moving the canvas so that circle radius is in the center of a container, drawing the circle (it’s just a helper, we’ll remove it later), and moving the canvas again to a
-radius along the y axis - later it will be easy to move and rotate it. We should get the following picture:
Let’s add text rendering:
As I said, the idea is rather simple: with each letter drawn we’re rotating the canvas to a calculated angle, so that the chord of the current letter is parallel to the x axis; then we draw the letter and move the canvas along the x axis to a distance equal to the letter width.
We can use our knowledge of geometry to find a chord’s angle and the new angle to rotate the canvas.
A chord’s length can be found from the following equation:
d = 2r * sin(⍺ / 2)
So it can be transformed like this to find the chord’s central angle:
⍺ = 2 * arcsin(d / 2r)
The angle to rotate the canvas can be found with this simple formula:
𝛥 = (⍺ + β) / 2
⍺ is the central angle of the previous chord,
β is the central angle of the current chord.
Now it looks like this:
Now we can remove the helper circle and take the initial angle into account:
That’s all! Here’s the final source code:
By the way, it’s also available as a Flutter package here.
Originally published at https://developers.mews.com on December 12, 2019.