diff --git a/packages/stream_chat_flutter/lib/src/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel_list_header.dart index ae017090..72145fe4 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_header.dart @@ -45,7 +45,7 @@ typedef TitleBuilder = Widget Function( /// if you don't have it in the widget tree. /// /// The widget components render the ui based on the first ancestor of type -/// [StreamChatTheme] and on its [ChannelListHeaderTheme] property. +/// [StreamChatTheme] and on its [ChannelListHeaderThemeData] property. /// Modify it to change the widget appearance. class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { /// Instantiates a ChannelListHeader @@ -115,6 +115,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { } final chatThemeData = StreamChatTheme.of(context); + final channelListHeaderThemeData = ChannelListHeaderTheme.of(context); return InfoTile( // ignore: avoid_bool_literals_in_conditional_expressions showMessage: showConnectionStateTile ? showStatus : false, @@ -123,7 +124,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { textTheme: Theme.of(context).textTheme, brightness: Theme.of(context).brightness, elevation: 1, - backgroundColor: chatThemeData.channelListHeaderTheme.color, + backgroundColor: channelListHeaderThemeData.color, centerTitle: true, leading: leading ?? Center( @@ -138,10 +139,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { } Scaffold.of(context).openDrawer(); }, - borderRadius: chatThemeData - .channelListHeaderTheme.avatarTheme?.borderRadius, - constraints: chatThemeData - .channelListHeaderTheme.avatarTheme?.constraints, + borderRadius: channelListHeaderThemeData + .avatarTheme?.borderRadius, + constraints: channelListHeaderThemeData + .avatarTheme?.constraints, ) : const Offstage(), ), @@ -227,10 +228,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { const SizedBox(width: 10), Text( 'Searching for Network', - style: StreamChatTheme.of(context) - .channelListHeaderTheme - .title - ?.copyWith( + style: ChannelListHeaderTheme.of(context).titleStyle?.copyWith( fontSize: 16, fontWeight: FontWeight.bold, ), @@ -248,7 +246,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { children: [ Text( 'Offline...', - style: chatThemeData.channelListHeaderTheme.title?.copyWith( + style: chatThemeData.channelListHeaderTheme.titleStyle?.copyWith( fontSize: 16, fontWeight: FontWeight.bold, ), @@ -259,7 +257,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { ..openConnection(), child: Text( 'Try Again', - style: chatThemeData.channelListHeaderTheme.title?.copyWith( + style: chatThemeData.channelListHeaderTheme.titleStyle?.copyWith( fontSize: 16, fontWeight: FontWeight.bold, color: chatThemeData.colorTheme.accentPrimary, 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 f82b916c..5fe56dbc 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -47,7 +47,7 @@ class StreamChatThemeData { Brightness? brightness, TextTheme? textTheme, ColorTheme? colorTheme, - ChannelListHeaderTheme? channelListHeaderTheme, + ChannelListHeaderThemeData? channelListHeaderTheme, ChannelPreviewThemeData? channelPreviewTheme, ChannelHeaderThemeData? channelHeaderTheme, MessageThemeData? otherMessageTheme, @@ -145,7 +145,7 @@ class StreamChatThemeData { final ChannelPreviewThemeData channelPreviewTheme; /// Theme of the [ChannelListHeader] - final ChannelListHeaderTheme channelListHeaderTheme; + final ChannelListHeaderThemeData channelListHeaderTheme; /// Theme of the chat widgets dedicated to a channel header final ChannelHeaderThemeData channelHeaderTheme; @@ -200,7 +200,7 @@ class StreamChatThemeData { MessageInputThemeData? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, IconThemeData? primaryIconTheme, - ChannelListHeaderTheme? channelListHeaderTheme, + ChannelListHeaderThemeData? channelListHeaderTheme, List? reactionIcons, GalleryHeaderThemeData? galleryHeaderTheme, GalleryFooterThemeData? galleryFooterTheme, @@ -312,7 +312,7 @@ class StreamChatThemeData { ), ), channelPreviewTheme: channelPreviewTheme, - channelListHeaderTheme: ChannelListHeaderTheme( + channelListHeaderTheme: ChannelListHeaderThemeData( avatarTheme: AvatarThemeData( borderRadius: BorderRadius.circular(20), constraints: const BoxConstraints.tightFor( @@ -321,7 +321,7 @@ class StreamChatThemeData { ), ), color: colorTheme.barsBg, - title: textTheme.headlineBold, + titleStyle: textTheme.headlineBold, ), channelHeaderTheme: channelHeaderTheme, ownMessageTheme: MessageThemeData( 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 index 339361b9..3846232e 100644 --- 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 @@ -1,17 +1,63 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -/// Theme dedicated to the [ChannelListHeader] -class ChannelListHeaderTheme { - /// Returns a new [ChannelListHeaderTheme] +/// Overrides the default style of [ChannelListHeader] descendants. +/// +/// See also: +/// +/// * [ChannelListHeaderThemeData], which is used to configure this theme. +class ChannelListHeaderTheme extends InheritedTheme { + /// Creates a [ChannelListHeaderTheme]. + /// + /// The [data] parameter must not be null. const ChannelListHeaderTheme({ - this.title, + Key? key, + required this.data, + required Widget child, + }) : super(key: key, child: child); + + /// The configuration of this theme. + final ChannelListHeaderThemeData data; + + /// The closest instance of this class that encloses the given context. + /// + /// If there is no enclosing [ChannelListHeaderTheme] widget, then + /// [StreamChatThemeData.channelListHeaderTheme] is used. + /// + /// Typical usage is as follows: + /// + /// ```dart + /// final theme = ChannelListHeaderTheme.of(context); + /// ``` + static ChannelListHeaderThemeData of(BuildContext context) { + final channelListHeaderTheme = + context.dependOnInheritedWidgetOfExactType(); + return channelListHeaderTheme?.data ?? + StreamChatTheme.of(context).channelListHeaderTheme; + } + + @override + Widget wrap(BuildContext context, Widget child) => + ChannelListHeaderTheme(data: data, child: child); + + @override + bool updateShouldNotify(ChannelListHeaderTheme oldWidget) => + data != oldWidget.data; +} + +/// Theme dedicated to the [ChannelListHeader] +class ChannelListHeaderThemeData with Diagnosticable { + /// Returns a new [ChannelListHeaderThemeData] + const ChannelListHeaderThemeData({ + this.titleStyle, this.avatarTheme, this.color, }); /// Style of the title text - final TextStyle? title; + final TextStyle? titleStyle; /// Theme dedicated to the userAvatar final AvatarThemeData? avatarTheme; @@ -19,25 +65,61 @@ class ChannelListHeaderTheme { /// Background color of the appbar final Color? color; - /// Returns a new [ChannelListHeaderTheme] replacing some of its properties - ChannelListHeaderTheme copyWith({ - TextStyle? title, + /// Returns a new [ChannelListHeaderThemeData] replacing some of its + /// properties + ChannelListHeaderThemeData copyWith({ + TextStyle? titleStyle, AvatarThemeData? avatarTheme, Color? color, }) => - ChannelListHeaderTheme( - title: title ?? this.title, + ChannelListHeaderThemeData( + titleStyle: titleStyle ?? this.titleStyle, avatarTheme: avatarTheme ?? this.avatarTheme, color: color ?? this.color, ); - /// Merges [this] [ChannelListHeaderTheme] with the [other] - ChannelListHeaderTheme merge(ChannelListHeaderTheme? other) { + /// Linearly interpolate from one [ChannelListHeaderThemeData] to another. + ChannelListHeaderThemeData lerp( + ChannelListHeaderThemeData a, + ChannelListHeaderThemeData b, + double t, + ) => + ChannelListHeaderThemeData( + avatarTheme: + const AvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), + color: Color.lerp(a.color, b.color, t), + titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t), + ); + + /// Merges [this] [ChannelListHeaderThemeData] with the [other] + ChannelListHeaderThemeData merge(ChannelListHeaderThemeData? other) { if (other == null) return this; return copyWith( - title: title?.merge(other.title) ?? other.title, + titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle, avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, color: other.color, ); } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ChannelListHeaderThemeData && + runtimeType == other.runtimeType && + titleStyle == other.titleStyle && + avatarTheme == other.avatarTheme && + color == other.color; + + @override + int get hashCode => + titleStyle.hashCode ^ avatarTheme.hashCode ^ color.hashCode; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('titleStyle', titleStyle)) + ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) + ..add(ColorProperty('color', color)); + } }