My first disappointment with Flutter

Image for post
Image for post
sad

I like Flutter. I like developing once and having the code run on both Android and iOS. I like how much time that saves. I like being a web developer now without having done any extra work. I like hot reload. I like building the UI quickly by composing widgets into layouts. I like how much simpler it is to make a ListView. I like state management. (Ok, just kidding about that one. But I can deal with it.) I like Dart. I like how how much easier Futures and async/await are than Android AsyncTasks and even iOS Dispatch Queues.

But after spending the last two weeks researching how Flutter renders text, I’m disappointed with the tools that we’ve been given.

We’ve been told:

Flutter’s layered architecture gives you control over every pixel on the screen.

That apparently doesn’t apply to the pixels used to paint text.

Low level text capabilities in Flutter

Flutter renders text using a combination of Skia, Harfbuzz, Minikin, and ICU using a library called LibTxt. Developers use it indirectly when they use a Text widget or TextSpan or even a TextPainter. At the lowest level we have available to us, that is, dart:iu, there is the Paragraph class, which is built using a ParagraphBuilder. These classes are basically just wrappers for the underlying LibTxt engine. Almost all the work is done by this engine with very little exposed in dart:ui.

The Paragraph class gives us the following control:

  • Size: I can get the width and the height of the whole rendered paragraph, which might be a single line or multi-line.
  • Distance to the baseline (for the first line only)
  • Whether the text overflowed the maxLines variable.
  • The size and relative position of boxes of text runs (that is, lines or sections of bidi text). Here is an example:
Image for post
Image for post
Image from here
  • The text character index closest to some pixel location. In the example above, pixel (1, 1) would correspond to index 0 in the string, that is, the letter “M” of “My text line.”
  • The word boundary for some character offset in the string.

A subsequent update also gave us LineMetrics, which provided many details for each line:

class LineMetrics {
final bool hardBreak;
final double ascent;
final double descent;
final double unscaledAscent;
final double height;
final double width;
final double left;
final double baseline;
final int lineNumber;
}
Image for post
Image for post

However, what we are not given:

  • A way to get the actual text within the text boxes.
  • A way to control how the text is laid out.
  • A way to draw text on a path.
  • A way to measure and draw short runs of text without building a whole paragraph.
  • A way to get line break locations from a string of text

Comparison to Android and iOS

In Android, although most people would use a TextView, you are given the low level control to do all the things I listed above by using the StaticLayout, Canvas, and Paint classes. Here are a just few of the many options available:

I don’t have as much low level text drawing experience on iOS (because I thought I would just learn to do everything in Flutter), but Core Text has a rich set of tools.

The Flutter repo style guide stays:

It is common for SDKs that target multiple platforms … to provide APIs that work on all their target platforms. Unfortunately, this usually means that features that are unique to one platform or another are unavailable.

For Flutter, we want to avoid this by explicitly aiming to be the best way to develop for each platform individually. Our ability to be used cross- platform is secondary to our ability to be used on each platform. (emphasis added)

Currently, for those of us who need to do low level text rendering in our apps, Flutter is not the best platform to develop on.

Use cases

You may say that Flutter already provides the Text and RichText widgets. That’s true and they are very good. They will meet the needs of 99.9% of developers. But there are use cases for accessing lower level text rendering tools.

Mongolian

My use case is laying out and rendering traditional Mongolian text, which is written vertically and line wraps from left to right. English is written sideways, but CJK and emoji characters should retain their normal orientation.

Image for post
Image for post
Mongolian text layout. Image adapted from here

There are a few “solutions” using widget composition, but when you add the need for text styling (like “underlining” by drawing a vertical line to the right of the text), a much more robust solution would be to do all the text measuring, layout, and painting by hand. I’ve started working on that here.

CJK languages

Chinese, Japanese, and Korean can also be laid out in various vertical orientations. Like Mongolian, there are workarounds for one off situations, but for common usage, a rendering package would be more helpful. Read this for a more detailed description of the requirements.

Image for post
Image for post
Vertical CJK text layout. Image adapted from here

Flutter only supports supports right-to-left and left-to-right layouts. Vertical layouts aren’t (and won’t be 😞) supported. I don’t fault the Flutter team for that. It’s a lot of work. But I wish they would have given us more tools to do it ourselves.

Text art

Apps that do text art would also benefit from a low level access to the text painting tools.

Image for post
Image for post
Image from here

Filling a non-rectangular shape with text

In order to fit text inside something that is not a rectangle, you have to do a lot of measuring. Where to make the line breaks is another difficult problem.

Image for post
Image for post
image source

Text flow around exclusion paths

This is available in iOS but not in Flutter. And there is no easy way to implement it ourselves.

Image for post
Image for post
image source

Conclusion

I’m not trying to talk anyone out of using Flutter. I still like it a lot. I don’t ever want to go back to building the same app multiple times for different platforms.

In writing this article I’m kind of hoping someone will say, “No, you’re wrong. If you do such and such, then you’ll have access to the low level text rendering tools.” I‘m not holding out too much hope for that, though, since one of the main Flutter developers commented this:

If you want “real” vertical text, with emphasis marks and ruby and inline horizontal bidi text and everything, then the best I can offer is that you could try to write a package to support this using what poor primitives we have provided, but it probably won’t be a satisfactory experience.

What I’m really hoping is that the Flutter team will give us the same freedom with text that we already have at the UI layout level. It wouldn’t be especially hard to add a dart:ui class exposing more of the LibTxt library. True, it would be more to maintain, and developers might occasionally use it to write inefficient code, but the freedom is worth the cost in my opinion. Make Flutter the best way to develop for any platform in any language.

Update

February 2020

When I originally posted this article the Flutter team quickly responded (see the comments below). A lot of work was being done (as recorded in this GitHub issue, thank you Gary Qian), and it looked hopeful that there might be some significant improvements. However, despite being highly upvoted, that GitHub issue ended up being closed because it was going to be too much work.

I recently learned that work is being done to replace LibTxt with the Skia SkParagraph module. That sounds exciting, but I don’t know the details yet. You can track the progress here. Since this is a big change, it’s a good time to let the Flutter team know what your needs are related to custom text rendering. See the section below.

What you can do

Even though the following issue is currently closed (though unsolved), continue to upvote it and leave comments on it if you also need to do custom text rendering.

In order to break that issue into smaller pieces, I’ve added the following specific feature requests. If you also need them, upvote and/or comment.

I haven’t filed any specific bug reports yet related to efficiency with the measurement and painting of many small Paragraph objects. The reason is that I haven’t noticed any performance issues myself. If you have come across such problems, create a detailed GitHub issue and @suragch me. I’ll link to it here.

Written by

A Flutter and Dart developer. Follow me on Twitter @suragch1 to get updates of new articles.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store