DEV Community

Cover image for Writing cleaner Jetpack Compose code with the Twitter Compose Ruleset
Tristan Elliott
Tristan Elliott

Posted on

Writing cleaner Jetpack Compose code with the Twitter Compose Ruleset

Table of contents

  1. Introduction
  2. Code we will be refactoring
  3. Mistake 1
  4. Mistake 2
  5. Mistake 3
  6. Mistake 4
  7. Refactored code

Introduction

  • Lately I have been trying to find ways to refactor my Jetpack compose functions and thankfully I stumbled across the Twitter Compose Ruleset, which can be found HERE.
  • What the Twitter Compose Ruleset actually is, is a set of custom ktlint rules to ensure that your composables don't fall into common pitfalls, that might be easy to miss in code reviews. You can even find a tutorial on how to set it up with ktlin HERE. But for right now we are going to focus on hand refactoring some code from my latest app.

My latest app for cattle farmers in North America

The GitHub

The code we will be refactoring:

  • The code below is the code we will be refactoring by applying the rules found HERE
@Composable
fun CalendarDock(viewModel: EditCalfViewModel){
val calfDate = viewModel.uiState.value.birthDate!!
    val convertedDate = calfDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    val selectedDate = remember {
        mutableStateOf<LocalDate?>(convertedDate)
    }
    val calendarState = rememberSheetState()

    CalendarDialog(
        state = calendarState,
        config = CalendarConfig(
            monthSelection = true,
            yearSelection = true
        ),
        selection = CalendarSelection.Date(selectedDate = selectedDate.value){ newDate ->

            selectedDate.value = newDate
            viewModel.updateDate(newDate)

        }
    )
    Row(
        horizontalArrangement = Arrangement.SpaceBetween) {
        OutlinedTextField(
            //state
            enabled = false,
            value = "Date born: "+ selectedDate.value.toString(),
            onValueChange = {  },
            //style
            singleLine = true,
            placeholder = {
                Text(text = "Date", fontSize = 20.sp)
            },
            modifier = Modifier
                .fillMaxWidth(),
            textStyle = TextStyle(fontSize = 20.sp),


            )
    }


}

Enter fullscreen mode Exit fullscreen mode

Mistake 1: Passing a ViewModel as parameter

  • Notice in the signature of the compose function I am passing in a viewModel as a parameter:
fun CalendarDock(viewModel: EditCalfViewModel)
Enter fullscreen mode Exit fullscreen mode
  • As stated in the RULES, instead of passing a viewModel, we should pass in the relevant data or an optional lambda

Mistake 2: No modifier parameter

  • Notice how this code above has no modifier parameter. This is a serious red flag when writing compose code. Every composable function (yes I do mean literally every compose function) should have a modifier parameter. The main reason for this is because it promotes flexibility and code reuse but if you want a deeper understanding I recommend you read the blog post HERE

Mistake 3: Position of the modifier parameter

  • When adding the modifier parameter we should make sure to add it in the correct position. When adding parameters to compose functions, the mandatory parameters come first and then comes the modifier parameter. The modifier parameter should occupy the first optional parameter slot to set a consistent exception for other developers. This means as our project grows and we add more people to our team, they can expect a consistent programming experience.

Mistake 4: Modifiers should have default parameters

  • as the rules state:
    Composables that accept a Modifier as a parameter to be applied to the whole component represented by the composable function should name the parameter modifier and assign the parameter a default value of Modifier.

  • Long story should if you have a modifier parameter you should give it a default value of Modifier

Refactored code:

  • So with all these rules applied our refactored code now looks like this:
@Composable
fun CalendarDock2(dateBorn: Date,selectedAction:(LocalDate)->Unit,modifier: Modifier = Modifier){

    val convertedDate = dateBorn.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    val selectedDate = remember { mutableStateOf<LocalDate?>(convertedDate) }
    val calendarState = rememberSheetState()

    CalendarDialog(
        state = calendarState,
        config = CalendarConfig(
            monthSelection = true,
            yearSelection = true
        ),
        selection = CalendarSelection.Date(selectedDate = selectedDate.value){ newDate ->

            selectedDate.value = newDate
            selectedAction(newDate)

        }
    )
    Row(
        horizontalArrangement = Arrangement.SpaceBetween,
    ) {
        OutlinedTextField(
            //state
            enabled = false,
            value = "Date born: "+ selectedDate.value.toString(),
            onValueChange = {  },
            //style
            singleLine = true,
            placeholder = {
                Text(text = "Date", fontSize = 20.sp)
            },
            modifier = modifier,
            textStyle = TextStyle(fontSize = 20.sp),


            )
    }


}


Enter fullscreen mode Exit fullscreen mode

Conclusion

  • Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.

Top comments (0)