This is beta documentation for Stream Chat Android SDK v7. For the latest stable version, see the latest version (v6) .

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, and ai-assistant merged or removed
  • Plugin system replaced with direct ChatClient.Builder configuration
  • New StreamDesign theming system with ChatUiConfig for 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-based ReactionIconFactory)
  • Swipeable channel list items with self-describing ChannelAction model
  • Slot-based message composer architecture
  • Simplified avatar system with standardized sizes

Dependencies

Four modules have been removed. Remove them from your build.gradle:

Removed ModuleMerged Into
stream-chat-android-offlinestream-chat-android-client
stream-chat-android-statestream-chat-android-client
stream-chat-android-ui-utilsstream-chat-android-ui-common
stream-chat-android-ai-assistantRemoved (use stream-chat-android-ai repo)
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
}

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() — use config()
  • StreamOfflinePluginFactory, StreamStatePluginFactory, StatePluginConfig — removed
  • PluginFactory and Plugin interfaces — no longer public
  • ChatClient.Builder.withRepositoryFactoryProvider() - removed
  • RepositoryFactory, RepositoryFactory.Provider and all -Repository interfaces - 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)
v6v7
io.getstream.chat.android.state.plugin.config.StatePluginConfigio.getstream.chat.android.client.api.ChatCoreConfig
io.getstream.chat.android.state.plugin.state.global.GlobalStateio.getstream.chat.android.client.api.state.GlobalState
io.getstream.chat.android.state.plugin.state.StateRegistryio.getstream.chat.android.client.api.state.StateRegistry
io.getstream.chat.android.state.plugin.state.querychannels.QueryChannelsStateio.getstream.chat.android.client.api.state.QueryChannelsState
io.getstream.chat.android.state.plugin.state.querythreads.QueryThreadsStateio.getstream.chat.android.client.api.state.QueryThreadsState
io.getstream.chat.android.state.plugin.state.channel.thread.ThreadStateio.getstream.chat.android.client.api.state.ThreadState
io.getstream.chat.android.state.plugin.state.channel.ChannelStateio.getstream.chat.android.client.channel.state.ChannelState
io.getstream.chat.android.state.plugin.state.querychannels.ChannelsStateDataio.getstream.chat.android.client.api.state.ChannelsStateData
io.getstream.chat.android.state.event.handler.chat.ChatEventHandlerio.getstream.chat.android.client.api.event.ChatEventHandler
io.getstream.chat.android.state.event.handler.chat.ChatEventHandlerFactoryio.getstream.chat.android.client.api.event.ChatEventHandlerFactory
io.getstream.chat.android.state.event.handler.chat.EventHandlingResultio.getstream.chat.android.client.api.event.EventHandlingResult
io.getstream.chat.android.state.event.handler.chat.BaseChatEventHandlerio.getstream.chat.android.client.api.event.BaseChatEventHandler
io.getstream.chat.android.state.event.handler.chat.DefaultChatEventHandlerio.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.state
UI-Utils module → UI-Common module (full list)
v6v7
io.getstream.chat.android.uiutils.model.MimeTypeio.getstream.chat.android.ui.common.model.MimeType
io.getstream.chat.android.uiutils.util.ColorUtilsio.getstream.chat.android.ui.common.utils.ColorUtil
io.getstream.chat.android.uiutils.util.EmojiUtilio.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.IntentUtilsio.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.common

Theming

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
v6v7
primaryAccentaccentPrimary
errorAccentaccentError
textHighEmphasistextPrimary
textLowEmphasistextSecondary
disabledtextDisabled
barsBackgroundbackgroundCoreSurface
appBackgroundbackgroundCoreApp
bordersborderCoreDefault
inputBackgroundcomposerBg
ownMessagesBackgroundchatBgOutgoing
otherMessagesBackgroundchatBgIncoming

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
v6v7
title1headingLarge
title3headingMedium
title3BoldheadingSmall
bodybodyDefault
bodyBoldbodyEmphasis
footnotecaptionDefault
footnoteBoldcaptionEmphasis
captionBoldmetadataEmphasis
(new)headingExtraSmall
(new)metadataDefault
(new)numericMedium
(new)numericLarge
(new)numericExtraLarge

Removed Theme Classes

RemovedReplacement
StreamDimensStreamTokens (internal)
StreamShapesStreamTokens.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 ParameterReplacement
dimensRemoved (internal StreamTokens)
shapesRemoved (internal StreamTokens.radius*)
reactionIconFactoryreactionResolver
messageContentFactorycomponentFactory (ChatComponentFactory)
autoTranslationEnabledconfig.translation.enabled
isComposerLinkPreviewEnabledconfig.composer.linkPreviewEnabled
videoThumbnailsEnabledconfig.messageList.videoThumbnailsEnabled
readCountEnabledconfig.messageList.readCountEnabled
userPresenceRemoved (online status always shown; control via showIndicator on UserAvatar/ChannelAvatar)
ownMessageTheme, otherMessageThemeRemoved
messageDateSeparatorTheme, messageUnreadSeparatorThemeRemoved
attachmentPickerThemeRemoved
attachmentsPickerTabFactoriesconfig.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  // ReactionResolver

Attachment Picker

The attachment picker has been completely redesigned with a mode-based architecture replacing the tab factory pattern.

Key Renames

v6v7
AttachmentsPickerAttachmentPicker
AttachmentsPickerMode (sealed class)AttachmentPickerMode (interface)
AttachmentsPickerMode.ImagesGalleryPickerMode()
AttachmentsPickerMode.FilesFilePickerMode()
AttachmentsPickerMode.MediaCaptureCameraPickerMode()
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

v6v7
ReactionIconFactoryReactionResolver
ReactionPushEmojiFactoryReactionResolver
ReactionOptionItemState(painter: Painter)ReactionOptionItemState(emojiCode: String?)
UserReactionItemState(painter: Painter)UserReactionItemState(emojiCode: String?)
SelectedReactionsMenuSelectedReactionsMenu (redesigned, uses ReactionsMenuViewModel with cursor-based pagination)
ReactionOptionsRemoved
ReactionOptionItemRemoved
AdaptiveMessageReactionsRemoved
// 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

v6v7
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: SearchMode enum (None, Channels, Messages) replaces boolean flags
  • Loading shimmer: ChannelListLoadingItem adds 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

SlotComponentPurpose
LeadingMessageComposerLeadingContentAttachment button
CenterMessageComposerInputText input, quote/edit indicator, attachments
TrailingMessageComposerTrailingContentEmpty 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

RemovedReplacement
mentionPopupContentBuilt-in SuggestionsMenu (customize via ChatComponentFactory)
commandPopupContentBuilt-in SuggestionsMenu (customize via ChatComponentFactory)
headerContentRemoved (use ChatComponentFactory overrides)
footerContentRemoved (use ChatComponentFactory overrides)
integrationsonAttachmentsClick callback

Renamed Components

v6v7
MentionSuggestionItemUserSuggestionItem (internal, customize via ChatComponentFactory)
MentionSuggestionListUserSuggestionList (internal, customize via ChatComponentFactory)
SuggestionListSuggestionsMenu (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.

RemovedReplacement
GroupAvatarChannelAvatar (handles groups internally)
ImageAvatarAvatar (internal)
InitialsAvatarUserAvatarPlaceholder (internal)
UserAvatarRowUserAvatarStack (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

  • Crossfade animation 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 metadataEmphasis typography
  • 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

  1. Update dependencies — remove offline, state, ui-utils, ai-assistant artifacts
  2. Update client setup — replace withPlugins(...) with config(...)
  3. Update imports — fix all io.getstream.chat.android.state.* and io.getstream.chat.android.offline.* imports (see Package Relocations)
  4. Update theming — migrate from StreamColors/StreamDimens/StreamTypography/StreamShapes to StreamDesign
  5. Update ChatTheme — pass config for feature flags, reactionResolver for reactions, componentFactory for UI customization
  6. Update attachment picker — replace AttachmentsPicker with AttachmentPicker, remove tab factories, configure modes via ChatUiConfig
  7. Update reaction code — replace ReactionIconFactory with ReactionResolver, update state models from painter to emojiCode
  8. Update channel list — handle new swipe actions, replace ChannelOptionState with ChannelAction subclasses
  9. Update avatar usage — replace GroupAvatar/ImageAvatar/InitialsAvatar with UserAvatar/ChannelAvatar
  10. Build and test — verify compilation and run UI tests