feat: group chat UI changes

This commit is contained in:
Freek van de Ven 2023-03-31 14:20:28 +02:00
parent b99bdfd081
commit 35d89faf04
4 changed files with 170 additions and 110 deletions

View file

@ -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,
]); ]);
}, },

View file

@ -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,
); );

View file

@ -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),
), ),
), ),

View file

@ -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,
], ),
), ],
); ),
);
}
} }