diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 520b6cba6..11134275a 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -2,6 +2,7 @@ ✅ Added +- Added `bottom` and `bottomOpacity` to the `StreamChannelHeader` widget. - Added `StreamChat.maybeOf()` method for safe context access in async operations. 🐞 Fixed diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel/channel_header.dart index b97af4922..20dd2d5ae 100644 --- a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel/channel_header.dart @@ -66,9 +66,11 @@ class StreamChannelHeader extends StatelessWidget this.centerTitle, this.leading, this.actions, + this.bottom, this.backgroundColor, this.elevation = 1, - }) : preferredSize = const Size.fromHeight(kToolbarHeight); + this.bottomOpacity = 1, + }); /// Whether to show the leading back button /// @@ -106,6 +108,9 @@ class StreamChannelHeader extends StatelessWidget /// Leading widget final Widget? leading; + /// The bottom widget + final PreferredSizeWidget? bottom; + /// {@macro flutter.material.appbar.actions} /// /// The [StreamChannelAvatar] is shown by default @@ -117,8 +122,14 @@ class StreamChannelHeader extends StatelessWidget /// The elevation for this [StreamChannelHeader]. final double elevation; + /// The opacity of the bottom widget. + final double bottomOpacity; + @override - final Size preferredSize; + Size get preferredSize { + final bottomHeight = bottom?.preferredSize.height ?? 0; + return Size.fromHeight(kToolbarHeight + bottomHeight); + } @override Widget build(BuildContext context) { @@ -169,6 +180,8 @@ class StreamChannelHeader extends StatelessWidget : SystemUiOverlayStyle.dark, elevation: elevation, leading: leadingWidget, + bottom: bottom, + bottomOpacity: bottomOpacity, backgroundColor: backgroundColor ?? channelHeaderTheme.color, actions: actions ?? [ diff --git a/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart b/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart index 9200e5e67..0e1bd7cbd 100644 --- a/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart +++ b/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart @@ -1,3 +1,4 @@ +import 'package:alchemist/alchemist.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -476,4 +477,73 @@ void main() { expect(titleTapped, true); }, ); + + goldenTest( + 'golden test for StreamChannelHeader with bottom widget', + fileName: 'channel_header_bottom_widget', + constraints: const BoxConstraints.tightFor(width: 300, height: 60), + builder: () { + final client = MockClient(); + final clientState = MockClientState(); + final channel = MockChannel(); + final channelState = MockChannelState(); + final user = OwnUser(id: 'user-id'); + final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); + + when(() => client.state).thenReturn(clientState); + when(() => clientState.currentUser).thenReturn(user); + when(() => clientState.currentUserStream) + .thenAnswer((_) => Stream.value(user)); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); + when(() => channel.name).thenReturn('test'); + when(() => channel.imageStream) + .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); + when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); + when(() => channelState.unreadCount).thenReturn(1); + when(() => client.wsConnectionStatusStream) + .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer( + (i) => Stream.value([ + Member( + userId: 'user-id', + user: User(id: 'user-id'), + ) + ]), + ); + when(() => channelState.members).thenReturn([ + Member( + userId: 'user-id', + user: User(id: 'user-id'), + ), + ]); + + return MaterialAppWrapper( + home: StreamChat( + client: client, + connectivityStream: Stream.value([ConnectivityResult.wifi]), + child: StreamChannel( + channel: channel, + child: const Scaffold( + body: StreamChannelHeader( + bottom: PreferredSize( + preferredSize: Size.fromHeight(1), + child: Divider(height: 1, color: Colors.red), + ), + ), + ), + ), + ), + ); + }, + ); } diff --git a/packages/stream_chat_flutter/test/src/channel/goldens/ci/channel_header_bottom_widget.png b/packages/stream_chat_flutter/test/src/channel/goldens/ci/channel_header_bottom_widget.png new file mode 100644 index 000000000..6c3aa5467 Binary files /dev/null and b/packages/stream_chat_flutter/test/src/channel/goldens/ci/channel_header_bottom_widget.png differ