flutter_interactive_text 0.1.1 copy "flutter_interactive_text: ^0.1.1" to clipboard
flutter_interactive_text: ^0.1.1 copied to clipboard

A performant Flutter widget for detecting, styling, interacting with, and optionally expanding rich text patterns.

flutter_interactive_text #

A performant, theme-aware Flutter widget for detecting, styling, and handling interactive text. The same InteractiveText widget can render ordinary, limited, selectable, or expandable content without requiring an editing controller or application-specific model.

Features #

  • One widget for static and expandable interactive text.
  • Built-in URLs, deep links, email addresses, phone numbers, mentions, hashtags, cashtags, emoji, commands, references, dates, times, currency, percentages, UUIDs, IPv4 addresses, variables, and lightweight Markdown.
  • Stable 22 font size for complete emoji graphemes in both mixed and emoji-only text.
  • Theme-aware defaults and a TextStyle override for every built-in type.
  • Typed tap callbacks and automatic URL, email, and phone launching.
  • Optional domain allowlist for user-generated links.
  • Custom regex, trigger, emoji, or function-based detectors.
  • Grapheme-safe expand and collapse behavior.
  • Optional selectable text.
  • Cached parsing, spans, recognizers, and line measurements for list usage.

Installation #

Add the package to pubspec.yaml:

dependencies:
  flutter_interactive_text: ^0.1.1

Basic usage #

The default configuration recognizes common social and messaging content:

import 'package:flutter_interactive_text/flutter_interactive_text.dart';

InteractiveText(
  'Write to hello@example.com or visit example.org. Hi @maria #Flutter πŸ‘πŸ½',
)

URLs, email addresses, and phone numbers launch automatically. Supply onTap when the application should own navigation or another action:

InteractiveText(
  'Explore #Flutter with @maria',
  onTap: (match) {
    if (match.type == InteractiveTextType.hashtag) {
      openSearch(match.text);
    } else if (match.type == InteractiveTextType.mention) {
      openProfile(match.text.substring(1));
    }
  },
)

Expandable text #

Expansion is opt-in and remains part of the same widget:

InteractiveText(
  longDescription,
  expansion: InteractiveTextExpansion(
    collapsedLines: 3,
    expandText: 'More',
    collapseText: 'Less',
    onChanged: (expanded) => analytics.track(expanded),
  ),
)

The collapsed value is measured with the effective theme, locale, text scaler, strut, alignment, and available width. Truncation only occurs at complete Unicode grapheme boundaries.

Choose detected types #

InteractiveTextGroups.common is enabled by default. More focused and advanced presets are available:

InteractiveText(
  message,
  types: InteractiveTextGroups.chat,
)

InteractiveText(
  assistantResponse,
  types: InteractiveTextGroups.assistantChat,
)

InteractiveText(
  technicalDetails,
  types: InteractiveTextGroups.combine([
    InteractiveTextGroups.links,
    InteractiveTextGroups.identifiers,
    InteractiveTextGroups.markdown,
  ]),
)

Available groups include links, contact, social, chat, common, commerce, temporal, identifiers, markdown, assistantChat, and all.

Styling #

Every built-in type has an independent TextStyle. Defaults use the current ColorScheme, so they react to light, dark, and custom application themes.

InteractiveText(
  content,
  style: Theme.of(context).textTheme.bodyLarge,
  styles: const InteractiveTextStyles(
    mention: TextStyle(fontWeight: FontWeight.w700),
    hashtag: TextStyle(color: Colors.teal),
    emoji: TextStyle(fontSize: 24),
    emojiOnly: TextStyle(fontSize: 24),
    expansion: TextStyle(fontWeight: FontWeight.w700),
  ),
)

Use styleResolver when the style depends on match metadata or source value:

InteractiveText(
  content,
  styleResolver: (details) {
    if (details.match.type == InteractiveTextType.mention &&
        details.match.text == '@admin') {
      return details.suggestedStyle.copyWith(color: Colors.red);
    }
    return details.suggestedStyle;
  },
)

The package does not define a domain allowlist. A null or empty allowedDomains collection permits every web host. To make only selected domains interactive, provide their host names from the host application:

InteractiveText(
  userGeneratedContent,
  allowedDomains: const ['example.com', 'example.org'],
)

Subdomains are included. Disallowed URLs remain visible and styled but do not receive a recognizer.

Custom detection #

InteractiveTextType is a value object, so applications are not limited to an enum. Define a type and detector, then provide an optional custom style:

const orderType = InteractiveTextType('order');

final orderDetector = RegexInteractiveTextDetector(
  type: orderType,
  id: 'order',
  pattern: RegExp(r'ORDER-\d+'),
  priority: 100,
);

InteractiveText(
  'Track ORDER-2048',
  detectors: [orderDetector],
  onTap: (match) => openOrder(match.text),
  styles: InteractiveTextStyles(
    custom: {
      orderType: const TextStyle(
        color: Colors.indigo,
        fontWeight: FontWeight.w700,
      ),
    },
  ),
)

For non-regex requirements, use FunctionInteractiveTextDetector. Trigger syntax can use TriggerInteractiveTextDetector, and its character and boundary policies are fully configurable.

Interaction rules #

  • Without onTap, supported links and contact values launch automatically.
  • With onTap, the callback owns the action for non-formatting matches.
  • interactiveTypes can explicitly include or exclude actionable types.
  • Emoji and formatting-only ranges are not tappable by default.
  • Set autoLaunch: false to render styles without automatic actions.

Performance #

Each mounted widget parses only when its text or parser changes. It reuses tap recognizers and caches built spans and collapsed measurements across ordinary rebuilds. Built-in regular expressions are also shared. For repeated custom rules, create one InteractiveTextParser and pass it to every list item.

See example/lib/main.dart for a runnable example.

1
likes
160
points
197
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A performant Flutter widget for detecting, styling, interacting with, and optionally expanding rich text patterns.

Homepage

Topics

#rich-text #links #mentions #hashtags #widget

License

MIT (license)

Dependencies

characters, flutter, url_launcher

More

Packages that depend on flutter_interactive_text