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 08d0de0e..d632c10d 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -62,6 +62,7 @@ class StreamChatThemeData { GalleryFooterThemeData? imageFooterTheme, MessageListViewThemeData? messageListViewTheme, ChannelListViewThemeData? channelListViewTheme, + UserListViewThemeData? userListViewTheme, }) { brightness ??= colorTheme?.brightness ?? Brightness.light; final isDark = brightness == Brightness.dark; @@ -87,6 +88,7 @@ class StreamChatThemeData { galleryFooterTheme: imageFooterTheme, messageListViewTheme: messageListViewTheme, channelListViewTheme: channelListViewTheme, + userListViewTheme: userListViewTheme, ); return defaultData.merge(customizedData); @@ -117,6 +119,7 @@ class StreamChatThemeData { required this.galleryFooterTheme, required this.messageListViewTheme, required this.channelListViewTheme, + required this.userListViewTheme, }); /// Create a theme from a Material [Theme] @@ -178,6 +181,9 @@ class StreamChatThemeData { /// Theme configuration for the [ChannelListView] widget. final ChannelListViewThemeData channelListViewTheme; + /// Theme configuration for the [UserListView] widget. + final UserListViewThemeData userListViewTheme; + /// Creates a copy of [StreamChatThemeData] with specified attributes /// overridden. StreamChatThemeData copyWith({ @@ -196,6 +202,7 @@ class StreamChatThemeData { GalleryFooterThemeData? galleryFooterTheme, MessageListViewThemeData? messageListViewTheme, ChannelListViewThemeData? channelListViewTheme, + UserListViewThemeData? userListViewTheme, }) => StreamChatThemeData.raw( channelListHeaderTheme: @@ -215,6 +222,7 @@ class StreamChatThemeData { galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, channelListViewTheme: channelListViewTheme ?? this.channelListViewTheme, + userListViewTheme: userListViewTheme ?? this.userListViewTheme, ); /// Merge themes @@ -239,6 +247,7 @@ class StreamChatThemeData { messageListViewTheme.merge(other.messageListViewTheme), channelListViewTheme: channelListViewTheme.merge(other.channelListViewTheme), + userListViewTheme: userListViewTheme.merge(other.userListViewTheme), ); } @@ -464,6 +473,9 @@ class StreamChatThemeData { channelListViewTheme: ChannelListViewThemeData( backgroundColor: colorTheme.appBg, ), + userListViewTheme: UserListViewThemeData( + backgroundColor: colorTheme.appBg, + ), ); } } @@ -1950,3 +1962,111 @@ class ChannelListViewThemeData with Diagnosticable { 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)); + } +} 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 923e0334..0be283bd 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -160,33 +160,36 @@ class _UserListViewState extends State @override Widget build(BuildContext context) { - final child = UserListCore( - errorBuilder: widget.errorBuilder ?? - (BuildContext context, Object err) => _buildError(err), - emptyBuilder: widget.emptyBuilder ?? (context) => _buildEmpty(), - loadingBuilder: widget.loadingBuilder ?? - (context) => LayoutBuilder( - builder: (context, viewportConstraints) => - SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: const Center( - child: CircularProgressIndicator(), + final child = ColoredBox( + color: UserListViewTheme.of(context).backgroundColor!, + child: UserListCore( + errorBuilder: widget.errorBuilder ?? + (BuildContext context, Object err) => _buildError(err), + emptyBuilder: widget.emptyBuilder ?? (context) => _buildEmpty(), + loadingBuilder: widget.loadingBuilder ?? + (context) => LayoutBuilder( + builder: (context, viewportConstraints) => + SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: const Center( + child: CircularProgressIndicator(), + ), ), ), - ), - ), - listBuilder: - widget.listBuilder ?? (context, list) => _buildListView(list), - pagination: widget.pagination, - sort: widget.sort, - filter: widget.filter, - presence: widget.presence, - groupAlphabetically: widget.groupAlphabetically, - userListController: _userListController, + ), + listBuilder: + widget.listBuilder ?? (context, list) => _buildListView(list), + pagination: widget.pagination, + sort: widget.sort, + filter: widget.filter, + presence: widget.presence, + groupAlphabetically: widget.groupAlphabetically, + userListController: _userListController, + ), ); if (!widget.pullToRefresh) {