Themes
An overview of customization levels, key concepts, and architecture is on the Design system: introduction page.
This page describes the details of working with ChatComponents and ChatFlows, basic customization, and ready-made theme examples.
This page uses the following terms (consistent with Design system: introduction and Components):
| Term | What it is |
|---|---|
| Token | A base app resource: R.color.*, R.drawable.*, R.font.*, R.string.*. |
| Palette | A dictionary of tokens: ChatColors, ChatImages, ChatTypography. (ChatTexts — a legacy string palette, all fields are @Deprecated; see below.) |
Components (ChatComponents) | Container of shared styles. Changes apply to all screens at once. |
| Component field | A field on the ChatComponents object: iconButtonComponent, navigationBarStyle, inputTextComponent, bubbleComponent, etc. |
Flows (ChatFlows) | Container of screen-specific styles; full list is in Flows. |
| Flow style | A specific field inside a flow: chatFlow.navigationBar.backButton, searchFlow.searchBar.textInput, etc. |
Only parameters explicitly specified in the ChatComponents signature and its fields are supported. Fields added via reflection or internal SDK mechanisms are not part of the public API. For the ChatTexts class, the entire mechanism is legacy and marked @Deprecated — use strings.xml (see Localization).
Some ChatConfig fields (searchEnabled, linkPreviewEnabled, voiceRecordingEnabled, autoScrollToLatest, etc.) are overridden by server configuration: client values are applied only as a fallback when the server did not send its own. Do not rely on these fields in production — see Known limitations.
ChatComponents signature
ChatComponents is a data class with five parameters:
data class ChatComponents(
val context: Context,
val colors: ChatColors = ChatColors(),
val images: ChatImages = ChatImages(),
val text: ChatTexts = ChatTexts(), // ← legacy, all fields @Deprecated
val typography: ChatTypography = ChatTypography()
)
All parameters except context have default values, so in the examples below we pass only what we are overriding.
ChatTexts is entirely deprecatedAll ChatTexts fields are marked @Deprecated with the message "Override the keys in the strings file". In new integrations, leave the parameter at its default value (ChatTexts()) and override strings via strings.xml (ecc_* keys) — see Localization. The exact field names, if you still need them for migration, are in the API reference on Dokka.
Customizing ChatComponents fields
Each ChatComponents field (iconButtonComponent, inputTextComponent, bubbleComponent, imageComponent, messageStatusesComponent, navigationBarStyle, etc.) is a var that can be modified via apply { ... } after creation. Changes apply to all screens at once. Full list — in Components.
For example, to globally replace the icon and color of the "back" button on all SDK screens:
val components = ChatComponents(applicationContext, colors = yourColors).apply {
iconButtonComponent.apply {
backBtnImage = R.drawable.ic_your_back // @DrawableRes
backButtonColor = R.color.your_brand_main // @ColorRes
}
}
iconButtonComponent fields (backBtnImage, backButtonColor, chatToolbarInverseIconTintColor, quoteClearIconColor, etc.) are annotated @DrawableRes/@ColorRes and accept your app's resource identifiers.
If you need to override the button on a single screen only (e.g., in the chat but not in the gallery), use ChatFlows, see Flows.
ChatTheme constructors
ChatTheme is a regular class (not a data class) with three constructors. Pick the call form by customization level:
// 1. Minimal: only colors / icons / fonts — for most integrations.
ChatTheme(
context: Context,
colors: ChatColors = ChatColors(),
images: ChatImages = ChatImages(),
texts: ChatTexts = ChatTexts(), // legacy, see above
typography: ChatTypography = ChatTypography()
)
// 2. Via a ready ChatComponents — when configuring component fields (e.g., navigationBarStyle).
ChatTheme(chatComponents: ChatComponents)
// 3. Via a ready ChatFlows — when per-element changes to flow styles (chatFlow, searchFlow, etc.).
ChatTheme(flows: ChatFlows)
The forms are hierarchical — pick one by customization level: token palettes (form 1), components (form 2), or flows (form 3). Each next form "wraps" the previous: ChatFlows is built from ChatComponents, and ChatComponents from palettes. A flow style takes priority over the same-named component field, and a component field over a palette value.
Basic customization
Define colors and images, overriding only the parameters you need:
val lightColors = ChatColors(
main = R.color.your_light_main,
searchingProgressLoader = R.color.your_light_main,
bodyIconsTint = R.color.your_light_main,
incomingText = R.color.your_black,
incomingTimeText = R.color.your_light_time_text,
outgoingTimeText = R.color.your_light_time_text,
outgoingText = R.color.your_black,
incomingBubble = R.color.your_white,
outgoingBubble = R.color.your_light_outgoing_bubble,
toolbarText = R.color.your_white,
messageSendingStatus = R.color.your_light_icons,
messageSentStatus = R.color.your_light_icons,
messageDeliveredStatus = R.color.your_light_icons,
messageReadStatus = R.color.your_light_icons,
messageFailedStatus = R.color.your_light_icons,
incomingLink = R.color.your_light_links,
outgoingLink = R.color.your_light_links,
toolbar = R.color.your_light_main,
statusBar = R.color.your_light_statusbar,
menuItem = R.color.your_light_main
)
Assemble ChatComponents, passing in colors and images. Component fields can be additionally configured via apply:
val lightChatComponents = ChatComponents(
applicationContext,
colors = lightColors,
images = lightImages
).apply {
navigationBarStyle = navigationBarStyle.copy(closeButtonEnabled = false)
}
Create the theme and set it during SDK initialization:
chatLightTheme = ChatTheme(lightChatComponents)
init()The theme and darkTheme fields must be set before the ChatCenterUI.init(...) call. Setting the theme after init() will not apply to the already-created UI — this is a typical reason "the theme silently does not work".
// chatCenterUI — a field of your Application / Activity (e.g., lateinit var).
// chatConfig is created following the example in quick_start.md.
chatCenterUI = ChatCenterUI(applicationContext).apply {
theme = chatLightTheme
darkTheme = chatDarkTheme
init("YOUR_PROVIDER_UID", "YOUR_APP_MARKER", chatConfig)
}
The init(...) method has two overloads: with appMarker (as above) and without it — init(providerUid, config). appMarker is needed when you have several Android apps under one providerUid (e.g., prod and staging); if you have a single app, use the form without appMarker.
Per-element customization via ChatFlows
ChatFlows collects styles of all flows (chatFlow, searchFlow, galleryFlow, …) and is writable through apply { ... }. To override, for example, only the "back" button in the chat:
val flows = ChatFlows(ChatComponents(applicationContext)).apply {
// change the "back" button
chatFlow.navigationBar.backButton = IconButtonChatStyle(
IconButtonColorStyle(
iconTintColor = R.color.blue_color
),
R.drawable.ic_cloud
)
// center the toolbar text
chatFlow.navigationBar.centerToolbarText = true
}
chatLightTheme = ChatTheme(flows) // theme instance with per-element flow style overrides
copy() vs direct assignment- Direct assignment (
chatFlow.navigationBar.backButton = ...) — fits replacing the whole object (a flow style or a component field). copy()— required for changing individual fields of a data class whose field is declaredval(or for batch-changing multiple fields at once):Direct assignment to asearchFlow = searchFlow.copy(backgroundColor = R.color.your_search_bg)valfield has no effect; forvarfields both work.
For example, to override the input field hint only on the search screen:
val flows = ChatFlows(ChatComponents(applicationContext)).apply {
searchFlow.searchBar.textInput?.placeholderTextResId = R.string.your_search_hint
}
placeholderText is deprecatedUse placeholderTextResId: Int? (@StringRes) — it has priority over placeholderText: String? and works correctly with localization. The placeholderText field is marked @Deprecated.
If, however, the text color should be changed for all input fields at once (not just in search), use the inputTextComponent field of the ChatComponents object (lightChatComponents is defined above):
ChatTheme(
lightChatComponents.apply {
inputTextComponent.inputMessageColor = R.color.your_blue
}
)
A live theme setup example is in the demo app. All public classes, methods, and fields with types and annotations — in the API reference on Dokka, generated from the SDK sources.
Ready-made theme example (light + dark)
Below is a production-ready theme setup example covering common customizations.
chatCenterUI— a field of yourApplication/Activity(e.g.,lateinit var chatCenterUI: ChatCenterUI).chatConfigis created following the example in Quick start:ChatConfig(transportConfig, networkConfig). There is no builder API.- The
fullScreenModefield of theChatFlowobject is marked@Deprecatedand is not used in new integrations — no need to set it.
// --- Light theme ---
val lightColors = ChatColors(
main = R.color.your_brand_main,
incomingBubble = R.color.your_incoming_bubble,
outgoingBubble = R.color.your_outgoing_bubble,
toolbar = R.color.your_toolbar_bg,
toolbarText = R.color.your_toolbar_text,
incomingText = R.color.your_incoming_text,
outgoingText = R.color.your_outgoing_text,
)
val lightImages = ChatImages(
logoImage = R.drawable.ic_your_brand_logo,
pushIcon = R.drawable.ic_your_push_icon,
)
val lightComponents = ChatComponents(
context = applicationContext,
colors = lightColors,
images = lightImages
).apply {
navigationBarStyle = navigationBarStyle.copy(
closeButtonEnabled = false
)
}
val lightFlows = ChatFlows(lightComponents)
val chatLightTheme = ChatTheme(lightFlows)
// --- Dark theme ---
val darkColors = ChatColors(
main = R.color.your_dark_main,
incomingBubble = R.color.your_dark_incoming_bubble,
outgoingBubble = R.color.your_dark_outgoing_bubble,
toolbar = R.color.your_dark_toolbar_bg,
toolbarText = R.color.your_white,
incomingText = R.color.your_white,
outgoingText = R.color.your_white,
chatBackground = R.color.your_dark_background,
)
val darkComponents = ChatComponents(
context = applicationContext,
colors = darkColors,
images = lightImages // icons can be reused
).apply {
navigationBarStyle = navigationBarStyle.copy(
closeButtonEnabled = false
)
}
val darkFlows = ChatFlows(darkComponents)
val chatDarkTheme = ChatTheme(darkFlows)
// --- Initialization ---
chatCenterUI = ChatCenterUI(applicationContext).apply {
theme = chatLightTheme
darkTheme = chatDarkTheme
init("YOUR_PROVIDER_UID", "YOUR_APP_MARKER", chatConfig)
}
Screen-specific customization (all 8 flows: chatFlow, searchFlow, galleryFlow, filesFlow, imageFlow, popupsFlow, balloonsChatStyle, bottomMenuChatFlow) — see Flows.
FAQ
Do I need to recreate ChatTheme on system theme change (light/dark switch)?
No, if you mean switching between already-set theme and darkTheme. The SDK determines the current mode via AppCompatDelegate.getDefaultNightMode() and Configuration.UI_MODE_NIGHT_MASK on every chat opening and picks the appropriate palette. It is enough to call AppCompatDelegate.setDefaultNightMode(...) — the standard Android mechanism re-creates the Activity, and the SDK picks up the right theme.
This does not apply to runtime replacement of the theme = newTheme object itself: a re-assignment after init() will not apply to an already-open chat. If you want to substitute the palette (for example, after loading brand settings from the API), set the new theme / darkTheme before the chat is opened by the user.
What happens if I don't set a ChatColors / ChatImages / ChatTypography field?
The SDK default is used. All fields of these classes are of type Int (non-nullable, @ColorRes / @DrawableRes) with a pre-set value (internal resources with the ecc_ prefix). You cannot pass null; to keep the default, simply do not specify the parameter in the constructor:
ChatColors(main = R.color.your_main /* other fields stay at the default */)
Does the SDK conflict with my Application / Activity theme (AppCompatTheme, MaterialTheme)?
No. The SDK sets its own theme for its Activities (inherits from Theme.MaterialComponents.Light.NoActionBar.Bridge). The theme set by your app does not affect the SDK UI. The chat UI appearance can be changed only via ChatColors, ChatImages, ChatComponents, ChatFlows, described above.
Where can I see the SDK default values?
In the API reference on Dokka — for each ChatColors / ChatImages / ChatTypography data-class field, the default value is shown. All internal SDK resources have the ecc_ prefix (e.g., R.color.ecc_green_83b144, R.drawable.ecc_ic_arrow_back_white_24dp).
Related sections
- Design system: overview — customization levels, key concepts, theme architecture.
- Colors —
ChatColorstable by category (toolbar, messages, input, push, etc.). - Typography —
ChatTypography: 6 sizes + 20 TTF substitution points. - Images —
ChatImages: drawables by category (toolbar, input, messages, surveys, etc.). - Components — chat element infographic, theming priorities, typical customization scenarios.
- Flows — screen-specific customization: chat / search / gallery / files / image / popups / balloons / bottomMenu.
- Accessibility — TalkBack, contrast, RTL.
- Known limitations — parameters with server-side priority.