Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create custom LazyColumn/LazyList to add custom items in between

I'm trying to create a custom LazyColumn/LazyRowto create a generic list that shows ads in different types: at the top, bottom or combined (each 3 items for instance).

I've just copied the implementation of LazyColumn and created my custom, adding some new parameters.

@Composable
fun CustomLazyColumnWithAds(
    modifier: Modifier = Modifier,
    state: LazyListState = rememberLazyListState(),
    contentPadding: PaddingValues = PaddingValues(0.dp),
    reverseLayout: Boolean = false,
    verticalArrangement: Arrangement.Vertical =
        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
    userScrollEnabled: Boolean = true,
    adsPosition: AdsPosition,
    content: LazyListScope.() -> Unit
) {
    LazyColumn(
        modifier = modifier,
        state = state,
        contentPadding = contentPadding,
        reverseLayout = reverseLayout,
        verticalArrangement = verticalArrangement,
        horizontalAlignment = horizontalAlignment,
        flingBehavior = flingBehavior,
        userScrollEnabled = userScrollEnabled
    ) {
        when (adsPosition) {
            is AdsPosition.Top -> {
                item {
                    Ad()
                }
                content()
            }

            is AdsPosition.Combined -> {
                content()
            }

            is AdsPosition.Bottom -> {
                content()
                item {
                    Ad()
                }
            }
        }
    }
}

The problem doing this is that in case one list is using stickyHeader wouldn't work neither if I have one or more Footer for instance legal terms and an animation at the bottom of the list, so I'm looking for more ways of doing this, I need to do this because I need to implement to a bunch of lists of my app this and Ads are from my backend so I need to do an extra-call to get this ads before creating this list.

Any idea of how can I improve this?

Problems I've faced : If I add items in between I lost the index, so if other features need a callback perhaps the index is wrong. If feature have more than one header or footer I can not determine the order.

TL;DR

I'd like to have a LazyColumn, Column, LazyRow, Row, whatever so other feature can configure it as : offerPosition = Position.TOP so the list is smart enough to add an Offer there, the problem is that I'd have to create a system to let the feature owner decide which one is the first position, because maybe the first position is something super important and it must be below of it, so that's why the idea I've made for the first iteration won't work because it's just the happy path.

Does it make sense to create this? Is there any other way to do this? I'm trying to add Offers in other features list and I want the less work for them.

Example of how should work this list :

enter image description here

The image represents the happy path, but the list may differ between features, so the important thing here is that this CustomList should let the user choose if they want a multiple footer or not, because there are features that have 2 footer (last items) and perhaps even if they put OfferPosition.BOTTOM they want it before the footer or just after.

Here is also important that I wouldn't like to touch/change a lot the implementaiton of the other features, so the way they introduce their items shouldn't change at all, because they order it and chose the position of each section. It's important that in the screen each section could be like different types of flights for instance and they want it to split between LazyRow.

Here the question is, what's better :

  1. Create a custom list that ads the items as the same config of the user, if they want top it goes to top.
  2. Create an Offer composable that they can get and choose where they want to add it.

Also I'm afraid of how will affect adding this "Offer" to the index of their list when tapping on an item that is not an Offer.

like image 505
StuartDTO Avatar asked Oct 29 '25 02:10

StuartDTO


1 Answers

instead of encapsulating the entire LazyColumn, consider creating a utility function that modifies the LazyListScope content dynamically. This utility function can inject the ads into the content provided based on the desired position.

@Composable
fun LazyListScope.InjectAds(
    adsPosition: AdsPosition,
    content: LazyListScope.() -> Unit
) {
    when (adsPosition) {
        is AdsPosition.Top -> {
            item { Ad() }
            content()
        }
        is AdsPosition.Combined -> {
            var index = 0
            content().also {
                if (index % 3 == 0) {
                    item { Ad() }
                }
                index++
            }
        }
        is AdsPosition.Bottom -> {
            content()
            item { Ad() }
        }
    }
}

@Composable
fun MyLazyColumnWithAds(
    modifier: Modifier = Modifier,
    adsPosition: AdsPosition,
    content: LazyListScope.() -> Unit
) {
    LazyColumn(modifier = modifier) {
        InjectAds(adsPosition) {
            content()
        }
    }
}

MyLazyColumnWithAds(adsPosition = AdsPosition.Combined) {
    items(listOfItems) { item ->
        // Render each item
    }

    stickyHeader {
        // Header content
    }

    item {
        // Footer or other items
    }
}

update

maybe you can try the following create an new item list

sealed class ItemHolder {
    data class Content(val data: YourDataModel) : ItemHolder()
    data class Ad(val adData: AdDataModel) : ItemHolder()
    data class Header(val headerData: HeaderDataModel) : ItemHolder()
    //... add more types if needed
}

in your lazy column check whether is a content, ad or header. order the list according to your needs

prepare your list

fun prepareListWithAds(
    originalList: List<YourDataModel>,
    adsPosition: AdsPosition
): List<ItemHolder> {
    val resultList = mutableListOf<ItemHolder>()
    when (adsPosition) {
        is AdsPosition.Top -> {
            resultList.add(ItemHolder.Ad(AdDataModel()))
            resultList.addAll(originalList.map { ItemHolder.Content(it) })
        }
        is AdsPosition.Bottom -> {
            resultList.addAll(originalList.map { ItemHolder.Content(it) })
            resultList.add(ItemHolder.Ad(AdDataModel()))
        }
        is AdsPosition.Combined -> {
            // Add your logic for combined ads
        }
    }
    return resultList
}

then use it

val itemsWithAds = prepareListWithAds(myOriginalList, AdsPosition.Top)

LazyColumn {
    items(itemsWithAds) { item ->
        when (item) {
            is ItemHolder.Content -> { /* Display content */ }
            is ItemHolder.Ad -> { Ad() }
            is ItemHolder.Header -> { /* Display header */ }
            // ... handle other cases
        }
    }
}

you can handle the click events for each type of the itemHolder

like image 174
Hezy Ziv Avatar answered Oct 31 '25 16:10

Hezy Ziv