Mobile Development 7 min read

Design and Implementation of a Powerful Rich Text Component in Flutter

To meet Xianyu’s e‑commerce product‑detail needs, the team built a custom RichText solution in Flutter that uses a zero‑width‑space placeholder (SpaceSpan) with adjustable letter‑spacing and font‑size to reserve space, then positions any widget via a Stack, enabling inline images, emojis and custom components without altering Flutter’s core rendering, though further work is needed for automatic sizing, text selection and editor integration.

Xianyu Technology
Xianyu Technology
Xianyu Technology
Design and Implementation of a Powerful Rich Text Component in Flutter

Background: The Xianyu team, one of the earliest and largest Flutter users in China, needs a more capable text layout component for e‑commerce product detail pages, where rich mixed‑style text is essential.

Problem: Flutter's built‑in Text widget only supports simple styling, and even RichText with TextSpan cannot meet complex design requirements such as embedding images, emojis, or custom widgets inline.

RichText Principle – Creation: When a RichText node is built, Flutter creates a LeafRenderObjectElement , then calls RichText.createRenderObject to generate a RenderParagraph . The RenderParagraph creates a TextPainter , which holds the TextSpan tree and handles layout and painting.

RichText Principle – Rendering: During layout, TextPainter.layout walks the TextSpan tree, adding text via addText and finally computes paragraph height. During paint, the canvas is clipped, TextPainter.paint draws the paragraph, and the final rectangle is rendered.

Design Idea: Use a zero‑width space character ( \u200B ) as a placeholder. By setting letterSpacing to the desired width and fontSize to the desired height, the placeholder occupies arbitrary space. The placeholder’s offset is obtained with TextPainter.getOffsetForCaret , allowing a widget to be positioned precisely via a Stack .

Implementation – SpaceSpan (core placeholder): class SpaceSpan extends TextSpan { SpaceSpan({this.contentWidth, this.contentHeight, this.widgetChild, GestureRecognizer recognizer}) : super( style: TextStyle( color: Colors.transparent, letterSpacing: contentWidth, height: 1.0, fontSize: contentHeight, ), text: '\u200B', recognizer: recognizer); }

Getting the placeholder position: for (TextSpan textSpan in widget.text.children) { if (textSpan is SpaceSpan) { final SpaceSpan targetSpan = textSpan; Offset offsetForCaret = painter.getOffsetForCaret( TextPosition(offset: textIndex), Rect.fromLTRB(0.0, targetSpan.contentHeight, targetSpan.contentWidth, 0.0)); // use offsetForCaret to place the widget } textIndex += textSpan.toPlainText().length; }

Integration: The RichText and the positioned widget are wrapped in a Stack so that the widget appears exactly where the placeholder was defined. Stack( children: [ RichText(...), Positioned(left: position.dx, top: position.dy, child: child), ], );

Result: The approach allows any widget—images, custom tags, buttons—to be embedded in a RichText without heavily modifying Flutter’s core rendering code. Screenshots show the mixed layout working smoothly.

Future Work: Current limitations include the need to manually specify placeholder width/height, lack of text selection support, and no rich‑text editor integration. Further improvements aim to support custom background, selection, and richer editing capabilities.

FlutterMobile DevelopmentRichTextSpaceSpanTextSpanUI rendering
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.