flutter_text_sniffer 5.0.2 copy "flutter_text_sniffer: ^5.0.2" to clipboard
flutter_text_sniffer: ^5.0.2 copied to clipboard

A widget that detects specific patterns within a text and makes them interactive.

TextSniffer #

pub package CI/CD codecov License: MIT

text_sniffer

TextSniffer is a powerful Flutter widget designed to detect and interact with specific text patterns. It allows developers to define custom patterns using regular expressions, apply unique styles to detected text, and handle user interactions such as taps on links or specific words.

Table of Contents #

Features #

  • Customizable Patterns: Use regular expressions to define text patterns.
  • Interactive Text: Make text segments interactive, responding to user taps.
  • Styling Options: Apply styles to both matching and non-matching text.
  • Custom Match Builders: Define how detected patterns appear.
  • Multiple Search Sniffers: Supports for emails and links by default but you can create own Sniffers.
  • Individual Styling: Style different types of matches individually.

Installation #

To use TextSniffer, add the following to your pubspec.yaml:

dependencies:
  flutter_text_sniffer: ^latest_version

Then, run flutter pub get to install the package.

Usage #

Basic Example #

Here’s a simple example of how to use the TextSniffer widget:

TextSniffer(
   text: "Contact us at support@example.com or visit https://example.com/product?name=iPhone",
   snifferTypes: [
    // They are built in sniffers
     EmailSnifferType(), 
     LinkSnifferType(),
   ],
   onTapMatch: (match, matchText, type, index, error) {
     if (error == null) {
       print('Tapped on: $matchText');
     }
   },
)
image

Custom Builder #

To customize how matched text is displayed, use the matchBuilder property:

final images = <String>[
  "assets/flutter.png",
  "assets/google.png",
];

class CustomSnifferType extends SnifferType {
  @override
  RegExp get pattern => RegExp(r'\[(.*?)\]');

  @override
  TextStyle? get style => const TextStyle(color: Colors.indigoAccent, fontWeight: FontWeight.bold);

  @override
  String toString() => 'custom';
}

TextSniffer<String>(
  text: "Check out [Flutter] and [Google]!",
  matchEntries: const ['https://flutter.dev', 'https://google.com'],
  snifferTypes: [
    CustomSnifferType(),
  ],
  onTapMatch: (entry, match, type, index, error) {
    if (error == null) {
      showSnackBar(context, entry ?? "Not found");
    }
  },
  matchBuilder: (match, index, type, entry) {
    return Container(
      padding: const EdgeInsets.only(left: 4.0, right: 4.0),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Image.asset(
            images[index],
            width: 20,
            height: 20,
          ),
          Text(
            match,
            style: type.style,
          ),
        ],
      ),
    );
  },
)
image

Defining Custom Patterns #

You can define custom patterns using regular expressions. For example, to detect IP addresses, hashtag and custom:

// Custom sniffer. For example: [Example] => word in brackets => Example
class CustomSnifferType extends SnifferType {
  @override
  RegExp get pattern => RegExp(r'\[(.*?)\]');

  @override
  TextStyle? get style => const TextStyle(color: Colors.indigoAccent, fontWeight: FontWeight.bold);

  @override
  String toString() => 'custom';
}

// IP address sniffer
class IpAddressSnifferType extends SnifferType {
  @override
  RegExp get pattern => RegExp(r'\b' // Start of word (word borders)
      r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' // 1 octet
      r'\.' // Dot
      r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' // 2 octet
      r'\.' // Dot
      r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' // 3 octet
      r'\.' // Dot
      r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' // 4 octet
      r'\b' // End of word
      );

  @override
  TextStyle? get style => const TextStyle(color: Colors.orange, fontStyle: FontStyle.italic);

  @override
  String toString() => 'ip_address';
}

// Hashtag sniffer
class HashtagSnifferType extends SnifferType {
  @override
  RegExp get pattern => RegExp(r'\B#\w\w+');

  @override
  TextStyle? get style => const TextStyle(color: Colors.purple, fontWeight: FontWeight.bold);

  @override
  String toString() => 'hashtag';
}

TextSniffer(
  text: "Check out [Flutter] and [Google]!\nCheck out #Flutter and #Google! IP addresses: 192.168.0.1, 192.168.0.124",
  snifferTypes: [
    CustomSnifferType(),
    HashtagSnifferType(),
    IpAddressSnifferType(),
  ],
  matchEntries: const [
    'https://flutter.dev',
    'https://google.com',
  ],
  onTapMatch: (entry, matchText, type, index, error) {
    if (error == null) {
      print('Tapped on: $matchText');
    }
  },
)
image

Handling Taps & matchEntries #

onTapMatch is called whenever a matched segment is tapped:

onTapMatch: (entry, matchText, type, index, error) { ... }
  • matchText, type and index are always provided — index is the zero-based position across all matches in the text (not per type), so use type to tell match kinds apart.
  • entry is the item from matchEntries for this match, or null if you did not supply one. matchEntries is fully optional and per-match: provide it only when you need extra data (e.g. a URL behind a [label]). Mixing types where only some need entries is fine — taps on the others simply get entry: null.
  • error is reserved for future error reporting and is currently always null.
// Optional: attach data to specific matches.
TextSniffer<String>(
  text: "Visit [Flutter] or [Google]",
  snifferTypes: [CustomSnifferType()],
  matchEntries: const ['https://flutter.dev', 'https://google.com'],
  onTapMatch: (url, matchText, type, index, error) {
    // url == 'https://google.com' when "[Google]" is tapped
  },
)

Large Texts (books, articles) #

TextSniffer renders a single RichText, and RichText lays out its entire span tree eagerly. So do not put a whole book into one TextSniffer — split the text into chunks (e.g. paragraphs) and render them lazily with ListView.builder. Each chunk gets its own TextSniffer, so layout and pattern matching only run for what is on screen:

ListView.builder(
  itemCount: paragraphs.length,
  itemBuilder: (context, index) => TextSniffer(
    text: paragraphs[index],
    snifferTypes: [EmailSnifferType(), LinkSnifferType()],
    onTapMatch: (entry, matchText, type, i, error) { /* ... */ },
  ),
)

Within each TextSniffer, parsing (running the regex) happens only when text or snifferTypes change — not on every rebuild — so scrolling stays smooth. See example/lib/long_text_example.dart for a runnable demo.

Contributing #

We welcome contributions! To contribute to TextSniffer, please follow these steps:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feature/AmazingFeature).
  3. Commit your changes (git commit -m 'Add some AmazingFeature').
  4. Push to the branch (git push origin feature/AmazingFeature).
  5. Open a Pull Request.

License #

This project is licensed under the MIT License. See the LICENSE file for more details.

8
likes
160
points
223
downloads
screenshot

Documentation

API reference

Publisher

verified publisherinterlib.dev

Weekly Downloads

A widget that detects specific patterns within a text and makes them interactive.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_text_sniffer