diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 54930c6..697e3f7 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -31,4 +31,18 @@ android:name="flutterEmbedding" android:value="2" /> + + + + + + + + + + + + + + diff --git a/example/assets/messages.json b/example/assets/messages.json index 3f35b36..5eb574a 100644 --- a/example/assets/messages.json +++ b/example/assets/messages.json @@ -1,4 +1,16 @@ [ + { + "author": { + "firstName": "John", + "id": "b4878b96-efbc-479a-8291-474ef323dec7", + "imageUrl": "https://avatars.githubusercontent.com/u/14123304?v=4" + }, + "createdAt": 1598438797000, + "id": "e7a673e9-86eb-4572-936f-2882b0183cdc", + "status": "seen", + "text": "**bold** _italic_ ~strikethrough~ `code`\nhttps://link-test.com test@email.com", + "type": "text" + }, { "author": { "firstName": "John", diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 713fcc1..2e377b3 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -22,6 +22,11 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationQueriesSchemes + + https + http + LSRequiresIPhoneOS NSCameraUsageDescription diff --git a/lib/src/chat_theme.dart b/lib/src/chat_theme.dart index 083e270..68062f6 100644 --- a/lib/src/chat_theme.dart +++ b/lib/src/chat_theme.dart @@ -74,6 +74,8 @@ abstract class ChatTheme { required this.receivedEmojiMessageTextStyle, this.receivedMessageBodyLinkTextStyle, required this.receivedMessageBodyTextStyle, + this.receivedMessageBodyBoldTextStyle, + this.receivedMessageBodyCodeTextStyle, required this.receivedMessageCaptionTextStyle, required this.receivedMessageDocumentIconColor, required this.receivedMessageLinkDescriptionTextStyle, @@ -86,6 +88,8 @@ abstract class ChatTheme { required this.sentEmojiMessageTextStyle, this.sentMessageBodyLinkTextStyle, required this.sentMessageBodyTextStyle, + this.sentMessageBodyBoldTextStyle, + this.sentMessageBodyCodeTextStyle, required this.sentMessageCaptionTextStyle, required this.sentMessageDocumentIconColor, required this.sentMessageLinkDescriptionTextStyle, @@ -175,6 +179,14 @@ abstract class ChatTheme { /// of received messages final TextStyle receivedMessageBodyTextStyle; + /// Body text style used for displaying bold text on received text messages. + /// Default to a bold version of [receivedMessageBodyTextStyle]. + final TextStyle? receivedMessageBodyBoldTextStyle; + + /// Body text style used for displaying code text on received text messages. + /// Defaults to a mono version of [receivedMessageBodyTextStyle]. + final TextStyle? receivedMessageBodyCodeTextStyle; + /// Caption text style used for displaying secondary info (e.g. file size) /// on different types of received messages final TextStyle receivedMessageCaptionTextStyle; @@ -215,6 +227,14 @@ abstract class ChatTheme { /// of sent messages final TextStyle sentMessageBodyTextStyle; + /// Body text style used for displaying bold text on sent text messages. + /// Defaults to a bold version of [sentMessageBodyTextStyle]. + final TextStyle? sentMessageBodyBoldTextStyle; + + /// Body text style used for displaying code text on sent text messages. + /// Defaults to a mono version of [sentMessageBodyTextStyle]. + final TextStyle? sentMessageBodyCodeTextStyle; + /// Caption text style used for displaying secondary info (e.g. file size) /// on different types of sent messages final TextStyle sentMessageCaptionTextStyle; diff --git a/lib/src/widgets/text_message.dart b/lib/src/widgets/text_message.dart index 9e4bcbe..75a32db 100644 --- a/lib/src/widgets/text_message.dart +++ b/lib/src/widgets/text_message.dart @@ -1,7 +1,12 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'package:flutter_link_previewer/flutter_link_previewer.dart' - show LinkPreview, regexLink; + show LinkPreview, regexEmail, regexLink; +import 'package:flutter_parsed_text/flutter_parsed_text.dart'; +import 'package:url_launcher/url_launcher.dart'; + import '../models/emoji_enlargement_behavior.dart'; import '../util.dart'; import 'inherited_chat_theme.dart'; @@ -50,12 +55,6 @@ class TextMessage extends StatelessWidget { double width, BuildContext context, ) { - final bodyLinkTextStyle = user.id == message.author.id - ? InheritedChatTheme.of(context).theme.sentMessageBodyLinkTextStyle - : InheritedChatTheme.of(context).theme.receivedMessageBodyLinkTextStyle; - final bodyTextStyle = user.id == message.author.id - ? InheritedChatTheme.of(context).theme.sentMessageBodyTextStyle - : InheritedChatTheme.of(context).theme.receivedMessageBodyTextStyle; final linkDescriptionTextStyle = user.id == message.author.id ? InheritedChatTheme.of(context) .theme @@ -69,18 +68,9 @@ class TextMessage extends StatelessWidget { .theme .receivedMessageLinkTitleTextStyle; - final color = getUserAvatarNameColor(message.author, - InheritedChatTheme.of(context).theme.userAvatarNameColors); - final name = getUserName(message.author); - return LinkPreview( enableAnimation: true, - header: showName ? name : null, - headerStyle: InheritedChatTheme.of(context) - .theme - .userNameTextStyle - .copyWith(color: color), - linkStyle: bodyLinkTextStyle ?? bodyTextStyle, + textWidget: _textWidgetBuilder(user, context, false), metadataTextStyle: linkDescriptionTextStyle, metadataTitleStyle: linkTitleTextStyle, onPreviewDataFetched: _onPreviewDataFetched, @@ -91,7 +81,6 @@ class TextMessage extends StatelessWidget { ), previewData: message.previewData, text: message.text, - textStyle: bodyTextStyle, width: width, ); } @@ -105,6 +94,22 @@ class TextMessage extends StatelessWidget { final color = getUserAvatarNameColor(message.author, theme.userAvatarNameColors); final name = getUserName(message.author); + final bodyTextStyle = user.id == message.author.id + ? enlargeEmojis + ? theme.sentEmojiMessageTextStyle + : theme.sentMessageBodyTextStyle + : enlargeEmojis + ? theme.receivedEmojiMessageTextStyle + : theme.receivedMessageBodyTextStyle; + final boldTextStyle = user.id == message.author.id + ? theme.sentMessageBodyBoldTextStyle + : theme.receivedMessageBodyBoldTextStyle; + final codeTextStyle = user.id == message.author.id + ? theme.sentMessageBodyCodeTextStyle + : theme.receivedMessageBodyCodeTextStyle; + final bodyLinkTextStyle = user.id == message.author.id + ? InheritedChatTheme.of(context).theme.sentMessageBodyLinkTextStyle + : InheritedChatTheme.of(context).theme.receivedMessageBodyLinkTextStyle; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -119,16 +124,72 @@ class TextMessage extends StatelessWidget { style: theme.userNameTextStyle.copyWith(color: color), ), ), - SelectableText( - message.text, - style: user.id == message.author.id - ? enlargeEmojis - ? theme.sentEmojiMessageTextStyle - : theme.sentMessageBodyTextStyle - : enlargeEmojis - ? theme.receivedEmojiMessageTextStyle - : theme.receivedMessageBodyTextStyle, - textWidthBasis: TextWidthBasis.longestLine, + ParsedText( + text: message.text, + selectable: true, + style: bodyTextStyle, + regexOptions: const RegexOptions(multiLine: true, dotAll: true), + parse: [ + MatchText( + pattern: regexEmail, + onTap: (mail) async { + final url = 'mailto:$mail'; + if (await canLaunch(url)) { + await launch(url); + } + }, + style: bodyLinkTextStyle ?? + bodyTextStyle.copyWith(decoration: TextDecoration.underline), + ), + MatchText( + pattern: regexLink, + onTap: (url) async { + if (await canLaunch(url)) { + await launch(url); + } + }, + style: bodyLinkTextStyle ?? + bodyTextStyle.copyWith(decoration: TextDecoration.underline), + ), + MatchText( + pattern: '(\\*\\*|\\*)(.*?)(\\*\\*|\\*)', + onTap: (_) {}, + style: boldTextStyle ?? + bodyTextStyle.copyWith(fontWeight: FontWeight.bold), + renderText: ({required String str, required String pattern}) { + return {'display': str.replaceAll(RegExp('(\\*\\*|\\*)'), '')}; + }, + ), + MatchText( + pattern: '_(.*?)_', + onTap: (_) {}, + style: bodyTextStyle.copyWith(fontStyle: FontStyle.italic), + renderText: ({required String str, required String pattern}) { + return {'display': str.replaceAll('_', '')}; + }, + ), + MatchText( + pattern: '~(.*?)~', + onTap: (_) {}, + style: bodyTextStyle.copyWith( + decoration: TextDecoration.lineThrough, + ), + renderText: ({required String str, required String pattern}) { + return {'display': str.replaceAll('~', '')}; + }, + ), + MatchText( + pattern: '`(.*?)`', + onTap: (_) {}, + style: codeTextStyle ?? + bodyTextStyle.copyWith( + fontFamily: Platform.isIOS ? 'Courier' : 'monospace', + ), + renderText: ({required String str, required String pattern}) { + return {'display': str.replaceAll('`', '')}; + }, + ), + ], ), ], ); diff --git a/pubspec.yaml b/pubspec.yaml index 231112e..f2fd568 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,9 +17,11 @@ dependencies: equatable: ^2.0.3 flutter_chat_types: ^3.3.2 flutter_link_previewer: ^2.6.4 + flutter_parsed_text: ^2.2.1 intl: ^0.17.0 meta: ^1.7.0 photo_view: ^0.13.0 + url_launcher: ^6.0.20 visibility_detector: ^0.2.2 dev_dependencies: