From 52f51e5898064b08d4bfecb06afeeb54de011bf3 Mon Sep 17 00:00:00 2001 From: Drew Ballance Date: Mon, 21 Jul 2025 08:56:44 -0500 Subject: [PATCH 1/5] feat(ui): add bottom and bottomOpacity to StreamChannelHeader --- .../lib/src/channel/channel_header.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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 b97af49222..20dd2d5ae3 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 ?? [ From 8ac0c672dd4f548e5f47a89ac23aa41672943018 Mon Sep 17 00:00:00 2001 From: Drew Ballance Date: Thu, 24 Jul 2025 12:24:32 -0500 Subject: [PATCH 2/5] chore: update changelogs --- packages/stream_chat/CHANGELOG.md | 4 ++++ packages/stream_chat_flutter/CHANGELOG.md | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 298468ba21..34cfd4e5d3 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -4,6 +4,10 @@ - Fixed `WebSocket` race condition where reconnection could access null user during disconnect. +✅ Added + +- Added `bottom` and `bottomOpacity` to the `StreamChannelHeader` widget. + ## 9.14.0 🐞 Fixed diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index e7eecf17b6..b4c148c69c 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## Upcoming + +✅ Added + +- Added `bottom` and `bottomOpacity` to the `StreamChannelHeader` widget. + ## 9.14.0 🐞 Fixed From dae7c8b390f98b5c858c2174fb26c2c9cdd68007 Mon Sep 17 00:00:00 2001 From: Drew Ballance Date: Thu, 24 Jul 2025 15:12:49 -0500 Subject: [PATCH 3/5] fix: remove stream_chat changelog changes Remove --- packages/stream_chat/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 2cc93a6e6c..9af83846b7 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,9 +1,5 @@ ## Upcoming -✅ Added - -- Added `avgResponseTime` field to the `User` model to track average response time in seconds. - 🐞 Fixed - Fixed `WebSocket` race condition where reconnection could access null user during disconnect. From 529572be1e6a72035022f873b61e83a9ffff922b Mon Sep 17 00:00:00 2001 From: Drew Ballance Date: Thu, 24 Jul 2025 15:13:39 -0500 Subject: [PATCH 4/5] fix: changelog --- packages/stream_chat/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 9af83846b7..93ba63b677 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,15 +1,15 @@ ## Upcoming +✅ Added + +- Added `avgResponseTime` field to the `User` model to track average response time in seconds. + 🐞 Fixed - Fixed `WebSocket` race condition where reconnection could access null user during disconnect. - Fixed draft message persistence issues where removed drafts were not properly deleted from the database. -✅ Added - -- Added `bottom` and `bottomOpacity` to the `StreamChannelHeader` widget. - ## 9.14.0 🐞 Fixed From 8de5adba049f9476d99caad99d511cd92e648bed Mon Sep 17 00:00:00 2001 From: Drew Ballance Date: Thu, 24 Jul 2025 22:01:11 -0500 Subject: [PATCH 5/5] test: add golden test --- .../test/src/channel/channel_header_test.dart | 70 ++++++++++++++++++ .../ci/channel_header_bottom_widget.png | Bin 0 -> 1555 bytes 2 files changed, 70 insertions(+) create mode 100644 packages/stream_chat_flutter/test/src/channel/goldens/ci/channel_header_bottom_widget.png 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 9200e5e67d..0e1bd7cbd4 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 0000000000000000000000000000000000000000..6c3aa5467e90155679e2984ba329437ee24a6d66 GIT binary patch literal 1555 zcmV+u2JHEXP)Px#1am@3R0s$N2z&@+hyVZuy-7qtRCt{2-D_+VRT#$ce_N(E4F#5nxP}5tL|j9GB_giT%jp6{Bzc|u9cmB5fkrs`Gc^4I$4(mC z3I`dVA%kG_5V-e7$myHddeSs;R7C#i#A#Sv13M4Gp`f|8qw-i5 z{vFt~724aQYm=T1vmS;8^PrdR7l_2;*RwEn6?_x0XS^{!*zpVuxF+^KoC&NWN8!a+ z;CKt{`_f$7J10PK5xi7se2a)_=P$zeS7Dz`VRgw}Lt#%P^v;N_y)*N$zSv`gRjiI2 zHTn>dm^}S~{b2?0)lbIQv9dp~4t) zoUY=sb$g9DU}o4KSeco&4=Ez>@^-iw)F&BGLHq1T=)4uHs4!A)~fv*&0~EV2}sats&fD!EtF;7EGOD zMOh*}-nMU@t{^fqwb>R}!C;Wm(o+0Qjl6m8OcX7;@z^}@=2=miNRPKaiKi?cG;#0D zmcY6cR!vP!_DjpO=Lu@9y{WMzpWJzA*M=?WqO`|)@@ zuzodcco)V_H23iH>>2RrT;pdEiAu=i^Z`4WsV(6TKbMr0(9qC8ad9zqb#>(C=62m7 z$(NT&OG~qLJdytr@4soE`S8~T%gV7-b{3po6WzWEKf>zOxeQFiU-*dOTH{J2!TR)0 z-oP>=;pbH~H8uErJ_3ONC&Z6mmp$^MH7wn z)RSSwQ99>_EtzDljq@*36%pG!zZF*Oa=HpbWjQRLZmx|2OC*J~wn6?9*ksdD->8Fsb}UL#_5<-D$8NU?Xk7hDWOOTYVUxuHi3LFs9*^+^~!z`ikW$brp|#~xNFutZYVb^YMT3RqZ{__5}X zhrmkX8d^MbN+=>!*TPfpTmRm|jC81;V+58J6j&kxov++_?Gt$KYY4}=*kA7qcw`Jb zS8nYiK?*Dp=_2?WEZ+rh*PEXe-}fq5aJMmo-J4^5Z3-+AiD+K{K0E*i8sXGAIQ0jd zK4)y}f3@++=?i0vq2e|eJtVR9D6mAtHFTFyL|j9GB_gh&z!DMHP+*COYbdZp#5ELH zBH|hfED>=H1(t}oh5}1OTtk5+BCe5kcEV^qnj<2v@Hc9~vKb^8!Sw(D002ovPDHLk FV1n#Z