One of the first tasks I was given, as a professional Flutter developer, was to implement a widget that would allow users to create a signature by drawing on the surface of a mobile phone. So I did what most people do: I searched the internet to see how other programmers had dealt with this problem.
What I found was several finger-signing widgets, differing in most of the details, but all implemented pretty much exactly the same way. They all maintained the widget’s state as a list of Offset objects. (Flutter uses an Offset to represent what you would call a point in most other graphical environments.)
I tried the first one. Doing a test-signing, using the mouse on the surface of the iOS emulator, it quickly became unusably sluggish. You’d be surprised how quickly your finger-scribblings can add up to hundreds or thousands of saved points. And given that Flutter forces you to throw away all saved state and recreate it on every redraw, it was quickly adding up to thousands of line-draws per second.
Android has much worse graphical performance than iOS, so it was not surprising that the widget performed even worse on the Android simulator. On my mid-range Motorola Android phone that I used for testing, it was so bad that it routinely crashed after a mere second or two of screen-scribbling.
The other two or three finger-signing widgets I tested all suffered from the same issues: sluggish updates, close to unusable on iOS devices, crashing within seconds on Android devices.
Well! Unable to successfully complete my very first assigned task in my new adopted tech specialization! My Flutter career was off to a great start.
This was a couple of years ago, so I cannot remember how I figured out how to solve this problem. But once I did solve it, what I came up with was so obviously correct, and so much better than all the examples I had found, that it seemed odd to me that I was the first person to discover it. I won’t leave you in suspense: the right way to go about this is to use a Flutter Path object as your persistent state, rather than a list of Offset objects.
You can check out my finger-signing demo app here: finger-signer
The example app presents you with two buttons: one for a “bad” finger-signing widget, implemented as a list of Offset objects, and another for a “good” finger-signing widget, implemented with a Path object. (Actually, both buttons lead to the same widget, but it uses a different persistent state object in either case. Have a look at the code for more details.)
Flutter’s drawing and caching abilities are much better these days than they were when I first encountered this issue. I have to admit, the so-called “bad” finger-signing widget is probably usable in a production app these days. But it is not nearly as efficient as the “good” version. Trying out both of them, I don’t think there is any doubt as to which one is better. And the “good” version lends itself to being extended more easily. There are all kinds of extra drawing functions you could add that would be handled for you by the underlying Path object.
I really should have written this example app a couple of years ago, when it would have been more useful to other Flutter developers. Better late than never, I guess!