DEV Community

Cover image for A quick example of how to model complex state in Jetpack compose
Tristan Elliott
Tristan Elliott

Posted on

A quick example of how to model complex state in Jetpack compose

Table of contents

  1. TLDR(Too long didn't read)
  2. Introduction
  3. Real example
  4. The state modeling

My app on the Google play store

TLDR(Too long didn't read)

  • Basically to model any sort of complex Jetpack Compose state do this:
@Composable
fun rememberDraggableActions():ModViewDragState{
    return remember {ModViewDragState()}
}

@Stable
class ModViewDragState(){
  // all the complex state goes in here
}

Enter fullscreen mode Exit fullscreen mode
  • Then you can use rememberDraggableActions() just like a normal remember function:
@Composable
fun TestingComplexState(){
    val state = rememberDraggableActions()
//all complex state can now be accessed through state

}


Enter fullscreen mode Exit fullscreen mode

Introduction

  • This blog post is not going to be an in-depth analysis of every little section of code. Instead, it will act as a simple visual demonstration and example on a alternative way to model compose state

Real example

  • This is my code before the state refactor:
@Composable
fun DraggableText(
    setDragging:(Boolean)->Unit
){
    var offsetX = remember { mutableStateOf(0f) }
    val draggableState = rememberDraggableState { delta ->
        if (offsetX.value >= 300f){
            offsetX.value += delta/10
        }
        else if (offsetX.value <= -300f){
            offsetX.value += delta/10
        }
        else{
            offsetX.value += delta
        }

    }
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .background(Color.Blue)
            .draggable(
                orientation = Orientation.Horizontal,
                onDragStopped = {
                    draggableState.drag(MutatePriority.PreventUserInput) {
                        Animatable(offsetX.value).animateTo(
                            targetValue = 0f,
                            tween(durationMillis = 300)
                        ) {
                            dragBy(value - offsetX.value)
                        }
                    }
                },

                enabled = true,
                state = draggableState
            )
    ){

        CardDemo(
            offsetX.value,
            setDragging={newValue ->setDragging(newValue)}
        )



    }

}


Enter fullscreen mode Exit fullscreen mode
  • after the state refactor:
@Composable
fun DraggableText(
    setDragging:(Boolean)->Unit
){
    val state = rememberDraggableActions()
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .background(Color.Blue)
            .draggable(
                orientation = Orientation.Horizontal,
                onDragStopped = {
                    state.resetOffset()
                },

                enabled = true,
                state = state.draggableState
            )
    ){
        CardDemo(
            state.offset.value,
            setDragging={newValue ->setDragging(newValue)}
        )
    }

}


Enter fullscreen mode Exit fullscreen mode
  • Which as you can see is a lot easier to read and just overall cleaner

The state modeling

  • All I did was just put all the complex state dealing with the draggable into a class marked with the Stable annotation(allowing compose to skip recomposition if needed).
@Stable
class ModViewDragState(){
    val offset: State<Float> get() = offsetX
    private var offsetX = mutableStateOf(0f)


    val draggableState = DraggableState { delta ->
        if (offsetX.value >= 300f){
            offsetX.value += delta/5
        }
        else if (offsetX.value <= -300f){
            offsetX.value += delta/5
        }
        else{
            offsetX.value += delta
        }

    }

    suspend fun resetOffset(){
        draggableState.drag(MutatePriority.PreventUserInput) {
            Animatable(offsetX.value).animateTo(
                targetValue = 0f,
                tween(durationMillis = 300)
            ) {
                dragBy(value - offsetX.value)
            }
        }
    }

}

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)