mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-18 18:33:49 +02:00
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:
parent
7a30621ab9
commit
d60917028a
3 changed files with 202 additions and 225 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -86,46 +86,36 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var appBar = _AppBar(
|
||||
chatTitle: chatTitle,
|
||||
chatOptions: widget.chatOptions,
|
||||
onPressChatTitle: widget.onPressChatTitle,
|
||||
chatModel: widget.chat,
|
||||
);
|
||||
|
||||
var body = _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,
|
||||
);
|
||||
|
||||
if (widget.chatOptions.builders.baseScreenBuilder == null) {
|
||||
return Scaffold(
|
||||
appBar: _AppBar(
|
||||
chatTitle: chatTitle,
|
||||
chatOptions: widget.chatOptions,
|
||||
onPressChatTitle: widget.onPressChatTitle,
|
||||
chatModel: widget.chat,
|
||||
),
|
||||
body: _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: 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,16 +365,53 @@ class _ChatBottomState extends State<_ChatBottom> {
|
|||
var theme = Theme.of(context);
|
||||
|
||||
_textEditingController.addListener(() {
|
||||
if (_textEditingController.text.isEmpty) {
|
||||
setState(() {
|
||||
_isTyping = false;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_isTyping = true;
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
_isTyping = _textEditingController.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
|
||||
Future<void> sendMessage() async {
|
||||
setState(() {
|
||||
_isSending = true;
|
||||
});
|
||||
|
||||
var value = _textEditingController.text;
|
||||
if (value.isNotEmpty) {
|
||||
await widget.onMessageSubmit(value);
|
||||
_textEditingController.clear();
|
||||
}
|
||||
setState(() {
|
||||
_isSending = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> Function()? onClickSendMessage;
|
||||
if (_isTyping && !_isSending) {
|
||||
onClickSendMessage = () async => sendMessage();
|
||||
}
|
||||
|
||||
var messageSendButtons = Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
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: onClickSendMessage,
|
||||
icon: const Icon(
|
||||
Icons.send,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
|
@ -395,44 +422,7 @@ class _ChatBottomState extends State<_ChatBottom> {
|
|||
child: widget.options.builders.messageInputBuilder?.call(
|
||||
context,
|
||||
_textEditingController,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
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,123 +511,130 @@ class _ChatBubbleState extends State<_ChatBubble> {
|
|||
|
||||
var user = snapshot.data!;
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: isNewDate || isSameSender ? 25.0 : 0,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isNewDate || isSameSender) ...[
|
||||
InkWell(
|
||||
onTap: () => widget.onPressUserProfile(user),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: user.imageUrl?.isNotEmpty ?? false
|
||||
? _ChatImage(
|
||||
image: user.imageUrl!,
|
||||
)
|
||||
: widget.options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
40,
|
||||
) ??
|
||||
Avatar(
|
||||
key: ValueKey(user.id),
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl:
|
||||
user.imageUrl != "" ? user.imageUrl : null,
|
||||
),
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
const SizedBox(
|
||||
width: 50,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (isNewDate || isSameSender) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: widget.options.builders.usernameBuilder
|
||||
?.call(
|
||||
user.fullname ?? "",
|
||||
) ??
|
||||
Text(
|
||||
user.fullname ?? translations.anonymousUser,
|
||||
style: theme.textTheme.titleMedium,
|
||||
return widget.options.builders.chatMessageBuilder?.call(
|
||||
context,
|
||||
widget.message,
|
||||
widget.previousMessage,
|
||||
) ??
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: isNewDate || isSameSender ? 25.0 : 0,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isNewDate || isSameSender) ...[
|
||||
InkWell(
|
||||
onTap: () => widget.onPressUserProfile(user),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: user.imageUrl?.isNotEmpty ?? false
|
||||
? _ChatImage(
|
||||
image: user.imageUrl!,
|
||||
)
|
||||
: widget.options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
40,
|
||||
) ??
|
||||
Avatar(
|
||||
key: ValueKey(user.id),
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl: user.imageUrl != ""
|
||||
? user.imageUrl
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
dateFormatter.format(
|
||||
date: widget.message.timestamp,
|
||||
showFullDate: true,
|
||||
size: 40,
|
||||
),
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
const SizedBox(
|
||||
width: 50,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (isNewDate || isSameSender) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: widget.options.builders.usernameBuilder
|
||||
?.call(
|
||||
user.fullname ?? "",
|
||||
) ??
|
||||
Text(
|
||||
user.fullname ??
|
||||
translations.anonymousUser,
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
dateFormatter.format(
|
||||
date: widget.message.timestamp,
|
||||
showFullDate: true,
|
||||
),
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 3.0),
|
||||
child: widget.message.isTextMessage
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.message.text ?? "",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
if (widget.options.showTimes &&
|
||||
!isSameMinute &&
|
||||
!isNewDate &&
|
||||
!hasHeader)
|
||||
Text(
|
||||
dateFormatter
|
||||
.format(
|
||||
date: widget.message.timestamp,
|
||||
showFullDate: true,
|
||||
)
|
||||
.split(" ")
|
||||
.last,
|
||||
style: theme.textTheme.labelSmall,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
],
|
||||
)
|
||||
: widget.message.isImageMessage
|
||||
? CachedNetworkImage(
|
||||
imageUrl: widget.message.imageUrl ?? "",
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 3.0),
|
||||
child: widget.message.isTextMessage
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.message.text ?? "",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
if (widget.options.showTimes &&
|
||||
!isSameMinute &&
|
||||
!isNewDate &&
|
||||
!hasHeader)
|
||||
Text(
|
||||
dateFormatter
|
||||
.format(
|
||||
date: widget.message.timestamp,
|
||||
showFullDate: true,
|
||||
)
|
||||
.split(" ")
|
||||
.last,
|
||||
style: theme.textTheme.labelSmall,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
: widget.message.isImageMessage
|
||||
? CachedNetworkImage(
|
||||
imageUrl: widget.message.imageUrl ?? "",
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue