migrate ui
This commit is contained in:
@@ -912,7 +912,10 @@ class Channel {
|
|||||||
|
|
||||||
void _initState(ChannelState channelState) {
|
void _initState(ChannelState channelState) {
|
||||||
state = ChannelClientState(this, channelState);
|
state = ChannelClientState(this, channelState);
|
||||||
client.state.channels[cid!] = this;
|
|
||||||
|
if (cid != null) {
|
||||||
|
client.state.channels[cid!] = this;
|
||||||
|
}
|
||||||
if (!_initializedCompleter.isCompleted) {
|
if (!_initializedCompleter.isCompleted) {
|
||||||
_initializedCompleter.complete(true);
|
_initializedCompleter.complete(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -798,11 +798,13 @@ class StreamChatClient {
|
|||||||
for (final channelState in channelStates) {
|
for (final channelState in channelStates) {
|
||||||
final channel = channels[channelState.channel!.cid];
|
final channel = channels[channelState.channel!.cid];
|
||||||
if (channel != null) {
|
if (channel != null) {
|
||||||
channel.state!.updateChannelState(channelState);
|
channel.state?.updateChannelState(channelState);
|
||||||
newChannels.add(channel);
|
newChannels.add(channel);
|
||||||
} else {
|
} else {
|
||||||
final newChannel = Channel.fromState(this, channelState);
|
final newChannel = Channel.fromState(this, channelState);
|
||||||
channels[newChannel.cid!] = newChannel;
|
if (newChannel.cid != null) {
|
||||||
|
channels[newChannel.cid!] = newChannel;
|
||||||
|
}
|
||||||
newChannels.add(newChannel);
|
newChannels.add(newChannel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1513,7 +1515,7 @@ class ClientState {
|
|||||||
/// The current list of channels in memory
|
/// The current list of channels in memory
|
||||||
Map<String, Channel> get channels => _channelsController.value!;
|
Map<String, Channel> get channels => _channelsController.value!;
|
||||||
|
|
||||||
set channels(Map<String, Channel>? v) {
|
set channels(Map<String, Channel> v) {
|
||||||
if (v != null) _channelsController.add(v);
|
if (v != null) _channelsController.add(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Attachment extends Equatable {
|
|||||||
this.authorIcon,
|
this.authorIcon,
|
||||||
this.assetUrl,
|
this.assetUrl,
|
||||||
List<Action>? actions,
|
List<Action>? actions,
|
||||||
this.extraData,
|
this.extraData = const {},
|
||||||
this.file,
|
this.file,
|
||||||
UploadState? uploadState,
|
UploadState? uploadState,
|
||||||
}) : id = id ?? const Uuid().v4(),
|
}) : id = id ?? const Uuid().v4(),
|
||||||
@@ -110,8 +110,11 @@ class Attachment extends Equatable {
|
|||||||
late final UploadState uploadState;
|
late final UploadState uploadState;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(
|
||||||
final Map<String, Object>? extraData;
|
includeIfNull: false,
|
||||||
|
defaultValue: {},
|
||||||
|
)
|
||||||
|
final Map<String, Object> extraData;
|
||||||
|
|
||||||
/// The attachment ID.
|
/// The attachment ID.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ Attachment _$AttachmentFromJson(Map<String, dynamic> json) {
|
|||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(k, e as Object),
|
(k, e) => MapEntry(k, e as Object),
|
||||||
),
|
) ??
|
||||||
|
{},
|
||||||
file: json['file'] == null
|
file: json['file'] == null
|
||||||
? null
|
? null
|
||||||
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
|
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
|
||||||
@@ -71,7 +72,7 @@ Map<String, dynamic> _$AttachmentToJson(Attachment instance) {
|
|||||||
val['actions'] = instance.actions.map((e) => e.toJson()).toList();
|
val['actions'] = instance.actions.map((e) => e.toJson()).toList();
|
||||||
writeNotNull('file', instance.file?.toJson());
|
writeNotNull('file', instance.file?.toJson());
|
||||||
val['upload_state'] = instance.uploadState.toJson();
|
val['upload_state'] = instance.uploadState.toJson();
|
||||||
writeNotNull('extra_data', instance.extraData);
|
val['extra_data'] = instance.extraData;
|
||||||
val['id'] = instance.id;
|
val['id'] = instance.id;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ String? _toString(Uint8List? bytes) {
|
|||||||
class AttachmentFile {
|
class AttachmentFile {
|
||||||
/// Creates a new [AttachmentFile] instance.
|
/// Creates a new [AttachmentFile] instance.
|
||||||
const AttachmentFile({
|
const AttachmentFile({
|
||||||
|
required this.size,
|
||||||
this.path,
|
this.path,
|
||||||
this.name,
|
this.name,
|
||||||
this.bytes,
|
this.bytes,
|
||||||
this.size,
|
|
||||||
}) : assert(
|
}) : assert(
|
||||||
path != null || bytes != null,
|
path != null || bytes != null,
|
||||||
'Either path or bytes should be != null',
|
'Either path or bytes should be != null',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class ChannelModel {
|
|||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
this.deletedAt,
|
this.deletedAt,
|
||||||
this.memberCount = 0,
|
this.memberCount = 0,
|
||||||
this.extraData,
|
this.extraData = const {},
|
||||||
this.team,
|
this.team,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
(cid != null && cid.contains(':')) || (id != null && type != null),
|
(cid != null && cid.contains(':')) || (id != null && type != null),
|
||||||
@@ -83,8 +83,11 @@ class ChannelModel {
|
|||||||
final int memberCount;
|
final int memberCount;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(
|
||||||
final Map<String, Object>? extraData;
|
includeIfNull: false,
|
||||||
|
defaultValue: {},
|
||||||
|
)
|
||||||
|
final Map<String, Object> extraData;
|
||||||
|
|
||||||
/// The team the channel belongs to
|
/// The team the channel belongs to
|
||||||
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
||||||
@@ -108,9 +111,8 @@ class ChannelModel {
|
|||||||
];
|
];
|
||||||
|
|
||||||
/// Shortcut for channel name
|
/// Shortcut for channel name
|
||||||
String get name => extraData?.containsKey('name') == true
|
String get name =>
|
||||||
? extraData!['name'] as String
|
extraData.containsKey('name') ? extraData['name'] as String : cid;
|
||||||
: cid;
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
|
|||||||
: DateTime.parse(json['deleted_at'] as String),
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
memberCount: json['member_count'] as int? ?? 0,
|
memberCount: json['member_count'] as int? ?? 0,
|
||||||
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(k, e as Object),
|
(k, e) => MapEntry(k, e as Object),
|
||||||
),
|
) ??
|
||||||
|
{},
|
||||||
team: json['team'] as String?,
|
team: json['team'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
|||||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||||
writeNotNull('member_count', readonly(instance.memberCount));
|
writeNotNull('member_count', readonly(instance.memberCount));
|
||||||
writeNotNull('extra_data', instance.extraData);
|
val['extra_data'] = instance.extraData;
|
||||||
writeNotNull('team', readonly(instance.team));
|
writeNotNull('team', readonly(instance.team));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class EventChannel extends ChannelModel {
|
|||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
deletedAt: deletedAt,
|
deletedAt: deletedAt,
|
||||||
memberCount: memberCount,
|
memberCount: memberCount,
|
||||||
extraData: extraData,
|
extraData: extraData ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
|
|||||||
@@ -90,8 +90,9 @@ EventChannel _$EventChannelFromJson(Map<String, dynamic> json) {
|
|||||||
: DateTime.parse(json['deleted_at'] as String),
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
memberCount: json['member_count'] as int? ?? 0,
|
memberCount: json['member_count'] as int? ?? 0,
|
||||||
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
extraData: (json['extra_data'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(k, e as Object),
|
(k, e) => MapEntry(k, e as Object),
|
||||||
),
|
) ??
|
||||||
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ Map<String, dynamic> _$EventChannelToJson(EventChannel instance) {
|
|||||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||||
writeNotNull('member_count', readonly(instance.memberCount));
|
writeNotNull('member_count', readonly(instance.memberCount));
|
||||||
writeNotNull('extra_data', instance.extraData);
|
val['extra_data'] = instance.extraData;
|
||||||
val['members'] = instance.members?.map((e) => e.toJson()).toList();
|
val['members'] = instance.members?.map((e) => e.toJson()).toList();
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import '../utils.dart';
|
|||||||
|
|
||||||
class AttachmentTitle extends StatelessWidget {
|
class AttachmentTitle extends StatelessWidget {
|
||||||
const AttachmentTitle({
|
const AttachmentTitle({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.attachment,
|
required this.attachment,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
final Attachment attachment;
|
final Attachment attachment;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -19,7 +19,7 @@ class AttachmentTitle extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (attachment.titleLink != null) {
|
if (attachment.titleLink != null) {
|
||||||
launchURL(context, attachment.titleLink);
|
launchURL(context, attachment.titleLink!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -28,17 +28,18 @@ class AttachmentTitle extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
if (attachment.title != null)
|
||||||
attachment.title,
|
Text(
|
||||||
overflow: TextOverflow.ellipsis,
|
attachment.title!,
|
||||||
style: messageTheme.messageText.copyWith(
|
overflow: TextOverflow.ellipsis,
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
style: messageTheme?.messageText?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
|
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
|
||||||
Text(
|
Text(
|
||||||
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl)
|
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!)
|
||||||
.authority
|
.authority
|
||||||
.split('.')
|
.split('.')
|
||||||
.reversed
|
.reversed
|
||||||
@@ -46,7 +47,7 @@ class AttachmentTitle extends StatelessWidget {
|
|||||||
.toList()
|
.toList()
|
||||||
.reversed
|
.reversed
|
||||||
.join('.'),
|
.join('.'),
|
||||||
style: messageTheme.messageText,
|
style: messageTheme?.messageText,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
+50
-48
@@ -8,55 +8,52 @@ typedef FailedBuilder = Widget Function(BuildContext, String);
|
|||||||
class AttachmentUploadStateBuilder extends StatelessWidget {
|
class AttachmentUploadStateBuilder extends StatelessWidget {
|
||||||
final Message message;
|
final Message message;
|
||||||
final Attachment attachment;
|
final Attachment attachment;
|
||||||
final FailedBuilder failedBuilder;
|
final FailedBuilder? failedBuilder;
|
||||||
final WidgetBuilder successBuilder;
|
final WidgetBuilder? successBuilder;
|
||||||
final InProgressBuilder inProgressBuilder;
|
final InProgressBuilder? inProgressBuilder;
|
||||||
final WidgetBuilder preparingBuilder;
|
final WidgetBuilder? preparingBuilder;
|
||||||
|
|
||||||
const AttachmentUploadStateBuilder({
|
const AttachmentUploadStateBuilder({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.attachment,
|
required this.attachment,
|
||||||
this.failedBuilder,
|
this.failedBuilder,
|
||||||
this.successBuilder,
|
this.successBuilder,
|
||||||
this.inProgressBuilder,
|
this.inProgressBuilder,
|
||||||
this.preparingBuilder,
|
this.preparingBuilder,
|
||||||
}) : assert(message != null),
|
}) : super(key: key);
|
||||||
assert(attachment != null),
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (message.status == null || message.status == MessageSendingStatus.sent) {
|
if (message.status == MessageSendingStatus.sent) {
|
||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
|
|
||||||
final messageId = message.id;
|
final messageId = message.id;
|
||||||
final attachmentId = attachment.id;
|
final attachmentId = attachment.id;
|
||||||
|
|
||||||
var inProgress = inProgressBuilder;
|
final inProgress = inProgressBuilder ??
|
||||||
inProgress ??= (context, int sent, int total) {
|
(context, int sent, int total) {
|
||||||
return _InProgressState(
|
return _InProgressState(
|
||||||
sent: sent,
|
sent: sent,
|
||||||
total: total,
|
total: total,
|
||||||
attachmentId: attachmentId,
|
attachmentId: attachmentId,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var failed = failedBuilder;
|
final failed = failedBuilder ??
|
||||||
failed ??= (context, error) {
|
(context, error) {
|
||||||
return _FailedState(
|
return _FailedState(
|
||||||
error: error,
|
error: error,
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
attachmentId: attachmentId,
|
attachmentId: attachmentId,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var success = successBuilder;
|
final success = successBuilder ?? (context) => _SuccessState();
|
||||||
success ??= (context) => _SuccessState();
|
|
||||||
|
|
||||||
var preparing = preparingBuilder;
|
final preparing = preparingBuilder ??
|
||||||
preparing ??= (context) => _PreparingState(attachmentId: attachmentId);
|
(context) => _PreparingState(attachmentId: attachmentId);
|
||||||
|
|
||||||
return attachment.uploadState.when(
|
return attachment.uploadState.when(
|
||||||
preparing: () => preparing(context),
|
preparing: () => preparing(context),
|
||||||
@@ -68,13 +65,13 @@ class AttachmentUploadStateBuilder extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _IconButton extends StatelessWidget {
|
class _IconButton extends StatelessWidget {
|
||||||
final Widget icon;
|
final Widget? icon;
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final Color fillColor;
|
final Color? fillColor;
|
||||||
|
|
||||||
const _IconButton({
|
const _IconButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.iconSize = 24.0,
|
this.iconSize = 24.0,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
@@ -95,7 +92,9 @@ class _IconButton extends StatelessWidget {
|
|||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
fillColor:
|
fillColor:
|
||||||
fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark,
|
fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
child: icon,
|
child: icon,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -106,8 +105,8 @@ class _PreparingState extends StatelessWidget {
|
|||||||
final String attachmentId;
|
final String attachmentId;
|
||||||
|
|
||||||
const _PreparingState({
|
const _PreparingState({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.attachmentId,
|
required this.attachmentId,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -144,10 +143,10 @@ class _InProgressState extends StatelessWidget {
|
|||||||
final String attachmentId;
|
final String attachmentId;
|
||||||
|
|
||||||
const _InProgressState({
|
const _InProgressState({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.sent,
|
required this.sent,
|
||||||
@required this.total,
|
required this.total,
|
||||||
@required this.attachmentId,
|
required this.attachmentId,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -179,15 +178,15 @@ class _InProgressState extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FailedState extends StatelessWidget {
|
class _FailedState extends StatelessWidget {
|
||||||
final String error;
|
final String? error;
|
||||||
final String messageId;
|
final String messageId;
|
||||||
final String attachmentId;
|
final String attachmentId;
|
||||||
|
|
||||||
const _FailedState({
|
const _FailedState({
|
||||||
Key key,
|
Key? key,
|
||||||
this.error,
|
this.error,
|
||||||
@required this.messageId,
|
required this.messageId,
|
||||||
@required this.attachmentId,
|
required this.attachmentId,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -203,7 +202,7 @@ class _FailedState extends StatelessWidget {
|
|||||||
color: theme.colorTheme.white,
|
color: theme.colorTheme.white,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
return channel.retryAttachmentUpload(messageId, attachmentId);
|
channel.retryAttachmentUpload(messageId, attachmentId);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
@@ -213,7 +212,10 @@ class _FailedState extends StatelessWidget {
|
|||||||
color: theme.colorTheme.overlayDark.withOpacity(0.6),
|
color: theme.colorTheme.overlayDark.withOpacity(0.6),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 6,
|
||||||
|
horizontal: 12,
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'UPLOAD ERROR',
|
'UPLOAD ERROR',
|
||||||
style: theme.textTheme.footnote.copyWith(
|
style: theme.textTheme.footnote.copyWith(
|
||||||
|
|||||||
@@ -13,15 +13,9 @@ extension AttachmentSourceX on AttachmentSource {
|
|||||||
/// Its prototype depends on the AttachmentSource defined.
|
/// Its prototype depends on the AttachmentSource defined.
|
||||||
// ignore: missing_return
|
// ignore: missing_return
|
||||||
T when<T>({
|
T when<T>({
|
||||||
@required T Function() local,
|
required T Function() local,
|
||||||
@required T Function() network,
|
required T Function() network,
|
||||||
}) {
|
}) {
|
||||||
assert(() {
|
|
||||||
if (local == null || network == null) {
|
|
||||||
throw 'check for all possible cases';
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}());
|
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case AttachmentSource.local:
|
case AttachmentSource.local:
|
||||||
return local();
|
return local();
|
||||||
@@ -32,30 +26,32 @@ extension AttachmentSourceX on AttachmentSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class AttachmentWidget extends StatelessWidget {
|
abstract class AttachmentWidget extends StatelessWidget {
|
||||||
final Size size;
|
final Size? size;
|
||||||
|
final AttachmentSource? _source;
|
||||||
final Message message;
|
final Message message;
|
||||||
final Attachment attachment;
|
final Attachment attachment;
|
||||||
final AttachmentSource _source;
|
|
||||||
|
|
||||||
AttachmentSource get source => _source ?? attachment.file != null
|
AttachmentSource get source =>
|
||||||
? AttachmentSource.local
|
_source ??
|
||||||
: AttachmentSource.network;
|
(attachment.file != null
|
||||||
|
? AttachmentSource.local
|
||||||
|
: AttachmentSource.network);
|
||||||
|
|
||||||
const AttachmentWidget({
|
const AttachmentWidget({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.attachment,
|
required this.attachment,
|
||||||
this.size,
|
this.size,
|
||||||
AttachmentSource source,
|
AttachmentSource? source,
|
||||||
}) : _source = source,
|
}) : _source = source,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AttachmentError extends StatelessWidget {
|
class AttachmentError extends StatelessWidget {
|
||||||
final Size size;
|
final Size? size;
|
||||||
|
|
||||||
const AttachmentError({
|
const AttachmentError({
|
||||||
Key key,
|
Key? key,
|
||||||
this.size,
|
this.size,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|||||||
@@ -11,19 +11,24 @@ import '../upload_progress_indicator.dart';
|
|||||||
import 'attachment_widget.dart';
|
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;
|
final VoidCallback? onAttachmentTap;
|
||||||
|
|
||||||
const FileAttachment({
|
const FileAttachment({
|
||||||
Key key,
|
Key? key,
|
||||||
@required Message message,
|
required Message message,
|
||||||
@required Attachment attachment,
|
required Attachment attachment,
|
||||||
Size size,
|
Size? size,
|
||||||
this.title,
|
this.title,
|
||||||
this.trailing,
|
this.trailing,
|
||||||
this.onAttachmentTap,
|
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,6 +36,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||||
return Material(
|
return Material(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: onAttachmentTap,
|
onTap: onAttachmentTap,
|
||||||
@@ -38,10 +44,10 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
width: size?.width ?? 100,
|
width: size?.width ?? 100,
|
||||||
height: 56.0,
|
height: 56.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: colorTheme.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
color: colorTheme.greyWhisper,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -60,7 +66,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
attachment?.title ?? 'File',
|
attachment.title ?? 'File',
|
||||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
@@ -93,34 +99,51 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
shape: _getDefaultShape(context),
|
shape: _getDefaultShape(context),
|
||||||
child: source.when(
|
child: source.when(
|
||||||
local: () => Image.memory(
|
local: () {
|
||||||
attachment.file.bytes,
|
if (attachment.file?.bytes == null) {
|
||||||
fit: BoxFit.cover,
|
return getFileTypeImage(attachment.extraData['other'] as String?);
|
||||||
errorBuilder: (_, obj, trace) {
|
}
|
||||||
return getFileTypeImage(attachment.extraData['other']);
|
return Image.memory(
|
||||||
},
|
attachment.file!.bytes!,
|
||||||
),
|
fit: BoxFit.cover,
|
||||||
network: () => CachedNetworkImage(
|
errorBuilder: (_, obj, trace) {
|
||||||
imageUrl: attachment.imageUrl ??
|
return getFileTypeImage(
|
||||||
attachment.assetUrl ??
|
attachment.extraData['other'] as String?);
|
||||||
attachment.thumbUrl,
|
},
|
||||||
fit: BoxFit.cover,
|
);
|
||||||
errorWidget: (_, obj, trace) {
|
},
|
||||||
return getFileTypeImage(attachment.extraData['other']);
|
network: () {
|
||||||
},
|
if ((attachment.imageUrl ??
|
||||||
placeholder: (_, __) {
|
attachment.assetUrl ??
|
||||||
return Shimmer.fromColors(
|
attachment.thumbUrl) ==
|
||||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
null) {
|
||||||
highlightColor:
|
return getFileTypeImage(attachment.extraData['other'] as String?);
|
||||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
}
|
||||||
child: Image.asset(
|
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',
|
'images/placeholder.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
package: 'stream_chat_flutter',
|
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),
|
shape: _getDefaultShape(context),
|
||||||
child: source.when(
|
child: source.when(
|
||||||
local: () => VideoThumbnailImage(
|
local: () => VideoThumbnailImage(
|
||||||
video: attachment.file.path,
|
video: attachment.file?.path,
|
||||||
placeholderBuilder: (_) {
|
placeholderBuilder: (_) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
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 _buildButton({
|
||||||
Widget icon,
|
Widget? icon,
|
||||||
double iconSize = 24.0,
|
double iconSize = 24.0,
|
||||||
VoidCallback onPressed,
|
VoidCallback? onPressed,
|
||||||
Color fillColor,
|
Color? fillColor,
|
||||||
}) {
|
}) {
|
||||||
return Container(
|
return Container(
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
@@ -189,7 +212,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
final attachmentId = attachment.id;
|
final attachmentId = attachment.id;
|
||||||
var trailingWidget = trailing;
|
var trailingWidget = trailing;
|
||||||
trailingWidget ??= attachment.uploadState?.when(
|
trailingWidget ??= attachment.uploadState.when(
|
||||||
preparing: () => Padding(
|
preparing: () => Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: _buildButton(
|
child: _buildButton(
|
||||||
@@ -220,7 +243,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
icon: StreamSvgIcon.retry(color: theme.colorTheme.white),
|
icon: StreamSvgIcon.retry(color: theme.colorTheme.white),
|
||||||
fillColor: theme.colorTheme.overlayDark,
|
fillColor: theme.colorTheme.overlayDark,
|
||||||
onPressed: () => channel.retryAttachmentUpload(
|
onPressed: () => channel.retryAttachmentUpload(
|
||||||
message?.id,
|
message.id,
|
||||||
attachmentId,
|
attachmentId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -236,9 +259,7 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (message != null &&
|
if (message.status == MessageSendingStatus.sent) {
|
||||||
(message.status == null ||
|
|
||||||
message.status == MessageSendingStatus.sent)) {
|
|
||||||
trailingWidget = IconButton(
|
trailingWidget = IconButton(
|
||||||
icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black),
|
icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@@ -262,11 +283,8 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
final textStyle = theme.textTheme.footnote.copyWith(
|
final textStyle = theme.textTheme.footnote.copyWith(
|
||||||
color: theme.colorTheme.grey,
|
color: theme.colorTheme.grey,
|
||||||
);
|
);
|
||||||
return attachment.uploadState?.when(
|
return attachment.uploadState.when(
|
||||||
preparing: () {
|
preparing: () {
|
||||||
if (message == null) {
|
|
||||||
return Text('${fileSize(size, 2)}', style: textStyle);
|
|
||||||
}
|
|
||||||
return UploadProgressIndicator(
|
return UploadProgressIndicator(
|
||||||
uploaded: 0,
|
uploaded: 0,
|
||||||
total: double.maxFinite.toInt(),
|
total: double.maxFinite.toInt(),
|
||||||
|
|||||||
@@ -9,17 +9,15 @@ import '../stream_svg_icon.dart';
|
|||||||
import 'attachment_widget.dart';
|
import 'attachment_widget.dart';
|
||||||
|
|
||||||
class GiphyAttachment extends AttachmentWidget {
|
class GiphyAttachment extends AttachmentWidget {
|
||||||
final MessageTheme messageTheme;
|
final ShowMessageCallback? onShowMessage;
|
||||||
final ShowMessageCallback onShowMessage;
|
final ValueChanged<ReturnActionType>? onReturnAction;
|
||||||
final ValueChanged<ReturnActionType> onReturnAction;
|
final VoidCallback? onAttachmentTap;
|
||||||
final VoidCallback onAttachmentTap;
|
|
||||||
|
|
||||||
const GiphyAttachment({
|
const GiphyAttachment({
|
||||||
Key key,
|
Key? key,
|
||||||
@required Message message,
|
required Message message,
|
||||||
@required Attachment attachment,
|
required Attachment attachment,
|
||||||
Size size,
|
Size? size,
|
||||||
this.messageTheme,
|
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
@@ -29,10 +27,10 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final imageUrl =
|
final imageUrl =
|
||||||
attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl;
|
attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl;
|
||||||
if (imageUrl == null && source == AttachmentSource.network) {
|
if (imageUrl == null) {
|
||||||
return AttachmentError();
|
return AttachmentError();
|
||||||
}
|
}
|
||||||
if (attachment.actions != null) {
|
if (attachment.actions.isNotEmpty) {
|
||||||
return _buildSendingAttachment(context, imageUrl);
|
return _buildSendingAttachment(context, imageUrl);
|
||||||
}
|
}
|
||||||
return _buildSentAttachment(context, imageUrl);
|
return _buildSentAttachment(context, imageUrl);
|
||||||
@@ -73,7 +71,7 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
if (attachment.title != null)
|
if (attachment.title != null)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
attachment.title,
|
attachment.title!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
@@ -199,10 +197,11 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Send',
|
'Send',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.accentBlue,
|
.accentBlue,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -259,8 +258,7 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: FullScreenMedia(
|
child: FullScreenMedia(
|
||||||
mediaAttachments: [attachment],
|
mediaAttachments: [attachment],
|
||||||
userName: message.user.name,
|
userName: message.user?.name,
|
||||||
sentAt: message.createdAt,
|
|
||||||
message: message,
|
message: message,
|
||||||
onShowMessage: onShowMessage,
|
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) {
|
Widget _buildSentAttachment(BuildContext context, String imageUrl) {
|
||||||
@@ -282,14 +280,13 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: FullScreenMedia(
|
child: FullScreenMedia(
|
||||||
mediaAttachments: [attachment],
|
mediaAttachments: [attachment],
|
||||||
userName: message.user.name,
|
userName: message.user?.name,
|
||||||
sentAt: message.createdAt,
|
|
||||||
message: message,
|
message: message,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
if (res != null) onReturnAction(res);
|
if (res != null) onReturnAction!(res);
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -297,16 +294,17 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
height: size?.height,
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
placeholder: (_, __) {
|
placeholder: (_, __) {
|
||||||
|
final image = Image.asset(
|
||||||
|
'images/placeholder.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
package: 'stream_chat_flutter',
|
||||||
|
);
|
||||||
|
|
||||||
|
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor:
|
baseColor: colorTheme.greyGainsboro,
|
||||||
StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
highlightColor: colorTheme.whiteSmoke,
|
||||||
highlightColor:
|
child: image,
|
||||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
|
||||||
child: Image.asset(
|
|
||||||
'images/placeholder.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
package: 'stream_chat_flutter',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
|
|||||||
@@ -10,35 +10,40 @@ import 'attachment_title.dart';
|
|||||||
import 'attachment_widget.dart';
|
import 'attachment_widget.dart';
|
||||||
|
|
||||||
class ImageAttachment extends AttachmentWidget {
|
class ImageAttachment extends AttachmentWidget {
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
final ShowMessageCallback onShowMessage;
|
final ShowMessageCallback? onShowMessage;
|
||||||
final ValueChanged<ReturnActionType> onReturnAction;
|
final ValueChanged<ReturnActionType>? onReturnAction;
|
||||||
final VoidCallback onAttachmentTap;
|
final VoidCallback? onAttachmentTap;
|
||||||
|
|
||||||
const ImageAttachment({
|
const ImageAttachment({
|
||||||
Key key,
|
Key? key,
|
||||||
@required Message message,
|
required Message message,
|
||||||
@required Attachment attachment,
|
required Attachment attachment,
|
||||||
Size size,
|
Size? size,
|
||||||
this.messageTheme,
|
this.messageTheme,
|
||||||
this.showTitle = false,
|
this.showTitle = false,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
}) : super(key: key, message: message, attachment: attachment, size: size);
|
}) : super(
|
||||||
|
key: key,
|
||||||
|
message: message,
|
||||||
|
attachment: attachment,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return source.when(
|
return source.when(
|
||||||
local: () {
|
local: () {
|
||||||
if (attachment.localUri == null) {
|
if (attachment.localUri == null || attachment.file?.bytes == null) {
|
||||||
return AttachmentError(size: size);
|
return AttachmentError(size: size);
|
||||||
}
|
}
|
||||||
return _buildImageAttachment(
|
return _buildImageAttachment(
|
||||||
context,
|
context,
|
||||||
Image.memory(
|
Image.memory(
|
||||||
attachment.file.bytes,
|
attachment.file!.bytes!,
|
||||||
height: size?.height,
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@@ -85,15 +90,16 @@ class ImageAttachment extends AttachmentWidget {
|
|||||||
height: size?.height,
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
placeholder: (_, __) {
|
placeholder: (_, __) {
|
||||||
|
final image = Image.asset(
|
||||||
|
'images/placeholder.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
package: 'stream_chat_flutter',
|
||||||
|
);
|
||||||
|
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
baseColor: colorTheme.greyGainsboro,
|
||||||
highlightColor:
|
highlightColor: colorTheme.whiteSmoke,
|
||||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
child: image,
|
||||||
child: Image.asset(
|
|
||||||
'images/placeholder.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
package: 'stream_chat_flutter',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
@@ -109,7 +115,7 @@ class ImageAttachment extends AttachmentWidget {
|
|||||||
|
|
||||||
Widget _buildImageAttachment(BuildContext context, Widget imageWidget) {
|
Widget _buildImageAttachment(BuildContext context, Widget imageWidget) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints.loose(size),
|
constraints: BoxConstraints.loose(size!),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -127,8 +133,7 @@ class ImageAttachment extends AttachmentWidget {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: FullScreenMedia(
|
child: FullScreenMedia(
|
||||||
mediaAttachments: [attachment],
|
mediaAttachments: [attachment],
|
||||||
userName: message.user.name,
|
userName: message.user?.name,
|
||||||
sentAt: message.createdAt,
|
|
||||||
message: message,
|
message: message,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
),
|
),
|
||||||
@@ -136,7 +141,7 @@ class ImageAttachment extends AttachmentWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (result != null) onReturnAction(result);
|
if (result != null) onReturnAction?.call(result);
|
||||||
},
|
},
|
||||||
child: imageWidget,
|
child: imageWidget,
|
||||||
),
|
),
|
||||||
@@ -152,7 +157,7 @@ class ImageAttachment extends AttachmentWidget {
|
|||||||
),
|
),
|
||||||
if (showTitle && attachment.title != null)
|
if (showTitle && attachment.title != null)
|
||||||
Material(
|
Material(
|
||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme?.messageBackgroundColor,
|
||||||
child: AttachmentTitle(
|
child: AttachmentTitle(
|
||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
|
|||||||
@@ -8,21 +8,26 @@ import 'attachment_upload_state_builder.dart';
|
|||||||
import 'attachment_widget.dart';
|
import 'attachment_widget.dart';
|
||||||
|
|
||||||
class VideoAttachment extends AttachmentWidget {
|
class VideoAttachment 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;
|
final VoidCallback? onAttachmentTap;
|
||||||
|
|
||||||
const VideoAttachment({
|
const VideoAttachment({
|
||||||
Key key,
|
Key? key,
|
||||||
@required Message message,
|
required Message message,
|
||||||
@required Attachment attachment,
|
required Attachment attachment,
|
||||||
Size size,
|
Size? size,
|
||||||
this.messageTheme,
|
this.messageTheme,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
}) : super(key: key, message: message, attachment: attachment, size: size);
|
}) : super(
|
||||||
|
key: key,
|
||||||
|
message: message,
|
||||||
|
attachment: attachment,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -34,7 +39,7 @@ class VideoAttachment extends AttachmentWidget {
|
|||||||
return _buildVideoAttachment(
|
return _buildVideoAttachment(
|
||||||
context,
|
context,
|
||||||
VideoThumbnailImage(
|
VideoThumbnailImage(
|
||||||
video: attachment.file.path,
|
video: attachment.file?.path,
|
||||||
height: size?.height,
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@@ -62,7 +67,7 @@ class VideoAttachment extends AttachmentWidget {
|
|||||||
|
|
||||||
Widget _buildVideoAttachment(BuildContext context, Widget videoWidget) {
|
Widget _buildVideoAttachment(BuildContext context, Widget videoWidget) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints.loose(size),
|
constraints: BoxConstraints.loose(size ?? Size.infinite),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -77,15 +82,14 @@ class VideoAttachment extends AttachmentWidget {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: FullScreenMedia(
|
child: FullScreenMedia(
|
||||||
mediaAttachments: [attachment],
|
mediaAttachments: [attachment],
|
||||||
userName: message.user.name,
|
userName: message.user?.name,
|
||||||
sentAt: message.createdAt,
|
|
||||||
message: message,
|
message: message,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (res != null) onReturnAction(res);
|
if (res != null) onReturnAction?.call(res);
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -112,7 +116,7 @@ class VideoAttachment extends AttachmentWidget {
|
|||||||
),
|
),
|
||||||
if (attachment.title != null)
|
if (attachment.title != null)
|
||||||
Material(
|
Material(
|
||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme?.messageBackgroundColor,
|
||||||
child: AttachmentTitle(
|
child: AttachmentTitle(
|
||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import 'extension.dart';
|
|||||||
/// Callback to download an attachment asset
|
/// Callback to download an attachment asset
|
||||||
typedef AttachmentDownloader = Future<String> Function(
|
typedef AttachmentDownloader = Future<String> Function(
|
||||||
Attachment attachment, {
|
Attachment attachment, {
|
||||||
ProgressCallback progressCallback,
|
ProgressCallback? progressCallback,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Widget that shows the options in the gallery view
|
/// Widget that shows the options in the gallery view
|
||||||
@@ -21,25 +21,25 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
/// Current page index
|
/// Current page index
|
||||||
final currentIndex;
|
final int currentIndex;
|
||||||
|
|
||||||
/// Callback to show the message
|
/// Callback to show the message
|
||||||
final VoidCallback onShowMessage;
|
final VoidCallback? onShowMessage;
|
||||||
|
|
||||||
/// Callback to download images
|
/// Callback to download images
|
||||||
final AttachmentDownloader imageDownloader;
|
final AttachmentDownloader? imageDownloader;
|
||||||
|
|
||||||
/// Callback to provide download files
|
/// Callback to provide download files
|
||||||
final AttachmentDownloader fileDownloader;
|
final AttachmentDownloader? fileDownloader;
|
||||||
|
|
||||||
/// Returns a new [AttachmentActionsModal]
|
/// Returns a new [AttachmentActionsModal]
|
||||||
const AttachmentActionsModal({
|
const AttachmentActionsModal({
|
||||||
@required this.currentIndex,
|
required this.currentIndex,
|
||||||
this.message,
|
required this.message,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.imageDownloader,
|
this.imageDownloader,
|
||||||
this.fileDownloader,
|
this.fileDownloader,
|
||||||
}) : assert(currentIndex != null, 'currentIndex cannot be null');
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -99,11 +99,16 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
() {
|
() {
|
||||||
final attachment = message.attachments[currentIndex];
|
final attachment = message.attachments[currentIndex];
|
||||||
final isImage = attachment.type == 'image';
|
final isImage = attachment.type == 'image';
|
||||||
final saveFile = fileDownloader ?? _downloadAttachment;
|
final Future<String?> Function(Attachment,
|
||||||
final saveImage = imageDownloader ?? _downloadAttachment;
|
{void Function(int, int) progressCallback})
|
||||||
|
saveFile = fileDownloader ?? _downloadAttachment;
|
||||||
|
final Future<String?> Function(Attachment,
|
||||||
|
{void Function(int, int) progressCallback})
|
||||||
|
saveImage = imageDownloader ?? _downloadAttachment;
|
||||||
final downloader = isImage ? saveImage : saveFile;
|
final downloader = isImage ? saveImage : saveFile;
|
||||||
|
|
||||||
final progressNotifier = ValueNotifier<_DownloadProgress>(
|
final progressNotifier =
|
||||||
|
ValueNotifier<_DownloadProgress?>(
|
||||||
_DownloadProgress.initial(),
|
_DownloadProgress.initial(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -134,7 +139,7 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (StreamChat.of(context).user.id == message.user.id)
|
if (StreamChat.of(context).user?.id == message.user?.id)
|
||||||
_buildButton(
|
_buildButton(
|
||||||
context,
|
context,
|
||||||
'Delete',
|
'Delete',
|
||||||
@@ -164,8 +169,10 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
color: theme.colorTheme.accentRed,
|
color: theme.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.map<Widget>((e) =>
|
.map<Widget>((e) => Align(
|
||||||
Align(alignment: Alignment.centerRight, child: e))
|
alignment: Alignment.centerRight,
|
||||||
|
child: e,
|
||||||
|
))
|
||||||
.insertBetween(
|
.insertBetween(
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
@@ -184,9 +191,9 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
String title,
|
String title,
|
||||||
StreamSvgIcon icon,
|
StreamSvgIcon icon,
|
||||||
VoidCallback onTap, {
|
VoidCallback? onTap, {
|
||||||
Color color,
|
Color? color,
|
||||||
Key key,
|
Key? key,
|
||||||
}) {
|
}) {
|
||||||
return Material(
|
return Material(
|
||||||
key: key,
|
key: key,
|
||||||
@@ -215,16 +222,16 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildDownloadProgressDialog(
|
Widget _buildDownloadProgressDialog(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
ValueNotifier<_DownloadProgress> progressNotifier,
|
ValueNotifier<_DownloadProgress?> progressNotifier,
|
||||||
) {
|
) {
|
||||||
final theme = StreamChatTheme.of(context);
|
final theme = StreamChatTheme.of(context);
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () => Future.value(false),
|
onWillPop: () => Future.value(false),
|
||||||
child: ValueListenableBuilder(
|
child: ValueListenableBuilder(
|
||||||
valueListenable: progressNotifier,
|
valueListenable: progressNotifier,
|
||||||
builder: (_, _DownloadProgress progress, __) {
|
builder: (_, _DownloadProgress? progress, __) {
|
||||||
// Pop the dialog in case the progress is null or it's completed.
|
// Pop the dialog in case the progress is null or it's completed.
|
||||||
if (progress == null || progress?.toProgressIndicatorValue == 1.0) {
|
if (progress == null || progress.toProgressIndicatorValue == 1.0) {
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
const Duration(milliseconds: 500),
|
const Duration(milliseconds: 500),
|
||||||
Navigator.of(context).maybePop,
|
Navigator.of(context).maybePop,
|
||||||
@@ -291,23 +298,23 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> _downloadAttachment(
|
Future<String?> _downloadAttachment(
|
||||||
Attachment attachment, {
|
Attachment attachment, {
|
||||||
ProgressCallback progressCallback,
|
ProgressCallback? progressCallback,
|
||||||
}) async {
|
}) async {
|
||||||
String filePath;
|
String? filePath;
|
||||||
final appDocDir = await getTemporaryDirectory();
|
final appDocDir = await getTemporaryDirectory();
|
||||||
await Dio().download(
|
await Dio().download(
|
||||||
attachment.assetUrl ?? attachment.imageUrl ?? attachment.thumbUrl,
|
attachment.assetUrl ?? attachment.imageUrl ?? attachment.thumbUrl!,
|
||||||
(Headers responseHeaders) {
|
(Headers responseHeaders) {
|
||||||
final contentType = responseHeaders[Headers.contentTypeHeader];
|
final contentType = responseHeaders[Headers.contentTypeHeader]!;
|
||||||
final mimeType = contentType.first?.split('/')?.last;
|
final mimeType = contentType.first.split('/').last;
|
||||||
filePath ??= '${appDocDir.path}/${attachment.id}.$mimeType';
|
filePath ??= '${appDocDir.path}/${attachment.id}.$mimeType';
|
||||||
return filePath;
|
return filePath;
|
||||||
},
|
},
|
||||||
onReceiveProgress: progressCallback,
|
onReceiveProgress: progressCallback,
|
||||||
);
|
);
|
||||||
final result = await ImageGallerySaver.saveFile(filePath);
|
final result = await ImageGallerySaver.saveFile(filePath!);
|
||||||
return (result as Map)['filePath'];
|
return (result as Map)['filePath'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import '../stream_chat_flutter.dart';
|
|||||||
|
|
||||||
class StreamBackButton extends StatelessWidget {
|
class StreamBackButton extends StatelessWidget {
|
||||||
const StreamBackButton({
|
const StreamBackButton({
|
||||||
Key key,
|
Key? key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.showUnreads = false,
|
this.showUnreads = false,
|
||||||
this.cid,
|
this.cid,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final bool showUnreads;
|
final bool showUnreads;
|
||||||
|
|
||||||
/// Channel cid used to retrieve unread count
|
/// Channel cid used to retrieve unread count
|
||||||
final String cid;
|
final String? cid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -34,7 +34,7 @@ class StreamBackButton extends StatelessWidget {
|
|||||||
hoverElevation: 0,
|
hoverElevation: 0,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (onPressed != null) {
|
if (onPressed != null) {
|
||||||
onPressed();
|
onPressed!();
|
||||||
} else {
|
} else {
|
||||||
Navigator.maybePop(context);
|
Navigator.maybePop(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'channel_info.dart';
|
|||||||
import 'option_list_tile.dart';
|
import 'option_list_tile.dart';
|
||||||
|
|
||||||
class ChannelBottomSheet extends StatefulWidget {
|
class ChannelBottomSheet extends StatefulWidget {
|
||||||
final VoidCallback onViewInfoTap;
|
final VoidCallback? onViewInfoTap;
|
||||||
|
|
||||||
ChannelBottomSheet({this.onViewInfoTap});
|
ChannelBottomSheet({this.onViewInfoTap});
|
||||||
|
|
||||||
@@ -18,13 +18,13 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
var members = channel.state.members;
|
final members = channel.state?.members ?? [];
|
||||||
|
|
||||||
var userAsMember =
|
final userAsMember = members
|
||||||
members.firstWhere((e) => e.user.id == StreamChat.of(context).user.id);
|
.firstWhere((e) => e.user?.id == StreamChat.of(context).user?.id);
|
||||||
var isOwner = userAsMember.role == 'owner';
|
final isOwner = userAsMember.role == 'owner';
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: StreamChatTheme.of(context).colorTheme.white,
|
||||||
@@ -73,8 +73,8 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
UserAvatar(
|
UserAvatar(
|
||||||
user: members
|
user: members
|
||||||
.firstWhere(
|
.firstWhere(
|
||||||
(e) => e.user.id != userAsMember.user.id)
|
(e) => e.user?.id != userAsMember.user?.id)
|
||||||
.user,
|
.user!,
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxHeight: 64.0,
|
maxHeight: 64.0,
|
||||||
maxWidth: 64.0,
|
maxWidth: 64.0,
|
||||||
@@ -88,10 +88,11 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
members
|
members
|
||||||
.firstWhere(
|
.firstWhere(
|
||||||
(e) => e.user.id != userAsMember.user.id)
|
(e) => e.user?.id != userAsMember.user?.id)
|
||||||
.user
|
.user
|
||||||
.name,
|
?.name ??
|
||||||
|
'',
|
||||||
style:
|
style:
|
||||||
StreamChatTheme.of(context).textTheme.footnoteBold,
|
StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@@ -113,7 +114,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
user: members[index].user,
|
user: members[index].user!,
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
height: 64.0,
|
height: 64.0,
|
||||||
width: 64.0,
|
width: 64.0,
|
||||||
@@ -126,7 +127,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
height: 6.0,
|
height: 6.0,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
members[index].user.name,
|
members[index].user?.name ?? '',
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.footnoteBold,
|
.footnoteBold,
|
||||||
@@ -220,7 +221,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
var channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
await channel.delete();
|
await channel.delete();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@@ -240,7 +241,10 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
);
|
);
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
await channel.removeMembers([StreamChat.of(context).user.id]);
|
final user = StreamChat.of(context).user;
|
||||||
|
if (user != null) {
|
||||||
|
await channel.removeMembers([user.id]);
|
||||||
|
}
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
|
|
||||||
/// Callback to call when pressing the back button.
|
/// Callback to call when pressing the back button.
|
||||||
/// By default it calls [Navigator.pop]
|
/// By default it calls [Navigator.pop]
|
||||||
final VoidCallback onBackPressed;
|
final VoidCallback? onBackPressed;
|
||||||
|
|
||||||
/// Callback to call when the header is tapped.
|
/// Callback to call when the header is tapped.
|
||||||
final VoidCallback onTitleTap;
|
final VoidCallback? onTitleTap;
|
||||||
|
|
||||||
/// Callback to call when the image is tapped.
|
/// Callback to call when the image is tapped.
|
||||||
final VoidCallback onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
/// If true the typing indicator will be rendered if a user is typing
|
/// If true the typing indicator will be rendered if a user is typing
|
||||||
final bool showTypingIndicator;
|
final bool showTypingIndicator;
|
||||||
@@ -72,21 +72,21 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final bool showConnectionStateTile;
|
final bool showConnectionStateTile;
|
||||||
|
|
||||||
/// Title widget
|
/// Title widget
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
|
|
||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// AppBar actions
|
/// AppBar actions
|
||||||
/// By default it shows the [ChannelImage]
|
/// By default it shows the [ChannelImage]
|
||||||
final List<Widget> actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
/// Creates a channel header
|
/// Creates a channel header
|
||||||
ChannelHeader({
|
ChannelHeader({
|
||||||
Key key,
|
Key? key,
|
||||||
this.showBackButton = true,
|
this.showBackButton = true,
|
||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
this.onTitleTap,
|
this.onTitleTap,
|
||||||
@@ -151,12 +151,12 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
.channelTheme
|
.channelTheme
|
||||||
.channelHeaderTheme
|
.channelHeaderTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius,
|
?.borderRadius,
|
||||||
constraints: StreamChatTheme.of(context)
|
constraints: StreamChatTheme.of(context)
|
||||||
.channelTheme
|
.channelTheme
|
||||||
.channelHeaderTheme
|
.channelHeaderTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
onTap: onImageTap,
|
onTap: onImageTap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
class ChannelImage extends StatelessWidget {
|
class ChannelImage extends StatelessWidget {
|
||||||
/// Instantiate a new ChannelImage
|
/// Instantiate a new ChannelImage
|
||||||
const ChannelImage({
|
const ChannelImage({
|
||||||
Key key,
|
Key? key,
|
||||||
this.channel,
|
this.channel,
|
||||||
this.constraints,
|
this.constraints,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
@@ -56,20 +56,20 @@ class ChannelImage extends StatelessWidget {
|
|||||||
this.selectionThickness = 4,
|
this.selectionThickness = 4,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
|
|
||||||
/// The channel to show the image of
|
/// The channel to show the image of
|
||||||
final Channel channel;
|
final Channel? channel;
|
||||||
|
|
||||||
/// The diameter of the image
|
/// The diameter of the image
|
||||||
final BoxConstraints constraints;
|
final BoxConstraints? constraints;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
final bool selected;
|
final bool selected;
|
||||||
|
|
||||||
final Color selectionColor;
|
final Color? selectionColor;
|
||||||
|
|
||||||
final double selectionThickness;
|
final double selectionThickness;
|
||||||
|
|
||||||
@@ -81,30 +81,30 @@ class ChannelImage extends StatelessWidget {
|
|||||||
stream: channel.extraDataStream,
|
stream: channel.extraDataStream,
|
||||||
initialData: channel.extraData,
|
initialData: channel.extraData,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
String image;
|
String? image;
|
||||||
if (snapshot.data?.containsKey('image') == true) {
|
if (snapshot.data!.containsKey('image') == true) {
|
||||||
image = snapshot.data['image'];
|
image = snapshot.data!['image'];
|
||||||
} else if (channel.state.members?.length == 2) {
|
} else if (channel.state?.members.length == 2) {
|
||||||
final otherMember = channel.state.members
|
final otherMember = channel.state?.members
|
||||||
.firstWhere((member) => member.user.id != streamChat.user.id);
|
.firstWhere((member) => member.user?.id != streamChat.user?.id);
|
||||||
return StreamBuilder<User>(
|
return StreamBuilder<User>(
|
||||||
stream: streamChat.client.state.usersStream
|
stream: streamChat.client.state.usersStream.map(
|
||||||
.map((users) => users[otherMember.userId]),
|
(users) => users[otherMember?.userId] ?? otherMember!.user!),
|
||||||
initialData: otherMember.user,
|
initialData: otherMember!.user,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return UserAvatar(
|
return UserAvatar(
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius,
|
?.borderRadius,
|
||||||
user: snapshot.data ?? otherMember.user,
|
user: snapshot.data ?? otherMember.user!,
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
onTap: onTap != null ? (_) => onTap() : null,
|
onTap: onTap != null ? (_) => onTap!() : null,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
selectionColor: selectionColor ??
|
selectionColor: selectionColor ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -112,25 +112,25 @@ class ChannelImage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final images = channel.state.members
|
final images = channel.state?.members
|
||||||
.where((member) =>
|
.where((member) =>
|
||||||
member.user.id != streamChat.user.id &&
|
member.user?.id != streamChat.user?.id &&
|
||||||
member.user.extraData['image'] != null)
|
member.user?.extraData['image'] != null)
|
||||||
.take(4)
|
.take(4)
|
||||||
.map((e) => e.user.extraData['image'] as String)
|
.map((e) => e.user?.extraData['image'] as String?)
|
||||||
.toList();
|
.toList();
|
||||||
return GroupImage(
|
return GroupImage(
|
||||||
images: images,
|
images: images ?? [],
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius,
|
?.borderRadius,
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
selectionColor: selectionColor ??
|
selectionColor: selectionColor ??
|
||||||
@@ -144,13 +144,13 @@ class ChannelImage extends StatelessWidget {
|
|||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius,
|
?.borderRadius,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
@@ -165,7 +165,7 @@ class ChannelImage extends StatelessWidget {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
snapshot.data?.containsKey('name') ?? false
|
snapshot.data?.containsKey('name') ?? false
|
||||||
? snapshot.data['name'][0]
|
? snapshot.data!['name'][0]
|
||||||
: '',
|
: '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
@@ -178,8 +178,10 @@ class ChannelImage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: StreamChatTheme.of(context)
|
: StreamChatTheme.of(context).defaultChannelImage(
|
||||||
.defaultChannelImage(context, channel),
|
context,
|
||||||
|
channel,
|
||||||
|
),
|
||||||
Material(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -197,14 +199,15 @@ class ChannelImage extends StatelessWidget {
|
|||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.ownMessageTheme
|
.ownMessageTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius) +
|
?.borderRadius ??
|
||||||
|
BorderRadius.zero) +
|
||||||
BorderRadius.circular(selectionThickness),
|
BorderRadius.circular(selectionThickness),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.ownMessageTheme
|
.ownMessageTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
color: selectionColor ??
|
color: selectionColor ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
@@ -8,14 +9,14 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
final Channel channel;
|
final Channel channel;
|
||||||
|
|
||||||
/// The style of the text displayed
|
/// The style of the text displayed
|
||||||
final TextStyle textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
/// If true the typing indicator will be rendered if a user is typing
|
/// If true the typing indicator will be rendered if a user is typing
|
||||||
final bool showTypingIndicator;
|
final bool showTypingIndicator;
|
||||||
|
|
||||||
const ChannelInfo({
|
const ChannelInfo({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.channel,
|
required this.channel,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.showTypingIndicator = true,
|
this.showTypingIndicator = true,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -24,8 +25,8 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
return StreamBuilder<List<Member>>(
|
return StreamBuilder<List<Member>>(
|
||||||
stream: channel.state.membersStream,
|
stream: channel.state?.membersStream,
|
||||||
initialData: channel.state.members,
|
initialData: channel.state?.members,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return ConnectionStatusBuilder(
|
return ConnectionStatusBuilder(
|
||||||
statusBuilder: (context, status) {
|
statusBuilder: (context, status) {
|
||||||
@@ -45,12 +46,13 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildConnectedTitleState(BuildContext context, List<Member> members) {
|
Widget _buildConnectedTitleState(
|
||||||
|
BuildContext context, List<Member>? members) {
|
||||||
var alternativeWidget;
|
var alternativeWidget;
|
||||||
|
|
||||||
if (channel.memberCount != null && channel.memberCount > 2) {
|
if (channel.memberCount != null && channel.memberCount! > 2) {
|
||||||
var text = '${channel.memberCount} Members';
|
var text = '${channel.memberCount} Members';
|
||||||
final watcherCount = channel.state.watcherCount ?? 0;
|
final watcherCount = channel.state?.watcherCount ?? 0;
|
||||||
if (watcherCount > 0) text += ' $watcherCount Online';
|
if (watcherCount > 0) text += ' $watcherCount Online';
|
||||||
alternativeWidget = Text(
|
alternativeWidget = Text(
|
||||||
text,
|
text,
|
||||||
@@ -60,20 +62,19 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
.subtitle,
|
.subtitle,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final otherMember = members.firstWhere(
|
final otherMember = members?.firstWhereOrNull(
|
||||||
(element) => element.userId != StreamChat.of(context).user.id,
|
(element) => element.userId != StreamChat.of(context).user?.id,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (otherMember != null) {
|
if (otherMember != null) {
|
||||||
if (otherMember.user.online) {
|
if (otherMember.user?.online == true) {
|
||||||
alternativeWidget = Text(
|
alternativeWidget = Text(
|
||||||
'Online',
|
'Online',
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
alternativeWidget = Text(
|
alternativeWidget = Text(
|
||||||
'Last seen ${Jiffy(otherMember.user.lastActive).fromNow()}',
|
'Last seen ${Jiffy(otherMember.user?.lastActive).fromNow()}',
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,9 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDisconnectedTitleState(
|
Widget _buildDisconnectedTitleState(
|
||||||
BuildContext context, StreamChatClient client) {
|
BuildContext context,
|
||||||
|
StreamChatClient client,
|
||||||
|
) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -131,11 +134,11 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await client.disconnect();
|
await client.disconnect();
|
||||||
return client.connect();
|
await client.connect();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Try Again',
|
'Try Again',
|
||||||
style: textStyle.copyWith(
|
style: textStyle?.copyWith(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import 'connection_status_builder.dart';
|
|||||||
import 'info_tile.dart';
|
import 'info_tile.dart';
|
||||||
import 'stream_chat.dart';
|
import 'stream_chat.dart';
|
||||||
|
|
||||||
typedef _TitleBuilder = Widget Function(
|
typedef TitleBuilder = Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
ConnectionStatus status,
|
ConnectionStatus status,
|
||||||
StreamChatClient client,
|
StreamChatClient client,
|
||||||
@@ -50,7 +50,7 @@ typedef _TitleBuilder = Widget Function(
|
|||||||
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||||
/// Instantiates a ChannelListHeader
|
/// Instantiates a ChannelListHeader
|
||||||
const ChannelListHeader({
|
const ChannelListHeader({
|
||||||
Key key,
|
Key? key,
|
||||||
this.client,
|
this.client,
|
||||||
this.titleBuilder,
|
this.titleBuilder,
|
||||||
this.onUserAvatarTap,
|
this.onUserAvatarTap,
|
||||||
@@ -63,32 +63,32 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Pass this if you don't have a [StreamChatClient] in your widget tree.
|
/// Pass this if you don't have a [StreamChatClient] in your widget tree.
|
||||||
final StreamChatClient client;
|
final StreamChatClient? client;
|
||||||
|
|
||||||
/// Use this to build your own title as per different [ConnectionStatus]
|
/// Use this to build your own title as per different [ConnectionStatus]
|
||||||
final _TitleBuilder titleBuilder;
|
final TitleBuilder? titleBuilder;
|
||||||
|
|
||||||
/// Callback to call when pressing the user avatar button.
|
/// Callback to call when pressing the user avatar button.
|
||||||
/// By default it calls Scaffold.of(context).openDrawer()
|
/// By default it calls Scaffold.of(context).openDrawer()
|
||||||
final Function(User) onUserAvatarTap;
|
final Function(User)? onUserAvatarTap;
|
||||||
|
|
||||||
/// Callback to call when pressing the new chat button.
|
/// Callback to call when pressing the new chat button.
|
||||||
final VoidCallback onNewChatButtonTap;
|
final VoidCallback? onNewChatButtonTap;
|
||||||
|
|
||||||
final bool showConnectionStateTile;
|
final bool showConnectionStateTile;
|
||||||
|
|
||||||
final VoidCallback preNavigationCallback;
|
final VoidCallback? preNavigationCallback;
|
||||||
|
|
||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
/// By default it shows the logged in user avatar
|
/// By default it shows the logged in user avatar
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// AppBar actions
|
/// AppBar actions
|
||||||
/// By default it shows the new chat button
|
/// By default it shows the new chat button
|
||||||
final List<Widget> actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -123,25 +123,27 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
Center(
|
Center(
|
||||||
child: UserAvatar(
|
child: user != null
|
||||||
user: user,
|
? UserAvatar(
|
||||||
showOnlineStatus: false,
|
user: user,
|
||||||
onTap: onUserAvatarTap ??
|
showOnlineStatus: false,
|
||||||
(_) {
|
onTap: onUserAvatarTap ??
|
||||||
if (preNavigationCallback != null) {
|
(_) {
|
||||||
preNavigationCallback();
|
if (preNavigationCallback != null) {
|
||||||
}
|
preNavigationCallback!();
|
||||||
Scaffold.of(context).openDrawer();
|
}
|
||||||
},
|
Scaffold.of(context).openDrawer();
|
||||||
borderRadius: StreamChatTheme.of(context)
|
},
|
||||||
.channelListHeaderTheme
|
borderRadius: StreamChatTheme.of(context)
|
||||||
.avatarTheme
|
.channelListHeaderTheme
|
||||||
.borderRadius,
|
.avatarTheme
|
||||||
constraints: StreamChatTheme.of(context)
|
?.borderRadius,
|
||||||
.channelListHeaderTheme
|
constraints: StreamChatTheme.of(context)
|
||||||
.avatarTheme
|
.channelListHeaderTheme
|
||||||
.constraints,
|
.avatarTheme
|
||||||
),
|
?.constraints,
|
||||||
|
)
|
||||||
|
: Offstage(),
|
||||||
),
|
),
|
||||||
actions: actions ??
|
actions: actions ??
|
||||||
[
|
[
|
||||||
@@ -181,7 +183,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
if (titleBuilder != null) {
|
if (titleBuilder != null) {
|
||||||
return titleBuilder(context, status, _client);
|
return titleBuilder!(context, status, _client);
|
||||||
}
|
}
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
@@ -225,40 +227,46 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Text(
|
Text(
|
||||||
'Searching for Network',
|
'Searching for Network',
|
||||||
style:
|
style: StreamChatTheme.of(context)
|
||||||
StreamChatTheme.of(context).channelListHeaderTheme.title.copyWith(
|
.channelListHeaderTheme
|
||||||
fontSize: 16,
|
.title
|
||||||
fontWeight: FontWeight.bold,
|
?.copyWith(
|
||||||
),
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDisconnectedTitleState(
|
Widget _buildDisconnectedTitleState(
|
||||||
BuildContext context, StreamChatClient client) {
|
BuildContext context,
|
||||||
|
StreamChatClient client,
|
||||||
|
) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Offline...',
|
'Offline...',
|
||||||
style:
|
style: StreamChatTheme.of(context)
|
||||||
StreamChatTheme.of(context).channelListHeaderTheme.title.copyWith(
|
.channelListHeaderTheme
|
||||||
fontSize: 16,
|
.title
|
||||||
fontWeight: FontWeight.bold,
|
?.copyWith(
|
||||||
),
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await client.disconnect();
|
await client.disconnect();
|
||||||
return client.connect();
|
await client.connect();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Try Again',
|
'Try Again',
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.channelListHeaderTheme
|
.channelListHeaderTheme
|
||||||
.title
|
.title
|
||||||
.copyWith(
|
?.copyWith(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
@@ -11,7 +12,7 @@ import 'channel_bottom_sheet.dart';
|
|||||||
import 'channel_preview.dart';
|
import 'channel_preview.dart';
|
||||||
|
|
||||||
/// Callback called when tapping on a channel
|
/// Callback called when tapping on a channel
|
||||||
typedef ChannelTapCallback = void Function(Channel, Widget);
|
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
||||||
|
|
||||||
/// Builder used to create a custom [ChannelPreview] from a [Channel]
|
/// Builder used to create a custom [ChannelPreview] from a [Channel]
|
||||||
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
|
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
|
||||||
@@ -54,7 +55,7 @@ typedef ViewInfoCallback = void Function(Channel);
|
|||||||
class ChannelListView extends StatefulWidget {
|
class ChannelListView extends StatefulWidget {
|
||||||
/// Instantiate a new ChannelListView
|
/// Instantiate a new ChannelListView
|
||||||
ChannelListView({
|
ChannelListView({
|
||||||
Key key,
|
Key? key,
|
||||||
this.filter,
|
this.filter,
|
||||||
this.options,
|
this.options,
|
||||||
this.sort,
|
this.sort,
|
||||||
@@ -90,67 +91,67 @@ class ChannelListView extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
/// state: if true returns the Channel state
|
/// state: if true returns the Channel state
|
||||||
/// watch: if true listen to changes to this Channel in real time.
|
/// watch: if true listen to changes to this Channel in real time.
|
||||||
final Map<String, dynamic> options;
|
final Map<String, dynamic>? options;
|
||||||
|
|
||||||
/// The sorting used for the channels matching the filters.
|
/// The sorting used for the channels matching the filters.
|
||||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption<ChannelModel>> sort;
|
final List<SortOption<ChannelModel>>? sort;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of channels to return (max is 30)
|
/// limit: the number of channels to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams pagination;
|
final PaginationParams? pagination;
|
||||||
|
|
||||||
/// Function called when tapping on a channel
|
/// Function called when tapping on a channel
|
||||||
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
||||||
/// with the widget [channelWidget] as child.
|
/// with the widget [channelWidget] as child.
|
||||||
final ChannelTapCallback onChannelTap;
|
final ChannelTapCallback? onChannelTap;
|
||||||
|
|
||||||
/// Function called when long pressing on a channel
|
/// Function called when long pressing on a channel
|
||||||
final Function(Channel) onChannelLongPress;
|
final Function(Channel)? onChannelLongPress;
|
||||||
|
|
||||||
/// Widget used when opening a channel
|
/// Widget used when opening a channel
|
||||||
final Widget channelWidget;
|
final Widget? channelWidget;
|
||||||
|
|
||||||
/// Builder used to create a custom channel preview
|
/// Builder used to create a custom channel preview
|
||||||
final ChannelPreviewBuilder channelPreviewBuilder;
|
final ChannelPreviewBuilder? channelPreviewBuilder;
|
||||||
|
|
||||||
/// Builder used to create a custom item separator
|
/// Builder used to create a custom item separator
|
||||||
final Function(BuildContext, int) separatorBuilder;
|
final Function(BuildContext, int)? separatorBuilder;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final Function(Channel) onImageTap;
|
final Function(Channel)? onImageTap;
|
||||||
|
|
||||||
/// Set it to false to disable the pull-to-refresh widget
|
/// Set it to false to disable the pull-to-refresh widget
|
||||||
final bool pullToRefresh;
|
final bool pullToRefresh;
|
||||||
|
|
||||||
/// Callback used in the default empty list widget
|
/// Callback used in the default empty list widget
|
||||||
final VoidCallback onStartChatPressed;
|
final VoidCallback? onStartChatPressed;
|
||||||
|
|
||||||
/// The number of children in the cross axis.
|
/// The number of children in the cross axis.
|
||||||
final int crossAxisCount;
|
final int crossAxisCount;
|
||||||
|
|
||||||
/// The amount of space by which to inset the children.
|
/// The amount of space by which to inset the children.
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
final List<Channel> selectedChannels;
|
final List<Channel> selectedChannels;
|
||||||
|
|
||||||
final ViewInfoCallback onViewInfoTap;
|
final ViewInfoCallback? onViewInfoTap;
|
||||||
|
|
||||||
/// The builder that will be used in case of error
|
/// The builder that will be used in case of error
|
||||||
final ErrorBuilder errorBuilder;
|
final ErrorBuilder? errorBuilder;
|
||||||
|
|
||||||
/// The builder that will be used in case of loading
|
/// The builder that will be used in case of loading
|
||||||
final WidgetBuilder loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
/// The builder which is used when list of channels loads
|
/// The builder which is used when list of channels loads
|
||||||
final Function(BuildContext, List<Channel>) listBuilder;
|
final Function(BuildContext, List<Channel>)? listBuilder;
|
||||||
|
|
||||||
/// The builder used when the channel list is empty.
|
/// The builder used when the channel list is empty.
|
||||||
final WidgetBuilder emptyBuilder;
|
final WidgetBuilder? emptyBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ChannelListViewState createState() => _ChannelListViewState();
|
_ChannelListViewState createState() => _ChannelListViewState();
|
||||||
@@ -164,7 +165,10 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = ChannelListCore(
|
Widget child = ChannelListCore(
|
||||||
pagination: widget.pagination,
|
pagination: widget.pagination ??
|
||||||
|
const PaginationParams(
|
||||||
|
limit: 25,
|
||||||
|
),
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
sort: widget.sort,
|
sort: widget.sort,
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
@@ -177,26 +181,27 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
|
|
||||||
if (widget.pullToRefresh) {
|
if (widget.pullToRefresh) {
|
||||||
child = RefreshIndicator(
|
child = RefreshIndicator(
|
||||||
onRefresh: () => _channelListController.loadData(),
|
onRefresh: () => _channelListController.loadData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return LazyLoadScrollView(
|
return LazyLoadScrollView(
|
||||||
onEndOfPage: () => _channelListController.paginateData(),
|
onEndOfPage: () => _channelListController.paginateData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(BuildContext context, List<Channel> channels) {
|
Widget _buildListView(BuildContext context, List<Channel> channels) {
|
||||||
Widget child;
|
late Widget child;
|
||||||
|
|
||||||
if (channels.isNotEmpty) {
|
if (channels.isNotEmpty) {
|
||||||
if (widget.crossAxisCount > 1) {
|
if (widget.crossAxisCount > 1) {
|
||||||
child = GridView.builder(
|
child = GridView.builder(
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: widget.crossAxisCount),
|
crossAxisCount: widget.crossAxisCount,
|
||||||
|
),
|
||||||
itemCount: channels.length,
|
itemCount: channels.length,
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@@ -211,7 +216,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
||||||
separatorBuilder: (_, index) {
|
separatorBuilder: (_, index) {
|
||||||
if (widget.separatorBuilder != null) {
|
if (widget.separatorBuilder != null) {
|
||||||
return widget.separatorBuilder(context, index);
|
return widget.separatorBuilder!(context, index);
|
||||||
}
|
}
|
||||||
return _separatorBuilder(context, index);
|
return _separatorBuilder(context, index);
|
||||||
},
|
},
|
||||||
@@ -317,7 +322,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
if (widget.crossAxisCount == 1) {
|
if (widget.crossAxisCount == 1) {
|
||||||
if (i % 2 != 0) {
|
if (i % 2 != 0) {
|
||||||
if (widget.separatorBuilder != null) {
|
if (widget.separatorBuilder != null) {
|
||||||
return widget.separatorBuilder(context, i);
|
return widget.separatorBuilder!(context, i);
|
||||||
}
|
}
|
||||||
return _separatorBuilder(context, i);
|
return _separatorBuilder(context, i);
|
||||||
}
|
}
|
||||||
@@ -447,7 +452,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
style: Theme.of(context).textTheme.headline6,
|
style: Theme.of(context).textTheme.headline6,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _channelListController.loadData(),
|
onPressed: () => _channelListController.loadData!(),
|
||||||
child: Text('Retry'),
|
child: Text('Retry'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -459,24 +464,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
final channelsProvider = ChannelsBloc.of(context);
|
final channelsProvider = ChannelsBloc.of(context);
|
||||||
if (i < channels.length) {
|
if (i < channels.length) {
|
||||||
final channel = channels[i];
|
final channel = channels[i];
|
||||||
ChannelTapCallback onTap;
|
final onTap = _getChannelTap(context);
|
||||||
if (widget.onChannelTap != null) {
|
|
||||||
onTap = widget.onChannelTap;
|
|
||||||
} else {
|
|
||||||
onTap = (client, _) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return StreamChannel(
|
|
||||||
channel: client,
|
|
||||||
child: widget.channelWidget,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke;
|
final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke;
|
||||||
return StreamChannel(
|
return StreamChannel(
|
||||||
@@ -509,7 +497,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: ChannelBottomSheet(
|
child: ChannelBottomSheet(
|
||||||
onViewInfoTap: () {
|
onViewInfoTap: () {
|
||||||
widget.onViewInfoTap(channel);
|
widget.onViewInfoTap?.call(channel);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -520,9 +508,9 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
if ([
|
if ([
|
||||||
'admin',
|
'admin',
|
||||||
'owner',
|
'owner',
|
||||||
].contains(channel.state.members
|
].contains(channel.state!.members
|
||||||
.firstWhere((m) => m.userId == channel.client.state.user.id,
|
.firstWhereOrNull(
|
||||||
orElse: () => null)
|
(m) => m.userId == channel.client.state.user?.id)
|
||||||
?.role))
|
?.role))
|
||||||
IconSlideAction(
|
IconSlideAction(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
@@ -567,10 +555,35 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _gridItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
ChannelTapCallback _getChannelTap(BuildContext context) {
|
||||||
var channel = channels[i];
|
ChannelTapCallback onTap;
|
||||||
|
if (widget.onChannelTap != null) {
|
||||||
|
onTap = widget.onChannelTap!;
|
||||||
|
} else {
|
||||||
|
onTap = (client, _) {
|
||||||
|
if (widget.channelWidget == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return StreamChannel(
|
||||||
|
channel: client,
|
||||||
|
child: widget.channelWidget!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return onTap;
|
||||||
|
}
|
||||||
|
|
||||||
var selected = widget.selectedChannels.contains(channel);
|
Widget _gridItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||||
|
final channel = channels[i];
|
||||||
|
|
||||||
|
final selected = widget.selectedChannels.contains(channel);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
||||||
@@ -586,7 +599,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
),
|
),
|
||||||
onTap: () => widget.onChannelTap(channel, null),
|
onTap: () => _getChannelTap(context),
|
||||||
),
|
),
|
||||||
SizedBox(height: 7),
|
SizedBox(height: 7),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -628,7 +641,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return snapshot.data
|
return snapshot.data!
|
||||||
? Center(
|
? Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
@@ -644,7 +657,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: effect.color.withOpacity(effect.alpha ?? 1.0),
|
color: effect.color!.withOpacity(effect.alpha ?? 1.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import '../stream_chat_flutter.dart';
|
|||||||
class ChannelName extends StatelessWidget {
|
class ChannelName extends StatelessWidget {
|
||||||
/// Instantiate a new ChannelName
|
/// Instantiate a new ChannelName
|
||||||
const ChannelName({
|
const ChannelName({
|
||||||
Key key,
|
Key? key,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The style of the text displayed
|
/// The style of the text displayed
|
||||||
final TextStyle textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -26,31 +26,31 @@ class ChannelName extends StatelessWidget {
|
|||||||
stream: channel.extraDataStream,
|
stream: channel.extraDataStream,
|
||||||
initialData: channel.extraData,
|
initialData: channel.extraData,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return _buildName(snapshot.data, channel.state.members, client);
|
return _buildName(snapshot.data!, channel.state?.members, client);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildName(
|
Widget _buildName(
|
||||||
Map<String, dynamic> extraData,
|
Map<String, dynamic> extraData,
|
||||||
List<Member> members,
|
List<Member>? members,
|
||||||
StreamChatState client,
|
StreamChatState client,
|
||||||
) {
|
) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
String title;
|
String? title;
|
||||||
if (extraData['name'] == null) {
|
if (extraData['name'] == null) {
|
||||||
final otherMembers =
|
final otherMembers =
|
||||||
members.where((member) => member.userId != client.user.id);
|
members?.where((member) => member.userId != client.user!.id);
|
||||||
if (otherMembers.length == 1) {
|
if (otherMembers?.length == 1) {
|
||||||
title = otherMembers.first.user.name;
|
title = otherMembers!.first.user?.name;
|
||||||
} else if (otherMembers.isNotEmpty) {
|
} else if (otherMembers?.isNotEmpty == true) {
|
||||||
final maxWidth = constraints.maxWidth;
|
final maxWidth = constraints.maxWidth;
|
||||||
final maxChars = maxWidth / textStyle.fontSize;
|
final maxChars = maxWidth / (textStyle?.fontSize ?? 1);
|
||||||
var currentChars = 0;
|
var currentChars = 0;
|
||||||
final currentMembers = <Member>[];
|
final currentMembers = <Member>[];
|
||||||
otherMembers.forEach((element) {
|
otherMembers!.forEach((element) {
|
||||||
final newLength = currentChars + element.user.name.length;
|
final newLength = currentChars + (element.user?.name.length ?? 0);
|
||||||
if (newLength < maxChars) {
|
if (newLength < maxChars) {
|
||||||
currentChars = newLength;
|
currentChars = newLength;
|
||||||
currentMembers.add(element);
|
currentMembers.add(element);
|
||||||
@@ -60,7 +60,7 @@ class ChannelName extends StatelessWidget {
|
|||||||
final exceedingMembers =
|
final exceedingMembers =
|
||||||
otherMembers.length - currentMembers.length;
|
otherMembers.length - currentMembers.length;
|
||||||
title =
|
title =
|
||||||
'${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
|
'${currentMembers.map((e) => e.user?.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
|
||||||
} else {
|
} else {
|
||||||
title = 'No title';
|
title = 'No title';
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ class ChannelName extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
title,
|
title!,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
@@ -20,35 +21,35 @@ import 'channel_name.dart';
|
|||||||
/// Modify it to change the widget appearance.
|
/// Modify it to change the widget appearance.
|
||||||
class ChannelPreview extends StatelessWidget {
|
class ChannelPreview extends StatelessWidget {
|
||||||
/// Function called when tapping this widget
|
/// Function called when tapping this widget
|
||||||
final void Function(Channel) onTap;
|
final void Function(Channel)? onTap;
|
||||||
|
|
||||||
/// Function called when long pressing this widget
|
/// Function called when long pressing this widget
|
||||||
final void Function(Channel) onLongPress;
|
final void Function(Channel)? onLongPress;
|
||||||
|
|
||||||
/// Channel displayed
|
/// Channel displayed
|
||||||
final Channel channel;
|
final Channel channel;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final VoidCallback onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
/// Widget rendering the title
|
/// Widget rendering the title
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
|
|
||||||
/// Widget rendering the subtitle
|
/// Widget rendering the subtitle
|
||||||
final Widget subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Widget rendering the leading element, by default it shows the [ChannelImage]
|
/// Widget rendering the leading element, by default it shows the [ChannelImage]
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// Widget rendering the trailing element, by default it shows the last message date
|
/// Widget rendering the trailing element, by default it shows the last message date
|
||||||
final Widget trailing;
|
final Widget? trailing;
|
||||||
|
|
||||||
/// Widget rendering the sending indicator, by default it uses the [SendingIndicator] widget
|
/// Widget rendering the sending indicator, by default it uses the [SendingIndicator] widget
|
||||||
final Widget sendingIndicator;
|
final Widget? sendingIndicator;
|
||||||
|
|
||||||
ChannelPreview({
|
ChannelPreview({
|
||||||
@required this.channel,
|
required this.channel,
|
||||||
Key key,
|
Key? key,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
this.onImageTap,
|
this.onImageTap,
|
||||||
@@ -67,7 +68,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
initialData: channel.isMuted,
|
initialData: channel.isMuted,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: snapshot.data ? 0.5 : 1,
|
opacity: snapshot.data! ? 0.5 : 1,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
@@ -75,12 +76,12 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
onTap(channel);
|
onTap!(channel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (onLongPress != null) {
|
if (onLongPress != null) {
|
||||||
onLongPress(channel);
|
onLongPress!(channel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
@@ -97,13 +98,13 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
StreamBuilder<List<Member>>(
|
StreamBuilder<List<Member>>(
|
||||||
stream: channel.state.membersStream,
|
stream: channel.state?.membersStream,
|
||||||
initialData: channel.state.members,
|
initialData: channel.state?.members,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData ||
|
if (!snapshot.hasData ||
|
||||||
snapshot.data.isEmpty ||
|
snapshot.data!.isEmpty ||
|
||||||
!snapshot.data.any((Member e) =>
|
!snapshot.data!.any((Member e) =>
|
||||||
e.user.id == channel.client.state.user.id)) {
|
e.user!.id == channel.client.state.user?.id)) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
return UnreadIndicator(
|
return UnreadIndicator(
|
||||||
@@ -120,24 +121,24 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
sendingIndicator ??
|
sendingIndicator ??
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final lastMessage = channel.state.messages.lastWhere(
|
final lastMessage =
|
||||||
|
channel.state?.messages.lastWhereOrNull(
|
||||||
(m) => !m.isDeleted && m.shadowed != true,
|
(m) => !m.isDeleted && m.shadowed != true,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
if (lastMessage?.user?.id ==
|
if (lastMessage?.user?.id ==
|
||||||
StreamChat.of(context).user.id) {
|
StreamChat.of(context).user?.id) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
child: SendingIndicator(
|
child: SendingIndicator(
|
||||||
message: lastMessage,
|
message: lastMessage!,
|
||||||
size: channelPreviewTheme.indicatorIconSize,
|
size: channelPreviewTheme.indicatorIconSize,
|
||||||
isMessageRead: channel.state.read
|
isMessageRead: channel.state!.read
|
||||||
?.where((element) =>
|
?.where((element) =>
|
||||||
element.user.id !=
|
element.user.id !=
|
||||||
channel.client.state.user.id)
|
channel.client.state.user!.id)
|
||||||
?.where((element) => element.lastRead
|
.where((element) => element.lastRead
|
||||||
.isAfter(lastMessage.createdAt))
|
.isAfter(lastMessage.createdAt))
|
||||||
?.isNotEmpty ==
|
.isNotEmpty ==
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -154,14 +155,14 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDate(BuildContext context) {
|
Widget _buildDate(BuildContext context) {
|
||||||
return StreamBuilder<DateTime>(
|
return StreamBuilder<DateTime?>(
|
||||||
stream: channel.lastMessageAtStream,
|
stream: channel.lastMessageAtStream,
|
||||||
initialData: channel.lastMessageAt,
|
initialData: channel.lastMessageAt,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
final lastMessageAt = snapshot.data.toLocal();
|
final lastMessageAt = snapshot.data!.toLocal();
|
||||||
|
|
||||||
String stringDate;
|
String stringDate;
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
@@ -211,60 +212,58 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLastMessage(BuildContext context) {
|
Widget _buildLastMessage(BuildContext context) {
|
||||||
return StreamBuilder<List<Message>>(
|
return StreamBuilder<List<Message>?>(
|
||||||
stream: channel.state.messagesStream,
|
stream: channel.state!.messagesStream,
|
||||||
initialData: channel.state.messages,
|
initialData: channel.state!.messages,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final lastMessage = snapshot.data?.lastWhere(
|
final lastMessage = snapshot.data
|
||||||
(m) => m.shadowed != true && !m.isDeleted,
|
?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted);
|
||||||
orElse: () => null);
|
|
||||||
if (lastMessage == null) {
|
if (lastMessage == null) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = lastMessage.text;
|
var text = lastMessage.text;
|
||||||
if (lastMessage.attachments != null) {
|
final parts = <String>[
|
||||||
final parts = <String>[
|
...lastMessage.attachments.map((e) {
|
||||||
...lastMessage.attachments.map((e) {
|
if (e.type == 'image') {
|
||||||
if (e.type == 'image') {
|
return '📷';
|
||||||
return '📷';
|
} else if (e.type == 'video') {
|
||||||
} else if (e.type == 'video') {
|
return '🎬';
|
||||||
return '🎬';
|
} else if (e.type == 'giphy') {
|
||||||
} else if (e.type == 'giphy') {
|
return '[GIF]';
|
||||||
return '[GIF]';
|
}
|
||||||
}
|
return e == lastMessage.attachments.last
|
||||||
return e == lastMessage.attachments.last
|
? (e.title ?? 'File')
|
||||||
? (e.title ?? 'File')
|
: '${e.title ?? 'File'} , ';
|
||||||
: '${e.title ?? 'File'} , ';
|
}),
|
||||||
}).where((e) => e != null),
|
lastMessage.text ?? '',
|
||||||
lastMessage.text ?? '',
|
];
|
||||||
];
|
|
||||||
|
|
||||||
text = parts.join(' ');
|
text = parts.join(' ');
|
||||||
}
|
|
||||||
|
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
_getDisplayText(
|
_getDisplayText(
|
||||||
text,
|
text,
|
||||||
lastMessage.mentionedUsers,
|
lastMessage.mentionedUsers,
|
||||||
lastMessage.attachments,
|
lastMessage.attachments,
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle
|
||||||
.color,
|
?.color,
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal),
|
: FontStyle.normal),
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle
|
||||||
.color,
|
?.color,
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal,
|
: FontStyle.normal,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
@@ -274,29 +273,28 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextSpan _getDisplayText(
|
TextSpan _getDisplayText(
|
||||||
String text,
|
String text,
|
||||||
List<User> mentions,
|
List<User> mentions,
|
||||||
List<Attachment> attachments,
|
List<Attachment> attachments,
|
||||||
TextStyle normalTextStyle,
|
TextStyle? normalTextStyle,
|
||||||
TextStyle mentionsTextStyle) {
|
TextStyle? mentionsTextStyle,
|
||||||
var textList = text.split(' ');
|
) {
|
||||||
var resList = <TextSpan>[];
|
final textList = text.split(' ');
|
||||||
for (var e in textList) {
|
final resList = <TextSpan>[];
|
||||||
if (mentions != null &&
|
for (final e in textList) {
|
||||||
mentions.isNotEmpty &&
|
if (mentions.isNotEmpty &&
|
||||||
mentions.any((element) => '@${element.name}' == e)) {
|
mentions.any((element) => '@${element.name}' == e)) {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
text: '$e ',
|
text: '$e ',
|
||||||
style: mentionsTextStyle,
|
style: mentionsTextStyle,
|
||||||
));
|
));
|
||||||
} else if (attachments != null &&
|
} else if (attachments.isNotEmpty &&
|
||||||
attachments.isNotEmpty &&
|
|
||||||
attachments
|
attachments
|
||||||
.where((e) => e.title != null)
|
.where((e) => e.title != null)
|
||||||
.any((element) => element.title == e)) {
|
.any((element) => element.title == e)) {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
text: '$e ',
|
text: '$e ',
|
||||||
style: normalTextStyle.copyWith(fontStyle: FontStyle.italic),
|
style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
|
|||||||
@@ -11,26 +11,25 @@ import 'stream_chat.dart';
|
|||||||
class ConnectionStatusBuilder extends StatelessWidget {
|
class ConnectionStatusBuilder extends StatelessWidget {
|
||||||
/// Creates a new ConnectionStatusBuilder
|
/// Creates a new ConnectionStatusBuilder
|
||||||
const ConnectionStatusBuilder({
|
const ConnectionStatusBuilder({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.statusBuilder,
|
required this.statusBuilder,
|
||||||
this.initialStatus = ConnectionStatus.disconnected,
|
this.initialStatus = ConnectionStatus.disconnected,
|
||||||
this.connectionStatusStream,
|
this.connectionStatusStream,
|
||||||
this.errorBuilder,
|
this.errorBuilder,
|
||||||
this.loadingBuilder,
|
this.loadingBuilder,
|
||||||
}) : assert(statusBuilder != null),
|
}) : super(key: key);
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
/// The connection status that will be used to create the initial snapshot.
|
/// The connection status that will be used to create the initial snapshot.
|
||||||
final ConnectionStatus initialStatus;
|
final ConnectionStatus initialStatus;
|
||||||
|
|
||||||
/// The asynchronous computation to which this builder is currently connected.
|
/// The asynchronous computation to which this builder is currently connected.
|
||||||
final Stream<ConnectionStatus> connectionStatusStream;
|
final Stream<ConnectionStatus>? connectionStatusStream;
|
||||||
|
|
||||||
/// The builder that will be used in case of error
|
/// The builder that will be used in case of error
|
||||||
final Widget Function(BuildContext context, Object error) errorBuilder;
|
final Widget Function(BuildContext context, Object? error)? errorBuilder;
|
||||||
|
|
||||||
/// The builder that will be used in case of loading
|
/// The builder that will be used in case of loading
|
||||||
final WidgetBuilder loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
/// The builder that will be used in case of data
|
/// The builder that will be used in case of data
|
||||||
final Widget Function(BuildContext context, ConnectionStatus status)
|
final Widget Function(BuildContext context, ConnectionStatus status)
|
||||||
@@ -46,15 +45,15 @@ class ConnectionStatusBuilder extends StatelessWidget {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
if (errorBuilder != null) {
|
if (errorBuilder != null) {
|
||||||
return errorBuilder(context, snapshot.error);
|
return errorBuilder!(context, snapshot.error);
|
||||||
}
|
}
|
||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
if (loadingBuilder != null) return loadingBuilder(context);
|
if (loadingBuilder != null) return loadingBuilder!(context);
|
||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
return statusBuilder(context, snapshot.data);
|
return statusBuilder(context, snapshot.data!);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ class DateDivider extends StatelessWidget {
|
|||||||
final bool uppercase;
|
final bool uppercase;
|
||||||
|
|
||||||
const DateDivider({
|
const DateDivider({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.dateTime,
|
required this.dateTime,
|
||||||
this.uppercase = false,
|
this.uppercase = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
|||||||
|
|
||||||
class DeletedMessage extends StatelessWidget {
|
class DeletedMessage extends StatelessWidget {
|
||||||
const DeletedMessage({
|
const DeletedMessage({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.borderRadiusGeometry,
|
this.borderRadiusGeometry,
|
||||||
this.shape,
|
this.shape,
|
||||||
this.borderSide,
|
this.borderSide,
|
||||||
@@ -14,16 +14,16 @@ class DeletedMessage extends StatelessWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The theme of the message
|
/// The theme of the message
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
|
|
||||||
/// The border radius of the message text
|
/// The border radius of the message text
|
||||||
final BorderRadiusGeometry borderRadiusGeometry;
|
final BorderRadiusGeometry? borderRadiusGeometry;
|
||||||
|
|
||||||
/// The shape of the message text
|
/// The shape of the message text
|
||||||
final ShapeBorder shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
/// The borderside of the message text
|
/// The borderside of the message text
|
||||||
final BorderSide borderSide;
|
final BorderSide? borderSide;
|
||||||
|
|
||||||
/// If true the widget will be mirrored
|
/// If true the widget will be mirrored
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
@@ -34,7 +34,7 @@ class DeletedMessage extends StatelessWidget {
|
|||||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Material(
|
child: Material(
|
||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme?.messageBackgroundColor,
|
||||||
shape: shape ??
|
shape: shape ??
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
||||||
@@ -61,9 +61,9 @@ class DeletedMessage extends StatelessWidget {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Message deleted',
|
'Message deleted',
|
||||||
style: messageTheme.messageText.copyWith(
|
style: messageTheme?.messageText?.copyWith(
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
color: messageTheme.createdAt.color,
|
color: messageTheme?.createdAt?.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -47,53 +47,53 @@ extension PlatformFileX on PlatformFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension InputDecorationX on InputDecoration {
|
extension InputDecorationX on InputDecoration {
|
||||||
InputDecoration merge(InputDecoration other) {
|
InputDecoration merge(InputDecoration? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
icon: other?.icon,
|
icon: other.icon,
|
||||||
labelText: other?.labelText,
|
labelText: other.labelText,
|
||||||
labelStyle: labelStyle?.merge(other.labelStyle) ?? other.labelStyle,
|
labelStyle: labelStyle?.merge(other.labelStyle) ?? other.labelStyle,
|
||||||
helperText: other?.helperText,
|
helperText: other.helperText,
|
||||||
helperStyle: helperStyle?.merge(other.helperStyle) ?? other.helperStyle,
|
helperStyle: helperStyle?.merge(other.helperStyle) ?? other.helperStyle,
|
||||||
helperMaxLines: other?.helperMaxLines,
|
helperMaxLines: other.helperMaxLines,
|
||||||
hintText: other?.hintText,
|
hintText: other.hintText,
|
||||||
hintStyle: hintStyle?.merge(other.hintStyle) ?? other.hintStyle,
|
hintStyle: hintStyle?.merge(other.hintStyle) ?? other.hintStyle,
|
||||||
hintTextDirection: other?.hintTextDirection,
|
hintTextDirection: other.hintTextDirection,
|
||||||
hintMaxLines: other?.hintMaxLines,
|
hintMaxLines: other.hintMaxLines,
|
||||||
errorText: other?.errorText,
|
errorText: other.errorText,
|
||||||
errorStyle: errorStyle?.merge(other.errorStyle) ?? other.errorStyle,
|
errorStyle: errorStyle?.merge(other.errorStyle) ?? other.errorStyle,
|
||||||
errorMaxLines: other?.errorMaxLines,
|
errorMaxLines: other.errorMaxLines,
|
||||||
floatingLabelBehavior: other?.floatingLabelBehavior,
|
floatingLabelBehavior: other.floatingLabelBehavior,
|
||||||
isCollapsed: other?.isCollapsed,
|
isCollapsed: other.isCollapsed,
|
||||||
isDense: other?.isDense,
|
isDense: other.isDense,
|
||||||
contentPadding: other?.contentPadding,
|
contentPadding: other.contentPadding,
|
||||||
prefixIcon: other?.prefixIcon,
|
prefixIcon: other.prefixIcon,
|
||||||
prefix: other?.prefix,
|
prefix: other.prefix,
|
||||||
prefixText: other?.prefixText,
|
prefixText: other.prefixText,
|
||||||
prefixIconConstraints: other?.prefixIconConstraints,
|
prefixIconConstraints: other.prefixIconConstraints,
|
||||||
prefixStyle: prefixStyle?.merge(other.prefixStyle) ?? other.prefixStyle,
|
prefixStyle: prefixStyle?.merge(other.prefixStyle) ?? other.prefixStyle,
|
||||||
suffixIcon: other?.suffixIcon,
|
suffixIcon: other.suffixIcon,
|
||||||
suffix: other?.suffix,
|
suffix: other.suffix,
|
||||||
suffixText: other?.suffixText,
|
suffixText: other.suffixText,
|
||||||
suffixStyle: suffixStyle?.merge(other.suffixStyle) ?? other.suffixStyle,
|
suffixStyle: suffixStyle?.merge(other.suffixStyle) ?? other.suffixStyle,
|
||||||
suffixIconConstraints: other?.suffixIconConstraints,
|
suffixIconConstraints: other.suffixIconConstraints,
|
||||||
counter: other?.counter,
|
counter: other.counter,
|
||||||
counterText: other?.counterText,
|
counterText: other.counterText,
|
||||||
counterStyle:
|
counterStyle:
|
||||||
counterStyle?.merge(other.counterStyle) ?? other.counterStyle,
|
counterStyle?.merge(other.counterStyle) ?? other.counterStyle,
|
||||||
filled: other?.filled,
|
filled: other.filled,
|
||||||
fillColor: other?.fillColor,
|
fillColor: other.fillColor,
|
||||||
focusColor: other?.focusColor,
|
focusColor: other.focusColor,
|
||||||
hoverColor: other?.hoverColor,
|
hoverColor: other.hoverColor,
|
||||||
errorBorder: other?.errorBorder,
|
errorBorder: other.errorBorder,
|
||||||
focusedBorder: other?.focusedBorder,
|
focusedBorder: other.focusedBorder,
|
||||||
focusedErrorBorder: other?.focusedErrorBorder,
|
focusedErrorBorder: other.focusedErrorBorder,
|
||||||
disabledBorder: other?.disabledBorder,
|
disabledBorder: other.disabledBorder,
|
||||||
enabledBorder: other?.enabledBorder,
|
enabledBorder: other.enabledBorder,
|
||||||
border: other?.border,
|
border: other.border,
|
||||||
enabled: other?.enabled,
|
enabled: other.enabled,
|
||||||
semanticCounterText: other?.semanticCounterText,
|
semanticCounterText: other.semanticCounterText,
|
||||||
alignLabelWithHint: other?.alignLabelWithHint,
|
alignLabelWithHint: other.alignLabelWithHint,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,19 +24,18 @@ class FullScreenMedia extends StatefulWidget {
|
|||||||
|
|
||||||
final int startIndex;
|
final int startIndex;
|
||||||
final String userName;
|
final String userName;
|
||||||
final DateTime sentAt;
|
final ShowMessageCallback? onShowMessage;
|
||||||
final ShowMessageCallback onShowMessage;
|
|
||||||
|
|
||||||
/// Instantiate a new FullScreenImage
|
/// Instantiate a new FullScreenImage
|
||||||
const FullScreenMedia({
|
const FullScreenMedia({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.mediaAttachments,
|
required this.mediaAttachments,
|
||||||
this.message,
|
required this.message,
|
||||||
this.startIndex = 0,
|
this.startIndex = 0,
|
||||||
this.userName = '',
|
String? userName,
|
||||||
this.sentAt,
|
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
}) : super(key: key);
|
}) : userName = userName ?? '',
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_FullScreenMediaState createState() => _FullScreenMediaState();
|
_FullScreenMediaState createState() => _FullScreenMediaState();
|
||||||
@@ -46,10 +45,10 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
bool _optionsShown = true;
|
bool _optionsShown = true;
|
||||||
|
|
||||||
AnimationController _controller;
|
late final AnimationController _controller;
|
||||||
PageController _pageController;
|
late final PageController _pageController;
|
||||||
|
|
||||||
int _currentPage;
|
late int _currentPage;
|
||||||
|
|
||||||
final videoPackages = <String, VideoPackage>{};
|
final videoPackages = <String, VideoPackage>{};
|
||||||
|
|
||||||
@@ -101,10 +100,11 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
attachment.assetUrl ??
|
attachment.assetUrl ??
|
||||||
attachment.thumbUrl;
|
attachment.thumbUrl;
|
||||||
return PhotoView(
|
return PhotoView(
|
||||||
imageProvider:
|
imageProvider: (imageUrl == null &&
|
||||||
imageUrl == null && attachment.localUri != null
|
attachment.localUri != null &&
|
||||||
? Image.memory(attachment.file.bytes).image
|
attachment.file?.bytes != null)
|
||||||
: CachedNetworkImageProvider(imageUrl),
|
? Image.memory(attachment.file!.bytes!).image
|
||||||
|
: CachedNetworkImageProvider(imageUrl!),
|
||||||
maxScale: PhotoViewComputedScale.covered,
|
maxScale: PhotoViewComputedScale.covered,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
heroAttributes: PhotoViewHeroAttributes(
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
@@ -112,12 +112,12 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
),
|
),
|
||||||
backgroundDecoration: BoxDecoration(
|
backgroundDecoration: BoxDecoration(
|
||||||
color: ColorTween(
|
color: ColorTween(
|
||||||
begin: StreamChatTheme.of(context)
|
begin: StreamChatTheme.of(context)
|
||||||
.channelTheme
|
.channelTheme
|
||||||
.channelHeaderTheme
|
.channelHeaderTheme
|
||||||
.color,
|
.color,
|
||||||
end: Colors.black)
|
end: Colors.black,
|
||||||
.lerp(_controller.value),
|
).lerp(_controller.value),
|
||||||
),
|
),
|
||||||
onTapUp: (a, b, c) {
|
onTapUp: (a, b, c) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -131,7 +131,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (attachment.type == 'video') {
|
} else if (attachment.type == 'video') {
|
||||||
final controller = videoPackages[attachment.id];
|
final controller = videoPackages[attachment.id]!;
|
||||||
if (!controller.initialized) {
|
if (!controller.initialized) {
|
||||||
return Center(
|
return Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
@@ -153,7 +153,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
vertical: 50.0,
|
vertical: 50.0,
|
||||||
),
|
),
|
||||||
child: Chewie(
|
child: Chewie(
|
||||||
controller: controller.chewieController,
|
controller: controller.chewieController!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -171,9 +171,8 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
children: [
|
children: [
|
||||||
ImageHeader(
|
ImageHeader(
|
||||||
userName: widget.userName,
|
userName: widget.userName,
|
||||||
sentAt: widget.message.createdAt == null
|
sentAt:
|
||||||
? ''
|
'Sent ${getDay(widget.message.createdAt.toLocal())} at ${Jiffy(widget.message.createdAt.toLocal()).format('HH:mm')}',
|
||||||
: 'Sent ${getDay(widget.message.createdAt)} at ${Jiffy(widget.sentAt.toLocal()).format('HH:mm')}',
|
|
||||||
onBackPressed: () {
|
onBackPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
@@ -181,8 +180,10 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
urls: widget.mediaAttachments,
|
urls: widget.mediaAttachments,
|
||||||
currentIndex: _currentPage,
|
currentIndex: _currentPage,
|
||||||
onShowMessage: () {
|
onShowMessage: () {
|
||||||
widget.onShowMessage(
|
widget.onShowMessage?.call(
|
||||||
widget.message, StreamChannel.of(context).channel);
|
widget.message,
|
||||||
|
StreamChannel.of(context).channel,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (widget.message.type != 'ephemeral')
|
if (widget.message.type != 'ephemeral')
|
||||||
@@ -194,9 +195,11 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
mediaSelectedCallBack: (val) {
|
mediaSelectedCallBack: (val) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentPage = val;
|
_currentPage = val;
|
||||||
_pageController.animateToPage(val,
|
_pageController.animateToPage(
|
||||||
duration: Duration(milliseconds: 300),
|
val,
|
||||||
curve: Curves.easeInOut);
|
duration: Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -210,7 +213,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getDay(DateTime dateTime) {
|
String getDay(DateTime dateTime) {
|
||||||
var now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
if (DateTime(dateTime.year, dateTime.month, dateTime.day) ==
|
if (DateTime(dateTime.year, dateTime.month, dateTime.day) ==
|
||||||
DateTime(now.year, now.month, now.day)) {
|
DateTime(now.year, now.month, now.day)) {
|
||||||
@@ -238,11 +241,11 @@ class VideoPackage {
|
|||||||
final bool _showControls;
|
final bool _showControls;
|
||||||
final bool _autoInitialize;
|
final bool _autoInitialize;
|
||||||
final VideoPlayerController _videoPlayerController;
|
final VideoPlayerController _videoPlayerController;
|
||||||
ChewieController _chewieController;
|
ChewieController? _chewieController;
|
||||||
|
|
||||||
VideoPlayerController get videoPlayer => _videoPlayerController;
|
VideoPlayerController get videoPlayer => _videoPlayerController;
|
||||||
|
|
||||||
ChewieController get chewieController => _chewieController;
|
ChewieController? get chewieController => _chewieController;
|
||||||
|
|
||||||
bool get initialized => _videoPlayerController.value.isInitialized;
|
bool get initialized => _videoPlayerController.value.isInitialized;
|
||||||
|
|
||||||
@@ -250,12 +253,11 @@ class VideoPackage {
|
|||||||
Attachment attachment, {
|
Attachment attachment, {
|
||||||
bool showControls = false,
|
bool showControls = false,
|
||||||
bool autoInitialize = true,
|
bool autoInitialize = true,
|
||||||
}) : assert(attachment != null),
|
}) : _showControls = showControls,
|
||||||
_showControls = showControls,
|
|
||||||
_autoInitialize = autoInitialize,
|
_autoInitialize = autoInitialize,
|
||||||
_videoPlayerController = attachment.localUri != null
|
_videoPlayerController = attachment.localUri != null
|
||||||
? VideoPlayerController.file(File.fromUri(attachment.localUri))
|
? VideoPlayerController.file(File.fromUri(attachment.localUri!))
|
||||||
: VideoPlayerController.network(attachment.assetUrl);
|
: VideoPlayerController.network(attachment.assetUrl!);
|
||||||
|
|
||||||
Future<void> initialize() {
|
Future<void> initialize() {
|
||||||
return _videoPlayerController.initialize().then((_) {
|
return _videoPlayerController.initialize().then((_) {
|
||||||
@@ -278,6 +280,6 @@ class VideoPackage {
|
|||||||
|
|
||||||
Future<void> dispose() {
|
Future<void> dispose() {
|
||||||
_chewieController?.dispose();
|
_chewieController?.dispose();
|
||||||
return _videoPlayerController?.dispose();
|
return _videoPlayerController.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import '../stream_chat_flutter.dart';
|
|||||||
|
|
||||||
class GroupImage extends StatelessWidget {
|
class GroupImage extends StatelessWidget {
|
||||||
const GroupImage({
|
const GroupImage({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.images,
|
required this.images,
|
||||||
this.constraints,
|
this.constraints,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
@@ -15,12 +15,12 @@ class GroupImage extends StatelessWidget {
|
|||||||
this.selectionThickness = 4,
|
this.selectionThickness = 4,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final List<String> images;
|
final List<String?> images;
|
||||||
final BoxConstraints constraints;
|
final BoxConstraints? constraints;
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
final Color selectionColor;
|
final Color? selectionColor;
|
||||||
final double selectionThickness;
|
final double selectionThickness;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -35,13 +35,13 @@ class GroupImage extends StatelessWidget {
|
|||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.ownMessageTheme
|
.ownMessageTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.borderRadius,
|
?.borderRadius,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.ownMessageTheme
|
.ownMessageTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
.constraints,
|
?.constraints,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
@@ -64,7 +64,7 @@ class GroupImage extends StatelessWidget {
|
|||||||
child: Transform.scale(
|
child: Transform.scale(
|
||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: url,
|
imageUrl: url!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -89,7 +89,7 @@ class GroupImage extends StatelessWidget {
|
|||||||
child: Transform.scale(
|
child: Transform.scale(
|
||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: url,
|
imageUrl: url!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -107,7 +107,8 @@ class GroupImage extends StatelessWidget {
|
|||||||
if (selected) {
|
if (selected) {
|
||||||
avatar = ClipRRect(
|
avatar = ClipRRect(
|
||||||
borderRadius: (borderRadius ??
|
borderRadius: (borderRadius ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ??
|
||||||
|
BorderRadius.zero) +
|
||||||
BorderRadius.circular(selectionThickness),
|
BorderRadius.circular(selectionThickness),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: selectionColor ??
|
color: selectionColor ??
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
@@ -15,13 +15,13 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||||
/// Callback to call when pressing the back button.
|
/// Callback to call when pressing the back button.
|
||||||
/// By default it calls [Navigator.pop]
|
/// By default it calls [Navigator.pop]
|
||||||
final VoidCallback onBackPressed;
|
final VoidCallback? onBackPressed;
|
||||||
|
|
||||||
/// Callback to call when the header is tapped.
|
/// Callback to call when the header is tapped.
|
||||||
final VoidCallback onTitleTap;
|
final VoidCallback? onTitleTap;
|
||||||
|
|
||||||
/// Callback to call when the image is tapped.
|
/// Callback to call when the image is tapped.
|
||||||
final VoidCallback onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
final int currentPage;
|
final int currentPage;
|
||||||
final int totalPages;
|
final int totalPages;
|
||||||
@@ -29,18 +29,18 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
|||||||
final List<Attachment> mediaAttachments;
|
final List<Attachment> mediaAttachments;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
final ValueChanged<int> mediaSelectedCallBack;
|
final ValueChanged<int>? mediaSelectedCallBack;
|
||||||
|
|
||||||
/// Creates a channel header
|
/// Creates a channel header
|
||||||
ImageFooter({
|
ImageFooter({
|
||||||
Key key,
|
Key? key,
|
||||||
|
required this.message,
|
||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
this.onTitleTap,
|
this.onTitleTap,
|
||||||
this.onImageTap,
|
this.onImageTap,
|
||||||
this.currentPage = 0,
|
this.currentPage = 0,
|
||||||
this.totalPages = 0,
|
this.totalPages = 0,
|
||||||
this.mediaAttachments,
|
this.mediaAttachments = const [],
|
||||||
this.message,
|
|
||||||
this.mediaSelectedCallBack,
|
this.mediaSelectedCallBack,
|
||||||
}) : preferredSize = Size.fromHeight(kToolbarHeight),
|
}) : preferredSize = Size.fromHeight(kToolbarHeight),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
@@ -53,14 +53,11 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ImageFooterState extends State<ImageFooter> {
|
class _ImageFooterState extends State<ImageFooter> {
|
||||||
TextEditingController _searchController;
|
|
||||||
final TextEditingController _messageController = TextEditingController();
|
final TextEditingController _messageController = TextEditingController();
|
||||||
final FocusNode _messageFocusNode = FocusNode();
|
final FocusNode _messageFocusNode = FocusNode();
|
||||||
|
|
||||||
final List<Channel> _selectedChannels = [];
|
final List<Channel> _selectedChannels = [];
|
||||||
|
|
||||||
Function modalSetStateCallback;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -69,13 +66,6 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_searchController?.clear();
|
|
||||||
_searchController?.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final showShareButton = !kIsWeb;
|
final showShareButton = !kIsWeb;
|
||||||
@@ -106,10 +96,10 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
widget.mediaAttachments[widget.currentPage];
|
widget.mediaAttachments[widget.currentPage];
|
||||||
final url = attachment.imageUrl ??
|
final url = attachment.imageUrl ??
|
||||||
attachment.assetUrl ??
|
attachment.assetUrl ??
|
||||||
attachment.thumbUrl;
|
attachment.thumbUrl!;
|
||||||
final type = attachment.type == 'image'
|
final type = attachment.type == 'image'
|
||||||
? 'jpg'
|
? 'jpg'
|
||||||
: url?.split('?')?.first?.split('.')?.last ?? 'jpg';
|
: url.split('?').first.split('.').last;
|
||||||
final request =
|
final request =
|
||||||
await HttpClient().getUrl(Uri.parse(url));
|
await HttpClient().getUrl(Uri.parse(url));
|
||||||
final response = await request.close();
|
final response = await request.close();
|
||||||
@@ -227,7 +217,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
final attachment = widget.mediaAttachments[index];
|
final attachment = widget.mediaAttachments[index];
|
||||||
if (attachment.type == 'video') {
|
if (attachment.type == 'video') {
|
||||||
media = InkWell(
|
media = InkWell(
|
||||||
onTap: () => widget.mediaSelectedCallBack(index),
|
onTap: () => widget.mediaSelectedCallBack!(index),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
child: VideoThumbnailImage(
|
child: VideoThumbnailImage(
|
||||||
@@ -238,13 +228,13 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
media = InkWell(
|
media = InkWell(
|
||||||
onTap: () => widget.mediaSelectedCallBack(index),
|
onTap: () => widget.mediaSelectedCallBack!(index),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1.0,
|
aspectRatio: 1.0,
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: attachment.imageUrl ??
|
imageUrl: attachment.imageUrl ??
|
||||||
attachment.assetUrl ??
|
attachment.assetUrl ??
|
||||||
attachment.thumbUrl,
|
attachment.thumbUrl!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -254,32 +244,33 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
media,
|
media,
|
||||||
Padding(
|
if (widget.message.user != null)
|
||||||
padding: EdgeInsets.all(8.0),
|
Padding(
|
||||||
child: Container(
|
padding: EdgeInsets.all(8.0),
|
||||||
clipBehavior: Clip.antiAlias,
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
clipBehavior: Clip.antiAlias,
|
||||||
shape: BoxShape.circle,
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.6),
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
color: Colors.white.withOpacity(0.6),
|
||||||
BoxShadow(
|
boxShadow: [
|
||||||
blurRadius: 8.0,
|
BoxShadow(
|
||||||
color: StreamChatTheme.of(context)
|
blurRadius: 8.0,
|
||||||
.colorTheme
|
color: StreamChatTheme.of(context)
|
||||||
.black
|
.colorTheme
|
||||||
.withOpacity(0.3),
|
.black
|
||||||
),
|
.withOpacity(0.3),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
padding: const EdgeInsets.all(2),
|
),
|
||||||
child: UserAvatar(
|
padding: const EdgeInsets.all(2),
|
||||||
user: widget.message.user,
|
child: UserAvatar(
|
||||||
constraints:
|
user: widget.message.user!,
|
||||||
BoxConstraints.tight(Size(24, 24)),
|
constraints:
|
||||||
showOnlineStatus: false,
|
BoxConstraints.tight(Size(24, 24)),
|
||||||
|
showOnlineStatus: false,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -302,7 +293,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
|
|
||||||
_messageController.clear();
|
_messageController.clear();
|
||||||
|
|
||||||
for (var channel in _selectedChannels) {
|
for (final channel in _selectedChannels) {
|
||||||
final message = Message(
|
final message = Message(
|
||||||
text: text,
|
text: text,
|
||||||
attachments: [attachments[widget.currentPage]],
|
attachments: [attachments[widget.currentPage]],
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
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/src/full_screen_media.dart';
|
import 'package:stream_chat_flutter/src/full_screen_media.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
class ImageGroup extends StatelessWidget {
|
class ImageGroup extends StatelessWidget {
|
||||||
const ImageGroup({
|
const ImageGroup({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.images,
|
required this.images,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
@required this.size,
|
required this.size,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final List<Attachment> images;
|
final List<Attachment> images;
|
||||||
final Message message;
|
final Message message;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
final Size size;
|
final Size size;
|
||||||
final ShowMessageCallback onShowMessage;
|
final ShowMessageCallback? onShowMessage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -106,9 +106,9 @@ class ImageGroup extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onTap(
|
void _onTap(
|
||||||
BuildContext context, [
|
BuildContext context,
|
||||||
int index,
|
int index,
|
||||||
]) {
|
) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@@ -119,8 +119,7 @@ class ImageGroup extends StatelessWidget {
|
|||||||
child: FullScreenMedia(
|
child: FullScreenMedia(
|
||||||
mediaAttachments: images,
|
mediaAttachments: images,
|
||||||
startIndex: index,
|
startIndex: index,
|
||||||
userName: message.user.name,
|
userName: message.user?.name,
|
||||||
sentAt: message.createdAt,
|
|
||||||
message: message,
|
message: message,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
|
|
||||||
/// Callback to call when pressing the back button.
|
/// Callback to call when pressing the back button.
|
||||||
/// By default it calls [Navigator.pop]
|
/// By default it calls [Navigator.pop]
|
||||||
final VoidCallback onBackPressed;
|
final VoidCallback? onBackPressed;
|
||||||
|
|
||||||
/// Callback to call when pressing the show message button.
|
/// Callback to call when pressing the show message button.
|
||||||
final VoidCallback onShowMessage;
|
final VoidCallback? onShowMessage;
|
||||||
|
|
||||||
/// Callback to call when the header is tapped.
|
/// Callback to call when the header is tapped.
|
||||||
final VoidCallback onTitleTap;
|
final VoidCallback? onTitleTap;
|
||||||
|
|
||||||
/// Callback to call when the image is tapped.
|
/// Callback to call when the image is tapped.
|
||||||
final VoidCallback onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
|
|
||||||
/// Creates a channel header
|
/// Creates a channel header
|
||||||
ImageHeader({
|
ImageHeader({
|
||||||
Key key,
|
Key? key,
|
||||||
this.message,
|
required this.message,
|
||||||
this.urls,
|
this.urls = const [],
|
||||||
this.currentIndex,
|
this.currentIndex,
|
||||||
this.showBackButton = true,
|
this.showBackButton = true,
|
||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
@@ -109,7 +109,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
void _showMessageActionModalBottomSheet(BuildContext context) async {
|
void _showMessageActionModalBottomSheet(BuildContext context) async {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
var result = await showDialog(
|
final result = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ class InfoTile extends StatelessWidget {
|
|||||||
final String message;
|
final String message;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final bool showMessage;
|
final bool showMessage;
|
||||||
final Alignment tileAnchor;
|
final Alignment? tileAnchor;
|
||||||
final Alignment childAnchor;
|
final Alignment? childAnchor;
|
||||||
final TextStyle textStyle;
|
final TextStyle? textStyle;
|
||||||
final Color backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
InfoTile({
|
InfoTile({
|
||||||
this.message,
|
required this.message,
|
||||||
this.child,
|
required this.child,
|
||||||
this.showMessage,
|
required this.showMessage,
|
||||||
this.tileAnchor,
|
this.tileAnchor,
|
||||||
this.childAnchor,
|
this.childAnchor,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ extension on Duration {
|
|||||||
|
|
||||||
class MediaListView extends StatefulWidget {
|
class MediaListView extends StatefulWidget {
|
||||||
final List<String> selectedIds;
|
final List<String> selectedIds;
|
||||||
final void Function(AssetEntity media) onSelect;
|
final void Function(AssetEntity media)? onSelect;
|
||||||
|
|
||||||
const MediaListView({
|
const MediaListView({
|
||||||
Key key,
|
Key? key,
|
||||||
this.selectedIds = const [],
|
this.selectedIds = const [],
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -58,7 +58,7 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.onSelect != null) {
|
if (widget.onSelect != null) {
|
||||||
widget.onSelect(media);
|
widget.onSelect!(media);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -147,7 +147,7 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
final assetList = await PhotoManager.getAssetPathList(
|
final assetList = await PhotoManager.getAssetPathList(
|
||||||
hasAll: true,
|
hasAll: true,
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (value?.isNotEmpty == true) {
|
if (value.isNotEmpty == true) {
|
||||||
return value.singleWhere((element) => element.isAll);
|
return value.singleWhere((element) => element.isAll);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -169,29 +169,29 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
|
|
||||||
class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
||||||
const MediaThumbnailProvider({
|
const MediaThumbnailProvider({
|
||||||
@required this.media,
|
required this.media,
|
||||||
}) : assert(media != null);
|
});
|
||||||
|
|
||||||
final AssetEntity media;
|
final AssetEntity media;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageStreamCompleter load(key, decode) {
|
ImageStreamCompleter load(key, decode) {
|
||||||
return MultiFrameImageStreamCompleter(
|
return MultiFrameImageStreamCompleter(
|
||||||
codec: _loadAsync(key, decode),
|
codec: _loadAsync(key, decode) as Future<ui.Codec>,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
informationCollector: () sync* {
|
informationCollector: () sync* {
|
||||||
yield ErrorDescription('Id: ${media?.id}');
|
yield ErrorDescription('Id: ${media.id}');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(
|
Future<ui.Codec?> _loadAsync(
|
||||||
MediaThumbnailProvider key, DecoderCallback decode) async {
|
MediaThumbnailProvider key, DecoderCallback decode) async {
|
||||||
assert(key == this);
|
assert(key == this);
|
||||||
final bytes = await media.thumbData;
|
final bytes = await media.thumbData;
|
||||||
if (bytes?.isNotEmpty != true) return null;
|
if (bytes?.isNotEmpty != true) return null;
|
||||||
|
|
||||||
return await decode(bytes);
|
return await decode(bytes!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -203,12 +203,12 @@ class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
|
|||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
if (other.runtimeType != runtimeType) return false;
|
if (other.runtimeType != runtimeType) return false;
|
||||||
final MediaThumbnailProvider typedOther = other;
|
final MediaThumbnailProvider typedOther = other;
|
||||||
return media?.id == typedOther.media?.id;
|
return media.id == typedOther.media.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => media?.id?.hashCode ?? 0;
|
int get hashCode => media.id.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '$runtimeType("${media?.id}")';
|
String toString() => '$runtimeType("${media.id}")';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ class MentionTile extends StatelessWidget {
|
|||||||
final Member member;
|
final Member member;
|
||||||
|
|
||||||
/// Widget to display as title
|
/// Widget to display as title
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
|
|
||||||
/// Widget to display below [title]
|
/// Widget to display below [title]
|
||||||
final Widget subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Widget at the start of the tile
|
/// Widget at the start of the tile
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// Widget at the end of tile
|
/// Widget at the end of tile
|
||||||
final Widget trailing;
|
final Widget? trailing;
|
||||||
|
|
||||||
MentionTile(
|
MentionTile(
|
||||||
this.member, {
|
this.member, {
|
||||||
@@ -46,7 +46,7 @@ class MentionTile extends StatelessWidget {
|
|||||||
40,
|
40,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
user: member.user,
|
user: member.user!,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 8.0,
|
width: 8.0,
|
||||||
@@ -60,7 +60,7 @@ class MentionTile extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
title ??
|
title ??
|
||||||
Text(
|
Text(
|
||||||
'${member.user.name}',
|
'${member.user!.name}',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||||
@@ -87,7 +87,10 @@ class MentionTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
trailing ??
|
trailing ??
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 18.0, left: 8.0),
|
padding: const EdgeInsets.only(
|
||||||
|
right: 18.0,
|
||||||
|
left: 8.0,
|
||||||
|
),
|
||||||
child: StreamSvgIcon.mentions(
|
child: StreamSvgIcon.mentions(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
/// Class describing a message action
|
/// Class describing a message action
|
||||||
class MessageAction {
|
class MessageAction {
|
||||||
/// leading widget
|
/// leading widget
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// title widget
|
/// title widget
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
|
|
||||||
/// callback called on tap
|
/// callback called on tap
|
||||||
final OnMessageTap onTap;
|
final OnMessageTap? onTap;
|
||||||
|
|
||||||
/// returns a new instance of a [MessageAction]
|
/// returns a new instance of a [MessageAction]
|
||||||
MessageAction({
|
MessageAction({
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import 'stream_chat.dart';
|
|||||||
import 'stream_chat_theme.dart';
|
import 'stream_chat_theme.dart';
|
||||||
|
|
||||||
class MessageActionsModal extends StatefulWidget {
|
class MessageActionsModal extends StatefulWidget {
|
||||||
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
final Widget Function(BuildContext, Message)? editMessageInputBuilder;
|
||||||
final OnMessageTap onThreadReplyTap;
|
final OnMessageTap? onThreadReplyTap;
|
||||||
final OnMessageTap onReplyTap;
|
final OnMessageTap? onReplyTap;
|
||||||
final Message message;
|
final Message message;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
final bool showReactions;
|
final bool showReactions;
|
||||||
final OnMessageTap onCopyTap;
|
final OnMessageTap? onCopyTap;
|
||||||
final bool showDeleteMessage;
|
final bool showDeleteMessage;
|
||||||
final bool showCopyMessage;
|
final bool showCopyMessage;
|
||||||
final bool showEditMessage;
|
final bool showEditMessage;
|
||||||
@@ -31,18 +31,18 @@ class MessageActionsModal extends StatefulWidget {
|
|||||||
final bool showThreadReplyMessage;
|
final bool showThreadReplyMessage;
|
||||||
final bool showFlagButton;
|
final bool showFlagButton;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
final ShapeBorder messageShape;
|
final ShapeBorder? messageShape;
|
||||||
final ShapeBorder attachmentShape;
|
final ShapeBorder? attachmentShape;
|
||||||
final DisplayWidget showUserAvatar;
|
final DisplayWidget showUserAvatar;
|
||||||
final BorderRadius attachmentBorderRadiusGeometry;
|
final BorderRadius? attachmentBorderRadiusGeometry;
|
||||||
|
|
||||||
/// List of custom actions
|
/// List of custom actions
|
||||||
final List<MessageAction> customActions;
|
final List<MessageAction> customActions;
|
||||||
|
|
||||||
const MessageActionsModal({
|
const MessageActionsModal({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.showReactions = true,
|
this.showReactions = true,
|
||||||
this.showDeleteMessage = true,
|
this.showDeleteMessage = true,
|
||||||
this.showEditMessage = true,
|
this.showEditMessage = true,
|
||||||
@@ -80,24 +80,26 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
final user = StreamChat.of(context).user;
|
final user = StreamChat.of(context).user;
|
||||||
|
|
||||||
final roughMaxSize = 2 * size.width / 3;
|
final roughMaxSize = 2 * size.width / 3;
|
||||||
var messageTextLength = widget.message.text.length;
|
var messageTextLength = widget.message.text!.length;
|
||||||
if (widget.message.quotedMessage != null) {
|
if (widget.message.quotedMessage != null) {
|
||||||
var quotedMessageLength = widget.message.quotedMessage.text.length + 40;
|
var quotedMessageLength =
|
||||||
if (widget.message.quotedMessage.attachments?.isNotEmpty == true) {
|
(widget.message.quotedMessage!.text?.length ?? 0) + 40;
|
||||||
|
if (widget.message.quotedMessage!.attachments.isNotEmpty) {
|
||||||
quotedMessageLength += 40;
|
quotedMessageLength += 40;
|
||||||
}
|
}
|
||||||
if (quotedMessageLength > messageTextLength) {
|
if (quotedMessageLength > messageTextLength) {
|
||||||
messageTextLength = quotedMessageLength;
|
messageTextLength = quotedMessageLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final roughSentenceSize =
|
final roughSentenceSize = messageTextLength *
|
||||||
messageTextLength * widget.messageTheme.messageText.fontSize * 1.2;
|
(widget.messageTheme?.messageText?.fontSize ?? 1) *
|
||||||
final divFactor = widget.message.attachments?.isNotEmpty == true
|
1.2;
|
||||||
|
final divFactor = widget.message.attachments.isNotEmpty == true
|
||||||
? 1
|
? 1
|
||||||
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
|
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
|
||||||
|
|
||||||
final hasFileAttachment =
|
final hasFileAttachment =
|
||||||
widget.message.attachments?.any((it) => it.type == 'file') == true;
|
widget.message.attachments.any((it) => it.type == 'file') == true;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
@@ -134,11 +136,10 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (widget.showReactions &&
|
if (widget.showReactions &&
|
||||||
(widget.message.status ==
|
(widget.message.status ==
|
||||||
MessageSendingStatus.sent ||
|
MessageSendingStatus.sent))
|
||||||
widget.message.status == null))
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment(
|
alignment: Alignment(
|
||||||
user.id == widget.message.user.id
|
user?.id == widget.message.user?.id
|
||||||
? (divFactor > 1.0
|
? (divFactor > 1.0
|
||||||
? 0.0
|
? 0.0
|
||||||
: (1.0 - divFactor))
|
: (1.0 - divFactor))
|
||||||
@@ -159,8 +160,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
attachmentBorderRadiusGeometry:
|
attachmentBorderRadiusGeometry:
|
||||||
widget.attachmentBorderRadiusGeometry,
|
widget.attachmentBorderRadiusGeometry,
|
||||||
message: widget.message.copyWith(
|
message: widget.message.copyWith(
|
||||||
text: widget.message.text.length > 200
|
text: widget.message.text!.length > 200
|
||||||
? '${widget.message.text.substring(0, 200)}...'
|
? '${widget.message.text!.substring(0, 200)}...'
|
||||||
: widget.message.text,
|
: widget.message.text,
|
||||||
),
|
),
|
||||||
messageTheme: widget.messageTheme,
|
messageTheme: widget.messageTheme,
|
||||||
@@ -177,15 +178,14 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
textPadding: EdgeInsets.symmetric(
|
textPadding: EdgeInsets.symmetric(
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
horizontal: widget.message.text.isOnlyEmoji
|
horizontal: widget.message.text!.isOnlyEmoji
|
||||||
? 0
|
? 0
|
||||||
: 16.0,
|
: 16.0,
|
||||||
),
|
),
|
||||||
showReactionPickerIndicator:
|
showReactionPickerIndicator:
|
||||||
widget.showReactions &&
|
widget.showReactions &&
|
||||||
(widget.message.status ==
|
(widget.message.status ==
|
||||||
MessageSendingStatus.sent ||
|
MessageSendingStatus.sent),
|
||||||
widget.message.status == null),
|
|
||||||
showInChannelIndicator: false,
|
showInChannelIndicator: false,
|
||||||
showSendingIndicator: false,
|
showSendingIndicator: false,
|
||||||
shape: widget.messageShape,
|
shape: widget.messageShape,
|
||||||
@@ -212,14 +212,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
CrossAxisAlignment.stretch,
|
CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (widget.showReplyMessage &&
|
if (widget.showReplyMessage &&
|
||||||
widget.message.status ==
|
widget.message.status ==
|
||||||
MessageSendingStatus.sent ||
|
MessageSendingStatus.sent)
|
||||||
widget.message.status == null)
|
|
||||||
_buildReplyButton(context),
|
_buildReplyButton(context),
|
||||||
if (widget.showThreadReplyMessage &&
|
if (widget.showThreadReplyMessage &&
|
||||||
(widget.message.status ==
|
(widget.message.status ==
|
||||||
MessageSendingStatus.sent ||
|
MessageSendingStatus.sent) &&
|
||||||
widget.message.status == null) &&
|
|
||||||
widget.message.parentId == null)
|
widget.message.parentId == null)
|
||||||
_buildThreadReplyButton(context),
|
_buildThreadReplyButton(context),
|
||||||
if (widget.showResendMessage)
|
if (widget.showResendMessage)
|
||||||
@@ -315,7 +313,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
okText: 'OK',
|
okText: 'OK',
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (json.decode(err?.body ?? {})['code'] == 4) {
|
if (err is ApiError && json.decode(err.body ?? '{}')['code'] == 4) {
|
||||||
await showInfoDialog(
|
await showInfoDialog(
|
||||||
context,
|
context,
|
||||||
icon: StreamSvgIcon.flag(
|
icon: StreamSvgIcon.flag(
|
||||||
@@ -337,7 +335,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_showActions = false;
|
_showActions = false;
|
||||||
});
|
});
|
||||||
var answer = await showConfirmationDialog(
|
final answer = await showConfirmationDialog(
|
||||||
context,
|
context,
|
||||||
title: 'Delete message',
|
title: 'Delete message',
|
||||||
icon: StreamSvgIcon.flag(
|
icon: StreamSvgIcon.flag(
|
||||||
@@ -349,7 +347,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
cancelText: 'CANCEL',
|
cancelText: 'CANCEL',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (answer) {
|
if (answer == true) {
|
||||||
try {
|
try {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
await StreamChannel.of(context).channel.deleteMessage(widget.message);
|
await StreamChannel.of(context).channel.deleteMessage(widget.message);
|
||||||
@@ -381,7 +379,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
if (widget.onReplyTap != null) {
|
if (widget.onReplyTap != null) {
|
||||||
widget.onReplyTap(widget.message);
|
widget.onReplyTap!(widget.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -578,7 +576,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
widget.editMessageInputBuilder != null
|
widget.editMessageInputBuilder != null
|
||||||
? widget.editMessageInputBuilder(context, widget.message)
|
? widget.editMessageInputBuilder!(context, widget.message)
|
||||||
: MessageInput(
|
: MessageInput(
|
||||||
editMessage: widget.message,
|
editMessage: widget.message,
|
||||||
preMessageSending: (m) {
|
preMessageSending: (m) {
|
||||||
@@ -599,7 +597,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
if (widget.onThreadReplyTap != null) {
|
if (widget.onThreadReplyTap != null) {
|
||||||
widget.onThreadReplyTap(widget.message);
|
widget.onThreadReplyTap!(widget.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
@@ -19,6 +20,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
|||||||
import 'package:stream_chat_flutter/src/video_service.dart';
|
import 'package:stream_chat_flutter/src/video_service.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:substring_highlight/substring_highlight.dart';
|
import 'package:substring_highlight/substring_highlight.dart';
|
||||||
|
import 'package:video_compress/video_compress.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'attachment/attachment.dart';
|
import 'attachment/attachment.dart';
|
||||||
@@ -109,7 +111,7 @@ const _kMaxAttachmentSize = 20971520; // 20MB in Bytes
|
|||||||
class MessageInput extends StatefulWidget {
|
class MessageInput extends StatefulWidget {
|
||||||
/// Instantiate a new MessageInput
|
/// Instantiate a new MessageInput
|
||||||
MessageInput({
|
MessageInput({
|
||||||
Key key,
|
Key? key,
|
||||||
this.onMessageSent,
|
this.onMessageSent,
|
||||||
this.preMessageSending,
|
this.preMessageSending,
|
||||||
this.parentMessage,
|
this.parentMessage,
|
||||||
@@ -135,20 +137,20 @@ class MessageInput extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Message to edit
|
/// Message to edit
|
||||||
final Message editMessage;
|
final Message? editMessage;
|
||||||
|
|
||||||
/// Message to start with
|
/// Message to start with
|
||||||
final Message initialMessage;
|
final Message? initialMessage;
|
||||||
|
|
||||||
/// Function called after sending the message
|
/// Function called after sending the message
|
||||||
final void Function(Message) onMessageSent;
|
final void Function(Message)? onMessageSent;
|
||||||
|
|
||||||
/// Function called right before sending the message
|
/// Function called right before sending the message
|
||||||
/// Use this to transform the message
|
/// Use this to transform the message
|
||||||
final FutureOr<Message> Function(Message) preMessageSending;
|
final FutureOr<Message> Function(Message)? preMessageSending;
|
||||||
|
|
||||||
/// Parent message in case of a thread
|
/// Parent message in case of a thread
|
||||||
final Message parentMessage;
|
final Message? parentMessage;
|
||||||
|
|
||||||
/// Maximum Height for the TextField to grow before it starts scrolling
|
/// Maximum Height for the TextField to grow before it starts scrolling
|
||||||
final double maxHeight;
|
final double maxHeight;
|
||||||
@@ -166,25 +168,25 @@ class MessageInput extends StatefulWidget {
|
|||||||
final bool hideSendAsDm;
|
final bool hideSendAsDm;
|
||||||
|
|
||||||
/// The text controller of the TextField
|
/// The text controller of the TextField
|
||||||
final TextEditingController textEditingController;
|
final TextEditingController? textEditingController;
|
||||||
|
|
||||||
/// List of action widgets
|
/// List of action widgets
|
||||||
final List<Widget> actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
/// The location of the custom actions
|
/// The location of the custom actions
|
||||||
final ActionsLocation actionsLocation;
|
final ActionsLocation actionsLocation;
|
||||||
|
|
||||||
/// Map that defines a thumbnail builder for an attachment type
|
/// Map that defines a thumbnail builder for an attachment type
|
||||||
final Map<String, AttachmentThumbnailBuilder> attachmentThumbnailBuilders;
|
final Map<String, AttachmentThumbnailBuilder>? attachmentThumbnailBuilders;
|
||||||
|
|
||||||
/// The focus node associated to the TextField
|
/// The focus node associated to the TextField
|
||||||
final FocusNode focusNode;
|
final FocusNode? focusNode;
|
||||||
|
|
||||||
///
|
///
|
||||||
final Message quotedMessage;
|
final Message? quotedMessage;
|
||||||
|
|
||||||
///
|
///
|
||||||
final VoidCallback onQuotedMessageCleared;
|
final VoidCallback? onQuotedMessageCleared;
|
||||||
|
|
||||||
/// The location of the send button
|
/// The location of the send button
|
||||||
final SendButtonLocation sendButtonLocation;
|
final SendButtonLocation sendButtonLocation;
|
||||||
@@ -193,20 +195,20 @@ class MessageInput extends StatefulWidget {
|
|||||||
final bool autofocus;
|
final bool autofocus;
|
||||||
|
|
||||||
/// Send button widget in an idle state
|
/// Send button widget in an idle state
|
||||||
final Widget idleSendButton;
|
final Widget? idleSendButton;
|
||||||
|
|
||||||
/// Send button widget in an active state
|
/// Send button widget in an active state
|
||||||
final Widget activeSendButton;
|
final Widget? activeSendButton;
|
||||||
|
|
||||||
/// Customize the tile for the mentions overlay
|
/// Customize the tile for the mentions overlay
|
||||||
final MentionTileBuilder mentionsTileBuilder;
|
final MentionTileBuilder? mentionsTileBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MessageInputState createState() => MessageInputState();
|
MessageInputState createState() => MessageInputState();
|
||||||
|
|
||||||
/// Use this method to get the current [StreamChatState] instance
|
/// Use this method to get the current [StreamChatState] instance
|
||||||
static MessageInputState of(BuildContext context) {
|
static MessageInputState of(BuildContext context) {
|
||||||
MessageInputState messageInputState;
|
MessageInputState? messageInputState;
|
||||||
|
|
||||||
messageInputState = context.findAncestorStateOfType<MessageInputState>();
|
messageInputState = context.findAncestorStateOfType<MessageInputState>();
|
||||||
|
|
||||||
@@ -224,15 +226,15 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
final List<User> _mentionedUsers = [];
|
final List<User> _mentionedUsers = [];
|
||||||
|
|
||||||
final _imagePicker = ImagePicker();
|
final _imagePicker = ImagePicker();
|
||||||
FocusNode _focusNode;
|
late final FocusNode _focusNode;
|
||||||
bool _inputEnabled = true;
|
bool _inputEnabled = true;
|
||||||
bool _messageIsPresent = false;
|
bool _messageIsPresent = false;
|
||||||
bool _animateContainer = true;
|
bool _animateContainer = true;
|
||||||
bool _commandEnabled = false;
|
bool _commandEnabled = false;
|
||||||
OverlayEntry _commandsOverlay, _mentionsOverlay, _emojiOverlay;
|
OverlayEntry? _commandsOverlay, _mentionsOverlay, _emojiOverlay;
|
||||||
Iterable<String> _emojiNames;
|
late Iterable<String> _emojiNames;
|
||||||
|
|
||||||
Command _chosenCommand;
|
Command? _chosenCommand;
|
||||||
bool _actionsShrunk = false;
|
bool _actionsShrunk = false;
|
||||||
bool _sendAsDm = false;
|
bool _sendAsDm = false;
|
||||||
bool _openFilePickerSection = false;
|
bool _openFilePickerSection = false;
|
||||||
@@ -242,7 +244,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
KeyboardVisibilityController();
|
KeyboardVisibilityController();
|
||||||
|
|
||||||
/// The editing controller passed to the input TextField
|
/// The editing controller passed to the input TextField
|
||||||
TextEditingController textEditingController;
|
late final TextEditingController textEditingController;
|
||||||
|
|
||||||
bool get _hasQuotedMessage => widget.quotedMessage != null;
|
bool get _hasQuotedMessage => widget.quotedMessage != null;
|
||||||
|
|
||||||
@@ -250,7 +252,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_focusNode = widget.focusNode ?? FocusNode();
|
_focusNode = widget.focusNode ?? FocusNode();
|
||||||
_emojiNames = Emoji.all().map((e) => e.name);
|
_emojiNames =
|
||||||
|
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
|
||||||
|
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
_keyboardListener =
|
_keyboardListener =
|
||||||
@@ -264,7 +267,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
textEditingController =
|
textEditingController =
|
||||||
widget.textEditingController ?? TextEditingController();
|
widget.textEditingController ?? TextEditingController();
|
||||||
if (widget.editMessage != null || widget.initialMessage != null) {
|
if (widget.editMessage != null || widget.initialMessage != null) {
|
||||||
_parseExistingMessage(widget.editMessage ?? widget.initialMessage);
|
_parseExistingMessage(widget.editMessage ?? widget.initialMessage!);
|
||||||
}
|
}
|
||||||
|
|
||||||
textEditingController.addListener(() {
|
textEditingController.addListener(() {
|
||||||
@@ -447,7 +450,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
firstChild: sendButton,
|
firstChild: sendButton,
|
||||||
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
|
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
|
||||||
duration:
|
duration:
|
||||||
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration,
|
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration!,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -490,9 +493,9 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
widget.editMessage == null &&
|
widget.editMessage == null &&
|
||||||
StreamChannel.of(context)
|
StreamChannel.of(context)
|
||||||
.channel
|
.channel
|
||||||
?.config
|
.config
|
||||||
?.commands
|
?.commands
|
||||||
?.isNotEmpty ==
|
.isNotEmpty ==
|
||||||
true)
|
true)
|
||||||
_buildCommandButton(),
|
_buildCommandButton(),
|
||||||
...widget.actions ?? [],
|
...widget.actions ?? [],
|
||||||
@@ -571,7 +574,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
return InputDecoration(
|
return InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
hintText: _getHint(),
|
hintText: _getHint(),
|
||||||
hintStyle: theme.messageInputTheme.inputTextStyle.copyWith(
|
hintStyle: theme.messageInputTheme.inputTextStyle!.copyWith(
|
||||||
color: theme.colorTheme.grey,
|
color: theme.colorTheme.grey,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
@@ -621,7 +624,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
size: 16.0,
|
size: 16.0,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_chosenCommand?.name?.toUpperCase() ?? '',
|
_chosenCommand?.name.toUpperCase() ?? '',
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.footnoteBold
|
.footnoteBold
|
||||||
@@ -676,9 +679,9 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
).merge(passedDecoration);
|
).merge(passedDecoration);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer _debounce;
|
Timer? _debounce;
|
||||||
|
|
||||||
String _previousValue;
|
String? _previousValue;
|
||||||
|
|
||||||
void _onChanged(BuildContext context, String s) {
|
void _onChanged(BuildContext context, String s) {
|
||||||
if (s == _previousValue) {
|
if (s == _previousValue) {
|
||||||
@@ -686,14 +689,14 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
_previousValue = s;
|
_previousValue = s;
|
||||||
|
|
||||||
if (_debounce?.isActive == true) _debounce.cancel();
|
if (_debounce?.isActive == true) _debounce!.cancel();
|
||||||
_debounce = Timer(
|
_debounce = Timer(
|
||||||
const Duration(milliseconds: 350),
|
const Duration(milliseconds: 350),
|
||||||
() {
|
() {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StreamChannel.of(context).channel.keyStroke()?.catchError((e) {});
|
StreamChannel.of(context).channel.keyStroke().catchError((e) {});
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_messageIsPresent = s.trim().isNotEmpty;
|
_messageIsPresent = s.trim().isNotEmpty;
|
||||||
@@ -721,7 +724,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getHint() {
|
String _getHint() {
|
||||||
if (_commandEnabled && _chosenCommand.name == 'giphy') {
|
if (_commandEnabled && _chosenCommand!.name == 'giphy') {
|
||||||
return 'Search GIFs';
|
return 'Search GIFs';
|
||||||
}
|
}
|
||||||
if (_attachments.isNotEmpty) {
|
if (_attachments.isNotEmpty) {
|
||||||
@@ -742,7 +745,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
final textToSelection = textEditingController.text
|
final textToSelection = textEditingController.text
|
||||||
.substring(0, textEditingController.value.selection.start);
|
.substring(0, textEditingController.value.selection.start);
|
||||||
final splits = textToSelection.split(':');
|
final splits = textToSelection.split(':');
|
||||||
final query = splits[splits.length - 2]?.toLowerCase();
|
final query = splits[splits.length - 2].toLowerCase();
|
||||||
final emoji = Emoji.byName(query);
|
final emoji = Emoji.byName(query);
|
||||||
|
|
||||||
if (textToSelection.endsWith(':') && emoji != null) {
|
if (textToSelection.endsWith(':') && emoji != null) {
|
||||||
@@ -751,7 +754,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
_emojiOverlay = _buildEmojiOverlay();
|
_emojiOverlay = _buildEmojiOverlay();
|
||||||
|
|
||||||
if (_emojiOverlay != null) {
|
if (_emojiOverlay != null) {
|
||||||
Overlay.of(context).insert(_emojiOverlay);
|
Overlay.of(context)!.insert(_emojiOverlay!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -767,7 +770,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.contains('@')) {
|
.contains('@')) {
|
||||||
_mentionsOverlay = _buildMentionsOverlayEntry();
|
_mentionsOverlay = _buildMentionsOverlayEntry();
|
||||||
if (_mentionsOverlay != null) {
|
if (_mentionsOverlay != null) {
|
||||||
Overlay.of(context).insert(_mentionsOverlay);
|
Overlay.of(context)!.insert(_mentionsOverlay!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -778,8 +781,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.channel
|
.channel
|
||||||
.config
|
.config
|
||||||
?.commands
|
?.commands
|
||||||
?.where((element) => element.name == s.substring(1))
|
.where((element) => element.name == s.substring(1))
|
||||||
?.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
if (matchedCommandsList.length == 1) {
|
if (matchedCommandsList.length == 1) {
|
||||||
@@ -789,32 +792,32 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_commandEnabled = true;
|
_commandEnabled = true;
|
||||||
});
|
});
|
||||||
_commandsOverlay.remove();
|
_commandsOverlay!.remove();
|
||||||
_commandsOverlay = null;
|
_commandsOverlay = null;
|
||||||
} else {
|
} else {
|
||||||
_commandsOverlay = _buildCommandsOverlayEntry();
|
_commandsOverlay = _buildCommandsOverlayEntry();
|
||||||
if (_commandsOverlay != null) {
|
if (_commandsOverlay != null) {
|
||||||
Overlay.of(context).insert(_commandsOverlay);
|
Overlay.of(context)!.insert(_commandsOverlay!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _buildCommandsOverlayEntry() {
|
OverlayEntry? _buildCommandsOverlayEntry() {
|
||||||
final text = textEditingController.text.trimLeft();
|
final text = textEditingController.text.trimLeft();
|
||||||
final commands = StreamChannel.of(context)
|
final commands = StreamChannel.of(context)
|
||||||
.channel
|
.channel
|
||||||
.config
|
.config
|
||||||
?.commands
|
?.commands
|
||||||
?.where((c) => c.name.contains(text.replaceFirst('/', '')))
|
.where((c) => c.name.contains(text.replaceFirst('/', '')))
|
||||||
?.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
if (commands.isEmpty) {
|
if (commands.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderBox renderBox = context.findRenderObject();
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
|
|
||||||
return OverlayEntry(builder: (context) {
|
return OverlayEntry(builder: (context) {
|
||||||
@@ -951,7 +954,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.colorTheme
|
.colorTheme
|
||||||
.black
|
.black
|
||||||
.withOpacity(0.2));
|
.withOpacity(0.2));
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
return _attachmentContainsFile
|
return _attachmentContainsFile
|
||||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||||
@@ -964,17 +966,14 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.colorTheme
|
.colorTheme
|
||||||
.black
|
.black
|
||||||
.withOpacity(0.2));
|
.withOpacity(0.2));
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
return _attachmentContainsFile && _attachments.isNotEmpty
|
return _attachmentContainsFile && _attachments.isNotEmpty
|
||||||
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
||||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
||||||
break;
|
|
||||||
case 3:
|
case 3:
|
||||||
return _attachmentContainsFile && _attachments.isNotEmpty
|
return _attachmentContainsFile && _attachments.isNotEmpty
|
||||||
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
||||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return Colors.black;
|
return Colors.black;
|
||||||
}
|
}
|
||||||
@@ -1106,10 +1105,10 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _addAttachment(AssetEntity medium) async {
|
void _addAttachment(AssetEntity medium) async {
|
||||||
final mediaFile = await medium.originFile.timeout(
|
final mediaFile = await (medium.originFile.timeout(
|
||||||
Duration(seconds: 5),
|
Duration(seconds: 5),
|
||||||
onTimeout: () => medium.originFile,
|
onTimeout: () => medium.originFile,
|
||||||
);
|
) as FutureOr<File>);
|
||||||
|
|
||||||
var file = AttachmentFile(
|
var file = AttachmentFile(
|
||||||
path: mediaFile.path,
|
path: mediaFile.path,
|
||||||
@@ -1117,11 +1116,12 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
bytes: mediaFile.readAsBytesSync(),
|
bytes: mediaFile.readAsBytesSync(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (file.size > _kMaxAttachmentSize) {
|
if (file.size! > _kMaxAttachmentSize) {
|
||||||
if (medium?.type == AssetType.video) {
|
if (medium.type == AssetType.video) {
|
||||||
final mediaInfo = await VideoService.compressVideo(file.path);
|
final mediaInfo = await (VideoService.compressVideo(file.path)
|
||||||
|
as FutureOr<MediaInfo>);
|
||||||
|
|
||||||
if (mediaInfo.filesize > _kMaxAttachmentSize) {
|
if (mediaInfo.filesize! > _kMaxAttachmentSize) {
|
||||||
_showErrorAlert(
|
_showErrorAlert(
|
||||||
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
|
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
|
||||||
);
|
);
|
||||||
@@ -1129,8 +1129,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
file = AttachmentFile(
|
file = AttachmentFile(
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: mediaInfo.filesize,
|
size: mediaInfo.filesize!,
|
||||||
bytes: await mediaInfo.file.readAsBytes(),
|
bytes: await mediaInfo.file?.readAsBytes(),
|
||||||
path: mediaInfo.path,
|
path: mediaInfo.path,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1159,7 +1159,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
size: 24.0,
|
size: 24.0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'ban':
|
case 'ban':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1169,7 +1168,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'flag':
|
case 'flag':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1179,7 +1177,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'imgur':
|
case 'imgur':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1190,7 +1187,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'mute':
|
case 'mute':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1200,7 +1196,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'unban':
|
case 'unban':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1210,7 +1205,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'unmute':
|
case 'unmute':
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1220,7 +1214,6 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1230,17 +1223,16 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _buildMentionsOverlayEntry() {
|
OverlayEntry? _buildMentionsOverlayEntry() {
|
||||||
final splits = textEditingController.text
|
final splits = textEditingController.text
|
||||||
.substring(0, textEditingController.value.selection.start)
|
.substring(0, textEditingController.value.selection.start)
|
||||||
.split('@');
|
.split('@');
|
||||||
final query = splits.last.toLowerCase();
|
final query = splits.last.toLowerCase();
|
||||||
|
|
||||||
Future<List<Member>> queryMembers;
|
Future<List<Member>>? queryMembers;
|
||||||
|
|
||||||
if (query.isNotEmpty) {
|
if (query.isNotEmpty) {
|
||||||
queryMembers = StreamChannel.of(context)
|
queryMembers = StreamChannel.of(context)
|
||||||
@@ -1249,16 +1241,16 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.then((res) => res.members);
|
.then((res) => res.members);
|
||||||
}
|
}
|
||||||
|
|
||||||
final members = StreamChannel.of(context).channel.state.members?.where((m) {
|
final members = StreamChannel.of(context).channel.state?.members.where((m) {
|
||||||
return m.user.name.toLowerCase().contains(query);
|
return m.user?.name.toLowerCase().contains(query) == true;
|
||||||
})?.toList() ??
|
}).toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
if (members.isEmpty) {
|
if (members.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderBox renderBox = context.findRenderObject();
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
|
|
||||||
return OverlayEntry(
|
return OverlayEntry(
|
||||||
@@ -1298,7 +1290,9 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
...snapshot.data.map(
|
...snapshot.data!
|
||||||
|
.where((it) => it.user != null)
|
||||||
|
.map(
|
||||||
(m) {
|
(m) {
|
||||||
return Material(
|
return Material(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
@@ -1306,9 +1300,11 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
.white,
|
.white,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_mentionedUsers.add(m.user);
|
if (m.user != null) {
|
||||||
|
_mentionedUsers.add(m.user!);
|
||||||
|
}
|
||||||
|
|
||||||
splits[splits.length - 1] = m.user.name;
|
splits[splits.length - 1] = m.user!.name;
|
||||||
final rejoin = splits.join('@');
|
final rejoin = splits.join('@');
|
||||||
|
|
||||||
textEditingController.value =
|
textEditingController.value =
|
||||||
@@ -1321,12 +1317,13 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
offset: rejoin.length,
|
offset: rejoin.length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_debounce.cancel();
|
_debounce!.cancel();
|
||||||
_mentionsOverlay?.remove();
|
_mentionsOverlay?.remove();
|
||||||
_mentionsOverlay = null;
|
_mentionsOverlay = null;
|
||||||
},
|
},
|
||||||
child: widget.mentionsTileBuilder != null
|
child: widget.mentionsTileBuilder != null
|
||||||
? widget.mentionsTileBuilder(context, m)
|
? widget.mentionsTileBuilder!(
|
||||||
|
context, m)
|
||||||
: MentionTile(m),
|
: MentionTile(m),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1349,7 +1346,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _buildEmojiOverlay() {
|
OverlayEntry? _buildEmojiOverlay() {
|
||||||
final splits = textEditingController.text
|
final splits = textEditingController.text
|
||||||
.substring(0, textEditingController.value.selection.start)
|
.substring(0, textEditingController.value.selection.start)
|
||||||
.split(':');
|
.split(':');
|
||||||
@@ -1368,7 +1365,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderBox renderBox = context.findRenderObject();
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
|
|
||||||
return OverlayEntry(builder: (context) {
|
return OverlayEntry(builder: (context) {
|
||||||
@@ -1431,19 +1428,20 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final emoji = emojis.elementAt(i - 1);
|
final emoji = emojis.elementAt(i - 1)!;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: SubstringHighlight(
|
title: SubstringHighlight(
|
||||||
text: "${emoji.char} ${emoji.name.replaceAll('_', ' ')}",
|
text: "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
|
||||||
term: query,
|
term: query,
|
||||||
textStyleHighlight:
|
textStyleHighlight:
|
||||||
Theme.of(context).textTheme.headline6.copyWith(
|
Theme.of(context).textTheme.headline6!.copyWith(
|
||||||
fontSize: 14.5,
|
fontSize: 14.5,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
textStyle: Theme.of(context).textTheme.headline6.copyWith(
|
textStyle:
|
||||||
fontSize: 14.5,
|
Theme.of(context).textTheme.headline6!.copyWith(
|
||||||
),
|
fontSize: 14.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_chooseEmoji(splits, emoji);
|
_chooseEmoji(splits, emoji);
|
||||||
@@ -1457,7 +1455,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _chooseEmoji(List<String> splits, Emoji emoji) {
|
void _chooseEmoji(List<String> splits, Emoji emoji) {
|
||||||
final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char;
|
final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char!;
|
||||||
|
|
||||||
textEditingController.value = TextEditingValue(
|
textEditingController.value = TextEditingValue(
|
||||||
text: rejoin +
|
text: rejoin +
|
||||||
@@ -1485,8 +1483,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
Widget _buildReplyToMessage() {
|
Widget _buildReplyToMessage() {
|
||||||
if (!_hasQuotedMessage) return Offstage();
|
if (!_hasQuotedMessage) return Offstage();
|
||||||
final containsUrl = widget.quotedMessage.attachments
|
final containsUrl = widget.quotedMessage!.attachments
|
||||||
?.any((element) => element.ogScrapeUrl != null) ==
|
.any((element) => element.ogScrapeUrl != null) ==
|
||||||
true;
|
true;
|
||||||
return Transform(
|
return Transform(
|
||||||
transform: Matrix4.rotationY(pi),
|
transform: Matrix4.rotationY(pi),
|
||||||
@@ -1494,7 +1492,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
child: QuotedMessageWidget(
|
child: QuotedMessageWidget(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
showBorder: !containsUrl,
|
showBorder: !containsUrl,
|
||||||
message: widget.quotedMessage,
|
message: widget.quotedMessage!,
|
||||||
messageTheme: StreamChatTheme.of(context).otherMessageTheme,
|
messageTheme: StreamChatTheme.of(context).otherMessageTheme,
|
||||||
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
|
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
|
||||||
),
|
),
|
||||||
@@ -1525,7 +1523,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: FileAttachment(
|
child: FileAttachment(
|
||||||
message: null,
|
message: Message(), // dummy message
|
||||||
attachment: e,
|
attachment: e,
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width * 0.65,
|
MediaQuery.of(context).size.width * 0.65,
|
||||||
@@ -1609,11 +1607,9 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAttachment(Attachment attachment) {
|
Widget _buildAttachment(Attachment attachment) {
|
||||||
if (attachment == null) return Offstage();
|
|
||||||
|
|
||||||
if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) ==
|
if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) ==
|
||||||
true) {
|
true) {
|
||||||
return widget.attachmentThumbnailBuilders[attachment.type](
|
return widget.attachmentThumbnailBuilders![attachment.type!]!(
|
||||||
context,
|
context,
|
||||||
attachment,
|
attachment,
|
||||||
);
|
);
|
||||||
@@ -1624,7 +1620,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
case 'giphy':
|
case 'giphy':
|
||||||
return attachment.file != null
|
return attachment.file != null
|
||||||
? Image.memory(
|
? Image.memory(
|
||||||
attachment.file.bytes,
|
attachment.file!.bytes!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, _, __) {
|
errorBuilder: (context, _, __) {
|
||||||
return Image.asset(
|
return Image.asset(
|
||||||
@@ -1636,10 +1632,11 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
: CachedNetworkImage(
|
: CachedNetworkImage(
|
||||||
imageUrl: attachment.imageUrl ??
|
imageUrl: attachment.imageUrl ??
|
||||||
attachment.assetUrl ??
|
attachment.assetUrl ??
|
||||||
attachment.thumbUrl,
|
attachment.thumbUrl!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorWidget: (_, obj, trace) {
|
errorWidget: (_, obj, trace) {
|
||||||
return getFileTypeImage(attachment.extraData['other']);
|
return getFileTypeImage(
|
||||||
|
attachment.extraData['other'] as String?);
|
||||||
},
|
},
|
||||||
progressIndicatorBuilder: (context, _, progress) {
|
progressIndicatorBuilder: (context, _, progress) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
@@ -1717,7 +1714,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_commandsOverlay = _buildCommandsOverlayEntry();
|
_commandsOverlay = _buildCommandsOverlayEntry();
|
||||||
if (_commandsOverlay != null) {
|
if (_commandsOverlay != null) {
|
||||||
Overlay.of(context).insert(_commandsOverlay);
|
Overlay.of(context)!.insert(_commandsOverlay!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -1852,7 +1849,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
void addAttachment(Attachment attachment) {
|
void addAttachment(Attachment attachment) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_attachments[attachment.id] = attachment.copyWith(
|
_attachments[attachment.id] = attachment.copyWith(
|
||||||
uploadState: attachment.uploadState ?? UploadState.success(),
|
uploadState: attachment.uploadState,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1862,8 +1859,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async {
|
void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async {
|
||||||
setState(() => _inputEnabled = false);
|
setState(() => _inputEnabled = false);
|
||||||
|
|
||||||
AttachmentFile file;
|
AttachmentFile? file;
|
||||||
String attachmentType;
|
String? attachmentType;
|
||||||
|
|
||||||
if (fileType == DefaultAttachmentTypes.image) {
|
if (fileType == DefaultAttachmentTypes.image) {
|
||||||
attachmentType = 'image';
|
attachmentType = 'image';
|
||||||
@@ -1874,7 +1871,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (camera) {
|
if (camera) {
|
||||||
PickedFile pickedFile;
|
PickedFile? pickedFile;
|
||||||
if (fileType == DefaultAttachmentTypes.image) {
|
if (fileType == DefaultAttachmentTypes.image) {
|
||||||
pickedFile = await _imagePicker.getImage(source: ImageSource.camera);
|
pickedFile = await _imagePicker.getImage(source: ImageSource.camera);
|
||||||
} else if (fileType == DefaultAttachmentTypes.video) {
|
} else if (fileType == DefaultAttachmentTypes.video) {
|
||||||
@@ -1890,7 +1887,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
FileType type;
|
late FileType type;
|
||||||
if (fileType == DefaultAttachmentTypes.image) {
|
if (fileType == DefaultAttachmentTypes.image) {
|
||||||
type = FileType.image;
|
type = FileType.image;
|
||||||
} else if (fileType == DefaultAttachmentTypes.video) {
|
} else if (fileType == DefaultAttachmentTypes.video) {
|
||||||
@@ -1902,8 +1899,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
type: type,
|
type: type,
|
||||||
withData: true,
|
withData: true,
|
||||||
);
|
);
|
||||||
if (res?.files?.isNotEmpty == true) {
|
if (res?.files.isNotEmpty == true) {
|
||||||
file = res.files.single.toAttachmentFile;
|
file = res!.files.single.toAttachmentFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1911,29 +1908,28 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
|
|
||||||
final mimeType = file.name?.mimeType ?? file.path.split('/').last.mimeType;
|
final mimeType = file.name?.mimeType ?? file.path!.split('/').last.mimeType;
|
||||||
|
|
||||||
final extraDataMap = <String, dynamic>{};
|
final extraDataMap = <String, Object>{};
|
||||||
|
|
||||||
if (mimeType?.subtype != null) {
|
if (mimeType?.subtype != null) {
|
||||||
extraDataMap['mime_type'] = mimeType.subtype.toLowerCase();
|
extraDataMap['mime_type'] = mimeType!.subtype.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.size != null) {
|
extraDataMap['file_size'] = file.size!;
|
||||||
extraDataMap['file_size'] = file.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
final attachment = Attachment(
|
final attachment = Attachment(
|
||||||
file: file,
|
file: file,
|
||||||
type: attachmentType,
|
type: attachmentType,
|
||||||
extraData: extraDataMap.isNotEmpty ? extraDataMap : null,
|
extraData: extraDataMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (file.size > _kMaxAttachmentSize) {
|
if (file.size! > _kMaxAttachmentSize) {
|
||||||
if (attachmentType == 'Video') {
|
if (attachmentType == 'Video') {
|
||||||
final mediaInfo = await VideoService.compressVideo(file.path);
|
final mediaInfo = await (VideoService.compressVideo(file.path)
|
||||||
|
as FutureOr<MediaInfo>);
|
||||||
|
|
||||||
if (mediaInfo.filesize > _kMaxAttachmentSize) {
|
if (mediaInfo.filesize! > _kMaxAttachmentSize) {
|
||||||
_showErrorAlert(
|
_showErrorAlert(
|
||||||
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
|
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
|
||||||
);
|
);
|
||||||
@@ -1941,8 +1937,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
file = AttachmentFile(
|
file = AttachmentFile(
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: mediaInfo.filesize,
|
size: mediaInfo.filesize!,
|
||||||
bytes: await mediaInfo.file.readAsBytes(),
|
bytes: await mediaInfo.file!.readAsBytes(),
|
||||||
path: mediaInfo.path,
|
path: mediaInfo.path,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1959,7 +1955,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
_attachments.update(attachment.id, (it) {
|
_attachments.update(attachment.id, (it) {
|
||||||
return it.copyWith(
|
return it.copyWith(
|
||||||
file: file,
|
file: file,
|
||||||
extraData: {...it.extraData}..update('file_size', (_) => file.size),
|
extraData: {...it.extraData}
|
||||||
|
..update('file_size', ((_) => file!.size!)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2023,7 +2020,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
final shouldUnfocus = _commandEnabled;
|
final shouldUnfocus = _commandEnabled;
|
||||||
|
|
||||||
if (_commandEnabled) {
|
if (_commandEnabled) {
|
||||||
text = '/${_chosenCommand.name} ' + text;
|
text = '/${_chosenCommand!.name} ' + text;
|
||||||
}
|
}
|
||||||
|
|
||||||
final attachments = [..._attachments.values];
|
final attachments = [..._attachments.values];
|
||||||
@@ -2031,7 +2028,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
textEditingController.clear();
|
textEditingController.clear();
|
||||||
_attachments.clear();
|
_attachments.clear();
|
||||||
if (widget.onQuotedMessageCleared != null) {
|
if (widget.onQuotedMessageCleared != null) {
|
||||||
widget.onQuotedMessageCleared();
|
widget.onQuotedMessageCleared!();
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -2047,7 +2044,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
Future sendingFuture;
|
Future sendingFuture;
|
||||||
Message message;
|
Message message;
|
||||||
if (widget.editMessage != null) {
|
if (widget.editMessage != null) {
|
||||||
message = widget.editMessage.copyWith(
|
message = widget.editMessage!.copyWith(
|
||||||
text: text,
|
text: text,
|
||||||
attachments: attachments,
|
attachments: attachments,
|
||||||
mentionedUsers:
|
mentionedUsers:
|
||||||
@@ -2066,25 +2063,25 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
if (widget.quotedMessage != null) {
|
if (widget.quotedMessage != null) {
|
||||||
message = message.copyWith(
|
message = message.copyWith(
|
||||||
quotedMessageId: widget.quotedMessage.id,
|
quotedMessageId: widget.quotedMessage!.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.preMessageSending != null) {
|
if (widget.preMessageSending != null) {
|
||||||
message = await widget.preMessageSending(message);
|
message = await widget.preMessageSending!(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final streamChannel = StreamChannel.of(context);
|
final streamChannel = StreamChannel.of(context);
|
||||||
final channel = streamChannel.channel;
|
final channel = streamChannel.channel;
|
||||||
if (!channel.state.isUpToDate) {
|
if (!channel.state!.isUpToDate) {
|
||||||
await streamChannel.reloadChannel();
|
await streamChannel.reloadChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
_mentionedUsers.clear();
|
_mentionedUsers.clear();
|
||||||
|
|
||||||
if (widget.editMessage == null ||
|
if (widget.editMessage == null ||
|
||||||
widget.editMessage.status == MessageSendingStatus.failed ||
|
widget.editMessage!.status == MessageSendingStatus.failed ||
|
||||||
widget.editMessage.status == MessageSendingStatus.sending) {
|
widget.editMessage!.status == MessageSendingStatus.sending) {
|
||||||
sendingFuture = channel.sendMessage(message);
|
sendingFuture = channel.sendMessage(message);
|
||||||
} else {
|
} else {
|
||||||
sendingFuture = channel.updateMessage(message);
|
sendingFuture = channel.updateMessage(message);
|
||||||
@@ -2099,12 +2096,12 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
_parseExistingMessage(message);
|
_parseExistingMessage(message);
|
||||||
}
|
}
|
||||||
if (widget.onMessageSent != null) {
|
if (widget.onMessageSent != null) {
|
||||||
widget.onMessageSent(resp.message);
|
widget.onMessageSent!(resp.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription _keyboardListener;
|
StreamSubscription? _keyboardListener;
|
||||||
|
|
||||||
void _showErrorAlert(String description) {
|
void _showErrorAlert(String description) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@@ -2178,14 +2175,12 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _parseExistingMessage(Message message) {
|
void _parseExistingMessage(Message message) {
|
||||||
textEditingController.text = message.text;
|
textEditingController.text = message.text!;
|
||||||
_messageIsPresent = true;
|
_messageIsPresent = true;
|
||||||
if (message.attachments != null) {
|
for (final attachment in message.attachments) {
|
||||||
for (final attachment in message.attachments) {
|
_attachments[attachment.id] = attachment.copyWith(
|
||||||
_attachments[attachment.id] = attachment.copyWith(
|
uploadState: attachment.uploadState,
|
||||||
uploadState: attachment.uploadState ?? UploadState.success(),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2266,12 +2261,12 @@ class _PickerWidget extends StatefulWidget {
|
|||||||
final void Function(AssetEntity) onMediaSelected;
|
final void Function(AssetEntity) onMediaSelected;
|
||||||
|
|
||||||
const _PickerWidget({
|
const _PickerWidget({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.filePickerIndex,
|
required this.filePickerIndex,
|
||||||
@required this.containsFile,
|
required this.containsFile,
|
||||||
@required this.selectedMedias,
|
required this.selectedMedias,
|
||||||
@required this.onAddMoreFilesClick,
|
required this.onAddMoreFilesClick,
|
||||||
@required this.onMediaSelected,
|
required this.onMediaSelected,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -2279,7 +2274,7 @@ class _PickerWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class __PickerWidgetState extends State<_PickerWidget> {
|
class __PickerWidgetState extends State<_PickerWidget> {
|
||||||
Future<bool> requestPermission;
|
Future<bool>? requestPermission;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -2299,7 +2294,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.data) {
|
if (snapshot.data!) {
|
||||||
if (widget.containsFile) {
|
if (widget.containsFile) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ typedef MessageBuilder = Widget Function(
|
|||||||
);
|
);
|
||||||
typedef ParentMessageBuilder = Widget Function(
|
typedef ParentMessageBuilder = Widget Function(
|
||||||
BuildContext,
|
BuildContext,
|
||||||
Message,
|
Message?,
|
||||||
);
|
);
|
||||||
typedef SystemMessageBuilder = Widget Function(
|
typedef SystemMessageBuilder = Widget Function(
|
||||||
BuildContext,
|
BuildContext,
|
||||||
Message,
|
Message,
|
||||||
);
|
);
|
||||||
typedef ThreadBuilder = Widget Function(BuildContext context, Message parent);
|
typedef ThreadBuilder = Widget Function(BuildContext context, Message? parent);
|
||||||
typedef ThreadTapCallback = void Function(Message, Widget);
|
typedef ThreadTapCallback = void Function(Message, Widget?);
|
||||||
|
|
||||||
typedef OnMessageSwiped = void Function(Message);
|
typedef OnMessageSwiped = void Function(Message);
|
||||||
typedef OnMessageTap = void Function(Message);
|
typedef OnMessageTap = void Function(Message);
|
||||||
@@ -41,13 +41,13 @@ typedef ReplyTapCallback = void Function(Message);
|
|||||||
|
|
||||||
class MessageDetails {
|
class MessageDetails {
|
||||||
/// True if the message belongs to the current user
|
/// True if the message belongs to the current user
|
||||||
bool isMyMessage;
|
bool? isMyMessage;
|
||||||
|
|
||||||
/// True if the user message is the same of the previous message
|
/// True if the user message is the same of the previous message
|
||||||
bool isLastUser;
|
bool? isLastUser;
|
||||||
|
|
||||||
/// True if the user message is the same of the next message
|
/// True if the user message is the same of the next message
|
||||||
bool isNextUser;
|
bool? isNextUser;
|
||||||
|
|
||||||
/// The message
|
/// The message
|
||||||
Message message;
|
Message message;
|
||||||
@@ -61,11 +61,11 @@ class MessageDetails {
|
|||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
this.index,
|
this.index,
|
||||||
) {
|
) {
|
||||||
isMyMessage = message.user.id == StreamChat.of(context).user.id;
|
isMyMessage = message.user?.id == StreamChat.of(context).user?.id;
|
||||||
isLastUser = index + 1 < messages.length &&
|
isLastUser = index + 1 < messages.length &&
|
||||||
message.user.id == messages[index + 1]?.user?.id;
|
message.user?.id == messages[index + 1].user?.id;
|
||||||
isNextUser =
|
isNextUser =
|
||||||
index - 1 >= 0 && message.user.id == messages[index - 1]?.user?.id;
|
index - 1 >= 0 && message.user!.id == messages[index - 1].user?.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class MessageDetails {
|
|||||||
class MessageListView extends StatefulWidget {
|
class MessageListView extends StatefulWidget {
|
||||||
/// Instantiate a new MessageListView
|
/// Instantiate a new MessageListView
|
||||||
MessageListView({
|
MessageListView({
|
||||||
Key key,
|
Key? key,
|
||||||
this.showScrollToBottom = true,
|
this.showScrollToBottom = true,
|
||||||
this.messageBuilder,
|
this.messageBuilder,
|
||||||
this.parentMessageBuilder,
|
this.parentMessageBuilder,
|
||||||
@@ -145,52 +145,52 @@ class MessageListView extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Function used to build a custom message widget
|
/// Function used to build a custom message widget
|
||||||
final MessageBuilder messageBuilder;
|
final MessageBuilder? messageBuilder;
|
||||||
|
|
||||||
/// Function used to build a custom system message widget
|
/// Function used to build a custom system message widget
|
||||||
final SystemMessageBuilder systemMessageBuilder;
|
final SystemMessageBuilder? systemMessageBuilder;
|
||||||
|
|
||||||
/// Function used to build a custom parent message widget
|
/// Function used to build a custom parent message widget
|
||||||
final ParentMessageBuilder parentMessageBuilder;
|
final ParentMessageBuilder? parentMessageBuilder;
|
||||||
|
|
||||||
/// Function used to build a custom thread widget
|
/// Function used to build a custom thread widget
|
||||||
final ThreadBuilder threadBuilder;
|
final ThreadBuilder? threadBuilder;
|
||||||
|
|
||||||
/// Function called when tapping on a thread
|
/// Function called when tapping on a thread
|
||||||
/// By default it calls [Navigator.push] using the widget built using [threadBuilder]
|
/// By default it calls [Navigator.push] using the widget built using [threadBuilder]
|
||||||
final ThreadTapCallback onThreadTap;
|
final ThreadTapCallback? onThreadTap;
|
||||||
|
|
||||||
/// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero
|
/// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero
|
||||||
final bool showScrollToBottom;
|
final bool showScrollToBottom;
|
||||||
|
|
||||||
/// Parent message in case of a thread
|
/// Parent message in case of a thread
|
||||||
final Message parentMessage;
|
final Message? parentMessage;
|
||||||
|
|
||||||
/// Builder used to render date dividers
|
/// Builder used to render date dividers
|
||||||
final Widget Function(DateTime) dateDividerBuilder;
|
final Widget Function(DateTime)? dateDividerBuilder;
|
||||||
|
|
||||||
/// Index of an item to initially align within the viewport.
|
/// Index of an item to initially align within the viewport.
|
||||||
final int initialScrollIndex;
|
final int? initialScrollIndex;
|
||||||
|
|
||||||
/// Determines where the leading edge of the item at [initialScrollIndex]
|
/// Determines where the leading edge of the item at [initialScrollIndex]
|
||||||
/// should be placed.
|
/// should be placed.
|
||||||
final double initialAlignment;
|
final double? initialAlignment;
|
||||||
|
|
||||||
/// Controller for jumping or scrolling to an item.
|
/// Controller for jumping or scrolling to an item.
|
||||||
final ItemScrollController scrollController;
|
final ItemScrollController? scrollController;
|
||||||
|
|
||||||
/// Provides a listenable iterable of [itemPositions] of items that are on
|
/// Provides a listenable iterable of [itemPositions] of items that are on
|
||||||
/// screen and their locations.
|
/// screen and their locations.
|
||||||
final ItemPositionsListener itemPositionListener;
|
final ItemPositionsListener? itemPositionListener;
|
||||||
|
|
||||||
/// The ScrollPhysics used by the ListView
|
/// The ScrollPhysics used by the ListView
|
||||||
final ScrollPhysics scrollPhysics;
|
final ScrollPhysics scrollPhysics;
|
||||||
|
|
||||||
/// Called when message item gets swiped
|
/// Called when message item gets swiped
|
||||||
final OnMessageSwiped onMessageSwiped;
|
final OnMessageSwiped? onMessageSwiped;
|
||||||
|
|
||||||
///
|
///
|
||||||
final ReplyTapCallback onReplyTap;
|
final ReplyTapCallback? onReplyTap;
|
||||||
|
|
||||||
/// If true the list will highlight the initialMessage if there is any.
|
/// If true the list will highlight the initialMessage if there is any.
|
||||||
///
|
///
|
||||||
@@ -198,64 +198,64 @@ class MessageListView extends StatefulWidget {
|
|||||||
final bool highlightInitialMessage;
|
final bool highlightInitialMessage;
|
||||||
|
|
||||||
/// Color used while highlighting initial message
|
/// Color used while highlighting initial message
|
||||||
final Color messageHighlightColor;
|
final Color? messageHighlightColor;
|
||||||
|
|
||||||
final ShowMessageCallback onShowMessage;
|
final ShowMessageCallback? onShowMessage;
|
||||||
|
|
||||||
final bool showConnectionStateTile;
|
final bool showConnectionStateTile;
|
||||||
|
|
||||||
/// Function called when messages are fetched
|
/// Function called when messages are fetched
|
||||||
final Widget Function(BuildContext, List<Message>) messageListBuilder;
|
final Widget Function(BuildContext, List<Message>)? messageListBuilder;
|
||||||
|
|
||||||
/// Function used to build a loading widget
|
/// Function used to build a loading widget
|
||||||
final WidgetBuilder loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
/// Function used to build an empty widget
|
/// Function used to build an empty widget
|
||||||
final WidgetBuilder emptyBuilder;
|
final WidgetBuilder? emptyBuilder;
|
||||||
|
|
||||||
/// Callback triggered when an error occurs while performing the given request.
|
/// Callback triggered when an error occurs while performing the given request.
|
||||||
/// This parameter can be used to display an error message to users in the event
|
/// This parameter can be used to display an error message to users in the event
|
||||||
/// of a connection failure.
|
/// of a connection failure.
|
||||||
final ErrorBuilder errorWidgetBuilder;
|
final ErrorBuilder? errorWidgetBuilder;
|
||||||
|
|
||||||
/// Predicate used to filter messages
|
/// Predicate used to filter messages
|
||||||
final bool Function(Message) messageFilter;
|
final bool Function(Message)? messageFilter;
|
||||||
|
|
||||||
/// Attachment builders for the default message widget
|
/// Attachment builders for the default message widget
|
||||||
/// Please change this in the [MessageWidget] if you are using a custom implementation
|
/// Please change this in the [MessageWidget] if you are using a custom implementation
|
||||||
final Map<String, AttachmentBuilder> customAttachmentBuilders;
|
final Map<String, AttachmentBuilder>? customAttachmentBuilders;
|
||||||
|
|
||||||
/// Called when any message is tapped except a system message (use [onSystemMessageTap] instead)
|
/// Called when any message is tapped except a system message (use [onSystemMessageTap] instead)
|
||||||
final OnMessageTap onMessageTap;
|
final OnMessageTap? onMessageTap;
|
||||||
|
|
||||||
/// Called when system message is tapped
|
/// Called when system message is tapped
|
||||||
final OnMessageTap onSystemMessageTap;
|
final OnMessageTap? onSystemMessageTap;
|
||||||
|
|
||||||
/// Customize onTap on attachment
|
/// Customize onTap on attachment
|
||||||
final void Function(Message message, Attachment attachment) onAttachmentTap;
|
final void Function(Message message, Attachment attachment)? onAttachmentTap;
|
||||||
|
|
||||||
/// Customize the MessageWidget textBuilder
|
/// Customize the MessageWidget textBuilder
|
||||||
final void Function(BuildContext context, Message message) textBuilder;
|
final void Function(BuildContext context, Message message)? textBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageListViewState createState() => _MessageListViewState();
|
_MessageListViewState createState() => _MessageListViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessageListViewState extends State<MessageListView> {
|
class _MessageListViewState extends State<MessageListView> {
|
||||||
ItemScrollController _scrollController;
|
ItemScrollController? _scrollController;
|
||||||
Function _onThreadTap;
|
Function? _onThreadTap;
|
||||||
bool _showScrollToBottom = false;
|
bool _showScrollToBottom = false;
|
||||||
ItemPositionsListener _itemPositionListener;
|
late final ItemPositionsListener _itemPositionListener;
|
||||||
int _messageListLength;
|
int? _messageListLength;
|
||||||
StreamChannelState streamChannel;
|
StreamChannelState? streamChannel;
|
||||||
|
|
||||||
int get _initialIndex {
|
int? get _initialIndex {
|
||||||
if (widget.initialScrollIndex != null) return widget.initialScrollIndex;
|
if (widget.initialScrollIndex != null) return widget.initialScrollIndex;
|
||||||
if (streamChannel.initialMessageId != null) {
|
if (streamChannel!.initialMessageId != null) {
|
||||||
final messages = streamChannel.channel.state.messages;
|
final messages = streamChannel!.channel.state!.messages;
|
||||||
final totalMessages = messages.length;
|
final totalMessages = messages.length;
|
||||||
final messageIndex = messages.indexWhere((e) {
|
final messageIndex = messages.indexWhere((e) {
|
||||||
return e.id == streamChannel.initialMessageId;
|
return e.id == streamChannel!.initialMessageId;
|
||||||
});
|
});
|
||||||
final index = totalMessages - messageIndex;
|
final index = totalMessages - messageIndex;
|
||||||
if (index != 0) return index - 1;
|
if (index != 0) return index - 1;
|
||||||
@@ -264,24 +264,24 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double get _initialAlignment {
|
double? get _initialAlignment {
|
||||||
if (widget.initialAlignment != null) return widget.initialAlignment;
|
if (widget.initialAlignment != null) return widget.initialAlignment;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isInitialMessage(String id) {
|
bool _isInitialMessage(String id) {
|
||||||
return streamChannel.initialMessageId == id;
|
return streamChannel!.initialMessageId == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _upToDate => streamChannel.channel.state.isUpToDate;
|
bool get _upToDate => streamChannel!.channel.state!.isUpToDate;
|
||||||
|
|
||||||
bool get _isThreadConversation => widget.parentMessage != null;
|
bool get _isThreadConversation => widget.parentMessage != null;
|
||||||
|
|
||||||
bool _topPaginationActive = false;
|
bool _topPaginationActive = false;
|
||||||
bool _bottomPaginationActive = false;
|
bool _bottomPaginationActive = false;
|
||||||
|
|
||||||
int initialIndex;
|
int? initialIndex;
|
||||||
double initialAlignment;
|
double? initialAlignment;
|
||||||
|
|
||||||
List<Message> messages = <Message>[];
|
List<Message> messages = <Message>[];
|
||||||
|
|
||||||
@@ -343,9 +343,9 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
|
|
||||||
if (_messageListLength != null) {
|
if (_messageListLength != null) {
|
||||||
if (_bottomPaginationActive || (_inBetweenList && _upToDate)) {
|
if (_bottomPaginationActive || (_inBetweenList && _upToDate)) {
|
||||||
if (_itemPositionListener.itemPositions.value?.isNotEmpty == true) {
|
if (_itemPositionListener.itemPositions.value.isNotEmpty == true) {
|
||||||
final first = _itemPositionListener.itemPositions.value.first;
|
final first = _itemPositionListener.itemPositions.value.first;
|
||||||
final diff = newMessagesListLength - _messageListLength;
|
final diff = newMessagesListLength - _messageListLength!;
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
initialIndex = first.index + diff;
|
initialIndex = first.index + diff;
|
||||||
initialAlignment = first.itemLeadingEdge;
|
initialAlignment = first.itemLeadingEdge;
|
||||||
@@ -413,7 +413,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
_inBetweenList = true;
|
_inBetweenList = true;
|
||||||
},
|
},
|
||||||
child: ScrollablePositionedList.separated(
|
child: ScrollablePositionedList.separated(
|
||||||
key: ValueKey(initialIndex + initialAlignment),
|
key: ValueKey(initialIndex! + initialAlignment!),
|
||||||
itemPositionsListener: _itemPositionListener,
|
itemPositionsListener: _itemPositionListener,
|
||||||
addAutomaticKeepAlives: true,
|
addAutomaticKeepAlives: true,
|
||||||
initialScrollIndex: initialIndex ?? 0,
|
initialScrollIndex: initialIndex ?? 0,
|
||||||
@@ -427,7 +427,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
if (i == messages.length) return Offstage();
|
if (i == messages.length) return Offstage();
|
||||||
if (i == 0) return SizedBox(height: 30);
|
if (i == 0) return SizedBox(height: 30);
|
||||||
if (i == messages.length + 1) {
|
if (i == messages.length + 1) {
|
||||||
final replyCount = widget.parentMessage.replyCount;
|
final replyCount = widget.parentMessage!.replyCount;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient:
|
gradient:
|
||||||
@@ -454,7 +454,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
Units.DAY,
|
Units.DAY,
|
||||||
)) {
|
)) {
|
||||||
final divider = widget.dateDividerBuilder != null
|
final divider = widget.dateDividerBuilder != null
|
||||||
? widget.dateDividerBuilder(
|
? widget.dateDividerBuilder!(
|
||||||
nextMessage.createdAt.toLocal(),
|
nextMessage.createdAt.toLocal(),
|
||||||
)
|
)
|
||||||
: DateDivider(
|
: DateDivider(
|
||||||
@@ -472,8 +472,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final isNextUserSame =
|
final isNextUserSame =
|
||||||
message.user.id == nextMessage.user?.id;
|
message.user!.id == nextMessage.user?.id;
|
||||||
final isThread = message.replyCount > 0;
|
final isThread = message.replyCount! > 0;
|
||||||
final isDeleted = message.isDeleted;
|
final isDeleted = message.isDeleted;
|
||||||
if (timeDiff >= 1 ||
|
if (timeDiff >= 1 ||
|
||||||
!isNextUserSame ||
|
!isNextUserSame ||
|
||||||
@@ -486,12 +486,12 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
if (i == messages.length + 2) {
|
if (i == messages.length + 2) {
|
||||||
if (widget.parentMessageBuilder != null) {
|
if (widget.parentMessageBuilder != null) {
|
||||||
return widget.parentMessageBuilder(
|
return widget.parentMessageBuilder!(
|
||||||
context,
|
context,
|
||||||
widget.parentMessage,
|
widget.parentMessage,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return buildParentMessage(widget.parentMessage);
|
return buildParentMessage(widget.parentMessage!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == messages.length + 1) {
|
if (i == messages.length + 1) {
|
||||||
@@ -528,7 +528,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
if (widget.messageBuilder != null) {
|
if (widget.messageBuilder != null) {
|
||||||
messageWidget = Builder(
|
messageWidget = Builder(
|
||||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||||
builder: (context) => widget.messageBuilder(
|
builder: (context) => widget.messageBuilder!(
|
||||||
context,
|
context,
|
||||||
MessageDetails(
|
MessageDetails(
|
||||||
context,
|
context,
|
||||||
@@ -555,7 +555,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
child: ValueListenableBuilder<Iterable<ItemPosition>>(
|
child: ValueListenableBuilder<Iterable<ItemPosition>>(
|
||||||
valueListenable: _itemPositionListener.itemPositions,
|
valueListenable: _itemPositionListener.itemPositions,
|
||||||
builder: (context, values, _) {
|
builder: (context, values, _) {
|
||||||
final items = _itemPositionListener.itemPositions?.value;
|
final items = _itemPositionListener.itemPositions.value;
|
||||||
if (items.isEmpty || messages.isEmpty) {
|
if (items.isEmpty || messages.isEmpty) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return widget.dateDividerBuilder != null
|
return widget.dateDividerBuilder != null
|
||||||
? widget.dateDividerBuilder(
|
? widget.dateDividerBuilder!(
|
||||||
messages[index].createdAt.toLocal(),
|
messages[index].createdAt.toLocal(),
|
||||||
)
|
)
|
||||||
: DateDivider(
|
: DateDivider(
|
||||||
@@ -585,8 +585,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _paginateData(
|
Future<void> _paginateData(
|
||||||
StreamChannelState channel, QueryDirection direction) {
|
StreamChannelState? channel, QueryDirection direction) {
|
||||||
return _messageListController.paginateData(direction: direction);
|
return _messageListController.paginateData!(direction: direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemPosition _getTopElement(Iterable<ItemPosition> values) {
|
ItemPosition _getTopElement(Iterable<ItemPosition> values) {
|
||||||
@@ -599,8 +599,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
Widget _buildScrollToBottom() {
|
Widget _buildScrollToBottom() {
|
||||||
return StreamBuilder<Tuple2<bool, int>>(
|
return StreamBuilder<Tuple2<bool, int>>(
|
||||||
stream: Rx.combineLatest2(
|
stream: Rx.combineLatest2(
|
||||||
streamChannel.channel.state.isUpToDateStream,
|
streamChannel!.channel.state!.isUpToDateStream,
|
||||||
streamChannel.channel.state.unreadCountStream,
|
streamChannel!.channel.state!.unreadCountStream,
|
||||||
(bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount),
|
(bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount),
|
||||||
),
|
),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
@@ -609,15 +609,15 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
} else if (!snapshot.hasData) {
|
} else if (!snapshot.hasData) {
|
||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
final isUpToDate = snapshot.data.item1;
|
final isUpToDate = snapshot.data!.item1;
|
||||||
final showScrollToBottom = !isUpToDate || _showScrollToBottom;
|
final showScrollToBottom = !isUpToDate || _showScrollToBottom;
|
||||||
if (!showScrollToBottom) {
|
if (!showScrollToBottom) {
|
||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
final unreadCount = snapshot.data.item2;
|
final unreadCount = snapshot.data!.item2;
|
||||||
final showUnreadCount = unreadCount > 0 &&
|
final showUnreadCount = unreadCount > 0 &&
|
||||||
streamChannel.channel.state.members.any(
|
streamChannel!.channel.state!.members.any((e) =>
|
||||||
(e) => e.userId == streamChannel.channel.client.state.user.id);
|
e.userId == streamChannel!.channel.client.state.user!.id);
|
||||||
return Positioned(
|
return Positioned(
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
right: 8,
|
right: 8,
|
||||||
@@ -630,15 +630,15 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (unreadCount > 0) {
|
if (unreadCount > 0) {
|
||||||
streamChannel.channel.markRead();
|
streamChannel!.channel.markRead();
|
||||||
}
|
}
|
||||||
if (!_upToDate) {
|
if (!_upToDate) {
|
||||||
_bottomPaginationActive = false;
|
_bottomPaginationActive = false;
|
||||||
_topPaginationActive = false;
|
_topPaginationActive = false;
|
||||||
streamChannel.reloadChannel();
|
streamChannel!.reloadChannel();
|
||||||
} else {
|
} else {
|
||||||
setState(() => _showScrollToBottom = false);
|
setState(() => _showScrollToBottom = false);
|
||||||
_scrollController.scrollTo(
|
_scrollController!.scrollTo(
|
||||||
index: 0,
|
index: 0,
|
||||||
duration: Duration(seconds: 1),
|
duration: Duration(seconds: 1),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
@@ -676,12 +676,12 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingIndicator(
|
Widget _buildLoadingIndicator(
|
||||||
StreamChannelState streamChannel,
|
StreamChannelState? streamChannel,
|
||||||
QueryDirection direction,
|
QueryDirection direction,
|
||||||
) {
|
) {
|
||||||
final stream = direction == QueryDirection.top
|
final stream = direction == QueryDirection.top
|
||||||
? streamChannel.queryTopMessages
|
? streamChannel!.queryTopMessages
|
||||||
: streamChannel.queryBottomMessages;
|
: streamChannel!.queryBottomMessages;
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
key: Key('LOADING-INDICATOR'),
|
key: Key('LOADING-INDICATOR'),
|
||||||
stream: stream,
|
stream: stream,
|
||||||
@@ -698,7 +698,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!snapshot.data) {
|
if (!snapshot.data!) {
|
||||||
if (!_isThreadConversation && direction == QueryDirection.top) {
|
if (!_isThreadConversation && direction == QueryDirection.top) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 52,
|
height: 52,
|
||||||
@@ -721,13 +721,13 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
Message message,
|
Message message,
|
||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
StreamChannelState streamChannel,
|
StreamChannelState? streamChannel,
|
||||||
) {
|
) {
|
||||||
Widget messageWidget;
|
Widget messageWidget;
|
||||||
if (widget.messageBuilder != null) {
|
if (widget.messageBuilder != null) {
|
||||||
messageWidget = Builder(
|
messageWidget = Builder(
|
||||||
key: ValueKey<String>('TOP-MESSAGE'),
|
key: ValueKey<String>('TOP-MESSAGE'),
|
||||||
builder: (_) => widget.messageBuilder(
|
builder: (_) => widget.messageBuilder!(
|
||||||
context,
|
context,
|
||||||
MessageDetails(
|
MessageDetails(
|
||||||
context,
|
context,
|
||||||
@@ -748,13 +748,13 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
Message message,
|
Message message,
|
||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
StreamChannelState streamChannel,
|
StreamChannelState? streamChannel,
|
||||||
) {
|
) {
|
||||||
Widget messageWidget;
|
Widget messageWidget;
|
||||||
if (widget.messageBuilder != null) {
|
if (widget.messageBuilder != null) {
|
||||||
messageWidget = Builder(
|
messageWidget = Builder(
|
||||||
key: ValueKey<String>('BOTTOM-MESSAGE-${message.id}'),
|
key: ValueKey<String>('BOTTOM-MESSAGE-${message.id}'),
|
||||||
builder: (_) => widget.messageBuilder(
|
builder: (_) => widget.messageBuilder!(
|
||||||
context,
|
context,
|
||||||
MessageDetails(
|
MessageDetails(
|
||||||
context,
|
context,
|
||||||
@@ -774,10 +774,10 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
onVisibilityChanged: (visibility) {
|
onVisibilityChanged: (visibility) {
|
||||||
final isVisible = visibility.visibleBounds != Rect.zero;
|
final isVisible = visibility.visibleBounds != Rect.zero;
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
final channel = streamChannel.channel;
|
final channel = streamChannel!.channel;
|
||||||
if (_upToDate &&
|
if (_upToDate &&
|
||||||
channel.config?.readEvents == true &&
|
channel.config?.readEvents == true &&
|
||||||
channel.state.unreadCount > 0) {
|
channel.state!.unreadCount! > 0) {
|
||||||
streamChannel.channel.markRead();
|
streamChannel.channel.markRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -792,8 +792,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
Widget buildParentMessage(
|
Widget buildParentMessage(
|
||||||
Message message,
|
Message message,
|
||||||
) {
|
) {
|
||||||
final isMyMessage = message.user.id == StreamChat.of(context).user.id;
|
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
|
||||||
final isOnlyEmoji = message.text.isOnlyEmoji;
|
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||||
|
|
||||||
return MessageWidget(
|
return MessageWidget(
|
||||||
showThreadReplyIndicator: false,
|
showThreadReplyIndicator: false,
|
||||||
@@ -809,7 +809,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
showUsername: !isMyMessage,
|
showUsername: !isMyMessage,
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
showSendingIndicator: false,
|
showSendingIndicator: false,
|
||||||
onThreadTap: _onThreadTap,
|
onThreadTap: _onThreadTap as void Function(Message)?,
|
||||||
borderRadiusGeometry: BorderRadius.only(
|
borderRadiusGeometry: BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
bottomLeft: Radius.circular(2),
|
bottomLeft: Radius.circular(2),
|
||||||
@@ -832,18 +832,19 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
break;
|
break;
|
||||||
case ReturnActionType.reply:
|
case ReturnActionType.reply:
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
widget.onMessageSwiped(message);
|
widget.onMessageSwiped!(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
customAttachmentBuilders: widget.customAttachmentBuilders,
|
customAttachmentBuilders: widget.customAttachmentBuilders,
|
||||||
onMessageTap: (message) {
|
onMessageTap: (message) {
|
||||||
if (widget.onMessageTap != null) {
|
if (widget.onMessageTap != null) {
|
||||||
widget.onMessageTap(message);
|
widget.onMessageTap!(message);
|
||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
textBuilder: widget.textBuilder,
|
textBuilder:
|
||||||
|
widget.textBuilder as Widget Function(BuildContext, Message)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,18 +861,18 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
message: message,
|
message: message,
|
||||||
onMessageTap: (message) {
|
onMessageTap: (message) {
|
||||||
if (widget.onSystemMessageTap != null) {
|
if (widget.onSystemMessageTap != null) {
|
||||||
widget.onSystemMessageTap(message);
|
widget.onSystemMessageTap!(message);
|
||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final userId = StreamChat.of(context).user.id;
|
final userId = StreamChat.of(context).user!.id;
|
||||||
final isMyMessage = message.user.id == userId;
|
final isMyMessage = message.user!.id == userId;
|
||||||
final nextMessage = index - 2 >= 0 ? messages[index - 2] : null;
|
final nextMessage = index - 2 >= 0 ? messages[index - 2] : null;
|
||||||
final isNextUserSame =
|
final isNextUserSame =
|
||||||
nextMessage != null && message.user.id == nextMessage.user.id;
|
nextMessage != null && message.user!.id == nextMessage.user!.id;
|
||||||
|
|
||||||
num timeDiff = 0;
|
num timeDiff = 0;
|
||||||
if (nextMessage != null) {
|
if (nextMessage != null) {
|
||||||
@@ -881,27 +882,26 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final channel = streamChannel.channel;
|
final channel = streamChannel!.channel;
|
||||||
final readList = channel.state?.read?.where((read) {
|
final readList = channel.state?.read?.where((read) {
|
||||||
if (read.user.id == userId) return false;
|
if (read.user.id == userId) return false;
|
||||||
return (read.lastRead.isAfter(message.createdAt) ||
|
return (read.lastRead.isAfter(message.createdAt) ||
|
||||||
read.lastRead.isAtSameMomentAs(message.createdAt));
|
read.lastRead.isAtSameMomentAs(message.createdAt));
|
||||||
})?.toList() ??
|
}).toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
final allRead = readList.length >= (channel.memberCount ?? 0) - 1;
|
final allRead = readList.length >= (channel.memberCount ?? 0) - 1;
|
||||||
final hasFileAttachment =
|
final hasFileAttachment =
|
||||||
message.attachments?.any((it) => it.type == 'file') == true;
|
message.attachments.any((it) => it.type == 'file') == true;
|
||||||
|
|
||||||
final isThreadMessage =
|
final isThreadMessage =
|
||||||
message?.parentId != null && message?.showInChannel == true;
|
message.parentId != null && message.showInChannel == true;
|
||||||
|
|
||||||
final hasReplies = message.replyCount > 0;
|
final hasReplies = message.replyCount! > 0;
|
||||||
|
|
||||||
final attachmentBorderRadius = hasFileAttachment ? 12.0 : 14.0;
|
final attachmentBorderRadius = hasFileAttachment ? 12.0 : 14.0;
|
||||||
|
|
||||||
final showTimeStamp = message.createdAt != null &&
|
final showTimeStamp = (!isThreadMessage || _isThreadConversation) &&
|
||||||
(!isThreadMessage || _isThreadConversation) &&
|
|
||||||
!hasReplies &&
|
!hasReplies &&
|
||||||
(timeDiff >= 1 || !isNextUserSame);
|
(timeDiff >= 1 || !isNextUserSame);
|
||||||
|
|
||||||
@@ -921,10 +921,10 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
|
|
||||||
final showInChannelIndicator = !_isThreadConversation && isThreadMessage;
|
final showInChannelIndicator = !_isThreadConversation && isThreadMessage;
|
||||||
final showThreadReplyIndicator = !_isThreadConversation && hasReplies;
|
final showThreadReplyIndicator = !_isThreadConversation && hasReplies;
|
||||||
final isOnlyEmoji = message.text.isOnlyEmoji;
|
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||||
|
|
||||||
final hasUrlAttachment =
|
final hasUrlAttachment =
|
||||||
message.attachments?.any((it) => it.ogScrapeUrl != null) == true;
|
message.attachments.any((it) => it.ogScrapeUrl != null) == true;
|
||||||
|
|
||||||
final borderSide =
|
final borderSide =
|
||||||
isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment)
|
isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment)
|
||||||
@@ -954,8 +954,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
} else {
|
} else {
|
||||||
await streamChannel.loadChannelAtMessage(quotedMessageId).then((_) {
|
await streamChannel!.loadChannelAtMessage(quotedMessageId).then((_) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
}
|
}
|
||||||
@@ -968,7 +968,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
showThreadReplyMessage: !isThreadMessage,
|
showThreadReplyMessage: !isThreadMessage,
|
||||||
showFlagButton: !isMyMessage,
|
showFlagButton: !isMyMessage,
|
||||||
borderSide: borderSide,
|
borderSide: borderSide,
|
||||||
onThreadTap: _onThreadTap,
|
onThreadTap: _onThreadTap as void Function(Message)?,
|
||||||
onReplyTap: widget.onReplyTap,
|
onReplyTap: widget.onReplyTap,
|
||||||
attachmentBorderRadiusGeometry: BorderRadius.only(
|
attachmentBorderRadiusGeometry: BorderRadius.only(
|
||||||
topLeft: Radius.circular(attachmentBorderRadius),
|
topLeft: Radius.circular(attachmentBorderRadius),
|
||||||
@@ -1008,19 +1008,20 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
break;
|
break;
|
||||||
case ReturnActionType.reply:
|
case ReturnActionType.reply:
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
widget.onMessageSwiped(message);
|
widget.onMessageSwiped!(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
customAttachmentBuilders: widget.customAttachmentBuilders,
|
customAttachmentBuilders: widget.customAttachmentBuilders,
|
||||||
onMessageTap: (message) {
|
onMessageTap: (message) {
|
||||||
if (widget.onMessageTap != null) {
|
if (widget.onMessageTap != null) {
|
||||||
widget.onMessageTap(message);
|
widget.onMessageTap!(message);
|
||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
onAttachmentTap: widget.onAttachmentTap,
|
onAttachmentTap: widget.onAttachmentTap,
|
||||||
textBuilder: widget.textBuilder,
|
textBuilder:
|
||||||
|
widget.textBuilder as Widget Function(BuildContext, Message)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!message.isDeleted &&
|
if (!message.isDeleted &&
|
||||||
@@ -1033,7 +1034,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
child: Swipeable(
|
child: Swipeable(
|
||||||
onSwipeEnd: () {
|
onSwipeEnd: () {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
widget.onMessageSwiped(message);
|
widget.onMessageSwiped!(message);
|
||||||
},
|
},
|
||||||
backgroundIcon: StreamSvgIcon.reply(
|
backgroundIcon: StreamSvgIcon.reply(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
@@ -1049,7 +1050,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||||
final highlightColor =
|
final highlightColor =
|
||||||
widget.messageHighlightColor ?? colorTheme.highlight;
|
widget.messageHighlightColor ?? colorTheme.highlight;
|
||||||
child = TweenAnimationBuilder<Color>(
|
child = TweenAnimationBuilder<Color?>(
|
||||||
tween: ColorTween(
|
tween: ColorTween(
|
||||||
begin: highlightColor,
|
begin: highlightColor,
|
||||||
end: colorTheme.white.withOpacity(0),
|
end: colorTheme.white.withOpacity(0),
|
||||||
@@ -1071,7 +1072,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription _messageNewListener;
|
StreamSubscription? _messageNewListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -1085,13 +1086,14 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
initialAlignment = _initialAlignment;
|
initialAlignment = _initialAlignment;
|
||||||
|
|
||||||
_messageNewListener =
|
_messageNewListener =
|
||||||
streamChannel.channel.on(EventType.messageNew).listen((event) {
|
streamChannel!.channel.on(EventType.messageNew).listen((event) {
|
||||||
if (_upToDate) {
|
if (_upToDate) {
|
||||||
_bottomPaginationActive = false;
|
_bottomPaginationActive = false;
|
||||||
_topPaginationActive = false;
|
_topPaginationActive = false;
|
||||||
}
|
}
|
||||||
if (event.message.user.id == streamChannel.channel.client.state.user.id) {
|
if (event.message!.user!.id ==
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
streamChannel!.channel.client.state.user!.id) {
|
||||||
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
_scrollController?.jumpTo(
|
_scrollController?.jumpTo(
|
||||||
index: 0,
|
index: 0,
|
||||||
);
|
);
|
||||||
@@ -1100,7 +1102,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (_isThreadConversation) {
|
if (_isThreadConversation) {
|
||||||
streamChannel.getReplies(widget.parentMessage.id);
|
streamChannel!.getReplies(widget.parentMessage!.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getOnThreadTap();
|
_getOnThreadTap();
|
||||||
@@ -1110,10 +1112,10 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
void _getOnThreadTap() {
|
void _getOnThreadTap() {
|
||||||
if (widget.onThreadTap != null) {
|
if (widget.onThreadTap != null) {
|
||||||
_onThreadTap = (Message message) {
|
_onThreadTap = (Message message) {
|
||||||
widget.onThreadTap(
|
widget.onThreadTap!(
|
||||||
message,
|
message,
|
||||||
widget.threadBuilder != null
|
widget.threadBuilder != null
|
||||||
? widget.threadBuilder(context, message)
|
? widget.threadBuilder!(context, message)
|
||||||
: null);
|
: null);
|
||||||
};
|
};
|
||||||
} else if (widget.threadBuilder != null) {
|
} else if (widget.threadBuilder != null) {
|
||||||
@@ -1122,14 +1124,14 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (_) {
|
MaterialPageRoute(builder: (_) {
|
||||||
return StreamBuilder<Message>(
|
return StreamBuilder<Message>(
|
||||||
stream: streamChannel.channel.state.messagesStream.map(
|
stream: streamChannel!.channel.state!.messagesStream.map(
|
||||||
(messages) =>
|
(messages) =>
|
||||||
messages.firstWhere((m) => m.id == message.id)),
|
messages!.firstWhere((m) => m.id == message.id)),
|
||||||
initialData: message,
|
initialData: message,
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
return StreamChannel(
|
return StreamChannel(
|
||||||
channel: streamChannel.channel,
|
channel: streamChannel!.channel,
|
||||||
child: widget.threadBuilder(context, snapshot.data),
|
child: widget.threadBuilder!(context, snapshot.data),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -1141,7 +1143,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (!_upToDate) {
|
if (!_upToDate) {
|
||||||
streamChannel.reloadChannel();
|
streamChannel!.reloadChannel();
|
||||||
}
|
}
|
||||||
_messageNewListener?.cancel();
|
_messageNewListener?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ import 'stream_chat_theme.dart';
|
|||||||
|
|
||||||
class MessageReactionsModal extends StatelessWidget {
|
class MessageReactionsModal extends StatelessWidget {
|
||||||
final Message message;
|
final Message message;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
final bool showReactions;
|
final bool showReactions;
|
||||||
final DisplayWidget showUserAvatar;
|
final DisplayWidget showUserAvatar;
|
||||||
final ShapeBorder messageShape;
|
final ShapeBorder? messageShape;
|
||||||
final ShapeBorder attachmentShape;
|
final ShapeBorder? attachmentShape;
|
||||||
final void Function(User) onUserAvatarTap;
|
final void Function(User)? onUserAvatarTap;
|
||||||
final BorderRadius attachmentBorderRadiusGeometry;
|
final BorderRadius? attachmentBorderRadiusGeometry;
|
||||||
|
|
||||||
const MessageReactionsModal({
|
const MessageReactionsModal({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.showReactions = true,
|
this.showReactions = true,
|
||||||
this.messageShape,
|
this.messageShape,
|
||||||
this.attachmentShape,
|
this.attachmentShape,
|
||||||
@@ -42,10 +42,10 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
final user = StreamChat.of(context).user;
|
final user = StreamChat.of(context).user;
|
||||||
|
|
||||||
final roughMaxSize = 2 * size.width / 3;
|
final roughMaxSize = 2 * size.width / 3;
|
||||||
var messageTextLength = message.text.length;
|
var messageTextLength = message.text!.length;
|
||||||
if (message.quotedMessage != null) {
|
if (message.quotedMessage != null) {
|
||||||
var quotedMessageLength = message.quotedMessage.text.length + 40;
|
var quotedMessageLength = message.quotedMessage!.text!.length + 40;
|
||||||
if (message.quotedMessage.attachments?.isNotEmpty == true) {
|
if (message.quotedMessage!.attachments.isNotEmpty == true) {
|
||||||
quotedMessageLength += 40;
|
quotedMessageLength += 40;
|
||||||
}
|
}
|
||||||
if (quotedMessageLength > messageTextLength) {
|
if (quotedMessageLength > messageTextLength) {
|
||||||
@@ -53,8 +53,8 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
final roughSentenceSize =
|
final roughSentenceSize =
|
||||||
messageTextLength * messageTheme.messageText.fontSize * 1.2;
|
messageTextLength * (messageTheme?.messageText?.fontSize ?? 1) * 1.2;
|
||||||
final divFactor = message.attachments?.isNotEmpty == true
|
final divFactor = message.attachments.isNotEmpty == true
|
||||||
? 1
|
? 1
|
||||||
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
|
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
curve: Curves.easeInOutBack,
|
curve: Curves.easeInOutBack,
|
||||||
builder: (context, val, snapshot) {
|
builder: (context, val, snapshot) {
|
||||||
final hasFileAttachment =
|
final hasFileAttachment =
|
||||||
message.attachments?.any((it) => it.type == 'file') == true;
|
message.attachments.any((it) => it.type == 'file') == true;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => Navigator.maybePop(context),
|
onTap: () => Navigator.maybePop(context),
|
||||||
@@ -92,11 +92,10 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (showReactions &&
|
if (showReactions &&
|
||||||
(message.status == MessageSendingStatus.sent ||
|
(message.status == MessageSendingStatus.sent))
|
||||||
message.status == null))
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment(
|
alignment: Alignment(
|
||||||
user.id == message.user.id
|
user!.id == message.user!.id
|
||||||
? (divFactor > 1.0
|
? (divFactor > 1.0
|
||||||
? 0.0
|
? 0.0
|
||||||
: (1.0 - divFactor))
|
: (1.0 - divFactor))
|
||||||
@@ -115,8 +114,8 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
key: Key('MessageWidget'),
|
key: Key('MessageWidget'),
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
message: message.copyWith(
|
message: message.copyWith(
|
||||||
text: message.text.length > 200
|
text: message.text!.length > 200
|
||||||
? '${message.text.substring(0, 200)}...'
|
? '${message.text!.substring(0, 200)}...'
|
||||||
: message.text,
|
: message.text,
|
||||||
),
|
),
|
||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
@@ -138,12 +137,11 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
showInChannelIndicator: false,
|
showInChannelIndicator: false,
|
||||||
textPadding: EdgeInsets.symmetric(
|
textPadding: EdgeInsets.symmetric(
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
horizontal: message.text.isOnlyEmoji ? 0 : 16.0,
|
horizontal:
|
||||||
|
message.text!.isOnlyEmoji ? 0 : 16.0,
|
||||||
),
|
),
|
||||||
showReactionPickerIndicator: showReactions &&
|
showReactionPickerIndicator: showReactions &&
|
||||||
(message.status ==
|
(message.status == MessageSendingStatus.sent),
|
||||||
MessageSendingStatus.sent ||
|
|
||||||
message.status == null),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (message.latestReactions?.isNotEmpty == true) ...[
|
if (message.latestReactions?.isNotEmpty == true) ...[
|
||||||
@@ -188,10 +186,10 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
spacing: 16,
|
spacing: 16,
|
||||||
runSpacing: 16,
|
runSpacing: 16,
|
||||||
alignment: WrapAlignment.start,
|
alignment: WrapAlignment.start,
|
||||||
children: message.latestReactions
|
children: message.latestReactions!
|
||||||
.map((e) => _buildReaction(
|
.map((e) => _buildReaction(
|
||||||
e,
|
e,
|
||||||
currentUser,
|
currentUser!,
|
||||||
context,
|
context,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
@@ -209,7 +207,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
User currentUser,
|
User currentUser,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final isCurrentUser = reaction.user.id == currentUser.id;
|
final isCurrentUser = reaction.user?.id == currentUser.id;
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints.loose(Size(
|
constraints: BoxConstraints.loose(Size(
|
||||||
64,
|
64,
|
||||||
@@ -225,7 +223,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
onTap: onUserAvatarTap,
|
onTap: onUserAvatarTap,
|
||||||
user: reaction.user,
|
user: reaction.user!,
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
height: 64,
|
height: 64,
|
||||||
width: 64,
|
width: 64,
|
||||||
@@ -246,8 +244,10 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
child: ReactionBubble(
|
child: ReactionBubble(
|
||||||
reactions: [reaction],
|
reactions: [reaction],
|
||||||
flipTail: !reverse,
|
flipTail: !reverse,
|
||||||
borderColor: messageTheme.reactionsBorderColor,
|
borderColor: messageTheme?.reactionsBorderColor ??
|
||||||
backgroundColor: messageTheme.reactionsBackgroundColor,
|
Colors.transparent,
|
||||||
|
backgroundColor: messageTheme?.reactionsBackgroundColor ??
|
||||||
|
Colors.transparent,
|
||||||
maskColor: StreamChatTheme.of(context).colorTheme.white,
|
maskColor: StreamChatTheme.of(context).colorTheme.white,
|
||||||
tailCirclesSpacing: 1,
|
tailCirclesSpacing: 1,
|
||||||
highlightOwnReactions: false,
|
highlightOwnReactions: false,
|
||||||
@@ -258,7 +258,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
reaction.user.name.split(' ')[0],
|
reaction.user!.name.split(' ')[0],
|
||||||
style: StreamChatTheme.of(context).textTheme.footnoteBold,
|
style: StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
class MessageSearchItem extends StatelessWidget {
|
class MessageSearchItem extends StatelessWidget {
|
||||||
/// Instantiate a new MessageSearchItem
|
/// Instantiate a new MessageSearchItem
|
||||||
const MessageSearchItem({
|
const MessageSearchItem({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.getMessageResponse,
|
required this.getMessageResponse,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.showOnlineStatus = true,
|
this.showOnlineStatus = true,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -22,7 +22,7 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
final GetMessageResponse getMessageResponse;
|
final GetMessageResponse getMessageResponse;
|
||||||
|
|
||||||
/// Function called when tapping this widget
|
/// Function called when tapping this widget
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
/// If true the [MessageSearchItem] will show the current online Status
|
/// If true the [MessageSearchItem] will show the current online Status
|
||||||
final bool showOnlineStatus;
|
final bool showOnlineStatus;
|
||||||
@@ -31,8 +31,8 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final message = getMessageResponse.message;
|
final message = getMessageResponse.message;
|
||||||
final channel = getMessageResponse.channel;
|
final channel = getMessageResponse.channel;
|
||||||
final channelName = channel.extraData['name'];
|
final channelName = channel?.extraData['name'];
|
||||||
final user = message.user;
|
final user = message.user!;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
leading: UserAvatar(
|
leading: UserAvatar(
|
||||||
@@ -46,7 +46,7 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
user.id == StreamChat.of(context).user.id ? 'You' : user.name,
|
user.id == StreamChat.of(context).user?.id ? 'You' : user.name,
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
||||||
),
|
),
|
||||||
if (channelName != null) ...[
|
if (channelName != null) ...[
|
||||||
@@ -55,12 +55,12 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.title
|
.title
|
||||||
.copyWith(
|
?.copyWith(
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
channelName,
|
channelName as String,
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -96,14 +96,10 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSubtitle(BuildContext context, Message message) {
|
Widget _buildSubtitle(BuildContext context, Message message) {
|
||||||
if (message == null) {
|
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = message.text;
|
var text = message.text;
|
||||||
if (message.isDeleted) {
|
if (message.isDeleted) {
|
||||||
text = 'This message was deleted.';
|
text = 'This message was deleted.';
|
||||||
} else if (message.attachments != null) {
|
} else if (message.attachments.isNotEmpty) {
|
||||||
final parts = <String>[
|
final parts = <String>[
|
||||||
...message.attachments.map((e) {
|
...message.attachments.map((e) {
|
||||||
if (e.type == 'image') {
|
if (e.type == 'image') {
|
||||||
@@ -116,7 +112,7 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
return e == message.attachments.last
|
return e == message.attachments.last
|
||||||
? (e.title ?? 'File')
|
? (e.title ?? 'File')
|
||||||
: '${e.title ?? 'File'} , ';
|
: '${e.title ?? 'File'} , ';
|
||||||
}).where((e) => e != null),
|
}),
|
||||||
message.text ?? '',
|
message.text ?? '',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -125,15 +121,15 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
|
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
_getDisplayText(
|
_getDisplayText(
|
||||||
text,
|
text!,
|
||||||
message.mentionedUsers,
|
message.mentionedUsers,
|
||||||
message.attachments,
|
message.attachments,
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||||
fontStyle: (message.isSystem || message.isDeleted)
|
fontStyle: (message.isSystem || message.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal,
|
: FontStyle.normal,
|
||||||
),
|
),
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||||
fontStyle: (message.isSystem || message.isDeleted)
|
fontStyle: (message.isSystem || message.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal,
|
: FontStyle.normal,
|
||||||
@@ -149,26 +145,24 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
String text,
|
String text,
|
||||||
List<User> mentions,
|
List<User> mentions,
|
||||||
List<Attachment> attachments,
|
List<Attachment> attachments,
|
||||||
TextStyle normalTextStyle,
|
TextStyle? normalTextStyle,
|
||||||
TextStyle mentionsTextStyle) {
|
TextStyle? mentionsTextStyle) {
|
||||||
var textList = text.split(' ');
|
var textList = text.split(' ');
|
||||||
var resList = <TextSpan>[];
|
var resList = <TextSpan>[];
|
||||||
for (var e in textList) {
|
for (var e in textList) {
|
||||||
if (mentions != null &&
|
if (mentions.isNotEmpty &&
|
||||||
mentions.isNotEmpty &&
|
|
||||||
mentions.any((element) => '@${element.name}' == e)) {
|
mentions.any((element) => '@${element.name}' == e)) {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
text: '$e ',
|
text: '$e ',
|
||||||
style: mentionsTextStyle,
|
style: mentionsTextStyle,
|
||||||
));
|
));
|
||||||
} else if (attachments != null &&
|
} else if (attachments.isNotEmpty &&
|
||||||
attachments.isNotEmpty &&
|
|
||||||
attachments
|
attachments
|
||||||
.where((e) => e.title != null)
|
.where((e) => e.title != null)
|
||||||
.any((element) => element.title == e)) {
|
.any((element) => element.title == e)) {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
text: '$e ',
|
text: '$e ',
|
||||||
style: normalTextStyle.copyWith(fontStyle: FontStyle.italic),
|
style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
resList.add(TextSpan(
|
resList.add(TextSpan(
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
|||||||
|
|
||||||
/// Builder used to create a custom [ListUserItem] from a [User]
|
/// Builder used to create a custom [ListUserItem] from a [User]
|
||||||
typedef MessageSearchItemBuilder = Widget Function(
|
typedef MessageSearchItemBuilder = Widget Function(
|
||||||
BuildContext, GetMessageResponse);
|
BuildContext,
|
||||||
|
GetMessageResponse,
|
||||||
|
);
|
||||||
|
|
||||||
/// Builder used when [MessageSearchListView] is empty
|
/// Builder used when [MessageSearchListView] is empty
|
||||||
typedef EmptyMessageSearchBuilder = Widget Function(
|
typedef EmptyMessageSearchBuilder = Widget Function(
|
||||||
BuildContext context, String searchQuery);
|
BuildContext context,
|
||||||
|
String searchQuery,
|
||||||
|
);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// It shows the list of searched messages.
|
/// It shows the list of searched messages.
|
||||||
@@ -47,9 +51,9 @@ typedef EmptyMessageSearchBuilder = Widget Function(
|
|||||||
class MessageSearchListView extends StatefulWidget {
|
class MessageSearchListView extends StatefulWidget {
|
||||||
/// Instantiate a new MessageSearchListView
|
/// Instantiate a new MessageSearchListView
|
||||||
const MessageSearchListView({
|
const MessageSearchListView({
|
||||||
Key key,
|
Key? key,
|
||||||
|
required this.filters,
|
||||||
this.messageQuery,
|
this.messageQuery,
|
||||||
this.filters,
|
|
||||||
this.sortOptions,
|
this.sortOptions,
|
||||||
this.paginationParams,
|
this.paginationParams,
|
||||||
this.messageFilters,
|
this.messageFilters,
|
||||||
@@ -66,7 +70,7 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Message String to search on
|
/// Message String to search on
|
||||||
final String messageQuery;
|
final String? messageQuery;
|
||||||
|
|
||||||
/// The query filters to use.
|
/// The query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
@@ -77,27 +81,27 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption> sortOptions;
|
final List<SortOption>? sortOptions;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of users to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams paginationParams;
|
final PaginationParams? paginationParams;
|
||||||
|
|
||||||
/// The message query filters to use.
|
/// The message query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
/// You can also filter other built-in channel fields.
|
/// You can also filter other built-in channel fields.
|
||||||
final Map<String, dynamic> messageFilters;
|
final Map<String, dynamic>? messageFilters;
|
||||||
|
|
||||||
/// Builder used to create a custom item preview
|
/// Builder used to create a custom item preview
|
||||||
final MessageSearchItemBuilder itemBuilder;
|
final MessageSearchItemBuilder? itemBuilder;
|
||||||
|
|
||||||
/// Function called when tapping on a [MessageSearchItem]
|
/// Function called when tapping on a [MessageSearchItem]
|
||||||
final MessageSearchItemTapCallback onItemTap;
|
final MessageSearchItemTapCallback? onItemTap;
|
||||||
|
|
||||||
/// Builder used to create a custom item separator
|
/// Builder used to create a custom item separator
|
||||||
final IndexedWidgetBuilder separatorBuilder;
|
final IndexedWidgetBuilder? separatorBuilder;
|
||||||
|
|
||||||
/// Set it to false to hide total results text
|
/// Set it to false to hide total results text
|
||||||
final bool showResultCount;
|
final bool showResultCount;
|
||||||
@@ -108,16 +112,16 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
final bool showErrorTile;
|
final bool showErrorTile;
|
||||||
|
|
||||||
/// The builder that is used when the search messages are fetched
|
/// The builder that is used when the search messages are fetched
|
||||||
final Widget Function(List<GetMessageResponse>) childBuilder;
|
final Widget Function(List<GetMessageResponse>)? childBuilder;
|
||||||
|
|
||||||
/// The builder used when the channel list is empty.
|
/// The builder used when the channel list is empty.
|
||||||
final WidgetBuilder emptyBuilder;
|
final WidgetBuilder? emptyBuilder;
|
||||||
|
|
||||||
/// The builder that will be used in case of error
|
/// The builder that will be used in case of error
|
||||||
final ErrorBuilder errorBuilder;
|
final ErrorBuilder? errorBuilder;
|
||||||
|
|
||||||
/// The builder that will be used in case of loading
|
/// The builder that will be used in case of loading
|
||||||
final WidgetBuilder loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageSearchListViewState createState() => _MessageSearchListViewState();
|
_MessageSearchListViewState createState() => _MessageSearchListViewState();
|
||||||
@@ -202,11 +206,11 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
Widget _listItemBuilder(
|
Widget _listItemBuilder(
|
||||||
BuildContext context, GetMessageResponse getMessageResponse) {
|
BuildContext context, GetMessageResponse getMessageResponse) {
|
||||||
if (widget.itemBuilder != null) {
|
if (widget.itemBuilder != null) {
|
||||||
return widget.itemBuilder(context, getMessageResponse);
|
return widget.itemBuilder!(context, getMessageResponse);
|
||||||
}
|
}
|
||||||
return MessageSearchItem(
|
return MessageSearchItem(
|
||||||
getMessageResponse: getMessageResponse,
|
getMessageResponse: getMessageResponse,
|
||||||
onTap: () => widget.onItemTap(getMessageResponse),
|
onTap: () => widget.onItemTap!(getMessageResponse),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +239,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
height: 100,
|
height: 100,
|
||||||
padding: EdgeInsets.all(32),
|
padding: EdgeInsets.all(32),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: snapshot.data ? CircularProgressIndicator() : Container(),
|
child: snapshot.data! ? CircularProgressIndicator() : Container(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -249,7 +253,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
separatorBuilder: (_, index) {
|
separatorBuilder: (_, index) {
|
||||||
if (widget.separatorBuilder != null) {
|
if (widget.separatorBuilder != null) {
|
||||||
return widget.separatorBuilder(context, index);
|
return widget.separatorBuilder!(context, index);
|
||||||
}
|
}
|
||||||
return _separatorBuilder(context, index);
|
return _separatorBuilder(context, index);
|
||||||
},
|
},
|
||||||
@@ -262,13 +266,13 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
);
|
);
|
||||||
if (widget.pullToRefresh) {
|
if (widget.pullToRefresh) {
|
||||||
child = RefreshIndicator(
|
child = RefreshIndicator(
|
||||||
onRefresh: () => _messageSearchListController.loadData(),
|
onRefresh: () => _messageSearchListController.loadData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
child = LazyLoadScrollView(
|
child = LazyLoadScrollView(
|
||||||
onEndOfPage: () => _messageSearchListController.paginateData(),
|
onEndOfPage: () => _messageSearchListController.paginateData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
@@ -7,43 +8,45 @@ import 'utils.dart';
|
|||||||
|
|
||||||
class MessageText extends StatelessWidget {
|
class MessageText extends StatelessWidget {
|
||||||
const MessageText({
|
const MessageText({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.onMentionTap,
|
this.onMentionTap,
|
||||||
this.onLinkTap,
|
this.onLinkTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Message message;
|
final Message message;
|
||||||
final void Function(User) onMentionTap;
|
final void Function(User)? onMentionTap;
|
||||||
final void Function(String) onLinkTap;
|
final void Function(String)? onLinkTap;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final text = _replaceMentions(message.text).replaceAll('\n', '\\\n');
|
final text = _replaceMentions(message.text)!.replaceAll('\n', '\\\n');
|
||||||
|
|
||||||
return MarkdownBody(
|
return MarkdownBody(
|
||||||
data: text,
|
data: text,
|
||||||
onTapLink: (
|
onTapLink: (
|
||||||
String link,
|
String link,
|
||||||
String href,
|
String? href,
|
||||||
String title,
|
String title,
|
||||||
) {
|
) {
|
||||||
if (link.startsWith('@')) {
|
if (link.startsWith('@')) {
|
||||||
final mentionedUser = message.mentionedUsers.firstWhere(
|
final mentionedUser = message.mentionedUsers.firstWhereOrNull(
|
||||||
(u) => '@${u.name}' == link,
|
(u) => '@${u.name}' == link,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
|
if (mentionedUser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (onMentionTap != null) {
|
if (onMentionTap != null) {
|
||||||
onMentionTap(mentionedUser);
|
onMentionTap!(mentionedUser);
|
||||||
} else {
|
} else {
|
||||||
print('tap on ${mentionedUser.name}');
|
print('tap on ${mentionedUser.name}');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (onLinkTap != null) {
|
if (onLinkTap != null) {
|
||||||
onLinkTap(link);
|
onLinkTap!(link);
|
||||||
} else {
|
} else {
|
||||||
launchURL(context, link);
|
launchURL(context, link);
|
||||||
}
|
}
|
||||||
@@ -52,23 +55,23 @@ class MessageText extends StatelessWidget {
|
|||||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||||
Theme.of(context).copyWith(
|
Theme.of(context).copyWith(
|
||||||
textTheme: Theme.of(context).textTheme.apply(
|
textTheme: Theme.of(context).textTheme.apply(
|
||||||
bodyColor: messageTheme.messageText.color,
|
bodyColor: messageTheme!.messageText!.color,
|
||||||
decoration: messageTheme.messageText.decoration,
|
decoration: messageTheme!.messageText!.decoration,
|
||||||
decorationColor: messageTheme.messageText.decorationColor,
|
decorationColor: messageTheme!.messageText!.decorationColor,
|
||||||
decorationStyle: messageTheme.messageText.decorationStyle,
|
decorationStyle: messageTheme!.messageText!.decorationStyle,
|
||||||
fontFamily: messageTheme.messageText.fontFamily,
|
fontFamily: messageTheme!.messageText!.fontFamily,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).copyWith(
|
).copyWith(
|
||||||
a: messageTheme.messageLinks,
|
a: messageTheme!.messageLinks,
|
||||||
p: messageTheme.messageText,
|
p: messageTheme!.messageText,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _replaceMentions(String text) {
|
String? _replaceMentions(String? text) {
|
||||||
message.mentionedUsers?.map((u) => u.name)?.toSet()?.forEach((userName) {
|
message.mentionedUsers.map((u) => u.name).toSet().forEach((userName) {
|
||||||
text = text.replaceAll(
|
text = text!.replaceAll(
|
||||||
'@$userName', '[@$userName](@${userName.replaceAll(' ', '')})');
|
'@$userName', '[@$userName](@${userName.replaceAll(' ', '')})');
|
||||||
});
|
});
|
||||||
return text;
|
return text;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ typedef AttachmentBuilder = Widget Function(
|
|||||||
Message,
|
Message,
|
||||||
List<Attachment>,
|
List<Attachment>,
|
||||||
);
|
);
|
||||||
typedef OnQuotedMessageTap = void Function(String);
|
typedef OnQuotedMessageTap = void Function(String?);
|
||||||
|
|
||||||
/// The display behaviour of a widget
|
/// The display behaviour of a widget
|
||||||
enum DisplayWidget {
|
enum DisplayWidget {
|
||||||
@@ -51,46 +51,46 @@ enum DisplayWidget {
|
|||||||
/// Modify it to change the widget appearance.
|
/// Modify it to change the widget appearance.
|
||||||
class MessageWidget extends StatefulWidget {
|
class MessageWidget extends StatefulWidget {
|
||||||
/// Function called on mention tap
|
/// Function called on mention tap
|
||||||
final void Function(User) onMentionTap;
|
final void Function(User)? onMentionTap;
|
||||||
|
|
||||||
/// The function called when tapping on replies
|
/// The function called when tapping on replies
|
||||||
final void Function(Message) onThreadTap;
|
final void Function(Message)? onThreadTap;
|
||||||
final void Function(Message) onReplyTap;
|
final void Function(Message)? onReplyTap;
|
||||||
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
final Widget Function(BuildContext, Message)? editMessageInputBuilder;
|
||||||
final Widget Function(BuildContext, Message) textBuilder;
|
final Widget Function(BuildContext, Message)? textBuilder;
|
||||||
|
|
||||||
/// Function called on long press
|
/// Function called on long press
|
||||||
final void Function(BuildContext, Message) onMessageActions;
|
final void Function(BuildContext, Message)? onMessageActions;
|
||||||
|
|
||||||
/// The message
|
/// The message
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
/// The message theme
|
/// The message theme
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
|
|
||||||
/// If true the widget will be mirrored
|
/// If true the widget will be mirrored
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
/// The shape of the message text
|
/// The shape of the message text
|
||||||
final ShapeBorder shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
/// The shape of an attachment
|
/// The shape of an attachment
|
||||||
final ShapeBorder attachmentShape;
|
final ShapeBorder? attachmentShape;
|
||||||
|
|
||||||
/// The borderside of the message text
|
/// The borderside of the message text
|
||||||
final BorderSide borderSide;
|
final BorderSide? borderSide;
|
||||||
|
|
||||||
/// The borderside of an attachment
|
/// The borderside of an attachment
|
||||||
final BorderSide attachmentBorderSide;
|
final BorderSide? attachmentBorderSide;
|
||||||
|
|
||||||
/// The border radius of the message text
|
/// The border radius of the message text
|
||||||
final BorderRadiusGeometry borderRadiusGeometry;
|
final BorderRadiusGeometry? borderRadiusGeometry;
|
||||||
|
|
||||||
/// The border radius of an attachment
|
/// The border radius of an attachment
|
||||||
final BorderRadiusGeometry attachmentBorderRadiusGeometry;
|
final BorderRadiusGeometry? attachmentBorderRadiusGeometry;
|
||||||
|
|
||||||
/// The padding of the widget
|
/// The padding of the widget
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
/// The internal padding of the message text
|
/// The internal padding of the message text
|
||||||
final EdgeInsetsGeometry textPadding;
|
final EdgeInsetsGeometry textPadding;
|
||||||
@@ -116,18 +116,18 @@ class MessageWidget extends StatefulWidget {
|
|||||||
final bool showInChannelIndicator;
|
final bool showInChannelIndicator;
|
||||||
|
|
||||||
/// The function called when tapping on UserAvatar
|
/// The function called when tapping on UserAvatar
|
||||||
final void Function(User) onUserAvatarTap;
|
final void Function(User)? onUserAvatarTap;
|
||||||
|
|
||||||
/// The function called when tapping on a link
|
/// The function called when tapping on a link
|
||||||
final void Function(String) onLinkTap;
|
final void Function(String)? onLinkTap;
|
||||||
|
|
||||||
/// Used in [MessageReactionsModal] and [MessageActionsModal]
|
/// Used in [MessageReactionsModal] and [MessageActionsModal]
|
||||||
final bool showReactionPickerIndicator;
|
final bool showReactionPickerIndicator;
|
||||||
|
|
||||||
final List<Read> readList;
|
final List<Read>? readList;
|
||||||
|
|
||||||
final ShowMessageCallback onShowMessage;
|
final ShowMessageCallback? onShowMessage;
|
||||||
final ValueChanged<ReturnActionType> onReturnAction;
|
final ValueChanged<ReturnActionType>? onReturnAction;
|
||||||
|
|
||||||
/// If true show the users username next to the timestamp of the message
|
/// If true show the users username next to the timestamp of the message
|
||||||
final bool showUsername;
|
final bool showUsername;
|
||||||
@@ -147,22 +147,22 @@ class MessageWidget extends StatefulWidget {
|
|||||||
final bool translateUserAvatar;
|
final bool translateUserAvatar;
|
||||||
|
|
||||||
/// Function called when quotedMessage is tapped
|
/// Function called when quotedMessage is tapped
|
||||||
final OnQuotedMessageTap onQuotedMessageTap;
|
final OnQuotedMessageTap? onQuotedMessageTap;
|
||||||
|
|
||||||
/// Function called when message is tapped
|
/// Function called when message is tapped
|
||||||
final void Function(Message) onMessageTap;
|
final void Function(Message)? onMessageTap;
|
||||||
|
|
||||||
/// List of custom actions shown on message long tap
|
/// List of custom actions shown on message long tap
|
||||||
final List<MessageAction> customActions;
|
final List<MessageAction> customActions;
|
||||||
|
|
||||||
// Customize onTap on attachment
|
// Customize onTap on attachment
|
||||||
final void Function(Message message, Attachment attachment) onAttachmentTap;
|
final void Function(Message message, Attachment attachment)? onAttachmentTap;
|
||||||
|
|
||||||
///
|
///
|
||||||
MessageWidget({
|
MessageWidget({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.translateUserAvatar = true,
|
this.translateUserAvatar = true,
|
||||||
this.shape,
|
this.shape,
|
||||||
@@ -197,7 +197,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.textBuilder,
|
this.textBuilder,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
Map<String, AttachmentBuilder> customAttachmentBuilders,
|
Map<String, AttachmentBuilder>? customAttachmentBuilders,
|
||||||
this.readList,
|
this.readList,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.textPadding = const EdgeInsets.symmetric(
|
this.textPadding = const EdgeInsets.symmetric(
|
||||||
@@ -222,7 +222,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
child: wrapAttachmentWidget(
|
child: wrapAttachmentWidget(
|
||||||
context,
|
context,
|
||||||
Material(
|
Material(
|
||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme?.messageBackgroundColor,
|
||||||
child: ImageGroup(
|
child: ImageGroup(
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
@@ -236,7 +236,8 @@ class MessageWidget extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
|
BorderRadius.zero,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -255,13 +256,14 @@ class MessageWidget extends StatefulWidget {
|
|||||||
onReturnAction: onReturnAction,
|
onReturnAction: onReturnAction,
|
||||||
onAttachmentTap: onAttachmentTap != null
|
onAttachmentTap: onAttachmentTap != null
|
||||||
? () {
|
? () {
|
||||||
onAttachmentTap?.call(message, attachments[0]);
|
onAttachmentTap.call(message, attachments[0]);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
|
BorderRadius.zero,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'video': (context, message, attachments) {
|
'video': (context, message, attachments) {
|
||||||
@@ -286,7 +288,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
onReturnAction: onReturnAction,
|
onReturnAction: onReturnAction,
|
||||||
onAttachmentTap: onAttachmentTap != null
|
onAttachmentTap: onAttachmentTap != null
|
||||||
? () {
|
? () {
|
||||||
onAttachmentTap?.call(message, attachment);
|
onAttachmentTap(message, attachment);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
@@ -294,11 +296,12 @@ class MessageWidget extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
|
BorderRadius.zero,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'giphy': (context, message, attachments) {
|
'giphy': (context, message, attachments) {
|
||||||
var border = RoundedRectangleBorder(
|
final border = RoundedRectangleBorder(
|
||||||
side: BorderSide.none,
|
side: BorderSide.none,
|
||||||
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
);
|
);
|
||||||
@@ -309,7 +312,6 @@ class MessageWidget extends StatefulWidget {
|
|||||||
children: attachments.map((attachment) {
|
children: attachments.map((attachment) {
|
||||||
return GiphyAttachment(
|
return GiphyAttachment(
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
messageTheme: messageTheme,
|
|
||||||
message: message,
|
message: message,
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
@@ -322,7 +324,8 @@ class MessageWidget extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
|
BorderRadius.zero,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'file': (context, message, attachments) {
|
'file': (context, message, attachments) {
|
||||||
@@ -349,7 +352,8 @@ class MessageWidget extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
|
BorderRadius.zero,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.insertBetween(SizedBox(
|
.insertBetween(SizedBox(
|
||||||
@@ -381,7 +385,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
bool get showInChannel => widget.showInChannelIndicator;
|
bool get showInChannel => widget.showInChannelIndicator;
|
||||||
|
|
||||||
bool get hasQuotedMessage => widget.message?.quotedMessage != null;
|
bool get hasQuotedMessage => widget.message.quotedMessage != null;
|
||||||
|
|
||||||
bool get isSendFailed => widget.message.status == MessageSendingStatus.failed;
|
bool get isSendFailed => widget.message.status == MessageSendingStatus.failed;
|
||||||
|
|
||||||
@@ -394,17 +398,17 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
|
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
|
||||||
|
|
||||||
bool get isGiphy =>
|
bool get isGiphy =>
|
||||||
widget.message.attachments?.any((element) => element.type == 'giphy') ==
|
widget.message.attachments.any((element) => element.type == 'giphy') ==
|
||||||
true;
|
true;
|
||||||
|
|
||||||
bool get hasNonUrlAttachments =>
|
bool get hasNonUrlAttachments =>
|
||||||
widget.message.attachments
|
widget.message.attachments
|
||||||
?.where((it) => it.ogScrapeUrl == null)
|
.where((it) => it.ogScrapeUrl == null)
|
||||||
?.isNotEmpty ==
|
.isNotEmpty ==
|
||||||
true;
|
true;
|
||||||
|
|
||||||
bool get hasUrlAttachments =>
|
bool get hasUrlAttachments =>
|
||||||
widget.message.attachments?.any((it) => it.ogScrapeUrl != null) == true;
|
widget.message.attachments.any((it) => it.ogScrapeUrl != null) == true;
|
||||||
|
|
||||||
bool get showBottomRow =>
|
bool get showBottomRow =>
|
||||||
showThreadReplyIndicator ||
|
showThreadReplyIndicator ||
|
||||||
@@ -415,12 +419,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
isDeleted;
|
isDeleted;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => widget.message.attachments?.isNotEmpty == true;
|
bool get wantKeepAlive => widget.message.attachments.isNotEmpty == true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
final avatarWidth = widget.messageTheme.avatarTheme.constraints.maxWidth;
|
final avatarWidth =
|
||||||
|
widget.messageTheme?.avatarTheme?.constraints.maxWidth ?? 40;
|
||||||
var leftPadding =
|
var leftPadding =
|
||||||
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
||||||
|
|
||||||
@@ -429,7 +434,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
child: Portal(
|
child: Portal(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
widget.onMessageTap(widget.message);
|
widget.onMessageTap!(widget.message);
|
||||||
},
|
},
|
||||||
onLongPress: widget.message.isDeleted && !isFailedState
|
onLongPress: widget.message.isDeleted && !isFailedState
|
||||||
? null
|
? null
|
||||||
@@ -460,7 +465,8 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (widget.showUserAvatar ==
|
if (widget.showUserAvatar ==
|
||||||
DisplayWidget.show) ...[
|
DisplayWidget.show &&
|
||||||
|
widget.message.user != null) ...[
|
||||||
_buildUserAvatar(),
|
_buildUserAvatar(),
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
@@ -533,13 +539,14 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
),
|
),
|
||||||
shape: widget.shape ??
|
shape: widget.shape ??
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
side:
|
side: widget
|
||||||
widget.borderSide ??
|
.borderSide ??
|
||||||
BorderSide(
|
BorderSide(
|
||||||
color: widget
|
color: widget
|
||||||
.messageTheme
|
.messageTheme
|
||||||
.messageBorderColor,
|
?.messageBorderColor ??
|
||||||
),
|
Colors.grey,
|
||||||
|
),
|
||||||
borderRadius: widget
|
borderRadius: widget
|
||||||
.borderRadiusGeometry ??
|
.borderRadiusGeometry ??
|
||||||
BorderRadius.zero,
|
BorderRadius.zero,
|
||||||
@@ -616,14 +623,14 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
Widget _buildQuotedMessage() {
|
Widget _buildQuotedMessage() {
|
||||||
final isMyMessage =
|
final isMyMessage =
|
||||||
widget.message.user.id == StreamChat.of(context).user.id;
|
widget.message.user?.id == StreamChat.of(context).user?.id;
|
||||||
final onTap = widget.message?.quotedMessage?.isDeleted != true &&
|
final onTap = widget.message.quotedMessage?.isDeleted != true &&
|
||||||
widget.onQuotedMessageTap != null
|
widget.onQuotedMessageTap != null
|
||||||
? () => widget.onQuotedMessageTap(widget.message.quotedMessageId)
|
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
|
||||||
: null;
|
: null;
|
||||||
return QuotedMessageWidget(
|
return QuotedMessageWidget(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
message: widget.message.quotedMessage,
|
message: widget.message.quotedMessage!,
|
||||||
messageTheme: isMyMessage
|
messageTheme: isMyMessage
|
||||||
? StreamChatTheme.of(context).otherMessageTheme
|
? StreamChatTheme.of(context).otherMessageTheme
|
||||||
: StreamChatTheme.of(context).ownMessageTheme,
|
: StreamChatTheme.of(context).ownMessageTheme,
|
||||||
@@ -660,12 +667,12 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
var children = <Widget>[];
|
var children = <Widget>[];
|
||||||
|
|
||||||
final threadParticipants = widget.message?.threadParticipants?.take(2);
|
final threadParticipants = widget.message.threadParticipants?.take(2);
|
||||||
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
||||||
final replyCount = widget.message.replyCount;
|
final replyCount = widget.message.replyCount;
|
||||||
|
|
||||||
var msg = 'Thread Reply';
|
var msg = 'Thread Reply';
|
||||||
if (showThreadReplyIndicator && replyCount > 1) {
|
if (showThreadReplyIndicator && replyCount! > 1) {
|
||||||
msg = '$replyCount Thread Replies';
|
msg = '$replyCount Thread Replies';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,9 +681,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
var message = widget.message;
|
var message = widget.message;
|
||||||
if (showInChannel) {
|
if (showInChannel) {
|
||||||
final channel = StreamChannel.of(context);
|
final channel = StreamChannel.of(context);
|
||||||
message = await channel.getMessage(widget.message.parentId);
|
message = await channel.getMessage(widget.message.parentId!);
|
||||||
}
|
}
|
||||||
return widget.onThreadTap(message);
|
return widget.onThreadTap!(message);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
print(e);
|
print(e);
|
||||||
print(stk);
|
print(stk);
|
||||||
@@ -690,7 +697,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
if (showInChannel || showThreadReplyIndicator) ...[
|
if (showInChannel || showThreadReplyIndicator) ...[
|
||||||
if (showThreadParticipants)
|
if (showThreadParticipants)
|
||||||
SizedBox.fromSize(
|
SizedBox.fromSize(
|
||||||
size: Size((threadParticipants.length * 8.0) + 8, 16),
|
size: Size((threadParticipants!.length * 8.0) + 8, 16),
|
||||||
child: _buildThreadParticipantsIndicator(threadParticipants),
|
child: _buildThreadParticipantsIndicator(threadParticipants),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
@@ -700,16 +707,16 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
],
|
],
|
||||||
if (showUsername)
|
if (showUsername)
|
||||||
Text(
|
Text(
|
||||||
widget.message.user.name,
|
widget.message.user!.name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
key: usernameKey,
|
key: usernameKey,
|
||||||
style: widget.messageTheme.messageAuthor,
|
style: widget.messageTheme?.messageAuthor,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
if (showTimeStamp)
|
if (showTimeStamp)
|
||||||
Text(
|
Text(
|
||||||
Jiffy(widget.message.createdAt.toLocal()).jm,
|
Jiffy(widget.message.createdAt.toLocal()).jm,
|
||||||
style: widget.messageTheme.createdAt,
|
style: widget.messageTheme?.createdAt,
|
||||||
),
|
),
|
||||||
if (showSendingIndicator) _buildSendingIndicator(),
|
if (showSendingIndicator) _buildSendingIndicator(),
|
||||||
]);
|
]);
|
||||||
@@ -724,13 +731,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
bottom: context.textScaleFactor *
|
bottom: context.textScaleFactor *
|
||||||
(widget.messageTheme.replies.fontSize / 2),
|
((widget.messageTheme?.replies?.fontSize ?? 1) / 2),
|
||||||
),
|
),
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
size: Size(16, 32) * context.textScaleFactor,
|
size: Size(16, 32) * context.textScaleFactor,
|
||||||
painter: _ThreadReplyPainter(
|
painter: _ThreadReplyPainter(
|
||||||
context: context,
|
context: context,
|
||||||
color: widget.messageTheme.messageBorderColor,
|
color: widget.messageTheme?.messageBorderColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -758,7 +765,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
var urlAttachment = widget.message.attachments
|
var urlAttachment = widget.message.attachments
|
||||||
.firstWhere((element) => element.ogScrapeUrl != null);
|
.firstWhere((element) => element.ogScrapeUrl != null);
|
||||||
|
|
||||||
var host = Uri.parse(urlAttachment.ogScrapeUrl).host;
|
var host = Uri.parse(urlAttachment.ogScrapeUrl!).host;
|
||||||
var splitList = host.split('.');
|
var splitList = host.split('.');
|
||||||
var hostName = splitList.length == 3 ? splitList[1] : splitList[0];
|
var hostName = splitList.length == 3 ? splitList[1] : splitList[0];
|
||||||
var hostDisplayName = urlAttachment.authorName?.capitalize() ??
|
var hostDisplayName = urlAttachment.authorName?.capitalize() ??
|
||||||
@@ -768,7 +775,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
return UrlAttachment(
|
return UrlAttachment(
|
||||||
urlAttachment: urlAttachment,
|
urlAttachment: urlAttachment,
|
||||||
hostDisplayName: hostDisplayName,
|
hostDisplayName: hostDisplayName,
|
||||||
textPadding: widget.textPadding,
|
textPadding: widget.textPadding as EdgeInsets,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,15 +808,16 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
Widget _buildReactionIndicator(
|
Widget _buildReactionIndicator(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final ownId = StreamChat.of(context).user.id;
|
final ownId = StreamChat.of(context).user!.id;
|
||||||
final reactionsMap = <String, Reaction>{};
|
final reactionsMap = <String, Reaction>{};
|
||||||
widget.message.latestReactions?.forEach((element) {
|
widget.message.latestReactions?.forEach((element) {
|
||||||
if (!reactionsMap.containsKey(element.type) || element.user.id == ownId) {
|
if (!reactionsMap.containsKey(element.type) ||
|
||||||
|
element.user!.id == ownId) {
|
||||||
reactionsMap[element.type] = element;
|
reactionsMap[element.type] = element;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final reactionsList = reactionsMap.values.toList()
|
final reactionsList = reactionsMap.values.toList()
|
||||||
..sort((a, b) => a.user.id == ownId ? 1 : -1);
|
..sort((a, b) => a.user!.id == ownId ? 1 : -1);
|
||||||
|
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
@@ -822,9 +830,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
key: ValueKey('${widget.message.id}.reactions'),
|
key: ValueKey('${widget.message.id}.reactions'),
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
flipTail: widget.reverse,
|
flipTail: widget.reverse,
|
||||||
backgroundColor: widget.messageTheme.reactionsBackgroundColor,
|
backgroundColor:
|
||||||
borderColor: widget.messageTheme.reactionsBorderColor,
|
widget.messageTheme?.reactionsBackgroundColor ??
|
||||||
maskColor: widget.messageTheme.reactionsMaskColor,
|
Colors.transparent,
|
||||||
|
borderColor: widget.messageTheme?.reactionsBorderColor ??
|
||||||
|
Colors.transparent,
|
||||||
|
maskColor: widget.messageTheme?.reactionsMaskColor ??
|
||||||
|
Colors.transparent,
|
||||||
reactions: reactionsList,
|
reactions: reactionsList,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -845,9 +857,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
onCopyTap: (message) =>
|
onCopyTap: (message) =>
|
||||||
Clipboard.setData(ClipboardData(text: message.text)),
|
Clipboard.setData(ClipboardData(text: message.text)),
|
||||||
attachmentBorderRadiusGeometry:
|
attachmentBorderRadiusGeometry:
|
||||||
widget.attachmentBorderRadiusGeometry,
|
widget.attachmentBorderRadiusGeometry as BorderRadius?,
|
||||||
showUserAvatar:
|
showUserAvatar:
|
||||||
widget.message.user.id == channel.client.state.user.id
|
widget.message.user!.id == channel.client.state.user!.id
|
||||||
? DisplayWidget.gone
|
? DisplayWidget.gone
|
||||||
: DisplayWidget.show,
|
: DisplayWidget.show,
|
||||||
messageTheme: widget.messageTheme,
|
messageTheme: widget.messageTheme,
|
||||||
@@ -864,11 +876,11 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
widget.showResendMessage && (isSendFailed || isUpdateFailed),
|
widget.showResendMessage && (isSendFailed || isUpdateFailed),
|
||||||
showCopyMessage: widget.showCopyMessage &&
|
showCopyMessage: widget.showCopyMessage &&
|
||||||
!isFailedState &&
|
!isFailedState &&
|
||||||
widget.message.text?.trim()?.isNotEmpty == true,
|
widget.message.text?.trim().isNotEmpty == true,
|
||||||
showEditMessage: widget.showEditMessage &&
|
showEditMessage: widget.showEditMessage &&
|
||||||
!isDeleteFailed &&
|
!isDeleteFailed &&
|
||||||
widget.message.attachments
|
widget.message.attachments
|
||||||
?.any((element) => element.type == 'giphy') !=
|
.any((element) => element.type == 'giphy') !=
|
||||||
true,
|
true,
|
||||||
showReactions: widget.showReactions,
|
showReactions: widget.showReactions,
|
||||||
showReplyMessage: widget.showReplyMessage &&
|
showReplyMessage: widget.showReplyMessage &&
|
||||||
@@ -894,9 +906,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
child: MessageReactionsModal(
|
child: MessageReactionsModal(
|
||||||
attachmentBorderRadiusGeometry:
|
attachmentBorderRadiusGeometry:
|
||||||
widget.attachmentBorderRadiusGeometry,
|
widget.attachmentBorderRadiusGeometry as BorderRadius?,
|
||||||
showUserAvatar:
|
showUserAvatar:
|
||||||
widget.message.user.id == channel.client.state.user.id
|
widget.message.user!.id == channel.client.state.user!.id
|
||||||
? DisplayWidget.gone
|
? DisplayWidget.gone
|
||||||
: DisplayWidget.show,
|
: DisplayWidget.show,
|
||||||
onUserAvatarTap: widget.onUserAvatarTap,
|
onUserAvatarTap: widget.onUserAvatarTap,
|
||||||
@@ -914,7 +926,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
ShapeBorder _getDefaultAttachmentShape(BuildContext context) {
|
ShapeBorder _getDefaultAttachmentShape(BuildContext context) {
|
||||||
final hasFiles =
|
final hasFiles =
|
||||||
widget.message.attachments?.any((it) => it.type == 'file') == true;
|
widget.message.attachments.any((it) => it.type == 'file') == true;
|
||||||
return RoundedRectangleBorder(
|
return RoundedRectangleBorder(
|
||||||
side: hasFiles
|
side: hasFiles
|
||||||
? widget.attachmentBorderSide ??
|
? widget.attachmentBorderSide ??
|
||||||
@@ -940,13 +952,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
final attachmentGroups = <String, List<Attachment>>{};
|
final attachmentGroups = <String, List<Attachment>>{};
|
||||||
|
|
||||||
widget.message.attachments
|
widget.message.attachments
|
||||||
.where((element) => element.ogScrapeUrl == null)
|
.where((element) => element.ogScrapeUrl == null && element.type != null)
|
||||||
.forEach((e) {
|
.forEach((e) {
|
||||||
if (attachmentGroups[e.type] == null) {
|
if (attachmentGroups[e.type] == null) {
|
||||||
attachmentGroups[e.type] = [];
|
attachmentGroups[e.type!] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentGroups[e.type].add(e);
|
attachmentGroups[e.type]?.add(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
final attachmentList = <Widget>[];
|
final attachmentList = <Widget>[];
|
||||||
@@ -954,7 +966,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
attachmentGroups.forEach((type, attachments) {
|
attachmentGroups.forEach((type, attachments) {
|
||||||
final attachmentBuilder = widget.attachmentBuilders[type];
|
final attachmentBuilder = widget.attachmentBuilders[type];
|
||||||
|
|
||||||
if (attachmentBuilder == null) return SizedBox();
|
if (attachmentBuilder == null) return;
|
||||||
final attachmentWidget = attachmentBuilder(
|
final attachmentWidget = attachmentBuilder(
|
||||||
context,
|
context,
|
||||||
widget.message,
|
widget.message,
|
||||||
@@ -967,10 +979,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
padding: widget.attachmentPadding,
|
padding: widget.attachmentPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: attachmentList?.insertBetween(SizedBox(
|
children: attachmentList.insertBetween(SizedBox(
|
||||||
height: widget.attachmentPadding.vertical / 2,
|
height: widget.attachmentPadding.vertical / 2,
|
||||||
)) ??
|
)),
|
||||||
[],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -982,7 +993,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (widget.onMessageActions != null) {
|
if (widget.onMessageActions != null) {
|
||||||
widget.onMessageActions(context, widget.message);
|
widget.onMessageActions!(context, widget.message);
|
||||||
} else {
|
} else {
|
||||||
_showMessageActionModalBottomSheet(context);
|
_showMessageActionModalBottomSheet(context);
|
||||||
}
|
}
|
||||||
@@ -990,7 +1001,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSendingIndicator() {
|
Widget _buildSendingIndicator() {
|
||||||
final style = widget.messageTheme.createdAt;
|
final style = widget.messageTheme?.createdAt;
|
||||||
final message = widget.message;
|
final message = widget.message;
|
||||||
|
|
||||||
if (hasNonUrlAttachments &&
|
if (hasNonUrlAttachments &&
|
||||||
@@ -1002,8 +1013,8 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}).length;
|
}).length;
|
||||||
if (uploadRemaining == 0) {
|
if (uploadRemaining == 0) {
|
||||||
return StreamSvgIcon.check(
|
return StreamSvgIcon.check(
|
||||||
size: style.fontSize,
|
size: style!.fontSize,
|
||||||
color: IconTheme.of(context).color.withOpacity(0.5),
|
color: IconTheme.of(context).color!.withOpacity(0.5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Text(
|
return Text(
|
||||||
@@ -1015,14 +1026,14 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
Widget child = SendingIndicator(
|
Widget child = SendingIndicator(
|
||||||
message: message,
|
message: message,
|
||||||
isMessageRead: isMessageRead,
|
isMessageRead: isMessageRead,
|
||||||
size: style.fontSize,
|
size: style!.fontSize,
|
||||||
);
|
);
|
||||||
if (isMessageRead) {
|
if (isMessageRead) {
|
||||||
child = Row(
|
child = Row(
|
||||||
children: [
|
children: [
|
||||||
if (StreamChannel.of(context).channel.memberCount > 2)
|
if (StreamChannel.of(context).channel.memberCount! > 2)
|
||||||
Text(
|
Text(
|
||||||
widget.readList.length.toString(),
|
widget.readList!.length.toString(),
|
||||||
style: style.copyWith(
|
style: style.copyWith(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
@@ -1042,21 +1053,23 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
offset: Offset(
|
offset: Offset(
|
||||||
0,
|
0,
|
||||||
widget.translateUserAvatar
|
widget.translateUserAvatar
|
||||||
? widget.messageTheme.avatarTheme.constraints.maxHeight / 2
|
? (widget.messageTheme?.avatarTheme?.constraints.maxHeight ??
|
||||||
|
40) /
|
||||||
|
2
|
||||||
: 0,
|
: 0,
|
||||||
),
|
),
|
||||||
child: UserAvatar(
|
child: UserAvatar(
|
||||||
user: widget.message.user,
|
user: widget.message.user!,
|
||||||
onTap: widget.onUserAvatarTap,
|
onTap: widget.onUserAvatarTap,
|
||||||
constraints: widget.messageTheme.avatarTheme.constraints,
|
constraints: widget.messageTheme?.avatarTheme!.constraints,
|
||||||
borderRadius: widget.messageTheme.avatarTheme.borderRadius,
|
borderRadius: widget.messageTheme?.avatarTheme!.borderRadius,
|
||||||
showOnlineStatus: false,
|
showOnlineStatus: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTextBubble() {
|
Widget _buildTextBubble() {
|
||||||
if (widget.message.text.trim().isEmpty) return Offstage();
|
if (widget.message.text!.trim().isEmpty) return Offstage();
|
||||||
return Transform(
|
return Transform(
|
||||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@@ -1066,15 +1079,15 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
Padding(
|
Padding(
|
||||||
padding: isOnlyEmoji ? EdgeInsets.zero : widget.textPadding,
|
padding: isOnlyEmoji ? EdgeInsets.zero : widget.textPadding,
|
||||||
child: widget.textBuilder != null
|
child: widget.textBuilder != null
|
||||||
? widget.textBuilder(context, widget.message)
|
? widget.textBuilder!(context, widget.message)
|
||||||
: MessageText(
|
: MessageText(
|
||||||
onLinkTap: widget.onLinkTap,
|
onLinkTap: widget.onLinkTap,
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
onMentionTap: widget.onMentionTap,
|
onMentionTap: widget.onMentionTap,
|
||||||
messageTheme: isOnlyEmoji
|
messageTheme: isOnlyEmoji
|
||||||
? widget.messageTheme.copyWith(
|
? widget.messageTheme?.copyWith(
|
||||||
messageText:
|
messageText:
|
||||||
widget.messageTheme.messageText.copyWith(
|
widget.messageTheme?.messageText!.copyWith(
|
||||||
fontSize: 42,
|
fontSize: 42,
|
||||||
))
|
))
|
||||||
: widget.messageTheme,
|
: widget.messageTheme,
|
||||||
@@ -1086,11 +1099,11 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isOnlyEmoji => widget.message.text.isOnlyEmoji;
|
bool get isOnlyEmoji => widget.message.text!.isOnlyEmoji;
|
||||||
|
|
||||||
Color _getBackgroundColor() {
|
Color? _getBackgroundColor() {
|
||||||
if (hasQuotedMessage) {
|
if (hasQuotedMessage) {
|
||||||
return widget.messageTheme.messageBackgroundColor;
|
return widget.messageTheme?.messageBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUrlAttachments) {
|
if (hasUrlAttachments) {
|
||||||
@@ -1105,7 +1118,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
return Colors.transparent;
|
return Colors.transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return widget.messageTheme.messageBackgroundColor;
|
return widget.messageTheme?.messageBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retryMessage(BuildContext context) {
|
void retryMessage(BuildContext context) {
|
||||||
@@ -1127,15 +1140,15 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ThreadReplyPainter extends CustomPainter {
|
class _ThreadReplyPainter extends CustomPainter {
|
||||||
final Color color;
|
final Color? color;
|
||||||
final BuildContext context;
|
final BuildContext? context;
|
||||||
|
|
||||||
const _ThreadReplyPainter({this.context, @required this.color});
|
const _ThreadReplyPainter({this.context, required this.color});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
final paint = Paint()
|
final paint = Paint()
|
||||||
..color = color ?? StreamChatTheme.of(context).colorTheme.greyGainsboro
|
..color = color ?? StreamChatTheme.of(context!).colorTheme.greyGainsboro
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 1
|
..strokeWidth = 1
|
||||||
..strokeCap = StrokeCap.round;
|
..strokeCap = StrokeCap.round;
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
|
||||||
class OptionListTile extends StatelessWidget {
|
class OptionListTile extends StatelessWidget {
|
||||||
final String title;
|
final String? title;
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
final Widget trailing;
|
final Widget? trailing;
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
final Color titleColor;
|
final Color? titleColor;
|
||||||
final Color tileColor;
|
final Color? tileColor;
|
||||||
final Color separatorColor;
|
final Color? separatorColor;
|
||||||
final TextStyle titleTextStyle;
|
final TextStyle? titleTextStyle;
|
||||||
|
|
||||||
OptionListTile({
|
OptionListTile({
|
||||||
this.title,
|
this.title,
|
||||||
@@ -47,7 +47,7 @@ class OptionListTile extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 4,
|
flex: 4,
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title!,
|
||||||
style: titleTextStyle ??
|
style: titleTextStyle ??
|
||||||
(titleColor == null
|
(titleColor == null
|
||||||
? StreamChatTheme.of(context).textTheme.bodyBold
|
? StreamChatTheme.of(context).textTheme.bodyBold
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ class _VideoAttachmentThumbnail extends StatefulWidget {
|
|||||||
final Attachment attachment;
|
final Attachment attachment;
|
||||||
|
|
||||||
const _VideoAttachmentThumbnail({
|
const _VideoAttachmentThumbnail({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.attachment,
|
required this.attachment,
|
||||||
this.size = const Size(32, 32),
|
this.size = const Size(32, 32),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -33,12 +33,12 @@ class _VideoAttachmentThumbnail extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> {
|
class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> {
|
||||||
VideoPlayerController _controller;
|
late VideoPlayerController _controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = VideoPlayerController.network(widget.attachment.assetUrl)
|
_controller = VideoPlayerController.network(widget.attachment.assetUrl!)
|
||||||
..initialize().then((_) {
|
..initialize().then((_) {
|
||||||
setState(() {}); //when your thumbnail will show.
|
setState(() {}); //when your thumbnail will show.
|
||||||
});
|
});
|
||||||
@@ -67,7 +67,7 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
/// The message theme
|
/// The message theme
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
|
|
||||||
/// If true the widget will be mirrored
|
/// If true the widget will be mirrored
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
@@ -79,18 +79,18 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
final int textLimit;
|
final int textLimit;
|
||||||
|
|
||||||
/// Map that defines a thumbnail builder for an attachment type
|
/// Map that defines a thumbnail builder for an attachment type
|
||||||
final Map<String, QuotedMessageAttachmentThumbnailBuilder>
|
final Map<String, QuotedMessageAttachmentThumbnailBuilder>?
|
||||||
attachmentThumbnailBuilders;
|
attachmentThumbnailBuilders;
|
||||||
|
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
final GestureTapCallback onTap;
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
///
|
///
|
||||||
QuotedMessageWidget({
|
QuotedMessageWidget({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.showBorder = false,
|
this.showBorder = false,
|
||||||
this.textLimit = 170,
|
this.textLimit = 170,
|
||||||
@@ -99,13 +99,12 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
bool get _hasAttachments => message.attachments?.isNotEmpty == true;
|
bool get _hasAttachments => message.attachments.isNotEmpty == true;
|
||||||
|
|
||||||
bool get _containsScrapeUrl =>
|
bool get _containsScrapeUrl =>
|
||||||
message.attachments?.any((element) => element.ogScrapeUrl != null) ==
|
message.attachments.any((element) => element.ogScrapeUrl != null) == true;
|
||||||
true;
|
|
||||||
|
|
||||||
bool get _containsText => message?.text?.isNotEmpty == true;
|
bool get _containsText => message.text?.isNotEmpty == true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -119,7 +118,7 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Flexible(child: _buildMessage(context)),
|
Flexible(child: _buildMessage(context)),
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
_buildUserAvatar(),
|
if (message.user != null) _buildUserAvatar(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -127,17 +126,17 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessage(BuildContext context) {
|
Widget _buildMessage(BuildContext context) {
|
||||||
final isOnlyEmoji = message.text.isOnlyEmoji;
|
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||||
var msg = _hasAttachments && !_containsText
|
var msg = _hasAttachments && !_containsText
|
||||||
? message.copyWith(text: message.attachments.last?.title ?? '')
|
? message.copyWith(text: message.attachments.last.title ?? '')
|
||||||
: message;
|
: message;
|
||||||
if (msg.text.length > textLimit) {
|
if (msg.text!.length > textLimit) {
|
||||||
msg = msg.copyWith(text: '${msg.text.substring(0, textLimit - 3)}...');
|
msg = msg.copyWith(text: '${msg.text!.substring(0, textLimit - 3)}...');
|
||||||
}
|
}
|
||||||
|
|
||||||
final children = [
|
final children = [
|
||||||
if (_hasAttachments) _parseAttachments(context),
|
if (_hasAttachments) _parseAttachments(context),
|
||||||
if (msg.text.isNotEmpty)
|
if (msg.text!.isNotEmpty)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Transform(
|
child: Transform(
|
||||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
@@ -145,12 +144,12 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
child: MessageText(
|
child: MessageText(
|
||||||
message: msg,
|
message: msg,
|
||||||
messageTheme: isOnlyEmoji && _containsText
|
messageTheme: isOnlyEmoji && _containsText
|
||||||
? messageTheme.copyWith(
|
? messageTheme?.copyWith(
|
||||||
messageText: messageTheme.messageText.copyWith(
|
messageText: messageTheme?.messageText?.copyWith(
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
))
|
))
|
||||||
: messageTheme.copyWith(
|
: messageTheme?.copyWith(
|
||||||
messageText: messageTheme.messageText.copyWith(
|
messageText: messageTheme?.messageText?.copyWith(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
@@ -193,7 +192,7 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
image: CachedNetworkImageProvider(
|
image: CachedNetworkImageProvider(
|
||||||
attachment.imageUrl,
|
attachment.imageUrl!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -211,16 +210,16 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
child = _buildUrlAttachment(attachment);
|
child = _buildUrlAttachment(attachment);
|
||||||
} else {
|
} else {
|
||||||
QuotedMessageAttachmentThumbnailBuilder attachmentBuilder;
|
QuotedMessageAttachmentThumbnailBuilder? attachmentBuilder;
|
||||||
attachment = message.attachments.last;
|
attachment = message.attachments.last;
|
||||||
if (attachmentThumbnailBuilders?.containsKey(attachment?.type) == true) {
|
if (attachmentThumbnailBuilders?.containsKey(attachment.type) == true) {
|
||||||
attachmentBuilder = attachmentThumbnailBuilders[attachment?.type];
|
attachmentBuilder = attachmentThumbnailBuilders![attachment.type];
|
||||||
}
|
}
|
||||||
attachmentBuilder = _defaultAttachmentBuilder[attachment?.type];
|
attachmentBuilder = _defaultAttachmentBuilder[attachment.type];
|
||||||
if (attachmentBuilder == null) {
|
if (attachmentBuilder == null) {
|
||||||
child = Offstage();
|
child = Offstage();
|
||||||
}
|
}
|
||||||
child = attachmentBuilder(context, attachment);
|
child = attachmentBuilder!(context, attachment);
|
||||||
}
|
}
|
||||||
child = AbsorbPointer(child: child);
|
child = AbsorbPointer(child: child);
|
||||||
return Transform(
|
return Transform(
|
||||||
@@ -247,7 +246,7 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: UserAvatar(
|
child: UserAvatar(
|
||||||
user: message.user,
|
user: message.user!,
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
height: 24,
|
height: 24,
|
||||||
width: 24,
|
width: 24,
|
||||||
@@ -277,19 +276,20 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
'giphy': (_, attachment) {
|
'giphy': (_, attachment) {
|
||||||
final size = Size(32, 32);
|
final size = Size(32, 32);
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
height: size?.height,
|
height: size.height,
|
||||||
width: size?.width,
|
width: size.width,
|
||||||
placeholder: (_, __) {
|
placeholder: (_, __) {
|
||||||
return Container(
|
return Container(
|
||||||
width: size?.width,
|
width: size.width,
|
||||||
height: size?.height,
|
height: size.height,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
imageUrl:
|
imageUrl: attachment.thumbUrl ??
|
||||||
attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl,
|
attachment.imageUrl ??
|
||||||
|
attachment.assetUrl!,
|
||||||
errorWidget: (context, url, error) {
|
errorWidget: (context, url, error) {
|
||||||
return AttachmentError(size: size);
|
return AttachmentError(size: size);
|
||||||
},
|
},
|
||||||
@@ -300,16 +300,16 @@ class QuotedMessageWidget extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
height: 32,
|
height: 32,
|
||||||
width: 32,
|
width: 32,
|
||||||
child: getFileTypeImage(attachment.extraData['mime_type']),
|
child: getFileTypeImage(attachment.extraData['mime_type'] as String?),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _getBackgroundColor(BuildContext context) {
|
Color? _getBackgroundColor(BuildContext context) {
|
||||||
if (_containsScrapeUrl) {
|
if (_containsScrapeUrl) {
|
||||||
return StreamChatTheme.of(context).colorTheme.blueAlice;
|
return StreamChatTheme.of(context).colorTheme.blueAlice;
|
||||||
}
|
}
|
||||||
return messageTheme.messageBackgroundColor;
|
return messageTheme?.messageBackgroundColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@@ -9,11 +10,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
|
|
||||||
class ReactionBubble extends StatelessWidget {
|
class ReactionBubble extends StatelessWidget {
|
||||||
const ReactionBubble({
|
const ReactionBubble({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.reactions,
|
required this.reactions,
|
||||||
@required this.borderColor,
|
required this.borderColor,
|
||||||
@required this.backgroundColor,
|
required this.backgroundColor,
|
||||||
@required this.maskColor,
|
required this.maskColor,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.flipTail = false,
|
this.flipTail = false,
|
||||||
this.highlightOwnReactions = true,
|
this.highlightOwnReactions = true,
|
||||||
@@ -108,9 +109,8 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
Reaction reaction,
|
Reaction reaction,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final reactionIcon = reactionIcons.firstWhere(
|
final reactionIcon = reactionIcons.firstWhereOrNull(
|
||||||
(r) => r.type == reaction.type,
|
(r) => r.type == reaction.type,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
@@ -123,7 +123,7 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
color: (!highlightOwnReactions ||
|
color: (!highlightOwnReactions ||
|
||||||
reaction.user.id == StreamChat.of(context).user.id)
|
reaction.user?.id == StreamChat.of(context).user?.id)
|
||||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||||
: StreamChatTheme.of(context)
|
: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
@@ -134,7 +134,7 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
Icons.help_outline_rounded,
|
Icons.help_outline_rounded,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: (!highlightOwnReactions ||
|
color: (!highlightOwnReactions ||
|
||||||
reaction.user.id == StreamChat.of(context).user.id)
|
reaction.user?.id == StreamChat.of(context).user?.id)
|
||||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||||
: StreamChatTheme.of(context)
|
: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class ReactionIcon {
|
|||||||
final String assetName;
|
final String assetName;
|
||||||
|
|
||||||
ReactionIcon({
|
ReactionIcon({
|
||||||
this.type,
|
required this.type,
|
||||||
this.assetName,
|
required this.assetName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import 'extension.dart';
|
|||||||
|
|
||||||
class ReactionPicker extends StatefulWidget {
|
class ReactionPicker extends StatefulWidget {
|
||||||
const ReactionPicker({
|
const ReactionPicker({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
@required this.messageTheme,
|
required this.messageTheme,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Message message;
|
final Message message;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme? messageTheme;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ReactionPickerState createState() => _ReactionPickerState();
|
_ReactionPickerState createState() => _ReactionPickerState();
|
||||||
@@ -98,7 +98,8 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
if (ownReactionIndex != -1) {
|
if (ownReactionIndex != -1) {
|
||||||
removeReaction(
|
removeReaction(
|
||||||
context,
|
context,
|
||||||
widget.message.ownReactions[ownReactionIndex],
|
widget
|
||||||
|
.message.ownReactions![ownReactionIndex],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
sendReaction(
|
sendReaction(
|
||||||
@@ -129,7 +130,7 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
.accentBlue
|
.accentBlue
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.iconTheme
|
.iconTheme
|
||||||
.color
|
.color!
|
||||||
.withOpacity(.5),
|
.withOpacity(.5),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -181,7 +182,7 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (var a in animations) {
|
for (var a in animations) {
|
||||||
a?.dispose();
|
a.dispose();
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
class SendingIndicator extends StatelessWidget {
|
class SendingIndicator extends StatelessWidget {
|
||||||
final Message message;
|
final Message message;
|
||||||
final bool isMessageRead;
|
final bool isMessageRead;
|
||||||
final double size;
|
final double? size;
|
||||||
|
|
||||||
const SendingIndicator({
|
const SendingIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
this.message,
|
required this.message,
|
||||||
this.isMessageRead = false,
|
this.isMessageRead = false,
|
||||||
this.size = 12,
|
this.size = 12,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -22,10 +22,10 @@ class SendingIndicator extends StatelessWidget {
|
|||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (message.status == MessageSendingStatus.sent || message.status == null) {
|
if (message.status == MessageSendingStatus.sent) {
|
||||||
return StreamSvgIcon.check(
|
return StreamSvgIcon.check(
|
||||||
size: size,
|
size: size,
|
||||||
color: IconTheme.of(context).color.withOpacity(0.5),
|
color: IconTheme.of(context).color!.withOpacity(0.5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (message.status == MessageSendingStatus.sending ||
|
if (message.status == MessageSendingStatus.sending ||
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -7,7 +8,6 @@ import 'package:jiffy/jiffy.dart';
|
|||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'dart:ui' as ui;
|
|
||||||
|
|
||||||
/// Widget used to provide information about the chat to the widget tree
|
/// Widget used to provide information about the chat to the widget tree
|
||||||
///
|
///
|
||||||
@@ -32,8 +32,8 @@ import 'dart:ui' as ui;
|
|||||||
/// Use [StreamChat.of] to get the current [StreamChatState] instance.
|
/// Use [StreamChat.of] to get the current [StreamChatState] instance.
|
||||||
class StreamChat extends StatefulWidget {
|
class StreamChat extends StatefulWidget {
|
||||||
final StreamChatClient client;
|
final StreamChatClient client;
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
final StreamChatThemeData streamChatThemeData;
|
final StreamChatThemeData? streamChatThemeData;
|
||||||
|
|
||||||
/// The amount of time that will pass before disconnecting the client in the background
|
/// The amount of time that will pass before disconnecting the client in the background
|
||||||
final Duration backgroundKeepAlive;
|
final Duration backgroundKeepAlive;
|
||||||
@@ -41,12 +41,12 @@ class StreamChat extends StatefulWidget {
|
|||||||
/// Handler called whenever the [client] receives a new [Event] while the app
|
/// Handler called whenever the [client] receives a new [Event] while the app
|
||||||
/// is in background. Can be used to display various notifications depending
|
/// is in background. Can be used to display various notifications depending
|
||||||
/// upon the [Event.type]
|
/// upon the [Event.type]
|
||||||
final EventHandler onBackgroundEventReceived;
|
final EventHandler? onBackgroundEventReceived;
|
||||||
|
|
||||||
StreamChat({
|
StreamChat({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.client,
|
required this.client,
|
||||||
@required this.child,
|
required this.child,
|
||||||
this.streamChatThemeData,
|
this.streamChatThemeData,
|
||||||
this.onBackgroundEventReceived,
|
this.onBackgroundEventReceived,
|
||||||
this.backgroundKeepAlive = const Duration(minutes: 1),
|
this.backgroundKeepAlive = const Duration(minutes: 1),
|
||||||
@@ -59,7 +59,7 @@ class StreamChat extends StatefulWidget {
|
|||||||
|
|
||||||
/// Use this method to get the current [StreamChatState] instance
|
/// Use this method to get the current [StreamChatState] instance
|
||||||
static StreamChatState of(BuildContext context) {
|
static StreamChatState of(BuildContext context) {
|
||||||
StreamChatState streamChatState;
|
StreamChatState? streamChatState;
|
||||||
|
|
||||||
streamChatState = context.findAncestorStateOfType<StreamChatState>();
|
streamChatState = context.findAncestorStateOfType<StreamChatState>();
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class StreamChatState extends State<StreamChat> {
|
|||||||
client: client,
|
client: client,
|
||||||
onBackgroundEventReceived: widget.onBackgroundEventReceived,
|
onBackgroundEventReceived: widget.onBackgroundEventReceived,
|
||||||
backgroundKeepAlive: widget.backgroundKeepAlive,
|
backgroundKeepAlive: widget.backgroundKeepAlive,
|
||||||
child: widget.child,
|
child: widget.child!,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -107,17 +107,18 @@ class StreamChatState extends State<StreamChat> {
|
|||||||
|
|
||||||
StreamChatThemeData _getTheme(
|
StreamChatThemeData _getTheme(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
StreamChatThemeData themeData,
|
StreamChatThemeData? themeData,
|
||||||
) {
|
) {
|
||||||
final defaultTheme = StreamChatThemeData.getDefaultTheme(Theme.of(context));
|
final appBrightness = Theme.of(context).brightness;
|
||||||
return defaultTheme.merge(themeData) ?? themeData;
|
final defaultTheme = StreamChatThemeData(brightness: appBrightness);
|
||||||
|
return defaultTheme.merge(themeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current user
|
/// The current user
|
||||||
User get user => widget.client.state.user;
|
User? get user => widget.client.state.user;
|
||||||
|
|
||||||
/// The current user as a stream
|
/// The current user as a stream
|
||||||
Stream<User> get userStream => widget.client.state.userStream;
|
Stream<User?> get userStream => widget.client.state.userStream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_header.dart';
|
import 'package:stream_chat_flutter/src/channel_header.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_preview.dart';
|
import 'package:stream_chat_flutter/src/channel_preview.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_input.dart';
|
import 'package:stream_chat_flutter/src/message_input.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_icon.dart';
|
import 'package:stream_chat_flutter/src/reaction_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
/// Inherited widget providing the [StreamChatThemeData] to the widget tree
|
/// Inherited widget providing the [StreamChatThemeData] to the widget tree
|
||||||
@@ -13,9 +14,9 @@ class StreamChatTheme extends InheritedWidget {
|
|||||||
final StreamChatThemeData data;
|
final StreamChatThemeData data;
|
||||||
|
|
||||||
StreamChatTheme({
|
StreamChatTheme({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.data,
|
required this.data,
|
||||||
Widget child,
|
required Widget child,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
key: key,
|
||||||
child: child,
|
child: child,
|
||||||
@@ -31,13 +32,12 @@ class StreamChatTheme extends InheritedWidget {
|
|||||||
final streamChatTheme =
|
final streamChatTheme =
|
||||||
context.dependOnInheritedWidgetOfExactType<StreamChatTheme>();
|
context.dependOnInheritedWidgetOfExactType<StreamChatTheme>();
|
||||||
|
|
||||||
if (streamChatTheme == null) {
|
assert(
|
||||||
throw Exception(
|
streamChatTheme != null,
|
||||||
'You must have a StreamChatTheme widget at the top of your widget tree',
|
'You must have a StreamChatTheme widget at the top of your widget tree',
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return streamChatTheme.data;
|
return streamChatTheme!.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,49 +80,96 @@ class StreamChatThemeData {
|
|||||||
final List<ReactionIcon> reactionIcons;
|
final List<ReactionIcon> reactionIcons;
|
||||||
|
|
||||||
/// Create a theme from scratch
|
/// Create a theme from scratch
|
||||||
const StreamChatThemeData({
|
factory StreamChatThemeData({
|
||||||
this.textTheme,
|
Brightness? brightness,
|
||||||
this.colorTheme,
|
TextTheme? textTheme,
|
||||||
this.channelListHeaderTheme,
|
ColorTheme? colorTheme,
|
||||||
this.channelPreviewTheme,
|
ChannelListHeaderTheme? channelListHeaderTheme,
|
||||||
this.channelTheme,
|
ChannelPreviewTheme? channelPreviewTheme,
|
||||||
this.otherMessageTheme,
|
ChannelTheme? channelTheme,
|
||||||
this.ownMessageTheme,
|
MessageTheme? otherMessageTheme,
|
||||||
this.messageInputTheme,
|
MessageTheme? ownMessageTheme,
|
||||||
this.defaultChannelImage,
|
MessageInputTheme? messageInputTheme,
|
||||||
this.defaultUserImage,
|
Widget Function(BuildContext, Channel)? defaultChannelImage,
|
||||||
this.primaryIconTheme,
|
Widget Function(BuildContext, User)? defaultUserImage,
|
||||||
this.reactionIcons,
|
IconThemeData? primaryIconTheme,
|
||||||
|
List<ReactionIcon>? reactionIcons,
|
||||||
|
}) {
|
||||||
|
brightness ??= colorTheme?.brightness ?? Brightness.light;
|
||||||
|
final isDark = brightness == Brightness.dark;
|
||||||
|
textTheme ??= isDark ? TextTheme.dark() : TextTheme.light();
|
||||||
|
colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light();
|
||||||
|
|
||||||
|
final defaultData = fromColorAndTextTheme(
|
||||||
|
colorTheme,
|
||||||
|
textTheme,
|
||||||
|
);
|
||||||
|
|
||||||
|
final customizedData = defaultData.copyWith(
|
||||||
|
channelListHeaderTheme: channelListHeaderTheme,
|
||||||
|
channelPreviewTheme: channelPreviewTheme,
|
||||||
|
channelTheme: channelTheme,
|
||||||
|
otherMessageTheme: otherMessageTheme,
|
||||||
|
ownMessageTheme: ownMessageTheme,
|
||||||
|
messageInputTheme: messageInputTheme,
|
||||||
|
defaultChannelImage: defaultChannelImage,
|
||||||
|
defaultUserImage: defaultUserImage,
|
||||||
|
primaryIconTheme: primaryIconTheme,
|
||||||
|
reactionIcons: reactionIcons,
|
||||||
|
);
|
||||||
|
|
||||||
|
return defaultData.merge(customizedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory StreamChatThemeData.light() =>
|
||||||
|
StreamChatThemeData(brightness: Brightness.light);
|
||||||
|
|
||||||
|
factory StreamChatThemeData.dark() =>
|
||||||
|
StreamChatThemeData(brightness: Brightness.dark);
|
||||||
|
|
||||||
|
const StreamChatThemeData.raw({
|
||||||
|
required this.textTheme,
|
||||||
|
required this.colorTheme,
|
||||||
|
required this.channelListHeaderTheme,
|
||||||
|
required this.channelPreviewTheme,
|
||||||
|
required this.channelTheme,
|
||||||
|
required this.otherMessageTheme,
|
||||||
|
required this.ownMessageTheme,
|
||||||
|
required this.messageInputTheme,
|
||||||
|
required this.defaultChannelImage,
|
||||||
|
required this.defaultUserImage,
|
||||||
|
required this.primaryIconTheme,
|
||||||
|
required this.reactionIcons,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Create a theme from a Material [Theme]
|
/// Create a theme from a Material [Theme]
|
||||||
factory StreamChatThemeData.fromTheme(ThemeData theme) {
|
factory StreamChatThemeData.fromTheme(ThemeData theme) {
|
||||||
final defaultTheme = getDefaultTheme(theme);
|
final defaultTheme = StreamChatThemeData(brightness: theme.brightness);
|
||||||
final customizedTheme = StreamChatThemeData.fromColorAndTextTheme(
|
final customizedTheme = StreamChatThemeData.fromColorAndTextTheme(
|
||||||
defaultTheme.colorTheme.copyWith(
|
defaultTheme.colorTheme.copyWith(
|
||||||
accentBlue: theme.accentColor,
|
accentBlue: theme.accentColor,
|
||||||
),
|
),
|
||||||
defaultTheme.textTheme,
|
defaultTheme.textTheme,
|
||||||
);
|
);
|
||||||
return defaultTheme.merge(customizedTheme) ?? customizedTheme;
|
return defaultTheme.merge(customizedTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a copy of [StreamChatThemeData] with specified attributes overridden.
|
/// Creates a copy of [StreamChatThemeData] with specified attributes overridden.
|
||||||
StreamChatThemeData copyWith({
|
StreamChatThemeData copyWith({
|
||||||
TextTheme textTheme,
|
TextTheme? textTheme,
|
||||||
ColorTheme colorTheme,
|
ColorTheme? colorTheme,
|
||||||
ChannelPreviewTheme channelPreviewTheme,
|
ChannelPreviewTheme? channelPreviewTheme,
|
||||||
ChannelTheme channelTheme,
|
ChannelTheme? channelTheme,
|
||||||
MessageTheme ownMessageTheme,
|
MessageTheme? ownMessageTheme,
|
||||||
MessageTheme otherMessageTheme,
|
MessageTheme? otherMessageTheme,
|
||||||
MessageInputTheme messageInputTheme,
|
MessageInputTheme? messageInputTheme,
|
||||||
Widget Function(BuildContext, Channel) defaultChannelImage,
|
Widget Function(BuildContext, Channel)? defaultChannelImage,
|
||||||
Widget Function(BuildContext, User) defaultUserImage,
|
Widget Function(BuildContext, User)? defaultUserImage,
|
||||||
IconThemeData primaryIconTheme,
|
IconThemeData? primaryIconTheme,
|
||||||
ChannelListHeaderTheme channelListHeaderTheme,
|
ChannelListHeaderTheme? channelListHeaderTheme,
|
||||||
List<ReactionIcon> reactionIcons,
|
List<ReactionIcon>? reactionIcons,
|
||||||
}) =>
|
}) =>
|
||||||
StreamChatThemeData(
|
StreamChatThemeData.raw(
|
||||||
channelListHeaderTheme:
|
channelListHeaderTheme:
|
||||||
channelListHeaderTheme ?? this.channelListHeaderTheme,
|
channelListHeaderTheme ?? this.channelListHeaderTheme,
|
||||||
textTheme: textTheme ?? this.textTheme,
|
textTheme: textTheme ?? this.textTheme,
|
||||||
@@ -138,28 +185,21 @@ class StreamChatThemeData {
|
|||||||
reactionIcons: reactionIcons ?? this.reactionIcons,
|
reactionIcons: reactionIcons ?? this.reactionIcons,
|
||||||
);
|
);
|
||||||
|
|
||||||
StreamChatThemeData merge(StreamChatThemeData other) {
|
StreamChatThemeData merge(StreamChatThemeData? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
channelListHeaderTheme:
|
channelListHeaderTheme:
|
||||||
channelListHeaderTheme?.merge(other.channelListHeaderTheme) ??
|
channelListHeaderTheme.merge(other.channelListHeaderTheme),
|
||||||
other.channelListHeaderTheme,
|
textTheme: textTheme.merge(other.textTheme),
|
||||||
textTheme: textTheme?.merge(other.textTheme) ?? other.textTheme,
|
colorTheme: colorTheme.merge(other.colorTheme),
|
||||||
colorTheme: colorTheme?.merge(other.colorTheme) ?? other.colorTheme,
|
|
||||||
primaryIconTheme: other.primaryIconTheme,
|
primaryIconTheme: other.primaryIconTheme,
|
||||||
defaultChannelImage: other.defaultChannelImage,
|
defaultChannelImage: other.defaultChannelImage,
|
||||||
defaultUserImage: other.defaultUserImage,
|
defaultUserImage: other.defaultUserImage,
|
||||||
channelPreviewTheme:
|
channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme),
|
||||||
channelPreviewTheme?.merge(other.channelPreviewTheme) ??
|
channelTheme: channelTheme.merge(other.channelTheme),
|
||||||
other.channelPreviewTheme,
|
ownMessageTheme: ownMessageTheme.merge(other.ownMessageTheme),
|
||||||
channelTheme:
|
otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme),
|
||||||
channelTheme?.merge(other.channelTheme) ?? other.channelTheme,
|
messageInputTheme: messageInputTheme.merge(other.messageInputTheme),
|
||||||
ownMessageTheme: ownMessageTheme?.merge(other.ownMessageTheme) ??
|
|
||||||
other.ownMessageTheme,
|
|
||||||
otherMessageTheme: otherMessageTheme?.merge(other.otherMessageTheme) ??
|
|
||||||
other.otherMessageTheme,
|
|
||||||
messageInputTheme: messageInputTheme?.merge(other.messageInputTheme) ??
|
|
||||||
other.messageInputTheme,
|
|
||||||
reactionIcons: other.reactionIcons,
|
reactionIcons: other.reactionIcons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -169,7 +209,7 @@ class StreamChatThemeData {
|
|||||||
TextTheme textTheme,
|
TextTheme textTheme,
|
||||||
) {
|
) {
|
||||||
final accentColor = colorTheme.accentBlue;
|
final accentColor = colorTheme.accentBlue;
|
||||||
return StreamChatThemeData(
|
return StreamChatThemeData.raw(
|
||||||
textTheme: textTheme,
|
textTheme: textTheme,
|
||||||
colorTheme: colorTheme,
|
colorTheme: colorTheme,
|
||||||
primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)),
|
primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)),
|
||||||
@@ -314,22 +354,6 @@ class StreamChatThemeData {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the default Stream Chat theme
|
|
||||||
static StreamChatThemeData getDefaultTheme(ThemeData theme) {
|
|
||||||
final isDark = theme.brightness == Brightness.dark;
|
|
||||||
final textTheme = isDark ? TextTheme.dark() : TextTheme.light();
|
|
||||||
final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light();
|
|
||||||
return fromColorAndTextTheme(
|
|
||||||
colorTheme,
|
|
||||||
textTheme,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TextThemeType {
|
|
||||||
light,
|
|
||||||
dark,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextTheme {
|
class TextTheme {
|
||||||
@@ -427,17 +451,17 @@ class TextTheme {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TextTheme copyWith({
|
TextTheme copyWith({
|
||||||
TextThemeType type = TextThemeType.light,
|
Brightness brightness = Brightness.light,
|
||||||
TextStyle body,
|
TextStyle? body,
|
||||||
TextStyle title,
|
TextStyle? title,
|
||||||
TextStyle headlineBold,
|
TextStyle? headlineBold,
|
||||||
TextStyle headline,
|
TextStyle? headline,
|
||||||
TextStyle bodyBold,
|
TextStyle? bodyBold,
|
||||||
TextStyle footnoteBold,
|
TextStyle? footnoteBold,
|
||||||
TextStyle footnote,
|
TextStyle? footnote,
|
||||||
TextStyle captionBold,
|
TextStyle? captionBold,
|
||||||
}) {
|
}) {
|
||||||
return type == TextThemeType.light
|
return brightness == Brightness.light
|
||||||
? TextTheme.light(
|
? TextTheme.light(
|
||||||
body: body ?? this.body,
|
body: body ?? this.body,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
@@ -460,28 +484,21 @@ class TextTheme {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextTheme merge(TextTheme other) {
|
TextTheme merge(TextTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
body: body?.merge(other.body) ?? other.body,
|
body: body.merge(other.body),
|
||||||
title: title?.merge(other.title) ?? other.title,
|
title: title.merge(other.title),
|
||||||
headlineBold:
|
headlineBold: headlineBold.merge(other.headlineBold),
|
||||||
headlineBold?.merge(other.headlineBold) ?? other.headlineBold,
|
headline: headline.merge(other.headline),
|
||||||
headline: headline?.merge(other.headline) ?? other.headline,
|
bodyBold: bodyBold.merge(other.bodyBold),
|
||||||
bodyBold: bodyBold?.merge(other.bodyBold) ?? other.bodyBold,
|
footnoteBold: footnoteBold.merge(other.footnoteBold),
|
||||||
footnoteBold:
|
footnote: footnote.merge(other.footnote),
|
||||||
footnoteBold?.merge(other.footnoteBold) ?? other.footnoteBold,
|
captionBold: captionBold.merge(other.captionBold),
|
||||||
footnote: footnote?.merge(other.footnote) ?? other.footnote,
|
|
||||||
captionBold: captionBold?.merge(other.captionBold) ?? other.captionBold,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ColorThemeType {
|
|
||||||
light,
|
|
||||||
dark,
|
|
||||||
}
|
|
||||||
|
|
||||||
class ColorTheme {
|
class ColorTheme {
|
||||||
final Color black;
|
final Color black;
|
||||||
final Color grey;
|
final Color grey;
|
||||||
@@ -502,6 +519,7 @@ class ColorTheme {
|
|||||||
final Color overlay;
|
final Color overlay;
|
||||||
final Color overlayDark;
|
final Color overlayDark;
|
||||||
final Gradient bgGradient;
|
final Gradient bgGradient;
|
||||||
|
final Brightness brightness;
|
||||||
|
|
||||||
ColorTheme.light({
|
ColorTheme.light({
|
||||||
this.black = const Color(0xff000000),
|
this.black = const Color(0xff000000),
|
||||||
@@ -536,7 +554,7 @@ class ColorTheme {
|
|||||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
||||||
this.modalShadow = const Effect(
|
this.modalShadow = const Effect(
|
||||||
sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8.0),
|
sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8.0),
|
||||||
});
|
}) : brightness = Brightness.light;
|
||||||
|
|
||||||
ColorTheme.dark({
|
ColorTheme.dark({
|
||||||
this.black = const Color(0xffffffff),
|
this.black = const Color(0xffffffff),
|
||||||
@@ -551,13 +569,32 @@ class ColorTheme {
|
|||||||
this.accentRed = const Color(0xffFF3742),
|
this.accentRed = const Color(0xffFF3742),
|
||||||
this.accentGreen = const Color(0xff20E070),
|
this.accentGreen = const Color(0xff20E070),
|
||||||
this.borderTop = const Effect(
|
this.borderTop = const Effect(
|
||||||
sigmaX: 0, sigmaY: -1, color: Color(0xff141924), blur: 0.0),
|
sigmaX: 0,
|
||||||
|
sigmaY: -1,
|
||||||
|
color: Color(0xff141924),
|
||||||
|
blur: 0.0,
|
||||||
|
),
|
||||||
this.borderBottom = const Effect(
|
this.borderBottom = const Effect(
|
||||||
sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0, alpha: 1.0),
|
sigmaX: 0,
|
||||||
|
sigmaY: 1,
|
||||||
|
color: Color(0xff141924),
|
||||||
|
blur: 0.0,
|
||||||
|
alpha: 1.0,
|
||||||
|
),
|
||||||
this.shadowIconButton = const Effect(
|
this.shadowIconButton = const Effect(
|
||||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
sigmaX: 0,
|
||||||
|
sigmaY: 2,
|
||||||
|
color: Color(0xff000000),
|
||||||
|
alpha: 0.5,
|
||||||
|
blur: 4.0,
|
||||||
|
),
|
||||||
this.modalShadow = const Effect(
|
this.modalShadow = const Effect(
|
||||||
sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8.0),
|
sigmaX: 0,
|
||||||
|
sigmaY: 0,
|
||||||
|
color: Color(0xff000000),
|
||||||
|
alpha: 1,
|
||||||
|
blur: 8.0,
|
||||||
|
),
|
||||||
this.highlight = const Color(0xff302d22),
|
this.highlight = const Color(0xff302d22),
|
||||||
this.overlay = const Color.fromRGBO(0, 0, 0, 0.4),
|
this.overlay = const Color.fromRGBO(0, 0, 0, 0.4),
|
||||||
this.overlayDark = const Color.fromRGBO(255, 255, 255, 0.6),
|
this.overlayDark = const Color.fromRGBO(255, 255, 255, 0.6),
|
||||||
@@ -570,31 +607,31 @@ class ColorTheme {
|
|||||||
],
|
],
|
||||||
stops: [0, 1],
|
stops: [0, 1],
|
||||||
),
|
),
|
||||||
});
|
}) : brightness = Brightness.dark;
|
||||||
|
|
||||||
ColorTheme copyWith({
|
ColorTheme copyWith({
|
||||||
ColorThemeType type = ColorThemeType.light,
|
Brightness brightness = Brightness.light,
|
||||||
Color black,
|
Color? black,
|
||||||
Color grey,
|
Color? grey,
|
||||||
Color greyGainsboro,
|
Color? greyGainsboro,
|
||||||
Color greyWhisper,
|
Color? greyWhisper,
|
||||||
Color whiteSmoke,
|
Color? whiteSmoke,
|
||||||
Color whiteSnow,
|
Color? whiteSnow,
|
||||||
Color white,
|
Color? white,
|
||||||
Color blueAlice,
|
Color? blueAlice,
|
||||||
Color accentBlue,
|
Color? accentBlue,
|
||||||
Color accentRed,
|
Color? accentRed,
|
||||||
Color accentGreen,
|
Color? accentGreen,
|
||||||
Effect borderTop,
|
Effect? borderTop,
|
||||||
Effect borderBottom,
|
Effect? borderBottom,
|
||||||
Effect shadowIconButton,
|
Effect? shadowIconButton,
|
||||||
Effect modalShadow,
|
Effect? modalShadow,
|
||||||
Color highlight,
|
Color? highlight,
|
||||||
Color overlay,
|
Color? overlay,
|
||||||
Color overlayDark,
|
Color? overlayDark,
|
||||||
Gradient bgGradient,
|
Gradient? bgGradient,
|
||||||
}) {
|
}) {
|
||||||
return type == ColorThemeType.light
|
return brightness == Brightness.light
|
||||||
? ColorTheme.light(
|
? ColorTheme.light(
|
||||||
black: black ?? this.black,
|
black: black ?? this.black,
|
||||||
grey: grey ?? this.grey,
|
grey: grey ?? this.grey,
|
||||||
@@ -639,7 +676,7 @@ class ColorTheme {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTheme merge(ColorTheme other) {
|
ColorTheme merge(ColorTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
black: other.black,
|
black: other.black,
|
||||||
@@ -671,65 +708,77 @@ class ChannelTheme {
|
|||||||
final ChannelHeaderTheme channelHeaderTheme;
|
final ChannelHeaderTheme channelHeaderTheme;
|
||||||
|
|
||||||
ChannelTheme({
|
ChannelTheme({
|
||||||
this.channelHeaderTheme,
|
required this.channelHeaderTheme,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Creates a copy of [ChannelTheme] with specified attributes overridden.
|
/// Creates a copy of [ChannelTheme] with specified attributes overridden.
|
||||||
ChannelTheme copyWith({
|
ChannelTheme copyWith({
|
||||||
ChannelHeaderTheme channelHeaderTheme,
|
ChannelHeaderTheme? channelHeaderTheme,
|
||||||
}) =>
|
}) =>
|
||||||
ChannelTheme(
|
ChannelTheme(
|
||||||
channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme,
|
channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme,
|
||||||
);
|
);
|
||||||
|
|
||||||
ChannelTheme merge(ChannelTheme other) {
|
ChannelTheme merge(ChannelTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
channelHeaderTheme: channelHeaderTheme?.merge(other.channelHeaderTheme) ??
|
channelHeaderTheme: channelHeaderTheme.merge(other.channelHeaderTheme),
|
||||||
other.channelHeaderTheme,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AvatarTheme {
|
class AvatarTheme {
|
||||||
final BoxConstraints constraints;
|
final BoxConstraints? _constraints;
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius? _borderRadius;
|
||||||
|
|
||||||
|
BoxConstraints get constraints {
|
||||||
|
return _constraints ??
|
||||||
|
BoxConstraints.tightFor(
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BorderRadius get borderRadius {
|
||||||
|
return _borderRadius ?? BorderRadius.circular(20);
|
||||||
|
}
|
||||||
|
|
||||||
AvatarTheme({
|
AvatarTheme({
|
||||||
this.constraints,
|
BoxConstraints? constraints,
|
||||||
this.borderRadius,
|
BorderRadius? borderRadius,
|
||||||
});
|
}) : _constraints = constraints,
|
||||||
|
_borderRadius = borderRadius;
|
||||||
|
|
||||||
AvatarTheme copyWith({
|
AvatarTheme copyWith({
|
||||||
BoxConstraints constraints,
|
BoxConstraints? constraints,
|
||||||
BorderRadius borderRadius,
|
BorderRadius? borderRadius,
|
||||||
}) =>
|
}) =>
|
||||||
AvatarTheme(
|
AvatarTheme(
|
||||||
constraints: constraints ?? this.constraints,
|
constraints: constraints ?? _constraints,
|
||||||
borderRadius: borderRadius ?? this.borderRadius,
|
borderRadius: borderRadius ?? _borderRadius,
|
||||||
);
|
);
|
||||||
|
|
||||||
AvatarTheme merge(AvatarTheme other) {
|
AvatarTheme merge(AvatarTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
constraints: other.constraints,
|
constraints: other._constraints,
|
||||||
borderRadius: other.borderRadius,
|
borderRadius: other._borderRadius,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageTheme {
|
class MessageTheme {
|
||||||
final TextStyle messageText;
|
final TextStyle? messageText;
|
||||||
final TextStyle messageAuthor;
|
final TextStyle? messageAuthor;
|
||||||
final TextStyle messageLinks;
|
final TextStyle? messageLinks;
|
||||||
final TextStyle createdAt;
|
final TextStyle? createdAt;
|
||||||
final TextStyle replies;
|
final TextStyle? replies;
|
||||||
final Color messageBackgroundColor;
|
final Color? messageBackgroundColor;
|
||||||
final Color messageBorderColor;
|
final Color? messageBorderColor;
|
||||||
final Color reactionsBackgroundColor;
|
final Color? reactionsBackgroundColor;
|
||||||
final Color reactionsBorderColor;
|
final Color? reactionsBorderColor;
|
||||||
final Color reactionsMaskColor;
|
final Color? reactionsMaskColor;
|
||||||
final AvatarTheme avatarTheme;
|
final AvatarTheme? avatarTheme;
|
||||||
|
|
||||||
const MessageTheme({
|
const MessageTheme({
|
||||||
this.replies,
|
this.replies,
|
||||||
@@ -746,17 +795,17 @@ class MessageTheme {
|
|||||||
});
|
});
|
||||||
|
|
||||||
MessageTheme copyWith({
|
MessageTheme copyWith({
|
||||||
TextStyle messageText,
|
TextStyle? messageText,
|
||||||
TextStyle messageAuthor,
|
TextStyle? messageAuthor,
|
||||||
TextStyle messageLinks,
|
TextStyle? messageLinks,
|
||||||
TextStyle createdAt,
|
TextStyle? createdAt,
|
||||||
TextStyle replies,
|
TextStyle? replies,
|
||||||
Color messageBackgroundColor,
|
Color? messageBackgroundColor,
|
||||||
Color messageBorderColor,
|
Color? messageBorderColor,
|
||||||
AvatarTheme avatarTheme,
|
AvatarTheme? avatarTheme,
|
||||||
Color reactionsBackgroundColor,
|
Color? reactionsBackgroundColor,
|
||||||
Color reactionsBorderColor,
|
Color? reactionsBorderColor,
|
||||||
Color reactionsMaskColor,
|
Color? reactionsMaskColor,
|
||||||
}) =>
|
}) =>
|
||||||
MessageTheme(
|
MessageTheme(
|
||||||
messageText: messageText ?? this.messageText,
|
messageText: messageText ?? this.messageText,
|
||||||
@@ -774,7 +823,7 @@ class MessageTheme {
|
|||||||
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
|
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
MessageTheme merge(MessageTheme other) {
|
MessageTheme merge(MessageTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
messageText: messageText?.merge(other.messageText) ?? other.messageText,
|
messageText: messageText?.merge(other.messageText) ?? other.messageText,
|
||||||
@@ -795,12 +844,12 @@ class MessageTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChannelPreviewTheme {
|
class ChannelPreviewTheme {
|
||||||
final TextStyle title;
|
final TextStyle? title;
|
||||||
final TextStyle subtitle;
|
final TextStyle? subtitle;
|
||||||
final TextStyle lastMessageAt;
|
final TextStyle? lastMessageAt;
|
||||||
final AvatarTheme avatarTheme;
|
final AvatarTheme? avatarTheme;
|
||||||
final Color unreadCounterColor;
|
final Color? unreadCounterColor;
|
||||||
final double indicatorIconSize;
|
final double? indicatorIconSize;
|
||||||
|
|
||||||
const ChannelPreviewTheme({
|
const ChannelPreviewTheme({
|
||||||
this.title,
|
this.title,
|
||||||
@@ -812,12 +861,12 @@ class ChannelPreviewTheme {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ChannelPreviewTheme copyWith({
|
ChannelPreviewTheme copyWith({
|
||||||
TextStyle title,
|
TextStyle? title,
|
||||||
TextStyle subtitle,
|
TextStyle? subtitle,
|
||||||
TextStyle lastMessageAt,
|
TextStyle? lastMessageAt,
|
||||||
AvatarTheme avatarTheme,
|
AvatarTheme? avatarTheme,
|
||||||
Color unreadCounterColor,
|
Color? unreadCounterColor,
|
||||||
double indicatorIconSize,
|
double? indicatorIconSize,
|
||||||
}) =>
|
}) =>
|
||||||
ChannelPreviewTheme(
|
ChannelPreviewTheme(
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
@@ -828,7 +877,7 @@ class ChannelPreviewTheme {
|
|||||||
indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize,
|
indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
ChannelPreviewTheme merge(ChannelPreviewTheme other) {
|
ChannelPreviewTheme merge(ChannelPreviewTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
title: title?.merge(other.title) ?? other.title,
|
title: title?.merge(other.title) ?? other.title,
|
||||||
@@ -842,10 +891,10 @@ class ChannelPreviewTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChannelHeaderTheme {
|
class ChannelHeaderTheme {
|
||||||
final TextStyle title;
|
final TextStyle? title;
|
||||||
final TextStyle subtitle;
|
final TextStyle? subtitle;
|
||||||
final AvatarTheme avatarTheme;
|
final AvatarTheme? avatarTheme;
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
const ChannelHeaderTheme({
|
const ChannelHeaderTheme({
|
||||||
this.title,
|
this.title,
|
||||||
@@ -855,10 +904,10 @@ class ChannelHeaderTheme {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ChannelHeaderTheme copyWith({
|
ChannelHeaderTheme copyWith({
|
||||||
TextStyle title,
|
TextStyle? title,
|
||||||
TextStyle subtitle,
|
TextStyle? subtitle,
|
||||||
AvatarTheme avatarTheme,
|
AvatarTheme? avatarTheme,
|
||||||
Color color,
|
Color? color,
|
||||||
}) =>
|
}) =>
|
||||||
ChannelHeaderTheme(
|
ChannelHeaderTheme(
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
@@ -867,7 +916,7 @@ class ChannelHeaderTheme {
|
|||||||
color: color ?? this.color,
|
color: color ?? this.color,
|
||||||
);
|
);
|
||||||
|
|
||||||
ChannelHeaderTheme merge(ChannelHeaderTheme other) {
|
ChannelHeaderTheme merge(ChannelHeaderTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
title: title?.merge(other.title) ?? other.title,
|
title: title?.merge(other.title) ?? other.title,
|
||||||
@@ -881,13 +930,13 @@ class ChannelHeaderTheme {
|
|||||||
/// Theme dedicated to the [ChannelListHeader]
|
/// Theme dedicated to the [ChannelListHeader]
|
||||||
class ChannelListHeaderTheme {
|
class ChannelListHeaderTheme {
|
||||||
/// Style of the title text
|
/// Style of the title text
|
||||||
final TextStyle title;
|
final TextStyle? title;
|
||||||
|
|
||||||
/// Theme dedicated to the userAvatar
|
/// Theme dedicated to the userAvatar
|
||||||
final AvatarTheme avatarTheme;
|
final AvatarTheme? avatarTheme;
|
||||||
|
|
||||||
/// Background color of the appbar
|
/// Background color of the appbar
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
/// Returns a new [ChannelListHeaderTheme]
|
/// Returns a new [ChannelListHeaderTheme]
|
||||||
const ChannelListHeaderTheme({
|
const ChannelListHeaderTheme({
|
||||||
@@ -898,9 +947,9 @@ class ChannelListHeaderTheme {
|
|||||||
|
|
||||||
/// Returns a new [ChannelListHeaderTheme] replacing some of its properties
|
/// Returns a new [ChannelListHeaderTheme] replacing some of its properties
|
||||||
ChannelListHeaderTheme copyWith({
|
ChannelListHeaderTheme copyWith({
|
||||||
TextStyle title,
|
TextStyle? title,
|
||||||
AvatarTheme avatarTheme,
|
AvatarTheme? avatarTheme,
|
||||||
Color color,
|
Color? color,
|
||||||
}) =>
|
}) =>
|
||||||
ChannelListHeaderTheme(
|
ChannelListHeaderTheme(
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
@@ -909,7 +958,7 @@ class ChannelListHeaderTheme {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Merges [this] [ChannelListHeaderTheme] with the [other]
|
/// Merges [this] [ChannelListHeaderTheme] with the [other]
|
||||||
ChannelListHeaderTheme merge(ChannelListHeaderTheme other) {
|
ChannelListHeaderTheme merge(ChannelListHeaderTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
title: title?.merge(other.title) ?? other.title,
|
title: title?.merge(other.title) ?? other.title,
|
||||||
@@ -922,40 +971,40 @@ class ChannelListHeaderTheme {
|
|||||||
/// Defines the theme dedicated to the [MessageInput] widget
|
/// Defines the theme dedicated to the [MessageInput] widget
|
||||||
class MessageInputTheme {
|
class MessageInputTheme {
|
||||||
/// Duration of the [MessageInput] send button animation
|
/// Duration of the [MessageInput] send button animation
|
||||||
final Duration sendAnimationDuration;
|
final Duration? sendAnimationDuration;
|
||||||
|
|
||||||
/// Background color of [MessageInput] send button
|
/// Background color of [MessageInput] send button
|
||||||
final Color sendButtonColor;
|
final Color? sendButtonColor;
|
||||||
|
|
||||||
/// Background color of [MessageInput] action buttons
|
/// Background color of [MessageInput] action buttons
|
||||||
final Color actionButtonColor;
|
final Color? actionButtonColor;
|
||||||
|
|
||||||
/// Background color of [MessageInput] send button
|
/// Background color of [MessageInput] send button
|
||||||
final Color sendButtonIdleColor;
|
final Color? sendButtonIdleColor;
|
||||||
|
|
||||||
/// Background color of [MessageInput] action buttons
|
/// Background color of [MessageInput] action buttons
|
||||||
final Color actionButtonIdleColor;
|
final Color? actionButtonIdleColor;
|
||||||
|
|
||||||
/// Background color of [MessageInput] expand button
|
/// Background color of [MessageInput] expand button
|
||||||
final Color expandButtonColor;
|
final Color? expandButtonColor;
|
||||||
|
|
||||||
/// Background color of [MessageInput]
|
/// Background color of [MessageInput]
|
||||||
final Color inputBackground;
|
final Color? inputBackground;
|
||||||
|
|
||||||
/// TextStyle of [MessageInput]
|
/// TextStyle of [MessageInput]
|
||||||
final TextStyle inputTextStyle;
|
final TextStyle? inputTextStyle;
|
||||||
|
|
||||||
/// InputDecoration of [MessageInput]
|
/// InputDecoration of [MessageInput]
|
||||||
final InputDecoration inputDecoration;
|
final InputDecoration? inputDecoration;
|
||||||
|
|
||||||
/// Border gradient when the [MessageInput] is not focused
|
/// Border gradient when the [MessageInput] is not focused
|
||||||
final Gradient idleBorderGradient;
|
final Gradient? idleBorderGradient;
|
||||||
|
|
||||||
/// Border gradient when the [MessageInput] is focused
|
/// Border gradient when the [MessageInput] is focused
|
||||||
final Gradient activeBorderGradient;
|
final Gradient? activeBorderGradient;
|
||||||
|
|
||||||
/// Border radius of [MessageInput]
|
/// Border radius of [MessageInput]
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
|
|
||||||
/// Returns a new [MessageInputTheme]
|
/// Returns a new [MessageInputTheme]
|
||||||
const MessageInputTheme({
|
const MessageInputTheme({
|
||||||
@@ -975,18 +1024,18 @@ class MessageInputTheme {
|
|||||||
|
|
||||||
/// Returns a new [MessageInputTheme] replacing some of its properties
|
/// Returns a new [MessageInputTheme] replacing some of its properties
|
||||||
MessageInputTheme copyWith({
|
MessageInputTheme copyWith({
|
||||||
Duration sendAnimationDuration,
|
Duration? sendAnimationDuration,
|
||||||
Color inputBackground,
|
Color? inputBackground,
|
||||||
Color actionButtonColor,
|
Color? actionButtonColor,
|
||||||
Color sendButtonColor,
|
Color? sendButtonColor,
|
||||||
Color actionButtonIdleColor,
|
Color? actionButtonIdleColor,
|
||||||
Color sendButtonIdleColor,
|
Color? sendButtonIdleColor,
|
||||||
Color expandButtonColor,
|
Color? expandButtonColor,
|
||||||
TextStyle inputTextStyle,
|
TextStyle? inputTextStyle,
|
||||||
InputDecoration inputDecoration,
|
InputDecoration? inputDecoration,
|
||||||
Gradient activeBorderGradient,
|
Gradient? activeBorderGradient,
|
||||||
Gradient idleBorderGradient,
|
Gradient? idleBorderGradient,
|
||||||
BorderRadius borderRadius,
|
BorderRadius? borderRadius,
|
||||||
}) =>
|
}) =>
|
||||||
MessageInputTheme(
|
MessageInputTheme(
|
||||||
sendAnimationDuration:
|
sendAnimationDuration:
|
||||||
@@ -1006,7 +1055,7 @@ class MessageInputTheme {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Merges [this] [MessageInputTheme] with the [other]
|
/// Merges [this] [MessageInputTheme] with the [other]
|
||||||
MessageInputTheme merge(MessageInputTheme other) {
|
MessageInputTheme merge(MessageInputTheme? other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
sendAnimationDuration: other.sendAnimationDuration,
|
sendAnimationDuration: other.sendAnimationDuration,
|
||||||
@@ -1027,11 +1076,11 @@ class MessageInputTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Effect {
|
class Effect {
|
||||||
final double sigmaX;
|
final double? sigmaX;
|
||||||
final double sigmaY;
|
final double? sigmaY;
|
||||||
final Color color;
|
final Color? color;
|
||||||
final double alpha;
|
final double? alpha;
|
||||||
final double blur;
|
final double? blur;
|
||||||
|
|
||||||
const Effect({
|
const Effect({
|
||||||
this.sigmaX,
|
this.sigmaX,
|
||||||
@@ -1042,17 +1091,17 @@ class Effect {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Effect copyWith({
|
Effect copyWith({
|
||||||
double sigmaX,
|
double? sigmaX,
|
||||||
double sigmaY,
|
double? sigmaY,
|
||||||
Color color,
|
Color? color,
|
||||||
double alpha,
|
double? alpha,
|
||||||
double blur,
|
double? blur,
|
||||||
}) =>
|
}) =>
|
||||||
Effect(
|
Effect(
|
||||||
sigmaX: sigmaX ?? this.sigmaX,
|
sigmaX: sigmaX ?? this.sigmaX,
|
||||||
sigmaY: sigmaY ?? this.sigmaY,
|
sigmaY: sigmaY ?? this.sigmaY,
|
||||||
color: color ?? this.color,
|
color: color ?? this.color,
|
||||||
alpha: color ?? this.alpha,
|
alpha: color as double? ?? this.alpha,
|
||||||
blur: blur ?? this.blur,
|
blur: blur ?? this.blur,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ class StreamNeumorphicButton extends StatelessWidget {
|
|||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
|
||||||
const StreamNeumorphicButton({
|
const StreamNeumorphicButton({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.child,
|
required this.child,
|
||||||
this.backgroundColor = Colors.white,
|
this.backgroundColor = Colors.white,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class StreamNeumorphicButton extends StatelessWidget {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.grey[700],
|
color: Colors.grey.shade700,
|
||||||
offset: Offset(0, 1.0),
|
offset: Offset(0, 1.0),
|
||||||
blurRadius: 0.5,
|
blurRadius: 0.5,
|
||||||
spreadRadius: 0,
|
spreadRadius: 0,
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class StreamSvgIcon extends StatelessWidget {
|
class StreamSvgIcon extends StatelessWidget {
|
||||||
final String assetName;
|
final String? assetName;
|
||||||
final double width;
|
final double? width;
|
||||||
final double height;
|
final double? height;
|
||||||
final Color color;
|
final Color? color;
|
||||||
|
|
||||||
const StreamSvgIcon({
|
const StreamSvgIcon({
|
||||||
this.assetName,
|
this.assetName,
|
||||||
@@ -30,8 +30,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.settings({
|
factory StreamSvgIcon.settings({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'settings.svg',
|
assetName: 'settings.svg',
|
||||||
@@ -42,8 +42,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.down({
|
factory StreamSvgIcon.down({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_down.svg',
|
assetName: 'Icon_down.svg',
|
||||||
@@ -54,8 +54,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.attach({
|
factory StreamSvgIcon.attach({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_attach.svg',
|
assetName: 'Icon_attach.svg',
|
||||||
@@ -66,8 +66,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.smile({
|
factory StreamSvgIcon.smile({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_smile.svg',
|
assetName: 'Icon_smile.svg',
|
||||||
@@ -78,8 +78,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.mentions({
|
factory StreamSvgIcon.mentions({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'mentions.svg',
|
assetName: 'mentions.svg',
|
||||||
@@ -90,8 +90,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.record({
|
factory StreamSvgIcon.record({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_record.svg',
|
assetName: 'Icon_record.svg',
|
||||||
@@ -102,8 +102,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.camera({
|
factory StreamSvgIcon.camera({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_camera.svg',
|
assetName: 'Icon_camera.svg',
|
||||||
@@ -114,8 +114,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.files({
|
factory StreamSvgIcon.files({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'files.svg',
|
assetName: 'files.svg',
|
||||||
@@ -126,8 +126,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.pictures({
|
factory StreamSvgIcon.pictures({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'pictures.svg',
|
assetName: 'pictures.svg',
|
||||||
@@ -138,8 +138,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.left({
|
factory StreamSvgIcon.left({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_left.svg',
|
assetName: 'Icon_left.svg',
|
||||||
@@ -150,8 +150,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.user({
|
factory StreamSvgIcon.user({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_user.svg',
|
assetName: 'Icon_user.svg',
|
||||||
@@ -162,8 +162,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.userAdd({
|
factory StreamSvgIcon.userAdd({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_User_add.svg',
|
assetName: 'Icon_User_add.svg',
|
||||||
@@ -174,8 +174,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.check({
|
factory StreamSvgIcon.check({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_check.svg',
|
assetName: 'Icon_check.svg',
|
||||||
@@ -186,8 +186,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.checkAll({
|
factory StreamSvgIcon.checkAll({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_check_all.svg',
|
assetName: 'Icon_check_all.svg',
|
||||||
@@ -198,8 +198,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.checkSend({
|
factory StreamSvgIcon.checkSend({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_check_send.svg',
|
assetName: 'Icon_check_send.svg',
|
||||||
@@ -210,8 +210,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.penWrite({
|
factory StreamSvgIcon.penWrite({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_pen-write.svg',
|
assetName: 'Icon_pen-write.svg',
|
||||||
@@ -222,8 +222,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.contacts({
|
factory StreamSvgIcon.contacts({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_contacts.svg',
|
assetName: 'Icon_contacts.svg',
|
||||||
@@ -234,8 +234,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.close({
|
factory StreamSvgIcon.close({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_close.svg',
|
assetName: 'Icon_close.svg',
|
||||||
@@ -246,8 +246,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.search({
|
factory StreamSvgIcon.search({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_search.svg',
|
assetName: 'Icon_search.svg',
|
||||||
@@ -258,8 +258,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.right({
|
factory StreamSvgIcon.right({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_right.svg',
|
assetName: 'Icon_right.svg',
|
||||||
@@ -270,8 +270,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.mute({
|
factory StreamSvgIcon.mute({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_mute.svg',
|
assetName: 'Icon_mute.svg',
|
||||||
@@ -282,8 +282,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.userRemove({
|
factory StreamSvgIcon.userRemove({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_User_deselect.svg',
|
assetName: 'Icon_User_deselect.svg',
|
||||||
@@ -294,8 +294,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.lightning({
|
factory StreamSvgIcon.lightning({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_lightning-command runner.svg',
|
assetName: 'Icon_lightning-command runner.svg',
|
||||||
@@ -306,8 +306,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.emptyCircleLeft({
|
factory StreamSvgIcon.emptyCircleLeft({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_empty_circle_left.svg',
|
assetName: 'Icon_empty_circle_left.svg',
|
||||||
@@ -318,8 +318,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.message({
|
factory StreamSvgIcon.message({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_message.svg',
|
assetName: 'Icon_message.svg',
|
||||||
@@ -330,8 +330,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.thread({
|
factory StreamSvgIcon.thread({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_Thread_Reply.svg',
|
assetName: 'Icon_Thread_Reply.svg',
|
||||||
@@ -342,8 +342,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.reply({
|
factory StreamSvgIcon.reply({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_curve_line_left_up_big.svg',
|
assetName: 'Icon_curve_line_left_up_big.svg',
|
||||||
@@ -354,8 +354,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.edit({
|
factory StreamSvgIcon.edit({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_edit.svg',
|
assetName: 'Icon_edit.svg',
|
||||||
@@ -366,8 +366,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.download({
|
factory StreamSvgIcon.download({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_download.svg',
|
assetName: 'Icon_download.svg',
|
||||||
@@ -378,8 +378,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.cloudDownload({
|
factory StreamSvgIcon.cloudDownload({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_cloud_download.svg',
|
assetName: 'Icon_cloud_download.svg',
|
||||||
@@ -390,8 +390,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.copy({
|
factory StreamSvgIcon.copy({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_copy.svg',
|
assetName: 'Icon_copy.svg',
|
||||||
@@ -402,8 +402,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.delete({
|
factory StreamSvgIcon.delete({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_delete.svg',
|
assetName: 'Icon_delete.svg',
|
||||||
@@ -414,8 +414,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.eye({
|
factory StreamSvgIcon.eye({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_eye-off.svg',
|
assetName: 'Icon_eye-off.svg',
|
||||||
@@ -426,8 +426,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.arrowRight({
|
factory StreamSvgIcon.arrowRight({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_arrow_right.svg',
|
assetName: 'Icon_arrow_right.svg',
|
||||||
@@ -438,8 +438,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.closeSmall({
|
factory StreamSvgIcon.closeSmall({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_close_sml.svg',
|
assetName: 'Icon_close_sml.svg',
|
||||||
@@ -450,8 +450,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconCurveLineLeftUp({
|
factory StreamSvgIcon.iconCurveLineLeftUp({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_curve_line_left_up.svg',
|
assetName: 'Icon_curve_line_left_up.svg',
|
||||||
@@ -462,8 +462,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconMoon({
|
factory StreamSvgIcon.iconMoon({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'icon_moon.svg',
|
assetName: 'icon_moon.svg',
|
||||||
@@ -474,8 +474,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconShare({
|
factory StreamSvgIcon.iconShare({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'icon_SHARE.svg',
|
assetName: 'icon_SHARE.svg',
|
||||||
@@ -486,8 +486,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconGrid({
|
factory StreamSvgIcon.iconGrid({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_grid.svg',
|
assetName: 'Icon_grid.svg',
|
||||||
@@ -498,8 +498,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconSendMessage({
|
factory StreamSvgIcon.iconSendMessage({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_send_message.svg',
|
assetName: 'Icon_send_message.svg',
|
||||||
@@ -510,8 +510,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconMenuPoint({
|
factory StreamSvgIcon.iconMenuPoint({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_menu_point_v.svg',
|
assetName: 'Icon_menu_point_v.svg',
|
||||||
@@ -522,8 +522,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconSave({
|
factory StreamSvgIcon.iconSave({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_save.svg',
|
assetName: 'Icon_save.svg',
|
||||||
@@ -534,8 +534,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.shareArrow({
|
factory StreamSvgIcon.shareArrow({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'share_arrow.svg',
|
assetName: 'share_arrow.svg',
|
||||||
@@ -546,8 +546,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetype7z({
|
factory StreamSvgIcon.filetype7z({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_7z.svg',
|
assetName: 'filetype_7z.svg',
|
||||||
@@ -558,8 +558,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeCsv({
|
factory StreamSvgIcon.filetypeCsv({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_CSV.svg',
|
assetName: 'filetype_CSV.svg',
|
||||||
@@ -570,8 +570,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeDoc({
|
factory StreamSvgIcon.filetypeDoc({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_DOC.svg',
|
assetName: 'filetype_DOC.svg',
|
||||||
@@ -582,8 +582,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeDocx({
|
factory StreamSvgIcon.filetypeDocx({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_DOCX.svg',
|
assetName: 'filetype_DOCX.svg',
|
||||||
@@ -594,8 +594,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeGeneric({
|
factory StreamSvgIcon.filetypeGeneric({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_Generic.svg',
|
assetName: 'filetype_Generic.svg',
|
||||||
@@ -606,8 +606,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeHtml({
|
factory StreamSvgIcon.filetypeHtml({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_html.svg',
|
assetName: 'filetype_html.svg',
|
||||||
@@ -618,8 +618,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeMd({
|
factory StreamSvgIcon.filetypeMd({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_MD.svg',
|
assetName: 'filetype_MD.svg',
|
||||||
@@ -630,8 +630,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeOdt({
|
factory StreamSvgIcon.filetypeOdt({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_ODT.svg',
|
assetName: 'filetype_ODT.svg',
|
||||||
@@ -642,8 +642,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypePdf({
|
factory StreamSvgIcon.filetypePdf({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_PDF.svg',
|
assetName: 'filetype_PDF.svg',
|
||||||
@@ -654,8 +654,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypePpt({
|
factory StreamSvgIcon.filetypePpt({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_PPT.svg',
|
assetName: 'filetype_PPT.svg',
|
||||||
@@ -666,8 +666,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypePptx({
|
factory StreamSvgIcon.filetypePptx({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_PPTX.svg',
|
assetName: 'filetype_PPTX.svg',
|
||||||
@@ -678,8 +678,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeRar({
|
factory StreamSvgIcon.filetypeRar({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_RAR.svg',
|
assetName: 'filetype_RAR.svg',
|
||||||
@@ -690,8 +690,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeRtf({
|
factory StreamSvgIcon.filetypeRtf({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_RTF.svg',
|
assetName: 'filetype_RTF.svg',
|
||||||
@@ -702,8 +702,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeTar({
|
factory StreamSvgIcon.filetypeTar({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_TAR.svg',
|
assetName: 'filetype_TAR.svg',
|
||||||
@@ -714,8 +714,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeTxt({
|
factory StreamSvgIcon.filetypeTxt({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_TXT.svg',
|
assetName: 'filetype_TXT.svg',
|
||||||
@@ -726,8 +726,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeXls({
|
factory StreamSvgIcon.filetypeXls({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_XLS.svg',
|
assetName: 'filetype_XLS.svg',
|
||||||
@@ -738,8 +738,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeXlsx({
|
factory StreamSvgIcon.filetypeXlsx({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_XLSX.svg',
|
assetName: 'filetype_XLSX.svg',
|
||||||
@@ -750,8 +750,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.filetypeZip({
|
factory StreamSvgIcon.filetypeZip({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'filetype_ZIP.svg',
|
assetName: 'filetype_ZIP.svg',
|
||||||
@@ -762,8 +762,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconGroup({
|
factory StreamSvgIcon.iconGroup({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_group.svg',
|
assetName: 'Icon_group.svg',
|
||||||
@@ -774,8 +774,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconNotification({
|
factory StreamSvgIcon.iconNotification({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_notification.svg',
|
assetName: 'Icon_notification.svg',
|
||||||
@@ -786,8 +786,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconUserDelete({
|
factory StreamSvgIcon.iconUserDelete({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_user_delete.svg',
|
assetName: 'Icon_user_delete.svg',
|
||||||
@@ -798,8 +798,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.error({
|
factory StreamSvgIcon.error({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_error.svg',
|
assetName: 'Icon_error.svg',
|
||||||
@@ -810,8 +810,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.circleUp({
|
factory StreamSvgIcon.circleUp({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_circle_up.svg',
|
assetName: 'Icon_circle_up.svg',
|
||||||
@@ -822,8 +822,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconUserSettings({
|
factory StreamSvgIcon.iconUserSettings({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'Icon_user_settings.svg',
|
assetName: 'Icon_user_settings.svg',
|
||||||
@@ -834,8 +834,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.giphyIcon({
|
factory StreamSvgIcon.giphyIcon({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'giphy_icon.svg',
|
assetName: 'giphy_icon.svg',
|
||||||
@@ -846,8 +846,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.imgur({
|
factory StreamSvgIcon.imgur({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'imgur.svg',
|
assetName: 'imgur.svg',
|
||||||
@@ -858,8 +858,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.volumeUp({
|
factory StreamSvgIcon.volumeUp({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'volume-up.svg',
|
assetName: 'volume-up.svg',
|
||||||
@@ -870,8 +870,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.flag({
|
factory StreamSvgIcon.flag({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'flag.svg',
|
assetName: 'flag.svg',
|
||||||
@@ -882,8 +882,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.iconFlag({
|
factory StreamSvgIcon.iconFlag({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'icon_flag.svg',
|
assetName: 'icon_flag.svg',
|
||||||
@@ -894,8 +894,8 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factory StreamSvgIcon.retry({
|
factory StreamSvgIcon.retry({
|
||||||
double size,
|
double? size,
|
||||||
Color color,
|
Color? color,
|
||||||
}) {
|
}) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: 'icon_retry.svg',
|
assetName: 'icon_retry.svg',
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import 'stream_chat_theme.dart';
|
|||||||
class Swipeable extends StatefulWidget {
|
class Swipeable extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Widget backgroundIcon;
|
final Widget backgroundIcon;
|
||||||
final VoidCallback onSwipeStart;
|
final VoidCallback? onSwipeStart;
|
||||||
final VoidCallback onSwipeCancel;
|
final VoidCallback? onSwipeCancel;
|
||||||
final VoidCallback onSwipeEnd;
|
final VoidCallback? onSwipeEnd;
|
||||||
final double threshold;
|
final double threshold;
|
||||||
|
|
||||||
///
|
///
|
||||||
const Swipeable({
|
const Swipeable({
|
||||||
@required this.child,
|
required this.child,
|
||||||
@required this.backgroundIcon,
|
required this.backgroundIcon,
|
||||||
this.onSwipeStart,
|
this.onSwipeStart,
|
||||||
this.onSwipeCancel,
|
this.onSwipeCancel,
|
||||||
this.onSwipeEnd,
|
this.onSwipeEnd,
|
||||||
@@ -29,11 +29,11 @@ class Swipeable extends StatefulWidget {
|
|||||||
|
|
||||||
class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
||||||
double _dragExtent = 0.0;
|
double _dragExtent = 0.0;
|
||||||
AnimationController _moveController;
|
late AnimationController _moveController;
|
||||||
AnimationController _iconMoveController;
|
late AnimationController _iconMoveController;
|
||||||
Animation<Offset> _moveAnimation;
|
late Animation<Offset> _moveAnimation;
|
||||||
Animation<Offset> _iconTransitionAnimation;
|
late Animation<Offset> _iconTransitionAnimation;
|
||||||
Animation<double> _iconFadeAnimation;
|
late Animation<double> _iconFadeAnimation;
|
||||||
bool _pastThreshold = false;
|
bool _pastThreshold = false;
|
||||||
|
|
||||||
final _animationDuration = const Duration(milliseconds: 200);
|
final _animationDuration = const Duration(milliseconds: 200);
|
||||||
@@ -67,18 +67,18 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
void _handleDragStart(DragStartDetails details) {
|
void _handleDragStart(DragStartDetails details) {
|
||||||
if (widget.onSwipeStart != null) {
|
if (widget.onSwipeStart != null) {
|
||||||
widget.onSwipeStart();
|
widget.onSwipeStart!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragUpdate(DragUpdateDetails details) {
|
void _handleDragUpdate(DragUpdateDetails details) {
|
||||||
final delta = details.primaryDelta;
|
final delta = details.primaryDelta!;
|
||||||
_dragExtent += delta;
|
_dragExtent += delta;
|
||||||
|
|
||||||
if (_dragExtent.isNegative) return;
|
if (_dragExtent.isNegative) return;
|
||||||
|
|
||||||
var movePastThresholdPixels = widget.threshold;
|
var movePastThresholdPixels = widget.threshold;
|
||||||
var newPos = _dragExtent.abs() / context.size.width;
|
var newPos = _dragExtent.abs() / context.size!.width;
|
||||||
|
|
||||||
if (_dragExtent.abs() > movePastThresholdPixels) {
|
if (_dragExtent.abs() > movePastThresholdPixels) {
|
||||||
// how many "thresholds" past the threshold we are. 1 = the threshold 2
|
// how many "thresholds" past the threshold we are. 1 = the threshold 2
|
||||||
@@ -90,7 +90,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
|||||||
var reducedThreshold = math.pow(n, 0.3);
|
var reducedThreshold = math.pow(n, 0.3);
|
||||||
|
|
||||||
var adjustedPixelPos = movePastThresholdPixels * reducedThreshold;
|
var adjustedPixelPos = movePastThresholdPixels * reducedThreshold;
|
||||||
newPos = adjustedPixelPos / context.size.width;
|
newPos = adjustedPixelPos / context.size!.width;
|
||||||
|
|
||||||
if (_dragExtent > 0 && !_pastThreshold) {
|
if (_dragExtent > 0 && !_pastThreshold) {
|
||||||
_iconMoveController.value = 1;
|
_iconMoveController.value = 1;
|
||||||
@@ -100,7 +100,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
|||||||
// Send a cancel event if the user has swiped back underneath the
|
// Send a cancel event if the user has swiped back underneath the
|
||||||
// threshold
|
// threshold
|
||||||
if (_pastThreshold && widget.onSwipeCancel != null) {
|
if (_pastThreshold && widget.onSwipeCancel != null) {
|
||||||
widget.onSwipeCancel();
|
widget.onSwipeCancel!();
|
||||||
}
|
}
|
||||||
_pastThreshold = false;
|
_pastThreshold = false;
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
|
|||||||
_iconMoveController.animateTo(0.0, duration: _animationDuration);
|
_iconMoveController.animateTo(0.0, duration: _animationDuration);
|
||||||
_dragExtent = 0.0;
|
_dragExtent = 0.0;
|
||||||
if (_pastThreshold && widget.onSwipeEnd != null) {
|
if (_pastThreshold && widget.onSwipeEnd != null) {
|
||||||
widget.onSwipeEnd();
|
widget.onSwipeEnd!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ class SystemMessage extends StatelessWidget {
|
|||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
/// The function called when tapping on the message when the message is not failed
|
/// The function called when tapping on the message when the message is not failed
|
||||||
final void Function(Message) onMessageTap;
|
final void Function(Message)? onMessageTap;
|
||||||
|
|
||||||
const SystemMessage({
|
const SystemMessage({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.message,
|
required this.message,
|
||||||
this.onMessageTap,
|
this.onMessageTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -22,11 +22,11 @@ class SystemMessage extends StatelessWidget {
|
|||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onMessageTap != null) {
|
if (onMessageTap != null) {
|
||||||
onMessageTap(message);
|
onMessageTap!(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
message.text,
|
message.text!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: theme.textTheme.captionBold.copyWith(
|
style: theme.textTheme.captionBold.copyWith(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import 'back_button.dart';
|
import 'back_button.dart';
|
||||||
import 'channel_name.dart';
|
import 'channel_name.dart';
|
||||||
@@ -61,30 +61,30 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
|
|
||||||
/// Callback to call when pressing the back button.
|
/// Callback to call when pressing the back button.
|
||||||
/// By default it calls [Navigator.pop]
|
/// By default it calls [Navigator.pop]
|
||||||
final VoidCallback onBackPressed;
|
final VoidCallback? onBackPressed;
|
||||||
|
|
||||||
/// Callback to call when the title is tapped.
|
/// Callback to call when the title is tapped.
|
||||||
final VoidCallback onTitleTap;
|
final VoidCallback? onTitleTap;
|
||||||
|
|
||||||
/// The message parent of this thread
|
/// The message parent of this thread
|
||||||
final Message parent;
|
final Message parent;
|
||||||
|
|
||||||
/// Title widget
|
/// Title widget
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
|
|
||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
final Widget leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// AppBar actions
|
/// AppBar actions
|
||||||
final List<Widget> actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
/// Instantiate a new ThreadHeader
|
/// Instantiate a new ThreadHeader
|
||||||
ThreadHeader({
|
ThreadHeader({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.parent,
|
required this.parent,
|
||||||
this.showBackButton = true,
|
this.showBackButton = true,
|
||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
this.title,
|
this.title,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
class TypingIndicator extends StatelessWidget {
|
class TypingIndicator extends StatelessWidget {
|
||||||
/// Instantiate a new TypingIndicator
|
/// Instantiate a new TypingIndicator
|
||||||
const TypingIndicator({
|
const TypingIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
this.channel,
|
this.channel,
|
||||||
this.alternativeWidget,
|
this.alternativeWidget,
|
||||||
this.style,
|
this.style,
|
||||||
@@ -15,13 +15,13 @@ class TypingIndicator extends StatelessWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Style of the text widget
|
/// Style of the text widget
|
||||||
final TextStyle style;
|
final TextStyle? style;
|
||||||
|
|
||||||
/// List of typing users
|
/// List of typing users
|
||||||
final Channel channel;
|
final Channel? channel;
|
||||||
|
|
||||||
/// Widget built when no typings is happening
|
/// Widget built when no typings is happening
|
||||||
final Widget alternativeWidget;
|
final Widget? alternativeWidget;
|
||||||
|
|
||||||
/// The padding of this widget
|
/// The padding of this widget
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
@@ -31,7 +31,7 @@ class TypingIndicator extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channelState =
|
final channelState =
|
||||||
channel?.state ?? StreamChannel.of(context).channel.state;
|
channel?.state ?? StreamChannel.of(context).channel.state!;
|
||||||
return StreamBuilder<List<User>>(
|
return StreamBuilder<List<User>>(
|
||||||
initialData: channelState.typingEvents,
|
initialData: channelState.typingEvents,
|
||||||
stream: channelState.typingEventsStream,
|
stream: channelState.typingEventsStream,
|
||||||
@@ -53,7 +53,7 @@ class TypingIndicator extends StatelessWidget {
|
|||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
' ${snapshot.data[0].name}${snapshot.data.length == 1 ? '' : ' and ${snapshot.data.length - 1} more'} ${snapshot.data.length == 1 ? 'is' : 'are'} typing',
|
' ${snapshot.data![0].name}${snapshot.data!.length == 1 ? '' : ' and ${snapshot.data!.length - 1} more'} ${snapshot.data!.length == 1 ? 'is' : 'are'} typing',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,23 +4,23 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
|
|
||||||
class UnreadIndicator extends StatelessWidget {
|
class UnreadIndicator extends StatelessWidget {
|
||||||
const UnreadIndicator({
|
const UnreadIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
this.cid,
|
this.cid,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Channel cid used to retrieve unread count
|
/// Channel cid used to retrieve unread count
|
||||||
final String cid;
|
final String? cid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
child: StreamBuilder<int>(
|
child: StreamBuilder<int?>(
|
||||||
stream: cid != null
|
stream: cid != null
|
||||||
? client.state.channels[cid].state.unreadCountStream
|
? client.state.channels[cid]?.state?.unreadCountStream
|
||||||
: client.state.totalUnreadCountStream,
|
: client.state.totalUnreadCountStream,
|
||||||
initialData: cid != null
|
initialData: cid != null
|
||||||
? client.state.channels[cid].state.unreadCount
|
? client.state.channels[cid]?.state?.unreadCount
|
||||||
: client.state.totalUnreadCount,
|
: client.state.totalUnreadCount,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData || snapshot.data == 0) {
|
if (!snapshot.hasData || snapshot.data == 0) {
|
||||||
@@ -40,7 +40,7 @@ class UnreadIndicator extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${snapshot.data > 99 ? '99+' : snapshot.data}',
|
'${snapshot.data! > 99 ? '99+' : snapshot.data}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|||||||
@@ -5,20 +5,27 @@ import 'stream_chat_theme.dart';
|
|||||||
class UploadProgressIndicator extends StatelessWidget {
|
class UploadProgressIndicator extends StatelessWidget {
|
||||||
final int uploaded;
|
final int uploaded;
|
||||||
final int total;
|
final int total;
|
||||||
final Color progressIndicatorColor;
|
late final Color progressIndicatorColor;
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
final bool showBackground;
|
final bool showBackground;
|
||||||
final TextStyle textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
const UploadProgressIndicator({
|
UploadProgressIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.uploaded,
|
required this.uploaded,
|
||||||
@required this.total,
|
required this.total,
|
||||||
this.progressIndicatorColor = const Color(0xffb2b2b2),
|
Color? progressIndicatorColor,
|
||||||
this.padding = const EdgeInsets.only(top: 5, bottom: 5, right: 11, left: 5),
|
this.padding = const EdgeInsets.only(
|
||||||
|
top: 5,
|
||||||
|
bottom: 5,
|
||||||
|
right: 11,
|
||||||
|
left: 5,
|
||||||
|
),
|
||||||
this.showBackground = true,
|
this.showBackground = true,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
}) : super(key: key);
|
}) : progressIndicatorColor =
|
||||||
|
progressIndicatorColor ?? const Color(0xffb2b2b2),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
final EdgeInsets textPadding;
|
final EdgeInsets textPadding;
|
||||||
|
|
||||||
UrlAttachment({
|
UrlAttachment({
|
||||||
@required this.urlAttachment,
|
required this.urlAttachment,
|
||||||
@required this.hostDisplayName,
|
required this.hostDisplayName,
|
||||||
@required this.textPadding,
|
required this.textPadding,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -19,7 +19,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => launchURL(
|
onTap: () => launchURL(
|
||||||
context,
|
context,
|
||||||
urlAttachment.ogScrapeUrl,
|
urlAttachment.ogScrapeUrl!,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -35,7 +35,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
CachedNetworkImage(
|
CachedNetworkImage(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
imageUrl: urlAttachment.imageUrl,
|
imageUrl: urlAttachment.imageUrl!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -78,7 +78,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (urlAttachment.title != null)
|
if (urlAttachment.title != null)
|
||||||
Text(
|
Text(
|
||||||
urlAttachment.title.trim(),
|
urlAttachment.title!.trim(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
@@ -88,7 +88,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (urlAttachment.text != null)
|
if (urlAttachment.text != null)
|
||||||
Text(
|
Text(
|
||||||
urlAttachment.text,
|
urlAttachment.text!,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.body
|
.body
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import '../stream_chat_flutter.dart';
|
|||||||
|
|
||||||
class UserAvatar extends StatelessWidget {
|
class UserAvatar extends StatelessWidget {
|
||||||
const UserAvatar({
|
const UserAvatar({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.user,
|
required this.user,
|
||||||
this.constraints,
|
this.constraints,
|
||||||
this.onlineIndicatorConstraints,
|
this.onlineIndicatorConstraints,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
@@ -22,19 +22,19 @@ class UserAvatar extends StatelessWidget {
|
|||||||
|
|
||||||
final User user;
|
final User user;
|
||||||
final Alignment onlineIndicatorAlignment;
|
final Alignment onlineIndicatorAlignment;
|
||||||
final BoxConstraints constraints;
|
final BoxConstraints? constraints;
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
final BoxConstraints onlineIndicatorConstraints;
|
final BoxConstraints? onlineIndicatorConstraints;
|
||||||
final void Function(User) onTap;
|
final void Function(User)? onTap;
|
||||||
final void Function(User) onLongPress;
|
final void Function(User)? onLongPress;
|
||||||
final bool showOnlineStatus;
|
final bool showOnlineStatus;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final Color selectionColor;
|
final Color? selectionColor;
|
||||||
final double selectionThickness;
|
final double selectionThickness;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final hasImage = user.extraData?.containsKey('image') == true &&
|
final hasImage = user.extraData.containsKey('image') &&
|
||||||
user.extraData['image'] != null &&
|
user.extraData['image'] != null &&
|
||||||
user.extraData['image'] != '';
|
user.extraData['image'] != '';
|
||||||
final streamChatTheme = StreamChatTheme.of(context);
|
final streamChatTheme = StreamChatTheme.of(context);
|
||||||
@@ -44,17 +44,17 @@ class UserAvatar extends StatelessWidget {
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius,
|
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
|
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: streamChatTheme.colorTheme.accentBlue,
|
color: streamChatTheme.colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
child: hasImage
|
child: hasImage
|
||||||
? CachedNetworkImage(
|
? CachedNetworkImage(
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
imageUrl: user.extraData['image'],
|
imageUrl: user.extraData['image'] as String,
|
||||||
errorWidget: (_, __, ___) {
|
errorWidget: (_, __, ___) {
|
||||||
return streamChatTheme.defaultUserImage(context, user);
|
return streamChatTheme.defaultUserImage(context, user);
|
||||||
},
|
},
|
||||||
@@ -68,11 +68,12 @@ class UserAvatar extends StatelessWidget {
|
|||||||
if (selected) {
|
if (selected) {
|
||||||
avatar = ClipRRect(
|
avatar = ClipRRect(
|
||||||
borderRadius: (borderRadius ??
|
borderRadius: (borderRadius ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ??
|
||||||
|
BorderRadius.zero) +
|
||||||
BorderRadius.circular(selectionThickness),
|
BorderRadius.circular(selectionThickness),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
|
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||||
color: selectionColor ??
|
color: selectionColor ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -83,12 +84,12 @@ class UserAvatar extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap != null ? () => onTap(user) : null,
|
onTap: onTap != null ? () => onTap!(user) : null,
|
||||||
onLongPress: onLongPress != null ? () => onLongPress(user) : null,
|
onLongPress: onLongPress != null ? () => onLongPress!(user) : null,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
avatar,
|
avatar,
|
||||||
if (showOnlineStatus && user.online == true)
|
if (showOnlineStatus && user.online)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: onlineIndicatorAlignment,
|
alignment: onlineIndicatorAlignment,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/user_list_view.dart';
|
import 'package:stream_chat_flutter/src/user_list_view.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import 'stream_chat_theme.dart';
|
import 'stream_chat_theme.dart';
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ import 'stream_chat_theme.dart';
|
|||||||
class UserItem extends StatelessWidget {
|
class UserItem extends StatelessWidget {
|
||||||
/// Instantiate a new UserItem
|
/// Instantiate a new UserItem
|
||||||
const UserItem({
|
const UserItem({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.user,
|
required this.user,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
this.onImageTap,
|
this.onImageTap,
|
||||||
@@ -29,16 +29,16 @@ class UserItem extends StatelessWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Function called when tapping this widget
|
/// Function called when tapping this widget
|
||||||
final void Function(User) onTap;
|
final void Function(User)? onTap;
|
||||||
|
|
||||||
/// Function called when long pressing this widget
|
/// Function called when long pressing this widget
|
||||||
final void Function(User) onLongPress;
|
final void Function(User)? onLongPress;
|
||||||
|
|
||||||
/// User displayed
|
/// User displayed
|
||||||
final User user;
|
final User user;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final void Function(User) onImageTap;
|
final void Function(User)? onImageTap;
|
||||||
|
|
||||||
/// If true the [UserItem] will show a trailing checkmark
|
/// If true the [UserItem] will show a trailing checkmark
|
||||||
final bool selected;
|
final bool selected;
|
||||||
@@ -51,12 +51,12 @@ class UserItem extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
onTap(user);
|
onTap!(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (onLongPress != null) {
|
if (onLongPress != null) {
|
||||||
onLongPress(user);
|
onLongPress!(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: UserAvatar(
|
leading: UserAvatar(
|
||||||
@@ -64,7 +64,7 @@ class UserItem extends StatelessWidget {
|
|||||||
showOnlineStatus: true,
|
showOnlineStatus: true,
|
||||||
onTap: (user) {
|
onTap: (user) {
|
||||||
if (onImageTap != null) {
|
if (onImageTap != null) {
|
||||||
onImageTap(user);
|
onImageTap!(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
import 'user_item.dart';
|
import 'user_item.dart';
|
||||||
|
|
||||||
/// Callback called when tapping on a user
|
/// Callback called when tapping on a user
|
||||||
typedef UserTapCallback = void Function(User, Widget);
|
typedef UserTapCallback = void Function(User, Widget?);
|
||||||
|
|
||||||
/// Builder used to create a custom [ListUserItem] from a [User]
|
/// Builder used to create a custom [ListUserItem] from a [User]
|
||||||
typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
||||||
@@ -44,7 +44,7 @@ typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
|||||||
class UserListView extends StatefulWidget {
|
class UserListView extends StatefulWidget {
|
||||||
/// Instantiate a new UserListView
|
/// Instantiate a new UserListView
|
||||||
const UserListView({
|
const UserListView({
|
||||||
Key key,
|
Key? key,
|
||||||
this.filter,
|
this.filter,
|
||||||
this.options,
|
this.options,
|
||||||
this.sort,
|
this.sort,
|
||||||
@@ -72,51 +72,51 @@ class UserListView extends StatefulWidget {
|
|||||||
/// The query filters to use.
|
/// The query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
/// You can also filter other built-in channel fields.
|
/// You can also filter other built-in channel fields.
|
||||||
final Filter filter;
|
final Filter? filter;
|
||||||
|
|
||||||
/// Query channels options.
|
/// Query channels options.
|
||||||
///
|
///
|
||||||
/// state: if true returns the Channel state
|
/// state: if true returns the Channel state
|
||||||
/// watch: if true listen to changes to this Channel in real time.
|
/// watch: if true listen to changes to this Channel in real time.
|
||||||
final Map<String, dynamic> options;
|
final Map<String, dynamic>? options;
|
||||||
|
|
||||||
/// The sorting used for the channels matching the filters.
|
/// The sorting used for the channels matching the filters.
|
||||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption> sort;
|
final List<SortOption>? sort;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of users to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams pagination;
|
final PaginationParams? pagination;
|
||||||
|
|
||||||
/// Function called when tapping on a channel
|
/// Function called when tapping on a channel
|
||||||
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
||||||
/// with the widget [userWidget] as child.
|
/// with the widget [userWidget] as child.
|
||||||
final UserTapCallback onUserTap;
|
final UserTapCallback? onUserTap;
|
||||||
|
|
||||||
/// Function called when long pressing on a channel
|
/// Function called when long pressing on a channel
|
||||||
final Function(User) onUserLongPress;
|
final Function(User)? onUserLongPress;
|
||||||
|
|
||||||
/// Widget used when opening a channel
|
/// Widget used when opening a channel
|
||||||
final Widget userWidget;
|
final Widget? userWidget;
|
||||||
|
|
||||||
/// Builder used to create a custom user preview
|
/// Builder used to create a custom user preview
|
||||||
final UserItemBuilder userItemBuilder;
|
final UserItemBuilder? userItemBuilder;
|
||||||
|
|
||||||
/// Builder used to create a custom item separator
|
/// Builder used to create a custom item separator
|
||||||
final Function(BuildContext, int) separatorBuilder;
|
final Function(BuildContext, int)? separatorBuilder;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final Function(User) onImageTap;
|
final Function(User)? onImageTap;
|
||||||
|
|
||||||
/// Set it to false to disable the pull-to-refresh widget
|
/// Set it to false to disable the pull-to-refresh widget
|
||||||
final bool pullToRefresh;
|
final bool pullToRefresh;
|
||||||
|
|
||||||
/// Sets a blue trailing checkMark in [ListUserItem] for all the [selectedUsers]
|
/// Sets a blue trailing checkMark in [ListUserItem] for all the [selectedUsers]
|
||||||
final Set<User> selectedUsers;
|
final Set<User>? selectedUsers;
|
||||||
|
|
||||||
/// Set it to true to group users by their first character
|
/// Set it to true to group users by their first character
|
||||||
///
|
///
|
||||||
@@ -127,16 +127,17 @@ class UserListView extends StatefulWidget {
|
|||||||
final int crossAxisCount;
|
final int crossAxisCount;
|
||||||
|
|
||||||
/// The builder that will be used in case of error
|
/// The builder that will be used in case of error
|
||||||
final Widget Function(Error error) errorBuilder;
|
final Widget Function(Error error)? errorBuilder;
|
||||||
|
|
||||||
/// The builder that will be used to build the list
|
/// The builder that will be used to build the list
|
||||||
final Widget Function(BuildContext context, List<ListItem> users) listBuilder;
|
final Widget Function(BuildContext context, List<ListItem> users)?
|
||||||
|
listBuilder;
|
||||||
|
|
||||||
/// The builder that will be used for loading
|
/// The builder that will be used for loading
|
||||||
final WidgetBuilder loadingBuilder;
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
/// The builder used when the channel list is empty.
|
/// The builder used when the channel list is empty.
|
||||||
final WidgetBuilder emptyBuilder;
|
final WidgetBuilder? emptyBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_UserListViewState createState() => _UserListViewState();
|
_UserListViewState createState() => _UserListViewState();
|
||||||
@@ -151,9 +152,9 @@ class _UserListViewState extends State<UserListView>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var child = UserListCore(
|
var child = UserListCore(
|
||||||
errorBuilder: widget.errorBuilder ??
|
errorBuilder: widget.errorBuilder as Widget Function(Object)? ??
|
||||||
(err) {
|
(err) {
|
||||||
return _buildError(err);
|
return _buildError(err as Error);
|
||||||
},
|
},
|
||||||
emptyBuilder: widget.emptyBuilder ??
|
emptyBuilder: widget.emptyBuilder ??
|
||||||
(context) {
|
(context) {
|
||||||
@@ -193,7 +194,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
return child;
|
return child;
|
||||||
} else {
|
} else {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () => _userListController.loadData(),
|
onRefresh: () => _userListController.loadData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -241,7 +242,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
child: Text(message),
|
child: Text(message),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _userListController.loadData(),
|
onPressed: () => _userListController.loadData!(),
|
||||||
child: Text('Retry'),
|
child: Text('Retry'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -276,7 +277,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
separatorBuilder: (_, index) {
|
separatorBuilder: (_, index) {
|
||||||
if (widget.separatorBuilder != null) {
|
if (widget.separatorBuilder != null) {
|
||||||
return widget.separatorBuilder(context, index);
|
return widget.separatorBuilder!(context, index);
|
||||||
}
|
}
|
||||||
return _separatorBuilder(context, index);
|
return _separatorBuilder(context, index);
|
||||||
},
|
},
|
||||||
@@ -296,7 +297,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
);
|
);
|
||||||
|
|
||||||
return LazyLoadScrollView(
|
return LazyLoadScrollView(
|
||||||
onEndOfPage: () => _userListController.paginateData(),
|
onEndOfPage: () => _userListController.paginateData!(),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -329,10 +330,10 @@ class _UserListViewState extends State<UserListView>
|
|||||||
return Container(
|
return Container(
|
||||||
key: ValueKey<String>('USER-${user.id}'),
|
key: ValueKey<String>('USER-${user.id}'),
|
||||||
child: widget.userItemBuilder != null
|
child: widget.userItemBuilder != null
|
||||||
? widget.userItemBuilder(context, user, selected)
|
? widget.userItemBuilder!(context, user, selected)
|
||||||
: UserItem(
|
: UserItem(
|
||||||
user: user,
|
user: user,
|
||||||
onTap: (user) => widget.onUserTap(user, widget.userWidget),
|
onTap: (user) => widget.onUserTap!(user, widget.userWidget),
|
||||||
onLongPress: widget.onUserLongPress,
|
onLongPress: widget.onUserLongPress,
|
||||||
onImageTap: widget.onImageTap,
|
onImageTap: widget.onImageTap,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
@@ -356,7 +357,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
return Container(
|
return Container(
|
||||||
key: ValueKey<String>('USER-${user.id}'),
|
key: ValueKey<String>('USER-${user.id}'),
|
||||||
child: widget.userItemBuilder != null
|
child: widget.userItemBuilder != null
|
||||||
? widget.userItemBuilder(context, user, selected)
|
? widget.userItemBuilder!(context, user, selected)
|
||||||
: Column(
|
: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@@ -374,7 +375,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
width: 12,
|
width: 12,
|
||||||
),
|
),
|
||||||
onTap: (user) =>
|
onTap: (user) =>
|
||||||
widget.onUserTap(user, widget.userWidget),
|
widget.onUserTap!(user, widget.userWidget),
|
||||||
onLongPress: widget.onUserLongPress,
|
onLongPress: widget.onUserLongPress,
|
||||||
),
|
),
|
||||||
SizedBox(height: 4),
|
SizedBox(height: 4),
|
||||||
@@ -424,7 +425,7 @@ class _UserListViewState extends State<UserListView>
|
|||||||
height: 100,
|
height: 100,
|
||||||
padding: EdgeInsets.all(32),
|
padding: EdgeInsets.all(32),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: snapshot.data ? CircularProgressIndicator() : Container(),
|
child: snapshot.data! ? CircularProgressIndicator() : Container(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
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/src/user_avatar.dart';
|
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
class UserReactionDisplay extends StatelessWidget {
|
class UserReactionDisplay extends StatelessWidget {
|
||||||
const UserReactionDisplay({
|
const UserReactionDisplay({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.reactionToEmoji,
|
required this.reactionToEmoji,
|
||||||
@required this.message,
|
required this.message,
|
||||||
this.size = 30,
|
this.size = 30,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -23,12 +23,13 @@ class UserReactionDisplay extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: reactionToEmoji.keys.map((reactionType) {
|
children: reactionToEmoji.keys.map((reactionType) {
|
||||||
var firstUserReaction = message.latestReactions.firstWhere(
|
var firstUserReaction = message.latestReactions!
|
||||||
(element) => element.type == reactionType, orElse: () {
|
.firstWhere((element) => element.type == reactionType,
|
||||||
return null;
|
orElse: () {
|
||||||
});
|
return null;
|
||||||
|
} as Reaction Function()?);
|
||||||
|
|
||||||
if (firstUserReaction == null) {
|
if (firstUserReaction.user == null) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
iconSize: size,
|
iconSize: size,
|
||||||
icon: Container(),
|
icon: Container(),
|
||||||
@@ -39,7 +40,7 @@ class UserReactionDisplay extends StatelessWidget {
|
|||||||
return IconButton(
|
return IconButton(
|
||||||
iconSize: size,
|
iconSize: size,
|
||||||
icon: UserAvatar(
|
icon: UserAvatar(
|
||||||
user: firstUserReaction.user,
|
user: firstUserReaction.user!,
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxHeight: size - 5,
|
maxHeight: size - 5,
|
||||||
maxWidth: size - 5,
|
maxWidth: size - 5,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'stream_svg_icon.dart';
|
import 'stream_svg_icon.dart';
|
||||||
|
|
||||||
Future<void> launchURL(BuildContext context, String url) async {
|
Future<void> launchURL(BuildContext context, String? url) async {
|
||||||
if (await canLaunch(url)) {
|
if (url != null && await canLaunch(url)) {
|
||||||
await launch(url);
|
await launch(url);
|
||||||
} else {
|
} else {
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
@@ -20,13 +20,13 @@ Future<void> launchURL(BuildContext context, String url) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> showConfirmationDialog(
|
Future<bool?> showConfirmationDialog(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
String title,
|
String? title,
|
||||||
Widget icon,
|
Widget? icon,
|
||||||
String question,
|
String? question,
|
||||||
String okText,
|
String? okText,
|
||||||
String cancelText,
|
String? cancelText,
|
||||||
}) {
|
}) {
|
||||||
return showModalBottomSheet(
|
return showModalBottomSheet(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||||
@@ -46,17 +46,17 @@ Future<bool> showConfirmationDialog(
|
|||||||
if (icon != null) icon,
|
if (icon != null) icon,
|
||||||
SizedBox(height: 26.0),
|
SizedBox(height: 26.0),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title!,
|
||||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||||
),
|
),
|
||||||
SizedBox(height: 7.0),
|
SizedBox(height: 7.0),
|
||||||
Text(
|
Text(
|
||||||
question,
|
question!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: 36.0),
|
SizedBox(height: 36.0),
|
||||||
Container(
|
Container(
|
||||||
color: effect.color.withOpacity(effect.alpha ?? 1),
|
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
||||||
height: 1,
|
height: 1,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@@ -69,7 +69,7 @@ Future<bool> showConfirmationDialog(
|
|||||||
Navigator.of(context).pop(false);
|
Navigator.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
cancelText,
|
cancelText!,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyBold
|
.bodyBold
|
||||||
@@ -90,7 +90,7 @@ Future<bool> showConfirmationDialog(
|
|||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
okText,
|
okText!,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyBold
|
.bodyBold
|
||||||
@@ -110,17 +110,17 @@ Future<bool> showConfirmationDialog(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> showInfoDialog(
|
Future<bool?> showInfoDialog(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
String title,
|
String? title,
|
||||||
Widget icon,
|
Widget? icon,
|
||||||
String details,
|
String? details,
|
||||||
String okText,
|
String? okText,
|
||||||
StreamChatThemeData theme,
|
StreamChatThemeData? theme,
|
||||||
}) {
|
}) {
|
||||||
return showModalBottomSheet(
|
return showModalBottomSheet(
|
||||||
backgroundColor: theme?.colorTheme?.white ??
|
backgroundColor:
|
||||||
StreamChatTheme.of(context).colorTheme.white,
|
theme?.colorTheme.white ?? StreamChatTheme.of(context).colorTheme.white,
|
||||||
context: context,
|
context: context,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -140,19 +140,19 @@ Future<bool> showInfoDialog(
|
|||||||
height: 26.0,
|
height: 26.0,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title!,
|
||||||
style: theme?.textTheme?.headlineBold ??
|
style: theme?.textTheme.headlineBold ??
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 7.0,
|
height: 7.0,
|
||||||
),
|
),
|
||||||
Text(details),
|
Text(details!),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 36.0,
|
height: 36.0,
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: theme?.colorTheme?.black?.withOpacity(.08) ??
|
color: theme?.colorTheme.black.withOpacity(.08) ??
|
||||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||||
height: 1.0,
|
height: 1.0,
|
||||||
),
|
),
|
||||||
@@ -162,9 +162,9 @@ Future<bool> showInfoDialog(
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
okText,
|
okText!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: theme?.colorTheme?.black?.withOpacity(0.5) ??
|
color: theme?.colorTheme.black.withOpacity(0.5) ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
@@ -183,7 +183,7 @@ String getRandomPicUrl(User user) =>
|
|||||||
'https://getstream.io/random_png/?id=${user.id}&name=${user.name}';
|
'https://getstream.io/random_png/?id=${user.id}&name=${user.name}';
|
||||||
|
|
||||||
/// Get websiteName from [hostName]
|
/// Get websiteName from [hostName]
|
||||||
String getWebsiteName(String hostName) {
|
String? getWebsiteName(String hostName) {
|
||||||
switch (hostName) {
|
switch (hostName) {
|
||||||
case 'reddit':
|
case 'reddit':
|
||||||
return 'Reddit';
|
return 'Reddit';
|
||||||
@@ -292,62 +292,44 @@ String fileSize(dynamic size, [int round = 2]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
StreamSvgIcon getFileTypeImage(String type) {
|
StreamSvgIcon getFileTypeImage(String? type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case '7z':
|
case '7z':
|
||||||
return StreamSvgIcon.filetype7z();
|
return StreamSvgIcon.filetype7z();
|
||||||
break;
|
|
||||||
case 'csv':
|
case 'csv':
|
||||||
return StreamSvgIcon.filetypeCsv();
|
return StreamSvgIcon.filetypeCsv();
|
||||||
break;
|
|
||||||
case 'doc':
|
case 'doc':
|
||||||
return StreamSvgIcon.filetypeDoc();
|
return StreamSvgIcon.filetypeDoc();
|
||||||
break;
|
|
||||||
case 'docx':
|
case 'docx':
|
||||||
return StreamSvgIcon.filetypeDocx();
|
return StreamSvgIcon.filetypeDocx();
|
||||||
break;
|
|
||||||
case 'html':
|
case 'html':
|
||||||
return StreamSvgIcon.filetypeHtml();
|
return StreamSvgIcon.filetypeHtml();
|
||||||
break;
|
|
||||||
case 'md':
|
case 'md':
|
||||||
return StreamSvgIcon.filetypeMd();
|
return StreamSvgIcon.filetypeMd();
|
||||||
break;
|
|
||||||
case 'odt':
|
case 'odt':
|
||||||
return StreamSvgIcon.filetypeOdt();
|
return StreamSvgIcon.filetypeOdt();
|
||||||
break;
|
|
||||||
case 'pdf':
|
case 'pdf':
|
||||||
return StreamSvgIcon.filetypePdf();
|
return StreamSvgIcon.filetypePdf();
|
||||||
break;
|
|
||||||
case 'ppt':
|
case 'ppt':
|
||||||
return StreamSvgIcon.filetypePpt();
|
return StreamSvgIcon.filetypePpt();
|
||||||
break;
|
|
||||||
case 'pptx':
|
case 'pptx':
|
||||||
return StreamSvgIcon.filetypePptx();
|
return StreamSvgIcon.filetypePptx();
|
||||||
break;
|
|
||||||
case 'rar':
|
case 'rar':
|
||||||
return StreamSvgIcon.filetypeRar();
|
return StreamSvgIcon.filetypeRar();
|
||||||
break;
|
|
||||||
case 'rtf':
|
case 'rtf':
|
||||||
return StreamSvgIcon.filetypeRtf();
|
return StreamSvgIcon.filetypeRtf();
|
||||||
break;
|
|
||||||
case 'tar':
|
case 'tar':
|
||||||
return StreamSvgIcon.filetypeTar();
|
return StreamSvgIcon.filetypeTar();
|
||||||
break;
|
|
||||||
case 'txt':
|
case 'txt':
|
||||||
return StreamSvgIcon.filetypeTxt();
|
return StreamSvgIcon.filetypeTxt();
|
||||||
break;
|
|
||||||
case 'xls':
|
case 'xls':
|
||||||
return StreamSvgIcon.filetypeXls();
|
return StreamSvgIcon.filetypeXls();
|
||||||
break;
|
|
||||||
case 'xlsx':
|
case 'xlsx':
|
||||||
return StreamSvgIcon.filetypeXlsx();
|
return StreamSvgIcon.filetypeXlsx();
|
||||||
break;
|
|
||||||
case 'zip':
|
case 'zip':
|
||||||
return StreamSvgIcon.filetypeZip();
|
return StreamSvgIcon.filetypeZip();
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return StreamSvgIcon.filetypeGeneric();
|
return StreamSvgIcon.filetypeGeneric();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'dart:typed_data';
|
|||||||
import 'package:synchronized/synchronized.dart';
|
import 'package:synchronized/synchronized.dart';
|
||||||
import 'package:video_compress/video_compress.dart';
|
import 'package:video_compress/video_compress.dart';
|
||||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
|
|
||||||
class IVideoService {
|
class IVideoService {
|
||||||
static final IVideoService instance = IVideoService._();
|
static final IVideoService instance = IVideoService._();
|
||||||
@@ -27,10 +26,10 @@ class IVideoService {
|
|||||||
/// );
|
/// );
|
||||||
/// debugPrint(info.toJson());
|
/// debugPrint(info.toJson());
|
||||||
/// ```
|
/// ```
|
||||||
Future<MediaInfo> compressVideo(String path) async {
|
Future<MediaInfo?> compressVideo(String? path) async {
|
||||||
return _lock.synchronized(() {
|
return _lock.synchronized(() {
|
||||||
return VideoCompress.compressVideo(
|
return VideoCompress.compressVideo(
|
||||||
path,
|
path!,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -39,8 +38,8 @@ class IVideoService {
|
|||||||
/// The video can be a local video file, or an URL repreents iOS or Android native supported video format.
|
/// The video can be a local video file, or an URL repreents iOS or Android native supported video format.
|
||||||
/// Speicify the maximum height or width for the thumbnail or 0 for same resolution as the original video.
|
/// Speicify the maximum height or width for the thumbnail or 0 for same resolution as the original video.
|
||||||
/// The lower quality value creates lower quality of the thumbnail image, but it gets ignored for PNG format.
|
/// The lower quality value creates lower quality of the thumbnail image, but it gets ignored for PNG format.
|
||||||
Future<Uint8List> generateVideoThumbnail({
|
Future<Uint8List?> generateVideoThumbnail({
|
||||||
@required String video,
|
required String video,
|
||||||
ImageFormat imageFormat = ImageFormat.PNG,
|
ImageFormat imageFormat = ImageFormat.PNG,
|
||||||
int maxHeight = 0,
|
int maxHeight = 0,
|
||||||
int maxWidth = 0,
|
int maxWidth = 0,
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ import 'stream_svg_icon.dart';
|
|||||||
import 'video_service.dart';
|
import 'video_service.dart';
|
||||||
|
|
||||||
class VideoThumbnailImage extends StatefulWidget {
|
class VideoThumbnailImage extends StatefulWidget {
|
||||||
final String video;
|
final String? video;
|
||||||
final double width;
|
final double? width;
|
||||||
final double height;
|
final double? height;
|
||||||
final BoxFit fit;
|
final BoxFit? fit;
|
||||||
final ImageFormat format;
|
final ImageFormat format;
|
||||||
final Widget Function(BuildContext, Object) errorBuilder;
|
final Widget Function(BuildContext, Object?)? errorBuilder;
|
||||||
final WidgetBuilder placeholderBuilder;
|
final WidgetBuilder? placeholderBuilder;
|
||||||
|
|
||||||
const VideoThumbnailImage({
|
const VideoThumbnailImage({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.video,
|
required this.video,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.fit,
|
this.fit,
|
||||||
@@ -33,12 +33,12 @@ class VideoThumbnailImage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
||||||
Future<Uint8List> thumbnailFuture;
|
late Future<Uint8List?> thumbnailFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
thumbnailFuture = VideoService.generateVideoThumbnail(
|
thumbnailFuture = VideoService.generateVideoThumbnail(
|
||||||
video: widget.video,
|
video: widget.video!,
|
||||||
imageFormat: widget.format,
|
imageFormat: widget.format,
|
||||||
);
|
);
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -48,7 +48,7 @@ class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
|||||||
void didUpdateWidget(covariant VideoThumbnailImage oldWidget) {
|
void didUpdateWidget(covariant VideoThumbnailImage oldWidget) {
|
||||||
if (oldWidget.video != widget.video || oldWidget.format != widget.format) {
|
if (oldWidget.video != widget.video || oldWidget.format != widget.format) {
|
||||||
thumbnailFuture = VideoService.generateVideoThumbnail(
|
thumbnailFuture = VideoService.generateVideoThumbnail(
|
||||||
video: widget.video,
|
video: widget.video!,
|
||||||
imageFormat: widget.format,
|
imageFormat: widget.format,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -57,13 +57,13 @@ class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder<Uint8List>(
|
return FutureBuilder<Uint8List?>(
|
||||||
future: thumbnailFuture,
|
future: thumbnailFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 350),
|
duration: const Duration(milliseconds: 350),
|
||||||
child: Builder(
|
child: Builder(
|
||||||
key: ValueKey<AsyncSnapshot<Uint8List>>(snapshot),
|
key: ValueKey<AsyncSnapshot<Uint8List?>>(snapshot),
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return widget.errorBuilder?.call(context, snapshot.error) ??
|
return widget.errorBuilder?.call(context, snapshot.error) ??
|
||||||
@@ -90,7 +90,7 @@ class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Image.memory(
|
return Image.memory(
|
||||||
snapshot.data,
|
snapshot.data!,
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
|||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@@ -19,7 +19,7 @@ dependencies:
|
|||||||
scrollable_positioned_list: ^0.2.0-nullsafety.0
|
scrollable_positioned_list: ^0.2.0-nullsafety.0
|
||||||
jiffy: ^4.1.0
|
jiffy: ^4.1.0
|
||||||
flutter_svg: ^0.21.0+1
|
flutter_svg: ^0.21.0+1
|
||||||
flutter_portal: ^0.4.0-nullsafety.0
|
flutter_portal: ^0.4.0
|
||||||
cached_network_image: ^3.0.0
|
cached_network_image: ^3.0.0
|
||||||
shimmer: ^2.0.0-nullsafety.0
|
shimmer: ^2.0.0-nullsafety.0
|
||||||
flutter_markdown: ^0.6.1
|
flutter_markdown: ^0.6.1
|
||||||
@@ -48,6 +48,7 @@ dependencies:
|
|||||||
characters: ^1.1.0
|
characters: ^1.1.0
|
||||||
path_provider: ^2.0.1
|
path_provider: ^2.0.1
|
||||||
video_thumbnail: ^0.3.3
|
video_thumbnail: ^0.3.3
|
||||||
|
collection: ^1.15.0
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
stream_chat:
|
stream_chat:
|
||||||
@@ -66,7 +67,7 @@ flutter:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
mockito: ^5.0.3
|
mocktail: ^0.1.2
|
||||||
pedantic: ^1.11.0
|
pedantic: ^1.11.0
|
||||||
golden_toolkit: ^0.9.0
|
golden_toolkit: ^0.9.0
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
|
|
||||||
class MockAttachmentDownloader extends Mock {
|
class MockAttachmentDownloader extends Mock {
|
||||||
ProgressCallback progressCallback;
|
ProgressCallback? progressCallback;
|
||||||
Completer<String> completer = Completer();
|
Completer<String> completer = Completer();
|
||||||
|
|
||||||
Future<String> call(
|
Future<String> call(
|
||||||
Attachment attachment, {
|
Attachment attachment, {
|
||||||
ProgressCallback progressCallback,
|
ProgressCallback? progressCallback,
|
||||||
}) {
|
}) {
|
||||||
this.progressCallback = progressCallback;
|
this.progressCallback = progressCallback;
|
||||||
return completer.future;
|
return completer.future;
|
||||||
@@ -22,17 +22,22 @@ class MockAttachmentDownloader extends Mock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
setUpAll(() {
|
||||||
|
registerFallbackValue(MaterialPageRoute(builder: (context) => SizedBox()));
|
||||||
|
registerFallbackValue(Message());
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'it should show all the actions',
|
'it should show all the actions',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
@@ -70,11 +75,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id2'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id2'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
@@ -112,11 +117,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
@@ -153,11 +158,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
final mockObserver = MockNavigatorObserver();
|
final mockObserver = MockNavigatorObserver();
|
||||||
|
|
||||||
@@ -190,7 +195,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Reply'));
|
await tester.tap(find.text('Reply'));
|
||||||
verify(mockObserver.didPop(any, any));
|
verify(() => mockObserver.didPop(any(), any()));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -200,11 +205,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
final onShowMessage = MockVoidCallback();
|
final onShowMessage = MockVoidCallback();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -234,7 +239,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Show in Chat'));
|
await tester.tap(find.text('Show in Chat'));
|
||||||
verify(onShowMessage.call()).called(1);
|
verify(() => onShowMessage.call()).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -245,11 +250,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final mockChannel = MockChannel();
|
final mockChannel = MockChannel();
|
||||||
|
|
||||||
when(mockChannel.updateMessage(any)).thenAnswer((_) {
|
when(() => mockChannel.updateMessage(any())).thenAnswer((_) async {
|
||||||
return;
|
return UpdateMessageResponse();
|
||||||
});
|
});
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
text: 'test',
|
text: 'test',
|
||||||
@@ -287,11 +292,11 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Delete'));
|
await tester.tap(find.text('Delete'));
|
||||||
verify(mockChannel.updateMessage(message.copyWith(
|
verify(() => mockChannel.updateMessage(message.copyWith(
|
||||||
attachments: [
|
attachments: [
|
||||||
message.attachments[1],
|
message.attachments[1],
|
||||||
],
|
],
|
||||||
))).called(1);
|
))).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -302,11 +307,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final mockChannel = MockChannel();
|
final mockChannel = MockChannel();
|
||||||
|
|
||||||
when(mockChannel.updateMessage(any)).thenAnswer((_) {
|
when(() => mockChannel.updateMessage(any())).thenAnswer((_) async {
|
||||||
return;
|
return UpdateMessageResponse();
|
||||||
});
|
});
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
text: 'test',
|
text: 'test',
|
||||||
@@ -340,9 +345,9 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Delete'));
|
await tester.tap(find.text('Delete'));
|
||||||
verify(mockChannel.updateMessage(message.copyWith(
|
verify(() => mockChannel.updateMessage(message.copyWith(
|
||||||
attachments: [],
|
attachments: [],
|
||||||
))).called(1);
|
))).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -353,11 +358,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final mockChannel = MockChannel();
|
final mockChannel = MockChannel();
|
||||||
|
|
||||||
when(mockChannel.deleteMessage(any)).thenAnswer((_) {
|
when(() => mockChannel.deleteMessage(any())).thenAnswer((_) async {
|
||||||
return;
|
return EmptyResponse();
|
||||||
});
|
});
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
user: User(
|
user: User(
|
||||||
@@ -390,7 +395,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Delete'));
|
await tester.tap(find.text('Delete'));
|
||||||
verify(mockChannel.deleteMessage(message)).called(1);
|
verify(() => mockChannel.deleteMessage(message)).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -400,8 +405,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final imageDownloader = MockAttachmentDownloader();
|
final imageDownloader = MockAttachmentDownloader();
|
||||||
|
|
||||||
@@ -435,15 +440,15 @@ void main() {
|
|||||||
|
|
||||||
await tester.tap(find.text('Save Image'));
|
await tester.tap(find.text('Save Image'));
|
||||||
|
|
||||||
imageDownloader.progressCallback(0, 100);
|
imageDownloader.progressCallback!(0, 100);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.text('0%'), findsOneWidget);
|
expect(find.text('0%'), findsOneWidget);
|
||||||
|
|
||||||
imageDownloader.progressCallback(50, 100);
|
imageDownloader.progressCallback!(50, 100);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.text('50%'), findsOneWidget);
|
expect(find.text('50%'), findsOneWidget);
|
||||||
|
|
||||||
imageDownloader.progressCallback(100, 100);
|
imageDownloader.progressCallback!(100, 100);
|
||||||
imageDownloader.completer.complete('path');
|
imageDownloader.completer.complete('path');
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
||||||
@@ -457,8 +462,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final fileDownloader = MockAttachmentDownloader();
|
final fileDownloader = MockAttachmentDownloader();
|
||||||
|
|
||||||
@@ -492,15 +497,15 @@ void main() {
|
|||||||
|
|
||||||
await tester.tap(find.text('Save Video'));
|
await tester.tap(find.text('Save Video'));
|
||||||
|
|
||||||
fileDownloader.progressCallback(0, 100);
|
fileDownloader.progressCallback!(0, 100);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.text('0%'), findsOneWidget);
|
expect(find.text('0%'), findsOneWidget);
|
||||||
|
|
||||||
fileDownloader.progressCallback(50, 100);
|
fileDownloader.progressCallback!(50, 100);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.text('50%'), findsOneWidget);
|
expect(find.text('50%'), findsOneWidget);
|
||||||
|
|
||||||
fileDownloader.progressCallback(100, 100);
|
fileDownloader.progressCallback!(100, 100);
|
||||||
fileDownloader.completer.complete('path');
|
fileDownloader.completer.complete('path');
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -12,10 +12,10 @@ void main() {
|
|||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/back_button.dart';
|
import 'package:stream_chat_flutter/src/back_button.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ void main() {
|
|||||||
return Material(
|
return Material(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: StreamChatTheme(
|
child: StreamChatTheme(
|
||||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
data: StreamChatThemeData.fromTheme(theme),
|
||||||
child: StreamBackButton(),
|
child: StreamBackButton(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -51,7 +51,7 @@ void main() {
|
|||||||
home: Material(
|
home: Material(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: StreamChatTheme(
|
child: StreamChatTheme(
|
||||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
data: StreamChatThemeData.fromTheme(theme),
|
||||||
child: StreamBackButton(),
|
child: StreamBackButton(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -82,7 +82,7 @@ void main() {
|
|||||||
return Material(
|
return Material(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: StreamChatTheme(
|
child: StreamChatTheme(
|
||||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
data: StreamChatThemeData.fromTheme(theme),
|
||||||
child: StreamBackButton(
|
child: StreamBackButton(
|
||||||
onPressed: () => customCallbackWasCalled = true,
|
onPressed: () => customCallbackWasCalled = true,
|
||||||
),
|
),
|
||||||
@@ -121,7 +121,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(0));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_info.dart';
|
import 'package:stream_chat_flutter/src/channel_info.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
@@ -16,28 +16,33 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => client.wsConnectionStatusStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connected));
|
||||||
|
when(() => channelState.unreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
@@ -72,35 +77,38 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -131,36 +139,38 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -194,33 +204,38 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
when(() => client.wsConnectionStatusStream)
|
||||||
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -263,34 +278,35 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
@@ -328,33 +344,38 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
when(() => client.wsConnectionStatusStream)
|
||||||
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
|
||||||
var backPressed = false;
|
var backPressed = false;
|
||||||
var imageTapped = false;
|
var imageTapped = false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/group_image.dart';
|
import 'package:stream_chat_flutter/src/group_image.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
@@ -16,15 +16,15 @@ void main() {
|
|||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
'image': 'imagetest',
|
'image': 'imagetest',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
'image': 'imagetest',
|
'image': 'imagetest',
|
||||||
});
|
});
|
||||||
@@ -55,17 +55,17 @@ void main() {
|
|||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
@@ -80,7 +80,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id2',
|
userId: 'user-id2',
|
||||||
user: User(
|
user: User(
|
||||||
@@ -95,7 +95,7 @@ void main() {
|
|||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(clientState.usersStream).thenAnswer((i) => Stream.value({
|
when(() => clientState.usersStream).thenAnswer((i) => Stream.value({
|
||||||
'user-id2': User(
|
'user-id2': User(
|
||||||
id: 'user-id2',
|
id: 'user-id2',
|
||||||
extraData: {
|
extraData: {
|
||||||
@@ -103,7 +103,7 @@ void main() {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -133,17 +133,17 @@ void main() {
|
|||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(
|
user: User(
|
||||||
@@ -172,7 +172,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(
|
user: User(
|
||||||
@@ -230,15 +230,15 @@ void main() {
|
|||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
'image': 'imagetest',
|
'image': 'imagetest',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
'image': 'imagetest',
|
'image': 'imagetest',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -12,9 +12,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connected));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connected));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -42,9 +42,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -71,9 +71,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -100,9 +100,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -140,9 +140,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
@@ -174,9 +174,9 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(client.wsConnectionStatusStream)
|
when(() => client.wsConnectionStatusStream)
|
||||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
|
||||||
var tapped = 0;
|
var tapped = 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,40 +15,41 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(channelState.messages).thenReturn([
|
when(() => channelState.messages).thenReturn([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,50 +15,55 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(channel.cid).thenReturn('cid');
|
when(() => channel.cid).thenReturn('cid');
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test name',
|
'name': 'test name',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test name',
|
'name': 'test name',
|
||||||
});
|
});
|
||||||
when(clientState.channels).thenReturn({
|
when(() => clientState.channels).thenReturn({
|
||||||
channel.cid: channel,
|
channel.cid!: channel,
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(channelState.messages).thenReturn([
|
when(() => channelState.messages).thenReturn([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
when(() => channelState.typingEvents).thenReturn([]);
|
||||||
|
when(() => channelState.typingEventsStream)
|
||||||
|
.thenAnswer((_) => Stream.value([]));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -12,8 +12,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -13,8 +13,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -45,21 +45,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
final materialTheme = ThemeData.light();
|
final materialTheme = ThemeData.light();
|
||||||
@@ -98,21 +97,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
final materialTheme = ThemeData.dark();
|
final materialTheme = ThemeData.dark();
|
||||||
@@ -151,21 +149,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
final materialTheme = ThemeData.light();
|
final materialTheme = ThemeData.light();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
@@ -16,51 +16,52 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(channelState.messages).thenReturn([
|
when(() => channelState.messages).thenReturn([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
when(channelState.typingEvents).thenAnswer((i) => [
|
when(() => channelState.typingEvents).thenAnswer((i) => [
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||||
]);
|
]);
|
||||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.typingEventsStream)
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
.thenAnswer((i) => Stream.value([
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
]));
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
|
]));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -78,7 +79,6 @@ void main() {
|
|||||||
message: Message(
|
message: Message(
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
),
|
),
|
||||||
sentAt: DateTime.now(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,17 +15,17 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +37,9 @@ void main() {
|
|||||||
child: WillPopScope(
|
child: WillPopScope(
|
||||||
onWillPop: () async => false,
|
onWillPop: () async => false,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: ImageFooter(),
|
body: ImageFooter(
|
||||||
|
message: Message(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -13,8 +13,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -43,8 +43,8 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
setUpAll(() {
|
||||||
|
registerFallbackValue(MaterialPageRoute(builder: (context) => SizedBox()));
|
||||||
|
registerFallbackValue(Message());
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'it should show the all actions',
|
'it should show the all actions',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
@@ -55,11 +60,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
@@ -102,11 +107,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -156,11 +161,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
|
|
||||||
@@ -173,7 +178,7 @@ void main() {
|
|||||||
child: Container(
|
child: Container(
|
||||||
child: MessageActionsModal(
|
child: MessageActionsModal(
|
||||||
onReplyTap: (m) {
|
onReplyTap: (m) {
|
||||||
return tapped = true;
|
tapped = true;
|
||||||
},
|
},
|
||||||
message: Message(
|
message: Message(
|
||||||
text: 'test',
|
text: 'test',
|
||||||
@@ -201,11 +206,11 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
|
|
||||||
@@ -218,7 +223,7 @@ void main() {
|
|||||||
child: Container(
|
child: Container(
|
||||||
child: MessageActionsModal(
|
child: MessageActionsModal(
|
||||||
onThreadReplyTap: (m) {
|
onThreadReplyTap: (m) {
|
||||||
return tapped = true;
|
tapped = true;
|
||||||
},
|
},
|
||||||
message: Message(
|
message: Message(
|
||||||
text: 'test',
|
text: 'test',
|
||||||
@@ -247,12 +252,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -298,12 +302,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -352,12 +355,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
|
|
||||||
@@ -404,12 +406,13 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.sendMessage(any()))
|
||||||
|
.thenAnswer((_) async => SendMessageResponse());
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -443,7 +446,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.tap(find.text('Resend'));
|
await tester.tap(find.text('Resend'));
|
||||||
|
|
||||||
verify(channel.sendMessage(any)).called(1);
|
verify(() => channel.sendMessage(any())).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -454,12 +457,13 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.updateMessage(any()))
|
||||||
|
.thenAnswer((_) async => UpdateMessageResponse());
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -493,7 +497,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.tap(find.text('Resend Edited Message'));
|
await tester.tap(find.text('Resend Edited Message'));
|
||||||
|
|
||||||
verify(channel.updateMessage(any)).called(1);
|
verify(() => channel.updateMessage(any())).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -504,12 +508,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -549,7 +552,7 @@ void main() {
|
|||||||
await tester.tap(find.text('FLAG'));
|
await tester.tap(find.text('FLAG'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
verify(client.flagMessage('testid')).called(1);
|
verify(() => client.flagMessage('testid')).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -560,16 +563,15 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => client.flagMessage(any())).thenThrow(ApiError(
|
||||||
when(client.flagMessage(any)).thenThrow(ApiError(
|
|
||||||
'{}',
|
'{}',
|
||||||
500,
|
500,
|
||||||
));
|
));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -620,16 +622,15 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => client.flagMessage(any())).thenThrow(ApiError(
|
||||||
when(client.flagMessage(any)).thenThrow(ApiError(
|
|
||||||
'{"code":4}',
|
'{"code":4}',
|
||||||
400,
|
400,
|
||||||
));
|
));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -680,12 +681,11 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@@ -725,7 +725,7 @@ void main() {
|
|||||||
await tester.tap(find.text('DELETE'));
|
await tester.tap(find.text('DELETE'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
verify(channel.deleteMessage(any)).called(1);
|
verify(() => channel.deleteMessage(any())).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -736,16 +736,15 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.deleteMessage(any())).thenThrow(ApiError(
|
||||||
when(channel.deleteMessage(any)).thenThrow(ApiError(
|
|
||||||
'{}',
|
'{}',
|
||||||
500,
|
500,
|
||||||
));
|
));
|
||||||
|
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,51 +15,52 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(channelState.messages).thenReturn([
|
when(() => channelState.messages).thenReturn([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
when(channelState.typingEvents).thenAnswer((i) => [
|
when(() => channelState.typingEvents).thenAnswer((i) => [
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||||
]);
|
]);
|
||||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.typingEventsStream)
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
.thenAnswer((i) => Stream.value([
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
]));
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
|
]));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
@@ -15,10 +15,10 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
id: 'test',
|
id: 'test',
|
||||||
@@ -75,10 +75,10 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
id: 'test',
|
id: 'test',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -16,19 +16,19 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
class MockClient extends Mock implements StreamChatClient {}
|
class MockClient extends Mock implements StreamChatClient {}
|
||||||
|
|
||||||
class MockClientState extends Mock implements ClientState {}
|
class MockClientState extends Mock implements ClientState {}
|
||||||
|
|
||||||
class MockChannel extends Mock implements Channel {}
|
class MockChannel extends Mock implements Channel {
|
||||||
|
@override
|
||||||
|
Future<bool> get initialized async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> keyStroke([String? parentId]) async {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MockChannelState extends Mock implements ChannelClientState {}
|
class MockChannelState extends Mock implements ChannelClientState {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
@@ -39,10 +39,10 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData.light();
|
final themeData = ThemeData.light();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
final theme = StreamChatThemeData.fromTheme(themeData);
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
StreamChat(
|
StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
@@ -55,9 +55,9 @@ void main() {
|
|||||||
user: User(id: 'test'),
|
user: User(id: 'test'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
borderColor: theme.ownMessageTheme.reactionsBorderColor!,
|
||||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!,
|
||||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -73,15 +73,15 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData.dark();
|
final themeData = ThemeData.dark();
|
||||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
final theme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
StreamChat(
|
StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
streamChatThemeData: StreamChatThemeData.fromTheme(themeData),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: ReactionBubble(
|
child: ReactionBubble(
|
||||||
@@ -91,9 +91,9 @@ void main() {
|
|||||||
user: User(id: 'test'),
|
user: User(id: 'test'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
borderColor: theme.ownMessageTheme.reactionsBorderColor!,
|
||||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!,
|
||||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -109,15 +109,15 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData.light();
|
final themeData = ThemeData.light();
|
||||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
final theme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
StreamChat(
|
StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
streamChatThemeData: StreamChatThemeData.fromTheme(themeData),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: ReactionBubble(
|
child: ReactionBubble(
|
||||||
@@ -135,9 +135,9 @@ void main() {
|
|||||||
user: User(id: 'test'),
|
user: User(id: 'test'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
borderColor: theme.ownMessageTheme.reactionsBorderColor!,
|
||||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!,
|
||||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -153,15 +153,15 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData.dark();
|
final themeData = ThemeData.dark();
|
||||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
final theme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
StreamChat(
|
StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
streamChatThemeData: StreamChatThemeData.fromTheme(themeData),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: ReactionBubble(
|
child: ReactionBubble(
|
||||||
@@ -179,9 +179,9 @@ void main() {
|
|||||||
user: User(id: 'test'),
|
user: User(id: 'test'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
borderColor: theme.ownMessageTheme.reactionsBorderColor!,
|
||||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!,
|
||||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -198,13 +198,13 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
StreamChat(
|
StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
streamChatThemeData: StreamChatThemeData.fromTheme(themeData),
|
||||||
child: Container(
|
child: Container(
|
||||||
child: ReactionBubble(
|
child: ReactionBubble(
|
||||||
reactions: [
|
reactions: [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
class SimpleFrame extends StatelessWidget {
|
class SimpleFrame extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const SimpleFrame({Key key, @required this.child}) : super(key: key);
|
const SimpleFrame({Key? key, required this.child}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -16,20 +16,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
var tapped = false;
|
var tapped = false;
|
||||||
@@ -67,21 +67,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
@@ -119,21 +118,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
await tester.pumpWidgetBuilder(
|
await tester.pumpWidgetBuilder(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,34 +15,38 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
when(() => client.wsConnectionStatusStream)
|
||||||
|
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||||
|
when(() => clientState.totalUnreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -75,29 +79,29 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.unreadCount).thenReturn(1);
|
when(() => channelState.unreadCount).thenReturn(1);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
when(() => channelState.unreadCountStream)
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
.thenAnswer((i) => Stream.value(1));
|
||||||
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,51 +15,52 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.isMuted).thenReturn(false);
|
when(() => channel.isMuted).thenReturn(false);
|
||||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
when(channelState.members).thenReturn([
|
when(() => channelState.members).thenReturn([
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(id: 'user-id'),
|
user: User(id: 'user-id'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
when(channelState.messages).thenReturn([
|
when(() => channelState.messages).thenReturn([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||||
Message(
|
Message(
|
||||||
text: 'hello',
|
text: 'hello',
|
||||||
user: User(id: 'other-user'),
|
user: User(id: 'other-user'),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
when(channelState.typingEvents).thenAnswer((i) => [
|
when(() => channelState.typingEvents).thenAnswer((i) => [
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||||
]);
|
]);
|
||||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
when(() => channelState.typingEventsStream)
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
.thenAnswer((i) => Stream.value([
|
||||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
]));
|
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||||
|
]));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
@@ -15,20 +15,20 @@ void main() {
|
|||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
}));
|
}));
|
||||||
when(channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
when(clientState.totalUnreadCount).thenReturn(10);
|
when(() => clientState.totalUnreadCount).thenReturn(10);
|
||||||
when(clientState.totalUnreadCountStream)
|
when(() => clientState.totalUnreadCountStream)
|
||||||
.thenAnswer((i) => Stream.value(10));
|
.thenAnswer((i) => Stream.value(10));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
@@ -54,19 +54,20 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
when(channel.cid).thenReturn('cid');
|
when(() => channel.cid).thenReturn('cid');
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(clientState.channels).thenReturn({
|
when(() => clientState.channels).thenReturn({
|
||||||
channel.cid: channel,
|
channel.cid!: channel,
|
||||||
});
|
});
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channelState.unreadCount).thenReturn(0);
|
when(() => channelState.unreadCount).thenReturn(0);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(0));
|
when(() => channelState.unreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(0));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -92,20 +93,21 @@ void main() {
|
|||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
when(channel.cid).thenReturn('cid');
|
when(() => channel.cid).thenReturn('cid');
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||||
|
|
||||||
when(client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||||
when(clientState.channels).thenReturn({
|
when(() => clientState.channels).thenReturn({
|
||||||
channel.cid: channel,
|
channel.cid!: channel,
|
||||||
});
|
});
|
||||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||||
when(channel.state).thenReturn(channelState);
|
when(() => channel.state).thenReturn(channelState);
|
||||||
when(channel.client).thenReturn(client);
|
when(() => channel.client).thenReturn(client);
|
||||||
when(channelState.unreadCount).thenReturn(100);
|
when(() => channelState.unreadCount).thenReturn(100);
|
||||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(100));
|
when(() => channelState.unreadCountStream)
|
||||||
|
.thenAnswer((i) => Stream.value(100));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
|
|||||||
import 'package:stream_chat_persistence/src/entity/channel_queries.dart';
|
import 'package:stream_chat_persistence/src/entity/channel_queries.dart';
|
||||||
import 'package:stream_chat_persistence/src/entity/channels.dart';
|
import 'package:stream_chat_persistence/src/entity/channels.dart';
|
||||||
import 'package:stream_chat_persistence/src/entity/users.dart';
|
import 'package:stream_chat_persistence/src/entity/users.dart';
|
||||||
|
|
||||||
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
|
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
|
||||||
|
|
||||||
part 'channel_query_dao.g.dart';
|
part 'channel_query_dao.g.dart';
|
||||||
@@ -93,7 +92,7 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
|
|
||||||
final possibleSortingFields = cachedChannels.fold<List<String>>(
|
final possibleSortingFields = cachedChannels.fold<List<String>>(
|
||||||
ChannelModel.topLevelFields, (previousValue, element) {
|
ChannelModel.topLevelFields, (previousValue, element) {
|
||||||
final extraData = element.extraData ?? {};
|
final extraData = element.extraData;
|
||||||
return {...previousValue, ...extraData.keys}.toList();
|
return {...previousValue, ...extraData.keys}.toList();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user