Skip to content

Commit

Permalink
refactor: ♻️ removed grouped_list and implement group separator for chat
Browse files Browse the repository at this point in the history
- Removed grouped_list package and implement the same group separator for message when date differ in chat.
  • Loading branch information
apurva010 committed May 31, 2024
1 parent aa04ada commit fdda6d2
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 73 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## [1.3.2] (Unreleased)

* **Feat**: [173](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/pull/173) Added
callback to sort message in chat.
* **Fix**: [139](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/issues/139) Added
support to customize view for the reply of any message.
* **Fix**: [174](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/issues/174) Fix
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,22 @@ ChatView(
```


30. Added callback `messageSorter` to sort message in `ChatBackgroundConfiguration`.
```dart
ChatView(
...
chatBackgroundConfig: ChatBackgroundConfiguration(
...
messageSorter: (message1, message2) {
return message1.message.compareTo(message2.message);
}
...
),
...
),
```


## How to use

Expand Down
6 changes: 5 additions & 1 deletion lib/src/models/message_list_configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
* SOFTWARE.
*/
import 'package:flutter/material.dart';
import 'package:grouped_list/grouped_list.dart';

import '../utils/constants/constants.dart';
import '../values/enumaration.dart';
import '../values/typedefs.dart';

class ChatBackgroundConfiguration {
Expand Down Expand Up @@ -71,6 +71,9 @@ class ChatBackgroundConfiguration {
/// message.
final Curve messageTimeAnimationCurve;

/// Provides callback to sort message
final MessageSorter? messageSorter;

const ChatBackgroundConfiguration({
this.defaultGroupSeparatorConfig,
this.backgroundColor,
Expand All @@ -86,6 +89,7 @@ class ChatBackgroundConfiguration {
this.messageTimeIconColor,
this.loadingWidget,
this.messageTimeAnimationCurve = Curves.decelerate,
this.messageSorter,
});
}

Expand Down
8 changes: 8 additions & 0 deletions lib/src/values/enumaration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ extension ChatViewStateExtension on ChatViewState {

bool get noMessages => this == ChatViewState.noData;
}

enum GroupedListOrder { ASC, DESC }

extension GroupedListOrderExtension on GroupedListOrder {
bool get isAsc => this == GroupedListOrder.ASC;

bool get isDesc => this == GroupedListOrder.DESC;
}
4 changes: 4 additions & 0 deletions lib/src/values/typedefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ typedef ReactedUserCallback = void Function(

/// customMessageType view for a reply of custom message type
typedef CustomMessageReplyViewBuilder = Widget Function(ReplyMessage state);
typedef MessageSorter = int Function(
Message message1,
Message message2,
);

/// customView for replying to any message
typedef CustomViewForReplyMessage = Widget Function(
Expand Down
201 changes: 130 additions & 71 deletions lib/src/widgets/chat_groupedlist_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import 'package:chatview/src/extensions/extensions.dart';
import 'package:chatview/src/widgets/chat_view_inherited_widget.dart';
import 'package:chatview/src/widgets/type_indicator_widget.dart';
import 'package:flutter/material.dart';
import 'package:grouped_list/grouped_list.dart';

import 'chat_bubble_widget.dart';
import 'chat_group_header.dart';
Expand Down Expand Up @@ -287,82 +286,142 @@ class _ChatGroupedListWidgetState extends State<ChatGroupedListWidget>
return StreamBuilder<List<Message>>(
stream: chatController?.messageStreamController.stream,
builder: (context, snapshot) {
return snapshot.connectionState.isActive
? GroupedListView<Message, DateTime>(
shrinkWrap: true,
elements: snapshot.data!,
groupBy: (message) {
/// If the conversation is ongoing on the same date,
/// return the same date [lastMatchedDate].
/// When the conversation starts on a new date,
/// we assign the new date [message.createdAt]
/// to [lastMatchedDate].
return lastMatchedDate =
lastMatchedDate.getDateFromDateTime ==
message.createdAt.getDateFromDateTime
? lastMatchedDate
: message.createdAt;
},
itemComparator: (message1, message2) =>
message1.message.compareTo(message2.message),
physics: const NeverScrollableScrollPhysics(),
order: chatBackgroundConfig.groupedListOrder,
sort: chatBackgroundConfig.sortEnable,
groupSeparatorBuilder: (separator) =>
featureActiveConfig?.enableChatSeparator ?? false
? _GroupSeparatorBuilder(
separator: separator,
defaultGroupSeparatorConfig: chatBackgroundConfig
.defaultGroupSeparatorConfig,
groupSeparatorBuilder:
chatBackgroundConfig.groupSeparatorBuilder,
)
: const SizedBox.shrink(),
indexedItemBuilder: (context, message, index) {
return ValueListenableBuilder<String?>(
valueListenable: _replyId,
builder: (context, state, child) {
return ChatBubbleWidget(
key: message.key,
messageTimeTextStyle:
chatBackgroundConfig.messageTimeTextStyle,
messageTimeIconColor:
chatBackgroundConfig.messageTimeIconColor,
message: message,
messageConfig: widget.messageConfig,
chatBubbleConfig: chatBubbleConfig,
profileCircleConfig: profileCircleConfig,
swipeToReplyConfig: widget.swipeToReplyConfig,
repliedMessageConfig: widget.repliedMessageConfig,
slideAnimation: _slideAnimation,
onLongPress: (yCoordinate, xCoordinate) =>
widget.onChatBubbleLongPress(
yCoordinate,
xCoordinate,
message,
),
onSwipe: widget.assignReplyMessage,
shouldHighlight: state == message.id,
onReplyTap: widget
.repliedMessageConfig
?.repliedMsgAutoScrollConfig
.enableScrollToRepliedMsg ??
false
? (replyId) => _onReplyTap(replyId, snapshot.data)
: null,
);
},
if (snapshot.connectionState.isActive) {
final messages = widget.chatBackgroundConfig.sortEnable
? sortMessage(snapshot.data!)
: snapshot.data!;
var count = 0;

/// Holds index and separator mapping to display in chat
Map<int, DateTime> messageSeparator = {};

for (var i = 0; (i < snapshot.data!.length); i++) {
if (messageSeparator.isEmpty) {
/// Separator for initial message
messageSeparator[0] = messages[0].createdAt;
continue;
}
lastMatchedDate = _groupBy(
messages[i],
lastMatchedDate,
);
var previousDate = _groupBy(
messages[i - 1],
lastMatchedDate,
);

if (previousDate != lastMatchedDate) {
/// Group separator when previous message and
/// current message time differ
count++;

messageSeparator[i + count] = messages[i].createdAt;
}
}
count = 0;
return ListView.builder(
key: widget.key,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: messages.length + messageSeparator.length,
itemBuilder: (context, index) {
var newIndex = index - count;

/// Check [messageSeparator] contains group separator for [index]
if (messageSeparator.containsKey(index)) {
count++;
return _groupSeparator(
messageSeparator[index]!,
);
}

return ValueListenableBuilder<String?>(
valueListenable: _replyId,
builder: (context, state, child) {
final message = messages[newIndex];
return ChatBubbleWidget(
key: message.key,
messageTimeTextStyle:
chatBackgroundConfig.messageTimeTextStyle,
messageTimeIconColor:
chatBackgroundConfig.messageTimeIconColor,
message: message,
messageConfig: widget.messageConfig,
chatBubbleConfig: chatBubbleConfig,
profileCircleConfig: profileCircleConfig,
swipeToReplyConfig: widget.swipeToReplyConfig,
repliedMessageConfig: widget.repliedMessageConfig,
slideAnimation: _slideAnimation,
onLongPress: (yCoordinate, xCoordinate) =>
widget.onChatBubbleLongPress(
yCoordinate,
xCoordinate,
message,
),
onSwipe: widget.assignReplyMessage,
shouldHighlight: state == message.id,
onReplyTap: widget
.repliedMessageConfig
?.repliedMsgAutoScrollConfig
.enableScrollToRepliedMsg ??
false
? (replyId) => _onReplyTap(replyId, snapshot.data)
: null,
);
},
)
: Center(
child: chatBackgroundConfig.loadingWidget ??
const CircularProgressIndicator(),
);
},
);
} else {
return Center(
child: chatBackgroundConfig.loadingWidget ??
const CircularProgressIndicator(),
);
}
},
);
}

List<Message> sortMessage(List<Message> messages) {
final elements = [...messages];

elements.sort(
widget.chatBackgroundConfig.messageSorter ??
(a, b) => a.createdAt.compareTo(b.createdAt),
);
if (widget.chatBackgroundConfig.groupedListOrder.isAsc) {
return elements;
} else {
return elements.reversed.toList();
}
}

/// return DateTime by checking lastMatchedDate and message created DateTime
DateTime _groupBy(
Message message,
DateTime lastMatchedDate,
) {
/// If the conversation is ongoing on the same date,
/// return the same date [lastMatchedDate].
/// When the conversation starts on a new date,
/// we are returning new date [message.createdAt].
return lastMatchedDate.getDateFromDateTime ==
message.createdAt.getDateFromDateTime
? lastMatchedDate
: message.createdAt;
}

Widget _groupSeparator(DateTime createdAt) {
return featureActiveConfig?.enableChatSeparator ?? false
? _GroupSeparatorBuilder(
separator: createdAt,
defaultGroupSeparatorConfig:
chatBackgroundConfig.defaultGroupSeparatorConfig,
groupSeparatorBuilder: chatBackgroundConfig.groupSeparatorBuilder,
)
: const SizedBox.shrink();
}
}

class _GroupSeparatorBuilder extends StatelessWidget {
Expand Down
1 change: 0 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ environment:
dependencies:
flutter:
sdk: flutter
grouped_list: ^5.1.2
intl: ^0.19.0
url_launcher: ^6.1.14
emoji_picker_flutter: ^2.1.1
Expand Down

0 comments on commit fdda6d2

Please sign in to comment.