How to change text color according to valid(@mentions) and all hashtags?
From what I've understood, you want to highlight hashtags and mentions (pre stored in a list) in a String.
Let me break this into 2 parts, the first would be to extract the hashtags & mentions from the text.
List<String> getAllHashtags(String text) {
final regexp = RegExp(r'\#[a-zA-Z0-9]+\b()');
List<String> hashtags = [];
regexp.allMatches(text).forEach((element) {
if (element.group(0) != null) {
hashtags.add(element.group(0).toString());
}
});
return hashtags;
}
List<String> getAllMentions(String text) {
final regexp = RegExp(r'\@[a-zA-Z0-9]+\b()');
List<String> mentions = [];
regexp.allMatches(text).forEach((element) {
if (element.group(0) != null) {
mentions.add(element.group(0).toString());
}
});
return mentions;
}
The above code snippet will successfully extract hashtags & mentions from the given sentence and return it as a list.
The next step would be to build the RichText with the different TextSpans.
RichText buildHighlightedText(String text) {
// clean the text
text = cleanText(text);
List<String> validMentions = ["@mention1", "@mention2"];
List<String> hashtags = getAllHashtags(text);
List<String> mentions = getAllMentions(text);
List<TextSpan> textSpans = [];
text.split(" ").forEach((value) {
if (hashtags.contains(value)) {
textSpans.add(TextSpan(
text: '$value ',
style: TextStyle(color: Colors.amber, fontWeight: FontWeight.bold),
));
} else if (mentions.contains(value) && validMentions.contains(value)) {
textSpans.add(TextSpan(
text: '$value ',
style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
));
} else {
textSpans.add(TextSpan(text: '$value '));
}
});
return RichText(text: TextSpan(children: textSpans));
}
The text has been split by empty spaces, and filters hashtags/mentions and returns differently styled TextSpans for each. This is a more concise and cleaner way of doing what you're looking for.
Here's an example:
Edit:
In order to parse hashtags without spaces, we need to add each instance with a space in front.
String cleanText(String text) {
text = text.replaceAllMapped(
RegExp(r'\w#+'), (Match m) => "${m[0]?.split('').join(" ")}");
text = text.replaceAllMapped(
RegExp(r'\w@+'), (Match m) => "${m[0]?.split('').join(" ")}");
return text;
}
kanwar manraj
Updated on December 30, 2022Comments
-
kanwar manraj over 1 year
I wanted to display hashtags and valid mentions with different colors in the text.
I got helped with this code which works only for hashtagsRichText _convertHashtag(String text) { List<String> split = text.split(RegExp("#")); List<String> hashtags = split.getRange(1, split.length).fold([], (t, e) { var texts = e.split(" "); if (texts.length > 1) { return List.from(t) ..addAll(["#${texts.first}", "${e.substring(texts.first.length)}"]); } return List.from(t)..add("#${texts.first}"); }); return RichText( text: TextSpan( children: [TextSpan(text: split.first)]..addAll(hashtags .map((text) => text.contains("#") ? TextSpan(text: text, style: TextStyle(color: Colors.blue)) : TextSpan(text: text)) .toList()), ), ); }
I modified it like:
List valid_mentions = ['@mention1', '@mention2'];//these are the valid mention RichText _convertHashtag(String text) { List<String> split = text.split(RegExp("#|@")); List<String> hashtags = split.getRange(1, split.length).fold([], (t, e) { var texts = e.split(" "); //here adding `@` sign and `#` sign to the given texts and storing them in the `hashtags` list if (texts.length > 1) { if (valid_mentions.contains(texts.first)) return List.from(t) ..addAll(["@${texts.first}", "${e.substring(texts.first.length)}"]); else if (text.contains('@${texts.first}')) { return List.from(t) ..addAll(["@${texts.first}", "${e.substring(texts.first.length)}"]); } else return List.from(t) ..addAll(["#${texts.first}", "${e.substring(texts.first.length)}"]); } else { if (valid_mentions.contains(texts.first)) return List.from(t)..addAll(["@${texts.first}"]); else if (text.contains('@${texts.first}')) { return List.from(t)..addAll(["@${texts.first}"]); } else return List.from(t)..addAll(["#${texts.first}"]); } }); return RichText( text: TextSpan( children: [TextSpan(text: split.first)]..addAll(hashtags.map((text) { return text.contains("@") ? valid_mentions.contains(text) ? //checking if the mention is valid TextSpan( text: text, recognizer: TapGestureRecognizer() ..onTap = () { print(text); }, style: TextStyle(color: Colors.blue)) : TextSpan( text: text, ) : text.contains("#") ? TextSpan( text: text, recognizer: TapGestureRecognizer() ..onTap = () { print(text); }, style: TextStyle(color: Colors.blue)) : TextSpan( text: text, ); }).toList()), ), ); }
I am able to make the required changes, but i belive its not an optimized way and there is a lot of boiler plate code. How can i optimize it?
input:"I love #flutter #android @mention1 @mention2 @mention3 "
output:"I love #flutter #android @mention1 @mention2 @mention3 "
here @mention3 is not hyperlinked because its not an valid mention. -
kanwar manraj almost 3 yearsThanks 😊 , that worked pretty well. One more thing, what changes need to be made if we want #hello#world or @mention1@mention2 even to be the valid hashtags and mention. As we are using text.split(" ").forEach((value) , they wouldn't split and will not be considered.
-
Akassharjun Shanmugarajah almost 3 yearsThat's true. In that case the text has to be edited to have spaces in front of the values. I've edited my answer. @kanwarmanraj