Set all text style changes in one place in Compose UI similar to styles.xml

2.1k views Asked by At

Previously you could define all of your style changes as a single style in XML which was really convenient especially if you had a lot of different styles. The project I'm currently working on has 50+ of these styles defined.

<!-- styles.xml -->
<style name="title_1">
    <item name="android:textColor">@color/purple_500</item>
    <item name="android:fontFamily">@font</item>
    <item name="android:textSize">18sp</item>
    <item name="android:maxLines">1</item>
    <item name="android:firstBaselineToTopHeight">12sp</item>
    <item name="android:lastBaselineToBottomHeight">9sp</item>
</style>

<!-- hello_world.xml -->
<TextView
    android:id="@+id/hello_world"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello, World!"
    style="@style/title_1" />

While looking to transition to Compose UI, I noticed that certain attributes are separated out. For example baseline can be changed as a modifier while other values can be changed as a text style or a separate parameter in the case of max lines.

// Styles.kt
@Composable
fun Title1(text: String) {
    Text(
        text = text,
        modifier = Modifier.paddingFromBaseline(top = 12.sp, bottom = 9.sp),
        maxLines = 1,
        style = TextStyle(
            color = colorResource(id = R.color.purple_500),
            fontFamily = FontFamily(Font(R.font.podkova_semibold)),
            fontSize = 18.sp
        )
    )
}

// MainActivity.kt
setContent {
    Title1("Hello, World")
}

This makes it more error prone since they're separated into different parameters and it's easy to forget to apply one of them. I know you could lump all the text styles together, which helps, but was wondering if there was a way to replicate what we had in XML and specify all the modifications in one place.

In my initial approach above I tried creating wrappers around the Text composable with all the predefined changes, but wanted a flexible solution that can take all the Text parameters without having 50+ composables with all the same parameters as the Text composable. Wondering if there's something in the Kotlin language that can help simplify this or if this is just the wrong approach to the problem.

// Would like to avoid having to do this 50+ times
@Composable
fun Title1(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = 1,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    Text(
        text = text,
        modifier = modifier
            .paddingFromBaseline(top = 12.sp, bottom = 9.sp),
        color = color,
        fontSize = fontSize,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
        overflow = overflow,
        softWrap = softWrap,
        maxLines = maxLines,
        onTextLayout = onTextLayout,
        style = style.merge(
            TextStyle(
                color = colorResource(id = R.color.purple_500),
                fontFamily = FontFamily(Font(R.font.podkova_semibold)),
                fontSize = 18.sp
            )
        )
    )
}
3

There are 3 answers

0
Deepak Ror On

You Can create it by making Typography.

object MyStyle{

  val myFontFamily = FontFamily(
    Font(R.font.custom_font, FontWeight.Bold)
    )

 
  val myTypepo = Typography(
    bodyLarge = TextStyle(
    fontFamily = myFontFamily,
    fontWeight = FontWeight.Bold,
    fontSize = 16.sp,
    lineHeight = 24.sp,
    letterSpacing = 0.5.sp
    ),)


}

Now OnTextView you Can use it like :

Text(text = title, style = MyStyle.myTypepo.bodyLarge)
0
Jack Lebbos On

You can use this code, when you apply your theme and it will be implemented all across the application

MaterialTheme(
                colorScheme = MyColorScheme,
                typography = MyTypo,
                content = {
                    ProvideTextStyle(value = defaultTextStyle, content)
                }
            )
0
D.J.M. Francis On

I think your initial approach is correct in the current version of Jetpack Compose (1.1.1), but they're currently working on APIs to enable setting this within a Text's TextStyle, so you can keep it all in one place, similar to XML.

See Compose UI 1.2.0-beta01: https://developer.android.com/jetpack/androidx/releases/compose-ui#version_12_2

It looks like they'll be adding a lineHeightStyle property to TextStyle, in addition to platformStyle (which allows setting includeFontPadding to false).