fix: attachment builder (#326)
* feat: Changed attachment builder structure * fix: fixed image attachments * feat: Added onTaps to remaining attachments * fix: merged fixes * add trailing comma Co-authored-by: Salvatore Giordano <[email protected]>
This commit is contained in:
co-authored by
Salvatore Giordano
parent
1ed11a753f
commit
4b6f1f54b6
@@ -14,6 +14,7 @@ import 'attachment_widget.dart';
|
|||||||
class FileAttachment extends AttachmentWidget {
|
class FileAttachment extends AttachmentWidget {
|
||||||
final Widget title;
|
final Widget title;
|
||||||
final Widget trailing;
|
final Widget trailing;
|
||||||
|
final VoidCallback onAttachmentTap;
|
||||||
|
|
||||||
const FileAttachment({
|
const FileAttachment({
|
||||||
Key key,
|
Key key,
|
||||||
@@ -22,6 +23,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
Size size,
|
Size size,
|
||||||
this.title,
|
this.title,
|
||||||
this.trailing,
|
this.trailing,
|
||||||
|
this.onAttachmentTap,
|
||||||
}) : super(key: key, message: message, attachment: attachment, size: size);
|
}) : super(key: key, message: message, attachment: attachment, size: size);
|
||||||
|
|
||||||
bool get isVideoAttachment => attachment.title?.mimeType?.type == 'video';
|
bool get isVideoAttachment => attachment.title?.mimeType?.type == 'video';
|
||||||
@@ -31,45 +33,48 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
child: Container(
|
child: GestureDetector(
|
||||||
width: size?.width ?? 100,
|
onTap: onAttachmentTap,
|
||||||
height: 56.0,
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
width: size?.width ?? 100,
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
height: 56.0,
|
||||||
borderRadius: BorderRadius.circular(12),
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
color: StreamChatTheme.of(context).colorTheme.white,
|
||||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
child: Row(
|
||||||
child: Row(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Container(
|
||||||
Container(
|
height: 40.0,
|
||||||
height: 40.0,
|
width: 33.33,
|
||||||
width: 33.33,
|
margin: EdgeInsets.all(8.0),
|
||||||
margin: EdgeInsets.all(8.0),
|
child: _getFileTypeImage(context),
|
||||||
child: _getFileTypeImage(context),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8.0),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
attachment?.title ?? 'File',
|
|
||||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
SizedBox(height: 3.0),
|
|
||||||
_buildSubtitle(context),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 8.0),
|
||||||
SizedBox(width: 8.0),
|
Expanded(
|
||||||
_buildTrailing(context),
|
child: Column(
|
||||||
],
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
attachment?.title ?? 'File',
|
||||||
|
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.0),
|
||||||
|
_buildSubtitle(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8.0),
|
||||||
|
_buildTrailing(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
final MessageTheme messageTheme;
|
final MessageTheme messageTheme;
|
||||||
final ShowMessageCallback onShowMessage;
|
final ShowMessageCallback onShowMessage;
|
||||||
final ValueChanged<ReturnActionType> onReturnAction;
|
final ValueChanged<ReturnActionType> onReturnAction;
|
||||||
|
final VoidCallback onAttachmentTap;
|
||||||
|
|
||||||
const GiphyAttachment({
|
const GiphyAttachment({
|
||||||
Key key,
|
Key key,
|
||||||
@@ -24,6 +25,7 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
this.messageTheme,
|
this.messageTheme,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
|
this.onAttachmentTap,
|
||||||
}) : super(key: key, message: message, attachment: attachment, size: size);
|
}) : super(key: key, message: message, attachment: attachment, size: size);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -65,7 +67,7 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _onImageTap(context),
|
onTap: () => onAttachmentTap ?? _onImageTap(context),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(8),
|
topLeft: Radius.circular(8),
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ import 'extension.dart';
|
|||||||
import 'image_group.dart';
|
import 'image_group.dart';
|
||||||
import 'message_text.dart';
|
import 'message_text.dart';
|
||||||
|
|
||||||
typedef AttachmentBuilder = Widget Function(BuildContext, Message, Attachment);
|
typedef AttachmentBuilder = Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
Message,
|
||||||
|
List<Attachment>,
|
||||||
|
);
|
||||||
typedef OnQuotedMessageTap = void Function(String);
|
typedef OnQuotedMessageTap = void Function(String);
|
||||||
|
|
||||||
/// The display behaviour of a widget
|
/// The display behaviour of a widget
|
||||||
@@ -205,63 +209,142 @@ class MessageWidget extends StatefulWidget {
|
|||||||
this.customActions = const [],
|
this.customActions = const [],
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
}) : attachmentBuilders = {
|
}) : attachmentBuilders = {
|
||||||
'image': (context, message, attachment) {
|
'image': (context, message, attachments) {
|
||||||
return ImageAttachment(
|
var border = RoundedRectangleBorder(
|
||||||
attachment: attachment,
|
side: BorderSide.none,
|
||||||
message: message,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
messageTheme: messageTheme,
|
);
|
||||||
size: Size(
|
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
if (attachments.length > 1) {
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
return Padding(
|
||||||
|
padding: attachmentPadding,
|
||||||
|
child: wrapAttachmentWidget(
|
||||||
|
context,
|
||||||
|
Material(
|
||||||
|
color: messageTheme.messageBackgroundColor,
|
||||||
|
child: ImageGroup(
|
||||||
|
size: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.3,
|
||||||
|
),
|
||||||
|
images: attachments,
|
||||||
|
message: message,
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
onShowMessage: onShowMessage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
border,
|
||||||
|
reverse,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapAttachmentWidget(
|
||||||
|
context,
|
||||||
|
ImageAttachment(
|
||||||
|
attachment: attachments[0],
|
||||||
|
message: message,
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
size: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.3,
|
||||||
|
),
|
||||||
|
onShowMessage: onShowMessage,
|
||||||
|
onReturnAction: onReturnAction,
|
||||||
|
onAttachmentTap: onAttachmentTap != null
|
||||||
|
? () {
|
||||||
|
onAttachmentTap?.call(message, attachments[0]);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
onShowMessage: onShowMessage,
|
border,
|
||||||
onReturnAction: onReturnAction,
|
reverse,
|
||||||
onAttachmentTap: onAttachmentTap != null
|
|
||||||
? () {
|
|
||||||
onAttachmentTap?.call(message, attachment);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'video': (context, message, attachment) {
|
'video': (context, message, attachments) {
|
||||||
return VideoAttachment(
|
var border = RoundedRectangleBorder(
|
||||||
attachment: attachment,
|
side: BorderSide.none,
|
||||||
messageTheme: messageTheme,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
size: Size(
|
);
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
return wrapAttachmentWidget(
|
||||||
|
context,
|
||||||
|
Column(
|
||||||
|
children: attachments.map((attachment) {
|
||||||
|
return VideoAttachment(
|
||||||
|
attachment: attachment,
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
size: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.3,
|
||||||
|
),
|
||||||
|
message: message,
|
||||||
|
onShowMessage: onShowMessage,
|
||||||
|
onReturnAction: onReturnAction,
|
||||||
|
onAttachmentTap: onAttachmentTap != null
|
||||||
|
? () {
|
||||||
|
onAttachmentTap?.call(message, attachment);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
message: message,
|
border,
|
||||||
onShowMessage: onShowMessage,
|
reverse,
|
||||||
onReturnAction: onReturnAction,
|
|
||||||
onAttachmentTap: onAttachmentTap != null
|
|
||||||
? () {
|
|
||||||
onAttachmentTap?.call(message, attachment);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'giphy': (context, message, attachment) {
|
'giphy': (context, message, attachments) {
|
||||||
return GiphyAttachment(
|
var border = RoundedRectangleBorder(
|
||||||
attachment: attachment,
|
side: BorderSide.none,
|
||||||
messageTheme: messageTheme,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
message: message,
|
);
|
||||||
size: Size(
|
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
return wrapAttachmentWidget(
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
context,
|
||||||
|
Column(
|
||||||
|
children: attachments.map((attachment) {
|
||||||
|
return GiphyAttachment(
|
||||||
|
attachment: attachment,
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
message: message,
|
||||||
|
size: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.3,
|
||||||
|
),
|
||||||
|
onShowMessage: onShowMessage,
|
||||||
|
onReturnAction: onReturnAction,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
onShowMessage: onShowMessage,
|
border,
|
||||||
onReturnAction: onReturnAction,
|
reverse,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'file': (context, message, attachment) {
|
'file': (context, message, attachments) {
|
||||||
return FileAttachment(
|
var border = RoundedRectangleBorder(
|
||||||
message: message,
|
side: attachmentBorderSide ??
|
||||||
attachment: attachment,
|
BorderSide(
|
||||||
size: Size(
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
),
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
|
);
|
||||||
|
|
||||||
|
return wrapAttachmentWidget(
|
||||||
|
context,
|
||||||
|
Column(
|
||||||
|
children: attachments.map((attachment) {
|
||||||
|
return FileAttachment(
|
||||||
|
message: message,
|
||||||
|
attachment: attachment,
|
||||||
|
size: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.3,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
border,
|
||||||
|
reverse,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}..addAll(customAttachmentBuilders ?? {}),
|
}..addAll(customAttachmentBuilders ?? {}),
|
||||||
@@ -830,55 +913,37 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _parseAttachments() {
|
Widget _parseAttachments() {
|
||||||
final images = widget.message.attachments
|
Map<String, List<Attachment>> attachmentGroups = {};
|
||||||
?.where((element) =>
|
|
||||||
element.type == 'image' && element.ogScrapeUrl == null)
|
|
||||||
?.toList() ??
|
|
||||||
[];
|
|
||||||
|
|
||||||
if (images.length > 1) {
|
widget.message.attachments
|
||||||
return Padding(
|
.where((element) => element.ogScrapeUrl == null)
|
||||||
padding: widget.attachmentPadding,
|
.forEach((e) {
|
||||||
child: wrapAttachmentWidget(
|
if (attachmentGroups[e.type] == null) {
|
||||||
context,
|
attachmentGroups[e.type] = [];
|
||||||
Material(
|
}
|
||||||
color: widget.messageTheme.messageBackgroundColor,
|
|
||||||
child: ImageGroup(
|
attachmentGroups[e.type].add(e);
|
||||||
size: Size(
|
});
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
List<Widget> attachmentList = [];
|
||||||
),
|
|
||||||
images: images,
|
attachmentGroups.forEach((type, attachments) {
|
||||||
message: widget.message,
|
final attachmentBuilder = widget.attachmentBuilders[type];
|
||||||
messageTheme: widget.messageTheme,
|
|
||||||
onShowMessage: widget.onShowMessage,
|
if (attachmentBuilder == null) return SizedBox();
|
||||||
),
|
final attachmentWidget = attachmentBuilder(
|
||||||
),
|
context,
|
||||||
),
|
widget.message,
|
||||||
|
attachments,
|
||||||
);
|
);
|
||||||
}
|
attachmentList.add(attachmentWidget);
|
||||||
|
});
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: widget.attachmentPadding,
|
padding: widget.attachmentPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: widget.message.attachments
|
children: attachmentList?.insertBetween(SizedBox(
|
||||||
?.where((element) => element.ogScrapeUrl == null)
|
|
||||||
?.map((attachment) {
|
|
||||||
final attachmentBuilder =
|
|
||||||
widget.attachmentBuilders[attachment.type];
|
|
||||||
|
|
||||||
if (attachmentBuilder == null) return SizedBox();
|
|
||||||
final attachmentWidget = attachmentBuilder(
|
|
||||||
context,
|
|
||||||
widget.message,
|
|
||||||
attachment,
|
|
||||||
);
|
|
||||||
return wrapAttachmentWidget(
|
|
||||||
context,
|
|
||||||
attachmentWidget,
|
|
||||||
);
|
|
||||||
})?.insertBetween(SizedBox(
|
|
||||||
height: widget.attachmentPadding.vertical / 2,
|
height: widget.attachmentPadding.vertical / 2,
|
||||||
)) ??
|
)) ??
|
||||||
[],
|
[],
|
||||||
@@ -886,24 +951,6 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget wrapAttachmentWidget(
|
|
||||||
BuildContext context,
|
|
||||||
Widget attachmentWidget,
|
|
||||||
) {
|
|
||||||
final attachmentShape =
|
|
||||||
widget.attachmentShape ?? _getDefaultAttachmentShape(context);
|
|
||||||
return Material(
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
shape: attachmentShape,
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: Transform(
|
|
||||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: attachmentWidget,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onLongPress(BuildContext context) {
|
void onLongPress(BuildContext context) {
|
||||||
if (widget.message.isEphemeral ||
|
if (widget.message.isEphemeral ||
|
||||||
widget.message.status == MessageSendingStatus.sending) {
|
widget.message.status == MessageSendingStatus.sending) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@@ -348,3 +350,21 @@ StreamSvgIcon getFileTypeImage(String type) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget wrapAttachmentWidget(
|
||||||
|
BuildContext context,
|
||||||
|
Widget attachmentWidget,
|
||||||
|
ShapeBorder attachmentShape,
|
||||||
|
bool reverse,
|
||||||
|
) {
|
||||||
|
return Material(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
shape: attachmentShape,
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: Transform(
|
||||||
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: attachmentWidget,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user