dependencies {
// Keep these
implementation("io.getstream:stream-chat-android-client:$version")
implementation("io.getstream:stream-chat-android-compose:$version")
// Or for XML Views:
// implementation("io.getstream:stream-chat-android-ui-components:$version")
// Remove these — no longer exist in v7
implementation("io.getstream:stream-chat-android-offline:$version") // REMOVE
implementation("io.getstream:stream-chat-android-state:$version") // REMOVE
implementation("io.getstream:stream-chat-android-ui-utils:$version") // REMOVE
implementation("io.getstream:stream-chat-android-ai-assistant:$version") // REMOVE
}Migrating from v6 to v7
This guide covers all breaking changes when migrating from v6 to v7 of the Stream Chat Android SDK. Version 7 consolidates modules, introduces a new design system, and redesigns several Compose UI components.
What's New in v7
- Four modules consolidated —
offline,state,ui-utils, andai-assistantmerged or removed - Plugin system replaced with direct
ChatClient.Builderconfiguration - New
StreamDesigntheming system withChatUiConfigfor feature flags ChatComponentFactory— single interface for customizing all UI components (~185 methods)- Attachment picker redesigned with mode-based configuration
- Emoji-based reactions via
ReactionResolver(replaces icon-basedReactionIconFactory) - Swipeable channel list items with self-describing
ChannelActionmodel - Slot-based message composer architecture
- Simplified avatar system with standardized sizes
Dependencies
Four modules have been removed. Remove them from your build.gradle:
| Removed Module | Merged Into |
|---|---|
stream-chat-android-offline | stream-chat-android-client |
stream-chat-android-state | stream-chat-android-client |
stream-chat-android-ui-utils | stream-chat-android-ui-common |
stream-chat-android-ai-assistant | Removed (use stream-chat-android-ai repo) |
If you depend on stream-chat-android-compose or stream-chat-android-ui-components, the client module is included transitively. You only need to add client explicitly if you use it standalone without a UI module.
Client Setup
The plugin system has been replaced with direct configuration on ChatClient.Builder.
// v6
val client = ChatClient.Builder(apiKey, context)
.withPlugins(
StreamOfflinePluginFactory(context),
StreamStatePluginFactory(StatePluginConfig(), context),
)
.build()// v7
val client = ChatClient.Builder(apiKey, context)
.config(ChatCoreConfig())
.build()config() has sensible defaults. Calling it with no arguments gives you the same behavior as the v6 defaults.
ChatCoreConfig
ChatCoreConfig replaces both StreamOfflinePluginFactory and StreamStatePluginFactory(StatePluginConfig(...)):
import io.getstream.chat.android.client.api.ChatCoreConfig
ChatCoreConfig(
offlineEnabled = true, // default (was OfflineConfig.enabled)
ignoredOfflineChannelTypes = emptySet(), // default (was OfflineConfig.ignoredChannelTypes)
backgroundSyncEnabled = true, // default
userPresence = true, // default
isAutomaticSyncOnReconnectEnabled = true, // default
syncMaxThreshold = TimeDuration.hours(12), // default
messageLimitConfig = MessageLimitConfig(), // default
)Removed APIs
ChatClient.Builder.withPlugins()— useconfig()StreamOfflinePluginFactory,StreamStatePluginFactory,StatePluginConfig— removedPluginFactoryandPlugininterfaces — no longer publicChatClient.Builder.withRepositoryFactoryProvider()- removedRepositoryFactory,RepositoryFactory.Providerand all-Repositoryinterfaces - no longer public
Package Relocations
Classes from removed modules have moved to new packages. Use the find-and-replace table below for bulk updates.
State module → Client module (full list)
| v6 | v7 |
|---|---|
io.getstream.chat.android.state.plugin.config.StatePluginConfig | io.getstream.chat.android.client.api.ChatCoreConfig |
io.getstream.chat.android.state.plugin.state.global.GlobalState | io.getstream.chat.android.client.api.state.GlobalState |
io.getstream.chat.android.state.plugin.state.StateRegistry | io.getstream.chat.android.client.api.state.StateRegistry |
io.getstream.chat.android.state.plugin.state.querychannels.QueryChannelsState | io.getstream.chat.android.client.api.state.QueryChannelsState |
io.getstream.chat.android.state.plugin.state.querythreads.QueryThreadsState | io.getstream.chat.android.client.api.state.QueryThreadsState |
io.getstream.chat.android.state.plugin.state.channel.thread.ThreadState | io.getstream.chat.android.client.api.state.ThreadState |
io.getstream.chat.android.state.plugin.state.channel.ChannelState | io.getstream.chat.android.client.channel.state.ChannelState |
io.getstream.chat.android.state.plugin.state.querychannels.ChannelsStateData | io.getstream.chat.android.client.api.state.ChannelsStateData |
io.getstream.chat.android.state.event.handler.chat.ChatEventHandler | io.getstream.chat.android.client.api.event.ChatEventHandler |
io.getstream.chat.android.state.event.handler.chat.ChatEventHandlerFactory | io.getstream.chat.android.client.api.event.ChatEventHandlerFactory |
io.getstream.chat.android.state.event.handler.chat.EventHandlingResult | io.getstream.chat.android.client.api.event.EventHandlingResult |
io.getstream.chat.android.state.event.handler.chat.BaseChatEventHandler | io.getstream.chat.android.client.api.event.BaseChatEventHandler |
io.getstream.chat.android.state.event.handler.chat.DefaultChatEventHandler | io.getstream.chat.android.client.api.event.DefaultChatEventHandler |
Extension functions (globalState, state, queryChannelsAsState, watchChannelAsState, loadOlderMessages, loadNewerMessages, loadNewestMessages, loadMessageById, loadMessagesAroundId, getRepliesAsState):
io.getstream.chat.android.state.extensions → io.getstream.chat.android.client.api.stateUI-Utils module → UI-Common module (full list)
| v6 | v7 |
|---|---|
io.getstream.chat.android.uiutils.model.MimeType | io.getstream.chat.android.ui.common.model.MimeType |
io.getstream.chat.android.uiutils.util.ColorUtils | io.getstream.chat.android.ui.common.utils.ColorUtil |
io.getstream.chat.android.uiutils.util.EmojiUtil | io.getstream.chat.android.ui.common.utils.EmojiUtil |
io.getstream.chat.android.uiutils.extension.* | io.getstream.chat.android.ui.common.utils.extensions.* |
io.getstream.chat.android.uiutils.util.IntentUtils | io.getstream.chat.android.ui.common.utils.ContextUtils |
ColorUtils has been renamed to ColorUtil (without the 's'). The API is otherwise unchanged.
Quick Find-and-Replace
Apply the most specific replacements first (longer package paths) to avoid partial matches:
io.getstream.chat.android.state.plugin.config → io.getstream.chat.android.client.api
io.getstream.chat.android.state.plugin.state.global → io.getstream.chat.android.client.api.state
io.getstream.chat.android.state.plugin.state.querychannels → io.getstream.chat.android.client.api.state
io.getstream.chat.android.state.plugin.state.querythreads → io.getstream.chat.android.client.api.state
io.getstream.chat.android.state.plugin.state.channel.thread → io.getstream.chat.android.client.api.state
io.getstream.chat.android.state.plugin.state.channel → io.getstream.chat.android.client.channel.state
io.getstream.chat.android.state.plugin.state → io.getstream.chat.android.client.api.state
io.getstream.chat.android.state.event.handler.chat → io.getstream.chat.android.client.api.event
io.getstream.chat.android.state.extensions → io.getstream.chat.android.client.api.state
io.getstream.chat.android.uiutils → io.getstream.chat.android.ui.commonTheming
The theming system has been completely rebuilt. Individual theme classes are replaced by a unified StreamDesign namespace, and behavioral feature flags are grouped in ChatUiConfig.
StreamDesign.Colors
StreamColors is replaced by StreamDesign.Colors:
// v6
val colors = StreamColors.defaultColors()
ChatTheme(colors = colors) { ... }
// v7
val colors = StreamDesign.Colors.default() // light
val darkColors = StreamDesign.Colors.defaultDark() // dark
ChatTheme(colors = colors) { ... }Color property mapping
| v6 | v7 |
|---|---|
primaryAccent | accentPrimary |
errorAccent | accentError |
textHighEmphasis | textPrimary |
textLowEmphasis | textSecondary |
disabled | textDisabled |
barsBackground | backgroundCoreSurface |
appBackground | backgroundCoreApp |
borders | borderCoreDefault |
inputBackground | composerBg |
ownMessagesBackground | chatBgOutgoing |
otherMessagesBackground | chatBgIncoming |
StreamDesign.Typography
StreamTypography is replaced by StreamDesign.Typography:
// v6
val typography = StreamTypography.defaultTypography(fontFamily = myFont)
// v7
val typography = StreamDesign.Typography.default(fontFamily = myFont)Typography mapping
| v6 | v7 |
|---|---|
title1 | headingLarge |
title3 | headingMedium |
title3Bold | headingSmall |
body | bodyDefault |
bodyBold | bodyEmphasis |
footnote | captionDefault |
footnoteBold | captionEmphasis |
captionBold | metadataEmphasis |
| (new) | headingExtraSmall |
| (new) | metadataDefault |
| (new) | numericMedium |
| (new) | numericLarge |
| (new) | numericExtraLarge |
Removed Theme Classes
| Removed | Replacement |
|---|---|
StreamDimens | StreamTokens (internal) |
StreamShapes | StreamTokens.radius* (internal) |
StreamTokens is internal API. If you referenced StreamDimens or StreamShapes directly, use the token values via ChatTheme.colors and ChatTheme.typography instead.
ChatTheme Changes
// v7
ChatTheme(
isInDarkMode = isSystemInDarkTheme(),
colors = StreamDesign.Colors.default(),
typography = StreamDesign.Typography.default(),
config = ChatUiConfig(),
componentFactory = object : ChatComponentFactory {},
reactionResolver = ReactionResolver.defaultResolver(),
) { content() }| Removed Parameter | Replacement |
|---|---|
dimens | Removed (internal StreamTokens) |
shapes | Removed (internal StreamTokens.radius*) |
reactionIconFactory | reactionResolver |
messageContentFactory | componentFactory (ChatComponentFactory) |
autoTranslationEnabled | config.translation.enabled |
isComposerLinkPreviewEnabled | config.composer.linkPreviewEnabled |
videoThumbnailsEnabled | config.messageList.videoThumbnailsEnabled |
readCountEnabled | config.messageList.readCountEnabled |
userPresence | Removed (online status always shown; control via showIndicator on UserAvatar/ChannelAvatar) |
ownMessageTheme, otherMessageTheme | Removed |
messageDateSeparatorTheme, messageUnreadSeparatorTheme | Removed |
attachmentPickerTheme | Removed |
attachmentsPickerTabFactories | config.attachmentPicker.modes |
ChatUiConfig
Feature flags that were scattered across ChatTheme parameters are now grouped in ChatUiConfig:
ChatTheme(
config = ChatUiConfig(
translation = TranslationConfig(enabled = false),
messageList = MessageListConfig(
readCountEnabled = true,
videoThumbnailsEnabled = true,
),
composer = ComposerConfig(
audioRecordingEnabled = false,
linkPreviewEnabled = false,
),
channelList = ChannelListConfig(
swipeActionsEnabled = true,
muteIndicatorPosition = MuteIndicatorPosition.InlineTitle,
),
attachmentPicker = AttachmentPickerConfig(
useSystemPicker = true,
modes = listOf(
GalleryPickerMode(),
FilePickerMode(),
CameraPickerMode(),
PollPickerMode(),
CommandPickerMode,
),
),
),
) { content() }ChatComponentFactory
MessageContentFactory has been replaced by ChatComponentFactory, a comprehensive interface with ~185 overridable composable methods covering every UI component:
// v6
ChatTheme(messageContentFactory = MyContentFactory()) { ... }
// v7
class MyComponentFactory : ChatComponentFactory {
@Composable
override fun MessageContent(...) { ... }
@Composable
override fun ChannelListItemContent(...) { ... }
}
ChatTheme(componentFactory = MyComponentFactory()) { ... }Implement the ChatComponentFactory interface and override only the methods you need. Default implementations are provided for all methods.
CompositionLocals
// v6
LocalStreamColors.current
LocalStreamDimens.current
LocalStreamTypography.current
LocalStreamShapes.current
// v7 — access via ChatTheme object
ChatTheme.colors // StreamDesign.Colors
ChatTheme.typography // StreamDesign.Typography
ChatTheme.config // ChatUiConfig
ChatTheme.componentFactory // ChatComponentFactory
ChatTheme.reactionResolver // ReactionResolverAttachment Picker
The attachment picker has been completely redesigned with a mode-based architecture replacing the tab factory pattern.
Key Renames
| v6 | v7 |
|---|---|
AttachmentsPicker | AttachmentPicker |
AttachmentsPickerMode (sealed class) | AttachmentPickerMode (interface) |
AttachmentsPickerMode.Images | GalleryPickerMode() |
AttachmentsPickerMode.Files | FilePickerMode() |
AttachmentsPickerMode.MediaCapture | CameraPickerMode() |
AttachmentsPickerMode.Poll(...) | PollPickerMode(...) |
| (none) | CommandPickerMode |
Each mode now carries its own configuration (multi-select, media type, capture mode).
Tab Factory Removal
// v6 — factory-based
AttachmentsPicker(
tabFactories = listOf(
AttachmentsPickerImagesTabFactory(),
AttachmentsPickerFilesTabFactory(),
AttachmentsPickerMediaCaptureTabFactory(),
),
)
// v7 — mode-based via ChatUiConfig
ChatTheme(
config = ChatUiConfig(
attachmentPicker = AttachmentPickerConfig(
useSystemPicker = true,
modes = listOf(GalleryPickerMode(), FilePickerMode(), CameraPickerMode()),
),
),
) { ... }The following classes no longer exist: AttachmentsPickerTabFactory, AttachmentsPickerImagesTabFactory, AttachmentsPickerFilesTabFactory, AttachmentsPickerMediaCaptureTabFactory.
System Picker vs. In-App Picker
v7 introduces useSystemPicker (default true) that uses native OS pickers instead of the in-app picker UI. When false, it shows the traditional in-app tabs with media grid and file list — this requires adding READ_MEDIA_IMAGES/READ_MEDIA_VIDEO permissions to your manifest.
AttachmentPickerActions
A new AttachmentPickerActions class consolidates all picker interaction handlers:
AttachmentPicker(
attachmentsPickerViewModel = pickerViewModel,
messageMode = MessageMode.Normal,
actions = AttachmentPickerActions.defaultActions(pickerViewModel, composerViewModel),
)Reactions
Reactions are now emoji-only by default. The icon-based reaction system has been replaced by ReactionResolver.
ReactionResolver
| v6 | v7 |
|---|---|
ReactionIconFactory | ReactionResolver |
ReactionPushEmojiFactory | ReactionResolver |
ReactionOptionItemState(painter: Painter) | ReactionOptionItemState(emojiCode: String?) |
UserReactionItemState(painter: Painter) | UserReactionItemState(emojiCode: String?) |
SelectedReactionsMenu | SelectedReactionsMenu (redesigned, uses ReactionsMenuViewModel with cursor-based pagination) |
ReactionOptions | Removed |
ReactionOptionItem | Removed |
AdaptiveMessageReactions | Removed |
// v6
ChatTheme(reactionIconFactory = ReactionIconFactory.defaultFactory()) { ... }
// v7
ChatTheme(reactionResolver = ReactionResolver.defaultResolver()) { ... }Custom Reactions
val customResolver = DefaultReactionResolver(
emojiMapping = linkedMapOf(
"like" to "\uD83D\uDC4D",
"love" to "\u2764\uFE0F",
"fire" to "\uD83D\uDD25",
),
defaultReactions = listOf("like", "love", "fire"),
)
ChatTheme(reactionResolver = customResolver) { ... }If you had custom Painter-based reactions via ReactionIconFactory, you'll need to switch to emoji characters. The v7 reaction system is emoji-only.
New: MessageReactionItemState
// v7 — new class for inline reaction display
data class MessageReactionItemState(
val type: String,
val emoji: String?,
val count: Int,
)Used by ClusteredMessageReactions and SegmentedMessageReactions (which replace AdaptiveMessageReactions).
Channel List
The channel list has been redesigned with swipe-to-action support and a self-describing action model.
SwipeableChannelItem
Swipe actions are enabled by default. Control via ChatUiConfig:
ChatTheme(
config = ChatUiConfig(
channelList = ChannelListConfig(swipeActionsEnabled = true),
),
) { ... }Customize swipe actions via ChatComponentFactory.ChannelSwipeActions. SwipeActionItem supports three styles: Primary (blue), Secondary (gray), Destructive (red).
ChannelAction Replaces ChannelOptionState
| v6 | v7 |
|---|---|
ChannelOptionState(title, iconPainter, action) | ChannelAction interface — self-describing with icon, label, isDestructive, requiredCapability, confirmationPopup, onAction |
Built-in actions: ViewInfo, MuteChannel/UnmuteChannel, PinChannel/UnpinChannel, ArchiveChannel/UnarchiveChannel, LeaveGroup, DeleteConversation, MuteUser/UnmuteUser, BlockUser/UnblockUser.
// v6
val options = listOf(
ChannelOptionState(
title = R.string.mute,
iconPainter = painterResource(R.drawable.ic_mute),
action = Mute(channel),
),
)
// v7
val actions = listOf(
MuteChannel(
channel = channel,
label = stringResource(R.string.mute),
onAction = { viewModel.muteChannel(channel) },
),
)ChannelItemState
ChannelItemState now includes isSelected: Boolean (default false). Set automatically when a channel is long-pressed to show the options menu.
Other Changes
- Search mode:
SearchModeenum (None,Channels,Messages) replaces boolean flags - Loading shimmer:
ChannelListLoadingItemadds skeleton placeholders during load - Mute indicator position: Configurable via
ChannelListConfig.muteIndicatorPosition
Message Composer
The composer has been restructured into a slot-based layout.
Slot Architecture
| Slot | Component | Purpose |
|---|---|---|
| Leading | MessageComposerLeadingContent | Attachment button |
| Center | MessageComposerInput | Text input, quote/edit indicator, attachments |
| Trailing | MessageComposerTrailingContent | Empty by default (extension point) |
The center input is further divided into InputLeadingContent (command chip), InputCenterContent (text field), InputCenterBottomContent ("Also send to channel" checkbox), and InputTrailingContent (send/save/record button).
Parameter Changes
| Removed | Replacement |
|---|---|
mentionPopupContent | Built-in SuggestionsMenu (customize via ChatComponentFactory) |
commandPopupContent | Built-in SuggestionsMenu (customize via ChatComponentFactory) |
headerContent | Removed (use ChatComponentFactory overrides) |
footerContent | Removed (use ChatComponentFactory overrides) |
integrations | onAttachmentsClick callback |
Renamed Components
| v6 | v7 |
|---|---|
MentionSuggestionItem | UserSuggestionItem (internal, customize via ChatComponentFactory) |
MentionSuggestionList | UserSuggestionList (internal, customize via ChatComponentFactory) |
SuggestionList | SuggestionsMenu (internal) |
Inline Edit Indicator
v7 adds a visual inline edit indicator that appears automatically when action is Edit in the composer state. Shows "Edit message" with a preview and cancel button.
Audio Recording
Audio recording UI components are now internal. Enable via ChatUiConfig:
ChatTheme(
config = ChatUiConfig(
composer = ComposerConfig(
audioRecordingEnabled = true,
audioRecordingSendOnComplete = true,
),
),
) { ... }Avatars
The avatar system removes several standalone composables and consolidates them.
| Removed | Replacement |
|---|---|
GroupAvatar | ChannelAvatar (handles groups internally) |
ImageAvatar | Avatar (internal) |
InitialsAvatar | UserAvatarPlaceholder (internal) |
UserAvatarRow | UserAvatarStack (internal) |
UserAvatar and ChannelAvatar remain the public API. Use these instead of the removed low-level components.
Migration Examples
// v6
UserAvatar(user = user, modifier = Modifier.size(40.dp), showOnlineIndicator = true)
// v7
UserAvatar(user = user, modifier = Modifier.size(AvatarSize.Large), showIndicator = true)// v6
GroupAvatar(users = members.map { it.user }, modifier = Modifier.size(48.dp))
// v7
ChannelAvatar(channel = channel, currentUser = currentUser, modifier = Modifier.size(AvatarSize.ExtraLarge))Standardized Sizes
v7 introduces AvatarSize constants: ExtraSmall (20dp), Small (24dp), Medium (32dp), Large (40dp), ExtraLarge (48dp), ExtraExtraLarge (64dp).
Other Changes
Crossfadeanimation removed for performance- Placeholder colors are deterministic based on user/channel ID (cycles through 5 color pairs)
Messages Screen
New: onChannelAvatarClick
MessagesScreen(
viewModelFactory = factory,
onChannelAvatarClick = { channel ->
navController.navigate("channel/${channel.cid}/info")
},
)When null (default), the channel avatar in the header is not clickable.
Typing Indicator
The typing indicator has moved from a separate overlay into the message list as an inline item. Remove any separate TypingIndicator component — it appears automatically.
Visual Changes
- Date separators: Rounded pill style with
metadataEmphasistypography - Unread separator: Same pill style, visually distinguishes new messages
- System messages: Redesigned bubble
- Deleted messages: Updated design
- Start of channel: New "beginning of conversation" indicator at the top
Migration Checklist
- Update dependencies — remove
offline,state,ui-utils,ai-assistantartifacts - Update client setup — replace
withPlugins(...)withconfig(...) - Update imports — fix all
io.getstream.chat.android.state.*andio.getstream.chat.android.offline.*imports (see Package Relocations) - Update theming — migrate from
StreamColors/StreamDimens/StreamTypography/StreamShapestoStreamDesign - Update ChatTheme — pass
configfor feature flags,reactionResolverfor reactions,componentFactoryfor UI customization - Update attachment picker — replace
AttachmentsPickerwithAttachmentPicker, remove tab factories, configure modes viaChatUiConfig - Update reaction code — replace
ReactionIconFactorywithReactionResolver, update state models frompaintertoemojiCode - Update channel list — handle new swipe actions, replace
ChannelOptionStatewithChannelActionsubclasses - Update avatar usage — replace
GroupAvatar/ImageAvatar/InitialsAvatarwithUserAvatar/ChannelAvatar - Build and test — verify compilation and run UI tests