feat: more implementation.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -57,6 +57,8 @@ class AttachmentWidgetCatalog {
|
||||
extension on List<Attachment> {
|
||||
/// Groups the attachments by their type.
|
||||
Map<String, List<Attachment>> get grouped {
|
||||
return groupBy(this, (attachment) => attachment.type!);
|
||||
return groupBy(where((it) {
|
||||
return it.type != null;
|
||||
}), (attachment) => attachment.type!);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-10
@@ -1,20 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/file_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/gallery_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/giphy_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/image_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/giphy_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/image_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/video_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/media_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/url_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/video_attachment.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/src/utils/utils.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import '../attachment_upload_state_builder.dart';
|
||||
|
||||
part 'fallback_attachment_builder.dart';
|
||||
|
||||
part 'file_attachment_builder.dart';
|
||||
@@ -82,41 +75,58 @@ abstract class StreamAttachmentWidgetBuilder {
|
||||
/// widget.
|
||||
static List<StreamAttachmentWidgetBuilder> defaultBuilders({
|
||||
required Message message,
|
||||
ShapeBorder? shape,
|
||||
EdgeInsetsGeometry padding = const EdgeInsets.all(4),
|
||||
StreamAttachmentWidgetTapCallback? onAttachmentTap,
|
||||
}) {
|
||||
return [
|
||||
// Handles a mix of image, gif, video, and file attachments.
|
||||
// Handles a mix of image, gif, video, url and file attachments.
|
||||
MixedAttachmentBuilder(
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
// Handles a mix of image, gif, and video attachments.
|
||||
GalleryAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
runSpacing: padding.vertical / 2,
|
||||
spacing: padding.horizontal / 2,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
// Handles file attachments.
|
||||
FileAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
// Handles giphy attachments.
|
||||
GiphyAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
// Handles image attachments.
|
||||
ImageAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
// Handles video attachments.
|
||||
VideoAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
// We don't handle URL attachments if the message is a reply.
|
||||
if (message.quotedMessage == null)
|
||||
UrlAttachmentBuilder(
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
|
||||
|
||||
+6
-32
@@ -93,16 +93,6 @@ class GalleryAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
attachments: galleryAttachments,
|
||||
itemBuilder: (context, index) {
|
||||
final attachment = galleryAttachments[index];
|
||||
final attachmentType = attachment.type;
|
||||
|
||||
final isImage = attachmentType == AttachmentType.image;
|
||||
final isVideo = attachmentType == AttachmentType.video;
|
||||
final isGiphy = attachmentType == AttachmentType.giphy;
|
||||
|
||||
assert(
|
||||
isImage || isVideo || isGiphy,
|
||||
'Attachment type should be image, video or giphy',
|
||||
);
|
||||
|
||||
VoidCallback? onTap;
|
||||
if (onAttachmentTap != null) {
|
||||
@@ -112,29 +102,13 @@ class GalleryAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
if (isImage)
|
||||
StreamImageAttachmentThumbnail(
|
||||
image: attachment,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
else if (isVideo)
|
||||
StreamVideoAttachmentThumbnail(
|
||||
video: attachment,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
else if (isGiphy)
|
||||
StreamGiphyAttachmentThumbnail(
|
||||
giphy: attachment,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
StreamMediaAttachmentThumbnail(
|
||||
media: attachment,
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxHeight,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: StreamAttachmentUploadStateBuilder(
|
||||
|
||||
@@ -62,6 +62,7 @@ class ImageAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: StreamImageAttachment(
|
||||
shape: shape,
|
||||
message: message,
|
||||
constraints: constraints,
|
||||
image: image,
|
||||
|
||||
+35
-27
@@ -3,67 +3,71 @@ part of 'attachment_widget_builder.dart';
|
||||
/// {@template mixedAttachmentBuilder}
|
||||
/// A widget builder for Mixed attachment type.
|
||||
///
|
||||
/// This builder is used when a message contains both image/video/giphy and file
|
||||
/// attachments.
|
||||
/// This builder is used when a message contains a mix of media type and file
|
||||
/// or url preview attachments.
|
||||
///
|
||||
/// This builder will render first image/video/giphy attachment and then render
|
||||
/// the file attachments.
|
||||
/// This builder will render first the url preview or file attachment and then
|
||||
/// the media attachments.
|
||||
/// {@endtemplate}
|
||||
class MixedAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
/// {@macro mixedAttachmentBuilder}
|
||||
MixedAttachmentBuilder({
|
||||
this.shape,
|
||||
this.padding = const EdgeInsets.all(4),
|
||||
this.onAttachmentTap,
|
||||
StreamAttachmentWidgetTapCallback? onAttachmentTap,
|
||||
}) : _imageAttachmentBuilder = ImageAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
padding: EdgeInsets.symmetric(horizontal: padding.horizontal),
|
||||
),
|
||||
_videoAttachmentBuilder = VideoAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
padding: EdgeInsets.symmetric(horizontal: padding.horizontal),
|
||||
),
|
||||
_giphyAttachmentBuilder = GiphyAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
padding: EdgeInsets.symmetric(horizontal: padding.horizontal),
|
||||
),
|
||||
_galleryAttachmentBuilder = GalleryAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
padding: EdgeInsets.symmetric(horizontal: padding.horizontal),
|
||||
),
|
||||
_fileAttachmentBuilder = FileAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
),
|
||||
_urlAttachmentBuilder = UrlAttachmentBuilder(
|
||||
padding: EdgeInsets.zero,
|
||||
onAttachmentTap: onAttachmentTap,
|
||||
padding: EdgeInsets.symmetric(horizontal: padding.horizontal),
|
||||
);
|
||||
|
||||
/// The shape of the gallery attachment.
|
||||
final ShapeBorder? shape;
|
||||
|
||||
/// The padding to apply to the gallery attachment widget.
|
||||
/// The padding to apply to the mixed attachment widget.
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
/// The callback to call when the attachment is tapped.
|
||||
final StreamAttachmentWidgetTapCallback? onAttachmentTap;
|
||||
|
||||
late final StreamAttachmentWidgetBuilder _imageAttachmentBuilder;
|
||||
late final StreamAttachmentWidgetBuilder _videoAttachmentBuilder;
|
||||
late final StreamAttachmentWidgetBuilder _giphyAttachmentBuilder;
|
||||
late final StreamAttachmentWidgetBuilder _galleryAttachmentBuilder;
|
||||
late final StreamAttachmentWidgetBuilder _fileAttachmentBuilder;
|
||||
late final StreamAttachmentWidgetBuilder _urlAttachmentBuilder;
|
||||
|
||||
@override
|
||||
bool canHandle(
|
||||
Message message,
|
||||
Map<String, List<Attachment>> attachments,
|
||||
) {
|
||||
final containsImage = attachments.keys.contains(AttachmentType.image);
|
||||
final containsVideo = attachments.keys.contains(AttachmentType.video);
|
||||
final containsGiphy = attachments.keys.contains(AttachmentType.giphy);
|
||||
final containsFile = attachments.keys.contains(AttachmentType.file);
|
||||
final types = attachments.keys;
|
||||
|
||||
final containsImage = types.contains(AttachmentType.image);
|
||||
final containsVideo = types.contains(AttachmentType.video);
|
||||
final containsGiphy = types.contains(AttachmentType.giphy);
|
||||
final containsFile = types.contains(AttachmentType.file);
|
||||
final containsUrlPreview = types.contains(AttachmentType.urlPreview);
|
||||
|
||||
final containsMedia = containsImage || containsVideo || containsGiphy;
|
||||
|
||||
return containsMedia && containsFile;
|
||||
return containsMedia && containsFile ||
|
||||
containsMedia && containsUrlPreview ||
|
||||
containsFile && containsUrlPreview ||
|
||||
containsMedia && containsFile && containsUrlPreview;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -74,6 +78,7 @@ class MixedAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
) {
|
||||
assert(debugAssertCanHandle(message, attachments), '');
|
||||
|
||||
final urls = attachments[AttachmentType.urlPreview];
|
||||
final files = attachments[AttachmentType.file];
|
||||
final images = attachments[AttachmentType.image];
|
||||
final videos = attachments[AttachmentType.video];
|
||||
@@ -86,11 +91,14 @@ class MixedAttachmentBuilder extends StreamAttachmentWidgetBuilder {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (urls != null)
|
||||
_urlAttachmentBuilder.build(context, message, {
|
||||
AttachmentType.urlPreview: urls,
|
||||
}),
|
||||
if (files != null)
|
||||
for (final file in files)
|
||||
_fileAttachmentBuilder.build(context, message, {
|
||||
AttachmentType.file: [file],
|
||||
}),
|
||||
_fileAttachmentBuilder.build(context, message, {
|
||||
AttachmentType.file: files,
|
||||
}),
|
||||
if (shouldBuildGallery)
|
||||
_galleryAttachmentBuilder.build(context, message, {
|
||||
if (images != null) AttachmentType.image: images,
|
||||
|
||||
@@ -128,19 +128,21 @@ class _FileTypeImage extends StatelessWidget {
|
||||
file: file,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
// fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
final mimeType = file.title?.mimeType?.type;
|
||||
final isImage = mimeType == 'image';
|
||||
final isVideo = mimeType == 'video';
|
||||
final mediaType = file.title?.mediaType;
|
||||
final isImage = mediaType?.type == AttachmentType.image;
|
||||
final isVideo = mediaType?.type == AttachmentType.video;
|
||||
if (isImage || isVideo) {
|
||||
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||
child = Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: colorTheme.borders),
|
||||
side: BorderSide(
|
||||
color: colorTheme.borders,
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -127,6 +127,8 @@ class StreamGalleryAttachment extends StatelessWidget {
|
||||
[1],
|
||||
[1],
|
||||
],
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
children: [
|
||||
itemBuilder(context, 0),
|
||||
itemBuilder(context, 1),
|
||||
@@ -145,6 +147,8 @@ class StreamGalleryAttachment extends StatelessWidget {
|
||||
pattern: const [
|
||||
[1, 1],
|
||||
],
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
children: [
|
||||
itemBuilder(context, 0),
|
||||
itemBuilder(context, 1),
|
||||
@@ -170,6 +174,8 @@ class StreamGalleryAttachment extends StatelessWidget {
|
||||
pattern: [
|
||||
if (isLandscape1) [2, 1] else [1, 2],
|
||||
],
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
children: [
|
||||
itemBuilder(context, 0),
|
||||
itemBuilder(context, 1),
|
||||
@@ -204,6 +210,8 @@ class StreamGalleryAttachment extends StatelessWidget {
|
||||
[1],
|
||||
[1, 1],
|
||||
],
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
reverse: !isLandscape1,
|
||||
children: [
|
||||
itemBuilder(context, 0),
|
||||
@@ -238,6 +246,8 @@ class StreamGalleryAttachment extends StatelessWidget {
|
||||
return FlexGrid(
|
||||
pattern: pattern,
|
||||
maxChildren: 4,
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
children: children,
|
||||
overlayBuilder: (context, remaining) {
|
||||
return IgnorePointer(
|
||||
|
||||
@@ -12,7 +12,7 @@ class StreamGiphyAttachment extends StatelessWidget {
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.giphy,
|
||||
this.type = GiphyInfoType.fixedHeightDownsampled,
|
||||
this.type = GiphyInfoType.original,
|
||||
this.shape,
|
||||
this.constraints = const BoxConstraints(),
|
||||
});
|
||||
|
||||
@@ -50,18 +50,18 @@ Future<AttachmentData> downloadAttachmentData(
|
||||
String? downloadUrl;
|
||||
String? fileName;
|
||||
/* ---IMAGES/GIFS--- */
|
||||
if (type == 'image') {
|
||||
if (type == AttachmentType.image) {
|
||||
downloadUrl = attachment.imageUrl ?? attachment.assetUrl;
|
||||
fileName = attachment.title;
|
||||
fileName ??= 'attachment.${attachment.mimeType ?? 'png'}';
|
||||
}
|
||||
/* ---GIPHY's--- */
|
||||
else if (type == 'giphy') {
|
||||
else if (type == AttachmentType.giphy) {
|
||||
downloadUrl = attachment.thumbUrl;
|
||||
fileName = '${attachment.title}.gif';
|
||||
}
|
||||
/* ---FILES AND VIDEOS--- */
|
||||
else if (type == 'file' || type == 'video') {
|
||||
else if (type == AttachmentType.file || type == AttachmentType.video) {
|
||||
downloadUrl = attachment.assetUrl;
|
||||
fileName = attachment.title;
|
||||
}
|
||||
|
||||
+4
-4
@@ -50,9 +50,9 @@ class StreamFileAttachmentThumbnail extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mimeType = file.title?.mimeType?.type;
|
||||
final mediaType = file.title?.mediaType;
|
||||
|
||||
final isImage = mimeType == 'image';
|
||||
final isImage = mediaType?.type == AttachmentType.image;
|
||||
if (isImage) {
|
||||
return StreamImageAttachmentThumbnail(
|
||||
image: file,
|
||||
@@ -62,7 +62,7 @@ class StreamFileAttachmentThumbnail extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
final isVideo = mimeType == 'video';
|
||||
final isVideo = mediaType?.type == AttachmentType.video;
|
||||
if (isVideo) {
|
||||
return StreamVideoAttachmentThumbnail(
|
||||
video: file,
|
||||
@@ -73,6 +73,6 @@ class StreamFileAttachmentThumbnail extends StatelessWidget {
|
||||
}
|
||||
|
||||
// Return a generic file type icon.
|
||||
return getFileTypeImage(mimeType);
|
||||
return getFileTypeImage(mediaType?.mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -50,6 +50,9 @@ class StreamGiphyAttachmentThumbnail extends StatelessWidget {
|
||||
return ThumbnailError(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+3
-37
@@ -2,49 +2,12 @@ import 'dart:io' show File;
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_size_getter/file_input.dart'; // For compatibility with flutter web.
|
||||
import 'package:image_size_getter/image_size_getter.dart' hide Size;
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart';
|
||||
import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/src/utils/utils.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
extension AspectRatioX on Attachment {
|
||||
/// Returns the size of the attachment if it is an image or giffy.
|
||||
/// Otherwise, returns null.
|
||||
Size? get originalSize {
|
||||
// Return null if the attachment is not an image or giffy.
|
||||
if (type != 'image' && type != 'giphy') return null;
|
||||
|
||||
// Calculate size locally if the attachment is not uploaded yet.
|
||||
final file = this.file;
|
||||
if (file != null) {
|
||||
ImageInput? input;
|
||||
if (file.bytes != null) {
|
||||
input = MemoryInput(file.bytes!);
|
||||
} else if (file.path != null) {
|
||||
input = FileInput(File(file.path!));
|
||||
}
|
||||
|
||||
// Return null if the file does not contain enough information.
|
||||
if (input == null) return null;
|
||||
|
||||
final size = ImageSizeGetter.getSize(input);
|
||||
if (size.needRotate) {
|
||||
return Size(size.height.toDouble(), size.width.toDouble());
|
||||
}
|
||||
return Size(size.width.toDouble(), size.height.toDouble());
|
||||
}
|
||||
|
||||
// Otherwise, use the size provided by the server.
|
||||
final width = originalWidth;
|
||||
final height = originalHeight;
|
||||
if (width == null || height == null) return null;
|
||||
return Size(width.toDouble(), height.toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template imageAttachmentThumbnail}
|
||||
/// Widget for building image attachment thumbnail.
|
||||
///
|
||||
@@ -101,6 +64,9 @@ class StreamImageAttachmentThumbnail extends StatelessWidget {
|
||||
return ThumbnailError(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/giphy_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/image_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment/thumbnail/video_attachment_thumbnail.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// {@template mediaAttachmentThumbnail}
|
||||
/// Widget for building media attachment thumbnail.
|
||||
///
|
||||
/// This widget is used when the [Attachment.type] is [AttachmentType.image],
|
||||
/// [AttachmentType.video] or [AttachmentType.giphy].
|
||||
///
|
||||
/// see also:
|
||||
/// * [StreamImageAttachmentThumbnail]
|
||||
/// * [StreamVideoAttachmentThumbnail]
|
||||
/// * [StreamGiphyAttachmentThumbnail]
|
||||
/// {@endtemplate}
|
||||
class StreamMediaAttachmentThumbnail extends StatelessWidget {
|
||||
/// {@macro mediaAttachmentThumbnail}
|
||||
const StreamMediaAttachmentThumbnail({
|
||||
super.key,
|
||||
required this.media,
|
||||
this.width,
|
||||
this.height,
|
||||
this.fit,
|
||||
this.thumbnailSize,
|
||||
this.thumbnailResizeType = 'clip',
|
||||
this.thumbnailCropType = 'center',
|
||||
this.gifInfoType = GiphyInfoType.original,
|
||||
this.errorBuilder = _defaultErrorBuilder,
|
||||
});
|
||||
|
||||
/// The giphy attachment to build the thumbnail for.
|
||||
final Attachment media;
|
||||
|
||||
/// The width of the thumbnail.
|
||||
final double? width;
|
||||
|
||||
/// The height of the thumbnail.
|
||||
final double? height;
|
||||
|
||||
/// How to inscribe the thumbnail into the space allocated during layout.
|
||||
final BoxFit? fit;
|
||||
|
||||
/// Builder used when the thumbnail fails to load.
|
||||
final ThumbnailErrorBuilder errorBuilder;
|
||||
|
||||
/// Size of the attachment image thumbnail.
|
||||
///
|
||||
/// Ignored if the [Attachment.type] is not [AttachmentType.image].
|
||||
final Size? thumbnailSize;
|
||||
|
||||
/// Resize type of the image attachment thumbnail.
|
||||
///
|
||||
/// Defaults to [crop]
|
||||
///
|
||||
/// Ignored if the [Attachment.type] is not [AttachmentType.image].
|
||||
final String /*clip|crop|scale|fill*/ thumbnailResizeType;
|
||||
|
||||
/// Crop type of the image attachment thumbnail.
|
||||
///
|
||||
/// Defaults to [center]
|
||||
///
|
||||
/// Ignored if the [Attachment.type] is not [AttachmentType.image].
|
||||
final String /*center|top|bottom|left|right*/ thumbnailCropType;
|
||||
|
||||
/// The type of giphy thumbnail to build.
|
||||
///
|
||||
/// Ignored if the [Attachment.type] is not [AttachmentType.giphy].
|
||||
final GiphyInfoType gifInfoType;
|
||||
|
||||
// Default error builder for image attachment thumbnail.
|
||||
static Widget _defaultErrorBuilder(
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
) {
|
||||
return ThumbnailError(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final type = media.type;
|
||||
if (type == AttachmentType.image) {
|
||||
return StreamImageAttachmentThumbnail(
|
||||
image: media,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
thumbnailSize: thumbnailSize,
|
||||
thumbnailResizeType: thumbnailResizeType,
|
||||
thumbnailCropType: thumbnailCropType,
|
||||
errorBuilder: errorBuilder,
|
||||
);
|
||||
}
|
||||
|
||||
if (type == AttachmentType.giphy) {
|
||||
return StreamGiphyAttachmentThumbnail(
|
||||
giphy: media,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
type: gifInfoType,
|
||||
errorBuilder: errorBuilder,
|
||||
);
|
||||
}
|
||||
|
||||
if (type == AttachmentType.video) {
|
||||
return StreamVideoAttachmentThumbnail(
|
||||
video: media,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
errorBuilder: errorBuilder,
|
||||
);
|
||||
}
|
||||
|
||||
return errorBuilder(
|
||||
context,
|
||||
'Unsupported attachment type: $type',
|
||||
StackTrace.current,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,20 @@ class ThumbnailError extends StatelessWidget {
|
||||
super.key,
|
||||
required this.error,
|
||||
this.stackTrace,
|
||||
this.width,
|
||||
this.height,
|
||||
this.fit,
|
||||
});
|
||||
|
||||
/// The width of the thumbnail.
|
||||
final double? width;
|
||||
|
||||
/// The height of the thumbnail.
|
||||
final double? height;
|
||||
|
||||
/// How to inscribe the thumbnail into the space allocated during layout.
|
||||
final BoxFit? fit;
|
||||
|
||||
/// The error that triggered this error widget.
|
||||
final Object error;
|
||||
|
||||
@@ -33,7 +45,9 @@ class ThumbnailError extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Image.asset(
|
||||
'images/placeholder.png',
|
||||
fit: BoxFit.cover,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
package: 'stream_chat_flutter',
|
||||
);
|
||||
}
|
||||
|
||||
+3
@@ -46,6 +46,9 @@ class StreamVideoAttachmentThumbnail extends StatelessWidget {
|
||||
return ThumbnailError(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class StreamUrlAttachment extends StatelessWidget {
|
||||
color: colorTheme.borders,
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
);
|
||||
|
||||
final backgroundColor = messageTheme.urlAttachmentBackgroundColor;
|
||||
@@ -62,44 +62,43 @@ class StreamUrlAttachment extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (urlAttachment.imageUrl != null)
|
||||
Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
// Default aspect ratio for Open Graph images.
|
||||
// https://www.kapwing.com/resources/what-is-an-og-image-make-and-format-og-images-for-your-blog-or-webpage
|
||||
aspectRatio: 1.91 / 1,
|
||||
child: StreamImageAttachmentThumbnail(
|
||||
image: urlAttachment,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
// Default aspect ratio for Open Graph images.
|
||||
// https://www.kapwing.com/resources/what-is-an-og-image-make-and-format-og-images-for-your-blog-or-webpage
|
||||
aspectRatio: 1.91 / 1,
|
||||
child: StreamImageAttachmentThumbnail(
|
||||
image: urlAttachment,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
color: backgroundColor,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 12,
|
||||
bottom: 4,
|
||||
),
|
||||
child: Text(
|
||||
hostDisplayName,
|
||||
style: messageTheme.urlAttachmentHostStyle,
|
||||
),
|
||||
color: backgroundColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 12,
|
||||
bottom: 4,
|
||||
),
|
||||
child: Text(
|
||||
hostDisplayName,
|
||||
style: messageTheme.urlAttachmentHostStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
|
||||
Reference in New Issue
Block a user