diff --git a/packages/flutter_widgets/example/lib/custom_theme.dart b/packages/flutter_widgets/example/lib/custom_theme.dart index d8abcb90..817d2e67 100644 --- a/packages/flutter_widgets/example/lib/custom_theme.dart +++ b/packages/flutter_widgets/example/lib/custom_theme.dart @@ -41,11 +41,12 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { final themeData = ThemeData(primarySwatch: Colors.green); final defaultTheme = StreamChatThemeData.fromTheme(themeData); + final colorTheme = defaultTheme.colorTheme; final customTheme = defaultTheme.merge(StreamChatThemeData( ownMessageTheme: MessageTheme( - messageBackgroundColor: StreamChatTheme.of(context).colorTheme.black, + messageBackgroundColor: colorTheme.black, messageText: TextStyle( - color: StreamChatTheme.of(context).colorTheme.white, + color: colorTheme.white, ), avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(8), diff --git a/packages/flutter_widgets/example/lib/customize_channel_preview.dart b/packages/flutter_widgets/example/lib/customize_channel_preview.dart index 29d22a82..9b7deb64 100644 --- a/packages/flutter_widgets/example/lib/customize_channel_preview.dart +++ b/packages/flutter_widgets/example/lib/customize_channel_preview.dart @@ -80,6 +80,17 @@ class ChannelListPage extends StatelessWidget { final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5; return ListTile( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => StreamChannel( + child: ChannelPage(), + channel: channel, + ), + ), + ); + }, leading: ChannelImage( channel: channel, ), diff --git a/packages/flutter_widgets/example/lib/customize_message_widget.dart b/packages/flutter_widgets/example/lib/customize_message_widget.dart index 84e65eab..14233dd7 100644 --- a/packages/flutter_widgets/example/lib/customize_message_widget.dart +++ b/packages/flutter_widgets/example/lib/customize_message_widget.dart @@ -97,7 +97,7 @@ class ChannelPage extends StatelessWidget { List messages, ) { final message = details.message; - final color = details.isMyMessage ? Colors.blueGrey : Colors.blue; + final color = details.isMyMessage ? Colors.red : Colors.blue; if (message.isSystem) { return SizedBox(); } @@ -110,7 +110,10 @@ class ChannelPage extends StatelessWidget { color: color, width: 2, ), - padding: const EdgeInsets.all(2), + padding: const EdgeInsets.symmetric( + vertical: 2, + horizontal: 4, + ), attachmentBorderSide: BorderSide( color: color, width: 2, diff --git a/packages/flutter_widgets/lib/src/channel_list_view.dart b/packages/flutter_widgets/lib/src/channel_list_view.dart index 61a0aabb..0cc7634b 100644 --- a/packages/flutter_widgets/lib/src/channel_list_view.dart +++ b/packages/flutter_widgets/lib/src/channel_list_view.dart @@ -529,123 +529,97 @@ class _ChannelListViewState extends State }; } + final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke; return StreamChannel( key: ValueKey('CHANNEL-${channel.id}'), channel: channel, child: Builder( builder: (context) { - Widget child; - if (widget.channelPreviewBuilder != null) { - child = Stack( - children: [ - widget.channelPreviewBuilder( - context, - channel, - ), - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - onTap(channel, widget.channelWidget); - }, - onLongPress: widget.onChannelLongPress != null - ? () { - widget.onChannelLongPress(channel); - } - : null, + return Slidable( + controller: _slideController, + enabled: widget.swipeToAction, + actionPane: SlidableBehindActionPane(), + actionExtentRatio: 0.12, + closeOnScroll: true, + secondaryActions: [ + IconSlideAction( + color: backgroundColor, + icon: Icons.more_horiz, + onTap: () { + showModalBottomSheet( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(32), + topRight: Radius.circular(32), + ), ), - ), - ), - ], - ); - } else { - final backgroundColor = - StreamChatTheme.of(context).colorTheme.whiteSmoke; - child = Slidable( - controller: _slideController, - enabled: widget.swipeToAction, - actionPane: SlidableBehindActionPane(), - actionExtentRatio: 0.12, - closeOnScroll: true, - secondaryActions: [ + context: context, + builder: (context) { + return StreamChannel( + child: ChannelBottomSheet( + onViewInfoTap: () { + widget.onViewInfoTap(channel); + }, + ), + channel: channel, + ); + }, + ); + }, + ), + if ([ + 'admin', + 'owner', + ].contains(channel.state.members + .firstWhere((m) => m.userId == channel.client.state.user.id, + orElse: () => null) + ?.role)) IconSlideAction( color: backgroundColor, - icon: Icons.more_horiz, - onTap: () { - showModalBottomSheet( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), - ), - context: context, - builder: (context) { - return StreamChannel( - child: ChannelBottomSheet( - onViewInfoTap: () { - widget.onViewInfoTap(channel); - }, - ), - channel: channel, - ); - }, - ); - }, - ), - if ([ - 'admin', - 'owner', - ].contains(channel.state.members - .firstWhere( - (m) => m.userId == channel.client.state.user.id, - orElse: () => null) - ?.role)) - IconSlideAction( - color: backgroundColor, - iconWidget: StreamSvgIcon.delete( - color: StreamChatTheme.of(context).colorTheme.accentRed, - ), - onTap: () async { - final res = await showConfirmationDialog( - context, - title: 'Delete Conversation', - okText: 'DELETE', - question: - 'Are you sure you want to delete this conversation?', - cancelText: 'CANCEL', - icon: StreamSvgIcon.delete( - color: StreamChatTheme.of(context) - .colorTheme - .accentRed, - ), - ); - if (res == true) { - await channel.delete(); - } - }, + iconWidget: StreamSvgIcon.delete( + color: StreamChatTheme.of(context).colorTheme.accentRed, ), - ], - child: Container( - color: StreamChatTheme.of(context).colorTheme.whiteSnow, - child: ChannelPreview( - onLongPress: widget.onChannelLongPress, - channel: channel, - onImageTap: widget.onImageTap != null - ? () { - widget.onImageTap(channel); - } - : null, - onTap: (channel) { - onTap(channel, widget.channelWidget); + onTap: () async { + final res = await showConfirmationDialog( + context, + title: 'Delete Conversation', + okText: 'DELETE', + question: + 'Are you sure you want to delete this conversation?', + cancelText: 'CANCEL', + icon: StreamSvgIcon.delete( + color: + StreamChatTheme.of(context).colorTheme.accentRed, + ), + ); + if (res == true) { + await channel.delete(); + } }, ), - ), - ); - } - return child; + ], + child: Container( + color: StreamChatTheme.of(context).colorTheme.whiteSnow, + child: widget.channelPreviewBuilder != null + ? widget.channelPreviewBuilder( + context, + channel, + ) + : ChannelPreview( + onLongPress: widget.onChannelLongPress, + channel: channel, + onImageTap: widget.onImageTap != null + ? () { + widget.onImageTap(channel); + } + : null, + onTap: (channel) { + onTap(channel, widget.channelWidget); + }, + ), + ), + ); }, ), ); diff --git a/packages/flutter_widgets/lib/src/file_attachment.dart b/packages/flutter_widgets/lib/src/file_attachment.dart index 8e3ef4d6..dc7fae4b 100644 --- a/packages/flutter_widgets/lib/src/file_attachment.dart +++ b/packages/flutter_widgets/lib/src/file_attachment.dart @@ -41,7 +41,7 @@ class _FileAttachmentState extends State { @override void initState() { super.initState(); - if (MediaUtils.getMimeType(widget.attachment.title).type == 'video') { + if (MediaUtils.getMimeType(widget.attachment.title)?.type == 'video') { if (widget.attachmentType == FileAttachmentType.online) { _controller = VideoPlayerController.network( widget.attachment.assetUrl, @@ -128,7 +128,7 @@ class _FileAttachmentState extends State { } Widget _getFileTypeImage() { - if ((MediaUtils.getMimeType(widget.attachment.title).type == 'image')) { + if ((MediaUtils.getMimeType(widget.attachment.title)?.type == 'image')) { switch (widget.attachmentType) { case FileAttachmentType.local: return Image.memory( @@ -165,7 +165,7 @@ class _FileAttachmentState extends State { } } - if ((MediaUtils.getMimeType(widget.attachment.title).type == 'video')) { + if ((MediaUtils.getMimeType(widget.attachment.title)?.type == 'video')) { switch (widget.attachmentType) { case FileAttachmentType.local: return FutureBuilder( diff --git a/packages/flutter_widgets/lib/src/message_widget.dart b/packages/flutter_widgets/lib/src/message_widget.dart index eeabaa13..2a15f898 100644 --- a/packages/flutter_widgets/lib/src/message_widget.dart +++ b/packages/flutter_widgets/lib/src/message_widget.dart @@ -159,8 +159,8 @@ class MessageWidget extends StatefulWidget { this.showReactionPickerIndicator = false, this.showUserAvatar = DisplayWidget.show, this.showSendingIndicator = true, - this.showThreadReplyIndicator = true, - this.showInChannelIndicator = true, + this.showThreadReplyIndicator = false, + this.showInChannelIndicator = false, this.onReplyTap, this.onThreadTap, this.showUsername = true, @@ -371,15 +371,24 @@ class _MessageWidgetState extends State { alignment: Alignment.center, transform: Matrix4.rotationY( widget.reverse ? pi : 0), - child: DeletedMessage( - reverse: widget.reverse, - borderRadiusGeometry: widget - .borderRadiusGeometry, - borderSide: - widget.borderSide, - shape: widget.shape, - messageTheme: - widget.messageTheme, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: + widget.showUserAvatar == + DisplayWidget + .gone + ? 0 + : 4.0), + child: DeletedMessage( + reverse: widget.reverse, + borderRadiusGeometry: widget + .borderRadiusGeometry, + borderSide: + widget.borderSide, + shape: widget.shape, + messageTheme: + widget.messageTheme, + ), ), ) : Card( diff --git a/packages/flutter_widgets/lib/src/reaction_bubble.dart b/packages/flutter_widgets/lib/src/reaction_bubble.dart index 74a2ae99..4ddcca93 100644 --- a/packages/flutter_widgets/lib/src/reaction_bubble.dart +++ b/packages/flutter_widgets/lib/src/reaction_bubble.dart @@ -67,7 +67,7 @@ class ReactionBubble extends StatelessWidget { children: [ if (constraints.maxWidth < double.infinity) ...reactions - .take((constraints.maxWidth) ~/ 22) + .take((constraints.maxWidth) ~/ 24) .map((reaction) { return _buildReaction( reactionIcons, diff --git a/packages/flutter_widgets/lib/src/stream_chat_theme.dart b/packages/flutter_widgets/lib/src/stream_chat_theme.dart index 5b5927c8..3945a033 100644 --- a/packages/flutter_widgets/lib/src/stream_chat_theme.dart +++ b/packages/flutter_widgets/lib/src/stream_chat_theme.dart @@ -45,9 +45,6 @@ class StreamChatThemeData { /// The text themes used in the widgets final TextTheme textTheme; - /// The button themes used in the widgets - final ButtonThemeData buttonTheme; - /// The text themes used in the widgets final ColorTheme colorTheme; @@ -78,7 +75,6 @@ class StreamChatThemeData { /// Create a theme from scratch const StreamChatThemeData({ this.textTheme, - this.buttonTheme, this.colorTheme, this.channelPreviewTheme, this.channelTheme, @@ -93,24 +89,20 @@ class StreamChatThemeData { /// Create a theme from a Material [Theme] factory StreamChatThemeData.fromTheme(ThemeData theme) { final defaultTheme = getDefaultTheme(theme); - final customizedTheme = StreamChatThemeData( - primaryIconTheme: theme.primaryIconTheme, - ownMessageTheme: MessageTheme( - replies: TextStyle(color: theme.accentColor), - messageLinks: TextStyle(color: theme.accentColor), + final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( + defaultTheme.colorTheme.copyWith( + accentBlue: theme.accentColor, ), - otherMessageTheme: MessageTheme( - replies: TextStyle(color: theme.accentColor), - messageLinks: TextStyle(color: theme.accentColor), - ), - ); + defaultTheme.textTheme, + ).copyWith( + // primaryIconTheme: theme.primaryIconTheme, + ); return defaultTheme.merge(customizedTheme) ?? customizedTheme; } /// Creates a copy of [StreamChatThemeData] with specified attributes overridden. StreamChatThemeData copyWith({ TextTheme textTheme, - ButtonThemeData buttonTheme, ColorTheme colorTheme, ChannelPreviewTheme channelPreviewTheme, ChannelTheme channelTheme, @@ -123,7 +115,6 @@ class StreamChatThemeData { }) => StreamChatThemeData( textTheme: textTheme ?? this.textTheme, - buttonTheme: buttonTheme ?? this.buttonTheme, colorTheme: colorTheme ?? this.colorTheme, primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme, defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage, @@ -139,7 +130,6 @@ class StreamChatThemeData { if (other == null) return this; return copyWith( textTheme: textTheme?.merge(other.textTheme) ?? other.textTheme, - buttonTheme: other.buttonTheme, colorTheme: colorTheme?.merge(other.colorTheme) ?? other.colorTheme, primaryIconTheme: other.primaryIconTheme, defaultChannelImage: other.defaultChannelImage, @@ -157,26 +147,14 @@ class StreamChatThemeData { ); } - /// Get the default Stream Chat theme - static StreamChatThemeData getDefaultTheme(ThemeData theme) { - final accentColor = Color(0xff006cff); - final isDark = theme.brightness == Brightness.dark; - final textTheme = isDark ? TextTheme.dark() : TextTheme.light(); - final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light(); + static StreamChatThemeData fromColorAndTextTheme( + ColorTheme colorTheme, + TextTheme textTheme, + ) { + final accentColor = colorTheme.accentBlue; return StreamChatThemeData( textTheme: textTheme, colorTheme: colorTheme, - buttonTheme: ButtonThemeData( - height: 48.0, - buttonColor: isDark ? Color(0xffffffff) : Color(0xff006aff), - textTheme: ButtonTextTheme.accent, - colorScheme: theme.colorScheme.copyWith( - secondary: isDark ? Color(0xff005eff) : Color(0xffffffff), - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26), - ), - ), primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)), defaultChannelImage: (context, channel) => SizedBox(), defaultUserImage: (context, user) => Center( @@ -204,7 +182,7 @@ class StreamChatThemeData { ), indicatorIconSize: 16.0), channelTheme: ChannelTheme( - messageInputButtonIconTheme: theme.iconTheme.copyWith( + messageInputButtonIconTheme: IconThemeData( color: accentColor, ), channelHeaderTheme: ChannelHeaderTheme( @@ -291,6 +269,17 @@ 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 {