flutter_richify 0.0.2
flutter_richify: ^0.0.2 copied to clipboard
A Flutter package to make it easy to create links, mentions, hashtags, emails, phones, and custom patterns.
flutter_richify #
A lightweight Flutter package for creating rich text patterns in TextField. Style mentions, hashtags, links, emails, phone numbers, or any custom pattern with full interaction support.
Should you use flutter_richify? #
Use flutter_richify for:
- Pattern detection in text inputs (mentions, hashtags, emails, links, phone numbers)
- Clickable elements in chat, social, or messaging UI
- Email recipient chips or tag inputs
- Lightweight enhancement of standard TextField widgets
Not recommended for:
- Full document editors with formatting toolbars and rich content blocks
- Large text documents
- Heavy content where performance is critical
Usage #
Social Media Mentions & Hashtags #
RichifyController(
text: 'Hey @john! Check out #flutter_richify',
matchers: [
RegexMatcher(
pattern: RegExp(r'@\w+'),
spanBuilder: (candidate) => TextSpan(
text: candidate.text,
style: const TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
),
options: const TextMatcherOptions(deleteOnBack: true),
),
RegexMatcher(
pattern: RegExp(r'#\w+'),
spanBuilder: (candidate) => TextSpan(
text: candidate.text,
style: const TextStyle(color: Colors.green, fontWeight: FontWeight.w600),
),
),
],
onMatch: (matches) => print('Detected: $matches'),
)
Email Chips #
RichifyController(
blockCursorMovement: true, // Lock cursor after chips
matchers: [
RegexMatcher(
pattern: RegExp(r'\S+@\S+\.\S+'),
spanBuilder: (candidate) => WidgetSpan(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(candidate.text),
GestureDetector(
onTap: () { /* Remove chip */ },
child: Icon(Icons.close, size: 16),
),
],
),
),
),
options: const TextMatcherOptions(deleteOnBack: true),
),
],
)
Interactive Links & Phone Numbers #
RichifyController(
text: 'Visit https://flutter.cn or call +1-234-567-8900',
matchers: [
RegexMatcher(
pattern: RegExp(r'https?://[^\s]+'),
spanBuilder: (candidate) => WidgetSpan(
child: GestureDetector(
onTap: () => launchUrl(Uri.parse(candidate.text)),
child: Text(
candidate.text,
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
),
),
priority: 2, // Higher priority wins conflicts
),
RegexMatcher(
pattern: RegExp(r'\+?\d{1,3}[-.\\s]?\(?\d{1,4}\)?[-.\\s]?\d{1,4}[-.\\s]?\d{1,9}'),
spanBuilder: (candidate) => WidgetSpan(
child: GestureDetector(
onTap: () => launchUrl(Uri.parse('tel:${candidate.text}')),
child: Text(
candidate.text,
style: const TextStyle(color: Colors.orange),
),
),
),
priority: 1,
),
],
)
Use with TextField #
TextField(
controller: richifyController,
decoration: const InputDecoration(
hintText: 'Type @username or #hashtag...',
),
maxLines: 3,
)
More Examples #
Custom Validation #
RegexMatcher(
pattern: RegExp(r'@\w+'),
validator: (candidate) {
// Return null to skip invalid matches
if (candidate.text == '@system') return null;
// Return data map to accept and pass context
return {'username': candidate.text.substring(1)};
},
spanBuilder: (candidate) => TextSpan(text: candidate.text),
)
Custom Matchers #
FunctionMatcher(
finder: (context) {
// Build your own matching logic, return list of MatchResult
return [
MatchResult(start: 0, end: 5, priority: 1),
];
},
spanBuilder: (candidate) => TextSpan(text: candidate.text),
)
Options #
TextMatcherOptions(
deleteOnBack: true, // Delete entire match on backspace
jumpOver: true, // Skip cursor over matches when navigating
)
RichifyController(
blockCursorMovement: true, // Force cursor after all matches
matchers: [...],
)
RegexMatcher(
priority: 2, // Higher priority wins when patterns overlap
...
)