diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e402ef..25244d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Changed the ChatBottomInputSection to be multiline and go from 45px to 120px in height depending on how many lines are in the textfield - 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 ## 4.0.0 - Move to the new user story architecture diff --git a/packages/flutter_chat/lib/src/config/chat_options.dart b/packages/flutter_chat/lib/src/config/chat_options.dart index 7cf3a51..dfa7a08 100644 --- a/packages/flutter_chat/lib/src/config/chat_options.dart +++ b/packages/flutter_chat/lib/src/config/chat_options.dart @@ -1,3 +1,4 @@ +import "package:cached_network_image/cached_network_image.dart"; import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:flutter/material.dart"; import "package:flutter_chat/src/config/chat_builders.dart"; @@ -23,6 +24,7 @@ class ChatOptions { this.iconDisabledColor, this.chatAlignment, this.onNoChats, + this.imageProviderResolver = _defaultImageProviderResolver, ChatRepositoryInterface? chatRepository, UserRepositoryInterface? userRepository, }) : chatRepository = chatRepository ?? LocalChatRepository(), @@ -90,6 +92,11 @@ class ChatOptions { /// [onNoChats] is a function that is triggered when there are no chats. final Function? onNoChats; + + /// If [imageProviderResolver] is set, it will be used to get the images for + /// the images in the entire userstory. If not provided, CachedNetworkImage + /// will be used. + final ImageProviderResolver imageProviderResolver; } /// Typedef for the chatTitleResolver function that is used to get a title for @@ -100,6 +107,13 @@ typedef ChatTitleResolver = String? Function(ChatModel chat); /// a sender. typedef SenderTitleResolver = String? Function(UserModel? user); +/// Typedef for the imageProviderResolver function that is used to get images +/// for the userstory. +typedef ImageProviderResolver = ImageProvider Function( + BuildContext context, + Uri image, +); + /// Typedef for the messageThemeResolver function that is used to get a /// [MessageTheme] for a message. This can return null so you can fall back to /// default values for some messages. @@ -252,6 +266,12 @@ MessageTheme? _defaultMessageThemeResolver( ) => null; +ImageProvider _defaultImageProviderResolver( + BuildContext context, + Uri image, +) => + CachedNetworkImageProvider(image.toString()); + /// All configurable paddings and whitespaces within the userstory class ChatSpacing { /// Creates a ChatSpacing object diff --git a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_message_builder.dart b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_message_builder.dart index dd55e24..0bc9a74 100644 --- a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_message_builder.dart +++ b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/default_message_builder.dart @@ -1,4 +1,3 @@ -import "package:cached_network_image/cached_network_image.dart"; import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:flutter/material.dart"; import "package:flutter_chat/src/config/chat_options.dart"; @@ -241,7 +240,10 @@ class _DefaultChatImage extends StatelessWidget { @override Widget build(BuildContext context) { + var chatScope = ChatScope.of(context); + var options = chatScope.options; var textTheme = Theme.of(context).textTheme; + var imageUrl = message.imageUrl!; return Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: SizedBox( @@ -250,11 +252,12 @@ class _DefaultChatImage extends StatelessWidget { borderRadius: BorderRadius.circular(12), child: AnimatedSize( duration: const Duration(milliseconds: 300), - child: CachedNetworkImage( - imageUrl: message.imageUrl!, + child: Image( + image: + options.imageProviderResolver(context, Uri.parse(imageUrl)), fit: BoxFit.fitWidth, - errorWidget: (context, url, error) => Text( - "Something went wrong", + errorBuilder: (context, error, stackTrace) => Text( + "Something went wrong with loading the image", style: textTheme.bodyLarge?.copyWith( color: messageTheme.textColor, ), diff --git a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/old_message_builder.dart b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/old_message_builder.dart index 1ed8fcc..4ff2f46 100644 --- a/packages/flutter_chat/lib/src/screens/chat_detail/widgets/old_message_builder.dart +++ b/packages/flutter_chat/lib/src/screens/chat_detail/widgets/old_message_builder.dart @@ -1,4 +1,3 @@ -import "package:cached_network_image/cached_network_image.dart"; import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:flutter/material.dart"; import "package:flutter_chat/src/services/date_formatter.dart"; @@ -159,8 +158,11 @@ class OldChatMessageBuilder extends StatelessWidget { ], ) : message.isImageMessage - ? CachedNetworkImage( - imageUrl: message.imageUrl ?? "", + ? Image( + image: options.imageProviderResolver( + context, + Uri.parse(message.imageUrl!), + ), ) : const SizedBox.shrink(), ), @@ -182,20 +184,24 @@ class _ChatImage extends StatelessWidget { final String image; @override - Widget build(BuildContext context) => Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(40.0), - ), - width: 40, - height: 40, - child: image.isNotEmpty - ? CachedNetworkImage( - fadeInDuration: Duration.zero, - imageUrl: image, - fit: BoxFit.cover, - ) - : null, - ); + Widget build(BuildContext context) { + var chatScope = ChatScope.of(context); + var options = chatScope.options; + + return Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(40.0), + ), + width: 40, + height: 40, + child: image.isNotEmpty + ? Image( + fit: BoxFit.cover, + image: options.imageProviderResolver(context, Uri.parse(image)), + ) + : null, + ); + } }