mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-19 10:53:51 +02:00
feat: group chat UI changes
This commit is contained in:
parent
b99bdfd081
commit
35d89faf04
4 changed files with 170 additions and 110 deletions
|
@ -18,13 +18,21 @@ class MyStatefulWidget extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
|
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
|
||||||
|
static final pietUser = ChatUserModel(
|
||||||
|
firstName: 'Piet',
|
||||||
|
lastName: 'Jansen',
|
||||||
|
imageUrl: 'https://xsgames.co/randomusers/avatar.php?g=female',
|
||||||
|
);
|
||||||
|
|
||||||
|
static final janUser = ChatUserModel(
|
||||||
|
firstName: 'Jan',
|
||||||
|
lastName: 'Jansen',
|
||||||
|
imageUrl: 'https://xsgames.co/randomusers/avatar.php?g=male',
|
||||||
|
);
|
||||||
|
|
||||||
static final messages = [
|
static final messages = [
|
||||||
ChatTextMessageModel(
|
ChatTextMessageModel(
|
||||||
sender: ChatUserModel(
|
sender: pietUser,
|
||||||
firstName: 'Piet',
|
|
||||||
lastName: 'Jansen',
|
|
||||||
imageUrl: 'https://xsgames.co/randomusers/avatar.php?g=female',
|
|
||||||
),
|
|
||||||
text: 'Hoe gaat het?',
|
text: 'Hoe gaat het?',
|
||||||
timestamp: DateTime.now(),
|
timestamp: DateTime.now(),
|
||||||
),
|
),
|
||||||
|
@ -48,13 +56,19 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
|
||||||
messages: messages,
|
messages: messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
Stream<List<PersonalChatModel>> get chatStream => (() {
|
static final groupChat = GroupChatModel(
|
||||||
late StreamController<List<PersonalChatModel>> controller;
|
title: 'Group chat',
|
||||||
controller = StreamController<List<PersonalChatModel>>(
|
imageUrl: 'https://xsgames.co/randomusers/avatar.php?g=male',
|
||||||
|
users: [pietUser, janUser],
|
||||||
|
messages: messages);
|
||||||
|
|
||||||
|
Stream<List<ChatModel>> get chatStream => (() {
|
||||||
|
late StreamController<List<ChatModel>> controller;
|
||||||
|
controller = StreamController<List<ChatModel>>(
|
||||||
onListen: () {
|
onListen: () {
|
||||||
controller.add([
|
controller.add([
|
||||||
chat,
|
chat,
|
||||||
chat,
|
groupChat,
|
||||||
chat,
|
chat,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,7 @@ class ChatOptions {
|
||||||
this.imagePickerContainerBuilder = _createImagePickerContainer,
|
this.imagePickerContainerBuilder = _createImagePickerContainer,
|
||||||
this.scaffoldBuilder = _createScaffold,
|
this.scaffoldBuilder = _createScaffold,
|
||||||
this.userAvatarBuilder = _createUserAvatar,
|
this.userAvatarBuilder = _createUserAvatar,
|
||||||
|
this.groupAvatarBuilder = _createGroupAvatar,
|
||||||
this.noChatsPlaceholderBuilder = _createNoChatsPlaceholder,
|
this.noChatsPlaceholderBuilder = _createNoChatsPlaceholder,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ class ChatOptions {
|
||||||
final ImagePickerContainerBuilder imagePickerContainerBuilder;
|
final ImagePickerContainerBuilder imagePickerContainerBuilder;
|
||||||
final ScaffoldBuilder scaffoldBuilder;
|
final ScaffoldBuilder scaffoldBuilder;
|
||||||
final UserAvatarBuilder userAvatarBuilder;
|
final UserAvatarBuilder userAvatarBuilder;
|
||||||
|
final GroupAvatarBuilder groupAvatarBuilder;
|
||||||
final NoChatsPlaceholderBuilder noChatsPlaceholderBuilder;
|
final NoChatsPlaceholderBuilder noChatsPlaceholderBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +103,14 @@ Widget _createUserAvatar(
|
||||||
image: user.imageUrl,
|
image: user.imageUrl,
|
||||||
size: size,
|
size: size,
|
||||||
);
|
);
|
||||||
|
Widget _createGroupAvatar(
|
||||||
|
String imageUrl,
|
||||||
|
double size,
|
||||||
|
) =>
|
||||||
|
ChatImage(
|
||||||
|
image: imageUrl,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
|
||||||
Widget _createNoChatsPlaceholder(
|
Widget _createNoChatsPlaceholder(
|
||||||
ChatTranslations translations,
|
ChatTranslations translations,
|
||||||
|
@ -147,6 +157,11 @@ typedef UserAvatarBuilder = Widget Function(
|
||||||
double size,
|
double size,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef GroupAvatarBuilder = Widget Function(
|
||||||
|
String imageUrl,
|
||||||
|
double size,
|
||||||
|
);
|
||||||
|
|
||||||
typedef NoChatsPlaceholderBuilder = Widget Function(
|
typedef NoChatsPlaceholderBuilder = Widget Function(
|
||||||
ChatTranslations translations,
|
ChatTranslations translations,
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ChatDetailScreen extends StatelessWidget {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final PersonalChatModel? chat;
|
final ChatModel? chat;
|
||||||
final ChatOptions options;
|
final ChatOptions options;
|
||||||
final ChatTranslations translations;
|
final ChatTranslations translations;
|
||||||
final Stream<List<ChatMessageModel>>? chatMessages;
|
final Stream<List<ChatMessageModel>>? chatMessages;
|
||||||
|
@ -64,15 +64,27 @@ class ChatDetailScreen extends StatelessWidget {
|
||||||
children: chat == null
|
children: chat == null
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
options.userAvatarBuilder(
|
if (chat is GroupChatModel) ...[
|
||||||
chat!.user,
|
options.groupAvatarBuilder(
|
||||||
36.0,
|
(chat! as GroupChatModel).imageUrl,
|
||||||
),
|
36.0,
|
||||||
|
),
|
||||||
|
] else if (chat is PersonalChatModel) ...[
|
||||||
|
options.userAvatarBuilder(
|
||||||
|
(chat! as PersonalChatModel).user,
|
||||||
|
36.0,
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
...[],
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 15.5),
|
padding: const EdgeInsets.only(left: 15.5),
|
||||||
child: Text(
|
child: Text(
|
||||||
chat!.user.fullName,
|
(chat is GroupChatModel)
|
||||||
|
? (chat! as GroupChatModel).title
|
||||||
|
: (chat is PersonalChatModel)
|
||||||
|
? (chat! as PersonalChatModel).user.fullName
|
||||||
|
: '',
|
||||||
style: const TextStyle(fontSize: 18),
|
style: const TextStyle(fontSize: 18),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -19,10 +19,10 @@ class ChatScreen extends StatefulWidget {
|
||||||
|
|
||||||
final ChatOptions options;
|
final ChatOptions options;
|
||||||
final ChatTranslations translations;
|
final ChatTranslations translations;
|
||||||
final Stream<List<PersonalChatModel>> chats;
|
final Stream<List<ChatModel>> chats;
|
||||||
final VoidCallback? onPressStartChat;
|
final VoidCallback? onPressStartChat;
|
||||||
final void Function(PersonalChatModel chat) onDeleteChat;
|
final void Function(ChatModel chat) onDeleteChat;
|
||||||
final void Function(PersonalChatModel chat) onPressChat;
|
final void Function(ChatModel chat) onPressChat;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatScreen> createState() => _ChatScreenState();
|
State<ChatScreen> createState() => _ChatScreenState();
|
||||||
|
@ -32,109 +32,128 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
final DateFormatter _dateFormatter = DateFormatter();
|
final DateFormatter _dateFormatter = DateFormatter();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => widget.options.scaffoldBuilder(
|
Widget build(BuildContext context) {
|
||||||
AppBar(
|
return widget.options.scaffoldBuilder(
|
||||||
title: Text(widget.translations.chatsTitle),
|
AppBar(
|
||||||
),
|
title: Text(widget.translations.chatsTitle),
|
||||||
Column(
|
),
|
||||||
children: [
|
Column(
|
||||||
Expanded(
|
children: [
|
||||||
child: ListView(
|
Expanded(
|
||||||
padding: const EdgeInsets.only(top: 15.0),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 15.0),
|
||||||
StreamBuilder<List<PersonalChatModel>>(
|
children: [
|
||||||
stream: widget.chats,
|
StreamBuilder<List<ChatModel>>(
|
||||||
builder: (BuildContext context, snapshot) => Column(
|
stream: widget.chats,
|
||||||
children: [
|
builder: (BuildContext context, snapshot) => Column(
|
||||||
for (PersonalChatModel chat in snapshot.data ?? [])
|
children: [
|
||||||
Builder(
|
for (ChatModel chat in snapshot.data ?? []) ...[
|
||||||
builder: (context) => Dismissible(
|
Builder(
|
||||||
confirmDismiss: (_) => showDialog(
|
builder: (context) => Dismissible(
|
||||||
context: context,
|
confirmDismiss: (_) => showDialog(
|
||||||
builder: (BuildContext context) => AlertDialog(
|
context: context,
|
||||||
title: Text(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
widget.translations.deleteChatModalTitle,
|
title: Text(
|
||||||
),
|
widget.translations.deleteChatModalTitle,
|
||||||
content: Text(
|
|
||||||
widget.translations
|
|
||||||
.deleteChatModalDescription,
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
child: Text(
|
|
||||||
widget
|
|
||||||
.translations.deleteChatModalCancel,
|
|
||||||
),
|
|
||||||
onPressed: () =>
|
|
||||||
Navigator.of(context).pop(false),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () =>
|
|
||||||
Navigator.of(context).pop(true),
|
|
||||||
child: Text(
|
|
||||||
widget.translations
|
|
||||||
.deleteChatModalConfirm,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
content: Text(
|
||||||
onDismissed: (_) => widget.onDeleteChat(chat),
|
widget
|
||||||
background: Container(
|
.translations.deleteChatModalDescription,
|
||||||
color: Colors.red,
|
),
|
||||||
child: Align(
|
actions: [
|
||||||
alignment: Alignment.centerRight,
|
TextButton(
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.translations.deleteChatButton,
|
widget.translations.deleteChatModalCancel,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).pop(false),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).pop(true),
|
||||||
|
child: Text(
|
||||||
|
widget
|
||||||
|
.translations.deleteChatModalConfirm,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
key: Key(
|
),
|
||||||
chat.id.toString(),
|
onDismissed: (_) => widget.onDeleteChat(chat),
|
||||||
),
|
background: Container(
|
||||||
child: GestureDetector(
|
color: Colors.red,
|
||||||
onTap: () => widget.onPressChat(chat),
|
child: Align(
|
||||||
child: widget.options.chatRowContainerBuilder(
|
alignment: Alignment.centerRight,
|
||||||
ChatRow(
|
child: Padding(
|
||||||
avatar: widget.options.userAvatarBuilder(
|
padding: const EdgeInsets.all(8.0),
|
||||||
chat.user,
|
child: Text(
|
||||||
40.0,
|
widget.translations.deleteChatButton,
|
||||||
),
|
|
||||||
title: chat.user.fullName,
|
|
||||||
subTitle: chat.lastMessage != null
|
|
||||||
? chat.lastMessage
|
|
||||||
is ChatTextMessageModel
|
|
||||||
? (chat.lastMessage!
|
|
||||||
as ChatTextMessageModel)
|
|
||||||
.text
|
|
||||||
: '📷 ${widget.translations.image}'
|
|
||||||
: null,
|
|
||||||
lastUsed: chat.lastUsed != null
|
|
||||||
? _dateFormatter.format(
|
|
||||||
date: chat.lastUsed!,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
key: ValueKey(
|
||||||
|
chat.id.toString(),
|
||||||
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => widget.onPressChat(chat),
|
||||||
|
child: widget.options.chatRowContainerBuilder(
|
||||||
|
(chat is PersonalChatModel)
|
||||||
|
? ChatRow(
|
||||||
|
avatar:
|
||||||
|
widget.options.userAvatarBuilder(
|
||||||
|
chat.user,
|
||||||
|
40.0,
|
||||||
|
),
|
||||||
|
title: chat.user.fullName,
|
||||||
|
subTitle: chat.lastMessage != null &&
|
||||||
|
chat.lastMessage
|
||||||
|
is ChatTextMessageModel
|
||||||
|
? (chat.lastMessage!
|
||||||
|
as ChatTextMessageModel)
|
||||||
|
.text
|
||||||
|
: '📷 ${widget.translations.image}',
|
||||||
|
lastUsed: chat.lastUsed != null
|
||||||
|
? _dateFormatter.format(
|
||||||
|
date: chat.lastUsed!,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
)
|
||||||
|
: ChatRow(
|
||||||
|
title: (chat as GroupChatModel).title,
|
||||||
|
subTitle: chat.lastMessage != null &&
|
||||||
|
chat.lastMessage
|
||||||
|
is ChatTextMessageModel
|
||||||
|
? (chat.lastMessage!
|
||||||
|
as ChatTextMessageModel)
|
||||||
|
.text
|
||||||
|
: '📷 '
|
||||||
|
'${widget.translations.image}',
|
||||||
|
avatar:
|
||||||
|
widget.options.groupAvatarBuilder(
|
||||||
|
chat.imageUrl,
|
||||||
|
40.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
if (widget.onPressStartChat != null)
|
),
|
||||||
widget.options.newChatButtonBuilder(
|
if (widget.onPressStartChat != null)
|
||||||
context,
|
widget.options.newChatButtonBuilder(
|
||||||
widget.onPressStartChat!,
|
context,
|
||||||
widget.translations,
|
widget.onPressStartChat!,
|
||||||
),
|
widget.translations,
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue