feat: add chatMessageBuilder to the chatoptions to override default behavior

With the chatMessageBuilder it is possible to run a null whenever you still want to use the default but only want to update the chat in very specific cases.
I also slightly refactored the chat_detail_screen.dart to remove duplicate code and make it more readable
This commit is contained in:
Freek van de Ven 2025-01-30 10:38:34 +01:00
parent 7a30621ab9
commit 3819943564
3 changed files with 202 additions and 225 deletions

View file

@ -1,5 +1,6 @@
## 5.0.0 - WIP
- Get the color for the imagepicker from the Theme's primaryColor
- Added chatMessageBuilder to the userstory configuration to customize the chat messages
## 4.0.0
- Move to the new user story architecture

View file

@ -17,6 +17,7 @@ class ChatBuilders {
this.newChatButtonBuilder,
this.noUsersPlaceholderBuilder,
this.chatTitleBuilder,
this.chatMessageBuilder,
this.usernameBuilder,
this.loadingWidgetBuilder,
});
@ -62,6 +63,9 @@ class ChatBuilders {
/// The chat title builder
final Widget Function(String chatTitle)? chatTitleBuilder;
/// The chat message builder
final ChatMessageBuilder? chatMessageBuilder;
/// The username builder
final Widget Function(String userFullName)? usernameBuilder;
@ -108,6 +112,17 @@ typedef ContainerBuilder = Widget Function(
Widget child,
);
/// The chat message builder
/// This builder is used to override the default chat message widget
/// If null is returned, the default chat message widget will be used so you can
/// override for specific cases
/// [previousMessage] is the previous message in the chat
typedef ChatMessageBuilder = Widget? Function(
BuildContext context,
MessageModel message,
MessageModel? previousMessage,
);
/// The group avatar builder
typedef GroupAvatarBuilder = Widget Function(
BuildContext context,

View file

@ -86,15 +86,14 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
@override
Widget build(BuildContext context) {
if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
var appBar = _AppBar(
chatTitle: chatTitle,
chatOptions: widget.chatOptions,
onPressChatTitle: widget.onPressChatTitle,
chatModel: widget.chat,
),
body: _Body(
);
var body = _Body(
chatService: widget.chatService,
options: widget.chatOptions,
chat: widget.chat,
@ -103,29 +102,20 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
onUploadImage: widget.onUploadImage,
onMessageSubmit: widget.onMessageSubmit,
onReadChat: widget.onReadChat,
),
);
if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: appBar,
body: body,
);
}
return widget.chatOptions.builders.baseScreenBuilder!.call(
context,
widget.mapScreenType,
_AppBar(
chatTitle: chatTitle,
chatOptions: widget.chatOptions,
onPressChatTitle: widget.onPressChatTitle,
chatModel: widget.chat,
),
_Body(
chatService: widget.chatService,
options: widget.chatOptions,
chat: widget.chat,
currentUserId: widget.userId,
onPressUserProfile: widget.onPressUserProfile,
onUploadImage: widget.onUploadImage,
onMessageSubmit: widget.onMessageSubmit,
onReadChat: widget.onReadChat,
),
appBar,
body,
);
}
}
@ -375,27 +365,32 @@ class _ChatBottomState extends State<_ChatBottom> {
var theme = Theme.of(context);
_textEditingController.addListener(() {
if (_textEditingController.text.isEmpty) {
setState(() {
_isTyping = false;
_isTyping = _textEditingController.text.isNotEmpty;
});
} else {
});
Future<void> sendMessage() async {
setState(() {
_isTyping = true;
_isSending = true;
});
var value = _textEditingController.text;
if (value.isNotEmpty) {
await widget.onMessageSubmit(value);
_textEditingController.clear();
}
setState(() {
_isSending = false;
});
}
});
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: SizedBox(
height: 45,
child: widget.options.builders.messageInputBuilder?.call(
context,
_textEditingController,
Row(
Future<void> Function()? onClickSendMessage;
if (_isTyping && !_isSending) {
onClickSendMessage = () async => sendMessage();
}
var messageSendButtons = Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
@ -409,30 +404,25 @@ class _ChatBottomState extends State<_ChatBottom> {
IconButton(
disabledColor: widget.options.iconDisabledColor,
color: widget.options.iconEnabledColor,
onPressed: _isTyping && !_isSending
? () async {
setState(() {
_isSending = true;
});
var value = _textEditingController.text;
if (value.isNotEmpty) {
await widget.onMessageSubmit(value);
_textEditingController.clear();
}
setState(() {
_isSending = false;
});
}
: null,
onPressed: onClickSendMessage,
icon: const Icon(
Icons.send,
),
),
],
);
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: SizedBox(
height: 45,
child: widget.options.builders.messageInputBuilder?.call(
context,
_textEditingController,
messageSendButtons,
widget.options.translations,
) ??
TextField(
@ -468,43 +458,7 @@ class _ChatBottomState extends State<_ChatBottom> {
),
borderSide: BorderSide.none,
),
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: widget.onPressSelectImage,
icon: Icon(
Icons.image_outlined,
color: widget.options.iconEnabledColor,
),
),
IconButton(
disabledColor: widget.options.iconDisabledColor,
color: widget.options.iconEnabledColor,
onPressed: _isTyping && !_isSending
? () async {
setState(() {
_isSending = true;
});
var value = _textEditingController.text;
if (value.isNotEmpty) {
await widget.onMessageSubmit(value);
_textEditingController.clear();
}
setState(() {
_isSending = false;
});
}
: null,
icon: const Icon(
Icons.send,
),
),
],
),
suffixIcon: messageSendButtons,
),
),
),
@ -557,7 +511,12 @@ class _ChatBubbleState extends State<_ChatBubble> {
var user = snapshot.data!;
return Padding(
return widget.options.builders.chatMessageBuilder?.call(
context,
widget.message,
widget.previousMessage,
) ??
Padding(
padding: EdgeInsets.only(
top: isNewDate || isSameSender ? 25.0 : 0,
),
@ -584,8 +543,9 @@ class _ChatBubbleState extends State<_ChatBubble> {
user: User(
firstName: user.firstName,
lastName: user.lastName,
imageUrl:
user.imageUrl != "" ? user.imageUrl : null,
imageUrl: user.imageUrl != ""
? user.imageUrl
: null,
),
size: 40,
),
@ -613,7 +573,8 @@ class _ChatBubbleState extends State<_ChatBubble> {
user.fullname ?? "",
) ??
Text(
user.fullname ?? translations.anonymousUser,
user.fullname ??
translations.anonymousUser,
style: theme.textTheme.titleMedium,
),
),