How to highlight a word in string in flutter programmatically
I recently developed a package called Dynamic Text Highlighting. It let's you to highlight programatically some given words inside a given text.
Take a look at https://pub.dev/packages/dynamic_text_highlighting
Example
Widget buildDTH(String text, List<String> highlights) {
return DynamicTextHighlighting(
text: text,
highlights: highlights,
color: Colors.yellow,
style: TextStyle(
fontSize: 18.0,
fontStyle: FontStyle.italic,
),
caseSensitive: false,
);
}
It is a stateless widget, so for any changes just call setState(() {...})
.
void applyChanges(List<String> newHighlights) {
setState(() {
highlights = newHighlights;
});
}
Wrap the word in a TextSpan and assign style
properties to change the text appearance and use RichText instead of Text
RichText( text: TextSpan( text: 'Hello ', style: DefaultTextStyle.of(context).style, children: <TextSpan>[ TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)), TextSpan(text: ' world!'), ], ), )
or use the Text.rich
constructor https://docs.flutter.io/flutter/widgets/Text-class.html
const Text.rich( TextSpan( text: 'Hello', // default text style children: <TextSpan>[ TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)), TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)), ], ), )
Extending on zhpoo's awesome answer, here's a widget snippet that would allow you to style/highlight anything in a string programmatically using regular expressions (dart's RegExp).
Link to dartpad demo: https://dartpad.dev/d7a0826ed1201f7247fafd9e65979953
class RegexTextHighlight extends StatelessWidget {
final String text;
final RegExp highlightRegex;
final TextStyle highlightStyle;
final TextStyle nonHighlightStyle;
const RegexTextHighlight({
@required this.text,
@required this.highlightRegex,
@required this.highlightStyle,
this.nonHighlightStyle,
});
@override
Widget build(BuildContext context) {
if (text == null || text.isEmpty) {
return Text("",
style: nonHighlightStyle ?? DefaultTextStyle.of(context).style);
}
List<TextSpan> spans = [];
int start = 0;
while (true) {
final String highlight =
highlightRegex.stringMatch(text.substring(start));
if (highlight == null) {
// no highlight
spans.add(_normalSpan(text.substring(start)));
break;
}
final int indexOfHighlight = text.indexOf(highlight, start);
if (indexOfHighlight == start) {
// starts with highlight
spans.add(_highlightSpan(highlight));
start += highlight.length;
} else {
// normal + highlight
spans.add(_normalSpan(text.substring(start, indexOfHighlight)));
spans.add(_highlightSpan(highlight));
start = indexOfHighlight + highlight.length;
}
}
return RichText(
text: TextSpan(
style: nonHighlightStyle ?? DefaultTextStyle.of(context).style,
children: spans,
),
);
}
TextSpan _highlightSpan(String content) {
return TextSpan(text: content, style: highlightStyle);
}
TextSpan _normalSpan(String content) {
return TextSpan(text: content);
}
}
Here is my code.
import 'package:flutter/material.dart';
class HighlightText extends StatelessWidget {
final String text;
final String highlight;
final TextStyle style;
final TextStyle highlightStyle;
final Color highlightColor;
final bool ignoreCase;
HighlightText({
Key key,
this.text,
this.highlight,
this.style,
this.highlightColor,
TextStyle highlightStyle,
this.ignoreCase: false,
}) : assert(
highlightColor == null || highlightStyle == null,
'highlightColor and highlightStyle cannot be provided at same time.',
),
highlightStyle = highlightStyle ?? style?.copyWith(color: highlightColor) ?? TextStyle(color: highlightColor),
super(key: key);
@override
Widget build(BuildContext context) {
final text = this.text ?? '';
if ((highlight?.isEmpty ?? true) || text.isEmpty) {
return Text(text, style: style);
}
var sourceText = ignoreCase ? text.toLowerCase() : text;
var targetHighlight = ignoreCase ? highlight.toLowerCase() : highlight;
List<TextSpan> spans = [];
int start = 0;
int indexOfHighlight;
do {
indexOfHighlight = sourceText.indexOf(targetHighlight, start);
if (indexOfHighlight < 0) {
// no highlight
spans.add(_normalSpan(text.substring(start)));
break;
}
if (indexOfHighlight > start) {
// normal text before highlight
spans.add(_normalSpan(text.substring(start, indexOfHighlight)));
}
start = indexOfHighlight + highlight.length;
spans.add(_highlightSpan(text.substring(indexOfHighlight, start)));
} while (true);
return Text.rich(TextSpan(children: spans));
}
TextSpan _highlightSpan(String content) {
return TextSpan(text: content, style: highlightStyle);
}
TextSpan _normalSpan(String content) {
return TextSpan(text: content, style: style);
}
}