Overlays and bottom sheets
Some of the SDK's UI opens on top of the screen: the message menu on a long press, the image gallery, the attachment picker, the list of who reacted, the poll screens. Each opens in one of these ways:
| Kind | What opens this way | Drawn by |
|---|---|---|
| The message overlay | the message menu | OverlayProvider, above your screens |
| The gallery overlay | the image gallery | OverlayProvider, above your screens |
| Bottom sheets | who reacted, more reactions, commands, the gallery's grid, a channel's options, a member's actions, the channel image picker | BottomSheetModal, in a native modal window |
| The attachment picker | the photo grid and the picker's sources | Channel and Thread, below the composer |
| Full-screen modals | polls' results, options and comments, poll creation, channel details' member list, media, files, pinned messages, editing | a native modal window |
The two overlays need OverlayProvider above the screen. The rest need nothing more than the Channel, Thread or ChannelDetails they belong to.
OverlayProvider
OverlayProvider goes around your navigation, above Chat, as in the Quick start. It renders your app, then on top of it a layer for the message menu and, while it's open, the image gallery. Its layer covers the whole window, so it draws above your navigation's headers and tab bars.
| Prop | What it does |
|---|---|
value.style |
the theme of the overlays and everything else below OverlayProvider. See Theming |
i18nInstance |
the Streami18n instance for the overlays' text. Pass the one you give Chat |
accessibility |
the opt-in accessibility layer. See Accessibility |
autoPlayVideo |
whether a video in the gallery starts playing when it opens |
giphyVersion |
which rendition of a Giphy the gallery shows |
numberOfImageGalleryGridColumns |
the number of columns of the gallery's grid |
OverlayProvider draws in the window it's mounted in. A screen in another native window, such as a React Native Modal or a native modal presentation on iOS, needs an OverlayProvider of its own inside it, or the message menu opens behind the modal. See Chat in modals.
The message menu
A long press on a message opens its menu. A press on a message that failed to send opens it too, so the user can retry or delete it.
- The keyboard closes. On a long press, the device gives a haptic tap.
- The SDK measures where the message is on screen, and the overlay dims the screen behind it.
- The message itself moves into the overlay, in place.
- The reaction picker shows above the message, and the action list below it.
The menu doesn't open for a blocked message, for a message whose attachment has actions of its own, such as a Giphy preview the user is still choosing, or when onLongPressMessage on Channel replaces the long press. A long press on a message the server bounced asks the user to send it anyway, edit it or delete it instead.
The menu closes when the user picks an action or a reaction, taps the dimmed background, presses the Android back button, or rotates the device. When the message and the menu are taller than the screen, the user scrolls them by dragging.
What's in the menu:
| Part | Override key | Shown when |
|---|---|---|
| The reaction picker, with a button for more emoji | MessageReactionPicker |
the user has send-reaction in the channel |
| The action list | MessageActionList, with MessageActionListItem for each item |
there are actions for the message |
| The dimmed background | MessageOverlayBackground |
always |
The actions in the list, and how to change them, are on Actions. The picker's reactions are the isMain entries of supportedReactions on Channel; the more-emoji button opens a bottom sheet with the SDK's full emoji set, whatever supportedReactions holds. See Reactions.
MessageOverlayBackground is read by OverlayProvider, so set it in a WithComponents around OverlayProvider. The picker and the action list render inside the message, so they take the overrides that reach the message.
The optional MessageActions key replaces the layout of the whole overlay. It receives the animated styles the default layout positions its parts with, hostStyle, topItemStyle, bottomItemStyle and portalHostStyle, and renders the portal hosts that the message, the picker and the action list move into. It's for a menu with a different arrangement; to change what's in the menu, override the parts above.
Fixed UI above a closing menu
When the menu closes, the message animates back into the list. A part of the screen that should stay drawn above it, such as a header inside the screen, goes in a PortalWhileClosingView: while the menu closes, it moves its children into the overlay above the message, and keeps their space in the layout. The SDK wraps the composer and the suggestion list this way. The Channel UI page shows it around a header.
The image gallery
Pressing an image or a video in a message opens the gallery on that item, with the message's other images and videos to swipe through.
| Part | Override key |
|---|---|
| The header: the back button, the sender and the time | ImageGalleryHeader |
| The footer: the share button, the position, the grid button | ImageGalleryFooter |
| The video controls in the footer | ImageGalleryVideoControls |
| The grid of every item, in a bottom sheet | ImageGalleryGrid |
A tap hides and shows the header and footer. Pinching or a double tap zooms, and a swipe down closes the gallery, as do the back button in the header and the Android back button. The share button shows only when the share and file-system handlers are registered, which depends on the optional packages your app installs: see Installation.
The gallery is rendered by OverlayProvider, so these keys, and the icons the gallery uses, take effect only from a WithComponents around OverlayProvider. Its time format is the timestamp.ImageGalleryHeader translation key: see Localization.
Bottom sheets
The SDK's bottom sheets are BottomSheetModals. Each one is a React Native Modal, its own native window, so it opens above everything, the overlays and your navigation included, and needs no provider:
| Sheet | Opens from | Content key |
|---|---|---|
| Who reacted | a press on a message's reactions | MessageUserReactions, with MessageUserReactionsItem and MessageUserReactionsAvatar |
| More reactions | the more-emoji button of the reaction picker | none |
| Commands | the commands button of the attachment picker | none |
| The gallery's grid | the grid button of the gallery | ImageGalleryGrid |
| A channel's options | the options button of a channel-list row's swipe actions | ChannelDetailsBottomSheet, with ChannelDetailsHeader |
| A member's actions | a member in channel details | ChannelMemberActionsSheet |
| The channel image | editing the channel in channel details | ChannelEditImageSheet |
A sheet closes when the user drags it down, taps the backdrop, or presses the Android back button. A sheet shorter than the screen can be dragged up to full height. On iOS, the sheet moves up above the keyboard while it's open.
BottomSheetModal is exported, so your own sheets can match the SDK's:
import { useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import { BottomSheetModal } from "stream-chat-react-native";
export const ChannelRulesButton = ({ rules }: { rules: string }) => {
const [visible, setVisible] = useState(false);
return (
<>
<Pressable accessibilityRole="button" onPress={() => setVisible(true)}>
<Text>Rules</Text>
</Pressable>
<BottomSheetModal
enableDynamicSizing
onClose={() => setVisible(false)}
visible={visible}
>
<View style={styles.content}>
<Text>{rules}</Text>
</View>
</BottomSheetModal>
</>
);
};
const styles = StyleSheet.create({
content: { padding: 16 },
});| Prop | Default | What it does |
|---|---|---|
visible |
whether the sheet is open. While it's false, nothing renders |
|
onClose |
called when the user closes the sheet. Set visible to false in it |
|
height |
half the window | the sheet's height, capped to the window |
enableDynamicSizing |
false |
sizes the sheet to its content, up to height |
lazy |
false |
renders the content after the opening animation |
It reads the theme, so render it below OverlayProvider or Chat. Its look is the theme's bottomSheetModal key: container, contentContainer, handle, overlay and wrapper.
The attachment picker
The attachment picker is a bottom sheet that Channel and Thread render below their children. The composer's attach button opens it. Unlike the sheets above, it isn't a separate window: it slides up inside the channel's screen, under the composer, and the screen's content stays above it. Opening the keyboard closes it, and so does the Android back button.
Its parts are AttachmentPickerSelectionBar, the row of sources, AttachmentPickerContent, what the selected source shows, and ImageOverlaySelectedComponent, the mark on a selected photo. Its sources and props are on Message composer UI.
Full-screen modals
The poll screens and the screens channel details opens are React Native Modals too. On iOS, most are presented as a page sheet, and the media, files and pinned messages screens of channel details as full screen; on Android, all are full screen. They close on the Android back button.
| Screen | Content key |
|---|---|
| A poll's results | PollResultsContent, and PollOptionFullResultsContent for one option |
| A poll's options | PollAllOptionsContent |
| A poll's comments | PollAnswersListContent |
| Creating a poll | CreatePollContent, which has no default and replaces the built-in form |
| Channel details' member list | ChannelMemberList |
| Channel details' media, files and pinned messages | MediaList, FileAttachmentList, PinnedMessageList |
| Editing a channel, adding members | ChannelEditDetailsForm, ChannelAddMembersForm |
A dialog that only asks for text, such as suggesting a poll option or adding a comment, is a small Modal of its own. Confirmations, such as deleting a message or leaving a channel, use the platform's Alert.
Back button handling
On Android, the back button closes what's on top before your navigation sees it:
| Open | The back button |
|---|---|
| the message menu | closes it |
| the image gallery | closes it |
| a bottom sheet or a full-screen modal | closes it, through the Modal's onRequestClose |
| the attachment picker, with a source selected | closes it |
| nothing | goes to your navigation |
The overlays and the picker register their BackHandler listeners while they're mounted and return true only while they have something open, so your own listeners keep working the rest of the time.
Pitfalls
OverlayProviderinside a screen. Its overlays then cover only that screen, under the navigation's header. Put it around the navigation container's content, once.- A chat screen in a native modal. The message menu opens in the window behind it. Give the modal its own
OverlayProvider. - Gallery or overlay overrides below
OverlayProvider.OverlayProvidernever sees them. Put thatWithComponentsabove it. - A header that the closing message animates over. Wrap it in
PortalWhileClosingView. - Expecting the attachment picker over the whole screen. It belongs to the
Channel's screen and opens under the composer, not in its own window.
Used in: Message actions, Reactions, Attachments and uploads and Chat in modals.