Adopting Jetpack Compose Theming

So Jetpack Compose is now stable and production-ready ๐ŸŽ‰ . With that comes the question, how can I integrate it into my existing project?

Obviously, we cannot hold back the updates to completely migrate all the screens and views to Compose UI. So it has to be done iteratively. So with that in mind, I recently did a small scoping exercise for adopting Compose for my work project (on my stream at twitch.tv/sasikanth, self-plug ๐Ÿ˜›. Feel free to follow). During that, I went over different aspects of adopting Compose to an existing project.

  • Theming

  • Compose in Views

  • Android Views in Compose

  • Navigation

  • Managing State

Today we are going, to begin with, the first and my favourite section theming ๐ŸŽจ ๐Ÿ™Œ. (Over the next few weeks I will publish articles covering the rest of the things)

Using existing Material themes

The first thing I looked into when adopting Compose was to see how much I can get away with the existing theming setup, I didnโ€™t want to re-implement all the Material theming setup from XML to Compose theming.

Fortunately, good folks at Google have a solution for that, MDC-Android Compose Theme Adapter which enables us to reuse the Material themes defined in XML for theming in Compose. Once we have added the dependency, we can use MdcTheme for composable instead of MaterialTheme (Which is the default when using Compose UI Material package). The MdcTheme will create the Compose theme based on the Activity/Context theme.

MdcTheme {
    // MaterialTheme.colors, MaterialTheme.shapes, MaterialTheme.typography
    // will now contain copies of the context's theme
}

One of the things I found useful was, you can customise the generated values from MdcTheme by using the createMdcTheme function and then modifying the colors, shapes and typography and creating a MaterialTheme. This is useful to slowly start migrating the theming implementation to Compose.

val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
var (colors, type, shapes) = createMdcTheme(
    context = context,
    layoutDirection = layoutDirection
)

// Modify colors, type or shapes as required. Then pass them
// through to MaterialTheme...

MaterialTheme(
    colors = colors,
    typography = type,
    shapes = shapes
) {
    // rest of layout
}

Custom attributes

One thing the MdcTheme didnโ€™t generate for us was custom attributes. For that we created Kotlin extension based on the type. For example, we had typography like Body0, Body2Numeric, Headline6Numeric, ButtonBig etc.,

val Typography.body0
  get() = TextStyle(
      fontWeight = FontWeight.Normal,
      fontSize = 18.sp,
      letterSpacing = 0.011.sp,
      lineHeight = 28.sp
  )

val Typography.buttonBig
  get() = button.copy(
      fontSize = 16.sp,
      letterSpacing = 0.0781.sp,
      lineHeight = 20.sp
  )

This helped us get all of our custom attributes that are missed when using MdcTheme.

Wrapping up

While the MDC theme adapter is great and covers the most commonly used theming functionality and attributes from the existing XML theming setup. It does have some limitations, so do take a look at those before using it. If youโ€™re using an AppCompat or non-MDC theme, then use the AppCompat Compose Theme Adapter.

Overall this approach helped us quickly jump into writing Compose code and migrating our fragments/views into Composable functions without needing to spend a bunch of time reimplementing our theming setup.