I need to display long text, which will occupy several screens/pages. I have to add some features also, so I would like to implement my own text displaying component.
I found two classes that corresponds to this task:
TextPainter
use TextSpan for text
use paint(canvas, offset) for painting
Paragraph
use "queue" for text and styles for them
use Canvas.drawParagraph(paragraph, offset) for painting
What is the difference between them and which one to use?!
If the text contains 100 lines and only 10 lines can be placed on a page, then how to draw truncated text on the next pages until nothing left?
Use \n for paragraphs, like this: Text('Hello,\n\nhow are you?')
RichText is a very useful widget in Flutter, which is used for displaying a paragraph of text on the UI with multiple styles. Inside the widget, we can have different styles by giving it a tree of TextSpan widgets. Each TextSpan can set its own style for overriding the default style.
To paint in flutter you use the CustomPaint Widget. The CustomPaint Widget takes a CustomPainter object as a parameter. In that class, you have to override the paint method, which gives you a canvas that you can paint on.
tl;dr: imo TextPainter
> Paragraph
(because of better API).
I created simple example app to compare both TextPainter
and Paragraph
methods of rendering text on Canvas
(of CustomPainter
). Both methods are pretty good, both uses different approaches, both have their weird wobbles.
TextPainter
At first I want to mention that TextPainter
interface seems to be easier - at least for me. You just need to specify text
as TextSpan
entry or tree and - what is weird, it isn't default - textDirection
. You can also provide options such as maxLines
, style
and textAlign
(and few others). Then you need to use layout
to specify how the rendering would be laying (well, maxWidth
only). And finally, paint
on certain Canvas
at specified Offset
.
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textAlign: TextAlign.justify,
textDirection: TextDirection.ltr
)
..layout(maxWidth: size.width - 12.0 - 12.0);
textPainter.paint(canvas, const Offset(12.0, 36.0));
Used TextSpan
is quite universal around the Flutter - RichText
and other widgets also are using this class. I also must notice that using TextPainter
allows you to check height
and width
of text in pixels (before rendering).
Paragraph
Second: Paragraph
. This seems to be more underlying, procedural method. As you can see below, Paragraph
method is less cleaner. First you must use ParagraphBuilder
(since Paragraph
have no constructor). You need to feed it with ParagraphStyle
that contains various text styling such as font information, textAlign
, maxLines
and so on. Then you can use pushStyle
, pop
and addText
to prepare next and next portion of the paragraph. After build
you get the Paragraph
which you can drawParagraph
on your Canvas
.
final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(
ui.ParagraphStyle(
fontSize: style.fontSize,
fontFamily: style.fontFamily,
fontStyle: style.fontStyle,
fontWeight: style.fontWeight,
textAlign: TextAlign.justify,
)
)
..pushStyle(style.getTextStyle())
..addText(text);
final ui.Paragraph paragraph = paragraphBuilder.build()
..layout(ui.ParagraphConstraints(width: size.width - 12.0 - 12.0));
canvas.drawParagraph(paragraph, const Offset(12.0, 36.0));
Be aware, there is two types of TextStyle
(Dart UI and Flutter). In line with pushStyle
you can see that Flutter Painting library TextStyle
got transformed into Dart UI TextStyle
. Another weird thing is that you can/need specify few font settings just in the ParagraphBuilder
- even though you are going to use pushStyle
in the line after. And the layout
must be specified with width
.
I think I could be better to use in situations like reading file, especially with formatting, since there will be no need to parse the file into TextSpan
tree, which could be costly. I suppose it can be also a bit faster than other methods if you are know what you are doing, but I do not have time to dig it that deeply.
You might want to clip the text when there is too much of it. Both Paragraph
and TextPainter
exposes maxLines
- to set max lines - and didExceedMaxLines
- to detect whether the limit was exceeded -, in one way or another. There is also canvas.clipRect
and related methods which allows to clip all drawing into selected space.
There is also simple performance test (on release), which shows that both methods are comparable (in my testing case TextPainter
was no more than 2% faster than Paragraph
). It might be also measurement error ¯\_(ツ)_/¯.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With