init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
/// Extensions on [Uri]
|
||||
extension UriX on Uri {
|
||||
/// Return the URI adding the http scheme if it is missing
|
||||
Uri get withScheme {
|
||||
if (hasScheme) return this;
|
||||
return Uri.parse('http://${toString()}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:link_preview_generator_for_us/link_preview_generator.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/extensions.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/link_preview_entry.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class LinkUtils {
|
||||
static RegExp urlReg = RegExp(
|
||||
r"([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/|[wW]{3}.|[wW][aA][pP].|[fF][tT][pP].|[fF][iI][lL][eE].)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");
|
||||
|
||||
/// Get all the URL from a text message
|
||||
static List<String> getURLMatches(String textMessage) {
|
||||
final matches = urlReg.allMatches(textMessage).toList();
|
||||
|
||||
List<String> urlMatches = [];
|
||||
|
||||
for (Match m in matches) {
|
||||
String match = m.group(0) ?? "";
|
||||
urlMatches.add(match);
|
||||
}
|
||||
|
||||
return urlMatches;
|
||||
}
|
||||
|
||||
/// Launch URL
|
||||
static Future<void> launchURL(BuildContext context, String url) async {
|
||||
try {
|
||||
await launchUrl(
|
||||
Uri.parse(url).withScheme,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(TIM_t("无法打开URL"))), // Cannot launch the url
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get color
|
||||
static Color hexToColor(String hexString) {
|
||||
return Color(int.parse(hexString, radix: 16)).withAlpha(255);
|
||||
}
|
||||
|
||||
/// Get the URL preview information
|
||||
static Future<List<LocalCustomDataModel>> getURLPreview(List<String> urlMatches) async {
|
||||
// Request for preview information for all URL links synchronously
|
||||
final List<LocalCustomDataModel> urlPreview = await Future.wait(urlMatches.map((e) async {
|
||||
String url = e;
|
||||
if (!e.contains("http")) {
|
||||
url = 'http://$e';
|
||||
}
|
||||
final WebInfo info = await LinkPreviewForUs.scrapeFromURL(url);
|
||||
|
||||
return LocalCustomDataModel(url: e, title: info.title, image: info.image, description: info.description);
|
||||
}));
|
||||
|
||||
return urlPreview;
|
||||
}
|
||||
|
||||
/// save the link info to local and call updating the message on UI, only works with [onUpdateMessage]
|
||||
static Future<void> saveToLocalAndUpdate(
|
||||
V2TimMessage message, LocalCustomDataModel previewItem, ValueChanged<V2TimMessage> onUpdateMessage) async {
|
||||
if (message.msgID != null) {
|
||||
String saveInfo = LinkPreviewEntry.linkInfoToString(previewItem);
|
||||
final currentInfo = message.localCustomData;
|
||||
if (currentInfo != null && currentInfo.isNotEmpty) {
|
||||
final Map<String, dynamic> data = json.decode(currentInfo);
|
||||
data['url'] = previewItem.url;
|
||||
data['image'] = previewItem.image;
|
||||
data['title'] = previewItem.title;
|
||||
data['description'] = previewItem.description;
|
||||
saveInfo = json.encode(data);
|
||||
}
|
||||
message.localCustomData = saveInfo;
|
||||
if (saveInfo != currentInfo) {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager
|
||||
.setLocalCustomData(msgID: message.msgID!, localCustomData: saveInfo);
|
||||
if (result.code == 0) {
|
||||
onUpdateMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/emoji_text.dart';
|
||||
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
|
||||
|
||||
RegExp emojiExp = RegExp(r'\[([\u4e00-\u9fa5A-Za-z0-9]+)\]');
|
||||
|
||||
String mdTextCompiler(String originalText, {
|
||||
bool isUseQQPackage = false,
|
||||
bool isUseTencentCloudChatPackage = false,
|
||||
bool isUseTencentCloudChatPackageOldKeys = false,
|
||||
List<CustomEmojiFaceData> customEmojiStickerList = const [],
|
||||
}) {
|
||||
String text = originalText;
|
||||
final EmojiUtil emojiUtil = EmojiUtil(
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
isUseTencentCloudChatPackageOldKeys: isUseTencentCloudChatPackageOldKeys,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
|
||||
text = text.replaceAllMapped(emojiExp, (match) {
|
||||
String key = match.group(0)!;
|
||||
|
||||
// Check if the emoji exists in the emoji map
|
||||
if (emojiUtil.emojiMap.containsKey(key)) {
|
||||
String assetPath = emojiUtil.emojiMap[key]!;
|
||||
return '';
|
||||
}
|
||||
return key;
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_preview.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_text.dart';
|
||||
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
|
||||
|
||||
import 'models/link_preview_content.dart';
|
||||
|
||||
class LinkPreviewEntry {
|
||||
/// get the text message with hyperlinks
|
||||
static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown,
|
||||
{Function(String)? onLinkTap,
|
||||
bool isEnableTextSelection = false,
|
||||
bool isUseQQPackage = false,
|
||||
bool isUseTencentCloudChatPackage = false,
|
||||
bool isUseTencentCloudChatPackageOldKeys = false,
|
||||
List<CustomEmojiFaceData> customEmojiStickerList = const []}) {
|
||||
return ({TextStyle? style}) {
|
||||
return isMarkdown
|
||||
? LinkTextMarkdown(
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
isUseTencentCloudChatPackageOldKeys: isUseTencentCloudChatPackageOldKeys,
|
||||
customEmojiStickerList: customEmojiStickerList,
|
||||
isEnableTextSelection: isEnableTextSelection,
|
||||
messageText: addSpaceAfterLeftBracket(addSpaceBeforeHttp(replaceSingleNewlineWithTwo(messageText))),
|
||||
style: style,
|
||||
onLinkTap: onLinkTap)
|
||||
: LinkText(
|
||||
isEnableTextSelection: isEnableTextSelection,
|
||||
messageText: messageText,
|
||||
style: style,
|
||||
onLinkTap: onLinkTap,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
isUseTencentCloudChatPackageOldKeys: isUseTencentCloudChatPackageOldKeys,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
};
|
||||
}
|
||||
|
||||
static String addSpaceAfterLeftBracket(String inputText) {
|
||||
return inputText.splitMapJoin(
|
||||
RegExp(r'<\w+[^<>]*>'),
|
||||
onMatch: (match) {
|
||||
return match.group(0)!.replaceFirst('<', '< ');
|
||||
},
|
||||
onNonMatch: (text) => text,
|
||||
);
|
||||
}
|
||||
|
||||
static String replaceSingleNewlineWithTwo(String inputText) {
|
||||
return inputText.split('\n').join('\n\n');
|
||||
}
|
||||
|
||||
static String addSpaceBeforeHttp(String inputText) {
|
||||
return inputText.splitMapJoin(
|
||||
RegExp(r'http'),
|
||||
onMatch: (match) {
|
||||
return ' http';
|
||||
},
|
||||
onNonMatch: (text) => text,
|
||||
);
|
||||
}
|
||||
|
||||
/// get the [LinkPreviewContent] with preview widget and website information for the first link.
|
||||
/// If you provide `onUpdateMessage(String linkInfoJson)`, it can save the link info to local custom data than call updating the message on UI automatically.
|
||||
static Future<LinkPreviewContent?> getFirstLinkPreviewContent(
|
||||
{required V2TimMessage message, ValueChanged<V2TimMessage>? onUpdateMessage}) async {
|
||||
final String? messageText = message.textElem?.text;
|
||||
if (messageText == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<String> urlMatches = LinkUtils.getURLMatches(messageText);
|
||||
if (urlMatches.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<LocalCustomDataModel?> previewItemList = await LinkUtils.getURLPreview([urlMatches[0]]);
|
||||
if (previewItemList.isNotEmpty) {
|
||||
final LocalCustomDataModel previewItem = previewItemList.first!;
|
||||
if (onUpdateMessage != null) {
|
||||
LinkUtils.saveToLocalAndUpdate(message, previewItem, onUpdateMessage);
|
||||
}
|
||||
return LinkPreviewContent(
|
||||
linkInfo: previewItem,
|
||||
linkPreviewWidget: LinkPreviewWidget(linkPreview: previewItem),
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// get the [LinkPreviewContent] with preview widget and website information for all the links
|
||||
static Future<List<LinkPreviewContent?>?> getAllLinkPreviewContent(V2TimMessage message) async {
|
||||
final String? messageText = message.textElem?.text;
|
||||
if (messageText == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<String> urlMatches = LinkUtils.getURLMatches(messageText);
|
||||
if (urlMatches.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final List<LocalCustomDataModel> previewItemList = await LinkUtils.getURLPreview([urlMatches[0]]);
|
||||
if (previewItemList.isNotEmpty) {
|
||||
final List<LinkPreviewContent?> resultList = previewItemList
|
||||
.map((e) => LinkPreviewContent(
|
||||
linkInfo: e,
|
||||
linkPreviewWidget: LinkPreviewWidget(linkPreview: e),
|
||||
))
|
||||
.toList();
|
||||
|
||||
return resultList;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static String linkInfoToString(LocalCustomDataModel linkInfo) {
|
||||
return linkInfo.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
typedef LinkPreviewText = Widget Function({TextStyle? style});
|
||||
|
||||
class LocalCustomDataModel {
|
||||
final String? description;
|
||||
final String? image;
|
||||
final String? url;
|
||||
final String? title;
|
||||
String? translatedText;
|
||||
|
||||
LocalCustomDataModel(
|
||||
{this.description, this.image, this.url, this.title, this.translatedText});
|
||||
|
||||
Map<String, String?> toMap() {
|
||||
final Map<String, String?> data = {};
|
||||
data['url'] = url;
|
||||
data['image'] = image;
|
||||
data['title'] = title;
|
||||
data['description'] = description;
|
||||
data['translatedText'] = translatedText;
|
||||
return data;
|
||||
}
|
||||
|
||||
LocalCustomDataModel.fromMap(Map map)
|
||||
: description = map['description'],
|
||||
image = map['image'],
|
||||
url = map['url'],
|
||||
translatedText = map['translatedText'],
|
||||
title = map['title'];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return json.encode(toMap());
|
||||
}
|
||||
|
||||
bool isLinkPreviewEmpty() {
|
||||
if ((image == null || image!.isEmpty) &&
|
||||
(title == null || title!.isEmpty) &&
|
||||
(description == null || description!.isEmpty)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class LinkPreviewContent {
|
||||
const LinkPreviewContent({
|
||||
this.linkInfo,
|
||||
this.linkPreviewWidget,
|
||||
});
|
||||
|
||||
final LocalCustomDataModel? linkInfo;
|
||||
final Widget? linkPreviewWidget;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_stateless_widget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/models/link_preview_content.dart';
|
||||
|
||||
class LinkPreviewWidget extends TIMStatelessWidget {
|
||||
final LocalCustomDataModel linkPreview;
|
||||
|
||||
const LinkPreviewWidget({Key? key, required this.linkPreview})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
if (linkPreview.isLinkPreviewEmpty()) {
|
||||
return Container();
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if(linkPreview.url != null){
|
||||
LinkUtils.launchURL(context, linkPreview.url!);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 8, right: 8),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0x19696969),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (linkPreview.title != null && linkPreview.title!.isNotEmpty)
|
||||
Text(
|
||||
linkPreview.title!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Color(0xFF444444),
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (linkPreview.image != null && linkPreview.image!.isNotEmpty)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: Image.network(linkPreview.image!),
|
||||
),
|
||||
),
|
||||
if (linkPreview.description != null &&
|
||||
linkPreview.description!.isNotEmpty)
|
||||
Expanded(
|
||||
child: Text(
|
||||
linkPreview.description!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0, color: Color(0xFF999999)),
|
||||
)),
|
||||
if ((linkPreview.description == null ||
|
||||
linkPreview.description!.isEmpty) &&
|
||||
linkPreview.title != null &&
|
||||
linkPreview.title!.isNotEmpty)
|
||||
Expanded(
|
||||
child: Text(
|
||||
linkPreview.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0, color: Color(0xFF999999)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
import 'package:extended_text/extended_text.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_stateless_widget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/compiler/md_text.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
||||
import 'package:markdown/markdown.dart' as md;
|
||||
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
|
||||
|
||||
typedef ImageBuilder = Widget Function(
|
||||
Uri uri, String? imageDirectory, double? width, double? height);
|
||||
|
||||
class LinkTextMarkdown extends TIMStatelessWidget {
|
||||
/// Callback for when link is tapped
|
||||
final void Function(String)? onLinkTap;
|
||||
|
||||
/// message text
|
||||
final String messageText;
|
||||
|
||||
/// text style for default words
|
||||
final TextStyle? style;
|
||||
|
||||
final bool? isEnableTextSelection;
|
||||
|
||||
final bool isUseQQPackage;
|
||||
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final bool isUseTencentCloudChatPackageOldKeys;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
const LinkTextMarkdown(
|
||||
{Key? key,
|
||||
required this.messageText,
|
||||
this.isUseQQPackage = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.isUseTencentCloudChatPackageOldKeys = false,
|
||||
this.customEmojiStickerList = const [],
|
||||
this.isEnableTextSelection,
|
||||
this.onLinkTap,
|
||||
this.style})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
return MarkdownBody(
|
||||
data: mdTextCompiler(messageText,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
customEmojiStickerList: customEmojiStickerList),
|
||||
selectable: isEnableTextSelection ?? false,
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
|
||||
textTheme: TextTheme(
|
||||
bodyMedium: style ?? const TextStyle(fontSize: 16.0))))
|
||||
.copyWith(
|
||||
a: TextStyle(color: LinkUtils.hexToColor("015fff")),
|
||||
),
|
||||
extensionSet: md.ExtensionSet.gitHubWeb,
|
||||
onTapLink: (
|
||||
String link,
|
||||
String? href,
|
||||
String title,
|
||||
) {
|
||||
if (onLinkTap != null) {
|
||||
onLinkTap!(href ?? "");
|
||||
} else {
|
||||
LinkUtils.launchURL(context, href ?? "");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkText extends TIMStatelessWidget {
|
||||
/// Callback for when link is tapped
|
||||
final void Function(String)? onLinkTap;
|
||||
|
||||
/// message text
|
||||
final String messageText;
|
||||
|
||||
/// text style for default words
|
||||
final TextStyle? style;
|
||||
|
||||
final bool isUseQQPackage;
|
||||
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final bool isUseTencentCloudChatPackageOldKeys;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final bool? isEnableTextSelection;
|
||||
|
||||
const LinkText(
|
||||
{Key? key,
|
||||
required this.messageText,
|
||||
this.onLinkTap,
|
||||
this.isEnableTextSelection,
|
||||
this.style,
|
||||
this.isUseQQPackage = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.isUseTencentCloudChatPackageOldKeys = false,
|
||||
this.customEmojiStickerList = const []})
|
||||
: super(key: key);
|
||||
|
||||
String _getContentSpan(String text, BuildContext context) {
|
||||
List<InlineSpan> _contentList = [];
|
||||
String contentData = PlatformUtils().isWeb ? '\u200B' : "";
|
||||
|
||||
Iterable<RegExpMatch> matches = LinkUtils.urlReg.allMatches(text);
|
||||
|
||||
int index = 0;
|
||||
for (RegExpMatch match in matches) {
|
||||
String c = text.substring(match.start, match.end);
|
||||
if (match.start == index) {
|
||||
index = match.end;
|
||||
}
|
||||
if (index < match.start) {
|
||||
String a = text.substring(index, match.start);
|
||||
index = match.end;
|
||||
contentData += a;
|
||||
_contentList.add(
|
||||
TextSpan(text: a),
|
||||
);
|
||||
}
|
||||
|
||||
if (LinkUtils.urlReg.hasMatch(c)) {
|
||||
contentData += HttpText.flag + c + HttpText.flag;
|
||||
_contentList.add(TextSpan(
|
||||
text: c,
|
||||
style: TextStyle(color: LinkUtils.hexToColor("015fff")),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
if (onLinkTap != null) {
|
||||
onLinkTap!(text.substring(match.start, match.end));
|
||||
} else {
|
||||
LinkUtils.launchURL(
|
||||
context, text.substring(match.start, match.end));
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
contentData += c;
|
||||
_contentList.add(
|
||||
TextSpan(text: c, style: style ?? const TextStyle(fontSize: 16.0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (index < text.length) {
|
||||
String a = text.substring(index, text.length);
|
||||
contentData += a;
|
||||
_contentList.add(
|
||||
TextSpan(text: a, style: style ?? const TextStyle(fontSize: 16.0)),
|
||||
);
|
||||
}
|
||||
|
||||
return contentData;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
return ExtendedText(_getContentSpan(messageText, context), softWrap: true,
|
||||
onSpecialTextTap: (dynamic parameter) {
|
||||
if (parameter.toString().startsWith(HttpText.flag)) {
|
||||
if (onLinkTap != null) {
|
||||
onLinkTap!((parameter.toString()).replaceAll(HttpText.flag, ''));
|
||||
} else {
|
||||
LinkUtils.launchURL(
|
||||
context, (parameter.toString()).replaceAll(HttpText.flag, ''));
|
||||
}
|
||||
}
|
||||
},
|
||||
style: style ?? const TextStyle(fontSize: 16.0),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
isUseTencentCloudChatPackageOldKeys: isUseTencentCloudChatPackageOldKeys,
|
||||
customEmojiStickerList: customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user