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
🐞 Fixed
@@ -531,6 +549,7 @@ Widget build(BuildContext context) {
},
home: ChannelListPage(),
);
}
```
- Fix reaction bubble going below previous message on iOS
@@ -118,13 +118,12 @@ class ChannelListPage extends StatelessWidget {
channel: channel,
),
title: ChannelName(
textStyle:
StreamChatTheme.of(context).channelPreviewTheme.title!.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.textHighEmphasis
.withOpacity(opacity),
),
textStyle: ChannelPreviewTheme.of(context).titleStyle!.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.textHighEmphasis
.withOpacity(opacity),
),
),
subtitle: Text(subtitle),
trailing: channel.state!.unreadCount > 0
@@ -58,17 +58,17 @@ class MyApp extends StatelessWidget {
final defaultTheme = StreamChatThemeData.fromTheme(themeData);
final colorTheme = defaultTheme.colorTheme;
final customTheme = defaultTheme.merge(StreamChatThemeData(
channelPreviewTheme: ChannelPreviewTheme(
avatarTheme: AvatarTheme(
channelPreviewTheme: ChannelPreviewThemeData(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(8),
),
),
otherMessageTheme: MessageTheme(
otherMessageTheme: MessageThemeData(
messageBackgroundColor: colorTheme.textHighEmphasis,
messageText: TextStyle(
messageTextStyle: TextStyle(
color: colorTheme.barsBg,
),
avatarTheme: AvatarTheme(
avatarTheme: AvatarThemeData(
borderRadius: BorderRadius.circular(8),
),
),
@@ -1,4 +1,5 @@
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_core/stream_chat_flutter_core.dart';
@@ -12,7 +13,7 @@ class AttachmentTitle extends StatelessWidget {
}) : super(key: key);
/// Theme to apply to text
final MessageTheme messageTheme;
final MessageThemeData messageTheme;
/// Attachment data to display
final Attachment attachment;
@@ -34,7 +35,7 @@ class AttachmentTitle extends StatelessWidget {
Text(
attachment.title!,
overflow: TextOverflow.ellipsis,
style: messageTheme.messageText?.copyWith(
style: messageTheme.messageTextStyle?.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
fontWeight: FontWeight.bold,
),
@@ -50,7 +51,7 @@ class AttachmentTitle extends StatelessWidget {
.toList()
.reversed
.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_upload_state_builder.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_core/stream_chat_flutter_core.dart';
@@ -27,8 +28,8 @@ class ImageAttachment extends AttachmentWidget {
size: size,
);
/// [MessageTheme] for showing image title
final MessageTheme messageTheme;
/// [MessageThemeData] for showing image title
final MessageThemeData messageTheme;
/// Flag for showing title
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_widget.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/stream_chat_flutter.dart';
@@ -24,8 +25,8 @@ class VideoAttachment extends AttachmentWidget {
size: size,
);
/// [MessageTheme] for showing title
final MessageTheme messageTheme;
/// [MessageThemeData] for showing title
final MessageThemeData messageTheme;
/// Callback when show message is tapped
final ShowMessageCallback? onShowMessage;
@@ -19,6 +19,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
bool _showActions = true;
late StreamChannelState _streamChannelState;
late ChannelPreviewThemeData _channelPreviewThemeData;
late StreamChatThemeData _streamChatThemeData;
late StreamChatState _streamChatState;
@@ -64,8 +65,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
child: ChannelInfo(
showTypingIndicator: false,
channel: _streamChannelState.channel,
textStyle:
_streamChatThemeData.channelPreviewTheme.subtitle,
textStyle: _channelPreviewThemeData.subtitleStyle,
),
),
const SizedBox(
@@ -213,6 +213,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
void didChangeDependencies() {
_streamChannelState = StreamChannel.of(context);
_streamChatThemeData = StreamChatTheme.of(context);
_channelPreviewThemeData = ChannelPreviewTheme.of(context);
_streamChatState = StreamChat.of(context);
super.didChangeDependencies();
}
@@ -105,7 +105,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
@override
Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel;
final chatThemeData = StreamChatTheme.of(context);
final channelHeaderTheme = ChannelHeaderTheme.of(context);
final leadingWidget = leading ??
(showBackButton
@@ -141,18 +141,17 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
brightness: Theme.of(context).brightness,
elevation: 1,
leading: leadingWidget,
backgroundColor:
chatThemeData.channelTheme.channelHeaderTheme.color,
backgroundColor: channelHeaderTheme.color,
actions: actions ??
<Widget>[
Padding(
padding: const EdgeInsets.only(right: 10),
child: Center(
child: ChannelAvatar(
borderRadius: chatThemeData.channelTheme
.channelHeaderTheme.avatarTheme?.borderRadius,
constraints: chatThemeData.channelTheme
.channelHeaderTheme.avatarTheme?.constraints,
borderRadius:
channelHeaderTheme.avatarTheme?.borderRadius,
constraints:
channelHeaderTheme.avatarTheme?.constraints,
onTap: onImageTap,
),
),
@@ -169,16 +168,14 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
children: <Widget>[
title ??
ChannelName(
textStyle: chatThemeData
.channelTheme.channelHeaderTheme.title,
textStyle: channelHeaderTheme.titleStyle,
),
const SizedBox(height: 2),
subtitle ??
ChannelInfo(
showTypingIndicator: showTypingIndicator,
channel: channel,
textStyle: chatThemeData
.channelTheme.channelHeaderTheme.subtitle,
textStyle: channelHeaderTheme.subtitleStyle,
),
],
),
@@ -65,10 +65,7 @@ class ChannelInfo extends StatelessWidget {
}
alternativeWidget = Text(
text,
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.subtitle,
style: ChannelHeaderTheme.of(context).subtitleStyle,
);
} else {
final userId = StreamChat.of(context).currentUser?.id;
@@ -2,10 +2,10 @@ import 'dart:ui';
import 'package:flutter/material.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/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:stream_chat_flutter/src/extension.dart';
/// Widget builder for title
typedef TitleBuilder = Widget Function(
@@ -46,7 +46,7 @@ typedef TitleBuilder = Widget Function(
/// if you don't have it in the widget tree.
///
/// 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.
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
/// Instantiates a ChannelListHeader
@@ -116,6 +116,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
}
final chatThemeData = StreamChatTheme.of(context);
final channelListHeaderThemeData = ChannelListHeaderTheme.of(context);
return InfoTile(
showMessage: showConnectionStateTile && showStatus,
message: statusString,
@@ -123,7 +124,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
textTheme: Theme.of(context).textTheme,
brightness: Theme.of(context).brightness,
elevation: 1,
backgroundColor: chatThemeData.channelListHeaderTheme.color,
backgroundColor: channelListHeaderThemeData.color,
centerTitle: true,
leading: leading ??
Center(
@@ -138,10 +139,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
}
Scaffold.of(context).openDrawer();
},
borderRadius: chatThemeData
.channelListHeaderTheme.avatarTheme?.borderRadius,
constraints: chatThemeData
.channelListHeaderTheme.avatarTheme?.constraints,
borderRadius: channelListHeaderThemeData
.avatarTheme?.borderRadius,
constraints: channelListHeaderThemeData
.avatarTheme?.constraints,
)
: const Offstage(),
),
@@ -227,10 +228,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
const SizedBox(width: 10),
Text(
context.translations.searchingForNetworkText,
style: StreamChatTheme.of(context)
.channelListHeaderTheme
.title
?.copyWith(
style: ChannelListHeaderTheme.of(context).titleStyle?.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
),
@@ -243,12 +241,13 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
StreamChatClient client,
) {
final chatThemeData = StreamChatTheme.of(context);
final channelListHeaderTheme = ChannelListHeaderTheme.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translations.offlineLabel,
style: chatThemeData.channelListHeaderTheme.title?.copyWith(
style: channelListHeaderTheme.titleStyle?.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
),
@@ -259,7 +258,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
..openConnection(),
child: Text(
context.translations.tryAgainLabel,
style: chatThemeData.channelListHeaderTheme.title?.copyWith(
style: channelListHeaderTheme.titleStyle?.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: chatThemeData.colorTheme.accentPrimary,
@@ -5,6 +5,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.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/theme/themes.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/src/extension.dart';
@@ -3,10 +3,10 @@ import 'package:collection/collection.dart'
import 'package:flutter/material.dart';
import 'package:flutter/widgets.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/stream_chat_flutter.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_paint.png)
@@ -69,7 +69,7 @@ class ChannelPreview extends StatelessWidget {
@override
Widget build(BuildContext context) {
final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme;
final channelPreviewTheme = ChannelPreviewTheme.of(context);
final streamChatState = StreamChat.of(context);
return BetterStreamBuilder<bool>(
stream: channel.isMutedStream,
@@ -91,7 +91,7 @@ class ChannelPreview extends StatelessWidget {
Flexible(
child: title ??
ChannelName(
textStyle: channelPreviewTheme.title,
textStyle: channelPreviewTheme.titleStyle,
),
),
BetterStreamBuilder<List<Member>?>(
@@ -183,14 +183,13 @@ class ChannelPreview extends StatelessWidget {
return Text(
stringDate,
style:
StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt,
style: ChannelPreviewTheme.of(context).lastMessageAtStyle,
);
},
);
Widget _buildSubtitle(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
final channelPreviewTheme = ChannelPreviewTheme.of(context);
if (channel.isMuted) {
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
@@ -200,7 +199,7 @@ class ChannelPreview extends StatelessWidget {
),
Text(
' ${context.translations.channelIsMutedText}',
style: chatThemeData.channelPreviewTheme.subtitle,
style: channelPreviewTheme.subtitleStyle,
),
],
);
@@ -208,7 +207,7 @@ class ChannelPreview extends StatelessWidget {
return TypingIndicator(
channel: channel,
alternativeWidget: _buildLastMessage(context),
style: chatThemeData.channelPreviewTheme.subtitle,
style: channelPreviewTheme.subtitleStyle,
);
}
@@ -243,19 +242,19 @@ class ChannelPreview extends StatelessWidget {
text = parts.join(' ');
final chatThemeData = StreamChatTheme.of(context);
final channelPreviewTheme = ChannelPreviewTheme.of(context);
return Text.rich(
_getDisplayText(
text,
lastMessage.mentionedUsers,
lastMessage.attachments,
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
color: chatThemeData.channelPreviewTheme.subtitle?.color,
channelPreviewTheme.subtitleStyle?.copyWith(
color: channelPreviewTheme.subtitleStyle?.color,
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
? FontStyle.italic
: FontStyle.normal),
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
color: chatThemeData.channelPreviewTheme.subtitle?.color,
channelPreviewTheme.subtitleStyle?.copyWith(
color: channelPreviewTheme.subtitleStyle?.color,
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
? FontStyle.italic
: FontStyle.normal,
@@ -1,6 +1,7 @@
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/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/themes.dart';
/// Widget to display deleted message
class DeletedMessage extends StatelessWidget {
@@ -15,7 +16,7 @@ class DeletedMessage extends StatelessWidget {
}) : super(key: key);
/// The theme of the message
final MessageTheme messageTheme;
final MessageThemeData messageTheme;
/// The border radius of the message text
final BorderRadiusGeometry? borderRadiusGeometry;
@@ -51,9 +52,9 @@ class DeletedMessage extends StatelessWidget {
),
child: Text(
context.translations.messageDeletedLabel,
style: messageTheme.messageText?.copyWith(
style: messageTheme.messageTextStyle?.copyWith(
fontStyle: FontStyle.italic,
color: messageTheme.createdAt?.color,
color: messageTheme.createdAtStyle?.color,
),
),
),
@@ -124,10 +124,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
),
backgroundDecoration: BoxDecoration(
color: ColorTween(
begin: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.color,
begin: ChannelHeaderTheme.of(context).color,
end: Colors.black,
).lerp(_controller.value),
),
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/theme/themes.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_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/stream_chat_theme.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';
/// Header/AppBar widget for media display screen
@@ -1,5 +1,6 @@
import 'package:flutter/material.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_core/stream_chat_flutter_core.dart';
@@ -25,8 +26,8 @@ class ImageGroup extends StatelessWidget {
/// Message which images are attached to
final Message message;
/// [MessageTheme] to apply to message
final MessageTheme messageTheme;
/// [MessageThemeData] to apply to message
final MessageThemeData messageTheme;
/// Size of iamges
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/reaction_picker.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/stream_chat_flutter.dart';
@@ -49,8 +50,8 @@ class MessageActionsModal extends StatefulWidget {
/// Message in focus for actions
final Message message;
/// [MessageTheme] for message
final MessageTheme messageTheme;
/// [MessageThemeData] for message
final MessageThemeData messageTheme;
/// Flag for showing reactions
final bool showReactions;
@@ -116,7 +117,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
}
}
final roughSentenceSize = messageTextLength *
(widget.messageTheme.messageText?.fontSize ?? 1) *
(widget.messageTheme.messageTextStyle?.fontSize ?? 1) *
1.2;
final divFactor = widget.message.attachments.isNotEmpty == true
? 1
@@ -565,7 +566,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
elevation: 2,
clipBehavior: Clip.hardEdge,
isScrollControlled: true,
backgroundColor: streamChatThemeData.messageInputTheme.inputBackground,
backgroundColor: MessageInputTheme.of(context).inputBackgroundColor,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
@@ -288,6 +288,7 @@ class MessageInputState extends State<MessageInput> {
late final TextEditingController textEditingController;
late StreamChatThemeData _streamChatTheme;
late MessageInputThemeData _messageInputTheme;
bool get _hasQuotedMessage => widget.quotedMessage != null;
@@ -328,7 +329,7 @@ class MessageInputState extends State<MessageInput> {
Widget build(BuildContext context) {
Widget child = DecoratedBox(
decoration: BoxDecoration(
color: _streamChatTheme.messageInputTheme.inputBackground,
color: _messageInputTheme.inputBackgroundColor,
),
child: SafeArea(
child: GestureDetector(
@@ -484,7 +485,7 @@ class MessageInputState extends State<MessageInput> {
: CrossFadeState.showSecond,
firstChild: sendButton,
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!,
duration: _messageInputTheme.sendAnimationDuration!,
alignment: Alignment.center,
);
}
@@ -509,7 +510,7 @@ class MessageInputState extends State<MessageInput> {
? pi
: 0,
child: StreamSvgIcon.emptyCircleLeft(
color: _streamChatTheme.messageInputTheme.expandButtonColor,
color: _messageInputTheme.expandButtonColor,
),
),
padding: const EdgeInsets.all(0),
@@ -555,17 +556,17 @@ class MessageInputState extends State<MessageInput> {
clipBehavior: Clip.hardEdge,
margin: margin,
decoration: BoxDecoration(
borderRadius: _streamChatTheme.messageInputTheme.borderRadius,
borderRadius: _messageInputTheme.borderRadius,
gradient: _focusNode.hasFocus
? _streamChatTheme.messageInputTheme.activeBorderGradient
: _streamChatTheme.messageInputTheme.idleBorderGradient,
? _messageInputTheme.activeBorderGradient
: _messageInputTheme.idleBorderGradient,
),
child: Padding(
padding: const EdgeInsets.all(1.5),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: _streamChatTheme.messageInputTheme.borderRadius,
color: _streamChatTheme.messageInputTheme.inputBackground,
borderRadius: _messageInputTheme.borderRadius,
color: _messageInputTheme.inputBackgroundColor,
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -583,7 +584,7 @@ class MessageInputState extends State<MessageInput> {
keyboardType: widget.keyboardType,
controller: textEditingController,
focusNode: _focusNode,
style: _streamChatTheme.messageInputTheme.inputTextStyle,
style: _messageInputTheme.inputTextStyle,
autofocus: widget.autofocus,
textAlignVertical: TextAlignVertical.center,
decoration: _getInputDecoration(context),
@@ -599,11 +600,11 @@ class MessageInputState extends State<MessageInput> {
}
InputDecoration _getInputDecoration(BuildContext context) {
final passedDecoration = _streamChatTheme.messageInputTheme.inputDecoration;
final passedDecoration = _messageInputTheme.inputDecoration;
return InputDecoration(
isDense: true,
hintText: _getHint(context),
hintStyle: _streamChatTheme.messageInputTheme.inputTextStyle!.copyWith(
hintStyle: _messageInputTheme.inputTextStyle!.copyWith(
color: _streamChatTheme.colorTheme.textLowEmphasis,
),
border: const OutlineInputBorder(
@@ -1688,8 +1689,8 @@ class MessageInputState extends State<MessageInput> {
color: s.isNotEmpty
? _streamChatTheme.colorTheme.disabled
: (_commandsOverlay != null
? _streamChatTheme.messageInputTheme.actionButtonColor
: _streamChatTheme.messageInputTheme.actionButtonIdleColor),
? _messageInputTheme.actionButtonColor
: _messageInputTheme.actionButtonIdleColor),
),
padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor(
@@ -1726,8 +1727,8 @@ class MessageInputState extends State<MessageInput> {
Widget _buildAttachmentButton() => IconButton(
icon: StreamSvgIcon.attach(
color: _openFilePickerSection
? _streamChatTheme.messageInputTheme.actionButtonColor
: _streamChatTheme.messageInputTheme.actionButtonIdleColor,
? _messageInputTheme.actionButtonColor
: _messageInputTheme.actionButtonIdleColor,
),
padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor(
@@ -1963,7 +1964,7 @@ class MessageInputState extends State<MessageInput> {
padding: const EdgeInsets.all(8),
child: StreamSvgIcon(
assetName: _getIdleSendIcon(),
color: _streamChatTheme.messageInputTheme.sendButtonIdleColor,
color: _messageInputTheme.sendButtonIdleColor,
),
);
@@ -1979,7 +1980,7 @@ class MessageInputState extends State<MessageInput> {
),
icon: StreamSvgIcon(
assetName: _getSendIcon(),
color: _streamChatTheme.messageInputTheme.sendButtonColor,
color: _messageInputTheme.sendButtonColor,
),
),
);
@@ -2186,6 +2187,7 @@ class MessageInputState extends State<MessageInput> {
@override
void didChangeDependencies() {
_streamChatTheme = StreamChatTheme.of(context);
_messageInputTheme = MessageInputTheme.of(context);
if (widget.editMessage != null && !_initialized) {
FocusScope.of(context).requestFocus(_focusNode);
_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/swipeable.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_core/stream_chat_flutter_core.dart';
import 'package:visibility_detector/visibility_detector.dart';
@@ -628,7 +629,7 @@ class _MessageListViewState extends State<MessageListView> {
child: Text(
context.translations.threadSeparatorText(replyCount),
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_picker.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/stream_chat_flutter.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
final Message message;
/// [MessageTheme] to apply to [message]
final MessageTheme messageTheme;
/// [MessageThemeData] to apply to [message]
final MessageThemeData messageTheme;
/// Flag to reverse message
final bool reverse;
@@ -56,8 +57,9 @@ class MessageReactionsModal extends StatelessWidget {
messageTextLength = quotedMessageLength;
}
}
final roughSentenceSize =
messageTextLength * (messageTheme.messageText?.fontSize ?? 1) * 1.2;
final roughSentenceSize = messageTextLength *
(messageTheme.messageTextStyle?.fontSize ?? 1) *
1.2;
final divFactor = message.attachments.isNotEmpty == true
? 1
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
@@ -36,7 +36,7 @@ class MessageSearchItem extends StatelessWidget {
final channel = getMessageResponse.channel;
final channelName = channel?.extraData['name'];
final user = message.user!;
final chatThemeData = StreamChatTheme.of(context);
final channelPreviewTheme = ChannelPreviewTheme.of(context);
return ListTile(
onTap: onTap,
leading: UserAvatar(
@@ -53,18 +53,18 @@ class MessageSearchItem extends StatelessWidget {
user.id == StreamChat.of(context).currentUser?.id
? context.translations.youText
: user.name,
style: chatThemeData.channelPreviewTheme.title,
style: channelPreviewTheme.titleStyle,
),
if (channelName != null) ...[
Text(
' ${context.translations.inText} ',
style: chatThemeData.channelPreviewTheme.title?.copyWith(
style: channelPreviewTheme.titleStyle?.copyWith(
fontWeight: FontWeight.normal,
),
),
Text(
channelName as String,
style: chatThemeData.channelPreviewTheme.title,
style: channelPreviewTheme.titleStyle,
),
],
],
@@ -94,7 +94,7 @@ class MessageSearchItem extends StatelessWidget {
return Text(
stringDate,
style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt,
style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAtStyle,
);
}
@@ -122,18 +122,18 @@ class MessageSearchItem extends StatelessWidget {
text = parts.join(' ');
}
final chatThemeData = StreamChatTheme.of(context);
final channelPreviewTheme = ChannelPreviewTheme.of(context);
return Text.rich(
_getDisplayText(
text!,
message.mentionedUsers,
message.attachments,
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
channelPreviewTheme.subtitleStyle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic
: FontStyle.normal,
),
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
channelPreviewTheme.subtitleStyle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic
: FontStyle.normal,
@@ -1,6 +1,7 @@
import 'package:flutter/material.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/theme/themes.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/src/extension.dart';
@@ -1,6 +1,7 @@
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.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_core/stream_chat_flutter_core.dart';
@@ -24,8 +25,8 @@ class MessageText extends StatelessWidget {
/// Callback for when link is tapped
final void Function(String)? onLinkTap;
/// [MessageTheme] whose text theme is to be applied
final MessageTheme messageTheme;
/// [MessageThemeData] whose text theme is to be applied
final MessageThemeData messageTheme;
@override
Widget build(BuildContext context) {
@@ -66,16 +67,16 @@ class MessageText extends StatelessWidget {
styleSheet: MarkdownStyleSheet.fromTheme(
themeData.copyWith(
textTheme: themeData.textTheme.apply(
bodyColor: messageTheme.messageText?.color,
decoration: messageTheme.messageText?.decoration,
decorationColor: messageTheme.messageText?.decorationColor,
decorationStyle: messageTheme.messageText?.decorationStyle,
fontFamily: messageTheme.messageText?.fontFamily,
bodyColor: messageTheme.messageTextStyle?.color,
decoration: messageTheme.messageTextStyle?.decoration,
decorationColor: messageTheme.messageTextStyle?.decorationColor,
decorationStyle: messageTheme.messageTextStyle?.decorationStyle,
fontFamily: messageTheme.messageTextStyle?.fontFamily,
),
),
).copyWith(
a: messageTheme.messageLinks,
p: messageTheme.messageText,
a: messageTheme.messageLinksStyle,
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/quoted_message_widget.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/stream_chat_flutter.dart';
@@ -290,7 +291,7 @@ class MessageWidget extends StatefulWidget {
final Message message;
/// The message theme
final MessageTheme messageTheme;
final MessageThemeData messageTheme;
/// If true the widget will be mirrored
final bool reverse;
@@ -422,7 +423,7 @@ class MessageWidget extends StatefulWidget {
Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
void Function(BuildContext, Message)? onMessageActions,
Message? message,
MessageTheme? messageTheme,
MessageThemeData? messageTheme,
bool? reverse,
ShapeBorder? shape,
ShapeBorder? attachmentShape,
@@ -893,14 +894,14 @@ class _MessageWidgetState extends State<MessageWidget>
),
InkWell(
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 (showTimeStamp)
Text(
Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAt,
style: widget.messageTheme.createdAtStyle,
),
if (showSendingIndicator) _buildSendingIndicator(),
]);
@@ -917,7 +918,7 @@ class _MessageWidgetState extends State<MessageWidget>
Container(
margin: EdgeInsets.only(
bottom: context.textScaleFactor *
((widget.messageTheme.replies?.fontSize ?? 1) / 2),
((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
),
child: CustomPaint(
size: const Size(16, 32) * context.textScaleFactor,
@@ -944,7 +945,7 @@ class _MessageWidgetState extends State<MessageWidget>
Container(
margin: EdgeInsets.only(
bottom: context.textScaleFactor *
((widget.messageTheme.replies?.fontSize ?? 1) / 2),
((widget.messageTheme.repliesStyle?.fontSize ?? 1) / 2),
),
child: CustomPaint(
size: const Size(16, 32) * context.textScaleFactor,
@@ -967,7 +968,7 @@ class _MessageWidgetState extends State<MessageWidget>
widget.message.user!.name,
maxLines: 1,
key: usernameKey,
style: widget.messageTheme.messageAuthor,
style: widget.messageTheme.messageAuthorStyle,
overflow: TextOverflow.ellipsis,
);
}
@@ -1191,7 +1192,7 @@ class _MessageWidgetState extends State<MessageWidget>
}
Widget _buildSendingIndicator() {
final style = widget.messageTheme.createdAt;
final style = widget.messageTheme.createdAtStyle;
final message = widget.message;
if (hasNonUrlAttachments &&
@@ -1271,8 +1272,8 @@ class _MessageWidgetState extends State<MessageWidget>
onMentionTap: widget.onMentionTap,
messageTheme: isOnlyEmoji
? widget.messageTheme.copyWith(
messageText:
widget.messageTheme.messageText!.copyWith(
messageTextStyle:
widget.messageTheme.messageTextStyle!.copyWith(
fontSize: 42,
))
: widget.messageTheme,
@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.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_core/stream_chat_flutter_core.dart';
import 'package:video_player/video_player.dart';
@@ -73,7 +74,7 @@ class QuotedMessageWidget extends StatelessWidget {
final Message message;
/// The message theme
final MessageTheme messageTheme;
final MessageThemeData messageTheme;
/// If true the widget will be mirrored
final bool reverse;
@@ -138,11 +139,11 @@ class QuotedMessageWidget extends StatelessWidget {
message: msg,
messageTheme: isOnlyEmoji && _containsText
? messageTheme.copyWith(
messageText: messageTheme.messageText?.copyWith(
messageTextStyle: messageTheme.messageTextStyle?.copyWith(
fontSize: 32,
))
: messageTheme.copyWith(
messageText: messageTheme.messageText?.copyWith(
messageTextStyle: messageTheme.messageTextStyle?.copyWith(
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
Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
final channelHeaderTheme = ChannelHeaderTheme.of(context);
final defaultSubtitle = subtitle ??
Row(
@@ -113,12 +113,11 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
children: [
Text(
'${context.translations.withText} ',
style: chatThemeData.channelTheme.channelHeaderTheme.subtitle,
style: channelHeaderTheme.subtitleStyle,
),
Flexible(
child: ChannelName(
textStyle:
chatThemeData.channelTheme.channelHeaderTheme.subtitle,
textStyle: channelHeaderTheme.subtitleStyle,
),
),
],
@@ -137,7 +136,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
showUnreads: true,
)
: const SizedBox()),
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
backgroundColor: channelHeaderTheme.color,
centerTitle: true,
actions: actions,
title: InkWell(
@@ -151,14 +150,14 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
title ??
Text(
context.translations.threadReplyLabel,
style: chatThemeData.channelTheme.channelHeaderTheme.title,
style: channelHeaderTheme.titleStyle,
),
const SizedBox(height: 2),
if (showTypingIndicator)
TypingIndicator(
alignment: Alignment.center,
channel: StreamChannel.of(context).channel,
style: chatThemeData.channelTheme.channelHeaderTheme.subtitle,
style: channelHeaderTheme.subtitleStyle,
parentId: parent.id,
alternativeWidget: defaultSubtitle,
)
@@ -1,4 +1,5 @@
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_core/stream_chat_flutter_core.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_svg_icon.dart';
export 'src/system_message.dart';
export 'src/theme/themes.dart';
export 'src/thread_header.dart';
export 'src/typing_indicator.dart';
export 'src/unread_indicator.dart';
export 'src/user_avatar.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/utils.dart';
export 'src/visible_footnote.dart';
@@ -21,11 +21,11 @@ void main() {
client: client,
child: const Scaffold(
body: DeletedMessage(
messageTheme: MessageTheme(
createdAt: TextStyle(
messageTheme: MessageThemeData(
createdAtStyle: TextStyle(
color: Colors.black,
),
messageText: TextStyle(),
messageTextStyle: TextStyle(),
),
),
),
@@ -84,7 +84,7 @@ void main() {
final clientState = MockClientState();
final channel = MockChannel();
final channelState = MockChannelState();
const messageTheme = MessageTheme();
const messageTheme = MessageThemeData();
final currentUser = OwnUser(
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:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart';
import '../mocks.dart';
void main() {
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:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart';
import '../mocks.dart';
class MockStreamChatClient extends Mock implements StreamChatClient {}
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart';
import '../mocks.dart';
void main() {
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:stream_chat_flutter/stream_chat_flutter.dart';
import 'mocks.dart';
import '../mocks.dart';
void main() {
test('UserListViewThemeData copyWith, ==, hashCode basics', () {