Text versus TextPainter

This is the story of an issue I discovered while using Flutter, a cross-platform framework for building apps that can run on iOS, Android, macOS, Windows, and the web. (As an aside: it’s pretty good! I have been using it to implement cross-platform iOS and Android apps for a few years now. I don’t have any major complaints.)

The most prominent way to add strings of text to a Flutter app is by using the Text widget. This is the way you want to go, about 95 percent of the time. But there are occasionally situations in which a Text widget will not get the job done. For example, say you are building a graphical image by drawing pixels on top of a Canvas object, and you also want to add some text in there. In that case, TextPainter is the right tool for the job. It operates in much the same way as Text does, but it is intended for drawing on a Canvas, rather than being added to a widget tree, which is what Text objects are for.

Flutter has adopted the React widget model, as opposed to the “old-fashioned” declarative paradigm, used by many other GUI frameworks. I put “old-fashioned” in scare quotes there, because this is a decision that I very much disagree with. One of my few complaints about Flutter is the React widget model. This causes me no end of grief. But there is no major piece of tech infrastructure of this scale that is going to incorporate nothing but decisions I agree with. So I have made my peace with the React widget model. (Grudgingly. A lot of the Flutter code I write is to put a nice, declarative layer on top, implemented with Flutter's React-style widgets under the hood. But I digress.)

By accident or by design, Flutter makes it difficult to lay out widgets by using pixel-counting. By that I mean performing calculations like: I know that this widget I just created will be 100 pixels wide, and I know the width of the device I am rendering to is 480 pixels, so how many pixels should i add to each side of this thing so that it will be centered? You are instead encouraged to use container widgets to perform these kinds of operations. For example, the Center widget is a much better way to go about that, rather than trying to write your own brute-force centering code.

This is just the tip of the iceberg, of course. Flutter has dozens of widgets that enable you to perform very sophisticated widget layouts. You can divvy up the width and height of the screen in all sorts of creative ways. You can take the child widgets’ size preferences into account, or by specifying weights (this widget should get 45 percent of the screen’s width and all others should share what’s left over), and so on. I agree that this is a lot better than pixel-counting. And the people who wrote those container widgets are handling all sorts of ugly corner cases that you would not think to account for, if you were to try to replace them with your own brute-force layout code. And of course I use these container widgets myself to perform widget layouts, almost all the time. It's the easiest way to go about it.

But every now and then, I run into a situation where I feel like the only way I can do a good job of child widget layout is by resorting to pixel-counting. And if you should ever find yourself in that situation: good luck! Not only is Flutter not going to make this easy on you, it is going to actively attempt to thwart you. It's just not "the Flutter way."

Yeah, okay, fine. But I am pretty stubborn. It is a very rare technical problem that I cannot solve, if i really put my mind to it. Flutter pixel-counting is one such problem that I solved for myself.

(to be continued ...)