migrate ui

This commit is contained in:
Salvatore Giordano
2021-04-30 14:58:50 +02:00
parent 2c02f0f631
commit 34888d0fff
103 changed files with 2622 additions and 2455 deletions
@@ -6,12 +6,12 @@ import '../utils.dart';
class AttachmentTitle extends StatelessWidget {
const AttachmentTitle({
Key key,
@required this.attachment,
@required this.messageTheme,
Key? key,
required this.attachment,
required this.messageTheme,
}) : super(key: key);
final MessageTheme messageTheme;
final MessageTheme? messageTheme;
final Attachment attachment;
@override
@@ -19,7 +19,7 @@ class AttachmentTitle extends StatelessWidget {
return GestureDetector(
onTap: () {
if (attachment.titleLink != null) {
launchURL(context, attachment.titleLink);
launchURL(context, attachment.titleLink!);
}
},
child: Padding(
@@ -28,17 +28,18 @@ class AttachmentTitle extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
attachment.title,
overflow: TextOverflow.ellipsis,
style: messageTheme.messageText.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
if (attachment.title != null)
Text(
attachment.title!,
overflow: TextOverflow.ellipsis,
style: messageTheme?.messageText?.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
),
),
),
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
Text(
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl)
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!)
.authority
.split('.')
.reversed
@@ -46,7 +47,7 @@ class AttachmentTitle extends StatelessWidget {
.toList()
.reversed
.join('.'),
style: messageTheme.messageText,
style: messageTheme?.messageText,
),
],
),
@@ -8,55 +8,52 @@ typedef FailedBuilder = Widget Function(BuildContext, String);
class AttachmentUploadStateBuilder extends StatelessWidget {
final Message message;
final Attachment attachment;
final FailedBuilder failedBuilder;
final WidgetBuilder successBuilder;
final InProgressBuilder inProgressBuilder;
final WidgetBuilder preparingBuilder;
final FailedBuilder? failedBuilder;
final WidgetBuilder? successBuilder;
final InProgressBuilder? inProgressBuilder;
final WidgetBuilder? preparingBuilder;
const AttachmentUploadStateBuilder({
Key key,
@required this.message,
@required this.attachment,
Key? key,
required this.message,
required this.attachment,
this.failedBuilder,
this.successBuilder,
this.inProgressBuilder,
this.preparingBuilder,
}) : assert(message != null),
assert(attachment != null),
super(key: key);
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (message.status == null || message.status == MessageSendingStatus.sent) {
if (message.status == MessageSendingStatus.sent) {
return Offstage();
}
final messageId = message.id;
final attachmentId = attachment.id;
var inProgress = inProgressBuilder;
inProgress ??= (context, int sent, int total) {
return _InProgressState(
sent: sent,
total: total,
attachmentId: attachmentId,
);
};
final inProgress = inProgressBuilder ??
(context, int sent, int total) {
return _InProgressState(
sent: sent,
total: total,
attachmentId: attachmentId,
);
};
var failed = failedBuilder;
failed ??= (context, error) {
return _FailedState(
error: error,
messageId: messageId,
attachmentId: attachmentId,
);
};
final failed = failedBuilder ??
(context, error) {
return _FailedState(
error: error,
messageId: messageId,
attachmentId: attachmentId,
);
};
var success = successBuilder;
success ??= (context) => _SuccessState();
final success = successBuilder ?? (context) => _SuccessState();
var preparing = preparingBuilder;
preparing ??= (context) => _PreparingState(attachmentId: attachmentId);
final preparing = preparingBuilder ??
(context) => _PreparingState(attachmentId: attachmentId);
return attachment.uploadState.when(
preparing: () => preparing(context),
@@ -68,13 +65,13 @@ class AttachmentUploadStateBuilder extends StatelessWidget {
}
class _IconButton extends StatelessWidget {
final Widget icon;
final Widget? icon;
final double iconSize;
final VoidCallback onPressed;
final Color fillColor;
final VoidCallback? onPressed;
final Color? fillColor;
const _IconButton({
Key key,
Key? key,
this.icon,
this.iconSize = 24.0,
this.onPressed,
@@ -95,7 +92,9 @@ class _IconButton extends StatelessWidget {
onPressed: onPressed,
fillColor:
fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: icon,
),
);
@@ -106,8 +105,8 @@ class _PreparingState extends StatelessWidget {
final String attachmentId;
const _PreparingState({
Key key,
@required this.attachmentId,
Key? key,
required this.attachmentId,
}) : super(key: key);
@override
@@ -144,10 +143,10 @@ class _InProgressState extends StatelessWidget {
final String attachmentId;
const _InProgressState({
Key key,
@required this.sent,
@required this.total,
@required this.attachmentId,
Key? key,
required this.sent,
required this.total,
required this.attachmentId,
}) : super(key: key);
@override
@@ -179,15 +178,15 @@ class _InProgressState extends StatelessWidget {
}
class _FailedState extends StatelessWidget {
final String error;
final String? error;
final String messageId;
final String attachmentId;
const _FailedState({
Key key,
Key? key,
this.error,
@required this.messageId,
@required this.attachmentId,
required this.messageId,
required this.attachmentId,
}) : super(key: key);
@override
@@ -203,7 +202,7 @@ class _FailedState extends StatelessWidget {
color: theme.colorTheme.white,
),
onPressed: () {
return channel.retryAttachmentUpload(messageId, attachmentId);
channel.retryAttachmentUpload(messageId, attachmentId);
},
),
Center(
@@ -213,7 +212,10 @@ class _FailedState extends StatelessWidget {
color: theme.colorTheme.overlayDark.withOpacity(0.6),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
padding: const EdgeInsets.symmetric(
vertical: 6,
horizontal: 12,
),
child: Text(
'UPLOAD ERROR',
style: theme.textTheme.footnote.copyWith(
@@ -13,15 +13,9 @@ extension AttachmentSourceX on AttachmentSource {
/// Its prototype depends on the AttachmentSource defined.
// ignore: missing_return
T when<T>({
@required T Function() local,
@required T Function() network,
required T Function() local,
required T Function() network,
}) {
assert(() {
if (local == null || network == null) {
throw 'check for all possible cases';
}
return true;
}());
switch (this) {
case AttachmentSource.local:
return local();
@@ -32,30 +26,32 @@ extension AttachmentSourceX on AttachmentSource {
}
abstract class AttachmentWidget extends StatelessWidget {
final Size size;
final Size? size;
final AttachmentSource? _source;
final Message message;
final Attachment attachment;
final AttachmentSource _source;
AttachmentSource get source => _source ?? attachment.file != null
? AttachmentSource.local
: AttachmentSource.network;
AttachmentSource get source =>
_source ??
(attachment.file != null
? AttachmentSource.local
: AttachmentSource.network);
const AttachmentWidget({
Key key,
@required this.message,
@required this.attachment,
Key? key,
required this.message,
required this.attachment,
this.size,
AttachmentSource source,
AttachmentSource? source,
}) : _source = source,
super(key: key);
}
class AttachmentError extends StatelessWidget {
final Size size;
final Size? size;
const AttachmentError({
Key key,
Key? key,
this.size,
}) : super(key: key);
@@ -11,19 +11,24 @@ import '../upload_progress_indicator.dart';
import 'attachment_widget.dart';
class FileAttachment extends AttachmentWidget {
final Widget title;
final Widget trailing;
final VoidCallback onAttachmentTap;
final Widget? title;
final Widget? trailing;
final VoidCallback? onAttachmentTap;
const FileAttachment({
Key key,
@required Message message,
@required Attachment attachment,
Size size,
Key? key,
required Message message,
required Attachment attachment,
Size? size,
this.title,
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';
@@ -31,6 +36,7 @@ class FileAttachment extends AttachmentWidget {
@override
Widget build(BuildContext context) {
final colorTheme = StreamChatTheme.of(context).colorTheme;
return Material(
child: GestureDetector(
onTap: onAttachmentTap,
@@ -38,10 +44,10 @@ class FileAttachment extends AttachmentWidget {
width: size?.width ?? 100,
height: 56.0,
decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white,
color: colorTheme.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
color: colorTheme.greyWhisper,
),
),
child: Row(
@@ -60,7 +66,7 @@ class FileAttachment extends AttachmentWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
attachment?.title ?? 'File',
attachment.title ?? 'File',
style: StreamChatTheme.of(context).textTheme.bodyBold,
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -93,34 +99,51 @@ class FileAttachment extends AttachmentWidget {
type: MaterialType.transparency,
shape: _getDefaultShape(context),
child: source.when(
local: () => Image.memory(
attachment.file.bytes,
fit: BoxFit.cover,
errorBuilder: (_, obj, trace) {
return getFileTypeImage(attachment.extraData['other']);
},
),
network: () => CachedNetworkImage(
imageUrl: attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
fit: BoxFit.cover,
errorWidget: (_, obj, trace) {
return getFileTypeImage(attachment.extraData['other']);
},
placeholder: (_, __) {
return Shimmer.fromColors(
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor:
StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Image.asset(
local: () {
if (attachment.file?.bytes == null) {
return getFileTypeImage(attachment.extraData['other'] as String?);
}
return Image.memory(
attachment.file!.bytes!,
fit: BoxFit.cover,
errorBuilder: (_, obj, trace) {
return getFileTypeImage(
attachment.extraData['other'] as String?);
},
);
},
network: () {
if ((attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl) ==
null) {
return getFileTypeImage(attachment.extraData['other'] as String?);
}
return CachedNetworkImage(
imageUrl: attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl!,
fit: BoxFit.cover,
errorWidget: (_, obj, trace) {
return getFileTypeImage(
attachment.extraData['other'] as String?);
},
placeholder: (_, __) {
final image = Image.asset(
'images/placeholder.png',
fit: BoxFit.cover,
package: 'stream_chat_flutter',
),
);
},
),
);
final colorTheme = StreamChatTheme.of(context).colorTheme;
return Shimmer.fromColors(
baseColor: colorTheme.greyGainsboro,
highlightColor: colorTheme.whiteSmoke,
child: image,
);
},
);
},
),
);
}
@@ -132,7 +155,7 @@ class FileAttachment extends AttachmentWidget {
shape: _getDefaultShape(context),
child: source.when(
local: () => VideoThumbnailImage(
video: attachment.file.path,
video: attachment.file?.path,
placeholderBuilder: (_) {
return Center(
child: Container(
@@ -158,14 +181,14 @@ class FileAttachment extends AttachmentWidget {
),
);
}
return getFileTypeImage(attachment.extraData['mime_type']);
return getFileTypeImage(attachment.extraData['mime_type'] as String?);
}
Widget _buildButton({
Widget icon,
Widget? icon,
double iconSize = 24.0,
VoidCallback onPressed,
Color fillColor,
VoidCallback? onPressed,
Color? fillColor,
}) {
return Container(
height: iconSize,
@@ -189,7 +212,7 @@ class FileAttachment extends AttachmentWidget {
final channel = StreamChannel.of(context).channel;
final attachmentId = attachment.id;
var trailingWidget = trailing;
trailingWidget ??= attachment.uploadState?.when(
trailingWidget ??= attachment.uploadState.when(
preparing: () => Padding(
padding: const EdgeInsets.all(8.0),
child: _buildButton(
@@ -220,7 +243,7 @@ class FileAttachment extends AttachmentWidget {
icon: StreamSvgIcon.retry(color: theme.colorTheme.white),
fillColor: theme.colorTheme.overlayDark,
onPressed: () => channel.retryAttachmentUpload(
message?.id,
message.id,
attachmentId,
),
),
@@ -236,9 +259,7 @@ class FileAttachment extends AttachmentWidget {
},
);
if (message != null &&
(message.status == null ||
message.status == MessageSendingStatus.sent)) {
if (message.status == MessageSendingStatus.sent) {
trailingWidget = IconButton(
icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black),
padding: const EdgeInsets.all(8),
@@ -262,11 +283,8 @@ class FileAttachment extends AttachmentWidget {
final textStyle = theme.textTheme.footnote.copyWith(
color: theme.colorTheme.grey,
);
return attachment.uploadState?.when(
return attachment.uploadState.when(
preparing: () {
if (message == null) {
return Text('${fileSize(size, 2)}', style: textStyle);
}
return UploadProgressIndicator(
uploaded: 0,
total: double.maxFinite.toInt(),
@@ -9,17 +9,15 @@ import '../stream_svg_icon.dart';
import 'attachment_widget.dart';
class GiphyAttachment extends AttachmentWidget {
final MessageTheme messageTheme;
final ShowMessageCallback onShowMessage;
final ValueChanged<ReturnActionType> onReturnAction;
final VoidCallback onAttachmentTap;
final ShowMessageCallback? onShowMessage;
final ValueChanged<ReturnActionType>? onReturnAction;
final VoidCallback? onAttachmentTap;
const GiphyAttachment({
Key key,
@required Message message,
@required Attachment attachment,
Size size,
this.messageTheme,
Key? key,
required Message message,
required Attachment attachment,
Size? size,
this.onShowMessage,
this.onReturnAction,
this.onAttachmentTap,
@@ -29,10 +27,10 @@ class GiphyAttachment extends AttachmentWidget {
Widget build(BuildContext context) {
final imageUrl =
attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl;
if (imageUrl == null && source == AttachmentSource.network) {
if (imageUrl == null) {
return AttachmentError();
}
if (attachment.actions != null) {
if (attachment.actions.isNotEmpty) {
return _buildSendingAttachment(context, imageUrl);
}
return _buildSentAttachment(context, imageUrl);
@@ -73,7 +71,7 @@ class GiphyAttachment extends AttachmentWidget {
if (attachment.title != null)
Flexible(
child: Text(
attachment.title,
attachment.title!,
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
@@ -199,10 +197,11 @@ class GiphyAttachment extends AttachmentWidget {
child: Text(
'Send',
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
fontWeight: FontWeight.bold),
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
fontWeight: FontWeight.bold,
),
),
),
),
@@ -259,8 +258,7 @@ class GiphyAttachment extends AttachmentWidget {
channel: channel,
child: FullScreenMedia(
mediaAttachments: [attachment],
userName: message.user.name,
sentAt: message.createdAt,
userName: message.user?.name,
message: message,
onShowMessage: onShowMessage,
),
@@ -268,7 +266,7 @@ class GiphyAttachment extends AttachmentWidget {
},
),
);
if (res != null) onReturnAction(res);
if (res != null) onReturnAction?.call(res);
}
Widget _buildSentAttachment(BuildContext context, String imageUrl) {
@@ -282,14 +280,13 @@ class GiphyAttachment extends AttachmentWidget {
channel: channel,
child: FullScreenMedia(
mediaAttachments: [attachment],
userName: message.user.name,
sentAt: message.createdAt,
userName: message.user?.name,
message: message,
onShowMessage: onShowMessage,
),
);
}));
if (res != null) onReturnAction(res);
if (res != null) onReturnAction!(res);
},
child: Stack(
children: [
@@ -297,16 +294,17 @@ class GiphyAttachment extends AttachmentWidget {
height: size?.height,
width: size?.width,
placeholder: (_, __) {
final image = Image.asset(
'images/placeholder.png',
fit: BoxFit.cover,
package: 'stream_chat_flutter',
);
final colorTheme = StreamChatTheme.of(context).colorTheme;
return Shimmer.fromColors(
baseColor:
StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor:
StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Image.asset(
'images/placeholder.png',
fit: BoxFit.cover,
package: 'stream_chat_flutter',
),
baseColor: colorTheme.greyGainsboro,
highlightColor: colorTheme.whiteSmoke,
child: image,
);
},
imageUrl: imageUrl,
@@ -10,35 +10,40 @@ import 'attachment_title.dart';
import 'attachment_widget.dart';
class ImageAttachment extends AttachmentWidget {
final MessageTheme messageTheme;
final MessageTheme? messageTheme;
final bool showTitle;
final ShowMessageCallback onShowMessage;
final ValueChanged<ReturnActionType> onReturnAction;
final VoidCallback onAttachmentTap;
final ShowMessageCallback? onShowMessage;
final ValueChanged<ReturnActionType>? onReturnAction;
final VoidCallback? onAttachmentTap;
const ImageAttachment({
Key key,
@required Message message,
@required Attachment attachment,
Size size,
Key? key,
required Message message,
required Attachment attachment,
Size? size,
this.messageTheme,
this.showTitle = false,
this.onShowMessage,
this.onReturnAction,
this.onAttachmentTap,
}) : super(key: key, message: message, attachment: attachment, size: size);
}) : super(
key: key,
message: message,
attachment: attachment,
size: size,
);
@override
Widget build(BuildContext context) {
return source.when(
local: () {
if (attachment.localUri == null) {
if (attachment.localUri == null || attachment.file?.bytes == null) {
return AttachmentError(size: size);
}
return _buildImageAttachment(
context,
Image.memory(
attachment.file.bytes,
attachment.file!.bytes!,
height: size?.height,
width: size?.width,
fit: BoxFit.cover,
@@ -85,15 +90,16 @@ class ImageAttachment extends AttachmentWidget {
height: size?.height,
width: size?.width,
placeholder: (_, __) {
final image = Image.asset(
'images/placeholder.png',
fit: BoxFit.cover,
package: 'stream_chat_flutter',
);
final colorTheme = StreamChatTheme.of(context).colorTheme;
return Shimmer.fromColors(
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor:
StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Image.asset(
'images/placeholder.png',
fit: BoxFit.cover,
package: 'stream_chat_flutter',
),
baseColor: colorTheme.greyGainsboro,
highlightColor: colorTheme.whiteSmoke,
child: image,
);
},
imageUrl: imageUrl,
@@ -109,7 +115,7 @@ class ImageAttachment extends AttachmentWidget {
Widget _buildImageAttachment(BuildContext context, Widget imageWidget) {
return ConstrainedBox(
constraints: BoxConstraints.loose(size),
constraints: BoxConstraints.loose(size!),
child: Column(
children: <Widget>[
Expanded(
@@ -127,8 +133,7 @@ class ImageAttachment extends AttachmentWidget {
channel: channel,
child: FullScreenMedia(
mediaAttachments: [attachment],
userName: message.user.name,
sentAt: message.createdAt,
userName: message.user?.name,
message: message,
onShowMessage: onShowMessage,
),
@@ -136,7 +141,7 @@ class ImageAttachment extends AttachmentWidget {
},
),
);
if (result != null) onReturnAction(result);
if (result != null) onReturnAction?.call(result);
},
child: imageWidget,
),
@@ -152,7 +157,7 @@ class ImageAttachment extends AttachmentWidget {
),
if (showTitle && attachment.title != null)
Material(
color: messageTheme.messageBackgroundColor,
color: messageTheme?.messageBackgroundColor,
child: AttachmentTitle(
messageTheme: messageTheme,
attachment: attachment,
@@ -8,21 +8,26 @@ import 'attachment_upload_state_builder.dart';
import 'attachment_widget.dart';
class VideoAttachment extends AttachmentWidget {
final MessageTheme messageTheme;
final ShowMessageCallback onShowMessage;
final ValueChanged<ReturnActionType> onReturnAction;
final VoidCallback onAttachmentTap;
final MessageTheme? messageTheme;
final ShowMessageCallback? onShowMessage;
final ValueChanged<ReturnActionType>? onReturnAction;
final VoidCallback? onAttachmentTap;
const VideoAttachment({
Key key,
@required Message message,
@required Attachment attachment,
Size size,
Key? key,
required Message message,
required Attachment attachment,
Size? size,
this.messageTheme,
this.onShowMessage,
this.onReturnAction,
this.onAttachmentTap,
}) : super(key: key, message: message, attachment: attachment, size: size);
}) : super(
key: key,
message: message,
attachment: attachment,
size: size,
);
@override
Widget build(BuildContext context) {
@@ -34,7 +39,7 @@ class VideoAttachment extends AttachmentWidget {
return _buildVideoAttachment(
context,
VideoThumbnailImage(
video: attachment.file.path,
video: attachment.file?.path,
height: size?.height,
width: size?.width,
fit: BoxFit.cover,
@@ -62,7 +67,7 @@ class VideoAttachment extends AttachmentWidget {
Widget _buildVideoAttachment(BuildContext context, Widget videoWidget) {
return ConstrainedBox(
constraints: BoxConstraints.loose(size),
constraints: BoxConstraints.loose(size ?? Size.infinite),
child: Column(
children: <Widget>[
Expanded(
@@ -77,15 +82,14 @@ class VideoAttachment extends AttachmentWidget {
channel: channel,
child: FullScreenMedia(
mediaAttachments: [attachment],
userName: message.user.name,
sentAt: message.createdAt,
userName: message.user?.name,
message: message,
onShowMessage: onShowMessage,
),
),
),
);
if (res != null) onReturnAction(res);
if (res != null) onReturnAction?.call(res);
},
child: Stack(
children: [
@@ -112,7 +116,7 @@ class VideoAttachment extends AttachmentWidget {
),
if (attachment.title != null)
Material(
color: messageTheme.messageBackgroundColor,
color: messageTheme?.messageBackgroundColor,
child: AttachmentTitle(
messageTheme: messageTheme,
attachment: attachment,