From 313ead2029703912d9a28e3cb62c6e7a7cbe50b5 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 20 Feb 2025 16:43:41 +0100 Subject: [PATCH] feat: improve loading visualisation on the chatdetailscreen --- CHANGELOG.md | 1 + .../lib/src/config/chat_builders.dart | 13 ++++--- .../lib/src/config/chat_options.dart | 2 +- .../chat_detail/chat_detail_screen.dart | 37 +++++++++++-------- .../chat_detail/widgets/chat_bottom.dart | 21 +++++++---- .../chat_detail/widgets/default_loader.dart | 9 +---- 6 files changed, 47 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25244d9..5692a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Added chatScreenBuilder to the userstory configuration to customize the specific chat screen with a ChatModel as argument - Added senderTitleResolver to the ChatOptions to resolve the title of the sender in the chat message - Added imageProviderResolver to the ChatOptions to resolve ImageProvider for all images in the userstory +- Added enabled boolean to the messageInputBuilder and made parameters named ## 4.0.0 - Move to the new user story architecture diff --git a/packages/flutter_chat/lib/src/config/chat_builders.dart b/packages/flutter_chat/lib/src/config/chat_builders.dart index 9928b0b..026c15c 100644 --- a/packages/flutter_chat/lib/src/config/chat_builders.dart +++ b/packages/flutter_chat/lib/src/config/chat_builders.dart @@ -122,12 +122,13 @@ typedef ImagePickerBuilder = Future Function( /// The text input builder typedef TextInputBuilder = Widget Function( - BuildContext context, - TextEditingController textEditingController, - Widget suffixIcon, - ChatTranslations translations, - VoidCallback onSubmit, -); + BuildContext context, { + required TextEditingController textEditingController, + required Widget suffixIcon, + required ChatTranslations translations, + required VoidCallback onSubmit, + required bool enabled, +}); /// The base screen builder /// [title] is the title of the screen and can be null while loading diff --git a/packages/flutter_chat/lib/src/config/chat_options.dart b/packages/flutter_chat/lib/src/config/chat_options.dart index dfa7a08..647ed79 100644 --- a/packages/flutter_chat/lib/src/config/chat_options.dart +++ b/packages/flutter_chat/lib/src/config/chat_options.dart @@ -11,7 +11,7 @@ class ChatOptions { ChatOptions({ this.dateformat, this.groupChatEnabled = true, - this.enableLoadingIndicator = false, + this.enableLoadingIndicator = true, this.translations = const ChatTranslations.empty(), this.builders = const ChatBuilders(), this.spacing = const ChatSpacing(), diff --git a/packages/flutter_chat/lib/src/screens/chat_detail/chat_detail_screen.dart b/packages/flutter_chat/lib/src/screens/chat_detail/chat_detail_screen.dart index 837f27f..d7eb15e 100644 --- a/packages/flutter_chat/lib/src/screens/chat_detail/chat_detail_screen.dart +++ b/packages/flutter_chat/lib/src/screens/chat_detail/chat_detail_screen.dart @@ -69,6 +69,10 @@ class ChatDetailScreen extends HookWidget { var usersSnapshot = useStream(allUsersStream); var allUsers = usersSnapshot.data ?? []; + var chatIsloading = + chatSnapshot.connectionState == ConnectionState.waiting || + usersSnapshot.connectionState == ConnectionState.waiting; + useEffect( () { if (chat == null) return; @@ -106,6 +110,7 @@ class ChatDetailScreen extends HookWidget { onUploadImage: onUploadImage, onMessageSubmit: onMessageSubmit, onReadChat: onReadChat, + chatIsLoading: chatIsloading, ); if (options.builders.chatScreenBuilder != null) { @@ -231,6 +236,7 @@ class _ChatBody extends HookWidget { required this.onUploadImage, required this.onMessageSubmit, required this.onReadChat, + required this.chatIsLoading, }); final String chatId; @@ -240,6 +246,7 @@ class _ChatBody extends HookWidget { final Function(Uint8List image) onUploadImage; final Function(String text) onMessageSubmit; final Function(ChatModel chat) onReadChat; + final bool chatIsLoading; @override Widget build(BuildContext context) { @@ -377,11 +384,6 @@ class _ChatBody extends HookWidget { [autoScrollEnabled.value], ); - if (chat == null) { - if (!options.enableLoadingIndicator) return const SizedBox.shrink(); - return options.builders.loadingWidgetBuilder.call(context); - } - var userMap = {}; for (var u in chatUsers) { userMap[u.id] = u; @@ -424,18 +426,23 @@ class _ChatBody extends HookWidget { return Column( children: [ - Expanded( - child: ListView.builder( - reverse: false, - controller: scrollController, - physics: const AlwaysScrollableScrollPhysics(), - padding: const EdgeInsets.only(top: 24), - itemCount: listViewChildren.length, - itemBuilder: (context, index) => listViewChildren[index], + if (chatIsLoading && options.enableLoadingIndicator) ...[ + Expanded(child: options.builders.loadingWidgetBuilder.call(context)), + ] else ...[ + Expanded( + child: ListView.builder( + reverse: false, + controller: scrollController, + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.only(top: 24), + itemCount: listViewChildren.length, + itemBuilder: (context, index) => listViewChildren[index], + ), ), - ), + ], ChatBottomInputSection( - chat: chat!, + chat: chat, + isLoading: chatIsLoading, onPressSelectImage: () async => onPressSelectImage( context, options, diff --git a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/chat_bottom.dart b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/chat_bottom.dart index 673f9c3..5daef17 100644 --- a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/chat_bottom.dart +++ b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/chat_bottom.dart @@ -8,13 +8,18 @@ class ChatBottomInputSection extends HookWidget { /// Creates a new [ChatBottomInputSection]. const ChatBottomInputSection({ required this.chat, + required this.isLoading, required this.onMessageSubmit, this.onPressSelectImage, super.key, }); /// The chat model. - final ChatModel chat; + final ChatModel? chat; + + /// Whether the chat is still loading. + /// The inputfield is disabled when the chat is loading. + final bool isLoading; /// Callback function invoked when a message is submitted. final Function(String text) onMessageSubmit; @@ -65,7 +70,7 @@ class ChatBottomInputSection extends HookWidget { children: [ IconButton( alignment: Alignment.bottomRight, - onPressed: onPressSelectImage, + onPressed: isLoading ? null : onPressSelectImage, icon: Icon( Icons.image_outlined, color: options.iconEnabledColor, @@ -75,7 +80,7 @@ class ChatBottomInputSection extends HookWidget { alignment: Alignment.bottomRight, disabledColor: options.iconDisabledColor, color: options.iconEnabledColor, - onPressed: onClickSendMessage, + onPressed: isLoading ? null : onClickSendMessage, icon: const Icon(Icons.send_rounded), ), ], @@ -95,6 +100,7 @@ class ChatBottomInputSection extends HookWidget { keyboardType: TextInputType.multiline, maxLines: null, controller: textController, + enabled: !isLoading, decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(25), @@ -141,10 +147,11 @@ class ChatBottomInputSection extends HookWidget { constraints: const BoxConstraints(maxHeight: 120, minHeight: 45), child: options.builders.messageInputBuilder?.call( context, - textController, - messageSendButtons, - options.translations, - onSubmitField, + textEditingController: textController, + suffixIcon: messageSendButtons, + translations: options.translations, + onSubmit: onSubmitField, + enabled: !isLoading, ) ?? defaultInputField, ), diff --git a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_loader.dart b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_loader.dart index faa5f56..f5cb1a4 100644 --- a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_loader.dart +++ b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_loader.dart @@ -11,13 +11,8 @@ class DefaultChatLoadingOverlay extends StatelessWidget { const DefaultChatLoadingOverlay(); @override - Widget build(BuildContext context) => const Column( - children: [ - SizedBox(height: 12), - Center(child: CircularProgressIndicator()), - SizedBox(height: 12), - ], - ); + Widget build(BuildContext context) => + const Center(child: CircularProgressIndicator()); } /// A small row spinner item to show partial loading