Design and Implementation of RichText Mixed Content in Flutter for Xianyu Messaging
The article details Xianyu’s migration of its messaging rich‑text system to Flutter, explaining how RichText became a MultiChildRenderObjectWidget, how custom emoji placeholders are converted to HTML tags and parsed into TextSpan and WidgetSpan elements, enabling colored text, clickable links, and emoji rendering across Flutter versions.
Background : In Xianyu's messaging system, rich text occupies a large proportion on the UI side. The project is migrating to Flutter, and solving the rich‑text problem on the Flutter side is an early risk point.
Industry status : Existing solutions for the old RichText (pre‑Flutter 1.7.3) are documented, but they do not cover the whole pipeline, and Flutter's RichText has evolved with newer SDK versions, requiring a complete evolution plan.
RichText mixing principle : Starting from Flutter 1.7.3, RichText no longer inherits from LeafRenderObjectWidget but from MultiChildRenderObjectWidget , turning it into a layout widget that can contain multiple child widgets such as TextSpan and WidgetSpan .
Creation process : 1. RichText extracts all WidgetSpan objects from its text parameter and passes them to the parent MultiChildRenderObjectWidget . 2. It creates a MultiChildRenderObjectElement and generates a RenderParagraph via createRenderObject . 3. RenderParagraph creates a TextPainter , which performs layout, painting and event distribution, and recursively extracts PlaceholderSpan (essentially WidgetSpan ).
Rendering process : The performLayout of RenderParagraph first layouts child widgets (the placeholders), then calls _layoutTextWithConstraints to layout the TextPainter . Text widgets add raw strings to the builder, while WidgetSpan adds placeholder dimensions. After layout, the paragraph paints the text with placeholders and then positions the child widgets according to the stored placeholder offsets.
Design approach : Use the existing HTML protocol as the entry point. Custom Xianyu emoji placeholders (e.g., [微笑] ) are first converted to HTML <img> tags, then the whole string is parsed with the html Dart package to build an HTML node tree. The tree is recursively traversed and mapped:
Text tags → TextSpan
Image tags → FDImageSpan (later replaced by WidgetSpan with an Image widget)
Link tags → TextSpan with a GestureRecognizer
Sample emoji placeholder string:
你好[微笑],你的宝贝不错哦[呲牙],包邮吗?[坏笑][坏笑]Sample HTML string:
<font color="#888888">交易全程在闲鱼,</font><strong><font color="#F54444">你敢买,我敢赔!</font></strong>…The workflow first transforms custom emoji placeholders into HTML elements, then uniformly processes the HTML string to generate a rich‑text representation. The architecture separates data‑parsing and rendering layers, allowing the solution to work on low‑version Flutter (by re‑implementing RichText as a MultiChildRenderObjectWidget ) and to be upgraded to newer SDKs without affecting business logic.
Effect : Demonstrated rich‑text messages with colored text, clickable links, and custom emoji, showing that the design can handle plain text, HTML strings, and emoji‑enhanced messages uniformly.
Future plans : Extend the HTML protocol to support arbitrary widgets, enrich the editor, and apply the same rich‑text capabilities to product publishing and detail pages, thereby increasing user engagement and business value.
Xianyu Technology
Official account of the Xianyu technology team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.