A quick example of how to model complex state in Jetpack compose
Tags: mobile, android, kotllin, tristan
Published: 2024-03-21 18:18:34

### Table of contents
1. [TLDR(Too long didn't read)](#tldr)
2. [Introduction](#intro)
3. [Real example](#real)
4. [The state modeling](#state)

### My app on the Google play store
- [The app](https://play.google.com/store/apps/details?id=elliott.software.clicker)


### TLDR(Too long didn't read) <a name="tldr">
- Basically to model any sort of complex Jetpack Compose state do this:
```kotlin
@Composable
fun rememberDraggableActions():ModViewDragState{
    return remember {ModViewDragState()}
}

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

```
- Then you can use `rememberDraggableActions()` just like a normal remember function:

```kotlin
@Composable
fun TestingComplexState(){
    val state = rememberDraggableActions()
//all complex state can now be accessed through state
    
}


```
### Introduction <a name="intro">
- 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 <a name="real">
 - This is my code before the state refactor:
```kotlin
@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)}
        )



    }

}


```
- after the state refactor:
```kotlin
@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)}
        )
    }

}


```
- Which as you can see is a lot easier to read and just overall cleaner

### The state modeling <a name="state">
- All I did was just put all the complex state dealing with the draggable into a class marked with the [Stable](https://developer.android.com/jetpack/compose/performance/stability) annotation(allowing compose to skip recomposition if needed).

```kotlin
@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)
            }
        }
    }
    
}

```

### 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](https://twitter.com/AndroidTristan).