rich_text_controllerx 1.1.0 copy "rich_text_controllerx: ^1.1.0" to clipboard
rich_text_controllerx: ^1.1.0 copied to clipboard

An extended text editing controller that supports different inline styles for custom regex patterns

rich_text_controller #

Text editing controller highlighting words, prefixes and suffixes based on RegExps and Strings.

  • User callback for all matched highlights
  • User callback for tapped highlights
  • User callback for selected highlights
  • User callback for constructing complex InlineSpans
  • Deletes highlights on backspace
  • Exact word, prefix, postfix matching
  • Disable highlighting and callbacks dynamically

Installation #

flutter pub add rich_text_controllerx

Quick Start #

MatchTargetItem

MatchTargetItem defines words or paterns to match and the TextStyle for matched words or patterns.

  • The optional function MatchTargetItem.onSelected is invoked when a matched words is selected.
  • The optional function MatchTargetItem.onTapInto is invoked when a matched words is tapped into. The needed minimum difference in cursor position is two chars.
  • Combining MatchTargetItem.onSelected and MatchTargetItem.onTapInto might not be useful.

MatchTargetItem.matchString offers various String matching patterns:

enum StringMatch {
  any, // No word boundaries, also inside a String
  prefix, // The beginning of a String
  postfix, // The beginning of a String
  prefixComplete, // The beginning of a String and the complete word
  postfixComplete, // The end of a String and the complete word
  exact, // The exact String within its own word boundaries
}

MatchTargetItem.matchPayload allows to set a custom payload, accessible in MatchResultItem.target.matchPayload.

MatchTargetItem.inlineSpanBuilder allows to define a custom function returning the InlineSpan to show. Useful for displaying tooltips etc.

RichTextController

The optional function RichTextController.onMatch is invoked everytime the set of matched words change.

late final RichTextController controller = RichTextController(
    text:
        'Home sweet home! Do good homework and housework at home. The happyness of an ending.',
    sortTargetsByPatternLength: true,
    mergeOverlappingStyles: true,
    deleteOnBack: true,
    enableHighlight: true, // toggable at runtime
    enableOnTapInto: true, // toggable at runtime
    enableOnSelected: true, // toggable at runtime
    onMatch: (List<MatchResultItem> matches) {
      for (final MatchResultItem match in matches) {
        log(match.toString());
      }
    },
    targets: [
      // --------------------------------------------------------------
      // SINGLE STRING
      // --------------------------------------------------------------

      MatchTargetItem.string(
        'homework',
        style:
            const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
        stringMatch: StringMatch.exact,
        caseSensitive: false,
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      MatchTargetItem.string(
        'Home',
        style: const TextStyle(color: Colors.red),
        stringMatch: StringMatch.any,
        caseSensitive: false,
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      // Prefix

      MatchTargetItem.string(
        'happy',
        style: const TextStyle(color: Colors.blue),
        stringMatch: StringMatch.prefix,
        caseSensitive: false,
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      // Postfix

      MatchTargetItem.string(
        'ing',
        style: const TextStyle(color: Colors.blue),
        stringMatch: StringMatch.postfix,
        caseSensitive: false,
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      // --------------------------------------------------------------
      // MULTIPLE STRINGS
      // --------------------------------------------------------------

      MatchTargetItem.strings(
        ['do', 'at'],
        style: const TextStyle(color: Colors.pink),
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      // --------------------------------------------------------------
      // SINGLE REGEXP
      // --------------------------------------------------------------

      MatchTargetItem.regex(
        RegExp(r'sweet'),
        style: const TextStyle(color: Colors.indigo),
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),

      // --------------------------------------------------------------
      // MUTIPLE REGEXPs
      // --------------------------------------------------------------

      MatchTargetItem.regexes(
        [RegExp(r'good'), RegExp(r'and')],
        style: const TextStyle(
          decoration: TextDecoration.underline,
          decorationStyle: TextDecorationStyle.wavy,
          decorationColor: Colors.indigo,
          decorationThickness: 3,
        ),
        onSelected: (MatchResultItem match) => _onWordMatchSelected(match),
        onTapInto: (MatchResultItem match) => _onWordMatchTap(match),
      ),
    ],
  );

// ...

TextField(controller: controller)

RichWrapper

RichWrapper(
  initialText: 'Good morning and good night at Github',
  targets: [
    MatchTargetItem.string('good',
        style: const TextStyle(color: Colors.red)),
    MatchTargetItem.strings(['morning', 'night'],
        style: const TextStyle(color: Colors.blue)),
    MatchTargetItem.regex(RegExp(r'and'),
        style: const TextStyle(color: Colors.orange)),
    MatchTargetItem.regexs([RegExp(r'at'), RegExp(r'Github')],
        style: const TextStyle(color: Colors.indigo)),
  ],
  child: (controller) => TextField(controller: controller),
)

InlineSpan-Builder

The [inlineSpanBuilder] is an optional custom builder. In the example below, it returns a WidgetSpan showing aTooltip.

If the [inlineSpanBuilder] is null, highlighting is done as per [style] definition in MatchTargetItem.

MatchTargetItem.strings(
  ['thing', 'it', 'there was', 'used to'],
  style: TextStyle(color: Colors.red),
  stringMatch: StringMatch.exact,
  ///
  /// A custom payload, may be `null`.
  ///
  matchPayload: WritingAidItem(
    'Vague words: Underspecified',
    'Not providing concrete information',
  ),
  ///
  /// InlineSpanBui8uilder returns a `WidgetSpan` with `Tooltip`.
  /// The `Tooltip` is based on the object defined in [matchPayload].
  ///
 inlineSpanBuilder: (
    String match,
    TextStyle matchStyle,
    WritingAidItem? matchPayload) {

    InlineSpan ret = TextSpan(text: match, style: matchStyle);

    if (matchPayload != null) {
      final TextSpan toolTipRichMessage = TextSpan(
          style: Theme.of(context)
              .textTheme
              .titleMedium!
              .copyWith(color: Colors.white),
          children: [
            TextSpan(
                text: match,
                style: const TextStyle(fontStyle: FontStyle.italic)),
            const TextSpan(text: '\n'),
            TextSpan(
                text: matchPayload.category,
                style: const TextStyle(fontWeight: FontWeight.bold)),
            const TextSpan(text: '\n'),
            TextSpan(
                text: matchPayload.description,
                style: const TextStyle(fontStyle: FontStyle.normal)),
          ]);

      ret = WidgetSpan(
          child: Tooltip(
              richMessage: toolTipRichMessage,
              child: Text(match, style: matchStyle)));
    }

    return ret;
  }
),

Example #

See the example app in example/flutter_example for code.

Inspiration #

This package is a complete rewrite of https://github.com/micazi/rich_text_controller.

License #

This project is licensed under the MIT License - see the LICENSE.md file for details

2
likes
160
points
32
downloads

Publisher

verified publishernovelplotter.com

Weekly Downloads

An extended text editing controller that supports different inline styles for custom regex patterns

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on rich_text_controllerx