Customizing Components

The ChatTheme component is a wrapper that you should use as the root of all Compose UI Components. It’s used to provide the default properties that help us style the application, such as:

  • isDarkTheme: Flag that determines if the application should be themed in light or dark mode.
  • colors: Defines a palette of colors we support in the app. These are applied to all components and provide us with a dark/light mode by default, but can be used to override the design system completely.
  • dimens: Used for defining the dimensions of various components such as avatars, attachment content, reactions etc.
  • typography: Used for all text elements, to apply different text styles to each component. Can be used to change the typography completely.
  • shapes: Defines several shapes we use across our Compose UI components. Can be used to change the shape of messages, input fields, avatars and attachments.
  • rippleTheme: Defines the appearance for ripples. Can be used to override the ripple colors used in light and dark modes.
  • attachmentFactories: Used to process messages and show different types of attachment UI, given the provided factories. Can be used to override the UI for file, image and link attachments, as well as to add custom attachment types.
  • attachmentPreviewHandlers: Used to provide previews for all supported attachment types. If you do not wish to use the default previews, you can customize this.
  • quotedAttachmentFactories: Used to process messages and show different types of attachment UI, when quoting a message that contains an attachment.
  • reactionIconFactory: Used to create a reaction icon for the given reaction type. You can use the default factory that supports a predefined set of reactions, or provide a custom one.
  • reactionOptionsTheme: Used to define the appearance of the reaction option list in the selected message menu. For theming the message option list in the same menu, see messageOptionsTheme below.
  • dateFormatter: Used to define the timestamp formatting in the app. You can use the default formatting, or customize it to your needs.
  • channelNameFormatter: Used to define the channel name formatting in the app. You can use the default implementation, or customize it according to your needs.
  • messagePreviewFormatter: Used to define the message preview formatting in the app. You can use the default implementation, or customize the display of message previews according to your needs.
  • imageLoaderFactory: Used to create Coil image loader instances. You can use the default image loader factory, or provide a custom one.
  • messageAlignmentProvider: Used to provide an alignment for a particular message. You can use the default implementation which aligns the messages of the current user to end, or customize it according to your needs.
  • messageOptionsTheme: Used to define the appearance of the message option list in the selected message menu. For theming the reaction option list in the same menu, see reactionOptionsTheme above.
  • messageOptionsUserReactionAlignment: Used to define how message reactions are aligned when browsing all reactions for a given message.
  • attachmentPickerTheme: Used to define the appearance of the attachment picker dialog. You can use the default implementation, or customize it according to your needs.
  • permissionHandlers: Used to handle permissions inside the app. Default implementation of the download permission handler automatically downloads files after the permission has been granted.
  • attachmentsPickerTabFactories: Used to display different tabs in the attachments picker dialog.
  • videoThumbnailsEnabled: Dictates whether video thumbnails will be displayed inside video previews. They are a paid feature and enabled by default and the pricing can be found here.

If any of these properties are not provided to our Compose UI Components due to not being wrapped inside of ChatTheme, you’ll get an exception saying that the required properties are missing.

Let’s see how to use the ChatTheme and how to customize the UI within.

Using ChatTheme

To use the ChatTheme, simply wrap your UI content with it, like in the following example:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ChatTheme {
            MessagesScreen(
                viewModelFactory = MessagesViewModelFactory(
                    context = this,
                    channelId = "messaging:123",
                    messageLimit = 30
                ),
                onBackPressed = { finish() },
                onHeaderTitleClick = {}
            )
        }
    }
}

The ChatTheme provides default implementations for all its styling properties. That way, you can keep using our default color palette, typography, shapes, attachment factories and reaction types.

All you have to do is pass in the UI content you want to show, within its trailing lambda. This snippet above will produce the following UI. You’ll also notice that if you switch to the dark theme in your system UI, the app will re-draw accordingly.

Light themeDark theme
Default MessagesScreen component
Default Dark Mode MessagesScreen Component

Let’s see how to customize the theme.

Customization

To customize the ChatTheme, simply override any of the default properties by passing in your custom design style, like so:

setContent {
    ChatTheme(
        shapes = StreamShapes.defaultShapes().copy( // Customizing the shapes
            avatar = RoundedCornerShape(8.dp),
            attachment = RoundedCornerShape(16.dp),
            inputField = RectangleShape,
            myMessageBubble = RoundedCornerShape(16.dp),
            otherMessageBubble = RoundedCornerShape(16.dp),
            bottomSheet = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
        )
    ) {
        MessagesScreen(
            viewModelFactory = MessagesViewModelFactory(
                context = this,
                channelId = "messaging:123",
            ),
            onBackPressed = { finish() },
            onHeaderTitleClick = {}
        )
    }
}

In the snippet above, we customized the shapes to be different from the default values. We made the message bubbles rounded, the input field rectangular and the avatar a rounded rectangle.

This snippet above will produce the following screen:

Light ThemeDark Theme
Custom ChatTheme Component
Custom ChatTheme Component

It’s really easy to customize these properties or provide static customization that you just reuse all over your app.

Let’s see what each property exposes and what the values are used for.

StreamColors

StreamColors are used to represent all the colors we use and apply to our components in the SDK.

You can find the definitions of all the colors we expose in the class documentation, as well as what the default provided colors are.

You can also browse which components are using the colors, to know what will be affected by any change.

StreamDimens

StreamDimens defines different sizes that can be customized in the SDK.

You can find the definitions of all the dimensions we expose in the class documentation, as well as what the default dimensions are.

You can also browse which components are using the dimensions, to know what will be affected by any change.

StreamTypography

StreamTypography is used to apply different font weights and sizes to our textual UI components.

You can find all the text style properties we expose in the class documentation, as well as what the default styles are.

You can also browse which components are using the styles, to know what will be affected by any change.

StreamShapes

StreamShapes provides a small collection of shapes that let us style our containers.

You can find all the shapes we expose in the class documentation, as well as what the default shapes are.

These are really easy to customize, as you’ve seen before, and can make your app feel closer to your design system.

RippleTheme

Defines the appearance for ripples. The default ripple theme is StreamRippleTheme.

You can find out more about it by reading the object documentation.

You can easily customize the ripple colors in light and dark modes by overriding ChatTheme.rippleTheme with your own implementation of RippleTheme.

StreamAttachmentFactories

StreamAttachmentFactories.defaultFactories() provides default factories for uploads, links, both Giphy and regular images and files.

If you want to know more you can take a look at the class documentation or read the detailed guide on Custom Attachments.

AttachmentPreviewHandler

AttachmentPreviewHandler.defaultAttachmentHandlers() provides default handlers for media, document and URL attachments.

If you want you can take a look at the class documentation.

You can customize file previews by creating your own list of AttachmentPreviewHandlers and overriding ChatTheme.attachmentPreviewHandlers with it.

Quoted AttachmentFactories

StreamAttachmentFactories.defaultQuotedFactories() provides default attachment factories for quoted messages.

If you want to know more you can take a look at the class documentation or read the detailed guide on Custom Attachments.

ReactionIconFactory

Used for defining reactions the user can add to messages. ReactionIconFactory.defaultFactory() provides our default basic reactions out of the box.

You can find their definitions in the class documentation.

Reactions are easily customizable by passing in your own ReactionIconFactory which contains reactions and overriding ChatTheme.reactionIconFactory with it.

ReactionOptionsTheme

Defines the appearance of the reaction option list in the selected message menu. Allows you to show/hide options through the areReactionOptionsVisible parameter.

See the class documentation for more details.

DateFormatter

Used for formatting various times and dates such as the timestamp you see when a message is displayed. The default date formatter in ChatTheme is Stream’s DefaultDateFormatter.

You can find out more about it by reading the class documentation.

The date formatter can be customized by overriding ChatTheme.dateFormatter with your own implementation of DateFormatter.

ChatTheme(
    dateFormatter = object : DateFormatter {
        private val dateFormat: DateFormat = SimpleDateFormat("dd/MM/yyyy")
        private val timeFormat: DateFormat = SimpleDateFormat("HH:mm")

        override fun formatDate(date: Date?): String {
            date ?: return ""
            return dateFormat.format(date)
        }

        override fun formatTime(date: Date?): String {
            date ?: return ""
            return timeFormat.format(date)
        }

        override fun formatRelativeTime(date: Date?): String {
            date ?: return ""
            return DateUtils.getRelativeDateTimeString(
                applicationContext,
                date.time,
                DateUtils.MINUTE_IN_MILLIS,
                DateUtils.WEEK_IN_MILLIS,
                0
            ).toString()
        }
    }
) {
    // Your UI content
}

TimeProvider

Used for providing the current time. The default time provider is TimeProvider.DEFAULT.

You can find out more about it by reading the class documentation.

The time provider can be customized by overriding ChatTheme.timeProvider with your own implementation of TimeProvider.

ChatTheme(
    timeProvider = object : TimeProvider {
        override fun invoke(): Long {
            // Return the current time in milliseconds
            // System.currentTimeMillis() is the default implementation
            // You can use your own implementation here which can be synchronized with a server
            // to avoid local clock tampering
            return System.currentTimeMillis()
        }
    }
) {
    // Your UI content
}

ChannelNameFormatter

Used for formatting channel names. The default channel name formatter is Stream’s DefaultChannelNameFormatter.

You can find out more about it by reading the class documentation.

The channel name formatter is customizable by overriding ChatTheme.channelNameFormatter with your own instance of ChannelNameFormatter.

MessagePreviewFormatter

Used for formatting the preview message in ChannelItem. The default message preview formatter is Stream’s DefaultMessagePreviewFormatter.

You can find out more about it by reading the class documentation.

The message preview formatter can be customized by overriding ChatTheme.messagePreviewFormatter with your own implementation of MessagePreviewFormatter

ChatTheme(
    messagePreviewFormatter = object : MessagePreviewFormatter {
        override fun formatMessagePreview(message: Message, currentUser: User?): AnnotatedString {
            return buildAnnotatedString {
                append(message.text)
                // add your custom styling here
            }
        }
    }
) {
    // Your UI content
}

MessageTextFormatter

Used for formatting the message text in MessageText component. The default message text formatter is Stream’s DefaultMessageTextFormatter.

You can find out more about it by reading the class documentation.

The message text formatter can be customized by overriding ChatTheme.messageTextFormatter with your own implementation of MessageTextFormatter

ChatTheme(
    messageTextFormatter = object : MessageTextFormatter {
        override fun format(message: Message, currentUser: User?): AnnotatedString {
            return buildAnnotatedString {
                append(message.text)
                // Your custom styling here
            }
        }
    }
) {
    // Your UI content
}

QuotedMessageTextFormatter

Used for formatting the quoted message text in QuotedMessageText component. The default quote message text formatter is Stream’s DefaultQuotedMessageTextFormatter.

You can find out more about it by reading the class documentation.

The quote message text formatter can be customized by overriding ChatTheme.quotedMessageTextFormatter with your own implementation of QuotedMessageTextFormatter

ChatTheme(
    quotedMessageTextFormatter = object : QuotedMessageTextFormatter {
        override fun format(message: Message, replyMessage: Message?, currentUser: User?): AnnotatedString {
            return buildAnnotatedString {
                append(message.text)
                // Your custom styling here
            }
        }
    }
) {
    // Your UI content
}

StreamCoilImageLoaderFactory

StreamCoilImageLoaderFactory.defaultFactory() provides the default factory that creates new Coil ImageLoader instances.

You can find out more about it by reading the class documentation.

You can easily customize how the images are loaded by passing your own implementation of StreamCoilImageLoaderFactory to ChatTheme or by using the default factory to provide an ImageLoader and calling loader.newBuilder() to expand or override its behavior.

MessageAlignmentProvider

Used for aligning messages. The default message alignment provider is DefaultMessageAlignmentProvider which will align your own messages to end while aligning the messages of others to start.

You can find out more about it by reading the class documentation.

As with all of the other ChatTheme properties, you can easily customize how the messages are aligned by overriding ChatTheme.messageAlignmentProvider with your own implementation of MessageAlignmentProvider.

MessageOptionsTheme

Defines the appearance of the message option list in the selected message menu. Allows you to show/hide options through the optionVisibility parameter.

See the class documentation for more details.

MessageOptionsUserReactionAlignment

Determines the alignment of the reaction icon inside user reactions. By default, they are aligned to the end.

You can find out more about it by reading the class documentation.

You can also align the reactions to the start of the user avatar or align them depending on the user who left them.

AttachmentPickerTheme

Defines the appearance of the attachment picker dialog. The default attachment picker theme is AttachmentPickerTheme.defaultTheme().

See the class documentation

PermissionHandlers

StreamPermissionHandlers.defaultHandlers() provides default handler for storage permission that automatically downloads files after the permission has been granted.

If you want to know more you can take a look at the class documentation.

AttachmentsPickerTabFactories

AttachmentsPickerTabFactories.defaultFactories() provides default factories for image, files and media capture tabs of the attachment picker dialog.

If you want to know more you can take a look at the class documentation.

You can pass a different set of factories to enable/disable a particular tab or introduce a custom one.

© Getstream.io, Inc. All Rights Reserved.