Mobile Development 13 min read

Implementing Custom Text Overflow with Highlight Support in ExtendedText for Flutter and HarmonyOS

This article explains how the ExtendedText component adds custom text‑overflow effects, including start, middle, end and auto modes, supports highlighted keywords with keep‑visible spans, and improves performance by replacing binary search with range estimation across Flutter, Android, iOS, Web and HarmonyOS platforms.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing Custom Text Overflow with Highlight Support in ExtendedText for Flutter and HarmonyOS

The ExtendedText widget, originally released for Flutter five years ago, now also supports native HarmonyOS Next platforms and provides customizable text‑overflow effects beyond the default "..." ellipsis.

Custom Overflow Modes

Four overflow positions are defined: start , middle , end and auto . The auto mode automatically selects the appropriate position based on the location of a highlighted keyword.

Implementation Steps

Clip the original text.

Calculate the range where the text does not overflow.

Render the overflow widget and mask the underlying text.

Detecting Visual Overflow (Flutter)

bool _didVisualOverflow({TextPainter? textPainter}) {
  final Size textSize = (textPainter ?? _textPainter).size;
  final bool textDidExceedMaxLines = (textPainter ?? _textPainter).didExceedMaxLines;
  final bool didOverflowHeight = size.height < textSize.height || textDidExceedMaxLines;
  final bool didOverflowWidth = size.width < textSize.width;
  if (size.height < textSize.height) {
    size = constraints.constrain(textSize);
  }
  return didOverflowWidth || didOverflowHeight;
}

Detecting Visual Overflow (HarmonyOS)

_didVisualOverflow(paragraph: text.Paragraph, constraint: ConstraintSizeOptions): boolean {
  let textSize: SizeResult = {
    width: px2vp(paragraph.getMaxWidth()),
    height: px2vp(paragraph.getHeight()),
  };
  let size: SizeResult = {
    width: constraint.maxWidth! as number,
    height: constraint.maxHeight! as number,
  };
  let textDidExceedMaxLines = paragraph.didExceedMaxLines();
  let didOverflowHeight = size.height < textSize.height || textDidExceedMaxLines;
  let didOverflowWidth = size.width < textSize.width;
  let hasVisualOverflow = didOverflowWidth || didOverflowHeight;
  return hasVisualOverflow;
}

Keeping Highlighted Span Visible

Mark the span that should stay visible with keepVisible: true . The framework then locates this span and ensures its range is preserved during clipping.

// Flutter
SpecialInlineSpanBase? keepVisibleSpan;
text.visitChildren((InlineSpan span) {
  if (span is SpecialInlineSpanBase && (span as SpecialInlineSpanBase).keepVisible == true) {
    keepVisibleSpan = span as SpecialInlineSpanBase;
    return false;
  }
  return true;
});
// HarmonyOS
let keepVisibleSpan: InlineSpan | null = null;
this.text.visitChildren((span) => {
  if (span.keepVisible === true) {
    keepVisibleSpan = span;
    return false;
  }
  return true;
});

Rendering and Masking Overflow

On Flutter, the overflow widget is drawn using canvas.clipRect before painting the text; on HarmonyOS a similar clipping operation is performed.

// Flutter clipping example
if (_overflowRects != null) {
  context.canvas.saveLayer(offset & size, Paint());
  if (overflowWidget?.clearType == TextOverflowClearType.clipRect) {
    if (_overflowClipTextRects != null) {
      for (final Rect rect in _overflowClipTextRects!) {
        context.canvas.clipRect(rect.shift(offset), clipOp: ui.ClipOp.difference);
      }
    }
    for (final Rect rect in _overflowRects!) {
      context.canvas.clipRect(rect.shift(offset), clipOp: ui.ClipOp.difference);
    }
  }
}
_textPainter.paint(context.canvas, offset);
paintInlineChildren(context, offset);
if (_overflowRects != null) {
  context.canvas.restore();
}

Performance Improvements

Instead of a binary‑search approach to find the overflow index, the new algorithm estimates a rough range using a single‑line TextPainter/Paragraph and then refines it, achieving more than 40% speedup for long texts.

Usage

Installation

Flutter: flutter pub add extended_text

HarmonyOS: ohpm install @candies/extended_text

Define Highlight Span

import 'package:extended_text/extended_text.dart';
import 'package:flutter/material.dart';

class HighlightText extends RegExpSpecialText {
  @override
  RegExp get regExp => RegExp("
(.*?)
");

  static String getHighlightString(String content) =>
      '
' + content + '
';

  @override
  InlineSpan finishText(int start, Match match,
      {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap}) {
    final String hexColor = match[1]!;
    return SpecialTextSpan(
      text: match[2]!,
      actualText: match[0],
      start: start,
      style: textStyle?.copyWith(color: Color(int.parse(hexColor.substring(1), radix: 16))),
      keepVisible: true,
    );
  }
}

Set Overflow Position

ExtendedText(
  searchMessages[index],
  specialTextSpanBuilder: HighlightTextSpanBuilder(),
  maxLines: searchText.isEmpty ? 3 : 1,
  overflowWidget: TextOverflowWidget(
    child: const Text('\u2026 '),
    position: TextOverflowPosition.auto,
  ),
);

With these steps, the ExtendedText component now supports rich, customizable overflow effects on all major platforms, including Web, Android, iOS, Windows, macOS, Linux, HarmonyOS, HyperOS, ColorOS, OriginOS, MagicOS, Chrome OS and FuchsiaOS.

FlutterPerformanceHarmonyOSMobile UICustom Text OverflowExtendedTextHighlight
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

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.