diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..28041566 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "explorer.excludeGitIgnore": true +} \ No newline at end of file diff --git a/README.md b/README.md index 856b856c..39284d7f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ![Banner](https://raw.githubusercontent.com/SimformSolutionsPvtLtd/flutter_chatview/main/preview/banner.png) # ChatView + [![chatview](https://img.shields.io/pub/v/chatview?label=chatview)](https://pub.dev/packages/chatview) A Flutter package that allows you to integrate Chat View with highly customization options such as one on one @@ -25,164 +26,178 @@ For web demo visit [Chat View Example](https://simformsolutionspvtltd.github.io/ - Removed `showTypingIndicator` field from `ChatView` and replaced it with `ChatController.showTypingIndicator`. Before: - ```dart - ChatView( - showTypingIndicator:false, - ), - ``` + + ```dart + ChatView( + showTypingIndicator:false, + ), + ``` After: - ```dart - /// use it with your [ChatController] instance. - _chatContoller.setTypingIndicator = true; // for showing indicator - _chatContoller.setTypingIndicator = false; // for hiding indicator - ``` + + ```dart + /// use it with your [ChatController] instance. + _chatContoller.setTypingIndicator = true; // for showing indicator + _chatContoller.setTypingIndicator = false; // for hiding indicator + ``` - Updated `ChatUser`, `Message` and `ReplyMessage` Data Model's `fromJson` and `toJson` methods: ##### in `ChatUser.fromJson`: Before: - ```dart - ChatUser.fromJson( - { - ... - 'imageType': ImageType.asset, - ... - }, - ), - ``` - - After: - ```dart - ChatUser.fromJson( - { - ... - 'imageType': 'asset', - ... - }, - ), - ``` - - ##### in `ChatUser.toJson`: - Before: - ```dart + ```dart + ChatUser.fromJson( { ... - imageType: ImageType.asset, + 'imageType': ImageType.asset, ... - } - ``` + }, + ), + ``` After: - ```dart + + ```dart + ChatUser.fromJson( { ... - imageType: asset, + 'imageType': 'asset', ... - } - ``` + }, + ), + ``` - ##### in `Message.fromJson`: + ##### in `ChatUser.toJson`: Before: - ```dart - Message.fromJson( - { - ... - 'createdAt': DateTime.now(), - 'message_type': MessageType.text, - 'voice_message_duration': Duration(seconds: 5), - ... - } - ) - ``` + + ```dart + { + ... + imageType: ImageType.asset, + ... + } + ``` After: - ```dart - Message.fromJson( - { - ... - 'createdAt': '2024-06-13T17:32:19.586412', - 'message_type': 'text', - 'voice_message_duration': '5000000', - ... - } - ) - ``` - ##### in `Message.toJson`: + ```dart + { + ... + imageType: asset, + ... + } + ``` + + ##### in `Message.fromJson`: Before: - ```dart + + ```dart + Message.fromJson( { ... - createdAt: 2024-06-13 17:23:19.454789, - message_type: MessageType.text, - voice_message_duration: 0:00:05.000000, + 'createdAt': DateTime.now(), + 'message_type': MessageType.text, + 'voice_message_duration': Duration(seconds: 5), ... } - ``` + ) + ``` After: - ```dart + + ```dart + Message.fromJson( { ... - createdAt: 2024-06-13T17:32:19.586412, - message_type: text, - voice_message_duration: 5000000, + 'createdAt': '2024-06-13T17:32:19.586412', + 'message_type': 'text', + 'voice_message_duration': '5000000', ... } - ``` + ) + ``` - ##### in `ReplyMessage.fromJson`: + ##### in `Message.toJson`: Before: - ```dart - ReplyMessage.fromJson( - { - ... - 'message_type': MessageType.text, - 'voiceMessageDuration': Duration(seconds: 5), - ... - } - ) - ``` + + ```dart + { + ... + createdAt: 2024-06-13 17:23:19.454789, + message_type: MessageType.text, + voice_message_duration: 0:00:05.000000, + ... + } + ``` After: - ```dart - ReplyMessage.fromJson( - { - ... - 'message_type': 'text', - 'voiceMessageDuration': '5000000', - ... - } - ) - ``` - in `ReplyMessage.toJson`: + ```dart + { + ... + createdAt: 2024-06-13T17:32:19.586412, + message_type: text, + voice_message_duration: 5000000, + ... + } + ``` + + ##### in `ReplyMessage.fromJson`: Before: - ```dart + + ```dart + ReplyMessage.fromJson( { ... - message_type: MessageType.text, - voiceMessageDuration: 0:00:05.000000, + 'message_type': MessageType.text, + 'voiceMessageDuration': Duration(seconds: 5), ... } - ``` + ) + ``` After: - ```dart + + ```dart + ReplyMessage.fromJson( { ... - message_type: text, - voiceMessageDuration: 5000000, + 'message_type': 'text', + 'voiceMessageDuration': '5000000', ... } - ``` + ) + ``` + + in `ReplyMessage.toJson`: + + Before: + + ```dart + { + ... + message_type: MessageType.text, + voiceMessageDuration: 0:00:05.000000, + ... + } + ``` + + After: + + ```dart + { + ... + message_type: text, + voiceMessageDuration: 5000000, + ... + } + ``` ## Installing @@ -192,14 +207,17 @@ For web demo visit [Chat View Example](https://simformsolutionspvtltd.github.io/ dependencies: chatview: ``` -*Get the latest version in the 'Installing' tab on [pub.dev](https://pub.dev/packages/chatview)* + +_Get the latest version in the 'Installing' tab on [pub.dev](https://pub.dev/packages/chatview)_ 2. Import the package + ```dart import 'package:chatview/chatview.dart'; ``` 3. Adding a chat controller. + ```dart final chatController = ChatController( initialMessageList: messageList, @@ -210,6 +228,7 @@ final chatController = ChatController( ``` 4. Adding a `ChatView` widget. + ```dart ChatView( chatController: chatController, @@ -219,6 +238,7 @@ ChatView( ``` 5. Adding a messageList with `Message` class. + ```dart List messageList = [ Message( @@ -237,6 +257,7 @@ List messageList = [ ``` 6. Adding a `onSendTap`. + ```dart void onSendTap(String message, ReplyMessage replyMessage, MessageType messageType){ final message = Message( @@ -255,19 +276,20 @@ Note: you can evaluate message type from `messageType` parameter, based on that ## Messages types compability -|Message Types | Android | iOS | MacOS | Web | Linux | Windows | -| :-----: | :-----: | :-: | :---: | :-: | :---: | :-----: | -|Text messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -|Image messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -|Voice messages | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | -|Custom messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | - +| Message Types | Android | iOS | MacOS | Web | Linux | Windows | +| :-------------: | :-----: | :-: | :---: | :-: | :---: | :-----: | +| Text messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Image messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Voice messages | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | +| Custom messages | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ## Platform specific configuration ### For image Picker + #### iOS -* Add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: + +- Add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: ``` NSCameraUsageDescription @@ -279,32 +301,40 @@ Note: you can evaluate message type from `messageType` parameter, based on that ``` ### For voice messages + #### iOS -* Add this two rows in `ios/Runner/Info.plist` + +- Add this two rows in `ios/Runner/Info.plist` + ``` NSMicrophoneUsageDescription This app requires Mic permission. ``` -* This plugin requires ios 10.0 or higher. So add this line in `Podfile` + +- This plugin requires ios 10.0 or higher. So add this line in `Podfile` + ``` platform :ios, '10.0' ``` #### Android -* Change the minimum Android sdk version to 21 (or higher) in your android/app/build.gradle file. + +- Change the minimum Android sdk version to 21 (or higher) in your android/app/build.gradle file. + ``` minSdkVersion 21 ``` -* Add RECORD_AUDIO permission in `AndroidManifest.xml` +- Add RECORD_AUDIO permission in `AndroidManifest.xml` + ``` ``` - ## Some more optional parameters 1. Enable and disable specific features with `FeatureActiveConfig`. + ```dart ChatView( ... @@ -317,6 +347,7 @@ ChatView( ``` 2. Adding an appbar with `ChatViewAppBar`. + ```dart ChatView( ... @@ -333,6 +364,7 @@ ChatView( ``` 3. Adding a message list configuration with `ChatBackgroundConfiguration` class. + ```dart ChatView( ... @@ -345,6 +377,7 @@ ChatView( ``` 4. Adding a send message configuration with `SendMessageConfiguration` class. + ```dart ChatView( ... @@ -359,6 +392,7 @@ ChatView( ``` 5. Adding a chat bubble configuration with `ChatBubbleConfiguration` class. + ```dart ChatView( ... @@ -366,15 +400,15 @@ ChatView( onDoubleTap: (){ // Your code goes here }, - outgoingChatBubbleConfig: ChatBubble( // Sender's message chat bubble + outgoingChatBubble: ChatBubble( // Sender's message chat bubble color: Colors.blue, - borderRadius: const BorderRadius.only( + borderRadius: const BorderRadius.only( topRight: Radius.circular(12), topLeft: Radius.circular(12), bottomLeft: Radius.circular(12), ), ), - inComingChatBubbleConfig: ChatBubble( // Receiver's message chat bubble + incomingChatBubble: ChatBubble( // Receiver's message chat bubble color: Colors.grey.shade200, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), @@ -388,6 +422,7 @@ ChatView( ``` 6. Adding swipe to reply configuration with `SwipeToReplyConfiguration` class. + ```dart ChatView( ... @@ -397,22 +432,23 @@ ChatView( }, onRightSwipe: (message, sentBy){ // Your code goes here - }, + }, ), ... ) ``` 7. Adding messages configuration with `MessageConfiguration` class. + ```dart ChatView( ... messageConfig: MessageConfiguration( - messageReactionConfig: MessageReactionConfiguration(), // Emoji reaction configuration for single message + messageReactionConfig: MessageReactionConfiguration(), // Emoji reaction configuration for single message imageMessageConfig: ImageMessageConfiguration( onTap: (){ // Your code goes here - }, + }, shareIconConfig: ShareIconConfiguration( onPressed: (){ // Your code goes here @@ -425,6 +461,7 @@ ChatView( ``` 8. Adding reaction pop-up configuration with `ReactionPopupConfiguration` class. + ```dart ChatView( ... @@ -444,6 +481,7 @@ ChatView( ``` 9. Adding reply pop-up configuration with `ReplyPopupConfiguration` class. + ```dart ChatView( ... @@ -467,6 +505,7 @@ ChatView( ``` 10. Adding replied message configuration with `RepliedMessageConfiguration` class. + ```dart ChatView( ... @@ -479,12 +518,13 @@ ChatView( ) ``` -11. For customizing typing indicators use `typeIndicatorConfig` with `TypeIndicatorConfig`. +11. For customizing typing indicators use `typingIndicatorConfig` with `TypeIndicatorConfig`. + ```dart ChatView( ... - typeIndicatorConfig: TypeIndicatorConfiguration( + typingIndicatorConfig: TypingIndicatorConfiguration( flashingCircleBrightColor: Colors.grey, flashingCircleDarkColor: Colors.black, ), @@ -492,7 +532,9 @@ ChatView( ) ``` + 12. For showing hiding typeIndicatorwidget use `ChatController.setTypingIndicaor`, for more info see `ChatController`. + ```dart /// use it with your [ChatController] instance. _chatContoller.setTypingIndicator = true; // for showing indicator @@ -500,6 +542,7 @@ _chatContoller.setTypingIndicator = false; // for hiding indicator ``` 13. Adding linkpreview configuration with `LinkPreviewConfiguration` class. + ```dart ChatView( ... @@ -525,6 +568,7 @@ ChatView( ``` 14. Adding pagination. + ```dart ChatView( ... @@ -538,6 +582,7 @@ ChatView( ``` 15. Add image picker configuration. + ```dart ChatView( ... @@ -554,6 +599,7 @@ ChatView( ``` 16. Add `ChatViewState` customisations. + ```dart ChatView( ... @@ -568,6 +614,7 @@ ChatView( ``` 17. Setting auto scroll and highlight config with `RepliedMsgAutoScrollConfig` class. + ```dart ChatView( ... @@ -586,14 +633,14 @@ ChatView( ChatView( ... sendMessageConfig: SendMessageConfiguration( - + textFieldConfig: TextFieldConfiguration( onMessageTyping: (status) { // send composing/composed status to other client // your code goes here - }, + }, + - /// After typing stopped, the threshold time after which the composing /// status to be changed to [TypeWriterStatus.typed]. /// Default is 1 second. @@ -605,7 +652,7 @@ ChatView( ) ``` -19. Passing customReceipts builder or handling stuffs related receipts see `ReceiptsWidgetConfig` in outgoingChatBubbleConfig. +19. Passing customReceipts builder or handling stuffs related receipts see `ReceiptsWidgetConfig` in outgoingChatBubble. ```dart ChatView( @@ -614,28 +661,28 @@ ChatView( /// Controls the visibility of message seen ago receipts default is true lastSeenAgoBuilderVisibility: false, /// Controls the visibility of the message [receiptsBuilder] - receiptsBuilderVisibility: false), + receiptsBuilderVisibility: false), ChatBubbleConfiguration( - inComingChatBubbleConfig: ChatBubble( + incomingChatBubble: ChatBubble( onMessageRead: (message) { /// send your message reciepts to the other client debugPrint('Message Read'); }, ), - outgoingChatBubbleConfig: ChatBubble( + outgoingChatBubble: ChatBubble( receiptsWidgetConfig: ReceiptsWidgetConfig( - /// custom receipts builder + /// custom receipts builder receiptsBuilder: _customReceiptsBuilder, - /// whether to display receipts in all + /// whether to display receipts in all /// message or just at the last one just like instagram showReceiptsIn: ShowReceiptsIn.lastMessage ), - ), - ), - + ), + ), + ... - + ) ``` @@ -686,7 +733,7 @@ ChatView( recentTabBehavior: RecentTabBehavior.NONE, ), ... - + ) ``` @@ -700,7 +747,7 @@ ChatView( voiceRecordingConfiguration: VoiceRecordingConfiguration( iosEncoder: IosEncoder.kAudioFormatMPEG4AAC, androidOutputFormat: AndroidOutputFormat.mpeg4, - androidEncoder: AndroidEncoder.aac, + androidEncoder: AndroidEncoder.aac, bitRate: 128000, sampleRate: 44100, waveStyle: WaveStyle( @@ -709,7 +756,7 @@ ChatView( extendWaveform: true, ), ), - + ... ) ) @@ -728,9 +775,10 @@ ChatView( ... ), ... - + ) ``` + 25. Added flag `isProfilePhotoInBase64` that defines whether provided image is url or base64 data. ```dart @@ -797,6 +845,7 @@ ChatView( ``` 28. Added callback of onTap on list of reacted users in reaction sheet `reactedUserCallback`. + ```dart ChatView( @@ -841,6 +890,7 @@ ChatView( 30. Add default avatar for profile image `defaultAvatarImage`, error builder for asset and network profile image `assetImageErrorBuilder` `networkImageErrorBuilder`, Enum `ImageType` to define image as asset, network or base64 data. + ```dart ChatView( ... @@ -918,7 +968,7 @@ ChatView( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: Text( + child: Text( state.message, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -949,7 +999,8 @@ ChatView( 33. Reply Suggestions functionalities. -* Add reply suggestions +- Add reply suggestions + ```dart _chatController.addReplySuggestions([ SuggestionItemData(text: 'Thanks.'), @@ -957,11 +1008,15 @@ _chatController.addReplySuggestions([ SuggestionItemData(text: 'Great.') ]); ``` -* Remove reply suggestions + +- Remove reply suggestions + ```dart _chatController.removeReplySuggestions(); ``` -* Update suggestions Config + +- Update suggestions Config + ```dart replySuggestionsConfig: ReplySuggestionsConfig( itemConfig: SuggestionItemConfig( @@ -1000,7 +1055,6 @@ ChatView( 35. Use `ScrollToBottomButtonConfig` to change the configuration of scroll to bottom button. - ```dart ChatView( ... @@ -1046,7 +1100,6 @@ Check out [blog](https://medium.com/simform-engineering/chatview-a-cutting-edge- Also, for whole example, check out the **example** app in the [example](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/tree/main/example) directory or the 'Example' tab on pub.dartlang.org for a more complete example. - ## Main Contributors diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion1.0MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 88359b22..279576f3 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 972adf77..20773d59 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -155,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -342,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -361,7 +361,7 @@ DEVELOPMENT_TEAM = KX234MPD84; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -421,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -470,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -490,7 +490,7 @@ DEVELOPMENT_TEAM = KX234MPD84; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -514,7 +514,7 @@ DEVELOPMENT_TEAM = KX234MPD84; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e..e67b2808 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { ), onReloadButtonTap: () {}, ), - typeIndicatorConfig: TypeIndicatorConfiguration( + typingIndicatorConfig: TypingIndicatorConfiguration( flashingCircleBrightColor: theme.flashingCircleBrightColor, flashingCircleDarkColor: theme.flashingCircleDarkColor, ), appBar: ChatViewAppBar( - elevation: theme.elevation, - backGroundColor: theme.appBarColor, + elevation: theme.elevation!, + backgroundColor: theme.appBarColor!, profilePicture: Data.profileImage, backArrowColor: theme.backArrowColor, chatTitle: "Chat view", @@ -135,14 +134,13 @@ class _ChatScreenState extends State { letterSpacing: 0.25, ), userStatus: "online", + centerTitle: false, userStatusTextStyle: const TextStyle(color: Colors.grey), actions: [ IconButton( onPressed: _onThemeIconTap, icon: Icon( - isDarkTheme - ? Icons.brightness_4_outlined - : Icons.dark_mode_outlined, + isDarkTheme ? Icons.brightness_4_outlined : Icons.dark_mode_outlined, color: theme.themeIconColor, ), ), @@ -187,6 +185,8 @@ class _ChatScreenState extends State { textFieldBackgroundColor: theme.textFieldBackgroundColor, closeIconColor: theme.closeIconColor, textFieldConfig: TextFieldConfiguration( + enableEnterToSubmit: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 8), onMessageTyping: (status) { /// Do with status debugPrint(status.toString()); @@ -206,17 +206,16 @@ class _ChatScreenState extends State { ), ), chatBubbleConfig: ChatBubbleConfiguration( - outgoingChatBubbleConfig: ChatBubble( + outgoingChatBubble: ChatBubble( linkPreviewConfig: LinkPreviewConfiguration( backgroundColor: theme.linkPreviewOutgoingChatColor, bodyStyle: theme.outgoingChatLinkBodyStyle, titleStyle: theme.outgoingChatLinkTitleStyle, ), - receiptsWidgetConfig: - const ReceiptsWidgetConfig(showReceiptsIn: ShowReceiptsIn.all), + receiptsWidgetConfig: const ReceiptsWidgetConfig(showReceiptsIn: ShowReceiptsIn.all), color: theme.outgoingChatBubbleColor, ), - inComingChatBubbleConfig: ChatBubble( + incomingChatBubble: ChatBubble( linkPreviewConfig: LinkPreviewConfiguration( linkStyle: TextStyle( color: theme.inComingChatBubbleTextColor, @@ -231,8 +230,7 @@ class _ChatScreenState extends State { /// send your message reciepts to the other client debugPrint('Message Read'); }, - senderNameTextStyle: - TextStyle(color: theme.inComingChatBubbleTextColor), + senderNameTextStyle: TextStyle(color: theme.inComingChatBubbleTextColor), color: theme.inComingChatBubbleColor, ), ), @@ -252,10 +250,8 @@ class _ChatScreenState extends State { messageReactionConfig: MessageReactionConfiguration( backgroundColor: theme.messageReactionBackGroundColor, borderColor: theme.messageReactionBackGroundColor, - reactedUserCountTextStyle: - TextStyle(color: theme.inComingChatBubbleTextColor), - reactionCountTextStyle: - TextStyle(color: theme.inComingChatBubbleTextColor), + reactedUserCountTextStyle: TextStyle(color: theme.inComingChatBubbleTextColor), + reactionCountTextStyle: TextStyle(color: theme.inComingChatBubbleTextColor), reactionsBottomSheetConfig: ReactionsBottomSheetConfiguration( backgroundColor: theme.backgroundColor, reactedUserTextStyle: TextStyle( @@ -316,8 +312,7 @@ class _ChatScreenState extends State { color: isDarkTheme ? Colors.white : Colors.black, ), ), - onTap: (item) => - _onSendTap(item.text, const ReplyMessage(), MessageType.text), + onTap: (item) => _onSendTap(item.text, const ReplyMessage(), MessageType.text), ), ), ); @@ -339,8 +334,7 @@ class _ChatScreenState extends State { ), ); Future.delayed(const Duration(milliseconds: 300), () { - _chatController.initialMessageList.last.setStatus = - MessageStatus.undelivered; + _chatController.initialMessageList.last.setStatus = MessageStatus.undelivered; }); Future.delayed(const Duration(seconds: 1), () { _chatController.initialMessageList.last.setStatus = MessageStatus.read; diff --git a/lib/src/inherited_widgets/configurations_inherited_widgets.dart b/lib/src/inherited_widgets/configurations_inherited_widgets.dart index b9059cc6..df827295 100644 --- a/lib/src/inherited_widgets/configurations_inherited_widgets.dart +++ b/lib/src/inherited_widgets/configurations_inherited_widgets.dart @@ -13,7 +13,7 @@ class ConfigurationsInheritedWidget extends InheritedWidget { this.profileCircleConfig, this.swipeToReplyConfig, this.repliedMessageConfig, - this.typeIndicatorConfig, + this.typingIndicatorConfig, this.replyPopupConfig, this.emojiPickerSheetConfig, this.scrollToBottomButtonConfig, @@ -43,7 +43,7 @@ class ConfigurationsInheritedWidget extends InheritedWidget { final RepliedMessageConfiguration? repliedMessageConfig; /// Provides configuration of typing indicator's appearance. - final TypeIndicatorConfiguration? typeIndicatorConfig; + final TypingIndicatorConfiguration? typingIndicatorConfig; /// Provides configuration for reply snack bar's appearance and options. final ReplyPopupConfiguration? replyPopupConfig; @@ -54,10 +54,9 @@ class ConfigurationsInheritedWidget extends InheritedWidget { /// Provides a configuration for scroll to bottom button config final ScrollToBottomButtonConfig? scrollToBottomButtonConfig; - static ConfigurationsInheritedWidget? of(BuildContext context) => context - .dependOnInheritedWidgetOfExactType(); + static ConfigurationsInheritedWidget? of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType(); @override - bool updateShouldNotify(covariant ConfigurationsInheritedWidget oldWidget) => - oldWidget != this; + bool updateShouldNotify(covariant ConfigurationsInheritedWidget oldWidget) => oldWidget != this; } diff --git a/lib/src/models/config_models/chat_bubble_configuration.dart b/lib/src/models/config_models/chat_bubble_configuration.dart index 09cfcb38..e8047826 100644 --- a/lib/src/models/config_models/chat_bubble_configuration.dart +++ b/lib/src/models/config_models/chat_bubble_configuration.dart @@ -39,10 +39,10 @@ class ChatBubbleConfiguration { final Duration? longPressAnimationDuration; /// Provides configuration of other users message's chat bubble. - final ChatBubble? inComingChatBubbleConfig; + final ChatBubble? incomingChatBubble; /// Provides configuration of current user message's chat bubble. - final ChatBubble? outgoingChatBubbleConfig; + final ChatBubble? outgoingChatBubble; /// Provides callback when user tap twice on chat bubble. final MessageCallBack? onDoubleTap; @@ -63,8 +63,8 @@ class ChatBubbleConfiguration { this.margin, this.maxWidth, this.longPressAnimationDuration, - this.inComingChatBubbleConfig, - this.outgoingChatBubbleConfig, + this.incomingChatBubble, + this.outgoingChatBubble, this.onDoubleTap, this.receiptsWidgetConfig, this.disableLinkPreview = false, diff --git a/lib/src/models/config_models/send_message_configuration.dart b/lib/src/models/config_models/send_message_configuration.dart index 8909d7a3..3ca7db41 100644 --- a/lib/src/models/config_models/send_message_configuration.dart +++ b/lib/src/models/config_models/send_message_configuration.dart @@ -168,6 +168,10 @@ class TextFieldConfiguration { /// Default is [true]. final bool enabled; + /// Determines if pressing enter submits the message. + /// Defaults to [false]. + final bool enableEnterToSubmit; + const TextFieldConfiguration({ this.contentPadding, this.maxLines, @@ -184,6 +188,7 @@ class TextFieldConfiguration { this.inputFormatters, this.textCapitalization, this.enabled = true, + this.enableEnterToSubmit = false, }); } diff --git a/lib/src/models/config_models/type_indicator_configuration.dart b/lib/src/models/config_models/type_indicator_configuration.dart index a8852f0c..e674b3a0 100644 --- a/lib/src/models/config_models/type_indicator_configuration.dart +++ b/lib/src/models/config_models/type_indicator_configuration.dart @@ -21,7 +21,7 @@ */ import 'package:flutter/material.dart'; -class TypeIndicatorConfiguration { +class TypingIndicatorConfiguration { /// Used for giving typing indicator size. final double? indicatorSize; @@ -34,7 +34,7 @@ class TypeIndicatorConfiguration { /// Used to give color of light circle dots. final Color? flashingCircleBrightColor; - const TypeIndicatorConfiguration({ + const TypingIndicatorConfiguration({ this.indicatorSize, this.indicatorSpacing, this.flashingCircleDarkColor, diff --git a/lib/src/widgets/chat_bubble_widget.dart b/lib/src/widgets/chat_bubble_widget.dart index 4c748f96..7f83103b 100644 --- a/lib/src/widgets/chat_bubble_widget.dart +++ b/lib/src/widgets/chat_bubble_widget.dart @@ -69,8 +69,7 @@ class _ChatBubbleWidgetState extends State { bool get isMessageBySender => widget.message.sentBy == currentUser?.id; - bool get isLastMessage => - chatController?.initialMessageList.last.id == widget.message.id; + bool get isLastMessage => chatController?.initialMessageList.last.id == widget.message.id; FeatureActiveConfig? featureActiveConfig; ChatController? chatController; @@ -123,19 +122,16 @@ class _ChatBubbleWidgetState extends State { margin: chatBubbleConfig?.margin ?? const EdgeInsets.only(bottom: 10), child: Row( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - isMessageBySender ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisAlignment: isMessageBySender ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - if (!isMessageBySender && - (featureActiveConfig?.enableOtherUserProfileAvatar ?? true)) + if (!isMessageBySender && (featureActiveConfig?.enableOtherUserProfileAvatar ?? true)) profileCircle(messagedUser), Expanded( child: _messagesWidgetColumn(messagedUser), ), if (isMessageBySender) ...[getReceipt()], - if (isMessageBySender && - (featureActiveConfig?.enableCurrentUserProfileAvatar ?? true)) + if (isMessageBySender && (featureActiveConfig?.enableCurrentUserProfileAvatar ?? true)) profileCircle(messagedUser), ], ), @@ -152,8 +148,7 @@ class _ChatBubbleWidgetState extends State { imageUrl: messagedUser?.profilePhoto, imageType: messagedUser?.imageType, defaultAvatarImage: messagedUser?.defaultAvatarImage ?? profileImage, - networkImageProgressIndicatorBuilder: - messagedUser?.networkImageProgressIndicatorBuilder, + networkImageProgressIndicatorBuilder: messagedUser?.networkImageProgressIndicatorBuilder, assetImageErrorBuilder: messagedUser?.assetImageErrorBuilder, networkImageErrorBuilder: messagedUser?.networkImageErrorBuilder, circleRadius: profileCircleConfig?.circleRadius, @@ -164,49 +159,39 @@ class _ChatBubbleWidgetState extends State { void onRightSwipe() { if (maxDuration != null) { - widget.message.voiceMessageDuration = - Duration(milliseconds: maxDuration!); + widget.message.voiceMessageDuration = Duration(milliseconds: maxDuration!); } if (chatListConfig.swipeToReplyConfig?.onRightSwipe != null) { - chatListConfig.swipeToReplyConfig?.onRightSwipe!( - widget.message.message, widget.message.sentBy); + chatListConfig.swipeToReplyConfig?.onRightSwipe!(widget.message.message, widget.message.sentBy); } widget.onSwipe(widget.message); } void onLeftSwipe() { if (maxDuration != null) { - widget.message.voiceMessageDuration = - Duration(milliseconds: maxDuration!); + widget.message.voiceMessageDuration = Duration(milliseconds: maxDuration!); } if (chatListConfig.swipeToReplyConfig?.onLeftSwipe != null) { - chatListConfig.swipeToReplyConfig?.onLeftSwipe!( - widget.message.message, widget.message.sentBy); + chatListConfig.swipeToReplyConfig?.onLeftSwipe!(widget.message.message, widget.message.sentBy); } widget.onSwipe(widget.message); } void _onAvatarTap(ChatUser? user) { - if (chatListConfig.profileCircleConfig?.onAvatarTap != null && - user != null) { + if (chatListConfig.profileCircleConfig?.onAvatarTap != null && user != null) { chatListConfig.profileCircleConfig?.onAvatarTap!(user); } } Widget getReceipt() { - final showReceipts = chatListConfig.chatBubbleConfig - ?.outgoingChatBubbleConfig?.receiptsWidgetConfig?.showReceiptsIn ?? + final showReceipts = chatListConfig.chatBubbleConfig?.outgoingChatBubble?.receiptsWidgetConfig?.showReceiptsIn ?? ShowReceiptsIn.lastMessage; if (showReceipts == ShowReceiptsIn.all) { return ValueListenableBuilder( valueListenable: widget.message.statusNotifier, builder: (context, value, child) { - if (ChatViewInheritedWidget.of(context) - ?.featureActiveConfig - .receiptsBuilderVisibility ?? - true) { - return chatListConfig.chatBubbleConfig?.outgoingChatBubbleConfig - ?.receiptsWidgetConfig?.receiptsBuilder + if (ChatViewInheritedWidget.of(context)?.featureActiveConfig.receiptsBuilderVisibility ?? true) { + return chatListConfig.chatBubbleConfig?.outgoingChatBubble?.receiptsWidgetConfig?.receiptsBuilder ?.call(value) ?? sendMessageAnimationBuilder(value); } @@ -215,15 +200,10 @@ class _ChatBubbleWidgetState extends State { ); } else if (showReceipts == ShowReceiptsIn.lastMessage && isLastMessage) { return ValueListenableBuilder( - valueListenable: - chatController!.initialMessageList.last.statusNotifier, + valueListenable: chatController!.initialMessageList.last.statusNotifier, builder: (context, value, child) { - if (ChatViewInheritedWidget.of(context) - ?.featureActiveConfig - .receiptsBuilderVisibility ?? - true) { - return chatListConfig.chatBubbleConfig?.outgoingChatBubbleConfig - ?.receiptsWidgetConfig?.receiptsBuilder + if (ChatViewInheritedWidget.of(context)?.featureActiveConfig.receiptsBuilderVisibility ?? true) { + return chatListConfig.chatBubbleConfig?.outgoingChatBubble?.receiptsWidgetConfig?.receiptsBuilder ?.call(value) ?? sendMessageAnimationBuilder(value); } @@ -234,59 +214,48 @@ class _ChatBubbleWidgetState extends State { } void _onAvatarLongPress(ChatUser? user) { - if (chatListConfig.profileCircleConfig?.onAvatarLongPress != null && - user != null) { + if (chatListConfig.profileCircleConfig?.onAvatarLongPress != null && user != null) { chatListConfig.profileCircleConfig?.onAvatarLongPress!(user); } } Widget _messagesWidgetColumn(ChatUser? messagedUser) { return Column( - crossAxisAlignment: - isMessageBySender ? CrossAxisAlignment.end : CrossAxisAlignment.start, + crossAxisAlignment: isMessageBySender ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ if ((chatController?.otherUsers.isNotEmpty ?? false) && !isMessageBySender && (featureActiveConfig?.enableOtherUserName ?? true)) Padding( - padding: chatListConfig - .chatBubbleConfig?.inComingChatBubbleConfig?.padding ?? + padding: chatListConfig.chatBubbleConfig?.incomingChatBubble?.padding ?? const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Text( messagedUser?.name ?? '', - style: chatListConfig.chatBubbleConfig?.inComingChatBubbleConfig - ?.senderNameTextStyle, + style: chatListConfig.chatBubbleConfig?.incomingChatBubble?.senderNameTextStyle, ), ), if (replyMessage.isNotEmpty) - chatListConfig.repliedMessageConfig?.repliedMessageWidgetBuilder != - null - ? chatListConfig.repliedMessageConfig! - .repliedMessageWidgetBuilder!(widget.message.replyMessage) + chatListConfig.repliedMessageConfig?.repliedMessageWidgetBuilder != null + ? chatListConfig.repliedMessageConfig!.repliedMessageWidgetBuilder!(widget.message.replyMessage) : ReplyMessageWidget( message: widget.message, repliedMessageConfig: chatListConfig.repliedMessageConfig, - onTap: () => widget.onReplyTap - ?.call(widget.message.replyMessage.messageId), + onTap: () => widget.onReplyTap?.call(widget.message.replyMessage.messageId), ), SwipeToReply( isMessageByCurrentUser: isMessageBySender, onSwipe: isMessageBySender ? onLeftSwipe : onRightSwipe, child: MessageView( - outgoingChatBubbleConfig: - chatListConfig.chatBubbleConfig?.outgoingChatBubbleConfig, - isLongPressEnable: - (featureActiveConfig?.enableReactionPopup ?? true) || - (featureActiveConfig?.enableReplySnackBar ?? true), - inComingChatBubbleConfig: - chatListConfig.chatBubbleConfig?.inComingChatBubbleConfig, + outgoingChatBubble: chatListConfig.chatBubbleConfig?.outgoingChatBubble, + isLongPressEnable: (featureActiveConfig?.enableReactionPopup ?? true) || + (featureActiveConfig?.enableReplySnackBar ?? true), + incomingChatBubble: chatListConfig.chatBubbleConfig?.incomingChatBubble, message: widget.message, isMessageBySender: isMessageBySender, messageConfig: chatListConfig.messageConfig, onLongPress: widget.onLongPress, chatBubbleMaxWidth: chatListConfig.chatBubbleConfig?.maxWidth, - longPressAnimationDuration: - chatListConfig.chatBubbleConfig?.longPressAnimationDuration, + longPressAnimationDuration: chatListConfig.chatBubbleConfig?.longPressAnimationDuration, onDoubleTap: featureActiveConfig?.enableDoubleTapToLike ?? false ? chatListConfig.chatBubbleConfig?.onDoubleTap ?? (message) => currentUser != null @@ -299,12 +268,9 @@ class _ChatBubbleWidgetState extends State { : null, shouldHighlight: widget.shouldHighlight, controller: chatController, - highlightColor: chatListConfig.repliedMessageConfig - ?.repliedMsgAutoScrollConfig.highlightColor ?? - Colors.grey, - highlightScale: chatListConfig.repliedMessageConfig - ?.repliedMsgAutoScrollConfig.highlightScale ?? - 1.1, + highlightColor: + chatListConfig.repliedMessageConfig?.repliedMsgAutoScrollConfig.highlightColor ?? Colors.grey, + highlightScale: chatListConfig.repliedMessageConfig?.repliedMsgAutoScrollConfig.highlightScale ?? 1.1, onMaxDuration: _onMaxDuration, ), ), diff --git a/lib/src/widgets/chat_groupedlist_widget.dart b/lib/src/widgets/chat_groupedlist_widget.dart index 9ba60f78..ebc74d04 100644 --- a/lib/src/widgets/chat_groupedlist_widget.dart +++ b/lib/src/widgets/chat_groupedlist_widget.dart @@ -66,8 +66,7 @@ class ChatGroupedListWidget extends StatefulWidget { State createState() => _ChatGroupedListWidgetState(); } -class _ChatGroupedListWidgetState extends State - with TickerProviderStateMixin { +class _ChatGroupedListWidgetState extends State with TickerProviderStateMixin { bool get showPopUp => widget.showPopUp; bool highlightMessage = false; @@ -82,8 +81,7 @@ class _ChatGroupedListWidgetState extends State bool get isEnableSwipeToSeeTime => widget.isEnableSwipeToSeeTime; - ChatBackgroundConfiguration get chatBackgroundConfig => - chatListConfig.chatBackgroundConfig; + ChatBackgroundConfiguration get chatBackgroundConfig => chatListConfig.chatBackgroundConfig; double chatTextFieldHeight = 0; @@ -104,8 +102,7 @@ class _ChatGroupedListWidgetState extends State WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; setState(() { - chatTextFieldHeight = - chatViewIW?.chatTextFieldViewKey.currentContext?.size?.height ?? 10; + chatTextFieldHeight = chatViewIW?.chatTextFieldViewKey.currentContext?.size?.height ?? 10; }); }); } @@ -142,8 +139,7 @@ class _ChatGroupedListWidgetState extends State @override Widget build(BuildContext context) { - final suggestionsListConfig = - suggestionsConfig?.listConfig ?? const SuggestionListConfig(); + final suggestionsListConfig = suggestionsConfig?.listConfig ?? const SuggestionListConfig(); return SingleChildScrollView( reverse: true, // When reaction popup is being appeared at that user should not scroll. @@ -154,13 +150,9 @@ class _ChatGroupedListWidgetState extends State children: [ GestureDetector( onHorizontalDragUpdate: (details) => - isEnableSwipeToSeeTime && !showPopUp - ? _onHorizontalDrag(details) - : null, + isEnableSwipeToSeeTime && !showPopUp ? _onHorizontalDrag(details) : null, onHorizontalDragEnd: (details) => - isEnableSwipeToSeeTime && !showPopUp - ? _animationController?.reverse() - : null, + isEnableSwipeToSeeTime && !showPopUp ? _animationController?.reverse() : null, onTap: widget.onChatListTap, child: _animationController != null ? AnimatedBuilder( @@ -175,9 +167,8 @@ class _ChatGroupedListWidgetState extends State ValueListenableBuilder( valueListenable: chatController!.typingIndicatorNotifier, builder: (context, value, child) => TypingIndicator( - typeIndicatorConfig: chatListConfig.typeIndicatorConfig, - chatBubbleConfig: - chatListConfig.chatBubbleConfig?.inComingChatBubbleConfig, + typingIndicatorConfig: chatListConfig.typingIndicatorConfig, + chatBubbleConfig: chatListConfig.chatBubbleConfig?.incomingChatBubble, showIndicator: value, ), ), @@ -202,18 +193,15 @@ class _ChatGroupedListWidgetState extends State Future _onReplyTap(String id, List? messages) async { // Finds the replied message if exists final repliedMessages = messages?.firstWhere((message) => id == message.id); - final repliedMsgAutoScrollConfig = - chatListConfig.repliedMessageConfig?.repliedMsgAutoScrollConfig; - final highlightDuration = repliedMsgAutoScrollConfig?.highlightDuration ?? - const Duration(milliseconds: 300); + final repliedMsgAutoScrollConfig = chatListConfig.repliedMessageConfig?.repliedMsgAutoScrollConfig; + final highlightDuration = repliedMsgAutoScrollConfig?.highlightDuration ?? const Duration(milliseconds: 300); // Scrolls to replied message and highlights if (repliedMessages != null && repliedMessages.key.currentState != null) { await Scrollable.ensureVisible( repliedMessages.key.currentState!.context, // This value will make widget to be in center when auto scrolled. alignment: 0.5, - curve: - repliedMsgAutoScrollConfig?.highlightScrollCurve ?? Curves.easeIn, + curve: repliedMsgAutoScrollConfig?.highlightScrollCurve ?? Curves.easeIn, duration: highlightDuration, ); if (repliedMsgAutoScrollConfig?.enableHighlightRepliedMsg ?? false) { @@ -238,9 +226,7 @@ class _ChatGroupedListWidgetState extends State ), ); - details.delta.dx > 1 - ? _animationController?.reverse() - : _animationController?.forward(); + details.delta.dx > 1 ? _animationController?.reverse() : _animationController?.forward(); } @override @@ -257,16 +243,12 @@ class _ChatGroupedListWidgetState extends State builder: (context, snapshot) { if (!snapshot.connectionState.isActive) { return Center( - child: chatBackgroundConfig.loadingWidget ?? - const CircularProgressIndicator(), + child: chatBackgroundConfig.loadingWidget ?? const CircularProgressIndicator(), ); } else { - final messages = chatBackgroundConfig.sortEnable - ? sortMessage(snapshot.data!) - : snapshot.data!; + final messages = chatBackgroundConfig.sortEnable ? sortMessage(snapshot.data!) : snapshot.data!; - final enableSeparator = - featureActiveConfig?.enableChatSeparator ?? false; + final enableSeparator = featureActiveConfig?.enableChatSeparator ?? false; Map messageSeparator = {}; @@ -287,9 +269,7 @@ class _ChatGroupedListWidgetState extends State physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, shrinkWrap: true, - itemCount: (enableSeparator - ? messages.length + messageSeparator.length - : messages.length), + itemCount: (enableSeparator ? messages.length + messageSeparator.length : messages.length), itemBuilder: (context, index) { /// By removing [count] from [index] will get actual index /// to display message in chat @@ -309,26 +289,20 @@ class _ChatGroupedListWidgetState extends State valueListenable: _replyId, builder: (context, state, child) { final message = messages[newIndex]; - final enableScrollToRepliedMsg = chatListConfig - .repliedMessageConfig - ?.repliedMsgAutoScrollConfig - .enableScrollToRepliedMsg ?? - false; + final enableScrollToRepliedMsg = + chatListConfig.repliedMessageConfig?.repliedMsgAutoScrollConfig.enableScrollToRepliedMsg ?? false; return ChatBubbleWidget( key: message.key, message: message, slideAnimation: _slideAnimation, - onLongPress: (yCoordinate, xCoordinate) => - widget.onChatBubbleLongPress( + onLongPress: (yCoordinate, xCoordinate) => widget.onChatBubbleLongPress( yCoordinate, xCoordinate, message, ), onSwipe: widget.assignReplyMessage, shouldHighlight: state == message.id, - onReplyTap: enableScrollToRepliedMsg - ? (replyId) => _onReplyTap(replyId, snapshot.data) - : null, + onReplyTap: enableScrollToRepliedMsg ? (replyId) => _onReplyTap(replyId, snapshot.data) : null, ); }, ); @@ -342,8 +316,7 @@ class _ChatGroupedListWidgetState extends State List sortMessage(List messages) { final elements = [...messages]; elements.sort( - chatBackgroundConfig.messageSorter ?? - (a, b) => a.createdAt.compareTo(b.createdAt), + chatBackgroundConfig.messageSorter ?? (a, b) => a.createdAt.compareTo(b.createdAt), ); if (chatBackgroundConfig.groupedListOrder.isAsc) { return elements.toList(); @@ -362,8 +335,7 @@ class _ChatGroupedListWidgetState extends State /// When the conversation starts on a new date, /// we are returning new date [message.createdAt]. - return lastMatchedDate.getDateFromDateTime == - message.createdAt.getDateFromDateTime + return lastMatchedDate.getDateFromDateTime == message.createdAt.getDateFromDateTime ? lastMatchedDate : message.createdAt; } @@ -372,8 +344,7 @@ class _ChatGroupedListWidgetState extends State return featureActiveConfig?.enableChatSeparator ?? false ? _GroupSeparatorBuilder( separator: createdAt, - defaultGroupSeparatorConfig: - chatBackgroundConfig.defaultGroupSeparatorConfig, + defaultGroupSeparatorConfig: chatBackgroundConfig.defaultGroupSeparatorConfig, groupSeparatorBuilder: chatBackgroundConfig.groupSeparatorBuilder, ) : const SizedBox.shrink(); diff --git a/lib/src/widgets/chat_view.dart b/lib/src/widgets/chat_view.dart index bde26517..b2f3be93 100644 --- a/lib/src/widgets/chat_view.dart +++ b/lib/src/widgets/chat_view.dart @@ -52,7 +52,7 @@ class ChatView extends StatefulWidget { this.isLastPage, this.appBar, ChatBackgroundConfiguration? chatBackgroundConfig, - this.typeIndicatorConfig, + this.typingIndicatorConfig, this.sendMessageBuilder, this.sendMessageConfig, this.onChatListTap, @@ -63,10 +63,8 @@ class ChatView extends StatefulWidget { this.replyMessageBuilder, this.replySuggestionsConfig, this.scrollToBottomButtonConfig, - }) : chatBackgroundConfig = - chatBackgroundConfig ?? const ChatBackgroundConfiguration(), - chatViewStateConfig = - chatViewStateConfig ?? const ChatViewStateConfiguration(), + }) : chatBackgroundConfig = chatBackgroundConfig ?? const ChatBackgroundConfiguration(), + chatViewStateConfig = chatViewStateConfig ?? const ChatViewStateConfiguration(), super(key: key); /// Provides configuration related to user profile circle avatar. @@ -115,7 +113,7 @@ class ChatView extends StatefulWidget { final ReplyMessageWithReturnWidget? sendMessageBuilder; /// Allow user to giving customisation typing indicator. - final TypeIndicatorConfiguration? typeIndicatorConfig; + final TypingIndicatorConfiguration? typingIndicatorConfig; /// Provides controller for accessing few function for running chat. final ChatController chatController; @@ -161,21 +159,17 @@ class ChatView extends StatefulWidget { State createState() => _ChatViewState(); } -class _ChatViewState extends State - with SingleTickerProviderStateMixin { +class _ChatViewState extends State with SingleTickerProviderStateMixin { final GlobalKey _sendMessageKey = GlobalKey(); - ValueNotifier replyMessage = - ValueNotifier(const ReplyMessage()); + ValueNotifier replyMessage = ValueNotifier(const ReplyMessage()); ChatController get chatController => widget.chatController; - ChatBackgroundConfiguration get chatBackgroundConfig => - widget.chatBackgroundConfig; + ChatBackgroundConfiguration get chatBackgroundConfig => widget.chatBackgroundConfig; ChatViewState get chatViewState => widget.chatViewState; - ChatViewStateConfiguration? get chatViewStateConfig => - widget.chatViewStateConfig; + ChatViewStateConfiguration? get chatViewStateConfig => widget.chatViewStateConfig; FeatureActiveConfig get featureActiveConfig => widget.featureActiveConfig; @@ -191,8 +185,7 @@ class _ChatViewState extends State @override Widget build(BuildContext context) { // Scroll to last message on in hasMessages state. - if (widget.chatController.showTypingIndicator && - chatViewState.hasMessages) { + if (widget.chatController.showTypingIndicator && chatViewState.hasMessages) { chatController.scrollToLastMessage(); } return ChatViewInheritedWidget( @@ -206,7 +199,7 @@ class _ChatViewState extends State return ConfigurationsInheritedWidget( chatBackgroundConfig: widget.chatBackgroundConfig, reactionPopupConfig: widget.reactionPopupConfig, - typeIndicatorConfig: widget.typeIndicatorConfig, + typingIndicatorConfig: widget.typingIndicatorConfig, chatBubbleConfig: widget.chatBubbleConfig, replyPopupConfig: widget.replyPopupConfig, messageConfig: widget.messageConfig, @@ -218,17 +211,14 @@ class _ChatViewState extends State child: Stack( children: [ Container( - height: chatBackgroundConfig.height ?? - MediaQuery.of(context).size.height, - width: chatBackgroundConfig.width ?? - MediaQuery.of(context).size.width, + height: chatBackgroundConfig.height ?? MediaQuery.of(context).size.height, + width: chatBackgroundConfig.width ?? MediaQuery.of(context).size.width, decoration: BoxDecoration( color: chatBackgroundConfig.backgroundColor ?? Colors.white, image: chatBackgroundConfig.backgroundImage != null ? DecorationImage( fit: BoxFit.fill, - image: NetworkImage( - chatBackgroundConfig.backgroundImage!), + image: NetworkImage(chatBackgroundConfig.backgroundImage!), ) : null, ), @@ -242,25 +232,20 @@ class _ChatViewState extends State children: [ if (chatViewState.isLoading) ChatViewStateWidget( - chatViewStateWidgetConfig: - chatViewStateConfig?.loadingWidgetConfig, + chatViewStateWidgetConfig: chatViewStateConfig?.loadingWidgetConfig, chatViewState: chatViewState, ) else if (chatViewState.noMessages) ChatViewStateWidget( - chatViewStateWidgetConfig: - chatViewStateConfig?.noMessageWidgetConfig, + chatViewStateWidgetConfig: chatViewStateConfig?.noMessageWidgetConfig, chatViewState: chatViewState, - onReloadButtonTap: - chatViewStateConfig?.onReloadButtonTap, + onReloadButtonTap: chatViewStateConfig?.onReloadButtonTap, ) else if (chatViewState.isError) ChatViewStateWidget( - chatViewStateWidgetConfig: - chatViewStateConfig?.errorWidgetConfig, + chatViewStateWidgetConfig: chatViewStateConfig?.errorWidgetConfig, chatViewState: chatViewState, - onReloadButtonTap: - chatViewStateConfig?.onReloadButtonTap, + onReloadButtonTap: chatViewStateConfig?.onReloadButtonTap, ) else if (chatViewState.hasMessages) ValueListenableBuilder( @@ -274,8 +259,7 @@ class _ChatViewState extends State loadingWidget: widget.loadingWidget, onChatListTap: widget.onChatListTap, assignReplyMessage: (message) => - _sendMessageKey.currentState - ?.assignReplyMessage(message), + _sendMessageKey.currentState?.assignReplyMessage(message), ); }, ), @@ -284,20 +268,14 @@ class _ChatViewState extends State key: _sendMessageKey, sendMessageBuilder: widget.sendMessageBuilder, sendMessageConfig: widget.sendMessageConfig, - onSendTap: - (message, replyMessage, messageType) { - if (context.suggestionsConfig - ?.autoDismissOnSelection ?? - true) { + onSendTap: (message, replyMessage, messageType) { + if (context.suggestionsConfig?.autoDismissOnSelection ?? true) { chatController.removeReplySuggestions(); } - _onSendTap( - message, replyMessage, messageType); + _onSendTap(message, replyMessage, messageType); }, - onReplyCallback: (reply) => - replyMessage.value = reply, - onReplyCloseCallback: () => - replyMessage.value = const ReplyMessage(), + onReplyCallback: (reply) => replyMessage.value = reply, + onReplyCloseCallback: () => replyMessage.value = const ReplyMessage(), messageConfig: widget.messageConfig, replyMessageBuilder: widget.replyMessageBuilder, ), diff --git a/lib/src/widgets/chat_view_appbar.dart b/lib/src/widgets/chat_view_appbar.dart index 8da6f4d2..0e9e8a90 100644 --- a/lib/src/widgets/chat_view_appbar.dart +++ b/lib/src/widgets/chat_view_appbar.dart @@ -19,9 +19,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import 'dart:io' if (kIsWeb) 'dart:html'; -import 'package:flutter/foundation.dart' show kIsWeb; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../chatview.dart'; @@ -32,14 +33,16 @@ class ChatViewAppBar extends StatelessWidget { const ChatViewAppBar({ Key? key, required this.chatTitle, - this.backGroundColor, + @Deprecated('Use backgroundColor instead') this.backGroundColor = Colors.white, + this.backgroundColor, + this.centerTitle = false, this.userStatus, this.profilePicture, this.chatTitleTextStyle, this.userStatusTextStyle, this.backArrowColor, this.actions, - this.elevation, + this.elevation = 1.0, this.onBackPress, this.padding, this.leading, @@ -51,8 +54,15 @@ class ChatViewAppBar extends StatelessWidget { this.networkImageProgressIndicatorBuilder, }) : super(key: key); + /// [Deprecated] use [backgroundColor] instead, this will be removed in next versions + @Deprecated('Use backgroundColor instead. This will be removed in future versions.') + final Color backGroundColor; + /// Allow user to change colour of appbar. - final Color? backGroundColor; + final Color? backgroundColor; + + /// Allow user to center title of appbar. + final bool centerTitle; /// Allow user to change title of appbar. final String chatTitle; @@ -76,7 +86,7 @@ class ChatViewAppBar extends StatelessWidget { final List? actions; /// Allow user to change elevation of appbar. - final double? elevation; + final double elevation; /// Provides callback when user tap on back arrow. final VoidCallBack? onBackPress; @@ -103,20 +113,19 @@ class ChatViewAppBar extends StatelessWidget { final ImageType imageType; /// Progress indicator builder for network image - final NetworkImageProgressIndicatorBuilder? - networkImageProgressIndicatorBuilder; + final NetworkImageProgressIndicatorBuilder? networkImageProgressIndicatorBuilder; @override Widget build(BuildContext context) { return Material( - elevation: elevation ?? 1, + elevation: elevation, child: Container( padding: padding ?? EdgeInsets.only( top: MediaQuery.of(context).padding.top, bottom: 4, ), - color: backGroundColor ?? Colors.white, + color: backgroundColor ?? backGroundColor, child: Row( children: [ if (showLeading) @@ -124,9 +133,7 @@ class ChatViewAppBar extends StatelessWidget { IconButton( onPressed: onBackPress ?? () => Navigator.pop(context), icon: Icon( - (!kIsWeb && Platform.isIOS) - ? Icons.arrow_back_ios - : Icons.arrow_back, + (!kIsWeb && Platform.isIOS) ? Icons.arrow_back_ios : Icons.arrow_back, color: backArrowColor, ), ), @@ -142,28 +149,30 @@ class ChatViewAppBar extends StatelessWidget { assetImageErrorBuilder: assetImageErrorBuilder, networkImageErrorBuilder: networkImageErrorBuilder, imageType: imageType, - networkImageProgressIndicatorBuilder: - networkImageProgressIndicatorBuilder, + networkImageProgressIndicatorBuilder: networkImageProgressIndicatorBuilder, ), ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - chatTitle, - style: chatTitleTextStyle ?? - const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - letterSpacing: 0.25, - ), - ), - if (userStatus != null) + Expanded( + child: Column( + crossAxisAlignment: centerTitle ? CrossAxisAlignment.center : CrossAxisAlignment.start, + mainAxisAlignment: centerTitle ? MainAxisAlignment.center : MainAxisAlignment.start, + children: [ Text( - userStatus!, - style: userStatusTextStyle, + chatTitle, + style: chatTitleTextStyle ?? + const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + letterSpacing: 0.25, + ), ), - ], + if (userStatus != null) + Text( + userStatus!, + style: userStatusTextStyle, + ), + ], + ), ), ], ), diff --git a/lib/src/widgets/chatui_textfield.dart b/lib/src/widgets/chatui_textfield.dart index ec6cf0bc..871f96ba 100644 --- a/lib/src/widgets/chatui_textfield.dart +++ b/lib/src/widgets/chatui_textfield.dart @@ -76,35 +76,28 @@ class _ChatUITextFieldState extends State { SendMessageConfiguration? get sendMessageConfig => widget.sendMessageConfig; - VoiceRecordingConfiguration? get voiceRecordingConfig => - widget.sendMessageConfig?.voiceRecordingConfiguration; + VoiceRecordingConfiguration? get voiceRecordingConfig => widget.sendMessageConfig?.voiceRecordingConfiguration; - ImagePickerIconsConfiguration? get imagePickerIconsConfig => - sendMessageConfig?.imagePickerIconsConfig; + ImagePickerIconsConfiguration? get imagePickerIconsConfig => sendMessageConfig?.imagePickerIconsConfig; - TextFieldConfiguration? get textFieldConfig => - sendMessageConfig?.textFieldConfig; + TextFieldConfiguration? get textFieldConfig => sendMessageConfig?.textFieldConfig; - CancelRecordConfiguration? get cancelRecordConfiguration => - sendMessageConfig?.cancelRecordConfiguration; + CancelRecordConfiguration? get cancelRecordConfiguration => sendMessageConfig?.cancelRecordConfiguration; OutlineInputBorder get _outLineBorder => OutlineInputBorder( borderSide: const BorderSide(color: Colors.transparent), - borderRadius: widget.sendMessageConfig?.textFieldConfig?.borderRadius ?? - BorderRadius.circular(textFieldBorderRadius), + borderRadius: + widget.sendMessageConfig?.textFieldConfig?.borderRadius ?? BorderRadius.circular(textFieldBorderRadius), ); - ValueNotifier composingStatus = - ValueNotifier(TypeWriterStatus.typed); + ValueNotifier composingStatus = ValueNotifier(TypeWriterStatus.typed); late Debouncer debouncer; @override void initState() { attachListeners(); - debouncer = Debouncer( - sendMessageConfig?.textFieldConfig?.compositionThresholdTime ?? - const Duration(seconds: 1)); + debouncer = Debouncer(sendMessageConfig?.textFieldConfig?.compositionThresholdTime ?? const Duration(seconds: 1)); super.initState(); if (!kIsWeb && (Platform.isIOS || Platform.isAndroid)) { @@ -123,8 +116,7 @@ class _ChatUITextFieldState extends State { void attachListeners() { composingStatus.addListener(() { - widget.sendMessageConfig?.textFieldConfig?.onMessageTyping - ?.call(composingStatus.value); + widget.sendMessageConfig?.textFieldConfig?.onMessageTyping?.call(composingStatus.value); }); } @@ -132,12 +124,10 @@ class _ChatUITextFieldState extends State { Widget build(BuildContext context) { final outlineBorder = _outLineBorder; return Container( - padding: - textFieldConfig?.padding ?? const EdgeInsets.symmetric(horizontal: 6), - margin: textFieldConfig?.margin, + padding: textFieldConfig?.padding ?? const EdgeInsets.symmetric(horizontal: 6), + margin: textFieldConfig?.margin ?? const EdgeInsets.symmetric(horizontal: 6), decoration: BoxDecoration( - borderRadius: textFieldConfig?.borderRadius ?? - BorderRadius.circular(textFieldBorderRadius), + borderRadius: textFieldConfig?.borderRadius ?? BorderRadius.circular(textFieldBorderRadius), color: sendMessageConfig?.textFieldBackgroundColor ?? Colors.white, ), child: ValueListenableBuilder( @@ -164,9 +154,7 @@ class _ChatUITextFieldState extends State { WaveStyle( extendWaveform: true, showMiddleLine: false, - waveColor: - voiceRecordingConfig?.waveStyle?.waveColor ?? - Colors.black, + waveColor: voiceRecordingConfig?.waveStyle?.waveColor ?? Colors.black, ), ), ) @@ -175,21 +163,21 @@ class _ChatUITextFieldState extends State { child: TextField( focusNode: widget.focusNode, controller: widget.textEditingController, - style: textFieldConfig?.textStyle ?? - const TextStyle(color: Colors.white), + style: textFieldConfig?.textStyle ?? const TextStyle(color: Colors.white), maxLines: textFieldConfig?.maxLines ?? 5, minLines: textFieldConfig?.minLines ?? 1, keyboardType: textFieldConfig?.textInputType, inputFormatters: textFieldConfig?.inputFormatters, onChanged: _onChanged, - enabled: textFieldConfig?.enabled, - textCapitalization: textFieldConfig?.textCapitalization ?? - TextCapitalization.sentences, + enabled: textFieldConfig?.enabled ?? true, + textCapitalization: textFieldConfig?.textCapitalization ?? TextCapitalization.sentences, + onSubmitted: (textFieldConfig?.enableEnterToSubmit ?? false) ? _onSubmit : null, + textInputAction: (textFieldConfig?.enableEnterToSubmit ?? false) + ? TextInputAction.send + : TextInputAction.newline, decoration: InputDecoration( - hintText: - textFieldConfig?.hintText ?? PackageStrings.message, - fillColor: sendMessageConfig?.textFieldBackgroundColor ?? - Colors.white, + hintText: textFieldConfig?.hintText ?? PackageStrings.message, + fillColor: sendMessageConfig?.textFieldBackgroundColor ?? Colors.white, filled: true, hintStyle: textFieldConfig?.hintStyle ?? TextStyle( @@ -198,8 +186,7 @@ class _ChatUITextFieldState extends State { color: Colors.grey.shade600, letterSpacing: 0.25, ), - contentPadding: textFieldConfig?.contentPadding ?? - const EdgeInsets.symmetric(horizontal: 6), + contentPadding: textFieldConfig?.contentPadding ?? const EdgeInsets.symmetric(horizontal: 6), border: outlineBorder, focusedBorder: outlineBorder, enabledBorder: outlineBorder, @@ -212,57 +199,47 @@ class _ChatUITextFieldState extends State { builder: (_, inputTextValue, child) { if (inputTextValue.isNotEmpty) { return IconButton( - color: sendMessageConfig?.defaultSendButtonColor ?? - Colors.green, + color: sendMessageConfig?.defaultSendButtonColor ?? Colors.green, onPressed: (textFieldConfig?.enabled ?? true) ? () { widget.onPressed(); _inputText.value = ''; } : null, - icon: sendMessageConfig?.sendButtonIcon ?? - const Icon(Icons.send), + icon: sendMessageConfig?.sendButtonIcon ?? const Icon(Icons.send), ); } else { return Row( children: [ if (!isRecordingValue) ...[ - if (sendMessageConfig?.enableCameraImagePicker ?? - true) + if (sendMessageConfig?.enableCameraImagePicker ?? true) IconButton( constraints: const BoxConstraints(), onPressed: (textFieldConfig?.enabled ?? true) ? () => _onIconPressed( ImageSource.camera, - config: sendMessageConfig - ?.imagePickerConfiguration, + config: sendMessageConfig?.imagePickerConfiguration, ) : null, - icon: imagePickerIconsConfig - ?.cameraImagePickerIcon ?? + icon: imagePickerIconsConfig?.cameraImagePickerIcon ?? Icon( Icons.camera_alt_outlined, - color: - imagePickerIconsConfig?.cameraIconColor, + color: imagePickerIconsConfig?.cameraIconColor, ), ), - if (sendMessageConfig?.enableGalleryImagePicker ?? - true) + if (sendMessageConfig?.enableGalleryImagePicker ?? true) IconButton( constraints: const BoxConstraints(), onPressed: (textFieldConfig?.enabled ?? true) ? () => _onIconPressed( ImageSource.gallery, - config: sendMessageConfig - ?.imagePickerConfiguration, + config: sendMessageConfig?.imagePickerConfiguration, ) : null, - icon: imagePickerIconsConfig - ?.galleryImagePickerIcon ?? + icon: imagePickerIconsConfig?.galleryImagePickerIcon ?? Icon( Icons.image, - color: imagePickerIconsConfig - ?.galleryIconColor, + color: imagePickerIconsConfig?.galleryIconColor, ), ), ], @@ -270,29 +247,21 @@ class _ChatUITextFieldState extends State { !kIsWeb && (Platform.isIOS || Platform.isAndroid)) IconButton( - onPressed: (textFieldConfig?.enabled ?? true) - ? _recordOrStop - : null, - icon: (isRecordingValue - ? voiceRecordingConfig?.stopIcon - : voiceRecordingConfig?.micIcon) ?? + onPressed: (textFieldConfig?.enabled ?? true) ? _recordOrStop : null, + icon: (isRecordingValue ? voiceRecordingConfig?.stopIcon : voiceRecordingConfig?.micIcon) ?? Icon( isRecordingValue ? Icons.stop : Icons.mic, - color: - voiceRecordingConfig?.recorderIconColor, + color: voiceRecordingConfig?.recorderIconColor, ), ), - if (isRecordingValue && - cancelRecordConfiguration != null) + if (isRecordingValue && cancelRecordConfiguration != null) IconButton( onPressed: () { cancelRecordConfiguration?.onCancel?.call(); _cancelRecording(); }, - icon: cancelRecordConfiguration?.icon ?? - const Icon(Icons.cancel_outlined), - color: cancelRecordConfiguration?.iconColor ?? - voiceRecordingConfig?.recorderIconColor, + icon: cancelRecordConfiguration?.icon ?? const Icon(Icons.cancel_outlined), + color: cancelRecordConfiguration?.iconColor ?? voiceRecordingConfig?.recorderIconColor, ), ], ); @@ -308,8 +277,7 @@ class _ChatUITextFieldState extends State { FutureOr _cancelRecording() async { assert( - defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android, + defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.android, "Voice messages are only supported with android and ios platform", ); if (!isRecording.value) return; @@ -329,8 +297,7 @@ class _ChatUITextFieldState extends State { Future _recordOrStop() async { assert( - defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android, + defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.android, "Voice messages are only supported with android and ios platform", ); if (!isRecording.value) { @@ -359,8 +326,7 @@ class _ChatUITextFieldState extends State { maxHeight: config?.maxHeight, maxWidth: config?.maxWidth, imageQuality: config?.imageQuality, - preferredCameraDevice: - config?.preferredCameraDevice ?? CameraDevice.rear, + preferredCameraDevice: config?.preferredCameraDevice ?? CameraDevice.rear, ); String? imagePath = image?.path; if (config?.onImagePicked != null) { @@ -381,4 +347,11 @@ class _ChatUITextFieldState extends State { }); _inputText.value = inputText; } + + void _onSubmit(String inputText) { + if (inputText.isNotEmpty) { + widget.onPressed(); + _inputText.value = ''; + } + } } diff --git a/lib/src/widgets/message_view.dart b/lib/src/widgets/message_view.dart index 2d1215f5..70abeabc 100644 --- a/lib/src/widgets/message_view.dart +++ b/lib/src/widgets/message_view.dart @@ -38,8 +38,8 @@ class MessageView extends StatefulWidget { required this.onLongPress, required this.isLongPressEnable, this.chatBubbleMaxWidth, - this.inComingChatBubbleConfig, - this.outgoingChatBubbleConfig, + this.incomingChatBubble, + this.outgoingChatBubble, this.longPressAnimationDuration, this.onDoubleTap, this.highlightColor = Colors.grey, @@ -63,10 +63,10 @@ class MessageView extends StatefulWidget { final double? chatBubbleMaxWidth; /// Provides configuration of chat bubble appearance from other user of chat. - final ChatBubble? inComingChatBubbleConfig; + final ChatBubble? incomingChatBubble; /// Provides configuration of chat bubble appearance from current user of chat. - final ChatBubble? outgoingChatBubbleConfig; + final ChatBubble? outgoingChatBubble; /// Allow users to give duration of animation when user long press on chat bubble. final Duration? longPressAnimationDuration; @@ -98,8 +98,7 @@ class MessageView extends StatefulWidget { State createState() => _MessageViewState(); } -class _MessageViewState extends State - with SingleTickerProviderStateMixin { +class _MessageViewState extends State with SingleTickerProviderStateMixin { AnimationController? _animationController; MessageConfiguration? get messageConfig => widget.messageConfig; @@ -112,14 +111,12 @@ class _MessageViewState extends State if (isLongPressEnable) { _animationController = AnimationController( vsync: this, - duration: widget.longPressAnimationDuration ?? - const Duration(milliseconds: 250), + duration: widget.longPressAnimationDuration ?? const Duration(milliseconds: 250), upperBound: 0.1, lowerBound: 0.0, ); - if (widget.message.status != MessageStatus.read && - !widget.isMessageBySender) { - widget.inComingChatBubbleConfig?.onMessageRead?.call(widget.message); + if (widget.message.status != MessageStatus.read && !widget.isMessageBySender) { + widget.incomingChatBubble?.onMessageRead?.call(widget.message); } _animationController?.addStatusListener((status) { if (status == AnimationStatus.completed) { @@ -175,26 +172,20 @@ class _MessageViewState extends State leftPadding2, 4, leftPadding2, - widget.message.reaction.reactions.isNotEmpty - ? 14 - : 0, + widget.message.reaction.reactions.isNotEmpty ? 14 : 0, ), child: Transform.scale( - scale: widget.shouldHighlight - ? widget.highlightScale - : 1.0, + scale: widget.shouldHighlight ? widget.highlightScale : 1.0, child: Text( message, - style: emojiMessageConfiguration?.textStyle ?? - const TextStyle(fontSize: 30), + style: emojiMessageConfiguration?.textStyle ?? const TextStyle(fontSize: 30), ), ), ), if (widget.message.reaction.reactions.isNotEmpty) ReactionWidget( reaction: widget.message.reaction, - messageReactionConfig: - messageConfig?.messageReactionConfig, + messageReactionConfig: messageConfig?.messageReactionConfig, isMessageBySender: widget.isMessageBySender, ), ], @@ -210,8 +201,8 @@ class _MessageViewState extends State ); } else if (widget.message.messageType.isText) { return TextMessageView( - inComingChatBubbleConfig: widget.inComingChatBubbleConfig, - outgoingChatBubbleConfig: widget.outgoingChatBubbleConfig, + incomingChatBubble: widget.incomingChatBubble, + outgoingChatBubble: widget.outgoingChatBubble, isMessageBySender: widget.isMessageBySender, message: widget.message, chatBubbleMaxWidth: widget.chatBubbleMaxWidth, @@ -227,11 +218,10 @@ class _MessageViewState extends State onMaxDuration: widget.onMaxDuration, isMessageBySender: widget.isMessageBySender, messageReactionConfig: messageConfig?.messageReactionConfig, - inComingChatBubbleConfig: widget.inComingChatBubbleConfig, - outgoingChatBubbleConfig: widget.outgoingChatBubbleConfig, + incomingChatBubble: widget.incomingChatBubble, + outgoingChatBubble: widget.outgoingChatBubble, ); - } else if (widget.message.messageType.isCustom && - messageConfig?.customMessageBuilder != null) { + } else if (widget.message.messageType.isCustom && messageConfig?.customMessageBuilder != null) { return messageConfig?.customMessageBuilder!(widget.message); } }()) ?? @@ -240,21 +230,12 @@ class _MessageViewState extends State valueListenable: widget.message.statusNotifier, builder: (context, value, child) { if (widget.isMessageBySender && - widget.controller?.initialMessageList.last.id == - widget.message.id && + widget.controller?.initialMessageList.last.id == widget.message.id && widget.message.status == MessageStatus.read) { - if (ChatViewInheritedWidget.of(context) - ?.featureActiveConfig - .lastSeenAgoBuilderVisibility ?? - true) { - return widget.outgoingChatBubbleConfig?.receiptsWidgetConfig - ?.lastSeenAgoBuilder - ?.call( - widget.message, - applicationDateFormatter( - widget.message.createdAt)) ?? - lastSeenAgoBuilder(widget.message, - applicationDateFormatter(widget.message.createdAt)); + if (ChatViewInheritedWidget.of(context)?.featureActiveConfig.lastSeenAgoBuilderVisibility ?? true) { + return widget.outgoingChatBubble?.receiptsWidgetConfig?.lastSeenAgoBuilder + ?.call(widget.message, applicationDateFormatter(widget.message.createdAt)) ?? + lastSeenAgoBuilder(widget.message, applicationDateFormatter(widget.message.createdAt)); } return const SizedBox(); } diff --git a/lib/src/widgets/text_message_view.dart b/lib/src/widgets/text_message_view.dart index 4dacc90b..69c17f17 100644 --- a/lib/src/widgets/text_message_view.dart +++ b/lib/src/widgets/text_message_view.dart @@ -34,8 +34,8 @@ class TextMessageView extends StatelessWidget { required this.isMessageBySender, required this.message, this.chatBubbleMaxWidth, - this.inComingChatBubbleConfig, - this.outgoingChatBubbleConfig, + this.incomingChatBubble, + this.outgoingChatBubble, this.messageReactionConfig, this.highlightMessage = false, this.highlightColor, @@ -51,10 +51,10 @@ class TextMessageView extends StatelessWidget { final double? chatBubbleMaxWidth; /// Provides configuration of chat bubble appearance from other user of chat. - final ChatBubble? inComingChatBubbleConfig; + final ChatBubble? incomingChatBubble; /// Provides configuration of chat bubble appearance from current user of chat. - final ChatBubble? outgoingChatBubbleConfig; + final ChatBubble? outgoingChatBubble; /// Provides configuration of reaction appearance in chat bubble. final MessageReactionConfiguration? messageReactionConfig; @@ -73,17 +73,13 @@ class TextMessageView extends StatelessWidget { clipBehavior: Clip.none, children: [ Container( - constraints: BoxConstraints( - maxWidth: chatBubbleMaxWidth ?? - MediaQuery.of(context).size.width * 0.75), + constraints: BoxConstraints(maxWidth: chatBubbleMaxWidth ?? MediaQuery.of(context).size.width * 0.75), padding: _padding ?? const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), - margin: _margin ?? - EdgeInsets.fromLTRB( - 5, 0, 6, message.reaction.reactions.isNotEmpty ? 15 : 2), + margin: _margin ?? EdgeInsets.fromLTRB(5, 0, 6, message.reaction.reactions.isNotEmpty ? 15 : 2), decoration: BoxDecoration( color: highlightMessage ? highlightColor : _color, borderRadius: _borderRadius(textMessage), @@ -113,33 +109,22 @@ class TextMessageView extends StatelessWidget { ); } - EdgeInsetsGeometry? get _padding => isMessageBySender - ? outgoingChatBubbleConfig?.padding - : inComingChatBubbleConfig?.padding; + EdgeInsetsGeometry? get _padding => isMessageBySender ? outgoingChatBubble?.padding : incomingChatBubble?.padding; - EdgeInsetsGeometry? get _margin => isMessageBySender - ? outgoingChatBubbleConfig?.margin - : inComingChatBubbleConfig?.margin; + EdgeInsetsGeometry? get _margin => isMessageBySender ? outgoingChatBubble?.margin : incomingChatBubble?.margin; - LinkPreviewConfiguration? get _linkPreviewConfig => isMessageBySender - ? outgoingChatBubbleConfig?.linkPreviewConfig - : inComingChatBubbleConfig?.linkPreviewConfig; + LinkPreviewConfiguration? get _linkPreviewConfig => + isMessageBySender ? outgoingChatBubble?.linkPreviewConfig : incomingChatBubble?.linkPreviewConfig; - TextStyle? get _textStyle => isMessageBySender - ? outgoingChatBubbleConfig?.textStyle - : inComingChatBubbleConfig?.textStyle; + TextStyle? get _textStyle => isMessageBySender ? outgoingChatBubble?.textStyle : incomingChatBubble?.textStyle; BorderRadiusGeometry _borderRadius(String message) => isMessageBySender - ? outgoingChatBubbleConfig?.borderRadius ?? - (message.length < 37 - ? BorderRadius.circular(replyBorderRadius1) - : BorderRadius.circular(replyBorderRadius2)) - : inComingChatBubbleConfig?.borderRadius ?? - (message.length < 29 - ? BorderRadius.circular(replyBorderRadius1) - : BorderRadius.circular(replyBorderRadius2)); + ? outgoingChatBubble?.borderRadius ?? + (message.length < 37 ? BorderRadius.circular(replyBorderRadius1) : BorderRadius.circular(replyBorderRadius2)) + : incomingChatBubble?.borderRadius ?? + (message.length < 29 ? BorderRadius.circular(replyBorderRadius1) : BorderRadius.circular(replyBorderRadius2)); Color get _color => isMessageBySender - ? outgoingChatBubbleConfig?.color ?? Colors.purple - : inComingChatBubbleConfig?.color ?? Colors.grey.shade500; + ? outgoingChatBubble?.color ?? Colors.purple + : incomingChatBubble?.color ?? Colors.grey.shade500; } diff --git a/lib/src/widgets/type_indicator_widget.dart b/lib/src/widgets/type_indicator_widget.dart index 8a97b5ee..7edb366a 100644 --- a/lib/src/widgets/type_indicator_widget.dart +++ b/lib/src/widgets/type_indicator_widget.dart @@ -33,7 +33,7 @@ class TypingIndicator extends StatefulWidget { Key? key, this.showIndicator = false, this.chatBubbleConfig, - this.typeIndicatorConfig, + this.typingIndicatorConfig, }) : super(key: key); /// Allow user to turn on/off typing indicator. @@ -44,14 +44,13 @@ class TypingIndicator extends StatefulWidget { final ChatBubble? chatBubbleConfig; /// Provides configurations related to typing indicator appearance. - final TypeIndicatorConfiguration? typeIndicatorConfig; + final TypingIndicatorConfiguration? typingIndicatorConfig; @override State createState() => _TypingIndicatorState(); } -class _TypingIndicatorState extends State - with TickerProviderStateMixin { +class _TypingIndicatorState extends State with TickerProviderStateMixin { late AnimationController _appearanceController; late Animation _indicatorSpaceAnimation; @@ -72,18 +71,15 @@ class _TypingIndicatorState extends State ChatBubble? get chatBubbleConfig => widget.chatBubbleConfig; - double get indicatorSize => widget.typeIndicatorConfig?.indicatorSize ?? 10; + double get indicatorSize => widget.typingIndicatorConfig?.indicatorSize ?? 10; - double get indicatorSpacing => - widget.typeIndicatorConfig?.indicatorSpacing ?? 4; + double get indicatorSpacing => widget.typingIndicatorConfig?.indicatorSpacing ?? 4; Color? get flashingCircleDarkColor => - widget.typeIndicatorConfig?.flashingCircleDarkColor ?? - const Color(0xFF939497); + widget.typingIndicatorConfig?.flashingCircleDarkColor ?? const Color(0xFF939497); Color? get flashingCircleBrightColor => - widget.typeIndicatorConfig?.flashingCircleBrightColor ?? - const Color(0xFFadacb0); + widget.typingIndicatorConfig?.flashingCircleBrightColor ?? const Color(0xFFadacb0); @override void initState() { @@ -245,15 +241,10 @@ class _TypingIndicatorState extends State bottomPadding: 0, imageUrl: profileCircleConfiguration?.profileImageUrl, imageType: profileCircleConfiguration?.imageType, - assetImageErrorBuilder: - profileCircleConfiguration?.assetImageErrorBuilder, - networkImageErrorBuilder: - profileCircleConfiguration?.networkImageErrorBuilder, - defaultAvatarImage: - profileCircleConfiguration?.defaultAvatarImage ?? - profileImage, - networkImageProgressIndicatorBuilder: profileCircleConfiguration - ?.networkImageProgressIndicatorBuilder, + assetImageErrorBuilder: profileCircleConfiguration?.assetImageErrorBuilder, + networkImageErrorBuilder: profileCircleConfiguration?.networkImageErrorBuilder, + defaultAvatarImage: profileCircleConfiguration?.defaultAvatarImage ?? profileImage, + networkImageProgressIndicatorBuilder: profileCircleConfiguration?.networkImageProgressIndicatorBuilder, ), bubble, ], @@ -264,13 +255,10 @@ class _TypingIndicatorState extends State Widget _buildStatusBubble() { return Container( - padding: chatBubbleConfig?.padding ?? - const EdgeInsets.fromLTRB( - leftPadding3, 0, leftPadding3, leftPadding3), + padding: chatBubbleConfig?.padding ?? const EdgeInsets.fromLTRB(leftPadding3, 0, leftPadding3, leftPadding3), margin: chatBubbleConfig?.margin ?? const EdgeInsets.fromLTRB(5, 0, 6, 2), decoration: BoxDecoration( - borderRadius: chatBubbleConfig?.borderRadius ?? - BorderRadius.circular(replyBorderRadius2), + borderRadius: chatBubbleConfig?.borderRadius ?? BorderRadius.circular(replyBorderRadius2), color: chatBubbleConfig?.color ?? Colors.grey.shade500, ), child: Padding( @@ -290,8 +278,7 @@ class _TypingIndicatorState extends State return AnimatedBuilder( animation: _jumpAnimations[value], builder: (context, child) { - final circleFlashPercent = - _dotIntervals[index].transform(_repeatingController.value); + final circleFlashPercent = _dotIntervals[index].transform(_repeatingController.value); final circleColorPercent = sin(pi * circleFlashPercent); return Transform.translate( offset: Offset(0, _jumpAnimations[value].value), @@ -301,8 +288,7 @@ class _TypingIndicatorState extends State margin: EdgeInsets.symmetric(horizontal: indicatorSpacing), decoration: BoxDecoration( shape: BoxShape.circle, - color: Color.lerp(flashingCircleDarkColor, - flashingCircleBrightColor, circleColorPercent), + color: Color.lerp(flashingCircleDarkColor, flashingCircleBrightColor, circleColorPercent), ), ), ); diff --git a/lib/src/widgets/voice_message_view.dart b/lib/src/widgets/voice_message_view.dart index c8ce902a..25cfc1b9 100644 --- a/lib/src/widgets/voice_message_view.dart +++ b/lib/src/widgets/voice_message_view.dart @@ -12,8 +12,8 @@ class VoiceMessageView extends StatefulWidget { required this.screenWidth, required this.message, required this.isMessageBySender, - this.inComingChatBubbleConfig, - this.outgoingChatBubbleConfig, + this.incomingChatBubble, + this.outgoingChatBubble, this.onMaxDuration, this.messageReactionConfig, this.config, @@ -36,10 +36,10 @@ class VoiceMessageView extends StatefulWidget { final MessageReactionConfiguration? messageReactionConfig; /// Provides configuration of chat bubble appearance from other user of chat. - final ChatBubble? inComingChatBubbleConfig; + final ChatBubble? incomingChatBubble; /// Provides configuration of chat bubble appearance from current user of chat. - final ChatBubble? outgoingChatBubbleConfig; + final ChatBubble? outgoingChatBubble; @override State createState() => _VoiceMessageViewState(); @@ -49,8 +49,7 @@ class _VoiceMessageViewState extends State { late PlayerController controller; late StreamSubscription playerStateSubscription; - final ValueNotifier _playerState = - ValueNotifier(PlayerState.stopped); + final ValueNotifier _playerState = ValueNotifier(PlayerState.stopped); PlayerState get playerState => _playerState.value; @@ -62,12 +61,10 @@ class _VoiceMessageViewState extends State { controller = PlayerController() ..preparePlayer( path: widget.message.message, - noOfSamples: widget.config?.playerWaveStyle - ?.getSamplesForWidth(widget.screenWidth * 0.5) ?? + noOfSamples: widget.config?.playerWaveStyle?.getSamplesForWidth(widget.screenWidth * 0.5) ?? playerWaveStyle.getSamplesForWidth(widget.screenWidth * 0.5), ).whenComplete(() => widget.onMaxDuration?.call(controller.maxDuration)); - playerStateSubscription = controller.onPlayerStateChanged - .listen((state) => _playerState.value = state); + playerStateSubscription = controller.onPlayerStateChanged.listen((state) => _playerState.value = state); } @override @@ -87,12 +84,9 @@ class _VoiceMessageViewState extends State { decoration: widget.config?.decoration ?? BoxDecoration( borderRadius: BorderRadius.circular(12), - color: widget.isMessageBySender - ? widget.outgoingChatBubbleConfig?.color - : widget.inComingChatBubbleConfig?.color, + color: widget.isMessageBySender ? widget.outgoingChatBubble?.color : widget.incomingChatBubble?.color, ), - padding: widget.config?.padding ?? - const EdgeInsets.symmetric(horizontal: 8), + padding: widget.config?.padding ?? const EdgeInsets.symmetric(horizontal: 8), margin: widget.config?.margin ?? EdgeInsets.symmetric( horizontal: 8, @@ -105,18 +99,17 @@ class _VoiceMessageViewState extends State { builder: (context, state, child) { return IconButton( onPressed: _playOrPause, - icon: - state.isStopped || state.isPaused || state.isInitialised - ? widget.config?.playIcon ?? - const Icon( - Icons.play_arrow, - color: Colors.white, - ) - : widget.config?.pauseIcon ?? - const Icon( - Icons.stop, - color: Colors.white, - ), + icon: state.isStopped || state.isPaused || state.isInitialised + ? widget.config?.playIcon ?? + const Icon( + Icons.play_arrow, + color: Colors.white, + ) + : widget.config?.pauseIcon ?? + const Icon( + Icons.stop, + color: Colors.white, + ), ); }, valueListenable: _playerState, @@ -125,14 +118,11 @@ class _VoiceMessageViewState extends State { size: Size(widget.screenWidth * 0.50, 60), playerController: controller, waveformType: WaveformType.fitWidth, - playerWaveStyle: - widget.config?.playerWaveStyle ?? playerWaveStyle, - padding: widget.config?.waveformPadding ?? - const EdgeInsets.only(right: 10), + playerWaveStyle: widget.config?.playerWaveStyle ?? playerWaveStyle, + padding: widget.config?.waveformPadding ?? const EdgeInsets.only(right: 10), margin: widget.config?.waveformMargin, animationCurve: widget.config?.animationCurve ?? Curves.easeIn, - animationDuration: widget.config?.animationDuration ?? - const Duration(milliseconds: 500), + animationDuration: widget.config?.animationDuration ?? const Duration(milliseconds: 500), enableSeekGesture: widget.config?.enableSeekGesture ?? true, ), ], @@ -150,8 +140,7 @@ class _VoiceMessageViewState extends State { void _playOrPause() { assert( - defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android, + defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.android, "Voice messages are only supported with android and ios platform", ); if (playerState.isInitialised ||