Skip to content

Commit 96988eb

Browse files
authored
feat(ui): add bottom and bottomOpacity to ChannelHeader (#2318)
1 parent b82ce14 commit 96988eb

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

packages/stream_chat_flutter/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
✅ Added
44

5+
- Added `bottom` and `bottomOpacity` to the `StreamChannelHeader` widget.
56
- Added `StreamChat.maybeOf()` method for safe context access in async operations.
67

78
🐞 Fixed

packages/stream_chat_flutter/lib/src/channel/channel_header.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ class StreamChannelHeader extends StatelessWidget
6666
this.centerTitle,
6767
this.leading,
6868
this.actions,
69+
this.bottom,
6970
this.backgroundColor,
7071
this.elevation = 1,
71-
}) : preferredSize = const Size.fromHeight(kToolbarHeight);
72+
this.bottomOpacity = 1,
73+
});
7274

7375
/// Whether to show the leading back button
7476
///
@@ -106,6 +108,9 @@ class StreamChannelHeader extends StatelessWidget
106108
/// Leading widget
107109
final Widget? leading;
108110

111+
/// The bottom widget
112+
final PreferredSizeWidget? bottom;
113+
109114
/// {@macro flutter.material.appbar.actions}
110115
///
111116
/// The [StreamChannelAvatar] is shown by default
@@ -117,8 +122,14 @@ class StreamChannelHeader extends StatelessWidget
117122
/// The elevation for this [StreamChannelHeader].
118123
final double elevation;
119124

125+
/// The opacity of the bottom widget.
126+
final double bottomOpacity;
127+
120128
@override
121-
final Size preferredSize;
129+
Size get preferredSize {
130+
final bottomHeight = bottom?.preferredSize.height ?? 0;
131+
return Size.fromHeight(kToolbarHeight + bottomHeight);
132+
}
122133

123134
@override
124135
Widget build(BuildContext context) {
@@ -169,6 +180,8 @@ class StreamChannelHeader extends StatelessWidget
169180
: SystemUiOverlayStyle.dark,
170181
elevation: elevation,
171182
leading: leadingWidget,
183+
bottom: bottom,
184+
bottomOpacity: bottomOpacity,
172185
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
173186
actions: actions ??
174187
<Widget>[

packages/stream_chat_flutter/test/src/channel/channel_header_test.dart

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:alchemist/alchemist.dart';
12
import 'package:flutter/material.dart';
23
import 'package:flutter_test/flutter_test.dart';
34
import 'package:mocktail/mocktail.dart';
@@ -476,4 +477,73 @@ void main() {
476477
expect(titleTapped, true);
477478
},
478479
);
480+
481+
goldenTest(
482+
'golden test for StreamChannelHeader with bottom widget',
483+
fileName: 'channel_header_bottom_widget',
484+
constraints: const BoxConstraints.tightFor(width: 300, height: 60),
485+
builder: () {
486+
final client = MockClient();
487+
final clientState = MockClientState();
488+
final channel = MockChannel();
489+
final channelState = MockChannelState();
490+
final user = OwnUser(id: 'user-id');
491+
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
492+
493+
when(() => client.state).thenReturn(clientState);
494+
when(() => clientState.currentUser).thenReturn(user);
495+
when(() => clientState.currentUserStream)
496+
.thenAnswer((_) => Stream.value(user));
497+
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
498+
when(() => channel.state).thenReturn(channelState);
499+
when(() => channel.client).thenReturn(client);
500+
when(() => channel.isMuted).thenReturn(false);
501+
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
502+
when(() => channel.nameStream).thenAnswer((_) => Stream.value('test'));
503+
when(() => channel.name).thenReturn('test');
504+
when(() => channel.imageStream)
505+
.thenAnswer((i) => Stream.value('https://bit.ly/321RmWb'));
506+
when(() => channel.image).thenReturn('https://bit.ly/321RmWb');
507+
when(() => channelState.unreadCount).thenReturn(1);
508+
when(() => client.wsConnectionStatusStream)
509+
.thenAnswer((_) => Stream.value(ConnectionStatus.connected));
510+
when(() => channelState.unreadCountStream)
511+
.thenAnswer((i) => Stream.value(1));
512+
when(() => clientState.totalUnreadCount).thenAnswer((i) => 1);
513+
when(() => clientState.totalUnreadCountStream)
514+
.thenAnswer((i) => Stream.value(1));
515+
when(() => channelState.membersStream).thenAnswer(
516+
(i) => Stream.value([
517+
Member(
518+
userId: 'user-id',
519+
user: User(id: 'user-id'),
520+
)
521+
]),
522+
);
523+
when(() => channelState.members).thenReturn([
524+
Member(
525+
userId: 'user-id',
526+
user: User(id: 'user-id'),
527+
),
528+
]);
529+
530+
return MaterialAppWrapper(
531+
home: StreamChat(
532+
client: client,
533+
connectivityStream: Stream.value([ConnectivityResult.wifi]),
534+
child: StreamChannel(
535+
channel: channel,
536+
child: const Scaffold(
537+
body: StreamChannelHeader(
538+
bottom: PreferredSize(
539+
preferredSize: Size.fromHeight(1),
540+
child: Divider(height: 1, color: Colors.red),
541+
),
542+
),
543+
),
544+
),
545+
),
546+
);
547+
},
548+
);
479549
}
1.52 KB
Loading

0 commit comments

Comments
 (0)