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.
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 :

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 :
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With