Flutter中生成动态富文本:实现提及与话题标签的可点击样式
Flutter评论组件:提及与话题标签的可点击渲染实现
要实现评论内容中提及(@开头)和话题标签(#开头)的可点击+异色显示,核心思路是把原评论字符串拆分成普通文本、提及项、话题标签三种类型的片段,用TextSpan分别定义样式和点击行为,最后通过RichText组件组合渲染。
具体实现代码
import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; class CommentWidget extends StatelessWidget { final String commentMessage; final List<String> mentionList; final List<String> hashtagList; const CommentWidget({ super.key, required this.commentMessage, required this.mentionList, required this.hashtagList, }); // 拆分评论内容为带样式的TextSpan列表 List<TextSpan> _buildTextSpans() { final List<_SpanItem> spanItems = []; // 添加提及项,标记类型为mention for (final mention in mentionList) { final startIndex = commentMessage.indexOf(mention); if (startIndex != -1) { spanItems.add(_SpanItem( text: mention, start: startIndex, type: _SpanType.mention, )); } } // 添加话题标签,标记类型为hashtag for (final hashtag in hashtagList) { final startIndex = commentMessage.indexOf(hashtag); if (startIndex != -1) { spanItems.add(_SpanItem( text: hashtag, start: startIndex, type: _SpanType.hashtag, )); } } // 按出现顺序排序,避免拆分混乱 spanItems.sort((a, b) => a.start.compareTo(b.start)); final List<TextSpan> textSpans = []; int currentIndex = 0; for (final item in spanItems) { // 添加前面的普通文本 if (item.start > currentIndex) { textSpans.add(TextSpan( text: commentMessage.substring(currentIndex, item.start), style: const TextStyle(color: Colors.black87), )); } // 添加带样式和点击事件的提及/话题标签 TextStyle style; TapGestureRecognizer? recognizer; if (item.type == _SpanType.mention) { style = const TextStyle(color: Colors.blue, decoration: TextDecoration.underline); recognizer = TapGestureRecognizer() ..onTap = () { // 处理提及点击事件,比如跳转到用户主页 print('点击了提及:${item.text}'); }; } else { style = const TextStyle(color: Colors.orange, decoration: TextDecoration.underline); recognizer = TapGestureRecognizer() ..onTap = () { // 处理话题标签点击事件,比如搜索话题 print('点击了话题:${item.text}'); }; } textSpans.add(TextSpan( text: item.text, style: style, recognizer: recognizer, )); currentIndex = item.start + item.text.length; } // 添加最后一段普通文本 if (currentIndex < commentMessage.length) { textSpans.add(TextSpan( text: commentMessage.substring(currentIndex), style: const TextStyle(color: Colors.black87), )); } return textSpans; } @override Widget build(BuildContext context) { return RichText( text: TextSpan( children: _buildTextSpans(), style: DefaultTextStyle.of(context).style, ), ); } } // 辅助类:标记每个特殊片段的信息 enum _SpanType { mention, hashtag } class _SpanItem { final String text; final int start; final _SpanType type; _SpanItem({required this.text, required this.start, required this.type}); }
使用示例
// 在页面中调用CommentWidget CommentWidget( commentMessage: 'Hi @Brad Pitt are u so cute #socute @Jackie Chan', mentionList: ['@Brad Pitt', '@Jackie Chan'], hashtagList: ['#socute'], )
关键说明
- 用
_SpanItem类统一管理特殊片段的文本、起始位置和类型,确保拆分顺序正确 - 通过
TapGestureRecognizer给提及和话题标签添加点击事件,可根据需求替换成跳转页面等逻辑 - 样式可自定义:比如调整颜色、是否显示下划线,匹配你的App设计规范
内容的提问来源于stack exchange,提问作者VolkanUstekidag
相关产品推荐
相关产品推荐

