Merge pull request #601 from GetStream/cds-362

This commit is contained in:
Sahil Kumar
2021-08-05 13:32:36 +05:30
committed by GitHub
59 changed files with 3040 additions and 1916 deletions
+19
View File
@@ -1,3 +1,21 @@
## Upcoming
🔄 Changed
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes
associated with them, and have been upgraded with some goodies like `lerp` functions.
Here's the full naming breakdown:
* `AvatarTheme` is now `AvatarThemeData`
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
* `ChannelListHeaderTheme` is now `ChannelListHeaderThemeData`
* `ChannelListViewTheme` is now `ChannelListViewThemeData`
* `ChannelPreviewTheme` is now `ChannelPreviewThemeData`
* `MessageInputTheme` is now `MessageInputThemeData`
* `MessageListViewTheme` is now `MessageListViewTheme`
* `MessageSearchListViewTheme` is now `MessageSearchListViewThemeData`
* `MessageTheme` is now `MessageThemeData`
* `UserListViewTheme` is now `UserListViewThemeData`
## 2.1.2 ## 2.1.2
🐞 Fixed 🐞 Fixed
@@ -531,6 +549,7 @@ Widget build(BuildContext context) {
}, },
home: ChannelListPage(), home: ChannelListPage(),
); );
}
``` ```
- Fix reaction bubble going below previous message on iOS - Fix reaction bubble going below previous message on iOS
@@ -118,13 +118,12 @@ class ChannelListPage extends StatelessWidget {
channel: channel, channel: channel,
), ),
title: ChannelName( title: ChannelName(
textStyle: textStyle: ChannelPreviewTheme.of(context).titleStyle!.copyWith(
StreamChatTheme.of(context).channelPreviewTheme.title!.copyWith( color: StreamChatTheme.of(context)
color: StreamChatTheme.of(context) .colorTheme
.colorTheme .textHighEmphasis
.textHighEmphasis .withOpacity(opacity),
.withOpacity(opacity), ),
),
), ),
subtitle: Text(subtitle), subtitle: Text(subtitle),
trailing: channel.state!.unreadCount > 0 trailing: channel.state!.unreadCount > 0
@@ -58,17 +58,17 @@ class MyApp extends StatelessWidget {
final defaultTheme = StreamChatThemeData.fromTheme(themeData); final defaultTheme = StreamChatThemeData.fromTheme(themeData);
final colorTheme = defaultTheme.colorTheme; final colorTheme = defaultTheme.colorTheme;
final customTheme = defaultTheme.merge(StreamChatThemeData( final customTheme = defaultTheme.merge(StreamChatThemeData(
channelPreviewTheme: ChannelPreviewTheme( channelPreviewTheme: ChannelPreviewThemeData(
avatarTheme: AvatarTheme( avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
otherMessageTheme: MessageTheme( otherMessageTheme: MessageThemeData(
messageBackgroundColor: colorTheme.textHighEmphasis, messageBackgroundColor: colorTheme.textHighEmphasis,
messageText: TextStyle( messageTextStyle: TextStyle(
color: colorTheme.barsBg, color: colorTheme.barsBg,
), ),
avatarTheme: AvatarTheme( avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/theme/themes.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';
@@ -12,7 +13,7 @@ class AttachmentTitle extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
/// Theme to apply to text /// Theme to apply to text
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Attachment data to display /// Attachment data to display
final Attachment attachment; final Attachment attachment;
@@ -34,7 +35,7 @@ class AttachmentTitle extends StatelessWidget {
Text( Text(
attachment.title!, attachment.title!,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: messageTheme.messageText?.copyWith( style: messageTheme.messageTextStyle?.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentPrimary, color: StreamChatTheme.of(context).colorTheme.accentPrimary,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -50,7 +51,7 @@ class AttachmentTitle extends StatelessWidget {
.toList() .toList()
.reversed .reversed
.join('.'), .join('.'),
style: messageTheme.messageText, style: messageTheme.messageTextStyle,
), ),
], ],
), ),
@@ -4,6 +4,7 @@ import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_title.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_title.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_upload_state_builder.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_upload_state_builder.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
import 'package:stream_chat_flutter/src/theme/themes.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';
@@ -27,8 +28,8 @@ class ImageAttachment extends AttachmentWidget {
size: size, size: size,
); );
/// [MessageTheme] for showing image title /// [MessageThemeData] for showing image title
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Flag for showing title /// Flag for showing title
final bool showTitle; final bool showTitle;
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_title.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_title.dart';
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart'; import 'package:stream_chat_flutter/src/attachment/attachment_widget.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/src/theme/themes.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';
@@ -24,8 +25,8 @@ class VideoAttachment extends AttachmentWidget {
size: size, size: size,
); );
/// [MessageTheme] for showing title /// [MessageThemeData] for showing title
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Callback when show message is tapped /// Callback when show message is tapped
final ShowMessageCallback? onShowMessage; final ShowMessageCallback? onShowMessage;
@@ -19,6 +19,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
bool _showActions = true; bool _showActions = true;
late StreamChannelState _streamChannelState; late StreamChannelState _streamChannelState;
late ChannelPreviewThemeData _channelPreviewThemeData;
late StreamChatThemeData _streamChatThemeData; late StreamChatThemeData _streamChatThemeData;
late StreamChatState _streamChatState; late StreamChatState _streamChatState;
@@ -64,8 +65,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
child: ChannelInfo( child: ChannelInfo(
showTypingIndicator: false, showTypingIndicator: false,
channel: _streamChannelState.channel, channel: _streamChannelState.channel,
textStyle: textStyle: _channelPreviewThemeData.subtitleStyle,
_streamChatThemeData.channelPreviewTheme.subtitle,
), ),
), ),
const SizedBox( const SizedBox(
@@ -213,6 +213,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
void didChangeDependencies() { void didChangeDependencies() {
_streamChannelState = StreamChannel.of(context); _streamChannelState = StreamChannel.of(context);
_streamChatThemeData = StreamChatTheme.of(context); _streamChatThemeData = StreamChatTheme.of(context);
_channelPreviewThemeData = ChannelPreviewTheme.of(context);
_streamChatState = StreamChat.of(context); _streamChatState = StreamChat.of(context);
super.didChangeDependencies(); super.didChangeDependencies();
} }
@@ -105,7 +105,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel; final channel = StreamChannel.of(context).channel;
final chatThemeData = StreamChatTheme.of(context); final channelHeaderTheme = ChannelHeaderTheme.of(context);
final leadingWidget = leading ?? final leadingWidget = leading ??
(showBackButton (showBackButton
@@ -141,18 +141,17 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
leading: leadingWidget, leading: leadingWidget,
backgroundColor: backgroundColor: channelHeaderTheme.color,
chatThemeData.channelTheme.channelHeaderTheme.color,
actions: actions ?? actions: actions ??
<Widget>[ <Widget>[
Padding( Padding(
padding: const EdgeInsets.only(right: 10), padding: const EdgeInsets.only(right: 10),
child: Center( child: Center(
child: ChannelAvatar( child: ChannelAvatar(
borderRadius: chatThemeData.channelTheme borderRadius:
.channelHeaderTheme.avatarTheme?.borderRadius, channelHeaderTheme.avatarTheme?.borderRadius,
constraints: chatThemeData.channelTheme constraints:
.channelHeaderTheme.avatarTheme?.constraints, channelHeaderTheme.avatarTheme?.constraints,
onTap: onImageTap, onTap: onImageTap,
), ),
), ),
@@ -169,16 +168,14 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
children: <Widget>[ children: <Widget>[
title ?? title ??
ChannelName( ChannelName(
textStyle: chatThemeData textStyle: channelHeaderTheme.titleStyle,
.channelTheme.channelHeaderTheme.title,
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
subtitle ?? subtitle ??
ChannelInfo( ChannelInfo(
showTypingIndicator: showTypingIndicator, showTypingIndicator: showTypingIndicator,
channel: channel, channel: channel,
textStyle: chatThemeData textStyle: channelHeaderTheme.subtitleStyle,
.channelTheme.channelHeaderTheme.subtitle,
), ),
], ],
), ),
@@ -65,10 +65,7 @@ class ChannelInfo extends StatelessWidget {
} }
alternativeWidget = Text( alternativeWidget = Text(
text, text,
style: StreamChatTheme.of(context) style: ChannelHeaderTheme.of(context).subtitleStyle,
.channelTheme
.channelHeaderTheme
.subtitle,
); );
} else { } else {
final userId = StreamChat.of(context).currentUser?.id; final userId = StreamChat.of(context).currentUser?.id;
@@ -2,10 +2,10 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart'; import 'package:stream_chat_flutter/src/stream_neumorphic_button.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 'package:stream_chat_flutter/src/extension.dart';
/// Widget builder for title /// Widget builder for title
typedef TitleBuilder = Widget Function( typedef TitleBuilder = Widget Function(
@@ -46,7 +46,7 @@ typedef TitleBuilder = Widget Function(
/// if you don't have it in the widget tree. /// if you don't have it in the widget tree.
/// ///
/// The widget components render the ui based on the first ancestor of type /// The widget components render the ui based on the first ancestor of type
/// [StreamChatTheme] and on its [ChannelListHeaderTheme] property. /// [StreamChatTheme] and on its [ChannelListHeaderThemeData] property.
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
/// Instantiates a ChannelListHeader /// Instantiates a ChannelListHeader
@@ -116,6 +116,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
} }
final chatThemeData = StreamChatTheme.of(context); final chatThemeData = StreamChatTheme.of(context);
final channelListHeaderThemeData = ChannelListHeaderTheme.of(context);
return InfoTile( return InfoTile(
showMessage: showConnectionStateTile && showStatus, showMessage: showConnectionStateTile && showStatus,
message: statusString, message: statusString,
@@ -123,7 +124,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
textTheme: Theme.of(context).textTheme, textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
backgroundColor: chatThemeData.channelListHeaderTheme.color, backgroundColor: channelListHeaderThemeData.color,
centerTitle: true, centerTitle: true,
leading: leading ?? leading: leading ??
Center( Center(
@@ -138,10 +139,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
} }
Scaffold.of(context).openDrawer(); Scaffold.of(context).openDrawer();
}, },
borderRadius: chatThemeData borderRadius: channelListHeaderThemeData
.channelListHeaderTheme.avatarTheme?.borderRadius, .avatarTheme?.borderRadius,
constraints: chatThemeData constraints: channelListHeaderThemeData
.channelListHeaderTheme.avatarTheme?.constraints, .avatarTheme?.constraints,
) )
: const Offstage(), : const Offstage(),
), ),
@@ -227,10 +228,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
const SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
context.translations.searchingForNetworkText, context.translations.searchingForNetworkText,
style: StreamChatTheme.of(context) style: ChannelListHeaderTheme.of(context).titleStyle?.copyWith(
.channelListHeaderTheme
.title
?.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -243,12 +241,13 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
StreamChatClient client, StreamChatClient client,
) { ) {
final chatThemeData = StreamChatTheme.of(context); final chatThemeData = StreamChatTheme.of(context);
final channelListHeaderTheme = ChannelListHeaderTheme.of(context);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
context.translations.offlineLabel, context.translations.offlineLabel,
style: chatThemeData.channelListHeaderTheme.title?.copyWith( style: channelListHeaderTheme.titleStyle?.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -259,7 +258,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
..openConnection(), ..openConnection(),
child: Text( child: Text(
context.translations.tryAgainLabel, context.translations.tryAgainLabel,
style: chatThemeData.channelListHeaderTheme.title?.copyWith( style: channelListHeaderTheme.titleStyle?.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: chatThemeData.colorTheme.accentPrimary, color: chatThemeData.colorTheme.accentPrimary,
@@ -5,6 +5,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart'; import 'package:stream_chat_flutter/src/channel_bottom_sheet.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/theme/themes.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 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
@@ -3,10 +3,10 @@ import 'package:collection/collection.dart'
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';
import 'package:stream_chat_flutter/src/extension.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/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 'package:stream_chat_flutter/src/extension.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview_paint.png)
@@ -69,7 +69,7 @@ class ChannelPreview extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme; final channelPreviewTheme = ChannelPreviewTheme.of(context);
final streamChatState = StreamChat.of(context); final streamChatState = StreamChat.of(context);
return BetterStreamBuilder<bool>( return BetterStreamBuilder<bool>(
stream: channel.isMutedStream, stream: channel.isMutedStream,
@@ -91,7 +91,7 @@ class ChannelPreview extends StatelessWidget {
Flexible( Flexible(
child: title ?? child: title ??
ChannelName( ChannelName(
textStyle: channelPreviewTheme.title, textStyle: channelPreviewTheme.titleStyle,
), ),
), ),
BetterStreamBuilder<List<Member>?>( BetterStreamBuilder<List<Member>?>(
@@ -183,14 +183,13 @@ class ChannelPreview extends StatelessWidget {
return Text( return Text(
stringDate, stringDate,
style: style: ChannelPreviewTheme.of(context).lastMessageAtStyle,
StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt,
); );
}, },
); );
Widget _buildSubtitle(BuildContext context) { Widget _buildSubtitle(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context); final channelPreviewTheme = ChannelPreviewTheme.of(context);
if (channel.isMuted) { if (channel.isMuted) {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
@@ -200,7 +199,7 @@ class ChannelPreview extends StatelessWidget {
), ),
Text( Text(
' ${context.translations.channelIsMutedText}', ' ${context.translations.channelIsMutedText}',
style: chatThemeData.channelPreviewTheme.subtitle, style: channelPreviewTheme.subtitleStyle,
), ),
], ],
); );
@@ -208,7 +207,7 @@ class ChannelPreview extends StatelessWidget {
return TypingIndicator( return TypingIndicator(
channel: channel, channel: channel,
alternativeWidget: _buildLastMessage(context), alternativeWidget: _buildLastMessage(context),
style: chatThemeData.channelPreviewTheme.subtitle, style: channelPreviewTheme.subtitleStyle,
); );
} }
@@ -243,19 +242,19 @@ class ChannelPreview extends StatelessWidget {
text = parts.join(' '); text = parts.join(' ');
final chatThemeData = StreamChatTheme.of(context); final channelPreviewTheme = ChannelPreviewTheme.of(context);
return Text.rich( return Text.rich(
_getDisplayText( _getDisplayText(
text, text,
lastMessage.mentionedUsers, lastMessage.mentionedUsers,
lastMessage.attachments, lastMessage.attachments,
chatThemeData.channelPreviewTheme.subtitle?.copyWith( channelPreviewTheme.subtitleStyle?.copyWith(
color: chatThemeData.channelPreviewTheme.subtitle?.color, color: channelPreviewTheme.subtitleStyle?.color,
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal), : FontStyle.normal),
chatThemeData.channelPreviewTheme.subtitle?.copyWith( channelPreviewTheme.subtitleStyle?.copyWith(
color: chatThemeData.channelPreviewTheme.subtitle?.color, color: channelPreviewTheme.subtitleStyle?.color,
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal, : FontStyle.normal,
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
/// Widget to display deleted message /// Widget to display deleted message
class DeletedMessage extends StatelessWidget { class DeletedMessage extends StatelessWidget {
@@ -15,7 +16,7 @@ class DeletedMessage extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
/// The theme of the message /// The theme of the message
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// The border radius of the message text /// The border radius of the message text
final BorderRadiusGeometry? borderRadiusGeometry; final BorderRadiusGeometry? borderRadiusGeometry;
@@ -51,9 +52,9 @@ class DeletedMessage extends StatelessWidget {
), ),
child: Text( child: Text(
context.translations.messageDeletedLabel, context.translations.messageDeletedLabel,
style: messageTheme.messageText?.copyWith( style: messageTheme.messageTextStyle?.copyWith(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
color: messageTheme.createdAt?.color, color: messageTheme.createdAtStyle?.color,
), ),
), ),
), ),
@@ -124,10 +124,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
), ),
backgroundDecoration: BoxDecoration( backgroundDecoration: BoxDecoration(
color: ColorTween( color: ColorTween(
begin: StreamChatTheme.of(context) begin: ChannelHeaderTheme.of(context).color,
.channelTheme
.channelHeaderTheme
.color,
end: Colors.black, end: Colors.black,
).lerp(_controller.value), ).lerp(_controller.value),
), ),
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.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/theme/themes.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';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -2,6 +2,7 @@ import 'package:flutter/material.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/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.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/theme/themes.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Header/AppBar widget for media display screen /// Header/AppBar widget for media display screen
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/src/theme/themes.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';
@@ -25,8 +26,8 @@ class ImageGroup extends StatelessWidget {
/// Message which images are attached to /// Message which images are attached to
final Message message; final Message message;
/// [MessageTheme] to apply to message /// [MessageThemeData] to apply to message
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Size of iamges /// Size of iamges
final Size size; final Size size;
@@ -6,6 +6,7 @@ import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/message_action.dart'; import 'package:stream_chat_flutter/src/message_action.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/reaction_picker.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/theme/themes.dart';
import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/src/utils.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -49,8 +50,8 @@ class MessageActionsModal extends StatefulWidget {
/// Message in focus for actions /// Message in focus for actions
final Message message; final Message message;
/// [MessageTheme] for message /// [MessageThemeData] for message
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Flag for showing reactions /// Flag for showing reactions
final bool showReactions; final bool showReactions;
@@ -116,7 +117,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
} }
} }
final roughSentenceSize = messageTextLength * final roughSentenceSize = messageTextLength *
(widget.messageTheme.messageText?.fontSize ?? 1) * (widget.messageTheme.messageTextStyle?.fontSize ?? 1) *
1.2; 1.2;
final divFactor = widget.message.attachments.isNotEmpty == true final divFactor = widget.message.attachments.isNotEmpty == true
? 1 ? 1
@@ -565,7 +566,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
elevation: 2, elevation: 2,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
isScrollControlled: true, isScrollControlled: true,
backgroundColor: streamChatThemeData.messageInputTheme.inputBackground, backgroundColor: MessageInputTheme.of(context).inputBackgroundColor,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
@@ -288,6 +288,7 @@ class MessageInputState extends State<MessageInput> {
late final TextEditingController textEditingController; late final TextEditingController textEditingController;
late StreamChatThemeData _streamChatTheme; late StreamChatThemeData _streamChatTheme;
late MessageInputThemeData _messageInputTheme;
bool get _hasQuotedMessage => widget.quotedMessage != null; bool get _hasQuotedMessage => widget.quotedMessage != null;
@@ -328,7 +329,7 @@ class MessageInputState extends State<MessageInput> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child = DecoratedBox( Widget child = DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _streamChatTheme.messageInputTheme.inputBackground, color: _messageInputTheme.inputBackgroundColor,
), ),
child: SafeArea( child: SafeArea(
child: GestureDetector( child: GestureDetector(
@@ -484,7 +485,7 @@ class MessageInputState extends State<MessageInput> {
: CrossFadeState.showSecond, : CrossFadeState.showSecond,
firstChild: sendButton, firstChild: sendButton,
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context), secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!, duration: _messageInputTheme.sendAnimationDuration!,
alignment: Alignment.center, alignment: Alignment.center,
); );
} }
@@ -509,7 +510,7 @@ class MessageInputState extends State<MessageInput> {
? pi ? pi
: 0, : 0,
child: StreamSvgIcon.emptyCircleLeft( child: StreamSvgIcon.emptyCircleLeft(
color: _streamChatTheme.messageInputTheme.expandButtonColor, color: _messageInputTheme.expandButtonColor,
), ),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
@@ -555,17 +556,17 @@ class MessageInputState extends State<MessageInput> {
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
margin: margin, margin: margin,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: _streamChatTheme.messageInputTheme.borderRadius, borderRadius: _messageInputTheme.borderRadius,
gradient: _focusNode.hasFocus gradient: _focusNode.hasFocus
? _streamChatTheme.messageInputTheme.activeBorderGradient ? _messageInputTheme.activeBorderGradient
: _streamChatTheme.messageInputTheme.idleBorderGradient, : _messageInputTheme.idleBorderGradient,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(1.5), padding: const EdgeInsets.all(1.5),
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: _streamChatTheme.messageInputTheme.borderRadius, borderRadius: _messageInputTheme.borderRadius,
color: _streamChatTheme.messageInputTheme.inputBackground, color: _messageInputTheme.inputBackgroundColor,
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -583,7 +584,7 @@ class MessageInputState extends State<MessageInput> {
keyboardType: widget.keyboardType, keyboardType: widget.keyboardType,
controller: textEditingController, controller: textEditingController,
focusNode: _focusNode, focusNode: _focusNode,
style: _streamChatTheme.messageInputTheme.inputTextStyle, style: _messageInputTheme.inputTextStyle,
autofocus: widget.autofocus, autofocus: widget.autofocus,
textAlignVertical: TextAlignVertical.center, textAlignVertical: TextAlignVertical.center,
decoration: _getInputDecoration(context), decoration: _getInputDecoration(context),
@@ -599,11 +600,11 @@ class MessageInputState extends State<MessageInput> {
} }
InputDecoration _getInputDecoration(BuildContext context) { InputDecoration _getInputDecoration(BuildContext context) {
final passedDecoration = _streamChatTheme.messageInputTheme.inputDecoration; final passedDecoration = _messageInputTheme.inputDecoration;
return InputDecoration( return InputDecoration(
isDense: true, isDense: true,
hintText: _getHint(context), hintText: _getHint(context),
hintStyle: _streamChatTheme.messageInputTheme.inputTextStyle!.copyWith( hintStyle: _messageInputTheme.inputTextStyle!.copyWith(
color: _streamChatTheme.colorTheme.textLowEmphasis, color: _streamChatTheme.colorTheme.textLowEmphasis,
), ),
border: const OutlineInputBorder( border: const OutlineInputBorder(
@@ -1688,8 +1689,8 @@ class MessageInputState extends State<MessageInput> {
color: s.isNotEmpty color: s.isNotEmpty
? _streamChatTheme.colorTheme.disabled ? _streamChatTheme.colorTheme.disabled
: (_commandsOverlay != null : (_commandsOverlay != null
? _streamChatTheme.messageInputTheme.actionButtonColor ? _messageInputTheme.actionButtonColor
: _streamChatTheme.messageInputTheme.actionButtonIdleColor), : _messageInputTheme.actionButtonIdleColor),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
@@ -1726,8 +1727,8 @@ class MessageInputState extends State<MessageInput> {
Widget _buildAttachmentButton() => IconButton( Widget _buildAttachmentButton() => IconButton(
icon: StreamSvgIcon.attach( icon: StreamSvgIcon.attach(
color: _openFilePickerSection color: _openFilePickerSection
? _streamChatTheme.messageInputTheme.actionButtonColor ? _messageInputTheme.actionButtonColor
: _streamChatTheme.messageInputTheme.actionButtonIdleColor, : _messageInputTheme.actionButtonIdleColor,
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
@@ -1963,7 +1964,7 @@ class MessageInputState extends State<MessageInput> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: StreamSvgIcon( child: StreamSvgIcon(
assetName: _getIdleSendIcon(), assetName: _getIdleSendIcon(),
color: _streamChatTheme.messageInputTheme.sendButtonIdleColor, color: _messageInputTheme.sendButtonIdleColor,
), ),
); );
@@ -1979,7 +1980,7 @@ class MessageInputState extends State<MessageInput> {
), ),
icon: StreamSvgIcon( icon: StreamSvgIcon(
assetName: _getSendIcon(), assetName: _getSendIcon(),
color: _streamChatTheme.messageInputTheme.sendButtonColor, color: _messageInputTheme.sendButtonColor,
), ),
), ),
); );
@@ -2186,6 +2187,7 @@ class MessageInputState extends State<MessageInput> {
@override @override
void didChangeDependencies() { void didChangeDependencies() {
_streamChatTheme = StreamChatTheme.of(context); _streamChatTheme = StreamChatTheme.of(context);
_messageInputTheme = MessageInputTheme.of(context);
if (widget.editMessage != null && !_initialized) { if (widget.editMessage != null && !_initialized) {
FocusScope.of(context).requestFocus(_focusNode); FocusScope.of(context).requestFocus(_focusNode);
_initialized = true; _initialized = true;
@@ -14,6 +14,7 @@ import 'package:stream_chat_flutter/src/message_widget.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/swipeable.dart'; import 'package:stream_chat_flutter/src/swipeable.dart';
import 'package:stream_chat_flutter/src/system_message.dart'; import 'package:stream_chat_flutter/src/system_message.dart';
import 'package:stream_chat_flutter/src/theme/themes.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 'package:visibility_detector/visibility_detector.dart'; import 'package:visibility_detector/visibility_detector.dart';
@@ -628,7 +629,7 @@ class _MessageListViewState extends State<MessageListView> {
child: Text( child: Text(
context.translations.threadSeparatorText(replyCount), context.translations.threadSeparatorText(replyCount),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: _streamTheme.channelTheme.channelHeaderTheme.subtitle, style: ChannelHeaderTheme.of(context).subtitleStyle,
), ),
), ),
); );
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_chat.dart'; import 'package:stream_chat_flutter/src/stream_chat.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart'; import 'package:stream_chat_flutter/src/user_avatar.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';
@@ -28,8 +29,8 @@ class MessageReactionsModal extends StatelessWidget {
/// Message to display reactions of /// Message to display reactions of
final Message message; final Message message;
/// [MessageTheme] to apply to [message] /// [MessageThemeData] to apply to [message]
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// Flag to reverse message /// Flag to reverse message
final bool reverse; final bool reverse;
@@ -56,8 +57,9 @@ class MessageReactionsModal extends StatelessWidget {
messageTextLength = quotedMessageLength; messageTextLength = quotedMessageLength;
} }
} }
final roughSentenceSize = final roughSentenceSize = messageTextLength *
messageTextLength * (messageTheme.messageText?.fontSize ?? 1) * 1.2; (messageTheme.messageTextStyle?.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));
@@ -36,7 +36,7 @@ class MessageSearchItem extends StatelessWidget {
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!;
final chatThemeData = StreamChatTheme.of(context); final channelPreviewTheme = ChannelPreviewTheme.of(context);
return ListTile( return ListTile(
onTap: onTap, onTap: onTap,
leading: UserAvatar( leading: UserAvatar(
@@ -53,18 +53,18 @@ class MessageSearchItem extends StatelessWidget {
user.id == StreamChat.of(context).currentUser?.id user.id == StreamChat.of(context).currentUser?.id
? context.translations.youText ? context.translations.youText
: user.name, : user.name,
style: chatThemeData.channelPreviewTheme.title, style: channelPreviewTheme.titleStyle,
), ),
if (channelName != null) ...[ if (channelName != null) ...[
Text( Text(
' ${context.translations.inText} ', ' ${context.translations.inText} ',
style: chatThemeData.channelPreviewTheme.title?.copyWith( style: channelPreviewTheme.titleStyle?.copyWith(
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
Text( Text(
channelName as String, channelName as String,
style: chatThemeData.channelPreviewTheme.title, style: channelPreviewTheme.titleStyle,
), ),
], ],
], ],
@@ -94,7 +94,7 @@ class MessageSearchItem extends StatelessWidget {
return Text( return Text(
stringDate, stringDate,
style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt, style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAtStyle,
); );
} }
@@ -122,18 +122,18 @@ class MessageSearchItem extends StatelessWidget {
text = parts.join(' '); text = parts.join(' ');
} }
final chatThemeData = StreamChatTheme.of(context); final channelPreviewTheme = ChannelPreviewTheme.of(context);
return Text.rich( return Text.rich(
_getDisplayText( _getDisplayText(
text!, text!,
message.mentionedUsers, message.mentionedUsers,
message.attachments, message.attachments,
chatThemeData.channelPreviewTheme.subtitle?.copyWith( channelPreviewTheme.subtitleStyle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted) fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal, : FontStyle.normal,
), ),
chatThemeData.channelPreviewTheme.subtitle?.copyWith( channelPreviewTheme.subtitleStyle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted) fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal, : FontStyle.normal,
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/info_tile.dart';
import 'package:stream_chat_flutter/src/message_search_item.dart'; import 'package:stream_chat_flutter/src/message_search_item.dart';
import 'package:stream_chat_flutter/src/theme/themes.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 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
@@ -1,6 +1,7 @@
import 'package:collection/collection.dart' show IterableExtension; 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/src/theme/themes.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';
@@ -24,8 +25,8 @@ class MessageText extends StatelessWidget {
/// Callback for when link is tapped /// Callback for when link is tapped
final void Function(String)? onLinkTap; final void Function(String)? onLinkTap;
/// [MessageTheme] whose text theme is to be applied /// [MessageThemeData] whose text theme is to be applied
final MessageTheme messageTheme; final MessageThemeData messageTheme;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -66,16 +67,16 @@ class MessageText extends StatelessWidget {
styleSheet: MarkdownStyleSheet.fromTheme( styleSheet: MarkdownStyleSheet.fromTheme(
themeData.copyWith( themeData.copyWith(
textTheme: themeData.textTheme.apply( textTheme: themeData.textTheme.apply(
bodyColor: messageTheme.messageText?.color, bodyColor: messageTheme.messageTextStyle?.color,
decoration: messageTheme.messageText?.decoration, decoration: messageTheme.messageTextStyle?.decoration,
decorationColor: messageTheme.messageText?.decorationColor, decorationColor: messageTheme.messageTextStyle?.decorationColor,
decorationStyle: messageTheme.messageText?.decorationStyle, decorationStyle: messageTheme.messageTextStyle?.decorationStyle,
fontFamily: messageTheme.messageText?.fontFamily, fontFamily: messageTheme.messageTextStyle?.fontFamily,
), ),
), ),
).copyWith( ).copyWith(
a: messageTheme.messageLinks, a: messageTheme.messageLinksStyle,
p: messageTheme.messageText, p: messageTheme.messageTextStyle,
), ),
); );
}, },
@@ -14,6 +14,7 @@ import 'package:stream_chat_flutter/src/message_actions_modal.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/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
import 'package:stream_chat_flutter/src/url_attachment.dart'; import 'package:stream_chat_flutter/src/url_attachment.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -290,7 +291,7 @@ class MessageWidget extends StatefulWidget {
final Message message; final Message message;
/// The message theme /// The message theme
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// If true the widget will be mirrored /// If true the widget will be mirrored
final bool reverse; final bool reverse;
@@ -422,7 +423,7 @@ class MessageWidget extends StatefulWidget {
Widget Function(BuildContext, Message)? deletedBottomRowBuilder, Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
void Function(BuildContext, Message)? onMessageActions, void Function(BuildContext, Message)? onMessageActions,
Message? message, Message? message,
MessageTheme? messageTheme, MessageThemeData? messageTheme,
bool? reverse, bool? reverse,
ShapeBorder? shape, ShapeBorder? shape,
ShapeBorder? attachmentShape, ShapeBorder? attachmentShape,
@@ -893,14 +894,14 @@ class _MessageWidgetState extends State<MessageWidget>
), ),
InkWell( InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null, onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text(msg, style: widget.messageTheme.replies), child: Text(msg, style: widget.messageTheme.repliesStyle),
), ),
], ],
if (showUsername) _buildUsername(usernameKey), if (showUsername) _buildUsername(usernameKey),
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.createdAtStyle,
), ),
if (showSendingIndicator) _buildSendingIndicator(), if (showSendingIndicator) _buildSendingIndicator(),
]); ]);
@@ -917,7 +918,7 @@ class _MessageWidgetState extends State<MessageWidget>
Container( Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
bottom: context.textScaleFactor * bottom: context.textScaleFactor *
((widget.messageTheme.replies?.fontSize ?? 1) / 2), ((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
), ),
child: CustomPaint( child: CustomPaint(
size: const Size(16, 32) * context.textScaleFactor, size: const Size(16, 32) * context.textScaleFactor,
@@ -944,7 +945,7 @@ class _MessageWidgetState extends State<MessageWidget>
Container( Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
bottom: context.textScaleFactor * bottom: context.textScaleFactor *
((widget.messageTheme.replies?.fontSize ?? 1) / 2), ((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
), ),
child: CustomPaint( child: CustomPaint(
size: const Size(16, 32) * context.textScaleFactor, size: const Size(16, 32) * context.textScaleFactor,
@@ -967,7 +968,7 @@ class _MessageWidgetState extends State<MessageWidget>
widget.message.user!.name, widget.message.user!.name,
maxLines: 1, maxLines: 1,
key: usernameKey, key: usernameKey,
style: widget.messageTheme.messageAuthor, style: widget.messageTheme.messageAuthorStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
} }
@@ -1191,7 +1192,7 @@ class _MessageWidgetState extends State<MessageWidget>
} }
Widget _buildSendingIndicator() { Widget _buildSendingIndicator() {
final style = widget.messageTheme.createdAt; final style = widget.messageTheme.createdAtStyle;
final message = widget.message; final message = widget.message;
if (hasNonUrlAttachments && if (hasNonUrlAttachments &&
@@ -1271,8 +1272,8 @@ class _MessageWidgetState extends State<MessageWidget>
onMentionTap: widget.onMentionTap, onMentionTap: widget.onMentionTap,
messageTheme: isOnlyEmoji messageTheme: isOnlyEmoji
? widget.messageTheme.copyWith( ? widget.messageTheme.copyWith(
messageText: messageTextStyle:
widget.messageTheme.messageText!.copyWith( widget.messageTheme.messageTextStyle!.copyWith(
fontSize: 42, fontSize: 42,
)) ))
: widget.messageTheme, : widget.messageTheme,
@@ -1,6 +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:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/theme/themes.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 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
@@ -73,7 +74,7 @@ class QuotedMessageWidget extends StatelessWidget {
final Message message; final Message message;
/// The message theme /// The message theme
final MessageTheme messageTheme; final MessageThemeData messageTheme;
/// If true the widget will be mirrored /// If true the widget will be mirrored
final bool reverse; final bool reverse;
@@ -138,11 +139,11 @@ class QuotedMessageWidget extends StatelessWidget {
message: msg, message: msg,
messageTheme: isOnlyEmoji && _containsText messageTheme: isOnlyEmoji && _containsText
? messageTheme.copyWith( ? messageTheme.copyWith(
messageText: messageTheme.messageText?.copyWith( messageTextStyle: messageTheme.messageTextStyle?.copyWith(
fontSize: 32, fontSize: 32,
)) ))
: messageTheme.copyWith( : messageTheme.copyWith(
messageText: messageTheme.messageText?.copyWith( messageTextStyle: messageTheme.messageTextStyle?.copyWith(
fontSize: 12, fontSize: 12,
)), )),
), ),
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,77 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// A style that overrides the default appearance of various avatar widgets.
class AvatarThemeData with Diagnosticable {
/// Creates an [AvatarThemeData].
const AvatarThemeData({
BoxConstraints? constraints,
BorderRadius? borderRadius,
}) : _constraints = constraints,
_borderRadius = borderRadius;
final BoxConstraints? _constraints;
final BorderRadius? _borderRadius;
/// Get constraints for avatar
BoxConstraints get constraints =>
_constraints ??
const BoxConstraints.tightFor(
height: 32,
width: 32,
);
/// Get border radius
BorderRadius get borderRadius => _borderRadius ?? BorderRadius.circular(20);
/// Copy this [AvatarThemeData] to another.
AvatarThemeData copyWith({
BoxConstraints? constraints,
BorderRadius? borderRadius,
}) =>
AvatarThemeData(
constraints: constraints ?? _constraints,
borderRadius: borderRadius ?? _borderRadius,
);
/// Linearly interpolate between two [UserAvatar] themes.
///
/// All the properties must be non-null.
AvatarThemeData lerp(
AvatarThemeData a,
AvatarThemeData b,
double t,
) =>
AvatarThemeData(
borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t),
constraints: BoxConstraints.lerp(a.constraints, b.constraints, t),
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AvatarThemeData &&
runtimeType == other.runtimeType &&
_constraints == other._constraints &&
_borderRadius == other._borderRadius;
@override
int get hashCode => _constraints.hashCode ^ _borderRadius.hashCode;
/// Merges one [AvatarThemeData] with the another
AvatarThemeData merge(AvatarThemeData? other) {
if (other == null) return this;
return copyWith(
constraints: other._constraints,
borderRadius: other._borderRadius,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('borderRadius', borderRadius))
..add(DiagnosticsProperty('constraints', constraints));
}
}
@@ -0,0 +1,149 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
/// Overrides the default style of [ChannelHeader] descendants.
///
/// See also:
///
/// * [ChannelHeaderThemeData], which is used to configure this theme.
class ChannelHeaderTheme extends InheritedTheme {
/// Creates a [ChannelHeaderTheme].
///
/// The [data] parameter must not be null.
const ChannelHeaderTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final ChannelHeaderThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [ChannelHeaderTheme] widget, then
/// [StreamChatThemeData.channelTheme.channelHeaderTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// final theme = ChannelHeaderTheme.of(context);
/// ```
static ChannelHeaderThemeData of(BuildContext context) {
final channelHeaderTheme =
context.dependOnInheritedWidgetOfExactType<ChannelHeaderTheme>();
return channelHeaderTheme?.data ??
StreamChatTheme.of(context).channelHeaderTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
ChannelHeaderTheme(data: data, child: child);
@override
bool updateShouldNotify(ChannelHeaderTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [ChannelHeader]s when used
/// with [ChannelHeaderTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.channelHeaderTheme].
///
/// See also:
///
/// * [ChannelHeaderTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.channelHeaderTheme], which can be used to override
/// the default style for [ChannelHeader]s below the overall [StreamChatTheme].
class ChannelHeaderThemeData with Diagnosticable {
/// Creates a [ChannelHeaderThemeData]
const ChannelHeaderThemeData({
this.titleStyle,
this.subtitleStyle,
this.avatarTheme,
this.color,
});
/// Theme for title
final TextStyle? titleStyle;
/// Theme for subtitle
final TextStyle? subtitleStyle;
/// Theme for avatar
final AvatarThemeData? avatarTheme;
/// Color for [ChannelHeaderThemeData]
final Color? color;
/// Copy with theme
ChannelHeaderThemeData copyWith({
TextStyle? titleStyle,
TextStyle? subtitleStyle,
AvatarThemeData? avatarTheme,
Color? color,
}) =>
ChannelHeaderThemeData(
titleStyle: titleStyle ?? this.titleStyle,
subtitleStyle: subtitleStyle ?? this.subtitleStyle,
avatarTheme: avatarTheme ?? this.avatarTheme,
color: color ?? this.color,
);
/// Linearly interpolate between two [ChannelHeaderThemeData].
///
/// All the properties must be non-null.
ChannelHeaderThemeData lerp(
ChannelHeaderThemeData a,
ChannelHeaderThemeData b,
double t,
) =>
ChannelHeaderThemeData(
titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t),
subtitleStyle: TextStyle.lerp(a.subtitleStyle, b.subtitleStyle, t),
avatarTheme:
const AvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t),
color: Color.lerp(a.color, b.color, t),
);
/// Merge with other [ChannelHeaderThemeData]
ChannelHeaderThemeData merge(ChannelHeaderThemeData? other) {
if (other == null) return this;
return copyWith(
titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle,
subtitleStyle:
subtitleStyle?.merge(other.subtitleStyle) ?? other.subtitleStyle,
avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme,
color: other.color,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChannelHeaderThemeData &&
runtimeType == other.runtimeType &&
titleStyle == other.titleStyle &&
subtitleStyle == other.subtitleStyle &&
avatarTheme == other.avatarTheme &&
color == other.color;
@override
int get hashCode =>
titleStyle.hashCode ^
subtitleStyle.hashCode ^
avatarTheme.hashCode ^
color.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('title', titleStyle))
..add(DiagnosticsProperty('subtitle', subtitleStyle))
..add(DiagnosticsProperty('avatarTheme', avatarTheme))
..add(ColorProperty('color', color));
}
}
@@ -0,0 +1,125 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
/// Overrides the default style of [ChannelListHeader] descendants.
///
/// See also:
///
/// * [ChannelListHeaderThemeData], which is used to configure this theme.
class ChannelListHeaderTheme extends InheritedTheme {
/// Creates a [ChannelListHeaderTheme].
///
/// The [data] parameter must not be null.
const ChannelListHeaderTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final ChannelListHeaderThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [ChannelListHeaderTheme] widget, then
/// [StreamChatThemeData.channelListHeaderTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// final theme = ChannelListHeaderTheme.of(context);
/// ```
static ChannelListHeaderThemeData of(BuildContext context) {
final channelListHeaderTheme =
context.dependOnInheritedWidgetOfExactType<ChannelListHeaderTheme>();
return channelListHeaderTheme?.data ??
StreamChatTheme.of(context).channelListHeaderTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
ChannelListHeaderTheme(data: data, child: child);
@override
bool updateShouldNotify(ChannelListHeaderTheme oldWidget) =>
data != oldWidget.data;
}
/// Theme dedicated to the [ChannelListHeader]
class ChannelListHeaderThemeData with Diagnosticable {
/// Returns a new [ChannelListHeaderThemeData]
const ChannelListHeaderThemeData({
this.titleStyle,
this.avatarTheme,
this.color,
});
/// Style of the title text
final TextStyle? titleStyle;
/// Theme dedicated to the userAvatar
final AvatarThemeData? avatarTheme;
/// Background color of the appbar
final Color? color;
/// Returns a new [ChannelListHeaderThemeData] replacing some of its
/// properties
ChannelListHeaderThemeData copyWith({
TextStyle? titleStyle,
AvatarThemeData? avatarTheme,
Color? color,
}) =>
ChannelListHeaderThemeData(
titleStyle: titleStyle ?? this.titleStyle,
avatarTheme: avatarTheme ?? this.avatarTheme,
color: color ?? this.color,
);
/// Linearly interpolate from one [ChannelListHeaderThemeData] to another.
ChannelListHeaderThemeData lerp(
ChannelListHeaderThemeData a,
ChannelListHeaderThemeData b,
double t,
) =>
ChannelListHeaderThemeData(
avatarTheme:
const AvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t),
color: Color.lerp(a.color, b.color, t),
titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t),
);
/// Merges [this] [ChannelListHeaderThemeData] with the [other]
ChannelListHeaderThemeData merge(ChannelListHeaderThemeData? other) {
if (other == null) return this;
return copyWith(
titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle,
avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme,
color: other.color,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChannelListHeaderThemeData &&
runtimeType == other.runtimeType &&
titleStyle == other.titleStyle &&
avatarTheme == other.avatarTheme &&
color == other.color;
@override
int get hashCode =>
titleStyle.hashCode ^ avatarTheme.hashCode ^ color.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('titleStyle', titleStyle))
..add(DiagnosticsProperty('avatarTheme', avatarTheme))
..add(ColorProperty('color', color));
}
}
@@ -0,0 +1,111 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [ChannelListView] descendants.
///
/// See also:
///
/// * [ChannelListViewThemeData], which is used to configure this theme.
class ChannelListViewTheme extends InheritedTheme {
/// Creates a [ChannelListViewTheme].
///
/// The [data] parameter must not be null.
const ChannelListViewTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final ChannelListViewThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [ChannelListViewTheme] widget, then
/// [StreamChatThemeData.channelListViewTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// ChannelListViewTheme theme = ChannelListViewTheme.of(context);
/// ```
static ChannelListViewThemeData of(BuildContext context) {
final channelListViewTheme =
context.dependOnInheritedWidgetOfExactType<ChannelListViewTheme>();
return channelListViewTheme?.data ??
StreamChatTheme.of(context).channelListViewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
ChannelListViewTheme(data: data, child: child);
@override
bool updateShouldNotify(ChannelListViewTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [ChannelListView]s when
/// used with [ChannelListViewTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.channelListViewTheme].
///
/// See also:
///
/// * [ChannelListViewTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.channelListViewTheme], which can be used to override
/// the default style for [ChannelListView]s below the overall
/// [StreamChatTheme].
class ChannelListViewThemeData with Diagnosticable {
/// Creates a [ChannelListViewThemeData].
const ChannelListViewThemeData({
this.backgroundColor,
});
/// The color of the [ChannelListView] background.
final Color? backgroundColor;
/// Copies this [ChannelListViewThemeData] to another.
ChannelListViewThemeData copyWith({
Color? backgroundColor,
}) =>
ChannelListViewThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
);
/// Linearly interpolate between two [ChannelListViewThemeData] themes.
///
/// All the properties must be non-null.
ChannelListViewThemeData lerp(
ChannelListViewThemeData a,
ChannelListViewThemeData b,
double t,
) =>
ChannelListViewThemeData(
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
);
/// Merges one [ChannelListViewThemeData] with another.
ChannelListViewThemeData merge(ChannelListViewThemeData? other) {
if (other == null) return this;
return copyWith(
backgroundColor: other.backgroundColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChannelListViewThemeData &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor;
@override
int get hashCode => backgroundColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor));
}
}
@@ -0,0 +1,169 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
/// Overrides the default style of [ChannelPreview] descendants.
///
/// See also:
///
/// * [ChannelPreviewThemeData], which is used to configure this theme.
class ChannelPreviewTheme extends InheritedTheme {
/// Creates a [ChannelPreviewTheme].
///
/// The [data] parameter must not be null.
const ChannelPreviewTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final ChannelPreviewThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [ChannelPreviewTheme] widget, then
/// [StreamChatThemeData.channelPreviewTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// final theme = ChannelPreviewTheme.of(context);
/// ```
static ChannelPreviewThemeData of(BuildContext context) {
final channelPreviewTheme =
context.dependOnInheritedWidgetOfExactType<ChannelPreviewTheme>();
return channelPreviewTheme?.data ??
StreamChatTheme.of(context).channelPreviewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
ChannelPreviewTheme(data: data, child: child);
@override
bool updateShouldNotify(ChannelPreviewTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [ChannelPreview]s when used
/// with [ChannelPreviewTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.channelPreviewTheme].
///
/// See also:
///
/// * [ChannelPreviewTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.channelPreviewTheme], which can be used to override
/// the default style for [ChannelHeader]s below the overall [StreamChatTheme].
class ChannelPreviewThemeData with Diagnosticable {
/// Creates a [ChannelPreviewThemeData].
const ChannelPreviewThemeData({
this.titleStyle,
this.subtitleStyle,
this.lastMessageAtStyle,
this.avatarTheme,
this.unreadCounterColor,
this.indicatorIconSize,
});
/// Theme for title
final TextStyle? titleStyle;
/// Theme for subtitle
final TextStyle? subtitleStyle;
/// Theme of last message at
final TextStyle? lastMessageAtStyle;
/// Avatar theme
final AvatarThemeData? avatarTheme;
/// Unread counter color
final Color? unreadCounterColor;
/// Indicator icon size
final double? indicatorIconSize;
/// Copy with theme
ChannelPreviewThemeData copyWith({
TextStyle? titleStyle,
TextStyle? subtitleStyle,
TextStyle? lastMessageAtStyle,
AvatarThemeData? avatarTheme,
Color? unreadCounterColor,
double? indicatorIconSize,
}) =>
ChannelPreviewThemeData(
titleStyle: titleStyle ?? this.titleStyle,
subtitleStyle: subtitleStyle ?? this.subtitleStyle,
lastMessageAtStyle: lastMessageAtStyle ?? this.lastMessageAtStyle,
avatarTheme: avatarTheme ?? this.avatarTheme,
unreadCounterColor: unreadCounterColor ?? this.unreadCounterColor,
indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize,
);
/// Linearly interpolate one [ChannelPreviewThemeData] to another.
ChannelPreviewThemeData lerp(
ChannelPreviewThemeData a,
ChannelPreviewThemeData b,
double t,
) =>
ChannelPreviewThemeData(
avatarTheme:
const AvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t),
indicatorIconSize: a.indicatorIconSize,
lastMessageAtStyle:
TextStyle.lerp(a.lastMessageAtStyle, b.lastMessageAtStyle, t),
subtitleStyle: TextStyle.lerp(a.subtitleStyle, b.subtitleStyle, t),
titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t),
unreadCounterColor:
Color.lerp(a.unreadCounterColor, b.unreadCounterColor, t),
);
/// Merge with theme
ChannelPreviewThemeData merge(ChannelPreviewThemeData? other) {
if (other == null) return this;
return copyWith(
titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle,
subtitleStyle:
subtitleStyle?.merge(other.subtitleStyle) ?? other.subtitleStyle,
lastMessageAtStyle: lastMessageAtStyle?.merge(other.lastMessageAtStyle) ??
other.lastMessageAtStyle,
avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme,
unreadCounterColor: other.unreadCounterColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChannelPreviewThemeData &&
runtimeType == other.runtimeType &&
titleStyle == other.titleStyle &&
subtitleStyle == other.subtitleStyle &&
lastMessageAtStyle == other.lastMessageAtStyle &&
avatarTheme == other.avatarTheme &&
unreadCounterColor == other.unreadCounterColor &&
indicatorIconSize == other.indicatorIconSize;
@override
int get hashCode =>
titleStyle.hashCode ^
subtitleStyle.hashCode ^
lastMessageAtStyle.hashCode ^
avatarTheme.hashCode ^
unreadCounterColor.hashCode ^
indicatorIconSize.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('titleStyle', titleStyle))
..add(DiagnosticsProperty('subtitleStyle', subtitleStyle))
..add(DiagnosticsProperty('lastMessageAtStyle', lastMessageAtStyle))
..add(DiagnosticsProperty('avatarTheme', avatarTheme))
..add(ColorProperty('unreadCounterColor', unreadCounterColor));
}
}
@@ -0,0 +1,286 @@
import 'package:flutter/material.dart';
/// Theme that holds colors
class ColorTheme {
/// Initialise with light theme
ColorTheme.light({
this.textHighEmphasis = const Color(0xff000000),
this.textLowEmphasis = const Color(0xff7a7a7a),
this.disabled = const Color(0xffdbdbdb),
this.borders = const Color(0xffecebeb),
this.inputBg = const Color(0xfff2f2f2),
this.appBg = const Color(0xfffcfcfc),
this.barsBg = const Color(0xffffffff),
this.linkBg = const Color(0xffe9f2ff),
this.accentPrimary = const Color(0xff005FFF),
this.accentError = const Color(0xffFF3842),
this.accentInfo = const Color(0xff20E070),
this.highlight = const Color(0xfffbf4dd),
this.overlay = const Color.fromRGBO(0, 0, 0, 0.2),
this.overlayDark = const Color.fromRGBO(0, 0, 0, 0.6),
this.bgGradient = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xfff7f7f7), Color(0xfffcfcfc)],
stops: [0, 1],
),
this.borderTop = const Effect(
sigmaX: 0, sigmaY: -1, color: Color(0xff000000), blur: 0, alpha: 0.08),
this.borderBottom = const Effect(
sigmaX: 0, sigmaY: 1, color: Color(0xff000000), blur: 0, alpha: 0.08),
this.shadowIconButton = const Effect(
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4),
this.modalShadow = const Effect(
sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8),
}) : brightness = Brightness.light;
/// Initialise with dark theme
ColorTheme.dark({
this.textHighEmphasis = const Color(0xffffffff),
this.textLowEmphasis = const Color(0xff7a7a7a),
this.disabled = const Color(0xff2d2f2f),
this.borders = const Color(0xff1c1e22),
this.inputBg = const Color(0xff13151b),
this.appBg = const Color(0xff070A0D),
this.barsBg = const Color(0xff101418),
this.linkBg = const Color(0xff00193D),
this.accentPrimary = const Color(0xff005FFF),
this.accentError = const Color(0xffFF3742),
this.accentInfo = const Color(0xff20E070),
this.borderTop = const Effect(
sigmaX: 0,
sigmaY: -1,
color: Color(0xff141924),
blur: 0,
),
this.borderBottom = const Effect(
sigmaX: 0,
sigmaY: 1,
color: Color(0xff141924),
blur: 0,
alpha: 1,
),
this.shadowIconButton = const Effect(
sigmaX: 0,
sigmaY: 2,
color: Color(0xff000000),
alpha: 0.5,
blur: 4,
),
this.modalShadow = const Effect(
sigmaX: 0,
sigmaY: 0,
color: Color(0xff000000),
alpha: 1,
blur: 8,
),
this.highlight = const Color(0xff302d22),
this.overlay = const Color.fromRGBO(0, 0, 0, 0.4),
this.overlayDark = const Color.fromRGBO(255, 255, 255, 0.6),
this.bgGradient = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xff101214),
Color(0xff070a0d),
],
stops: [0, 1],
),
}) : brightness = Brightness.dark;
///
final Color textHighEmphasis;
///
final Color textLowEmphasis;
///
final Color disabled;
///
final Color borders;
///
final Color inputBg;
///
final Color appBg;
///
final Color barsBg;
///
final Color linkBg;
///
final Color accentPrimary;
///
final Color accentError;
///
final Color accentInfo;
///
final Effect borderTop;
///
final Effect borderBottom;
///
final Effect shadowIconButton;
///
final Effect modalShadow;
///
final Color highlight;
///
final Color overlay;
///
final Color overlayDark;
///
final Gradient bgGradient;
///
final Brightness brightness;
/// Copy with theme
ColorTheme copyWith({
Brightness brightness = Brightness.light,
Color? textHighEmphasis,
Color? textLowEmphasis,
Color? disabled,
Color? borders,
Color? inputBg,
Color? appBg,
Color? barsBg,
Color? linkBg,
Color? accentPrimary,
Color? accentError,
Color? accentInfo,
Effect? borderTop,
Effect? borderBottom,
Effect? shadowIconButton,
Effect? modalShadow,
Color? highlight,
Color? overlay,
Color? overlayDark,
Gradient? bgGradient,
}) =>
brightness == Brightness.light
? ColorTheme.light(
textHighEmphasis: textHighEmphasis ?? this.textHighEmphasis,
textLowEmphasis: textLowEmphasis ?? this.textLowEmphasis,
disabled: disabled ?? this.disabled,
borders: borders ?? this.borders,
inputBg: inputBg ?? this.inputBg,
appBg: appBg ?? this.appBg,
barsBg: barsBg ?? this.barsBg,
linkBg: linkBg ?? this.linkBg,
accentPrimary: accentPrimary ?? this.accentPrimary,
accentError: accentError ?? this.accentError,
accentInfo: accentInfo ?? this.accentInfo,
borderTop: borderTop ?? this.borderTop,
borderBottom: borderBottom ?? this.borderBottom,
shadowIconButton: shadowIconButton ?? this.shadowIconButton,
modalShadow: modalShadow ?? this.modalShadow,
highlight: highlight ?? this.highlight,
overlay: overlay ?? this.overlay,
overlayDark: overlayDark ?? this.overlayDark,
bgGradient: bgGradient ?? this.bgGradient,
)
: ColorTheme.dark(
textHighEmphasis: textHighEmphasis ?? this.textHighEmphasis,
textLowEmphasis: textLowEmphasis ?? this.textLowEmphasis,
disabled: disabled ?? this.disabled,
borders: borders ?? this.borders,
inputBg: inputBg ?? this.inputBg,
appBg: appBg ?? this.appBg,
barsBg: barsBg ?? this.barsBg,
linkBg: linkBg ?? this.linkBg,
accentPrimary: accentPrimary ?? this.accentPrimary,
accentError: accentError ?? this.accentError,
accentInfo: accentInfo ?? this.accentInfo,
borderTop: borderTop ?? this.borderTop,
borderBottom: borderBottom ?? this.borderBottom,
shadowIconButton: shadowIconButton ?? this.shadowIconButton,
modalShadow: modalShadow ?? this.modalShadow,
highlight: highlight ?? this.highlight,
overlay: overlay ?? this.overlay,
overlayDark: overlayDark ?? this.overlayDark,
bgGradient: bgGradient ?? this.bgGradient,
);
/// Merge color theme
ColorTheme merge(ColorTheme? other) {
if (other == null) return this;
return copyWith(
textHighEmphasis: other.textHighEmphasis,
textLowEmphasis: other.textLowEmphasis,
disabled: other.disabled,
borders: other.borders,
inputBg: other.inputBg,
appBg: other.appBg,
barsBg: other.barsBg,
linkBg: other.linkBg,
accentPrimary: other.accentPrimary,
accentError: other.accentError,
accentInfo: other.accentInfo,
highlight: other.highlight,
overlay: other.overlay,
overlayDark: other.overlayDark,
bgGradient: other.bgGradient,
borderTop: other.borderTop,
borderBottom: other.borderBottom,
shadowIconButton: other.shadowIconButton,
modalShadow: other.modalShadow,
);
}
}
/// Effect store
class Effect {
/// Constructor for creating [Effect]
const Effect({
this.sigmaX,
this.sigmaY,
this.color,
this.alpha,
this.blur,
});
///
final double? sigmaX;
///
final double? sigmaY;
///
final Color? color;
///
final double? alpha;
///
final double? blur;
/// Copy with new effect
Effect copyWith({
double? sigmaX,
double? sigmaY,
Color? color,
double? alpha,
double? blur,
}) =>
Effect(
sigmaX: sigmaX ?? this.sigmaX,
sigmaY: sigmaY ?? this.sigmaY,
color: color ?? this.color,
alpha: color as double? ?? this.alpha,
blur: blur ?? this.blur,
);
}
@@ -0,0 +1,217 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [GalleryFooter] descendants.
///
/// See also:
///
/// * [GalleryFooterThemeData], which is used to configure this theme.
class GalleryFooterTheme extends InheritedTheme {
/// Creates an [GalleryFooterTheme].
///
/// The [data] parameter must not be null.
const GalleryFooterTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final GalleryFooterThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [GalleryFooterTheme] widget, then
/// [StreamChatThemeData.galleryFooterTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// ImageFooterTheme theme = ImageFooterTheme.of(context);
/// ```
static GalleryFooterThemeData of(BuildContext context) {
final imageFooterTheme =
context.dependOnInheritedWidgetOfExactType<GalleryFooterTheme>();
return imageFooterTheme?.data ??
StreamChatTheme.of(context).galleryFooterTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
GalleryFooterTheme(data: data, child: child);
@override
bool updateShouldNotify(GalleryFooterTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [GalleryFooter]s when used
/// with [GalleryFooterTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.galleryFooterTheme].
///
/// See also:
///
/// * [GalleryFooterTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.galleryFooterTheme], which can be used to override
/// the default style for [GalleryFooter]s below the overall [StreamChatTheme].
class GalleryFooterThemeData with Diagnosticable {
/// Creates an [GalleryFooterThemeData].
const GalleryFooterThemeData({
this.backgroundColor,
this.shareIconColor,
this.titleTextStyle,
this.gridIconButtonColor,
this.bottomSheetBarrierColor,
this.bottomSheetBackgroundColor,
this.bottomSheetPhotosTextStyle,
this.bottomSheetCloseIconColor,
});
/// The background color for the [GalleryFooter] widget.
///
/// Defaults to [ColorTheme.barsBg].
final Color? backgroundColor;
/// The color for the "share" icon.
///
/// Defaults to [ColorTheme.textHighEmphasis].
final Color? shareIconColor;
/// The [TextStyle] to use for the [GalleryFooter] title text.
///
/// Defaults to [TextTheme.headlineBold].
final TextStyle? titleTextStyle;
/// The color to use for the "grid" icon.
///
/// Defaults to [ColorTheme.textHighEmphasis].
final Color? gridIconButtonColor;
/// The color to use behind the bottom sheet.
///
/// Defaults to [ColorTheme.overlay].
final Color? bottomSheetBarrierColor;
/// The background color to use for the bottom sheet.
///
/// Defaults to [ColorTheme.barsBg].
final Color? bottomSheetBackgroundColor;
/// The [TextStyle] to use for the "photos" text in the bottom sheet.
///
/// Defaults to [TextTheme.headlineBold].
final TextStyle? bottomSheetPhotosTextStyle;
/// The color to use for the "close" icon.
///
/// Defaults to [ColorTheme.textHighEmphasis].
final Color? bottomSheetCloseIconColor;
/// Copies this [GalleryFooterThemeData] to another.
GalleryFooterThemeData copyWith({
Color? backgroundColor,
Color? shareIconColor,
TextStyle? titleTextStyle,
Color? gridIconButtonColor,
Color? bottomSheetBarrierColor,
Color? bottomSheetBackgroundColor,
TextStyle? bottomSheetPhotosTextStyle,
Color? bottomSheetCloseIconColor,
}) =>
GalleryFooterThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
shareIconColor: shareIconColor ?? this.shareIconColor,
titleTextStyle: titleTextStyle ?? this.titleTextStyle,
gridIconButtonColor: gridIconButtonColor ?? this.gridIconButtonColor,
bottomSheetBarrierColor:
bottomSheetBarrierColor ?? this.bottomSheetBarrierColor,
bottomSheetBackgroundColor:
bottomSheetBackgroundColor ?? this.bottomSheetBackgroundColor,
bottomSheetPhotosTextStyle:
bottomSheetPhotosTextStyle ?? this.bottomSheetPhotosTextStyle,
bottomSheetCloseIconColor:
bottomSheetCloseIconColor ?? this.bottomSheetCloseIconColor,
);
/// Linearly interpolate between two [GalleryFooter] themes.
///
/// All the properties must be non-null.
GalleryFooterThemeData lerp(
GalleryFooterThemeData a,
GalleryFooterThemeData b,
double t,
) =>
GalleryFooterThemeData(
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
shareIconColor: Color.lerp(a.shareIconColor, b.shareIconColor, t),
titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t),
gridIconButtonColor:
Color.lerp(a.gridIconButtonColor, b.gridIconButtonColor, t),
bottomSheetBarrierColor:
Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t),
bottomSheetBackgroundColor: Color.lerp(
a.bottomSheetBackgroundColor, b.bottomSheetBackgroundColor, t),
bottomSheetPhotosTextStyle: TextStyle.lerp(
a.bottomSheetPhotosTextStyle, b.bottomSheetPhotosTextStyle, t),
bottomSheetCloseIconColor: Color.lerp(
a.bottomSheetCloseIconColor, b.bottomSheetCloseIconColor, t),
);
/// Merges one [GalleryFooterThemeData] with another.
GalleryFooterThemeData merge(GalleryFooterThemeData? other) {
if (other == null) return this;
return copyWith(
backgroundColor: other.backgroundColor,
bottomSheetBarrierColor: other.bottomSheetBarrierColor,
bottomSheetBackgroundColor: other.bottomSheetBackgroundColor,
bottomSheetCloseIconColor: other.bottomSheetCloseIconColor,
bottomSheetPhotosTextStyle: other.bottomSheetPhotosTextStyle,
gridIconButtonColor: other.gridIconButtonColor,
titleTextStyle: other.titleTextStyle,
shareIconColor: other.shareIconColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is GalleryFooterThemeData &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor &&
shareIconColor == other.shareIconColor &&
titleTextStyle == other.titleTextStyle &&
gridIconButtonColor == other.gridIconButtonColor &&
bottomSheetBarrierColor == other.bottomSheetBarrierColor &&
bottomSheetBackgroundColor == other.bottomSheetBackgroundColor &&
bottomSheetPhotosTextStyle == other.bottomSheetPhotosTextStyle &&
bottomSheetCloseIconColor == other.bottomSheetCloseIconColor;
@override
int get hashCode =>
backgroundColor.hashCode ^
shareIconColor.hashCode ^
titleTextStyle.hashCode ^
gridIconButtonColor.hashCode ^
bottomSheetBarrierColor.hashCode ^
bottomSheetBackgroundColor.hashCode ^
bottomSheetPhotosTextStyle.hashCode ^
bottomSheetCloseIconColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ColorProperty('backgroundColor', backgroundColor))
..add(ColorProperty('shareIconColor', shareIconColor))
..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
..add(ColorProperty('gridIconButtonColor', gridIconButtonColor))
..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor))
..add(ColorProperty(
'bottomSheetBackgroundColor', bottomSheetBackgroundColor))
..add(DiagnosticsProperty(
'bottomSheetPhotosTextStyle', bottomSheetPhotosTextStyle))
..add(ColorProperty(
'bottomSheetCloseIconColor', bottomSheetCloseIconColor));
}
}
@@ -0,0 +1,179 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [GalleryHeader] descendants.
///
/// See also:
///
/// * [GalleryHeaderThemeData], which is used to configure this theme.
class GalleryHeaderTheme extends InheritedTheme {
/// Creates a [GalleryHeaderTheme].
///
/// The [data] parameter must not be null.
const GalleryHeaderTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final GalleryHeaderThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [GalleryHeaderTheme] widget, then
/// [StreamChatThemeData.galleryHeaderTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// ImageHeaderTheme theme = ImageHeaderTheme.of(context);
/// ```
static GalleryHeaderThemeData of(BuildContext context) {
final galleryHeaderTheme =
context.dependOnInheritedWidgetOfExactType<GalleryHeaderTheme>();
return galleryHeaderTheme?.data ??
StreamChatTheme.of(context).galleryHeaderTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
GalleryHeaderTheme(data: data, child: child);
@override
bool updateShouldNotify(GalleryHeaderTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [GalleryHeader]s when used
/// with [GalleryHeaderTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.galleryHeaderTheme].
///
/// See also:
///
/// * [GalleryHeaderTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.galleryHeaderTheme], which can be used to override
/// the default style for [GalleryHeader]s below the overall [StreamChatTheme].
class GalleryHeaderThemeData with Diagnosticable {
/// Creates an [GalleryHeaderThemeData].
const GalleryHeaderThemeData({
this.closeButtonColor,
this.backgroundColor,
this.iconMenuPointColor,
this.titleTextStyle,
this.subtitleTextStyle,
this.bottomSheetBarrierColor,
});
/// The color of the "close" button.
///
/// Defaults to [ColorTheme.textHighEmphasis].
final Color? closeButtonColor;
/// The background color of the [GalleryHeader] widget.
///
/// Defaults to [ChannelHeaderTheme.color].
final Color? backgroundColor;
/// Defaults to [ColorTheme.textHighEmphasis].
final Color? iconMenuPointColor;
/// The [TextStyle] to use for the [GalleryHeader] title text.
///
/// Defaults to [TextTheme.headlineBold].
final TextStyle? titleTextStyle;
/// The [TextStyle] to use for the [GalleryHeader] subtitle text.
///
/// Defaults to [ChannelPreviewTheme.subtitleStyle].
final TextStyle? subtitleTextStyle;
///
final Color? bottomSheetBarrierColor;
/// Copies this [GalleryHeaderThemeData] to another.
GalleryHeaderThemeData copyWith({
Color? closeButtonColor,
Color? backgroundColor,
Color? iconMenuPointColor,
TextStyle? titleTextStyle,
TextStyle? subtitleTextStyle,
Color? bottomSheetBarrierColor,
}) =>
GalleryHeaderThemeData(
closeButtonColor: closeButtonColor ?? this.closeButtonColor,
backgroundColor: backgroundColor ?? this.backgroundColor,
iconMenuPointColor: iconMenuPointColor ?? this.iconMenuPointColor,
titleTextStyle: titleTextStyle ?? this.titleTextStyle,
subtitleTextStyle: subtitleTextStyle ?? this.subtitleTextStyle,
bottomSheetBarrierColor:
bottomSheetBarrierColor ?? this.bottomSheetBarrierColor,
);
/// Linearly interpolate between two [GalleryHeader] themes.
///
/// All the properties must be non-null.
GalleryHeaderThemeData lerp(
GalleryHeaderThemeData a,
GalleryHeaderThemeData b,
double t,
) =>
GalleryHeaderThemeData(
closeButtonColor: Color.lerp(a.closeButtonColor, b.closeButtonColor, t),
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
iconMenuPointColor:
Color.lerp(a.iconMenuPointColor, b.iconMenuPointColor, t),
titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t),
subtitleTextStyle:
TextStyle.lerp(a.subtitleTextStyle, b.subtitleTextStyle, t),
bottomSheetBarrierColor:
Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t),
);
/// Merges one [GalleryHeaderThemeData] with the another
GalleryHeaderThemeData merge(GalleryHeaderThemeData? other) {
if (other == null) return this;
return copyWith(
closeButtonColor: other.closeButtonColor,
backgroundColor: other.backgroundColor,
iconMenuPointColor: other.iconMenuPointColor,
titleTextStyle: other.titleTextStyle,
subtitleTextStyle: other.subtitleTextStyle,
bottomSheetBarrierColor: other.bottomSheetBarrierColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is GalleryHeaderThemeData &&
runtimeType == other.runtimeType &&
closeButtonColor == other.closeButtonColor &&
backgroundColor == other.backgroundColor &&
iconMenuPointColor == other.iconMenuPointColor &&
titleTextStyle == other.titleTextStyle &&
subtitleTextStyle == other.subtitleTextStyle &&
bottomSheetBarrierColor == other.bottomSheetBarrierColor;
@override
int get hashCode =>
closeButtonColor.hashCode ^
backgroundColor.hashCode ^
iconMenuPointColor.hashCode ^
titleTextStyle.hashCode ^
subtitleTextStyle.hashCode ^
bottomSheetBarrierColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ColorProperty('closeButtonColor', closeButtonColor))
..add(ColorProperty('backgroundColor', backgroundColor))
..add(ColorProperty('iconMenuPointColor', iconMenuPointColor))
..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
..add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle))
..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor));
}
}
@@ -0,0 +1,237 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [MessageInput] descendants.
///
/// See also:
///
/// * [MessageInputThemeData], which is used to configure this theme.
class MessageInputTheme extends InheritedTheme {
/// Creates a [MessageInputTheme].
///
/// The [data] parameter must not be null.
const MessageInputTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final MessageInputThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [MessageInputTheme] widget, then
/// [StreamChatThemeData.messageInputTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// final theme = MessageInputTheme.of(context);
/// ```
static MessageInputThemeData of(BuildContext context) {
final messageInputTheme =
context.dependOnInheritedWidgetOfExactType<MessageInputTheme>();
return messageInputTheme?.data ??
StreamChatTheme.of(context).messageInputTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
MessageInputTheme(data: data, child: child);
@override
bool updateShouldNotify(MessageInputTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [MessageInput] widgets
/// when used with [MessageInputTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.messageInputTheme].
class MessageInputThemeData with Diagnosticable {
/// Creates a [MessageInputThemeData].
const MessageInputThemeData({
this.sendAnimationDuration,
this.actionButtonColor,
this.sendButtonColor,
this.actionButtonIdleColor,
this.sendButtonIdleColor,
this.inputBackgroundColor,
this.inputTextStyle,
this.inputDecoration,
this.activeBorderGradient,
this.idleBorderGradient,
this.borderRadius,
this.expandButtonColor,
});
/// Duration of the [MessageInput] send button animation
final Duration? sendAnimationDuration;
/// Background color of [MessageInput] send button
final Color? sendButtonColor;
/// Background color of [MessageInput] action buttons
final Color? actionButtonColor;
/// Background color of [MessageInput] send button
final Color? sendButtonIdleColor;
/// Background color of [MessageInput] action buttons
final Color? actionButtonIdleColor;
/// Background color of [MessageInput] expand button
final Color? expandButtonColor;
/// Background color of [MessageInput]
final Color? inputBackgroundColor;
/// TextStyle of [MessageInput]
final TextStyle? inputTextStyle;
/// InputDecoration of [MessageInput]
final InputDecoration? inputDecoration;
/// Border gradient when the [MessageInput] is not focused
final Gradient? idleBorderGradient;
/// Border gradient when the [MessageInput] is focused
final Gradient? activeBorderGradient;
/// Border radius of [MessageInput]
final BorderRadius? borderRadius;
/// Returns a new [MessageInputThemeData] replacing some of its properties
MessageInputThemeData copyWith({
Duration? sendAnimationDuration,
Color? inputBackgroundColor,
Color? actionButtonColor,
Color? sendButtonColor,
Color? actionButtonIdleColor,
Color? sendButtonIdleColor,
Color? expandButtonColor,
TextStyle? inputTextStyle,
InputDecoration? inputDecoration,
Gradient? activeBorderGradient,
Gradient? idleBorderGradient,
BorderRadius? borderRadius,
}) =>
MessageInputThemeData(
sendAnimationDuration:
sendAnimationDuration ?? this.sendAnimationDuration,
inputBackgroundColor: inputBackgroundColor ?? this.inputBackgroundColor,
actionButtonColor: actionButtonColor ?? this.actionButtonColor,
sendButtonColor: sendButtonColor ?? this.sendButtonColor,
actionButtonIdleColor:
actionButtonIdleColor ?? this.actionButtonIdleColor,
expandButtonColor: expandButtonColor ?? this.expandButtonColor,
inputTextStyle: inputTextStyle ?? this.inputTextStyle,
sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor,
inputDecoration: inputDecoration ?? this.inputDecoration,
activeBorderGradient: activeBorderGradient ?? this.activeBorderGradient,
idleBorderGradient: idleBorderGradient ?? this.idleBorderGradient,
borderRadius: borderRadius ?? this.borderRadius,
);
/// Linearly interpolate from one [MessageInputThemeData] to another.
MessageInputThemeData lerp(
MessageInputThemeData a,
MessageInputThemeData b,
double t,
) =>
MessageInputThemeData(
actionButtonColor:
Color.lerp(a.actionButtonColor, b.actionButtonColor, t),
actionButtonIdleColor:
Color.lerp(a.actionButtonIdleColor, b.actionButtonIdleColor, t),
activeBorderGradient:
Gradient.lerp(a.activeBorderGradient, b.activeBorderGradient, t),
borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t),
expandButtonColor:
Color.lerp(a.expandButtonColor, b.expandButtonColor, t),
idleBorderGradient:
Gradient.lerp(a.idleBorderGradient, b.idleBorderGradient, t),
inputBackgroundColor:
Color.lerp(a.inputBackgroundColor, b.inputBackgroundColor, t),
inputTextStyle: TextStyle.lerp(a.inputTextStyle, b.inputTextStyle, t),
sendButtonColor: Color.lerp(a.sendButtonColor, b.sendButtonColor, t),
sendButtonIdleColor:
Color.lerp(a.sendButtonIdleColor, b.sendButtonIdleColor, t),
sendAnimationDuration: a.sendAnimationDuration,
inputDecoration: a.inputDecoration,
);
/// Merges [this] [MessageInputThemeData] with the [other]
MessageInputThemeData merge(MessageInputThemeData? other) {
if (other == null) return this;
return copyWith(
sendAnimationDuration: other.sendAnimationDuration,
inputBackgroundColor: other.inputBackgroundColor,
actionButtonColor: other.actionButtonColor,
actionButtonIdleColor: other.actionButtonIdleColor,
sendButtonColor: other.sendButtonColor,
sendButtonIdleColor: other.sendButtonIdleColor,
inputTextStyle:
inputTextStyle?.merge(other.inputTextStyle) ?? other.inputTextStyle,
inputDecoration: inputDecoration?.merge(other.inputDecoration) ??
other.inputDecoration,
activeBorderGradient: other.activeBorderGradient,
idleBorderGradient: other.idleBorderGradient,
borderRadius: other.borderRadius,
expandButtonColor: other.expandButtonColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MessageInputThemeData &&
runtimeType == other.runtimeType &&
sendAnimationDuration == other.sendAnimationDuration &&
sendButtonColor == other.sendButtonColor &&
actionButtonColor == other.actionButtonColor &&
sendButtonIdleColor == other.sendButtonIdleColor &&
actionButtonIdleColor == other.actionButtonIdleColor &&
expandButtonColor == other.expandButtonColor &&
inputBackgroundColor == other.inputBackgroundColor &&
inputTextStyle == other.inputTextStyle &&
inputDecoration == other.inputDecoration &&
idleBorderGradient == other.idleBorderGradient &&
activeBorderGradient == other.activeBorderGradient &&
borderRadius == other.borderRadius;
@override
int get hashCode =>
sendAnimationDuration.hashCode ^
sendButtonColor.hashCode ^
actionButtonColor.hashCode ^
sendButtonIdleColor.hashCode ^
actionButtonIdleColor.hashCode ^
expandButtonColor.hashCode ^
inputBackgroundColor.hashCode ^
inputTextStyle.hashCode ^
inputDecoration.hashCode ^
idleBorderGradient.hashCode ^
activeBorderGradient.hashCode ^
borderRadius.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('sendAnimationDuration', sendAnimationDuration))
..add(ColorProperty('inputBackgroundColor', inputBackgroundColor))
..add(ColorProperty('actionButtonColor', actionButtonColor))
..add(ColorProperty('actionButtonIdleColor', actionButtonIdleColor))
..add(ColorProperty('sendButtonColor', sendButtonColor))
..add(ColorProperty('sendButtonIdleColor', sendButtonIdleColor))
..add(DiagnosticsProperty('inputTextStyle', inputTextStyle))
..add(DiagnosticsProperty('inputDecoration', inputDecoration))
..add(DiagnosticsProperty('activeBorderGradient', activeBorderGradient))
..add(DiagnosticsProperty('idleBorderGradient', idleBorderGradient))
..add(DiagnosticsProperty('borderRadius', borderRadius))
..add(ColorProperty('expandButtonColor', expandButtonColor));
}
}
@@ -0,0 +1,111 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [MessageListView] descendants.
///
/// See also:
///
/// * [MessageListViewThemeData], which is used to configure this theme.
class MessageListViewTheme extends InheritedTheme {
/// Creates a [MessageListViewTheme].
///
/// The [data] parameter must not be null.
const MessageListViewTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final MessageListViewThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [MessageListViewTheme] widget, then
/// [StreamChatThemeData.messageListViewTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// MessageListViewTheme theme = MessageListViewTheme.of(context);
/// ```
static MessageListViewThemeData of(BuildContext context) {
final messageListViewTheme =
context.dependOnInheritedWidgetOfExactType<MessageListViewTheme>();
return messageListViewTheme?.data ??
StreamChatTheme.of(context).messageListViewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
MessageListViewTheme(data: data, child: child);
@override
bool updateShouldNotify(MessageListViewTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [MessageListView]s when
/// used with [MessageListViewTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.messageListViewTheme].
///
/// See also:
///
/// * [MessageListViewTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.messageListViewTheme], which can be used to override
/// the default style for [MessageListView]s below the overall
/// [StreamChatTheme].
class MessageListViewThemeData with Diagnosticable {
/// Creates a [MessageListViewThemeData].
const MessageListViewThemeData({
this.backgroundColor,
});
/// The color of the [MessageListView] background.
final Color? backgroundColor;
/// Copies this [MessageListViewThemeData] to another.
MessageListViewThemeData copyWith({
Color? backgroundColor,
}) =>
MessageListViewThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
);
/// Linearly interpolate between two [MessageListView] themes.
///
/// All the properties must be non-null.
MessageListViewThemeData lerp(
MessageListViewThemeData a,
MessageListViewThemeData b,
double t,
) =>
MessageListViewThemeData(
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
);
/// Merges one [MessageListViewThemeData] with another.
MessageListViewThemeData merge(MessageListViewThemeData? other) {
if (other == null) return this;
return copyWith(
backgroundColor: other.backgroundColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MessageListViewThemeData &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor;
@override
int get hashCode => backgroundColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor));
}
}
@@ -0,0 +1,112 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [MessageSearchListView] descendants.
///
/// See also:
///
/// * [UserListViewThemeData], which is used to configure this theme.
class MessageSearchListViewTheme extends InheritedTheme {
/// Creates a [UserListViewTheme].
///
/// The [data] parameter must not be null.
const MessageSearchListViewTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final MessageSearchListViewThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [MessageSearchListView] widget, then
/// [StreamChatThemeData.messageSearchListViewTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// MessageSearchListViewTheme theme = MessageSearchListViewTheme.of(context);
/// ```
static MessageSearchListViewThemeData of(BuildContext context) {
final messageSearchListViewTheme = context
.dependOnInheritedWidgetOfExactType<MessageSearchListViewTheme>();
return messageSearchListViewTheme?.data ??
StreamChatTheme.of(context).messageSearchListViewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
MessageSearchListViewTheme(data: data, child: child);
@override
bool updateShouldNotify(MessageSearchListViewTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [MessageSearchListView]s
/// when used with [MessageSearchListView] or with the overall
/// [StreamChatTheme]'s [StreamChatThemeData.messageSearchListViewTheme].
///
/// See also:
///
/// * [MessageSearchListViewTheme], the theme which is configured with this
/// class.
/// * [StreamChatThemeData.messageSearchListViewTheme], which can be used to
/// override the default style for [UserListView]s below the overall
/// [StreamChatTheme].
class MessageSearchListViewThemeData with Diagnosticable {
/// Creates a [MessageSearchListViewThemeData].
const MessageSearchListViewThemeData({
this.backgroundColor,
});
/// The color of the [MessageSearchListView] background.
final Color? backgroundColor;
/// Copies this [MessageSearchListViewThemeData] to another.
MessageSearchListViewThemeData copyWith({
Color? backgroundColor,
}) =>
MessageSearchListViewThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
);
/// Linearly interpolate between two [UserListViewThemeData] themes.
///
/// All the properties must be non-null.
MessageSearchListViewThemeData lerp(
MessageSearchListViewThemeData a,
MessageSearchListViewThemeData b,
double t,
) =>
MessageSearchListViewThemeData(
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
);
/// Merges one [MessageSearchListViewThemeData] with another.
MessageSearchListViewThemeData merge(MessageSearchListViewThemeData? other) {
if (other == null) return this;
return copyWith(
backgroundColor: other.backgroundColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MessageSearchListViewThemeData &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor;
@override
int get hashCode => backgroundColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor));
}
}
@@ -0,0 +1,180 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/theme/avatar_theme.dart';
/// Class for getting message theme
class MessageThemeData with Diagnosticable {
/// Creates a [MessageThemeData].
const MessageThemeData({
this.repliesStyle,
this.messageTextStyle,
this.messageAuthorStyle,
this.messageLinksStyle,
this.messageBackgroundColor,
this.messageBorderColor,
this.reactionsBackgroundColor,
this.reactionsBorderColor,
this.reactionsMaskColor,
this.avatarTheme,
this.createdAtStyle,
});
/// Text style for message text
final TextStyle? messageTextStyle;
/// Text style for message author
final TextStyle? messageAuthorStyle;
/// Text style for message links
final TextStyle? messageLinksStyle;
/// Text style for created at text
final TextStyle? createdAtStyle;
/// Text style for replies
final TextStyle? repliesStyle;
/// Color for messageBackgroundColor
final Color? messageBackgroundColor;
/// Color for message border color
final Color? messageBorderColor;
/// Color for reactions
final Color? reactionsBackgroundColor;
/// Colors reaction border
final Color? reactionsBorderColor;
/// Color for reaction mask
final Color? reactionsMaskColor;
/// Theme of the avatar
final AvatarThemeData? avatarTheme;
/// Copy with a theme
MessageThemeData copyWith({
TextStyle? messageTextStyle,
TextStyle? messageAuthorStyle,
TextStyle? messageLinksStyle,
TextStyle? createdAtStyle,
TextStyle? repliesStyle,
Color? messageBackgroundColor,
Color? messageBorderColor,
AvatarThemeData? avatarTheme,
Color? reactionsBackgroundColor,
Color? reactionsBorderColor,
Color? reactionsMaskColor,
}) =>
MessageThemeData(
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
messageAuthorStyle: messageAuthorStyle ?? this.messageAuthorStyle,
messageLinksStyle: messageLinksStyle ?? this.messageLinksStyle,
createdAtStyle: createdAtStyle ?? this.createdAtStyle,
messageBackgroundColor:
messageBackgroundColor ?? this.messageBackgroundColor,
messageBorderColor: messageBorderColor ?? this.messageBorderColor,
avatarTheme: avatarTheme ?? this.avatarTheme,
repliesStyle: repliesStyle ?? this.repliesStyle,
reactionsBackgroundColor:
reactionsBackgroundColor ?? this.reactionsBackgroundColor,
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
);
/// Linearly interpolate from one [MessageThemeData] to another.
MessageThemeData lerp(MessageThemeData a, MessageThemeData b, double t) =>
MessageThemeData(
avatarTheme:
const AvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t),
createdAtStyle: TextStyle.lerp(a.createdAtStyle, b.createdAtStyle, t),
messageAuthorStyle:
TextStyle.lerp(a.messageAuthorStyle, b.messageAuthorStyle, t),
messageBackgroundColor:
Color.lerp(a.messageBackgroundColor, b.messageBackgroundColor, t),
messageBorderColor:
Color.lerp(a.messageBorderColor, b.messageBorderColor, t),
messageLinksStyle:
TextStyle.lerp(a.messageLinksStyle, b.messageLinksStyle, t),
messageTextStyle:
TextStyle.lerp(a.messageTextStyle, b.messageTextStyle, t),
reactionsBackgroundColor: Color.lerp(
a.reactionsBackgroundColor, b.reactionsBackgroundColor, t),
reactionsBorderColor:
Color.lerp(a.messageBorderColor, b.reactionsBorderColor, t),
reactionsMaskColor:
Color.lerp(a.reactionsMaskColor, b.reactionsMaskColor, t),
repliesStyle: TextStyle.lerp(a.repliesStyle, b.repliesStyle, t),
);
/// Merge with a theme
MessageThemeData merge(MessageThemeData? other) {
if (other == null) return this;
return copyWith(
messageTextStyle: messageTextStyle?.merge(other.messageTextStyle) ??
other.messageTextStyle,
messageAuthorStyle: messageAuthorStyle?.merge(other.messageAuthorStyle) ??
other.messageAuthorStyle,
messageLinksStyle: messageLinksStyle?.merge(other.messageLinksStyle) ??
other.messageLinksStyle,
createdAtStyle:
createdAtStyle?.merge(other.createdAtStyle) ?? other.createdAtStyle,
repliesStyle:
repliesStyle?.merge(other.repliesStyle) ?? other.repliesStyle,
messageBackgroundColor: other.messageBackgroundColor,
messageBorderColor: other.messageBorderColor,
avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme,
reactionsBackgroundColor: other.reactionsBackgroundColor,
reactionsBorderColor: other.reactionsBorderColor,
reactionsMaskColor: other.reactionsMaskColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MessageThemeData &&
runtimeType == other.runtimeType &&
messageTextStyle == other.messageTextStyle &&
messageAuthorStyle == other.messageAuthorStyle &&
messageLinksStyle == other.messageLinksStyle &&
createdAtStyle == other.createdAtStyle &&
repliesStyle == other.repliesStyle &&
messageBackgroundColor == other.messageBackgroundColor &&
messageBorderColor == other.messageBorderColor &&
reactionsBackgroundColor == other.reactionsBackgroundColor &&
reactionsBorderColor == other.reactionsBorderColor &&
reactionsMaskColor == other.reactionsMaskColor &&
avatarTheme == other.avatarTheme;
@override
int get hashCode =>
messageTextStyle.hashCode ^
messageAuthorStyle.hashCode ^
messageLinksStyle.hashCode ^
createdAtStyle.hashCode ^
repliesStyle.hashCode ^
messageBackgroundColor.hashCode ^
messageBorderColor.hashCode ^
reactionsBackgroundColor.hashCode ^
reactionsBorderColor.hashCode ^
reactionsMaskColor.hashCode ^
avatarTheme.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('messageTextStyle', messageTextStyle))
..add(DiagnosticsProperty('messageAuthorStyle', messageAuthorStyle))
..add(DiagnosticsProperty('messageLinksStyle', messageLinksStyle))
..add(DiagnosticsProperty('createdAtStyle', createdAtStyle))
..add(DiagnosticsProperty('repliesStyle', repliesStyle))
..add(ColorProperty('messageBackgroundColor', messageBackgroundColor))
..add(ColorProperty('messageBorderColor', messageBorderColor))
..add(DiagnosticsProperty('avatarTheme', avatarTheme))
..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor))
..add(ColorProperty('reactionsBorderColor', reactionsBorderColor))
..add(ColorProperty('reactionsMaskColor', reactionsMaskColor));
}
}
@@ -0,0 +1,163 @@
import 'package:flutter/material.dart';
/// Class for holding text theme
class TextTheme {
/// Initialise light text theme
TextTheme.light({
this.title = const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.headlineBold = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.headline = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.bodyBold = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.body = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.footnoteBold = const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.footnote = const TextStyle(
fontSize: 12,
color: Colors.black,
),
this.captionBold = const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.black,
),
});
/// Initialise with dark theme
TextTheme.dark({
this.title = const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.headlineBold = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.headline = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.bodyBold = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.body = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.footnoteBold = const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.footnote = const TextStyle(
fontSize: 12,
color: Colors.white,
),
this.captionBold = const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
});
/// Text theme for title
final TextStyle title;
/// Body Text theme for headline
final TextStyle headlineBold;
/// Text theme for headline
final TextStyle headline;
/// Bold Text theme for body
final TextStyle bodyBold;
/// Text theme body
final TextStyle body;
/// Bold Text theme for footnote
final TextStyle footnoteBold;
/// Text theme for footnote
final TextStyle footnote;
/// Bold Text theme for caption
final TextStyle captionBold;
/// Copy with theme
TextTheme copyWith({
Brightness brightness = Brightness.light,
TextStyle? body,
TextStyle? title,
TextStyle? headlineBold,
TextStyle? headline,
TextStyle? bodyBold,
TextStyle? footnoteBold,
TextStyle? footnote,
TextStyle? captionBold,
}) =>
brightness == Brightness.light
? TextTheme.light(
body: body ?? this.body,
title: title ?? this.title,
headlineBold: headlineBold ?? this.headlineBold,
headline: headline ?? this.headline,
bodyBold: bodyBold ?? this.bodyBold,
footnoteBold: footnoteBold ?? this.footnoteBold,
footnote: footnote ?? this.footnote,
captionBold: captionBold ?? this.captionBold,
)
: TextTheme.dark(
body: body ?? this.body,
title: title ?? this.title,
headlineBold: headlineBold ?? this.headlineBold,
headline: headline ?? this.headline,
bodyBold: bodyBold ?? this.bodyBold,
footnoteBold: footnoteBold ?? this.footnoteBold,
footnote: footnote ?? this.footnote,
captionBold: captionBold ?? this.captionBold,
);
/// Merge text theme
TextTheme merge(TextTheme? other) {
if (other == null) return this;
return copyWith(
body: body.merge(other.body),
title: title.merge(other.title),
headlineBold: headlineBold.merge(other.headlineBold),
headline: headline.merge(other.headline),
bodyBold: bodyBold.merge(other.bodyBold),
footnoteBold: footnoteBold.merge(other.footnoteBold),
footnote: footnote.merge(other.footnote),
captionBold: captionBold.merge(other.captionBold),
);
}
}
@@ -0,0 +1,14 @@
export 'avatar_theme.dart';
export 'channel_header_theme.dart';
export 'channel_list_header_theme.dart';
export 'channel_list_view_theme.dart';
export 'channel_preview_theme.dart';
export 'color_theme.dart';
export 'gallery_footer_theme.dart';
export 'gallery_header_theme.dart';
export 'message_input_theme.dart';
export 'message_list_view_theme.dart';
export 'message_search_list_view_theme.dart';
export 'message_theme.dart';
export 'text_theme.dart';
export 'user_list_view_theme.dart';
@@ -0,0 +1,111 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Overrides the default style of [UserListView] descendants.
///
/// See also:
///
/// * [UserListViewThemeData], which is used to configure this theme.
class UserListViewTheme extends InheritedTheme {
/// Creates a [UserListViewTheme].
///
/// The [data] parameter must not be null.
const UserListViewTheme({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
/// The configuration of this theme.
final UserListViewThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [UserListViewTheme] widget, then
/// [StreamChatThemeData.userListViewTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// UserListViewTheme theme = UserListViewTheme.of(context);
/// ```
static UserListViewThemeData of(BuildContext context) {
final userListViewTheme =
context.dependOnInheritedWidgetOfExactType<UserListViewTheme>();
return userListViewTheme?.data ??
StreamChatTheme.of(context).userListViewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) =>
UserListViewTheme(data: data, child: child);
@override
bool updateShouldNotify(UserListViewTheme oldWidget) =>
data != oldWidget.data;
}
/// A style that overrides the default appearance of [UserListView]s when
/// used with [UserListViewTheme] or with the overall [StreamChatTheme]'s
/// [StreamChatThemeData.userListViewTheme].
///
/// See also:
///
/// * [UserListViewTheme], the theme which is configured with this class.
/// * [StreamChatThemeData.userListViewTheme], which can be used to override
/// the default style for [UserListView]s below the overall
/// [StreamChatTheme].
class UserListViewThemeData with Diagnosticable {
/// Creates a [UserListViewThemeData].
const UserListViewThemeData({
this.backgroundColor,
});
/// The color of the [ChannelListView] background.
final Color? backgroundColor;
/// Copies this [ChannelListViewThemeData] to another.
UserListViewThemeData copyWith({
Color? backgroundColor,
}) =>
UserListViewThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
);
/// Linearly interpolate between two [UserListViewThemeData] themes.
///
/// All the properties must be non-null.
UserListViewThemeData lerp(
UserListViewThemeData a,
UserListViewThemeData b,
double t,
) =>
UserListViewThemeData(
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
);
/// Merges one [UserListViewThemeData] with another.
UserListViewThemeData merge(UserListViewThemeData? other) {
if (other == null) return this;
return copyWith(
backgroundColor: other.backgroundColor,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserListViewThemeData &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor;
@override
int get hashCode => backgroundColor.hashCode;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor));
}
}
@@ -104,7 +104,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context); final channelHeaderTheme = ChannelHeaderTheme.of(context);
final defaultSubtitle = subtitle ?? final defaultSubtitle = subtitle ??
Row( Row(
@@ -113,12 +113,11 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
children: [ children: [
Text( Text(
'${context.translations.withText} ', '${context.translations.withText} ',
style: chatThemeData.channelTheme.channelHeaderTheme.subtitle, style: channelHeaderTheme.subtitleStyle,
), ),
Flexible( Flexible(
child: ChannelName( child: ChannelName(
textStyle: textStyle: channelHeaderTheme.subtitleStyle,
chatThemeData.channelTheme.channelHeaderTheme.subtitle,
), ),
), ),
], ],
@@ -137,7 +136,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
showUnreads: true, showUnreads: true,
) )
: const SizedBox()), : const SizedBox()),
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color, backgroundColor: channelHeaderTheme.color,
centerTitle: true, centerTitle: true,
actions: actions, actions: actions,
title: InkWell( title: InkWell(
@@ -151,14 +150,14 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
title ?? title ??
Text( Text(
context.translations.threadReplyLabel, context.translations.threadReplyLabel,
style: chatThemeData.channelTheme.channelHeaderTheme.title, style: channelHeaderTheme.titleStyle,
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
if (showTypingIndicator) if (showTypingIndicator)
TypingIndicator( TypingIndicator(
alignment: Alignment.center, alignment: Alignment.center,
channel: StreamChannel.of(context).channel, channel: StreamChannel.of(context).channel,
style: chatThemeData.channelTheme.channelHeaderTheme.subtitle, style: channelHeaderTheme.subtitleStyle,
parentId: parent.id, parentId: parent.id,
alternativeWidget: defaultSubtitle, alternativeWidget: defaultSubtitle,
) )
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/theme/themes.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 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/extension.dart';
@@ -36,13 +36,12 @@ export 'src/stream_chat_theme.dart';
export 'src/stream_neumorphic_button.dart'; export 'src/stream_neumorphic_button.dart';
export 'src/stream_svg_icon.dart'; export 'src/stream_svg_icon.dart';
export 'src/system_message.dart'; export 'src/system_message.dart';
export 'src/theme/themes.dart';
export 'src/thread_header.dart'; export 'src/thread_header.dart';
export 'src/typing_indicator.dart'; export 'src/typing_indicator.dart';
export 'src/unread_indicator.dart'; export 'src/unread_indicator.dart';
export 'src/user_avatar.dart'; export 'src/user_avatar.dart';
export 'src/user_item.dart'; export 'src/user_item.dart';
export 'src/user_item.dart';
export 'src/user_list_view.dart';
export 'src/user_list_view.dart'; export 'src/user_list_view.dart';
export 'src/utils.dart'; export 'src/utils.dart';
export 'src/visible_footnote.dart'; export 'src/visible_footnote.dart';
@@ -21,11 +21,11 @@ void main() {
client: client, client: client,
child: const Scaffold( child: const Scaffold(
body: DeletedMessage( body: DeletedMessage(
messageTheme: MessageTheme( messageTheme: MessageThemeData(
createdAt: TextStyle( createdAtStyle: TextStyle(
color: Colors.black, color: Colors.black,
), ),
messageText: TextStyle(), messageTextStyle: TextStyle(),
), ),
), ),
), ),
@@ -84,7 +84,7 @@ void main() {
final clientState = MockClientState(); final clientState = MockClientState();
final channel = MockChannel(); final channel = MockChannel();
final channelState = MockChannelState(); final channelState = MockChannelState();
const messageTheme = MessageTheme(); const messageTheme = MessageThemeData();
final currentUser = OwnUser( final currentUser = OwnUser(
id: 'sahil', id: 'sahil',
@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('AvatarThemeData copyWith, ==, hashCode basics', () {
expect(const AvatarThemeData(), const AvatarThemeData().copyWith());
expect(const AvatarThemeData().hashCode,
const AvatarThemeData().copyWith().hashCode);
});
group('AvatarThemeData lerps correctly', () {
test('Lerp completely', () {
expect(
const AvatarThemeData()
.lerp(_avatarThemeDataControl1, _avatarThemeDataControl2, 1),
_avatarThemeDataControl2);
});
test('Lerp halfway', () {
expect(
const AvatarThemeData()
.lerp(_avatarThemeDataControl1, _avatarThemeDataControl2, 0.5),
_avatarThemeDataControlMidLerp);
});
});
test('Merging two AvatarThemeData results in the latter', () {
expect(_avatarThemeDataControl1.merge(_avatarThemeDataControl2),
_avatarThemeDataControl2);
});
}
const _avatarThemeDataControl1 = AvatarThemeData();
final _avatarThemeDataControlMidLerp = AvatarThemeData(
borderRadius: BorderRadius.circular(16),
constraints: const BoxConstraints.tightFor(
height: 33,
width: 33,
),
);
final _avatarThemeDataControl2 = AvatarThemeData(
borderRadius: BorderRadius.circular(12),
constraints: const BoxConstraints.tightFor(
height: 34,
width: 34,
),
);
@@ -0,0 +1,97 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('ChannelHeaderThemeData copyWith, ==, hashCode basics', () {
expect(const ChannelHeaderThemeData(),
const ChannelHeaderThemeData().copyWith());
expect(const ChannelHeaderThemeData().hashCode,
const ChannelHeaderThemeData().copyWith().hashCode);
});
group('ChannelHeaderThemeData lerps', () {
test(
'''Light ChannelHeaderThemeData lerps completely to dark ChannelHeaderThemeData''',
() {
expect(
const ChannelHeaderThemeData()
.lerp(_channelThemeControl, _channelThemeControlDark, 1),
_channelThemeControlDark);
});
test(
'''Light ChannelHeaderThemeData lerps halfway to dark ChannelHeaderThemeData''',
() {
expect(
const ChannelHeaderThemeData()
.lerp(_channelThemeControl, _channelThemeControlDark, 0.5),
_channelThemeControlMidLerp);
});
test(
'''Dark ChannelHeaderThemeData lerps completely to light ChannelHeaderThemeData''',
() {
expect(
const ChannelHeaderThemeData()
.lerp(_channelThemeControlDark, _channelThemeControl, 1),
_channelThemeControl);
});
});
test('Merging dark and light themes results in a dark theme', () {
expect(_channelThemeControl.merge(_channelThemeControlDark),
_channelThemeControlDark);
});
}
final _channelThemeControl = ChannelHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: const Color(0xff101418),
titleStyle: TextTheme.light().headlineBold.copyWith(
color: const Color(0xffffffff),
),
subtitleStyle: TextTheme.light().footnote.copyWith(
color: const Color(0xff7a7a7a),
),
);
final _channelThemeControlMidLerp = ChannelHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: const Color(0xff101418),
titleStyle: const TextStyle(
color: Color(0xffffffff),
fontWeight: FontWeight.bold,
fontSize: 16,
),
subtitleStyle: TextTheme.light().footnote.copyWith(
color: const Color(0xff7a7a7a),
),
);
final _channelThemeControlDark = ChannelHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: ColorTheme.dark().barsBg,
titleStyle: TextTheme.dark().headlineBold,
subtitleStyle: TextTheme.dark().footnote.copyWith(
color: const Color(0xff7A7A7A),
),
);
@@ -0,0 +1,94 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('ChannelListHeaderThemeData copyWith, ==, hashCode basics', () {
expect(const ChannelListHeaderThemeData(),
const ChannelListHeaderThemeData().copyWith());
expect(const ChannelListHeaderThemeData().hashCode,
const ChannelListHeaderThemeData().copyWith().hashCode);
});
group('ChannelListHeaderThemeData lerps', () {
test(
'''Light ChannelListHeaderThemeData lerps completely to dark ChannelListHeaderThemeData''',
() {
expect(
const ChannelListHeaderThemeData().lerp(
_channelListHeaderThemeControl,
_channelListHeaderThemeControlDark,
1),
_channelListHeaderThemeControlDark);
});
test(
'''Light ChannelListHeaderThemeData lerps halfway to dark ChannelListHeaderThemeData''',
() {
expect(
const ChannelListHeaderThemeData().lerp(
_channelListHeaderThemeControl,
_channelListHeaderThemeControlDark,
0.5),
_channelListHeaderThemeControlMidLerp);
});
test(
'''Dark ChannelListHeaderThemeData lerps completely to light ChannelListHeaderThemeData''',
() {
expect(
const ChannelListHeaderThemeData().lerp(
_channelListHeaderThemeControlDark,
_channelListHeaderThemeControl,
1),
_channelListHeaderThemeControl);
});
});
test('Merging dark and light themes results in a dark theme', () {
expect(
_channelListHeaderThemeControl
.merge(_channelListHeaderThemeControlDark),
_channelListHeaderThemeControlDark);
});
}
final _channelListHeaderThemeControl = ChannelListHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: ColorTheme.light().barsBg,
titleStyle: TextTheme.light().headlineBold,
);
final _channelListHeaderThemeControlMidLerp = ChannelListHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: const Color(0xff87898b),
titleStyle: const TextStyle(
color: Color(0xff7f7f7f),
fontSize: 16,
fontWeight: FontWeight.bold,
),
);
final _channelListHeaderThemeControlDark = ChannelListHeaderThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: ColorTheme.dark().barsBg,
titleStyle: TextTheme.dark().headlineBold,
);
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.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() {
test('ChannelListViewThemeData copyWith, ==, hashCode basics', () { test('ChannelListViewThemeData copyWith, ==, hashCode basics', () {
@@ -0,0 +1,108 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('ChannelPreviewThemeData copyWith, ==, hashCode basics', () {
expect(const ChannelPreviewThemeData(),
const ChannelPreviewThemeData().copyWith());
expect(const ChannelPreviewThemeData().hashCode,
const ChannelPreviewThemeData().copyWith().hashCode);
});
group('ChannelPreviewThemeData lerps', () {
test(
'''Light ChannelPreviewThemeData lerps completely to dark ChannelPreviewThemeData''',
() {
expect(
const ChannelPreviewThemeData().lerp(
_channelPreviewThemeControl, _channelPreviewThemeControlDark, 1),
_channelPreviewThemeControlDark);
});
test(
'''Light ChannelPreviewThemeData lerps halfway to dark ChannelPreviewThemeData''',
() {
expect(
const ChannelPreviewThemeData().lerp(_channelPreviewThemeControl,
_channelPreviewThemeControlDark, 0.5),
_channelPreviewThemeControlMidLerp);
});
test(
'''Dark ChannelPreviewThemeData lerps completely to light ChannelPreviewThemeData''',
() {
expect(
const ChannelPreviewThemeData().lerp(
_channelPreviewThemeControlDark, _channelPreviewThemeControl, 1),
_channelPreviewThemeControl);
});
});
test('Merging dark and light themes results in a dark theme', () {
expect(_channelPreviewThemeControl.merge(_channelPreviewThemeControlDark),
_channelPreviewThemeControlDark);
});
}
final _channelPreviewThemeControl = ChannelPreviewThemeData(
unreadCounterColor: ColorTheme.light().accentError,
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
titleStyle: TextTheme.light().bodyBold,
subtitleStyle: TextTheme.light().footnote.copyWith(
color: const Color(0xff7A7A7A),
),
lastMessageAtStyle: TextTheme.light().footnote.copyWith(
color: ColorTheme.light().textHighEmphasis.withOpacity(.5),
),
indicatorIconSize: 16,
);
final _channelPreviewThemeControlMidLerp = ChannelPreviewThemeData(
unreadCounterColor: const Color(0xffff3742),
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
titleStyle: const TextStyle(
color: Color(0xff7f7f7f),
fontSize: 14,
fontWeight: FontWeight.bold,
),
subtitleStyle: const TextStyle(
color: Color(0xff7a7a7a),
fontSize: 12,
),
lastMessageAtStyle: TextTheme.light().footnote.copyWith(
color: const Color(0x807f7f7f).withOpacity(.5),
),
indicatorIconSize: 16,
);
final _channelPreviewThemeControlDark = ChannelPreviewThemeData(
unreadCounterColor: ColorTheme.dark().accentError,
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
titleStyle: TextTheme.dark().bodyBold,
subtitleStyle: TextTheme.dark().footnote.copyWith(
color: const Color(0xff7A7A7A),
),
lastMessageAtStyle: TextTheme.dark().footnote.copyWith(
color: ColorTheme.dark().textHighEmphasis.withOpacity(.5),
),
indicatorIconSize: 16,
);
@@ -0,0 +1,122 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('MessageInputThemeData copyWith, ==, hashCode basics', () {
expect(const MessageInputThemeData(),
const MessageInputThemeData().copyWith());
expect(const MessageInputThemeData().hashCode,
const MessageInputThemeData().copyWith().hashCode);
});
group('MessageInputThemeData lerps correctly', () {
test('Lerp completely from light to dark', () {
expect(
const MessageInputThemeData().lerp(
_messageInputThemeControl, _messageInputThemeControlDark, 1),
_messageInputThemeControlDark);
});
test('Lerp halfway from light to dark', () {
expect(
const MessageInputThemeData().lerp(
_messageInputThemeControl, _messageInputThemeControlDark, 0.5),
_messageInputThemeControlMidLerp);
});
test('Lerp completely from dark to light', () {
expect(
const MessageInputThemeData().lerp(
_messageInputThemeControlDark, _messageInputThemeControl, 1),
_messageInputThemeControl);
});
});
test('Merging two MessageInputThemeData results in the latter', () {
expect(_messageInputThemeControl.merge(_messageInputThemeControlDark),
_messageInputThemeControlDark);
});
}
final _messageInputThemeControl = MessageInputThemeData(
borderRadius: BorderRadius.circular(20),
sendAnimationDuration: const Duration(milliseconds: 300),
actionButtonColor: ColorTheme.light().accentPrimary,
actionButtonIdleColor: ColorTheme.light().textLowEmphasis,
expandButtonColor: ColorTheme.light().accentPrimary,
sendButtonColor: ColorTheme.light().accentPrimary,
sendButtonIdleColor: ColorTheme.light().disabled,
inputBackgroundColor: ColorTheme.light().barsBg,
inputTextStyle: TextTheme.light().body,
idleBorderGradient: LinearGradient(
stops: const [0.0, 1.0],
colors: [
ColorTheme.light().disabled,
ColorTheme.light().disabled,
],
),
activeBorderGradient: LinearGradient(
stops: const [0.0, 1.0],
colors: [
ColorTheme.light().disabled,
ColorTheme.light().disabled,
],
),
);
final _messageInputThemeControlMidLerp = MessageInputThemeData(
borderRadius: BorderRadius.circular(20),
sendAnimationDuration: const Duration(milliseconds: 300),
inputBackgroundColor: const Color(0xff87898b),
actionButtonColor: const Color(0xff005fff),
actionButtonIdleColor: const Color(0xff7a7a7a),
sendButtonColor: const Color(0xff005fff),
sendButtonIdleColor: const Color(0xff848585),
expandButtonColor: const Color(0xff005fff),
inputTextStyle: const TextStyle(
color: Color(0xff7f7f7f),
fontSize: 14,
fontWeight: FontWeight.w500,
),
idleBorderGradient: const LinearGradient(
stops: [0.0, 1.0],
colors: [
Color(0xff848585),
Color(0xff848585),
],
),
activeBorderGradient: const LinearGradient(
stops: [0.0, 1.0],
colors: [
Color(0xff848585),
Color(0xff848585),
],
),
);
final _messageInputThemeControlDark = MessageInputThemeData(
borderRadius: BorderRadius.circular(20),
sendAnimationDuration: const Duration(milliseconds: 300),
actionButtonColor: ColorTheme.dark().accentPrimary,
actionButtonIdleColor: ColorTheme.dark().textLowEmphasis,
expandButtonColor: ColorTheme.dark().accentPrimary,
sendButtonColor: ColorTheme.dark().accentPrimary,
sendButtonIdleColor: ColorTheme.dark().disabled,
inputBackgroundColor: ColorTheme.dark().barsBg,
inputTextStyle: TextTheme.dark().body,
idleBorderGradient: LinearGradient(
stops: const [0.0, 1.0],
colors: [
ColorTheme.dark().disabled,
ColorTheme.dark().disabled,
],
),
activeBorderGradient: LinearGradient(
stops: const [0.0, 1.0],
colors: [
ColorTheme.dark().disabled,
ColorTheme.dark().disabled,
],
),
);
@@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.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';
class MockStreamChatClient extends Mock implements StreamChatClient {} class MockStreamChatClient extends Mock implements StreamChatClient {}
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.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() {
test('MessageSearchListViewThemeData copyWith, ==, hashCode basics', () { test('MessageSearchListViewThemeData copyWith, ==, hashCode basics', () {
@@ -0,0 +1,90 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
void main() {
test('MessageThemeData copyWith, ==, hashCode basics', () {
expect(const MessageThemeData(), const MessageThemeData().copyWith());
expect(const MessageThemeData().hashCode,
const MessageThemeData().copyWith().hashCode);
});
group('MessageThemeData lerps', () {
test('''Light MessageThemeData lerps completely to dark MessageThemeData''',
() {
expect(
const MessageThemeData()
.lerp(_messageThemeControl, _messageThemeControlDark, 1),
_messageThemeControlDark);
});
test('''Dark MessageThemeData lerps completely to light MessageThemeData''',
() {
expect(
const MessageThemeData()
.lerp(_messageThemeControlDark, _messageThemeControl, 1),
_messageThemeControl);
});
});
test('Merging dark and light themes results in a dark theme', () {
expect(_messageThemeControl.merge(_messageThemeControlDark),
_messageThemeControlDark);
});
}
final _messageThemeControl = MessageThemeData(
messageAuthorStyle: TextTheme.light().footnote.copyWith(
color: ColorTheme.light().textLowEmphasis,
),
messageTextStyle: TextTheme.light().body,
createdAtStyle: TextTheme.light().footnote.copyWith(
color: ColorTheme.light().textLowEmphasis,
),
repliesStyle: TextTheme.light().footnoteBold.copyWith(
color: ColorTheme.light().accentPrimary,
),
messageBackgroundColor: ColorTheme.light().disabled,
reactionsBackgroundColor: ColorTheme.light().barsBg,
reactionsBorderColor: ColorTheme.light().borders,
reactionsMaskColor: ColorTheme.light().appBg,
messageBorderColor: ColorTheme.light().disabled,
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 32,
width: 32,
),
),
messageLinksStyle: TextStyle(
color: ColorTheme.light().accentPrimary,
),
);
final _messageThemeControlDark = MessageThemeData(
messageAuthorStyle: TextTheme.dark().footnote.copyWith(
color: ColorTheme.dark().textLowEmphasis,
),
messageTextStyle: TextTheme.dark().body,
createdAtStyle: TextTheme.dark().footnote.copyWith(
color: ColorTheme.dark().textLowEmphasis,
),
repliesStyle: TextTheme.dark().footnoteBold.copyWith(
color: ColorTheme.dark().accentPrimary,
),
messageBackgroundColor: ColorTheme.dark().disabled,
reactionsBackgroundColor: ColorTheme.dark().barsBg,
reactionsBorderColor: ColorTheme.dark().borders,
reactionsMaskColor: ColorTheme.dark().appBg,
messageBorderColor: ColorTheme.dark().disabled,
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(20),
constraints: const BoxConstraints.tightFor(
height: 32,
width: 32,
),
),
messageLinksStyle: TextStyle(
color: ColorTheme.dark().accentPrimary,
),
);
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.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() {
test('UserListViewThemeData copyWith, ==, hashCode basics', () { test('UserListViewThemeData copyWith, ==, hashCode basics', () {