custom_text 0.1.2
custom_text: ^0.1.2 copied to clipboard

A highly customisable text widget that allows styles and tap gestures to be applied to strings in it flexibly.

custom_text #

Pub Version Flutter CI

A customisable text widget for Flutter that allows strings in it to be styled flexibly and/or tapped.

This widget is useful for making link strings tappable, such as URLs, email addresses or phone numbers, or for only highlighting partial strings in text with colors and different font settings depending on the types of the string elements.

Examples / Usage #

Web Demo

The examples here are all contained in the sample app in the example/ folder. Just run it and see what this package is like if you're reluctant to read through all the following descriptions.

The app also shows the source code with keywords highlighted, which is itself thanks to this package.

highlighting

Simplest example #

A very basic example with URLs and email addresses styled. They are not tappable in this example.

example1

example1.dart

CustomText(
  'URL: https://example.com/\n'
  'Email: foo@example.com',
  definitions: const [
    TextDefinition(matcher: UrlMatcher()),
    TextDefinition(matcher: EmailMatcher()),
  ],
  matchStyle: const TextStyle(color: Colors.lightBlue),
  // `tapStyle` is not used if both `onTap` and `onLongTap`
  // are null or not set.
  tapStyle: const TextStyle(color: Colors.yellow),
  onTap: null,
)

Unique styles and actions per definition #

An example to apply styles to URLs, email addresses and phone numbers, and also enable them to be tapped / long-tapped.

All of the three are styled, but only phone numbers among them are given unique matchStyle and tapStyle.

example2

example2.dart

TIPS: Use url_launcher or its equivalent to open a browser or another app by a tap/long-tap on a string.

CustomText(
  'URL: https://example.com/\n'
  'Email: foo@example.com\n'
  'Tel: +1-012-3456-7890',
  definitions: [
    const TextDefinition(matcher: UrlMatcher()),
    const TextDefinition(matcher: EmailMatcher()),
    TextDefinition(
      matcher: const TelMatcher(),
      // `matchStyle`, `tapStyle`, `onTap` and `onLongTap` here
      // override the equivalent parameters of CustomText.
      matchStyle: const TextStyle(
        color: Colors.green,
        decoration: TextDecoration.underline,
      ),
      tapStyle: const TextStyle(color: Colors.orange),
      onTap: (tel) => print(tel),
      onLongTap: (tel) => print('[Long tap] $tel'),
    ),
  ],
  matchStyle: const TextStyle(
    color: Colors.lightBlue,
    decoration: TextDecoration.underline,
  ),
  tapStyle: const TextStyle(color: Colors.indigo),
  onTap: (type, text) => print(text),
  onLongTap: (type, text) => print('[Long tap] $text'),
)

Overwriting match patterns #

An example to overwrite the default match pattern of TelMatcher.

The new pattern regards only the {3 digits}-{4 digits}-{4 digits} format as a phone number.

example3

example3.dart

CustomText(
  'Tel: +1-012-3456-7890',
  definitions: const [
    // Match patterns of preset matchers can be overwritten.
    TextDefinition(matcher: TelMatcher(r'\d{3}-\d{4}-\d{4}')),
  ],
  matchStyle: const TextStyle(color: Colors.lightBlue),
  onTap: (_, text) => print(text),
)

Custom matchers #

An example to parse hashtags using a custom matcher and apply styles to them.

A hashtag has a wide variety of definitions, but here as an example, it is defined as a string that starts with "#" followed by an alphabet and then by alphanumerics, and is enclosed with white spaces.

example4

example4.dart

// You can create a custom matcher easily by extending TextMatcher.
class HashTagMatcher extends TextMatcher {
  const HashTagMatcher()
      : super(r'(?<=\s|^)\#[a-zA-Z][a-zA-Z0-9]{1,}(?=\s|$)');
}

...

CustomText(
  'Hello world! #CustomText',
  definitions: const [
    TextDefinition(matcher: HashTagMatcher()),
  ],
  matchStyle: const TextStyle(color: Colors.lightBlue),
)

SelectiveDefinition #

An example to parse markdown-style links, like [label](url) using SelectiveDefinition, and make them tappable.

Each of the string shown in the widget and the string passed to the tap callbacks is selected individually from the fragments (groups) that have matched the patterns enclosed with parentheses within the match pattern.

For details of groups, please see the document of the text_parser package, which this package uses internally.

example5

example5.dart

class MdLinkMatcher extends TextMatcher {
  const MdLinkMatcher() : super(r'\[(.+?)\]\((.+?)\)');
}

...

CustomText(
  'Markdown-style link\n'
  '[Tap here](Tapped!)',
  definitions: [
    SelectiveDefinition(
      matcher: const MdLinkMatcher(),
      // `labelSelector` is used to choose the string to show.
      // `groups` provided to `labelSelector` is an array of
      // strings matching the fragments enclosed in parentheses
      // within the match pattern.
      labelSelector: (groups) => groups[0],
      // `tapSelector` is used to choose the string to be passed
      // to the `onTap` and `onLongTap` callbacks.
      tapSelector: (groups) => groups[1],
    ),
  ],
  matchStyle: const TextStyle(color: Colors.lightBlue),
  tapStyle: const TextStyle(color: Colors.green),
  onTap: (_, text) => print(text),
)

SpanDefinition #

An example to show both strings and icons using SpanDefinition.

The builder parameter takes a function returning an InlineSpan. The function is provided with matched string and groups, so it is possible to compose an InlineSpan flexibly with them.

Notes

  • SpanDefinition does not have arguments for styles and tap callbacks, so it is totally up to you how the InlineSpan returned from it is decorated and how it reacts to gestures.
  • The builder function is called on every rebuild. If you create a GestureRecognizer inside the function, store it in such a way that you can check if one already exists to avoid so many recognizers being created.

example6

example6.dart

CustomText(
  'Email 1: foo@example.com\n'
  'Email 2: bar@example.com',
  definitions: [
    SpanDefinition(
      matcher: const EmailMatcher(),
      builder: (text, groups) => TextSpan(
        children: [
          const WidgetSpan(
            child: Icon(
              Icons.email,
              color: Colors.blueGrey,
              size: 18.0,
            ),
            alignment: PlaceholderAlignment.middle,
          ),
          const WidgetSpan(
            child: SizedBox(width: 6.0),
          ),
          TextSpan(
            text: text,
            style: const TextStyle(color: Colors.lightBlue),
            recognizer: ...,
          ),
        ],
      ),
    ),
  ],
)

[Experimental] Changing mouse cursor on hover over tappable element #

New experimental feature to allow a different mouse cursor to be used while the pointer hovers over a tappable string. You can opt in to the feature by setting one of SystemMouseCursors other than SystemMouseCursors.basic to cursorOnHover.

CustomText(
  'text',
  definitions: [ ... ],
  onTap: (type, text) => ...,
  cursorOnHover: SystemMouseCursors.click,
)

Limitations

On web, the mouse cursor may change at a wrong position if a SpanDefinition contains WidgetSpan(s). It is probably a bug or a limitation of Flutter itself.

  • text_parser
    • CustomText is dependent on the text_parser package. Please see its documentation for details or troubleshooting on parsing.
5
likes
130
pub points
51%
popularity

Publisher

kaboc.cc

A highly customisable text widget that allows styles and tap gestures to be applied to strings in it flexibly.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter, meta, text_parser

More

Packages that depend on custom_text