diff --git a/doc/contributors.md b/doc/contributors.md
new file mode 100644
index 00000000..1acf2ae2
--- /dev/null
+++ b/doc/contributors.md
@@ -0,0 +1,12 @@
+## Main Contributors
+
+
+
\ No newline at end of file
diff --git a/doc/how_to_use.md b/doc/how_to_use.md
new file mode 100644
index 00000000..b517e834
--- /dev/null
+++ b/doc/how_to_use.md
@@ -0,0 +1,5 @@
+## How to use
+
+Check out [blog](https://medium.com/simform-engineering/chatview-a-cutting-edge-chat-ui-solution-7367b1f9d772) for better understanding and basic implementation.
+
+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.
\ No newline at end of file
diff --git a/doc/installation.md b/doc/installation.md
new file mode 100644
index 00000000..90d47693
--- /dev/null
+++ b/doc/installation.md
@@ -0,0 +1,59 @@
+1. Add dependencies to `pubspec.yaml`
+
+ Get the latest version in the 'Installing' tab
+ on [pub.dev](https://pub.dev/packages/chatview/install)
+
+ ```yaml
+ dependencies:
+ chatview:
+ ```
+
+2. Run pub get.
+
+ ```shell
+ flutter pub get
+ ```
+
+3. Import package.
+
+ ```dart
+ import 'package:chatview/chatview.dart';
+ ```
+
+## Platform specific configuration
+
+### For image Picker
+#### iOS
+* Add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`:
+
+```
+ NSCameraUsageDescription
+ Used to demonstrate image picker plugin
+ NSMicrophoneUsageDescription
+ Used to capture audio for image picker plugin
+ NSPhotoLibraryUsageDescription
+ Used to demonstrate image picker plugin
+```
+
+### For voice messages
+#### iOS
+* 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`
+```
+ platform :ios, '10.0'
+```
+
+#### Android
+* 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`
+```
+
+```
\ No newline at end of file
diff --git a/doc/migration_guide.md b/doc/migration_guide.md
new file mode 100644
index 00000000..ad9d9553
--- /dev/null
+++ b/doc/migration_guide.md
@@ -0,0 +1,171 @@
+## Migration guide for release 2.0.0
+
+- Renamed `sendBy` field to `sentBy` in `Message` class.
+
+- Renamed `chatUsers` field to `otherUsers` in `ChatController` class.
+
+- Moved `currentUser` field from `ChatView` widget to `ChatController` class
+
+- Updated `id` value in `copyWith` method of `Message` to have correct value.
+
+- Removed `showTypingIndicator` field from `ChatView` and replaced it with `ChatController.showTypingIndicator`.
+
+ Before:
+ ```dart
+ ChatView(
+ showTypingIndicator:false,
+ ),
+ ```
+
+ After:
+ ```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
+ {
+ ...
+ imageType: ImageType.asset,
+ ...
+ }
+ ```
+
+ After:
+ ```dart
+ {
+ ...
+ imageType: asset,
+ ...
+ }
+ ```
+
+ ##### in `Message.fromJson`:
+
+ Before:
+ ```dart
+ Message.fromJson(
+ {
+ ...
+ 'createdAt': DateTime.now(),
+ 'message_type': MessageType.text,
+ 'voice_message_duration': Duration(seconds: 5),
+ ...
+ }
+ )
+ ```
+
+ After:
+ ```dart
+ Message.fromJson(
+ {
+ ...
+ 'createdAt': '2024-06-13T17:32:19.586412',
+ 'message_type': 'text',
+ 'voice_message_duration': '5000000',
+ ...
+ }
+ )
+ ```
+
+ ##### in `Message.toJson`:
+
+ Before:
+ ```dart
+ {
+ ...
+ createdAt: 2024-06-13 17:23:19.454789,
+ message_type: MessageType.text,
+ voice_message_duration: 0:00:05.000000,
+ ...
+ }
+ ```
+
+ After:
+ ```dart
+ {
+ ...
+ createdAt: 2024-06-13T17:32:19.586412,
+ message_type: text,
+ voice_message_duration: 5000000,
+ ...
+ }
+ ```
+
+ ##### in `ReplyMessage.fromJson`:
+
+ Before:
+ ```dart
+ ReplyMessage.fromJson(
+ {
+ ...
+ 'message_type': MessageType.text,
+ 'voiceMessageDuration': Duration(seconds: 5),
+ ...
+ }
+ )
+ ```
+
+ After:
+ ```dart
+ ReplyMessage.fromJson(
+ {
+ ...
+ '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,
+ ...
+ }
+ ```
\ No newline at end of file
diff --git a/doc/optional_parameter.md b/doc/optional_parameter.md
new file mode 100644
index 00000000..3d80e168
--- /dev/null
+++ b/doc/optional_parameter.md
@@ -0,0 +1,695 @@
+## Some more optional parameters
+
+1. Enable and disable specific features with `FeatureActiveConfig`.
+```dart
+ChatView(
+ ...
+ featureActiveConfig: FeatureActiveConfig(
+ enableSwipeToReply: true,
+ enableSwipeToSeeTime: false,
+ ),
+ ...
+)
+```
+
+2. Adding an appbar with `ChatViewAppBar`.
+```dart
+ChatView(
+ ...
+ appBar: ChatViewAppBar(
+ profilePicture: profileImage,
+ chatTitle: "Simform",
+ userStatus: "online",
+ actions: [
+ Icon(Icons.more_vert),
+ ],
+ ),
+ ...
+)
+```
+
+3. Adding a message list configuration with `ChatBackgroundConfiguration` class.
+```dart
+ChatView(
+ ...
+ chatBackgroundConfig: ChatBackgroundConfiguration(
+ backgroundColor: Colors.white,
+ backgroundImage: backgroundImage,
+ ),
+ ...
+)
+```
+
+4. Adding a send message configuration with `SendMessageConfiguration` class.
+```dart
+ChatView(
+ ...
+ sendMessageConfig: SendMessageConfiguration(
+ replyMessageColor: Colors.grey,
+ replyDialogColor:Colors.blue,
+ replyTitleColor: Colors.black,
+ closeIconColor: Colors.black,
+ ),
+ ...
+)
+```
+
+5. Adding a chat bubble configuration with `ChatBubbleConfiguration` class.
+```dart
+ChatView(
+ ...
+ chatBubbleConfig: ChatBubbleConfiguration(
+ onDoubleTap: (){
+ // Your code goes here
+ },
+ outgoingChatBubbleConfig: ChatBubble( // Sender's message chat bubble
+ color: Colors.blue,
+ borderRadius: const BorderRadius.only(
+ topRight: Radius.circular(12),
+ topLeft: Radius.circular(12),
+ bottomLeft: Radius.circular(12),
+ ),
+ ),
+ inComingChatBubbleConfig: ChatBubble( // Receiver's message chat bubble
+ color: Colors.grey.shade200,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(12),
+ topRight: Radius.circular(12),
+ bottomRight: Radius.circular(12),
+ ),
+ ),
+ )
+ ...
+)
+```
+
+6. Adding swipe to reply configuration with `SwipeToReplyConfiguration` class.
+```dart
+ChatView(
+ ...
+ swipeToReplyConfig: SwipeToReplyConfiguration(
+ onLeftSwipe: (message, sentBy){
+ // Your code goes here
+ },
+ 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
+ imageMessageConfig: ImageMessageConfiguration(
+ onTap: (){
+ // Your code goes here
+ },
+ shareIconConfig: ShareIconConfiguration(
+ onPressed: (){
+ // Your code goes here
+ },
+ ),
+ ),
+ ),
+ ...
+)
+```
+
+8. Adding reaction pop-up configuration with `ReactionPopupConfiguration` class.
+```dart
+ChatView(
+ ...
+ reactionPopupConfig: ReactionPopupConfiguration(
+ backgroundColor: Colors.white,
+ userReactionCallback: (message, emoji){
+ // Your code goes here
+ }
+ padding: EdgeInsets.all(12),
+ shadow: BoxShadow(
+ color: Colors.black54,
+ blurRadius: 20,
+ ),
+ ),
+ ...
+)
+```
+
+9. Adding reply pop-up configuration with `ReplyPopupConfiguration` class.
+```dart
+ChatView(
+ ...
+ replyPopupConfig: ReplyPopupConfiguration(
+ backgroundColor: Colors.white,
+ onUnsendTap:(message){ // message is 'Message' class instance
+ // Your code goes here
+ },
+ onReplyTap:(message){ // message is 'Message' class instance
+ // Your code goes here
+ },
+ onReportTap:(){
+ // Your code goes here
+ },
+ onMoreTap:(){
+ // Your code goes here
+ },
+ ),
+ ...
+)
+```
+
+10. Adding replied message configuration with `RepliedMessageConfiguration` class.
+```dart
+ChatView(
+ ...
+ repliedMessageConfig: RepliedMessageConfiguration(
+ backgroundColor: Colors.blue,
+ verticalBarColor: Colors.black,
+ repliedMsgAutoScrollConfig: RepliedMsgAutoScrollConfig(),
+ ),
+ ...
+)
+```
+
+11. For customizing typing indicators use `typeIndicatorConfig` with `TypeIndicatorConfig`.
+```dart
+ChatView(
+ ...
+
+ typeIndicatorConfig: TypeIndicatorConfiguration(
+ flashingCircleBrightColor: Colors.grey,
+ flashingCircleDarkColor: Colors.black,
+ ),
+ ...
+)
+
+```
+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
+_chatContoller.setTypingIndicator = false; // for hiding indicator
+```
+
+13. Adding linkpreview configuration with `LinkPreviewConfiguration` class.
+```dart
+ChatView(
+ ...
+ chatBubbleConfig: ChatBubbleConfiguration(
+ linkPreviewConfig: LinkPreviewConfiguration(
+ linkStyle: const TextStyle(
+ color: Colors.white,
+ decoration: TextDecoration.underline,
+ ),
+ backgroundColor: Colors.grey,
+ bodyStyle: const TextStyle(
+ color: Colors.grey.shade200,
+ fontSize:16,
+ ),
+ titleStyle: const TextStyle(
+ color: Colors.black,
+ fontSize:20,
+ ),
+ ),
+ )
+ ...
+)
+```
+
+14. Adding pagination.
+```dart
+ChatView(
+ ...
+ isLastPage: false,
+ featureActiveConfig: FeatureActiveConfig(
+ enablePagination: true,
+ ),
+ loadMoreData: chatController.loadMoreData,
+ ...
+)
+```
+
+15. Add image picker configuration.
+```dart
+ChatView(
+ ...
+ sendMessageConfig: SendMessageConfiguration(
+ enableCameraImagePicker: false,
+ enableGalleryImagePicker: true,
+ imagePickerIconsConfig: ImagePickerIconsConfiguration(
+ cameraIconColor: Colors.black,
+ galleryIconColor: Colors.black,
+ )
+ )
+ ...
+)
+```
+
+16. Add `ChatViewState` customisations.
+```dart
+ChatView(
+ ...
+ chatViewStateConfig: ChatViewStateConfiguration(
+ loadingWidgetConfig: ChatViewStateWidgetConfiguration(
+ loadingIndicatorColor: Colors.pink,
+ ),
+ onReloadButtonTap: () {},
+ ),
+ ...
+)
+```
+
+17. Setting auto scroll and highlight config with `RepliedMsgAutoScrollConfig` class.
+```dart
+ChatView(
+ ...
+ repliedMsgAutoScrollConfig: RepliedMsgAutoScrollConfig(
+ enableHighlightRepliedMsg: true,
+ highlightColor: Colors.grey,
+ highlightScale: 1.1,
+ )
+ ...
+)
+```
+
+18. Callback when a user starts/stops typing in `TextFieldConfiguration`
+
+```dart
+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.
+ compositionThresholdTime: const Duration(seconds: 1),
+
+ ),
+ ...
+ )
+)
+```
+
+19. Passing customReceipts builder or handling stuffs related receipts see `ReceiptsWidgetConfig` in outgoingChatBubbleConfig.
+
+```dart
+ChatView(
+ ...
+ featureActiveConfig: const FeatureActiveConfig(
+ /// Controls the visibility of message seen ago receipts default is true
+ lastSeenAgoBuilderVisibility: false,
+ /// Controls the visibility of the message [receiptsBuilder]
+ receiptsBuilderVisibility: false),
+ ChatBubbleConfiguration(
+ inComingChatBubbleConfig: ChatBubble(
+ onMessageRead: (message) {
+ /// send your message reciepts to the other client
+ debugPrint('Message Read');
+ },
+
+ ),
+ outgoingChatBubbleConfig: ChatBubble(
+ receiptsWidgetConfig: ReceiptsWidgetConfig(
+ /// custom receipts builder
+ receiptsBuilder: _customReceiptsBuilder,
+ /// whether to display receipts in all
+ /// message or just at the last one just like instagram
+ showReceiptsIn: ShowReceiptsIn.lastMessage
+ ),
+ ),
+ ),
+
+ ...
+
+)
+```
+
+20. Flag `enableOtherUserName` to hide user name in chat.
+
+```dart
+ChatView(
+ ...
+ featureActiveConfig: const FeatureActiveConfig(
+ enableOtherUserName: false,
+ ),
+ ...
+
+)
+```
+
+21. Added report button for receiver message and update `onMoreTap` and `onReportTap` callbacks.
+
+```dart
+ChatView(
+ ...
+ replyPopupConfig: ReplyPopupConfiguration(
+ onReportTap: (Message message) {
+ debugPrint('Message: $message');
+ },
+ onMoreTap: (Message message, bool sentByCurrentUser) {
+ debugPrint('Message : $message');
+ },
+ ),
+ ...
+)
+```
+
+22. Added `emojiPickerSheetConfig` for configuration of emoji picker sheet.
+
+```dart
+ChatView(
+ ...
+ emojiPickerSheetConfig: Config(
+ emojiViewConfig: EmojiViewConfig(
+ columns: 7,
+ emojiSizeMax: 32,
+ recentsLimit: 28,
+ backgroundColor: Colors.white,
+ ),
+ categoryViewConfig: const CategoryViewConfig(
+ initCategory: Category.RECENT,
+ recentTabBehavior: RecentTabBehavior.NONE,
+ ),
+ ...
+
+)
+```
+
+23. Configure the styling & audio recording quality using `VoiceRecordingConfiguration` in sendMessageConfig.
+
+```dart
+ChatView(
+ ...
+ sendMessageConfig: SendMessageConfiguration(
+
+ voiceRecordingConfiguration: VoiceRecordingConfiguration(
+ iosEncoder: IosEncoder.kAudioFormatMPEG4AAC,
+ androidOutputFormat: AndroidOutputFormat.mpeg4,
+ androidEncoder: AndroidEncoder.aac,
+ bitRate: 128000,
+ sampleRate: 44100,
+ waveStyle: WaveStyle(
+ showMiddleLine: false,
+ waveColor: theme.waveColor ?? Colors.white,
+ extendWaveform: true,
+ ),
+ ),
+
+ ...
+ )
+)
+```
+
+24. Added `enabled` to enable/disable chat text field.
+
+```dart
+ChatView(
+ ...
+ sendMessageConfig: SendMessageConfiguration(
+ ...
+ textFieldConfig: TextFieldConfig(
+ enabled: true // [false] to disable text field.
+ ),
+ ...
+ ),
+ ...
+
+)
+```
+25. Added flag `isProfilePhotoInBase64` that defines whether provided image is url or base64 data.
+
+```dart
+final chatController = ChatController(
+ ...
+ chatUsers: [
+ ChatUser(
+ id: '1',
+ name: 'Simform',
+ isProfilePhotoInBase64: false,
+ profilePhoto: 'ImageNetworkUrl',
+ ),
+ ],
+ ...
+);
+
+ChatView(
+ ...
+ profileCircleConfig: const ProfileCircleConfiguration(
+ isProfilePhotoInBase64: false,
+ profileImageUrl: 'ImageNetworkUrl',
+ ),
+ ...
+)
+```
+
+26. Added `chatSeparatorDatePattern` in `DefaultGroupSeparatorConfiguration` to separate chats with provided pattern.
+
+```dart
+ChatView(
+ ...
+ chatBackgroundConfig: ChatBackgroundConfiguration(
+ ...
+ defaultGroupSeparatorConfig: DefaultGroupSeparatorConfiguration(
+ chatSeparatorDatePattern: 'MMM dd, yyyy'
+ ),
+ ...
+ ),
+ ...
+)
+```
+
+27. Field `cancelRecordConfiguration` to provide an configuration to cancel voice record message.
+
+```dart
+ChatView(
+ ...
+ sendMessageConfig: SendMessageConfiguration(
+ ...
+ cancelRecordConfiguration: CancelRecordConfiguration(
+ icon: const Icon(
+ Icons.cancel_outlined,
+ ),
+ onCancel: () {
+ debugPrint('Voice recording cancelled');
+ },
+ iconColor: Colors.black,
+ ),
+ ...
+ ),
+ ...
+
+)
+```
+
+28. Added callback of onTap on list of reacted users in reaction sheet `reactedUserCallback`.
+```dart
+
+ChatView(
+ ...
+ messageConfig: MessageConfiguration(
+ ...
+ messageReactionConfig: MessageReactionConfiguration(
+ reactionsBottomSheetConfig: ReactionsBottomSheetConfiguration(
+ reactedUserCallback: (reactedUser, reaction) {
+ debugPrint(reaction);
+ },
+ ),
+ ),
+ ...
+ ),
+ ...
+),
+```
+
+29. Added a `customMessageReplyViewBuilder` to customize reply message view for custom type message.
+
+```dart
+ChatView(
+ ...
+ messageConfig: MessageConfiguration(
+ customMessageBuilder: (ReplyMessage state) {
+ return Text(
+ state.message,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: const TextStyle(
+ fontSize: 12,
+ color: Colors.black,
+ ),
+ );
+ },
+ ),
+ ...
+)
+```
+
+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(
+ ...
+ appBar: ChatViewAppBar(
+ defaultAvatarImage: defaultAvatar,
+ imageType: ImageType.network,
+ networkImageErrorBuilder: (context, url, error) {
+ return Center(
+ child: Text('Error $error'),
+ );
+ },
+ assetImageErrorBuilder: (context, error, stackTrace) {
+ return Center(
+ child: Text('Error $error'),
+ );
+ },
+ ),
+ ...
+),
+```
+
+31. Added a `customMessageReplyViewBuilder` to customize reply message view for custom type message.
+
+```dart
+ChatView(
+ ...
+ messageConfig: MessageConfiguration(
+ customMessageBuilder: (ReplyMessage state) {
+ return Text(
+ state.message,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: const TextStyle(
+ fontSize: 12,
+ color: Colors.black,
+ ),
+ );
+ },
+ ),
+ ...
+)
+```
+
+32. Added a `replyMessageBuilder` to customize view for the reply.
+
+```dart
+ChatView(
+ ...
+ replyMessageBuilder: (context, state) {
+ return Container(
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.vertical(
+ top: Radius.circular(14),
+ ),
+ ),
+ margin: const EdgeInsets.only(
+ bottom: 17,
+ right: 0.4,
+ left: 0.4,
+ ),
+ padding: const EdgeInsets.fromLTRB(10, 10, 10, 30),
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 6,
+ ),
+ decoration: BoxDecoration(
+ color: Colors.grey.shade200,
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(
+ state.message,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: const TextStyle(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ IconButton(
+ constraints: const BoxConstraints(),
+ padding: EdgeInsets.zero,
+ icon: const Icon(
+ Icons.close,
+ size: 16,
+ ),
+ onPressed: () => ChatView.closeReplyMessageView(context),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ...
+)
+```
+
+33. Reply Suggestions functionalities.
+
+* Add reply suggestions
+```dart
+_chatController.addReplySuggestions([
+ SuggestionItemData(text: 'Thanks.'),
+ SuggestionItemData(text: 'Thank you very much.'),
+ SuggestionItemData(text: 'Great.')
+ ]);
+```
+* Remove reply suggestions
+```dart
+_chatController.removeReplySuggestions();
+```
+* Update suggestions Config
+```dart
+replySuggestionsConfig: ReplySuggestionsConfig(
+ itemConfig: SuggestionItemConfig(
+ decoration: BoxDecoration(),
+ textStyle: TextStyle(),
+ padding: EdgetInsets.all(8),
+ customItemBuilder: (index, suggestionItemData) => Container()
+ ),
+ listConfig: SuggestionListConfig(
+ decoration: BoxDecoration(),
+ padding: EdgetInsets.all(8),
+ itemSeparatorWidth: 8,
+ axisAlignment: SuggestionListAlignment.left
+ )
+ onTap: (item) =>
+ _onSendTap(item.text, const ReplyMessage(), MessageType.text),
+ autoDismissOnSelection: true
+),
+```
+
+34. Added callback `messageSorter` to sort message in `ChatBackgroundConfiguration`.
+
+```dart
+ChatView(
+ ...
+ chatBackgroundConfig: ChatBackgroundConfiguration(
+ ...
+ messageSorter: (message1, message2) {
+ return message1.createdAt.compareTo(message2.createdAt);
+ }
+ ...
+ ),
+ ...
+),
+```
\ No newline at end of file
diff --git a/doc/overview.md b/doc/overview.md
new file mode 100644
index 00000000..7f25c88e
--- /dev/null
+++ b/doc/overview.md
@@ -0,0 +1,12 @@
+![Banner](https://raw.githubusercontent.com/SimformSolutionsPvtLtd/flutter_chatview/main/preview/banner.png)
+
+# ChatView
+
+A Flutter package that allows you to integrate Chat View with highly customization options such as one on one
+chat, group chat, message reactions, reply messages, link preview and configurations for overall view.
+
+For web demo visit [Chat View Example](https://simformsolutionspvtltd.github.io/flutter_chatview/).
+
+## Preview
+
+![The example app running in iOS](https://raw.githubusercontent.com/SimformSolutionsPvtLtd/flutter_chatview/main/preview/chatview.gif)
\ No newline at end of file
diff --git a/doc/usage.md b/doc/usage.md
new file mode 100644
index 00000000..14562a45
--- /dev/null
+++ b/doc/usage.md
@@ -0,0 +1,51 @@
+1. Adding a chat controller.
+```dart
+final chatController = ChatController(
+ initialMessageList: messageList,
+ scrollController: ScrollController(),
+ currentUser: ChatUser(id: '1', name: 'Flutter'),
+ otherUsers: [ChatUser(id: '2', name: 'Simform')],
+);
+```
+
+2. Adding a `ChatView` widget.
+```dart
+ChatView(
+ chatController: chatController,
+ onSendTap: onSendTap,
+ chatViewState: ChatViewState.hasMessages, // Add this state once data is available.
+)
+```
+
+3. Adding a messageList with `Message` class.
+```dart
+List messageList = [
+ Message(
+ id: '1',
+ message: "Hi",
+ createdAt: createdAt,
+ sentBy: userId,
+ ),
+ Message(
+ id: '2',
+ message: "Hello",
+ createdAt: createdAt,
+ sentBy: userId,
+ ),
+];
+```
+
+4. Adding a `onSendTap`.
+```dart
+void onSendTap(String message, ReplyMessage replyMessage, MessageType messageType){
+ final message = Message(
+ id: '3',
+ message: "How are you",
+ createdAt: DateTime.now(),
+ senBy: currentUser.id,
+ replyMessage: replyMessage,
+ messageType: messageType,
+ );
+ chatController.addMessage(message);
+}
+```
\ No newline at end of file
diff --git a/lib/src/widgets/revised_group_list_view.dart b/lib/src/widgets/revised_group_list_view.dart
new file mode 100644
index 00000000..0b232564
--- /dev/null
+++ b/lib/src/widgets/revised_group_list_view.dart
@@ -0,0 +1,438 @@
+// import 'dart:async';
+// import 'dart:collection';
+// import 'dart:math' as math;
+//
+// import 'package:chatview/src/models/message.dart';
+// import 'package:flutter/gestures.dart';
+// import 'package:flutter/material.dart';
+// import 'package:flutter/widgets.dart';
+//
+// enum GroupedListOrder { ASC, DESC }
+// /// A groupable list of widgets similar to [ListView], execpt that the
+// /// items can be sectioned into groups.
+// ///
+// /// See [ListView.builder]
+// ///
+// @immutable
+// class GroupedListView extends StatefulWidget {
+// /// Items of which [itemBuilder] or [indexedItemBuilder] produce the list.
+// final List elements;
+//
+// /// Defines which elements are grouped together.
+// ///
+// /// Function is called for each element in the list, when equal for two
+// /// elements, those two belong to the same group.
+// final E Function(T element) groupBy;
+//
+// /// Can be used to define a custom sorting for the groups.
+// ///
+// /// If not set groups will be sorted with their natural sorting order or their
+// /// specific [Comparable] implementation.
+// final int Function(E value1, E value2)? groupComparator;
+//
+// /// Can be used to define a custom sorting for the elements inside each group.
+// ///
+// /// If not set elements will be sorted with their natural sorting order or
+// /// their specific [Comparable] implementation.
+// final int Function(T element1, T element2)? itemComparator;
+//
+// /// Called to build group separators for each group.
+// /// Value is always the groupBy result from the first element of the group.
+// ///
+// /// Will be ignored if [groupHeaderBuilder] is used.
+// final Widget Function(E value)? groupSeparatorBuilder;
+//
+// /// Same as [groupSeparatorBuilder], will be called to build group separators
+// /// for each group.
+// /// The passed element is always the first element of the group.
+// ///
+// /// If defined [groupSeparatorBuilder] wont be used.
+// final Widget Function(T element)? groupHeaderBuilder;
+//
+// /// Called to build children for the list with
+// /// 0 <= element < elements.length.
+// final Widget Function(BuildContext context, T element)? itemBuilder;
+//
+// /// Called to build children for the list with
+// /// 0 <= element, index < elements.length
+// final Widget Function(BuildContext context, T element, int index)?
+// indexedItemBuilder;
+//
+// /// Whether the order of the list is ascending or descending.
+// ///
+// /// Defaults to ASC.
+// final GroupedListOrder order;
+//
+// /// Whether the elements will be sorted or not. If not it must be done
+// /// manually.
+// ///
+// /// Defauts to true.
+// final bool sort;
+//
+// /// When set to true the group header of the current visible group will stick
+// /// on top.
+// final bool useStickyGroupSeparators;
+//
+// /// Called to build separators for between each item in the list.
+// final Widget separator;
+//
+// /// Whether the group headers float over the list or occupy their own space.
+// final bool floatingHeader;
+//
+// /// Background color of the sticky header.
+// /// Only used if [floatingHeader] is false.
+// final Color stickyHeaderBackgroundColor;
+//
+// /// An object that can be used to control the position to which this scroll
+// /// view is scrolled.
+// ///
+// /// See [ScrollView.controller]
+// final ScrollController? controller;
+//
+// /// The axis along which the scroll view scrolls.
+// ///
+// /// Defaults to [Axis.vertical].
+// final Axis scrollDirection;
+//
+// /// Whether this is the primary scroll view associated with the parent
+// /// [PrimaryScrollController].
+// ///
+// /// See [ScrollView.primary]
+// final bool? primary;
+//
+// /// How the scroll view should respond to user input.
+// ///
+// /// See [ScrollView.physics].
+// final ScrollPhysics? physics;
+//
+// /// Whether the extent of the scroll view in the [scrollDirection] should be
+// /// determined by the contents being viewed.
+// ///
+// /// See [ScrollView.shrinkWrap]
+// final bool shrinkWrap;
+//
+// /// The amount of space by which to inset the children.
+// final EdgeInsetsGeometry? padding;
+//
+// /// Whether the view scrolls in the reading direction.
+// ///
+// /// Defaults to false.
+// ///
+// /// See [ScrollView.reverse].
+// final bool reverse;
+//
+// /// Whether to wrap each child in an [AutomaticKeepAlive].
+// ///
+// /// See [SliverChildBuilderDelegate.addAutomaticKeepAlives].
+// final bool addAutomaticKeepAlives;
+//
+// /// Whether to wrap each child in a [RepaintBoundary].
+// ///
+// /// See [SliverChildBuilderDelegate.addRepaintBoundaries].
+// final bool addRepaintBoundaries;
+//
+// /// Whether to wrap each child in an [IndexedSemantics].
+// ///
+// /// See [SliverChildBuilderDelegate.addSemanticIndexes].
+// final bool addSemanticIndexes;
+//
+// /// Creates a scrollable, linear array of widgets that are created on demand.
+// ///
+// /// See [ScrollView.cacheExtent]
+// final double? cacheExtent;
+//
+// /// {@macro flutter.widgets.Clip}
+// ///
+// /// Defaults to [Clip.hardEdge].
+// final Clip clipBehavior;
+//
+// /// {@macro flutter.widgets.scrollable.dragStartBehavior}
+// final DragStartBehavior dragStartBehavior;
+//
+// /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
+// /// dismiss the keyboard automatically.
+// final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
+//
+// /// {@macro flutter.widgets.scrollable.restorationId}
+// final String? restorationId;
+//
+// /// The number of children that will contribute semantic information.
+// ///
+// /// Some subtypes of [ScrollView] can infer this value automatically. For
+// /// example [ListView] will use the number of widgets in the child list,
+// /// while the [ListView.separated] constructor will use half that amount.
+// ///
+// /// For [CustomScrollView] and other types which do not receive a builder
+// /// or list of widgets, the child count must be explicitly provided. If the
+// /// number is unknown or unbounded this should be left unset or set to null.
+// ///
+// /// See also:
+// ///
+// /// * [SemanticsConfiguration.scrollChildCount], the corresponding semantics property.
+// final int? semanticChildCount;
+//
+// /// If non-null, forces the children to have the given extent in the scroll
+// /// direction.
+// ///
+// /// Specifying an [itemExtent] is more efficient than letting the children
+// /// determine their own extent because the scrolling machinery can make use of
+// /// the foreknowledge of the children's extent to save work, for example when
+// /// the scroll position changes drastically.
+// final double? itemExtent;
+//
+// /// Creates a [GroupedListView]
+// const GroupedListView({
+// Key? key,
+// required this.elements,
+// required this.groupBy,
+// this.groupComparator,
+// this.groupSeparatorBuilder,
+// this.groupHeaderBuilder,
+// this.itemBuilder,
+// this.indexedItemBuilder,
+// this.itemComparator,
+// this.order = GroupedListOrder.ASC,
+// this.sort = true,
+// this.useStickyGroupSeparators = false,
+// this.separator = const SizedBox.shrink(),
+// this.floatingHeader = false,
+// this.stickyHeaderBackgroundColor = const Color(0xffF7F7F7),
+// this.scrollDirection = Axis.vertical,
+// this.controller,
+// this.primary,
+// this.physics,
+// this.shrinkWrap = false,
+// this.padding,
+// this.reverse = false,
+// this.addAutomaticKeepAlives = true,
+// this.addRepaintBoundaries = true,
+// this.addSemanticIndexes = true,
+// this.cacheExtent,
+// this.clipBehavior = Clip.hardEdge,
+// this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
+// this.dragStartBehavior = DragStartBehavior.start,
+// this.restorationId,
+// this.semanticChildCount,
+// this.itemExtent,
+// }) : assert(itemBuilder != null || indexedItemBuilder != null),
+// assert(groupSeparatorBuilder != null || groupHeaderBuilder != null),
+// super(key: key);
+//
+// @override
+// State createState() => _GroupedListViewState();
+// }
+//
+// class _GroupedListViewState extends State> {
+// final StreamController _streamController = StreamController();
+// final LinkedHashMap _keys = LinkedHashMap();
+// final GlobalKey _key = GlobalKey();
+// late final ScrollController _controller;
+// GlobalKey? _groupHeaderKey;
+// List _sortedElements = [];
+// int _topElementIndex = 0;
+// RenderBox? _headerBox;
+// RenderBox? _listBox;
+//
+// /// Fix for backwards compatability
+// ///
+// /// See:
+// /// * https://docs.flutter.dev/development/tools/sdk/release-notes/release-notes-3.0.0#your-code
+// I? _ambiguate(I? value) => value;
+//
+// @override
+// void initState() {
+// _controller = widget.controller ?? ScrollController();
+// if (widget.useStickyGroupSeparators) {
+// _controller.addListener(_scrollListener);
+// }
+// super.initState();
+// }
+//
+// @override
+// void dispose() {
+// if (widget.useStickyGroupSeparators) {
+// _controller.removeListener(_scrollListener);
+// }
+// if (widget.controller == null) {
+// _controller.dispose();
+// }
+// _streamController.close();
+// super.dispose();
+// }
+//
+// @override
+// Widget build(BuildContext context) {
+// _sortedElements = _sortElements();
+// var hiddenIndex = widget.reverse ? _sortedElements.length * 2 - 1 : 0;
+// var isSeparator = widget.reverse ? (int i) => i.isOdd : (int i) => i.isEven;
+//
+// if (widget.reverse) {
+// _ambiguate(WidgetsBinding.instance)!.addPostFrameCallback((_) {
+// _scrollListener();
+// });
+// }
+//
+// return Stack(
+// key: _key,
+// alignment: Alignment.topCenter,
+// children: [
+// ListView.builder(
+// key: widget.key,
+// scrollDirection: widget.scrollDirection,
+// controller: _controller,
+// primary: widget.primary,
+// physics: widget.physics,
+// shrinkWrap: widget.shrinkWrap,
+// padding: widget.padding,
+// reverse: widget.reverse,
+// clipBehavior: widget.clipBehavior,
+// dragStartBehavior: widget.dragStartBehavior,
+// itemExtent: widget.itemExtent,
+// restorationId: widget.restorationId,
+// keyboardDismissBehavior: widget.keyboardDismissBehavior,
+// semanticChildCount: widget.semanticChildCount,
+// itemCount: _sortedElements.length * 2,
+// addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
+// addRepaintBoundaries: widget.addRepaintBoundaries,
+// addSemanticIndexes: widget.addSemanticIndexes,
+// cacheExtent: widget.cacheExtent,
+// itemBuilder: (context, index) {
+// var actualIndex = index ~/ 2;
+// print('==> Index: $index || hiddenIndex: $hiddenIndex || actualIndex: $actualIndex || sortedElements[actualIndex]: ${(_sortedElements[actualIndex] as Message).message} || isSeparator(index): ${isSeparator(index)}');
+// if (index == hiddenIndex) {
+// return Opacity(
+// opacity: widget.useStickyGroupSeparators ? 0 : 1,
+// child: _buildGroupSeparator(_sortedElements[actualIndex]),
+// );
+// }
+// if (isSeparator(index)) {
+// var curr = widget.groupBy(_sortedElements[actualIndex]);
+// var prev = widget.groupBy(
+// _sortedElements[actualIndex + (widget.reverse ? 1 : -1)]);
+// print('==> prev: $prev and curr $curr');
+// if (prev != curr) {
+// return _buildGroupSeparator(_sortedElements[actualIndex]);
+// }
+// return widget.separator;
+// }
+// return _buildItem(context, actualIndex);
+// },
+// ),
+// ],
+// );
+// }
+//
+// Widget _buildItem(context, int index) {
+// final key = _keys.putIfAbsent('$index', () => GlobalKey());
+// final value = _sortedElements[index];
+// return KeyedSubtree(
+// key: key,
+// child: widget.indexedItemBuilder != null
+// ? widget.indexedItemBuilder!(context, value, index)
+// : widget.itemBuilder!(context, value),
+// );
+// }
+//
+// void _scrollListener() {
+// _listBox ??= _key.currentContext?.findRenderObject() as RenderBox?;
+// var listPos = _listBox?.localToGlobal(Offset.zero).dy ?? 0;
+// _headerBox ??=
+// _groupHeaderKey?.currentContext?.findRenderObject() as RenderBox?;
+// var headerHeight = _headerBox?.size.height ?? 0;
+// var max = double.negativeInfinity;
+// var topItemKey = widget.reverse ? '${_sortedElements.length - 1}' : '0';
+// for (var entry in _keys.entries) {
+// var key = entry.value;
+// if (_isListItemRendered(key)) {
+// var itemBox = key.currentContext!.findRenderObject() as RenderBox;
+// // position of the item's top border inside the list view
+// var y = itemBox.localToGlobal(Offset(0, -listPos - headerHeight)).dy;
+// if (y <= headerHeight && y > max) {
+// topItemKey = entry.key;
+// max = y;
+// }
+// }
+// }
+// var index = math.max(int.parse(topItemKey), 0);
+// if (index != _topElementIndex) {
+// var curr = widget.groupBy(_sortedElements[index]);
+// E prev;
+//
+// try {
+// prev = widget.groupBy(_sortedElements[_topElementIndex]);
+// } on RangeError catch (_) {
+// prev = widget.groupBy(_sortedElements[0]);
+// }
+//
+// if (prev != curr) {
+// _topElementIndex = index;
+// _streamController.add(_topElementIndex);
+// }
+// }
+// }
+//
+// List _sortElements() {
+// var elements = [...widget.elements];
+// if (widget.sort && elements.isNotEmpty) {
+// elements.sort((e1, e2) {
+// int? compareResult;
+// // compare groups
+// if (widget.groupComparator != null) {
+// compareResult =
+// widget.groupComparator!(widget.groupBy(e1), widget.groupBy(e2));
+// }
+// else if (widget.groupBy(e1) is Comparable) {
+// compareResult = (widget.groupBy(e1) as Comparable)
+// .compareTo(widget.groupBy(e2) as Comparable);
+// }
+// // compare elements inside group
+// if (compareResult == null || compareResult == 0) {
+// if (widget.itemComparator != null) {
+// compareResult = widget.itemComparator!(e1, e2);
+// } else if (e1 is Comparable) {
+// compareResult = e1.compareTo(e2);
+// }
+// }
+// return compareResult!;
+// });
+// if (widget.order == GroupedListOrder.DESC) {
+// elements = elements.reversed.toList();
+// }
+// }
+// return elements;
+// }
+//
+// Widget _showFixedGroupHeader(int topElementIndex) {
+// _groupHeaderKey = GlobalKey();
+// if (widget.useStickyGroupSeparators && _sortedElements.isNotEmpty) {
+// T topElement;
+//
+// try {
+// topElement = _sortedElements[topElementIndex];
+// } on RangeError catch (_) {
+// topElement = _sortedElements[0];
+// }
+//
+// return Container(
+// key: _groupHeaderKey,
+// color:
+// widget.floatingHeader ? null : widget.stickyHeaderBackgroundColor,
+// width: widget.floatingHeader ? null : MediaQuery.of(context).size.width,
+// child: _buildGroupSeparator(topElement),
+// );
+// }
+// return Container();
+// }
+//
+// bool _isListItemRendered(GlobalKey> key) {
+// return key.currentContext != null &&
+// key.currentContext!.findRenderObject() != null;
+// }
+//
+// Widget _buildGroupSeparator(T element) {
+// if (widget.groupHeaderBuilder == null) {
+// return widget.groupSeparatorBuilder!(widget.groupBy(element));
+// }
+// return widget.groupHeaderBuilder!(element);
+// }
+// }