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
  • Optimized channel state management with active-window pagination model (replaces eager gap-filling)

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(ChatClientConfig())
    .build()

config() has sensible defaults. Calling it with no arguments gives you the same behavior as the v6 defaults.

ChatClientConfig

ChatClientConfig replaces both StreamOfflinePluginFactory and StreamStatePluginFactory(StatePluginConfig(...)):

import io.getstream.chat.android.client.api.ChatClientConfig

ChatClientConfig(
    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
    useLegacyChannelLogic = false,                      // default (set true for v6 gap-filling behavior)
)

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

Channel State Management

v7 changes how the SDK handles message loading when jumping to a specific message (e.g., a pinned message, a quoted message, or a search result).

What changed

In v6, calling loadMessagesAroundId would load the target message page and then recursively fetch all intermediate pages between that page and the latest messages (the fillTheGap behavior). In v7, the SDK uses an active-window model — it loads only the requested page and lets the user paginate forward or backward by scrolling. This reduces unnecessary API calls and memory usage.

Who is affected

If you are using the Stream UI Components (Compose or XML), no action is needed — the message composer already handles this automatically.

This is a breaking change for developers who use the state layer directly (via watchChannelAsState, loadMessagesAroundId, and the ChatClient state extensions) to build a custom UI. Because the SDK no longer eagerly loads all messages up to the latest, it is possible for the user to be viewing a "middle page" where ChannelState.endOfNewerMessages is false. If a message is sent from this state, it will not appear alongside the latest messages unless you explicitly load them.

How to migrate

Before sending a message, check endOfNewerMessages and call loadNewestMessages if the user is not on the latest page:

if (!endOfNewerMessages) {
    chatClient.loadNewestMessages(cid, messageLimit = 30).enqueue()
}
chatClient.sendMessage(channelType, channelId, message).enqueue()

For a complete example with Flow-based observation, see the State Layer — Message Loading Behavior documentation.

Opting out

To restore the v6 gap-filling behavior, enable the legacy channel logic:

ChatClient.Builder(apiKey, context)
    .config(ChatClientConfig(useLegacyChannelLogic = true))
    .build()

The legacy channel logic is provided for backward compatibility and may be removed in a future release.


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.ChatClientConfig
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 = true),
        messageList = MessageListConfig(
            readCountEnabled = true,
            videoThumbnailsEnabled = true,
        ),
        composer = ComposerConfig(
            audioRecordingEnabled = true,
            linkPreviewEnabled = false,
        ),
        channelList = ChannelListConfig(
            swipeActionsEnabled = true,
            muteIndicatorPosition = MuteIndicatorPosition.InlineTitle,
        ),
        attachmentPicker = AttachmentPickerConfig(
            useSystemPicker = true,
            modes = listOf(
                GalleryPickerMode(),
                FilePickerMode(),
                CameraPickerMode(),
                PollPickerMode(),
                CommandPickerMode,
            ),
        ),
    ),
) { content() }

Changed Defaults

Several feature flags now default to true (previously false):

  • Translation: TranslationConfig.enabled, TranslationConfig.showOriginalEnabled, ChatUI.autoTranslationEnabled, ChatUI.showOriginalTranslationEnabled, NotificationConfig.autoTranslationEnabled
  • Audio recording: ComposerConfig.audioRecordingEnabled (audio recording button is visible and enabled by default)
  • Draft messages: ChatUI.draftMessagesEnabled, ChannelViewModelFactory.isDraftMessageEnabled, MessagesViewModelFactory.isComposerDraftMessageEnabled

Additionally, ComposerConfig.audioRecordingSendOnComplete now defaults to false (recordings are attached for manual send instead of auto-sending).

If you previously relied on these features being disabled, explicitly set them to false.

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 = false,
        ),
    ),
) { ... }

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

ChannelScreen(
    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.

Removed: DeletedMessageVisibility

DeletedMessageVisibility and the deletedMessageVisibility parameter on MessagesViewModelFactory (Compose) and MessageListViewModelFactory (XML) have been removed. Deleted messages are now always visible as placeholders.

This was removed because hiding deleted messages could break message pagination in rare edge cases. If you were passing deletedMessageVisibility to any ViewModel factory, remove that parameter.

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

CDN Configuration

All deprecated CDN-related classes and interfaces have been removed in v7. The unified CDN interface is now the only way to customize how the SDK loads files and images from your CDN.

Removed API → V7 Replacement

Removed APIV7 Replacement
ImageHeadersProviderCDN.imageRequest() — return headers in CDNRequest.headers
AsyncImageHeadersProviderCDN.imageRequest() — the CDN method is already suspend
VideoHeadersProviderCDN.fileRequest() — return headers in CDNRequest.headers
ImageAssetTransformerCDN.imageRequest() — return transformed URL in CDNRequest.url
DownloadAttachmentUriGeneratorCDN.imageRequest() / CDN.fileRequest() — return transformed URL in CDNRequest.url
DownloadRequestInterceptorCDN.imageRequest() / CDN.fileRequest() — return headers in CDNRequest.headers
ChatClient.Builder.shareFileDownloadRequestInterceptor()CDN.fileRequest() — return URL / headers in CDNRequest
AttachmentDocumentActivityDocuments always open via external apps
useDocumentGView (ChatUI / ChatTheme)Removed — documents always open via external apps

Migration Example

Before (v6) — scattered across multiple locations:

// Compose: image loading headers, download URI generation, and download request interception
ChatTheme(
    imageHeadersProvider = object : ImageHeadersProvider {
        override fun getImageRequestHeaders(url: String) =
            mapOf("Authorization" to "Bearer $token")
    },
    downloadAttachmentUriGenerator = DownloadAttachmentUriGenerator { attachment ->
        Uri.parse(signingService.sign(attachment.assetUrl ?: attachment.imageUrl))
    },
    downloadRequestInterceptor = DownloadRequestInterceptor { request ->
        request.addRequestHeader("Authorization", "Bearer $token")
    },
) {
    // Your chat UI content
}

// XML: image loading headers, video headers, download URI generation, and download request interception
ChatUI.imageHeadersProvider = object : ImageHeadersProvider {
    override fun getImageRequestHeaders(url: String) =
        mapOf("Authorization" to "Bearer $token")
}
ChatUI.videoHeadersProvider = object : VideoHeadersProvider {
    override fun getVideoRequestHeaders(url: String) =
        mapOf("Authorization" to "Bearer $token")
}
ChatUI.downloadAttachmentUriGenerator = DownloadAttachmentUriGenerator { attachment ->
    Uri.parse(signingService.sign(attachment.assetUrl ?: attachment.imageUrl))
}
ChatUI.downloadRequestInterceptor = DownloadRequestInterceptor { request ->
    request.addRequestHeader("Authorization", "Bearer $token")
}

After (v7) — single configuration point:

ChatClient.Builder("apiKey", context)
    .cdn(object : CDN {
        override suspend fun imageRequest(url: String) = CDNRequest(
            url = signingService.sign(url),
            headers = mapOf("Authorization" to "Bearer $token")
        )
        override suspend fun fileRequest(url: String) = CDNRequest(
            url = signingService.sign(url),
            headers = mapOf("Authorization" to "Bearer $token")
        )
    })
    .build()

For full documentation on the CDN interface, see Custom CDN.


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. Review channel state usage — if you use loadMessagesAroundId with a custom UI, ensure you call loadNewestMessages when sending messages from a non-latest page (see Channel State Management)
  11. Update CDN configuration — replace old CDN APIs (ImageHeadersProvider, AsyncImageHeadersProvider VideoHeadersProvider, DownloadAttachmentUriGenerator, DownloadRequestInterceptor, ImageAssetTransformer) with the unified CDN interface on ChatClient.Builder; remove useDocumentGView references
  12. Build and test — verify compilation and run UI tests