import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/channel_header.dart'; import 'package:stream_chat_flutter/src/channel_preview.dart'; import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/gradient_avatar.dart'; import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/reaction_icon.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// Inherited widget providing the [StreamChatThemeData] to the widget tree class StreamChatTheme extends InheritedWidget { /// Constructor for creating a [StreamChatTheme] const StreamChatTheme({ Key? key, required this.data, required Widget child, }) : super( key: key, child: child, ); /// Theme data final StreamChatThemeData data; @override bool updateShouldNotify(StreamChatTheme old) => data != old.data; /// Use this method to get the current [StreamChatThemeData] instance static StreamChatThemeData of(BuildContext context) { final streamChatTheme = context.dependOnInheritedWidgetOfExactType(); assert( streamChatTheme != null, 'You must have a StreamChatTheme widget at the top of your widget tree', ); return streamChatTheme!.data; } } /// Theme data class StreamChatThemeData { /// Create a theme from scratch factory StreamChatThemeData({ Brightness? brightness, TextTheme? textTheme, ColorTheme? colorTheme, ChannelListHeaderTheme? channelListHeaderTheme, ChannelPreviewTheme? channelPreviewTheme, ChannelTheme? channelTheme, MessageTheme? otherMessageTheme, MessageTheme? ownMessageTheme, MessageInputTheme? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, IconThemeData? primaryIconTheme, List? reactionIcons, GalleryHeaderThemeData? imageHeaderTheme, GalleryFooterThemeData? imageFooterTheme, MessageListViewThemeData? messageListViewTheme, ChannelListViewThemeData? channelListViewTheme, UserListViewThemeData? userListViewTheme, MessageSearchListViewThemeData? messageSearchListViewTheme, }) { brightness ??= colorTheme?.brightness ?? Brightness.light; final isDark = brightness == Brightness.dark; textTheme ??= isDark ? TextTheme.dark() : TextTheme.light(); colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light(); final defaultData = fromColorAndTextTheme( colorTheme, textTheme, ); final customizedData = defaultData.copyWith( channelListHeaderTheme: channelListHeaderTheme, channelPreviewTheme: channelPreviewTheme, channelTheme: channelTheme, otherMessageTheme: otherMessageTheme, ownMessageTheme: ownMessageTheme, messageInputTheme: messageInputTheme, defaultUserImage: defaultUserImage, primaryIconTheme: primaryIconTheme, reactionIcons: reactionIcons, galleryHeaderTheme: imageHeaderTheme, galleryFooterTheme: imageFooterTheme, messageListViewTheme: messageListViewTheme, channelListViewTheme: channelListViewTheme, userListViewTheme: userListViewTheme, messageSearchListViewTheme: messageSearchListViewTheme, ); return defaultData.merge(customizedData); } /// Theme initialised with light factory StreamChatThemeData.light() => StreamChatThemeData(brightness: Brightness.light); /// Theme initialised with dark factory StreamChatThemeData.dark() => StreamChatThemeData(brightness: Brightness.dark); /// Raw theme init const StreamChatThemeData.raw({ required this.textTheme, required this.colorTheme, required this.channelListHeaderTheme, required this.channelPreviewTheme, required this.channelTheme, required this.otherMessageTheme, required this.ownMessageTheme, required this.messageInputTheme, required this.defaultUserImage, required this.primaryIconTheme, required this.reactionIcons, required this.galleryHeaderTheme, required this.galleryFooterTheme, required this.messageListViewTheme, required this.channelListViewTheme, required this.userListViewTheme, required this.messageSearchListViewTheme, }); /// Create a theme from a Material [Theme] factory StreamChatThemeData.fromTheme(ThemeData theme) { final defaultTheme = StreamChatThemeData(brightness: theme.brightness); final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( defaultTheme.colorTheme.copyWith( accentPrimary: theme.accentColor, ), defaultTheme.textTheme, ); return defaultTheme.merge(customizedTheme); } /// The text themes used in the widgets final TextTheme textTheme; /// The color themes used in the widgets final ColorTheme colorTheme; /// Theme of the [ChannelPreview] final ChannelPreviewTheme channelPreviewTheme; /// Theme of the [ChannelListHeader] final ChannelListHeaderTheme channelListHeaderTheme; /// Theme of the chat widgets dedicated to a channel final ChannelTheme channelTheme; /// The default style for [GalleryHeader]s below the overall /// [StreamChatTheme]. final GalleryHeaderThemeData galleryHeaderTheme; /// The default style for [GalleryFooter]s below the overall /// [StreamChatTheme]. final GalleryFooterThemeData galleryFooterTheme; /// Theme of the current user messages final MessageTheme ownMessageTheme; /// Theme of other users messages final MessageTheme otherMessageTheme; /// Theme dedicated to the [MessageInput] widget final MessageInputTheme messageInputTheme; /// The widget that will be built when the user image is unavailable final Widget Function(BuildContext, User) defaultUserImage; /// Primary icon theme final IconThemeData primaryIconTheme; /// Assets used for rendering reactions final List reactionIcons; /// Theme configuration for the [MessageListView] widget. final MessageListViewThemeData messageListViewTheme; /// Theme configuration for the [ChannelListView] widget. final ChannelListViewThemeData channelListViewTheme; /// Theme configuration for the [UserListView] widget. final UserListViewThemeData userListViewTheme; /// Theme configuration for the [] widget. final MessageSearchListViewThemeData messageSearchListViewTheme; /// Creates a copy of [StreamChatThemeData] with specified attributes /// overridden. StreamChatThemeData copyWith({ TextTheme? textTheme, ColorTheme? colorTheme, ChannelPreviewTheme? channelPreviewTheme, ChannelTheme? channelTheme, MessageTheme? ownMessageTheme, MessageTheme? otherMessageTheme, MessageInputTheme? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, IconThemeData? primaryIconTheme, ChannelListHeaderTheme? channelListHeaderTheme, List? reactionIcons, GalleryHeaderThemeData? galleryHeaderTheme, GalleryFooterThemeData? galleryFooterTheme, MessageListViewThemeData? messageListViewTheme, ChannelListViewThemeData? channelListViewTheme, UserListViewThemeData? userListViewTheme, MessageSearchListViewThemeData? messageSearchListViewTheme, }) => StreamChatThemeData.raw( channelListHeaderTheme: this.channelListHeaderTheme.merge(channelListHeaderTheme), textTheme: this.textTheme.merge(textTheme), colorTheme: this.colorTheme.merge(colorTheme), primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme), defaultUserImage: defaultUserImage ?? this.defaultUserImage, channelPreviewTheme: this.channelPreviewTheme.merge(channelPreviewTheme), channelTheme: this.channelTheme.merge(channelTheme), ownMessageTheme: this.ownMessageTheme.merge(ownMessageTheme), otherMessageTheme: this.otherMessageTheme.merge(otherMessageTheme), messageInputTheme: this.messageInputTheme.merge(messageInputTheme), reactionIcons: reactionIcons ?? this.reactionIcons, galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme, galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, channelListViewTheme: channelListViewTheme ?? this.channelListViewTheme, userListViewTheme: userListViewTheme ?? this.userListViewTheme, messageSearchListViewTheme: messageSearchListViewTheme ?? this.messageSearchListViewTheme, ); /// Merge themes StreamChatThemeData merge(StreamChatThemeData? other) { if (other == null) return this; return copyWith( channelListHeaderTheme: channelListHeaderTheme.merge(other.channelListHeaderTheme), textTheme: textTheme.merge(other.textTheme), colorTheme: colorTheme.merge(other.colorTheme), primaryIconTheme: other.primaryIconTheme, defaultUserImage: other.defaultUserImage, channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme), channelTheme: channelTheme.merge(other.channelTheme), ownMessageTheme: ownMessageTheme.merge(other.ownMessageTheme), otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme), messageInputTheme: messageInputTheme.merge(other.messageInputTheme), reactionIcons: other.reactionIcons, galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme), galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme), messageListViewTheme: messageListViewTheme.merge(other.messageListViewTheme), channelListViewTheme: channelListViewTheme.merge(other.channelListViewTheme), userListViewTheme: userListViewTheme.merge(other.userListViewTheme), messageSearchListViewTheme: messageSearchListViewTheme.merge(other.messageSearchListViewTheme), ); } /// Create theme from color and text theme // ignore: prefer_constructors_over_static_methods static StreamChatThemeData fromColorAndTextTheme( ColorTheme colorTheme, TextTheme textTheme, ) { final accentColor = colorTheme.accentPrimary; final iconTheme = IconThemeData(color: colorTheme.textHighEmphasis.withOpacity(.5)); final channelTheme = ChannelTheme( channelHeaderTheme: ChannelHeaderTheme( avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( height: 40, width: 40, ), ), color: colorTheme.barsBg, title: textTheme.headlineBold, subtitle: textTheme.footnote.copyWith( color: const Color(0xff7A7A7A), ), ), ); final channelPreviewTheme = ChannelPreviewTheme( unreadCounterColor: colorTheme.accentError, avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( height: 40, width: 40, ), ), title: textTheme.bodyBold, subtitle: textTheme.footnote.copyWith( color: const Color(0xff7A7A7A), ), lastMessageAt: textTheme.footnote.copyWith( color: colorTheme.textHighEmphasis.withOpacity(.5), ), indicatorIconSize: 16, ); return StreamChatThemeData.raw( textTheme: textTheme, colorTheme: colorTheme, primaryIconTheme: iconTheme, defaultUserImage: (context, user) => Center( child: GradientAvatar( name: user.name, userId: user.id, ), ), channelPreviewTheme: channelPreviewTheme, channelListHeaderTheme: ChannelListHeaderTheme( avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( height: 40, width: 40, ), ), color: colorTheme.barsBg, title: textTheme.headlineBold, ), channelTheme: channelTheme, ownMessageTheme: MessageTheme( messageAuthor: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), messageText: textTheme.body, createdAt: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), replies: textTheme.footnoteBold.copyWith(color: accentColor), messageBackgroundColor: colorTheme.disabled, reactionsBackgroundColor: colorTheme.barsBg, reactionsBorderColor: colorTheme.borders, reactionsMaskColor: colorTheme.appBg, messageBorderColor: colorTheme.disabled, avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( height: 32, width: 32, ), ), messageLinks: TextStyle( color: accentColor, ), ), otherMessageTheme: MessageTheme( reactionsBackgroundColor: colorTheme.disabled, reactionsBorderColor: colorTheme.barsBg, reactionsMaskColor: colorTheme.appBg, messageText: textTheme.body, createdAt: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), messageAuthor: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), replies: textTheme.footnoteBold.copyWith(color: accentColor), messageLinks: TextStyle( color: accentColor, ), messageBackgroundColor: colorTheme.barsBg, messageBorderColor: colorTheme.borders, avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( height: 32, width: 32, ), ), ), messageInputTheme: MessageInputTheme( borderRadius: BorderRadius.circular(20), sendAnimationDuration: const Duration(milliseconds: 300), actionButtonColor: colorTheme.accentPrimary, actionButtonIdleColor: colorTheme.textLowEmphasis, expandButtonColor: colorTheme.accentPrimary, sendButtonColor: colorTheme.accentPrimary, sendButtonIdleColor: colorTheme.disabled, inputBackground: colorTheme.barsBg, inputTextStyle: textTheme.body, idleBorderGradient: LinearGradient( colors: [ colorTheme.disabled, colorTheme.disabled, ], ), activeBorderGradient: LinearGradient( colors: [ colorTheme.disabled, colorTheme.disabled, ], ), ), reactionIcons: [ ReactionIcon( type: 'love', builder: (context, highlighted, size) { final theme = StreamChatTheme.of(context); return StreamSvgIcon.loveReaction( color: highlighted ? theme.colorTheme.accentPrimary : theme.primaryIconTheme.color!.withOpacity(.5), size: size, ); }, ), ReactionIcon( type: 'like', builder: (context, highlighted, size) { final theme = StreamChatTheme.of(context); return StreamSvgIcon.thumbsUpReaction( color: highlighted ? theme.colorTheme.accentPrimary : theme.primaryIconTheme.color!.withOpacity(.5), size: size, ); }, ), ReactionIcon( type: 'sad', builder: (context, highlighted, size) { final theme = StreamChatTheme.of(context); return StreamSvgIcon.thumbsDownReaction( color: highlighted ? theme.colorTheme.accentPrimary : theme.primaryIconTheme.color!.withOpacity(.5), size: size, ); }, ), ReactionIcon( type: 'haha', builder: (context, highlighted, size) { final theme = StreamChatTheme.of(context); return StreamSvgIcon.lolReaction( color: highlighted ? theme.colorTheme.accentPrimary : theme.primaryIconTheme.color!.withOpacity(.5), size: size, ); }, ), ReactionIcon( type: 'wow', builder: (context, highlighted, size) { final theme = StreamChatTheme.of(context); return StreamSvgIcon.wutReaction( color: highlighted ? theme.colorTheme.accentPrimary : theme.primaryIconTheme.color!.withOpacity(.5), size: size, ); }, ), ], galleryHeaderTheme: GalleryHeaderThemeData( closeButtonColor: colorTheme.textHighEmphasis, backgroundColor: channelTheme.channelHeaderTheme.color, iconMenuPointColor: colorTheme.textHighEmphasis, titleTextStyle: textTheme.headlineBold, subtitleTextStyle: channelPreviewTheme.subtitle, bottomSheetBarrierColor: colorTheme.overlay, ), galleryFooterTheme: GalleryFooterThemeData( backgroundColor: colorTheme.barsBg, shareIconColor: colorTheme.textHighEmphasis, titleTextStyle: textTheme.headlineBold, gridIconButtonColor: colorTheme.textHighEmphasis, bottomSheetBarrierColor: colorTheme.overlay, bottomSheetBackgroundColor: colorTheme.barsBg, bottomSheetPhotosTextStyle: textTheme.headlineBold, bottomSheetCloseIconColor: colorTheme.textHighEmphasis, ), messageListViewTheme: MessageListViewThemeData( backgroundColor: colorTheme.barsBg, ), channelListViewTheme: ChannelListViewThemeData( backgroundColor: colorTheme.appBg, ), userListViewTheme: UserListViewThemeData( backgroundColor: colorTheme.appBg, ), messageSearchListViewTheme: MessageSearchListViewThemeData( backgroundColor: colorTheme.appBg, ), ); } } /// 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), ); } } /// 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, ); } } /// Channel theme data class ChannelTheme { /// Constructor for creating [ChannelTheme] ChannelTheme({ required this.channelHeaderTheme, }); /// Theme of the [ChannelHeader] widget final ChannelHeaderTheme channelHeaderTheme; /// Creates a copy of [ChannelTheme] with specified attributes overridden. ChannelTheme copyWith({ ChannelHeaderTheme? channelHeaderTheme, }) => ChannelTheme( channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme, ); /// Merge with theme ChannelTheme merge(ChannelTheme? other) { if (other == null) return this; return copyWith( channelHeaderTheme: channelHeaderTheme.merge(other.channelHeaderTheme), ); } } /// Theme for avatar class AvatarTheme { /// Constructor for creating [AvatarTheme] AvatarTheme({ 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 with another theme AvatarTheme copyWith({ BoxConstraints? constraints, BorderRadius? borderRadius, }) => AvatarTheme( constraints: constraints ?? _constraints, borderRadius: borderRadius ?? _borderRadius, ); /// Merge with another AvatarTheme AvatarTheme merge(AvatarTheme? other) { if (other == null) return this; return copyWith( constraints: other._constraints, borderRadius: other._borderRadius, ); } } /// Class for getting message theme class MessageTheme { /// Constructor into [MessageTheme] const MessageTheme({ this.replies, this.messageText, this.messageAuthor, this.messageLinks, this.messageBackgroundColor, this.messageBorderColor, this.reactionsBackgroundColor, this.reactionsBorderColor, this.reactionsMaskColor, this.avatarTheme, this.createdAt, }); /// Text style for message text final TextStyle? messageText; /// Text style for message author final TextStyle? messageAuthor; /// Text style for message links final TextStyle? messageLinks; /// Text style for created at text final TextStyle? createdAt; /// Text style for replies final TextStyle? replies; /// 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 AvatarTheme? avatarTheme; /// Copy with a theme MessageTheme copyWith({ TextStyle? messageText, TextStyle? messageAuthor, TextStyle? messageLinks, TextStyle? createdAt, TextStyle? replies, Color? messageBackgroundColor, Color? messageBorderColor, AvatarTheme? avatarTheme, Color? reactionsBackgroundColor, Color? reactionsBorderColor, Color? reactionsMaskColor, }) => MessageTheme( messageText: messageText ?? this.messageText, messageAuthor: messageAuthor ?? this.messageAuthor, messageLinks: messageLinks ?? this.messageLinks, createdAt: createdAt ?? this.createdAt, messageBackgroundColor: messageBackgroundColor ?? this.messageBackgroundColor, messageBorderColor: messageBorderColor ?? this.messageBorderColor, avatarTheme: avatarTheme ?? this.avatarTheme, replies: replies ?? this.replies, reactionsBackgroundColor: reactionsBackgroundColor ?? this.reactionsBackgroundColor, reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, ); /// Merge with a theme MessageTheme merge(MessageTheme? other) { if (other == null) return this; return copyWith( messageText: messageText?.merge(other.messageText) ?? other.messageText, messageAuthor: messageAuthor?.merge(other.messageAuthor) ?? other.messageAuthor, messageLinks: messageLinks?.merge(other.messageLinks) ?? other.messageLinks, createdAt: createdAt?.merge(other.createdAt) ?? other.createdAt, replies: replies?.merge(other.replies) ?? other.replies, messageBackgroundColor: other.messageBackgroundColor, messageBorderColor: other.messageBorderColor, avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, reactionsBackgroundColor: other.reactionsBackgroundColor, reactionsBorderColor: other.reactionsBorderColor, reactionsMaskColor: other.reactionsMaskColor, ); } } /// Theme for channel preview class ChannelPreviewTheme { /// Constructor for creating [ChannelPreviewTheme] const ChannelPreviewTheme({ this.title, this.subtitle, this.lastMessageAt, this.avatarTheme, this.unreadCounterColor, this.indicatorIconSize, }); /// Theme for title final TextStyle? title; /// Theme for subtitle final TextStyle? subtitle; /// Theme of last message at final TextStyle? lastMessageAt; /// Avatar theme final AvatarTheme? avatarTheme; /// Unread counter color final Color? unreadCounterColor; /// Indicator icon size final double? indicatorIconSize; /// Copy with theme ChannelPreviewTheme copyWith({ TextStyle? title, TextStyle? subtitle, TextStyle? lastMessageAt, AvatarTheme? avatarTheme, Color? unreadCounterColor, double? indicatorIconSize, }) => ChannelPreviewTheme( title: title ?? this.title, subtitle: subtitle ?? this.subtitle, lastMessageAt: lastMessageAt ?? this.lastMessageAt, avatarTheme: avatarTheme ?? this.avatarTheme, unreadCounterColor: unreadCounterColor ?? this.unreadCounterColor, indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize, ); /// Merge with theme ChannelPreviewTheme merge(ChannelPreviewTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, subtitle: subtitle?.merge(other.subtitle) ?? other.subtitle, lastMessageAt: lastMessageAt?.merge(other.lastMessageAt) ?? other.lastMessageAt, avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, unreadCounterColor: other.unreadCounterColor, ); } } /// Theme for [ChannelHeader] class ChannelHeaderTheme { /// Constructor for creating a [ChannelHeaderTheme] const ChannelHeaderTheme({ this.title, this.subtitle, this.avatarTheme, this.color, }); /// Theme for title final TextStyle? title; /// Theme for subtitle final TextStyle? subtitle; /// Theme for avatar final AvatarTheme? avatarTheme; /// Color for [ChannelHeaderTheme] final Color? color; /// Copy with theme ChannelHeaderTheme copyWith({ TextStyle? title, TextStyle? subtitle, AvatarTheme? avatarTheme, Color? color, }) => ChannelHeaderTheme( title: title ?? this.title, subtitle: subtitle ?? this.subtitle, avatarTheme: avatarTheme ?? this.avatarTheme, color: color ?? this.color, ); /// Merge with other [ChannelHeaderTheme] ChannelHeaderTheme merge(ChannelHeaderTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, subtitle: subtitle?.merge(other.subtitle) ?? other.subtitle, avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, color: other.color, ); } } /// Theme dedicated to the [ChannelListHeader] class ChannelListHeaderTheme { /// Returns a new [ChannelListHeaderTheme] const ChannelListHeaderTheme({ this.title, this.avatarTheme, this.color, }); /// Style of the title text final TextStyle? title; /// Theme dedicated to the userAvatar final AvatarTheme? avatarTheme; /// Background color of the appbar final Color? color; /// Returns a new [ChannelListHeaderTheme] replacing some of its properties ChannelListHeaderTheme copyWith({ TextStyle? title, AvatarTheme? avatarTheme, Color? color, }) => ChannelListHeaderTheme( title: title ?? this.title, avatarTheme: avatarTheme ?? this.avatarTheme, color: color ?? this.color, ); /// Merges [this] [ChannelListHeaderTheme] with the [other] ChannelListHeaderTheme merge(ChannelListHeaderTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, color: other.color, ); } } /// Defines the theme dedicated to the [MessageInput] widget class MessageInputTheme { /// Returns a new [MessageInputTheme] const MessageInputTheme({ this.sendAnimationDuration, this.actionButtonColor, this.sendButtonColor, this.actionButtonIdleColor, this.sendButtonIdleColor, this.inputBackground, 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? inputBackground; /// 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 [MessageInputTheme] replacing some of its properties MessageInputTheme copyWith({ Duration? sendAnimationDuration, Color? inputBackground, Color? actionButtonColor, Color? sendButtonColor, Color? actionButtonIdleColor, Color? sendButtonIdleColor, Color? expandButtonColor, TextStyle? inputTextStyle, InputDecoration? inputDecoration, Gradient? activeBorderGradient, Gradient? idleBorderGradient, BorderRadius? borderRadius, }) => MessageInputTheme( sendAnimationDuration: sendAnimationDuration ?? this.sendAnimationDuration, inputBackground: inputBackground ?? this.inputBackground, 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, ); /// Merges [this] [MessageInputTheme] with the [other] MessageInputTheme merge(MessageInputTheme? other) { if (other == null) return this; return copyWith( sendAnimationDuration: other.sendAnimationDuration, inputBackground: other.inputBackground, 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, ); } } /// 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, ); } /// Overrides the default style of [GalleryHeader] descendants. /// /// See also: /// /// * [GalleryHeaderThemeData], which is used to configure this theme. class GalleryHeaderTheme extends InheritedTheme { /// Creates an [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(); 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.subtitle]. 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)); } } /// 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(); 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)); } } /// 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(); 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)); } } /// 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(); 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)); } } /// 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(); 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)); } } /// 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(); 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)); } }