Skip to content

Tz-dev0/text_mark

Repository files navigation

Text Mark

style: very good analysis Powered by Mason License: MIT


Rich inline text styling for Flutter — your tags, your syntax, zero friction.

Define [tags] once, nest them freely, and render styled text natively via Flutter's RichText / TextSpan. No Markdown lock-in, no fixed syntax.

Installation

❗ In order to start using Text Mark you must have the Flutter SDK installed on your machine.

Install via flutter pub add:

dart pub add text_mark

Features

  • Your syntax[tag] by default, or any custom format via TagFormat
  • Nesting[bold][red]...[/red][/bold] just works; child styles win on conflict
  • Dynamic values[c=FF5733], [size=24] — value embedded in the tag
  • Three API levels — local styles / dynamic tags / global TextMarkTheme
  • LRU cache — parse results cached by (text + rules fingerprint), 200 entries default
  • Extensions.textmark() on String and Text
  • Zero dependencies — pure Flutter

Quick start

Standalone (no theme)

TextMark(
  text: "[bold]Hello[/bold] [red]world[/red]!",
  tagFormat: TagFormat.bracket,
  styles: {
    'bold': TextStyle(fontWeight: FontWeight.bold),
    'red':  TextStyle(color: Color(0xFFE53935)),
  },
)

With a global theme (recommended)

Define your styles once in a dedicated file:

// app_text_styles.dart
class AppTextStyles {
  static const styles = <String, TextStyle>{
    'red':  TextStyle(color: Color(0xFFE53935)),
    'bold': TextStyle(fontWeight: FontWeight.bold),
    'h1':   TextStyle(fontSize: 28, fontWeight: FontWeight.w800),
    'warn': TextStyle(color: Color(0xFFF57F17), backgroundColor: Color(0xFFFFF9C4)),
  };
 
  static final dynamicStyles = <String, TextStyle Function(String?)>{
    'c':    (v) => TextStyle(color: _hexColor(v ?? 'FF0000')),
    'size': (v) => TextStyle(fontSize: double.tryParse(v ?? '16') ?? 16),
  };
}

Place the theme inside MaterialApp.builder:

MaterialApp(
  builder: (context, child) => TextMarkTheme(
    tagFormat: TagFormat.bracket,
    styles: AppTextStyles.styles,
    dynamicStyles: AppTextStyles.dynamicStyles,
    child: child!,
  ),
)

Then anywhere in the tree — no repeated config:

// Widget
TextMark(text: "[h1]Title[/h1] with [red]alert[/red]")
 
// String extension
"[bold]Easy[/bold] as [red]that[/red].".textmark()
 
// Text extension — preserves style, textAlign, maxLines…
Text("[warn]Warning:[/warn] read carefully.", style: TextStyle(fontSize: 16))
  .textmark()

Nesting

TextMark(
  text: "[bold]This is bold and [red]this part is also red[/red], "
        "back to bold only[/bold], then plain.",
)
  • Child styles win on conflict (e.g. a red child inside a blue parent → red)
  • Parent styles propagate where the child is silent (e.g. bold parent + red child → bold + red)
  • Misordered closing tags degrade gracefully — no crash

Dynamic value tags

TextMark(
  text: "Price: [c=E53935]€49[/c]  Size: [size=24]big[/size]",
  dynamicStyles: {
    'c':    (v) => TextStyle(color: _hexColor(v ?? 'FF0000')),
    'size': (v) => TextStyle(fontSize: double.tryParse(v ?? '16') ?? 16),
    'bg':   (v) => TextStyle(backgroundColor: _hexColor(v ?? 'FFFF00')),
    'font': (v) => TextStyle(fontFamily: v),
  },
)

Local overrides

Local styles are merged on top of the theme — local keys always win:

TextMark(
  text: "[bold]Global bold[/bold] and [vip]local only[/vip].",
  styles: {
    'vip': TextStyle(color: Color(0xFFFFB300), fontWeight: FontWeight.w600),
  },
)

Tag formats

TagFormat.bracket          // [tag]...[/tag]   ← default, recommended
TagFormat.doubleBrace      // {{tag}}...{{/tag}}
TagFormat.wrap('@', '::')  // @tag::...@/tag::
TagFormat.custom(          // fully custom RegExp
  open:  RegExp(r'<(\w+)>'),
  close: RegExp(r'</(\w+)>'),
)

[tag] is the recommended default — safe in JSON, YAML, and API payloads with no escaping required.


Cache

The parse cache lives on TextMarkTheme and is shared across the entire subtree. Cache size is configurable:

TextMarkTheme(cacheSize: 500, ...) // default: 200

The cache key is hash(rawText) + hash(registeredTagNames). Style value changes without tag name changes do NOT invalidate the cache — style maps are expected to be constants.


Roadmap

  • WidgetSpan support — inline icons / custom widgets
  • Parse result caching with style-value fingerprint
  • Accessibility / semantics label support

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors