简体   繁体   中英

Vertical nested scroll in Jetpack Compose

Recently I just crush with a wall while developing a feature in one of my applications which is using 100% compose!. I'm trying to achieve a Vertical Nested Scroll View. My case is that I'm doing a view to edit/create workouts. These are split in different sets which has a list of exercises inside.

The idea is to have a list of Cards which has a list of items (exercises) inside ( Which can be moved using Drag&Drop ).

When I try to build the view it gave me the next error:

java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().

I understand that its not supported, what I'm asking if is there is any kind of workaround I could do. The code looks like:

    LazyColumn(Modifier.fillMaxSize()) {
    itemsIndexed(workout.workoutSets) { setIndex, workoutSet ->

        //State that contains the re-order drag & drop and calls the original list to be modified in my VM
        val state: ReorderableState = rememberReorderState(onMove = { from, to ->
            Grove.e { "Moving from $from to $to" }
            if (to <= workoutSet.exercises.lastIndex) {
                onExerciseMoved(setIndex, from, to)
            }
        })

        LazyColumn(
            state = state.listState,
            modifier = Modifier
                .fillMaxWidth()
                .reorderable(state) //Modifier that allows the drad&drop in this container
        ) {
            itemsIndexed(items = workoutSet.exercises, itemContent = { index, item ->
                Box(
                    Modifier
                        .fillMaxWidth()
                        .draggedItem(state.offset.takeIf { state.index == index }) //The dragged item modifier
                        .scale(if (state.index == null || state.index == index) 1f else .9f)
                ) {
                    CreatorExerciseItemRow(
                        onExerciseClick = { onExerciseClick(item) },
                        execution = item
                    )
                }
            })
        }
    }
}

You can find the Drag & Drop code that I'm using from the library (not mine) here .

Finally to make you an idea of what I want to achieve. The screen looks like:

在此处输入图片说明

The idea is that I can long-press the exercises and drag & drop them on their individual cards. But at the same time allowing the screen to get higher and scroll vertically if needed. Right now I have it working just replacing the first LazyColumn from code above for a repeat/foreach . However has soon has my list grows the view gets hide.

I'm afraid it's not possible.

As I understand, it's enough for you to restrict items to be moved between different sets.

You can take source code of ComposeReorderable and apply needed changes for your task. There's not much code(which I like compose for), so it shouldn't be too hard. Maybe even submit a pull request when you're done, because such functionality may be useful for others.

You can use a single plain list and draw the content based on the type.

First you should transform your data in a single plain list (this is, of course, a pseudo code).

val plainList = mutableListOf<Any>()
worksets.forEach {
    plainList.add(it)
    it.exercises.forEach{
        plainList.add(it)
    }
}

and then, in your LazyColumn you draw the item based on the item type.

LazyColumn(Modifier.fillMaxSize()) {
    items(plainList) { item ->
        when (item) {
            is Workset -> WorkSetHeaderItem(item)
            is Exercise -> ExerciseItem(item)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM