Merge pull request #549 from GetStream/fix/channel-image
fix(ui-kit)!: channel image
This commit is contained in:
@@ -114,7 +114,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
leading: ChannelImage(
|
leading: ChannelAvatar(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
),
|
),
|
||||||
title: ChannelName(
|
title: ChannelName(
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/group_avatar.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
|
/// 
|
||||||
|
/// 
|
||||||
|
///
|
||||||
|
/// It shows the current [Channel] image.
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// class MyApp extends StatelessWidget {
|
||||||
|
/// final StreamChatClient client;
|
||||||
|
/// final Channel channel;
|
||||||
|
///
|
||||||
|
/// MyApp(this.client, this.channel);
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Widget build(BuildContext context) {
|
||||||
|
/// return MaterialApp(
|
||||||
|
/// debugShowCheckedModeBanner: false,
|
||||||
|
/// home: StreamChat(
|
||||||
|
/// client: client,
|
||||||
|
/// child: StreamChannel(
|
||||||
|
/// channel: channel,
|
||||||
|
/// child: Center(
|
||||||
|
/// child: ChannelImage(
|
||||||
|
/// channel: channel,
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The widget uses a [StreamBuilder] to render the channel information
|
||||||
|
/// image as soon as it updates.
|
||||||
|
///
|
||||||
|
/// By default the widget radius size is 40x40 pixels.
|
||||||
|
/// Set the property [constraints] to set a custom dimension.
|
||||||
|
///
|
||||||
|
/// The widget renders the ui based on the first ancestor of type
|
||||||
|
/// [StreamChatTheme].
|
||||||
|
/// Modify it to change the widget appearance.
|
||||||
|
class ChannelAvatar extends StatelessWidget {
|
||||||
|
/// Instantiate a new ChannelImage
|
||||||
|
const ChannelAvatar({
|
||||||
|
Key? key,
|
||||||
|
this.channel,
|
||||||
|
this.constraints,
|
||||||
|
this.onTap,
|
||||||
|
this.borderRadius,
|
||||||
|
this.selected = false,
|
||||||
|
this.selectionColor,
|
||||||
|
this.selectionThickness = 4,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// [BorderRadius] to display the widget
|
||||||
|
final BorderRadius? borderRadius;
|
||||||
|
|
||||||
|
/// The channel to show the image of
|
||||||
|
final Channel? channel;
|
||||||
|
|
||||||
|
/// The diameter of the image
|
||||||
|
final BoxConstraints? constraints;
|
||||||
|
|
||||||
|
/// The function called when the image is tapped
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
/// If image is selected
|
||||||
|
final bool selected;
|
||||||
|
|
||||||
|
/// Selection color for image
|
||||||
|
final Color? selectionColor;
|
||||||
|
|
||||||
|
/// Thickness of selection image
|
||||||
|
final double selectionThickness;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final streamChat = StreamChat.of(context);
|
||||||
|
final channel = this.channel ?? StreamChannel.of(context).channel;
|
||||||
|
|
||||||
|
assert(channel.state != null, 'Channel ${channel.id} is not initialized');
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
|
final colorTheme = chatThemeData.colorTheme;
|
||||||
|
final previewTheme = chatThemeData.channelPreviewTheme.avatarTheme;
|
||||||
|
|
||||||
|
return BetterStreamBuilder<Map<String, dynamic>>(
|
||||||
|
stream: channel.extraDataStream,
|
||||||
|
initialData: channel.extraData,
|
||||||
|
builder: (context, extraData) {
|
||||||
|
final channelImage = extraData['image'];
|
||||||
|
|
||||||
|
if (channelImage != null) {
|
||||||
|
Widget child = ClipRRect(
|
||||||
|
borderRadius: borderRadius ?? previewTheme?.borderRadius,
|
||||||
|
child: Container(
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
decoration: BoxDecoration(color: colorTheme.accentPrimary),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: channelImage,
|
||||||
|
errorWidget: (_, __, ___) => Center(
|
||||||
|
child: Text(
|
||||||
|
extraData['name']?[0] ?? '',
|
||||||
|
style: TextStyle(
|
||||||
|
color: colorTheme.barsBg,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
child = ClipRRect(
|
||||||
|
key: const Key('selectedImage'),
|
||||||
|
borderRadius: BorderRadius.circular(selectionThickness) +
|
||||||
|
(borderRadius ??
|
||||||
|
previewTheme?.borderRadius ??
|
||||||
|
BorderRadius.zero),
|
||||||
|
child: Container(
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
color: selectionColor ?? colorTheme.accentPrimary,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(selectionThickness),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentUser = streamChat.user!;
|
||||||
|
final otherMembers = channel.state!.members
|
||||||
|
.where((it) => it.userId != currentUser.id)
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
// our own space, no other members
|
||||||
|
if (otherMembers.isEmpty) {
|
||||||
|
return BetterStreamBuilder<User>(
|
||||||
|
stream: streamChat.client.state.userStream.map((it) => it!),
|
||||||
|
initialData: currentUser,
|
||||||
|
builder: (context, user) => UserAvatar(
|
||||||
|
borderRadius: borderRadius ?? previewTheme?.borderRadius,
|
||||||
|
user: user,
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
onTap: onTap != null ? (_) => onTap!() : null,
|
||||||
|
selected: selected,
|
||||||
|
selectionColor: selectionColor ?? colorTheme.accentPrimary,
|
||||||
|
selectionThickness: selectionThickness,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1-1 Conversation
|
||||||
|
if (otherMembers.length == 1) {
|
||||||
|
final member = otherMembers.first;
|
||||||
|
return BetterStreamBuilder<Member>(
|
||||||
|
stream: channel.state!.membersStream.map(
|
||||||
|
(members) => members.firstWhere(
|
||||||
|
(it) => it.userId == member.userId,
|
||||||
|
orElse: () => member,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initialData: member,
|
||||||
|
builder: (context, member) => UserAvatar(
|
||||||
|
borderRadius: borderRadius ?? previewTheme?.borderRadius,
|
||||||
|
user: member.user!,
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
onTap: onTap != null ? (_) => onTap!() : null,
|
||||||
|
selected: selected,
|
||||||
|
selectionColor: selectionColor ?? colorTheme.accentPrimary,
|
||||||
|
selectionThickness: selectionThickness,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group conversation
|
||||||
|
return GroupAvatar(
|
||||||
|
members: otherMembers,
|
||||||
|
borderRadius: borderRadius ?? previewTheme?.borderRadius,
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
selectionColor: selectionColor ?? colorTheme.accentPrimary,
|
||||||
|
selectionThickness: selectionThickness,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,7 +98,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// AppBar actions
|
/// AppBar actions
|
||||||
/// By default it shows the [ChannelImage]
|
/// By default it shows the [ChannelAvatar]
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -147,7 +147,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
padding: const EdgeInsets.only(right: 10),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: ChannelImage(
|
child: ChannelAvatar(
|
||||||
borderRadius: chatThemeData.channelTheme
|
borderRadius: chatThemeData.channelTheme
|
||||||
.channelHeaderTheme.avatarTheme?.borderRadius,
|
.channelHeaderTheme.avatarTheme?.borderRadius,
|
||||||
constraints: chatThemeData.channelTheme
|
constraints: chatThemeData.channelTheme
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/group_image.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|
||||||
|
|
||||||
/// 
|
|
||||||
/// 
|
|
||||||
///
|
|
||||||
/// It shows the current [Channel] image.
|
|
||||||
///
|
|
||||||
/// ```dart
|
|
||||||
/// class MyApp extends StatelessWidget {
|
|
||||||
/// final StreamChatClient client;
|
|
||||||
/// final Channel channel;
|
|
||||||
///
|
|
||||||
/// MyApp(this.client, this.channel);
|
|
||||||
///
|
|
||||||
/// @override
|
|
||||||
/// Widget build(BuildContext context) {
|
|
||||||
/// return MaterialApp(
|
|
||||||
/// debugShowCheckedModeBanner: false,
|
|
||||||
/// home: StreamChat(
|
|
||||||
/// client: client,
|
|
||||||
/// child: StreamChannel(
|
|
||||||
/// channel: channel,
|
|
||||||
/// child: Center(
|
|
||||||
/// child: ChannelImage(
|
|
||||||
/// channel: channel,
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The widget uses a [StreamBuilder] to render the channel information
|
|
||||||
/// image as soon as it updates.
|
|
||||||
///
|
|
||||||
/// By default the widget radius size is 40x40 pixels.
|
|
||||||
/// Set the property [constraints] to set a custom dimension.
|
|
||||||
///
|
|
||||||
/// The widget renders the ui based on the first ancestor of type
|
|
||||||
/// [StreamChatTheme].
|
|
||||||
/// Modify it to change the widget appearance.
|
|
||||||
class ChannelImage extends StatelessWidget {
|
|
||||||
/// Instantiate a new ChannelImage
|
|
||||||
const ChannelImage({
|
|
||||||
Key? key,
|
|
||||||
this.channel,
|
|
||||||
this.constraints,
|
|
||||||
this.onTap,
|
|
||||||
this.borderRadius,
|
|
||||||
this.selected = false,
|
|
||||||
this.selectionColor,
|
|
||||||
this.selectionThickness = 4,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
/// [BorderRadius] to display the widget
|
|
||||||
final BorderRadius? borderRadius;
|
|
||||||
|
|
||||||
/// The channel to show the image of
|
|
||||||
final Channel? channel;
|
|
||||||
|
|
||||||
/// The diameter of the image
|
|
||||||
final BoxConstraints? constraints;
|
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
|
|
||||||
/// If image is selected
|
|
||||||
final bool selected;
|
|
||||||
|
|
||||||
/// Selection color for image
|
|
||||||
final Color? selectionColor;
|
|
||||||
|
|
||||||
/// Thickness of selection image
|
|
||||||
final double selectionThickness;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final streamChat = StreamChat.of(context);
|
|
||||||
final channel = this.channel ?? StreamChannel.of(context).channel;
|
|
||||||
return BetterStreamBuilder<Map<String, dynamic>>(
|
|
||||||
stream: channel.extraDataStream,
|
|
||||||
initialData: channel.extraData,
|
|
||||||
builder: (context, data) {
|
|
||||||
String? image;
|
|
||||||
final chatThemeData = StreamChatTheme.of(context);
|
|
||||||
if (data.containsKey('image') == true) {
|
|
||||||
image = data['image'];
|
|
||||||
} else if (channel.state?.members.length == 2) {
|
|
||||||
final otherMember = channel.state?.members
|
|
||||||
.firstWhere((member) => member.user?.id != streamChat.user?.id);
|
|
||||||
return BetterStreamBuilder<User?>(
|
|
||||||
stream: streamChat.client.state.usersStream
|
|
||||||
.map((users) =>
|
|
||||||
users[otherMember?.userId] ?? otherMember!.user!)
|
|
||||||
.distinct(),
|
|
||||||
initialData: otherMember!.user,
|
|
||||||
builder: (context, user) => UserAvatar(
|
|
||||||
borderRadius: borderRadius ??
|
|
||||||
chatThemeData
|
|
||||||
.channelPreviewTheme.avatarTheme?.borderRadius,
|
|
||||||
user: user ?? otherMember.user!,
|
|
||||||
constraints: constraints ??
|
|
||||||
chatThemeData
|
|
||||||
.channelPreviewTheme.avatarTheme?.constraints,
|
|
||||||
onTap: onTap != null ? (_) => onTap!() : null,
|
|
||||||
selected: selected,
|
|
||||||
selectionColor: selectionColor ??
|
|
||||||
chatThemeData.colorTheme.accentPrimary,
|
|
||||||
selectionThickness: selectionThickness,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
final images = channel.state?.members
|
|
||||||
.where((member) =>
|
|
||||||
member.user?.id != streamChat.user?.id &&
|
|
||||||
member.user?.extraData['image'] != null)
|
|
||||||
.take(4)
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
.map((e) => e.user?.extraData['image'] as String)
|
|
||||||
.toList();
|
|
||||||
return GroupImage(
|
|
||||||
images: images ?? [],
|
|
||||||
borderRadius: borderRadius ??
|
|
||||||
chatThemeData.channelPreviewTheme.avatarTheme?.borderRadius,
|
|
||||||
constraints: constraints ??
|
|
||||||
chatThemeData.channelPreviewTheme.avatarTheme?.constraints,
|
|
||||||
onTap: onTap,
|
|
||||||
selected: selected,
|
|
||||||
selectionColor:
|
|
||||||
selectionColor ?? chatThemeData.colorTheme.accentPrimary,
|
|
||||||
selectionThickness: selectionThickness,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget child = ClipRRect(
|
|
||||||
borderRadius: borderRadius ??
|
|
||||||
chatThemeData.channelPreviewTheme.avatarTheme?.borderRadius,
|
|
||||||
child: Container(
|
|
||||||
constraints: constraints ??
|
|
||||||
chatThemeData.channelPreviewTheme.avatarTheme?.constraints,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: chatThemeData.colorTheme.accentPrimary,
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: <Widget>[
|
|
||||||
if (image != null)
|
|
||||||
CachedNetworkImage(
|
|
||||||
imageUrl: image,
|
|
||||||
errorWidget: (_, __, ___) => Center(
|
|
||||||
child: Text(
|
|
||||||
data.containsKey('name') ? data['name'][0] : '',
|
|
||||||
style: TextStyle(
|
|
||||||
color: chatThemeData.colorTheme.barsBg,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
chatThemeData.defaultChannelImage(
|
|
||||||
context,
|
|
||||||
channel,
|
|
||||||
),
|
|
||||||
Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (selected) {
|
|
||||||
child = ClipRRect(
|
|
||||||
key: const Key('selectedImage'),
|
|
||||||
borderRadius: (borderRadius ??
|
|
||||||
chatThemeData.ownMessageTheme.avatarTheme?.borderRadius ??
|
|
||||||
BorderRadius.zero) +
|
|
||||||
BorderRadius.circular(selectionThickness),
|
|
||||||
child: Container(
|
|
||||||
constraints: constraints ??
|
|
||||||
chatThemeData.ownMessageTheme.avatarTheme?.constraints,
|
|
||||||
color: selectionColor ?? chatThemeData.colorTheme.accentPrimary,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(selectionThickness),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -620,7 +620,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ChannelImage(
|
ChannelAvatar(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
borderRadius: BorderRadius.circular(32),
|
borderRadius: BorderRadius.circular(32),
|
||||||
selected: selected,
|
selected: selected,
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
/// Widget rendering the leading element, by default
|
/// Widget rendering the leading element, by default
|
||||||
/// it shows the [ChannelImage]
|
/// it shows the [ChannelAvatar]
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
|
||||||
/// Widget rendering the trailing element,
|
/// Widget rendering the trailing element,
|
||||||
@@ -81,20 +81,9 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () => onTap?.call(channel),
|
||||||
if (onTap != null) {
|
onLongPress: () => onLongPress?.call(channel),
|
||||||
onTap!(channel);
|
leading: leading ?? ChannelAvatar(onTap: onImageTap),
|
||||||
}
|
|
||||||
},
|
|
||||||
onLongPress: () {
|
|
||||||
if (onLongPress != null) {
|
|
||||||
onLongPress!(channel);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leading: leading ??
|
|
||||||
ChannelImage(
|
|
||||||
onTap: onImageTap,
|
|
||||||
),
|
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
/// Widget for constructing a group of images
|
||||||
|
class GroupAvatar extends StatelessWidget {
|
||||||
|
/// Constructor for creating a [GroupAvatar]
|
||||||
|
const GroupAvatar({
|
||||||
|
Key? key,
|
||||||
|
required this.members,
|
||||||
|
this.constraints,
|
||||||
|
this.onTap,
|
||||||
|
this.borderRadius,
|
||||||
|
this.selected = false,
|
||||||
|
this.selectionColor,
|
||||||
|
this.selectionThickness = 4,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// List of images to display
|
||||||
|
final List<Member> members;
|
||||||
|
|
||||||
|
/// Constraints on the widget
|
||||||
|
final BoxConstraints? constraints;
|
||||||
|
|
||||||
|
/// Callback when widget is tapped
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
/// Highlights if selected
|
||||||
|
final bool selected;
|
||||||
|
|
||||||
|
/// [BorderRadius] to pass to the widget
|
||||||
|
final BorderRadius? borderRadius;
|
||||||
|
|
||||||
|
/// Color of selection if selected
|
||||||
|
final Color? selectionColor;
|
||||||
|
|
||||||
|
/// Thickness with which color of selection is shown
|
||||||
|
final double selectionThickness;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
|
assert(channel.state != null, 'Channel ${channel.id} is not initialized');
|
||||||
|
|
||||||
|
final streamChatTheme = StreamChatTheme.of(context);
|
||||||
|
final colorTheme = streamChatTheme.colorTheme;
|
||||||
|
final previewTheme = streamChatTheme.channelPreviewTheme.avatarTheme;
|
||||||
|
|
||||||
|
Widget avatar = GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: borderRadius ?? previewTheme?.borderRadius,
|
||||||
|
child: Container(
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
decoration: BoxDecoration(color: colorTheme.accentPrimary),
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.vertical,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: members
|
||||||
|
.take(2)
|
||||||
|
.map(
|
||||||
|
(member) => Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: 1.2,
|
||||||
|
child: BetterStreamBuilder<Member>(
|
||||||
|
stream: channel.state!.membersStream.map(
|
||||||
|
(members) => members.firstWhere(
|
||||||
|
(it) => it.userId == member.userId,
|
||||||
|
orElse: () => member,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initialData: member,
|
||||||
|
builder: (context, member) => UserAvatar(
|
||||||
|
user: member.user!,
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (members.length > 2)
|
||||||
|
Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: members
|
||||||
|
.skip(2)
|
||||||
|
.take(2)
|
||||||
|
.map(
|
||||||
|
(member) => Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: 1.2,
|
||||||
|
child: BetterStreamBuilder<Member>(
|
||||||
|
stream: channel.state!.membersStream.map(
|
||||||
|
(members) => members.firstWhere(
|
||||||
|
(it) => it.userId == member.userId,
|
||||||
|
orElse: () => member,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initialData: member,
|
||||||
|
builder: (context, member) => UserAvatar(
|
||||||
|
user: member.user!,
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
avatar = ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(selectionThickness) +
|
||||||
|
(borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero),
|
||||||
|
child: Container(
|
||||||
|
constraints: constraints ?? previewTheme?.constraints,
|
||||||
|
color: selectionColor ?? colorTheme.accentPrimary,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(selectionThickness),
|
||||||
|
child: avatar,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
|
|
||||||
/// Widget for constructing a group of images
|
|
||||||
class GroupImage extends StatelessWidget {
|
|
||||||
/// Constructor for creating a [GroupImage]
|
|
||||||
const GroupImage({
|
|
||||||
Key? key,
|
|
||||||
required this.images,
|
|
||||||
this.constraints,
|
|
||||||
this.onTap,
|
|
||||||
this.borderRadius,
|
|
||||||
this.selected = false,
|
|
||||||
this.selectionColor,
|
|
||||||
this.selectionThickness = 4,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
/// List of images to display
|
|
||||||
final List<String> images;
|
|
||||||
|
|
||||||
/// Constraints on the widget
|
|
||||||
final BoxConstraints? constraints;
|
|
||||||
|
|
||||||
/// Callback when widget is tapped
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
|
|
||||||
/// Highlights if selected
|
|
||||||
final bool selected;
|
|
||||||
|
|
||||||
/// [BorderRadius] to pass to the widget
|
|
||||||
final BorderRadius? borderRadius;
|
|
||||||
|
|
||||||
/// Color of selection if selected
|
|
||||||
final Color? selectionColor;
|
|
||||||
|
|
||||||
/// Thickness with which color of selection is shown
|
|
||||||
final double selectionThickness;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Widget? avatar;
|
|
||||||
final streamChatTheme = StreamChatTheme.of(context);
|
|
||||||
|
|
||||||
avatar = GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: borderRadius ??
|
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius,
|
|
||||||
child: Container(
|
|
||||||
constraints: constraints ??
|
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: streamChatTheme.colorTheme.accentPrimary,
|
|
||||||
),
|
|
||||||
child: Flex(
|
|
||||||
direction: Axis.vertical,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: Flex(
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: images
|
|
||||||
.take(2)
|
|
||||||
.map((url) => Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
child: Transform.scale(
|
|
||||||
scale: 1.2,
|
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: url,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (images.length > 2)
|
|
||||||
Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: Flex(
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: images
|
|
||||||
.skip(2)
|
|
||||||
.map((url) => Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
child: Transform.scale(
|
|
||||||
scale: 1.2,
|
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: url,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selected) {
|
|
||||||
avatar = ClipRRect(
|
|
||||||
borderRadius: (borderRadius ??
|
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ??
|
|
||||||
BorderRadius.zero) +
|
|
||||||
BorderRadius.circular(selectionThickness),
|
|
||||||
child: Container(
|
|
||||||
color: selectionColor ?? streamChatTheme.colorTheme.accentPrimary,
|
|
||||||
height: 64,
|
|
||||||
width: 64,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(selectionThickness),
|
|
||||||
child: avatar,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1859,11 +1859,11 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (camera) {
|
if (camera) {
|
||||||
PickedFile? pickedFile;
|
XFile? pickedFile;
|
||||||
if (fileType == DefaultAttachmentTypes.image) {
|
if (fileType == DefaultAttachmentTypes.image) {
|
||||||
pickedFile = await _imagePicker.getImage(source: ImageSource.camera);
|
pickedFile = await _imagePicker.pickImage(source: ImageSource.camera);
|
||||||
} else if (fileType == DefaultAttachmentTypes.video) {
|
} else if (fileType == DefaultAttachmentTypes.video) {
|
||||||
pickedFile = await _imagePicker.getVideo(source: ImageSource.camera);
|
pickedFile = await _imagePicker.pickVideo(source: ImageSource.camera);
|
||||||
}
|
}
|
||||||
if (pickedFile == null) {
|
if (pickedFile == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ class StreamChatThemeData {
|
|||||||
MessageTheme? otherMessageTheme,
|
MessageTheme? otherMessageTheme,
|
||||||
MessageTheme? ownMessageTheme,
|
MessageTheme? ownMessageTheme,
|
||||||
MessageInputTheme? messageInputTheme,
|
MessageInputTheme? messageInputTheme,
|
||||||
Widget Function(BuildContext, Channel)? defaultChannelImage,
|
|
||||||
Widget Function(BuildContext, User)? defaultUserImage,
|
Widget Function(BuildContext, User)? defaultUserImage,
|
||||||
IconThemeData? primaryIconTheme,
|
IconThemeData? primaryIconTheme,
|
||||||
List<ReactionIcon>? reactionIcons,
|
List<ReactionIcon>? reactionIcons,
|
||||||
@@ -79,7 +78,6 @@ class StreamChatThemeData {
|
|||||||
otherMessageTheme: otherMessageTheme,
|
otherMessageTheme: otherMessageTheme,
|
||||||
ownMessageTheme: ownMessageTheme,
|
ownMessageTheme: ownMessageTheme,
|
||||||
messageInputTheme: messageInputTheme,
|
messageInputTheme: messageInputTheme,
|
||||||
defaultChannelImage: defaultChannelImage,
|
|
||||||
defaultUserImage: defaultUserImage,
|
defaultUserImage: defaultUserImage,
|
||||||
primaryIconTheme: primaryIconTheme,
|
primaryIconTheme: primaryIconTheme,
|
||||||
reactionIcons: reactionIcons,
|
reactionIcons: reactionIcons,
|
||||||
@@ -108,7 +106,6 @@ class StreamChatThemeData {
|
|||||||
required this.otherMessageTheme,
|
required this.otherMessageTheme,
|
||||||
required this.ownMessageTheme,
|
required this.ownMessageTheme,
|
||||||
required this.messageInputTheme,
|
required this.messageInputTheme,
|
||||||
required this.defaultChannelImage,
|
|
||||||
required this.defaultUserImage,
|
required this.defaultUserImage,
|
||||||
required this.primaryIconTheme,
|
required this.primaryIconTheme,
|
||||||
required this.reactionIcons,
|
required this.reactionIcons,
|
||||||
@@ -160,9 +157,6 @@ class StreamChatThemeData {
|
|||||||
/// Theme dedicated to the [MessageInput] widget
|
/// Theme dedicated to the [MessageInput] widget
|
||||||
final MessageInputTheme messageInputTheme;
|
final MessageInputTheme messageInputTheme;
|
||||||
|
|
||||||
/// The widget that will be built when the channel image is unavailable
|
|
||||||
final Widget Function(BuildContext, Channel) defaultChannelImage;
|
|
||||||
|
|
||||||
/// The widget that will be built when the user image is unavailable
|
/// The widget that will be built when the user image is unavailable
|
||||||
final Widget Function(BuildContext, User) defaultUserImage;
|
final Widget Function(BuildContext, User) defaultUserImage;
|
||||||
|
|
||||||
@@ -182,7 +176,6 @@ class StreamChatThemeData {
|
|||||||
MessageTheme? ownMessageTheme,
|
MessageTheme? ownMessageTheme,
|
||||||
MessageTheme? otherMessageTheme,
|
MessageTheme? otherMessageTheme,
|
||||||
MessageInputTheme? messageInputTheme,
|
MessageInputTheme? messageInputTheme,
|
||||||
Widget Function(BuildContext, Channel)? defaultChannelImage,
|
|
||||||
Widget Function(BuildContext, User)? defaultUserImage,
|
Widget Function(BuildContext, User)? defaultUserImage,
|
||||||
IconThemeData? primaryIconTheme,
|
IconThemeData? primaryIconTheme,
|
||||||
ChannelListHeaderTheme? channelListHeaderTheme,
|
ChannelListHeaderTheme? channelListHeaderTheme,
|
||||||
@@ -196,7 +189,6 @@ class StreamChatThemeData {
|
|||||||
textTheme: this.textTheme.merge(textTheme),
|
textTheme: this.textTheme.merge(textTheme),
|
||||||
colorTheme: this.colorTheme.merge(colorTheme),
|
colorTheme: this.colorTheme.merge(colorTheme),
|
||||||
primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme),
|
primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme),
|
||||||
defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage,
|
|
||||||
defaultUserImage: defaultUserImage ?? this.defaultUserImage,
|
defaultUserImage: defaultUserImage ?? this.defaultUserImage,
|
||||||
channelPreviewTheme:
|
channelPreviewTheme:
|
||||||
this.channelPreviewTheme.merge(channelPreviewTheme),
|
this.channelPreviewTheme.merge(channelPreviewTheme),
|
||||||
@@ -218,7 +210,6 @@ class StreamChatThemeData {
|
|||||||
textTheme: textTheme.merge(other.textTheme),
|
textTheme: textTheme.merge(other.textTheme),
|
||||||
colorTheme: colorTheme.merge(other.colorTheme),
|
colorTheme: colorTheme.merge(other.colorTheme),
|
||||||
primaryIconTheme: other.primaryIconTheme,
|
primaryIconTheme: other.primaryIconTheme,
|
||||||
defaultChannelImage: other.defaultChannelImage,
|
|
||||||
defaultUserImage: other.defaultUserImage,
|
defaultUserImage: other.defaultUserImage,
|
||||||
channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme),
|
channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme),
|
||||||
channelTheme: channelTheme.merge(other.channelTheme),
|
channelTheme: channelTheme.merge(other.channelTheme),
|
||||||
@@ -278,7 +269,6 @@ class StreamChatThemeData {
|
|||||||
textTheme: textTheme,
|
textTheme: textTheme,
|
||||||
colorTheme: colorTheme,
|
colorTheme: colorTheme,
|
||||||
primaryIconTheme: iconTheme,
|
primaryIconTheme: iconTheme,
|
||||||
defaultChannelImage: (context, channel) => const SizedBox(),
|
|
||||||
defaultUserImage: (context, user) => Center(
|
defaultUserImage: (context, user) => Center(
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
|
|
||||||
export 'src/attachment/attachment.dart';
|
export 'src/attachment/attachment.dart';
|
||||||
export 'src/back_button.dart';
|
export 'src/back_button.dart';
|
||||||
|
export 'src/channel_avatar.dart';
|
||||||
export 'src/channel_header.dart';
|
export 'src/channel_header.dart';
|
||||||
export 'src/channel_image.dart';
|
|
||||||
export 'src/channel_list_header.dart';
|
export 'src/channel_list_header.dart';
|
||||||
export 'src/channel_list_view.dart';
|
export 'src/channel_list_view.dart';
|
||||||
export 'src/channel_name.dart';
|
export 'src/channel_name.dart';
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ dependencies:
|
|||||||
flutter_svg: ^0.22.0
|
flutter_svg: ^0.22.0
|
||||||
http_parser: ^4.0.0
|
http_parser: ^4.0.0
|
||||||
image_gallery_saver: ^1.6.9
|
image_gallery_saver: ^1.6.9
|
||||||
image_picker: ^0.8.0
|
image_picker: ^0.8.2
|
||||||
jiffy: ^4.1.0
|
jiffy: ^4.1.0
|
||||||
lottie: ^1.0.1
|
lottie: ^1.0.1
|
||||||
meta: ^1.3.0
|
meta: ^1.3.0
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -63,7 +65,7 @@ void main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
expect(find.text('test'), findsOneWidget);
|
expect(find.text('test'), findsOneWidget);
|
||||||
expect(find.byType(ChannelImage), findsOneWidget);
|
expect(find.byType(ChannelAvatar), findsOneWidget);
|
||||||
expect(find.byType(StreamBackButton), findsOneWidget);
|
expect(find.byType(StreamBackButton), findsOneWidget);
|
||||||
expect(find.byType(ChannelInfo), findsOneWidget);
|
expect(find.byType(ChannelInfo), findsOneWidget);
|
||||||
},
|
},
|
||||||
@@ -76,10 +78,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -141,10 +145,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -207,10 +213,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -263,7 +271,7 @@ void main() {
|
|||||||
|
|
||||||
expect(find.text('test'), findsNothing);
|
expect(find.text('test'), findsNothing);
|
||||||
expect(find.byType(StreamBackButton), findsNothing);
|
expect(find.byType(StreamBackButton), findsNothing);
|
||||||
expect(find.byType(ChannelImage), findsNothing);
|
expect(find.byType(ChannelAvatar), findsNothing);
|
||||||
expect(find.byType(ChannelInfo), findsNothing);
|
expect(find.byType(ChannelInfo), findsNothing);
|
||||||
expect(find.text('leading'), findsOneWidget);
|
expect(find.text('leading'), findsOneWidget);
|
||||||
expect(find.text('title'), findsOneWidget);
|
expect(find.text('title'), findsOneWidget);
|
||||||
@@ -281,10 +289,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -346,10 +356,12 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -403,7 +415,7 @@ void main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
await tester.tap(find.byType(StreamBackButton));
|
await tester.tap(find.byType(StreamBackButton));
|
||||||
await tester.tap(find.byType(ChannelImage));
|
await tester.tap(find.byType(ChannelAvatar));
|
||||||
await tester.tap(find.byType(ChannelName));
|
await tester.tap(find.byType(ChannelName));
|
||||||
|
|
||||||
expect(backPressed, true);
|
expect(backPressed, true);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ 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:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat_flutter/src/group_image.dart';
|
import 'package:stream_chat_flutter/src/group_avatar.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';
|
||||||
@@ -35,7 +35,7 @@ void main() {
|
|||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: const Scaffold(
|
child: const Scaffold(
|
||||||
body: ChannelImage(),
|
body: ChannelAvatar(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -113,7 +113,7 @@ void main() {
|
|||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: const Scaffold(
|
child: const Scaffold(
|
||||||
body: ChannelImage(),
|
body: ChannelAvatar(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -132,9 +132,10 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final currentUser = OwnUser(id: 'user-id');
|
||||||
|
|
||||||
when(() => client.state).thenReturn(clientState);
|
when(() => client.state).thenReturn(clientState);
|
||||||
when(() => clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
when(() => clientState.user).thenReturn(currentUser);
|
||||||
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({
|
||||||
@@ -143,36 +144,7 @@ void main() {
|
|||||||
when(() => channel.extraData).thenReturn({
|
when(() => channel.extraData).thenReturn({
|
||||||
'name': 'test',
|
'name': 'test',
|
||||||
});
|
});
|
||||||
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
final members = [
|
||||||
Member(
|
|
||||||
userId: 'user-id',
|
|
||||||
user: User(
|
|
||||||
id: 'user-id',
|
|
||||||
extraData: const {
|
|
||||||
'image': 'testimage1',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Member(
|
|
||||||
userId: 'user-id2',
|
|
||||||
user: User(
|
|
||||||
id: 'user-id2',
|
|
||||||
extraData: const {
|
|
||||||
'image': 'testimage2',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Member(
|
|
||||||
userId: 'user-id3',
|
|
||||||
user: User(
|
|
||||||
id: 'user-id3',
|
|
||||||
extraData: const {
|
|
||||||
'image': 'testimage3',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]));
|
|
||||||
when(() => channelState.members).thenReturn([
|
|
||||||
Member(
|
Member(
|
||||||
userId: 'user-id',
|
userId: 'user-id',
|
||||||
user: User(
|
user: User(
|
||||||
@@ -200,7 +172,10 @@ void main() {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]);
|
];
|
||||||
|
when(() => channelState.members).thenReturn(members);
|
||||||
|
when(() => channelState.membersStream)
|
||||||
|
.thenAnswer((_) => Stream.value(members));
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
@@ -208,17 +183,18 @@ void main() {
|
|||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: const Scaffold(
|
child: const Scaffold(
|
||||||
body: ChannelImage(),
|
body: ChannelAvatar(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
final image = tester.widget<GroupImage>(find.byType(GroupImage));
|
final image = tester.widget<GroupAvatar>(find.byType(GroupAvatar));
|
||||||
expect(image.images, [
|
final otherMembers = members.where((it) => it.userId != currentUser.id);
|
||||||
'testimage2',
|
expect(
|
||||||
'testimage3',
|
image.members.map((it) => it.user?.id),
|
||||||
]);
|
otherMembers.map((it) => it.user?.id),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -249,7 +225,7 @@ void main() {
|
|||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: const Scaffold(
|
child: const Scaffold(
|
||||||
body: ChannelImage(
|
body: ChannelAvatar(
|
||||||
selected: true,
|
selected: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ void main() {
|
|||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
final channel = MockChannel();
|
final channel = MockChannel();
|
||||||
final channelState = MockChannelState();
|
final channelState = MockChannelState();
|
||||||
|
final user = OwnUser(id: 'user-id');
|
||||||
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(user);
|
||||||
|
when(() => clientState.userStream).thenAnswer((_) => Stream.value(user));
|
||||||
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);
|
||||||
@@ -78,7 +80,7 @@ void main() {
|
|||||||
expect(find.text('test name'), findsOneWidget);
|
expect(find.text('test name'), findsOneWidget);
|
||||||
expect(find.text('1'), findsOneWidget);
|
expect(find.text('1'), findsOneWidget);
|
||||||
expect(find.text('hello'), findsOneWidget);
|
expect(find.text('hello'), findsOneWidget);
|
||||||
expect(find.byType(ChannelImage), findsOneWidget);
|
expect(find.byType(ChannelAvatar), findsOneWidget);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user