From bcabbf12db5bb532116cd4d231d5284a8a851c16 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Mon, 26 Jul 2021 14:46:14 -0400 Subject: [PATCH] chore: refactor most theme related classes into a /theme folder stream_chat_theme.dart remains in /src because I feel it will cause too much of a mess if moved. --- .../lib/src/attachment/attachment_title.dart | 1 + .../lib/src/attachment/image_attachment.dart | 1 + .../lib/src/attachment/video_attachment.dart | 1 + .../lib/src/channel_list_view.dart | 1 + .../lib/src/deleted_message.dart | 1 + .../lib/src/gallery_footer.dart | 1 + .../lib/src/gallery_header.dart | 1 + .../lib/src/image_group.dart | 1 + .../lib/src/message_actions_modal.dart | 1 + .../lib/src/message_list_view.dart | 1 + .../lib/src/message_reactions_modal.dart | 1 + .../lib/src/message_search_list_view.dart | 1 + .../lib/src/message_text.dart | 1 + .../lib/src/message_widget.dart | 1 + .../lib/src/quoted_message_widget.dart | 1 + .../lib/src/stream_chat_theme.dart | 1704 +---------------- .../lib/src/theme/avatar_theme.dart | 44 + .../lib/src/theme/channel_header_theme.dart | 50 + .../src/theme/channel_list_header_theme.dart | 43 + .../src/theme/channel_list_view_theme.dart | 111 ++ .../lib/src/theme/channel_preview_theme.dart | 64 + .../lib/src/theme/channel_theme.dart | 28 + .../lib/src/theme/color_theme.dart | 286 +++ .../lib/src/theme/gallery_footer_theme.dart | 217 +++ .../lib/src/theme/gallery_header_theme.dart | 179 ++ .../lib/src/theme/message_input_theme.dart | 110 ++ .../src/theme/message_list_view_theme.dart | 111 ++ .../theme/message_search_list_view_theme.dart | 112 ++ .../lib/src/theme/message_theme.dart | 103 + .../lib/src/theme/text_theme.dart | 163 ++ .../lib/src/theme/themes.dart | 15 + .../lib/src/theme/user_list_view_theme.dart | 111 ++ .../lib/src/user_list_view.dart | 1 + .../lib/stream_chat_flutter.dart | 1 + 34 files changed, 1766 insertions(+), 1702 deletions(-) create mode 100644 packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/channel_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/color_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/message_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/text_theme.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/themes.dart create mode 100644 packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart index b068a8f9..d0f45aad 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart index 20174567..f912f282 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart index b743cda1..b976532a 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 32dde4b6..ba086f41 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/deleted_message.dart b/packages/stream_chat_flutter/lib/src/deleted_message.dart index 011b0c72..a973deb7 100644 --- a/packages/stream_chat_flutter/lib/src/deleted_message.dart +++ b/packages/stream_chat_flutter/lib/src/deleted_message.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.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 { diff --git a/packages/stream_chat_flutter/lib/src/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery_footer.dart index c26c8dbc..53b20675 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_footer.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery_header.dart index 6c4ecfee..ad2969cb 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_header.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_header.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 diff --git a/packages/stream_chat_flutter/lib/src/image_group.dart b/packages/stream_chat_flutter/lib/src/image_group.dart index 4647fe57..ec50f294 100644 --- a/packages/stream_chat_flutter/lib/src/image_group.dart +++ b/packages/stream_chat_flutter/lib/src/image_group.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index 128918ad..efaf0ded 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 635e86c1..e7279579 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart index fadef6ff..669af376 100644 --- a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index 13f5ea55..59e09507 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index 6d006085..c11dc2fc 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.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'; diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index e73f3a6d..133b5dc7 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart index 22ca4d4c..5cf53a65 100644 --- a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 9d2df266..4336325b 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -1,11 +1,10 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/channel_header.dart'; +import 'package:flutter/material.dart' hide TextTheme; 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/src/theme/themes.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -492,1702 +491,3 @@ class StreamChatThemeData { } } -/// 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)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart b/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart new file mode 100644 index 00000000..f3e68597 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +/// 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, + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart new file mode 100644 index 00000000..2072b3c5 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; + +/// 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, + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart new file mode 100644 index 00000000..ca36b9bd --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; + +/// 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, + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_list_view_theme.dart new file mode 100644 index 00000000..d310e2bc --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/channel_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 [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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart new file mode 100644 index 00000000..4643bb8d --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; + +/// 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, + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_theme.dart new file mode 100644 index 00000000..bf752f94 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/channel_theme.dart @@ -0,0 +1,28 @@ +import 'package:stream_chat_flutter/src/theme/channel_header_theme.dart'; + +/// 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), + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/color_theme.dart b/packages/stream_chat_flutter/lib/src/theme/color_theme.dart new file mode 100644 index 00000000..d021c6cf --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/color_theme.dart @@ -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, + ); +} diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart new file mode 100644 index 00000000..bdf273e7 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart @@ -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(); + 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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart new file mode 100644 index 00000000..9b5faa83 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart @@ -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 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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart new file mode 100644 index 00000000..b8889e6b --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; + +/// 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, + ); + } +} \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart new file mode 100644 index 00000000..49bd05f9 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/message_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 [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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart new file mode 100644 index 00000000..670e05e7 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/message_search_list_view_theme.dart @@ -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(); + 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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart new file mode 100644 index 00000000..87543cb1 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; + +/// 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, + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/text_theme.dart b/packages/stream_chat_flutter/lib/src/theme/text_theme.dart new file mode 100644 index 00000000..ec70cf83 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/text_theme.dart @@ -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), + ); + } +} diff --git a/packages/stream_chat_flutter/lib/src/theme/themes.dart b/packages/stream_chat_flutter/lib/src/theme/themes.dart new file mode 100644 index 00000000..4236f874 --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/themes.dart @@ -0,0 +1,15 @@ +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 'channel_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'; \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/user_list_view_theme.dart new file mode 100644 index 00000000..0d99ccbd --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/theme/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(); + 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)); + } +} diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index 39d4453d..0374ffc8 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -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'; diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 12049fc6..0a65d672 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -33,6 +33,7 @@ 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';