Customizing the Compose Chat SDK with ChatTheme

Stream’s Compose UI components make it easy for you to customize them to your own requirements. In this article, you’ll learn how to add your own colors, shapes, and typography to your chat app.

Harun W.
Harun W.
Published December 17, 2021 Updated December 19, 2021
Customizing ChatTheme in Compose Chat SDK

To get started, you'll learn how to customize the chat features you build with Stream's Compose Chat SDK.

Specifically, you'll work with Stream's Compose ChatTheme component to define these features so that your app truly looks and feels the way you want it to.

You can find all the code in this article on GitHub.

Note: This article assumes you are familiar with Stream UI Components. If you're unfamiliar with Stream Jetpack Compose UI Components, check out the Jetpack Compose Chat Tutorial

Understanding the ChatTheme Component

The is the default wrapper for all Stream Compose components.

By default, it has properties that can style the entirety of any Stream Chat application. Some of these properties include (but are not limited to): colors, shapes, and typography.

With ChatTheme, it's possible to apply a different set of colors, shapes, and typography to your entire chat app.

kotlin
1
2
3
4
5
6
7
8
9
10
@Composable public fun ChatTheme( isInDarkMode: Boolean = isSystemInDarkTheme(), colors: StreamColors = if (isInDarkMode) StreamColors.defaultDarkColors() else StreamColors.defaultColors(), dimens: StreamDimens = StreamDimens.defaultDimens(), typography: StreamTypography = StreamTypography.defaultTypography(), shapes: StreamShapes = StreamShapes.defaultShapes(), // ... content: @Composable () -> Unit, )

To customize the ChatTheme component, you need to provide your own colors, shapes, and typography to override ChatTheme's default implementation.

Don't worry! You'll learn how you can add your own custom implementations to all the styling properties, starting with colors.

Adding Custom Colors

To add colors from your own design style, you need to use the StreamColors class. This contains all colors in the Stream color palette.

First, you need an object to define your own colors:

kotlin
1
2
3
4
5
6
7
8
9
object CustomColors { val Primary = Color(0xFF69ABFD) val PrimaryDark = Color(0xFF478CE9) val PrimaryLight = Color(0xFFD0E8FD) val OverLay = Color(0xA0A2C8FF) val Accent = Color(0xFFA20DFF) val TextHigh = Color(0xFF1A1A1A) val TextLow = Color(0xFF2E2E2E) }

In the code above, you define the custom colors that you'll use for the components inside your app.

With your colors defined, you need to specify which custom colors you want to swap with the components in StreamColors. This is what it looks like:

kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
val CustomLightStreamColors @Composable get() = StreamColors.defaultColors().copy( textHighEmphasis = CustomColors.TextHigh, textLowEmphasis = CustomColors.TextLow, disabled = CustomColors.PrimaryDark, borders = CustomColors.Primary, inputBackground = CustomColors.PrimaryLight, appBackground = CustomColors.PrimaryLight, barsBackground = CustomColors.Primary, linkBackground = CustomColors.PrimaryLight, overlay = CustomColors.OverLay, overlayDark = CustomColors.PrimaryDark, primaryAccent = CustomColors.Accent, highlight = CustomColors.PrimaryDark, ownMessagesBackground = CustomColors.PrimaryDark, otherMessagesBackground = CustomColors.PrimaryDark, deletedMessagesBackgroundColor = CustomColors.PrimaryDark )

In the code above, you provide your custom colors for the various properties defined in the StreamColors class. This will apply to your whole app. You can always change these values.

Additionally, you can re-use the default colors by using Stream.defaultColors(). This returns an instance that contains the Stream color palette.

Note: You can always use the default set of colors, shapes, typography (and more) from Stream, and simply copy that object to adjust a few values inside it. This way, you don't have to provide all values yourself, just the ones you want to change!

Adding Custom Shapes

Shapes let you define the appearance of your components in your app. To customize the shapes, you use the StreamShapes class, which contains the shapes for different components.

kotlin
1
2
3
4
5
6
7
8
9
val CustomStreamShapes = StreamShapes( avatar = RoundedCornerShape(40), myMessageBubble = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp, bottomStart = 20.dp), otherMessageBubble = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp, bottomEnd = 20.dp), inputField = RoundedCornerShape(24.dp), attachment = RoundedCornerShape(16.dp), imageThumbnail = RoundedCornerShape(10.dp), bottomSheet = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp), )

Using the StreamShapes class, you can define the shapes for attachments, bottom sheets, input fields, avatars, images, thumbnails, and message bubbles.

The last bit on customization is the typography which you'll learn about next.

Adding Custom Typography

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!

Typography allows you to add fonts to your app. The StreamTypography class lets you define custom typography.

First, you need to add any custom fonts you want to use in the fonts folder. For this example, you can use the free Source Code Pro font.

Then, create a FontFamily for your newly-added fonts:

kotlin
1
2
3
4
val SourceCodePro = FontFamily( Font(R.font.sourcecodepro_regular), Font(R.font.sourcecodepro_bold, FontWeight.Bold) )

This allows you to use the SourceCodePro font in your custom definitions.

kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
val StreamCustomTypography = StreamTypography( title1 = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontSize = 18.sp ), title3 = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontSize = 16.sp ), title3Bold = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Bold, fontSize = 16.sp ), body = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontSize = 14.sp ), bodyItalic = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontStyle = FontStyle.Italic, fontSize = 14.sp ), bodyBold = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Bold, fontSize = 14.sp ), footnote = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontSize = 12.sp ), footnoteItalic = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontStyle = FontStyle.Italic, fontSize = 12.sp ), footnoteBold = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Bold, fontSize = 12.sp ), captionBold = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Bold, fontSize = 12.sp ), tabBar = TextStyle( fontFamily = SourceCodePro, fontWeight = FontWeight.Normal, fontSize = 12.sp ) )

Here, you define all the text style properties that will be used across all the text style components in your chat app.

With the StreamTypography class, if you only want to change the font family and not the values for other components, you can do so like this:

kotlin
1
StreamTypography.defaultTypography(SourceCodePro)

This only changes the font family. Other properties, like font size and weight, will remain the same as the default Stream UI Component values.

Now that you have all these custom components defined, it's time you apply them to ChatTheme.

Joining the Pieces Together

Defining custom StreamTypography, StreamShapes, and StreamColors won't apply these changes to your app. You also need to provide them to ChatTheme.

To apply your customized components, wrap your UI components with ChatTheme:

kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
ChatTheme( shapes = CustomStreamShapes, colors = if (isSystemInDarkTheme()) StreamColors.defaultDarkColors() else CustomLightStreamColors, typography = StreamCustomTypography ) { ChannelsScreen( title = stringResource(id = R.string.app_name), onItemClick = { channel -> startActivity(MessagesActivity.getIntent(this, channel.cid)) }, onBackPressed = { finish() } ) }

Here's a breakdown of what the code above does:

  • You use the shapes property to provide your custom shapes.
  • You use colors to provide your custom colors. (You also need to provide colors for dark mode. The above example uses Stream's default dark theme color palette, but you can choose to provide your own colors as well.)
  • You use typography to provide your custom typography.

Finally, you must add your customizations to every ChatTheme instance across your app for your changes to apply. If you fail to apply your customizations to a ChatTheme instance, it will use the default Stream theming instead.

Having applied these customizations to ChannelsScreen and MessageScreen, your app will now look like this:

Customized ChannelsScreen preview

What changed:

  • You can see that the colors of components across the whole app have changed to be blue, as per the colors defined in the CustomLightStreamColors.
  • The corners on the input field, image thumbnails, and messages bubbles are slightly more rounded.
  • The avatars have changed from a circle to a square.
  • And of course, the font for all text components is now Source Code Pro.

Conclusion

In this article, you learned how to add your own custom theming to Stream Compose UI components and how easy it is to provide your own custom values to the different properties. This makes it easy for you to provide your own design system alongside Stream's UI Components.

You can find the full sample project with examples in this tutorial on GitHub.

To learn more about the Compose components and how to use them, take a look at the Compose SDK documentation.

The Compose SDK is still in beta. In case you have any feedback on using the SDK, reach the team on Twitter and on GitHub, or show us what customizations you're building!

And as always, happy coding!

Integrating Video With Your App?
We've built a Video and Audio solution just for you. Check out our APIs and SDKs.
Learn more ->