Reputation: 77
I have an Android Compose screen with a Pager and some content below it. I have created a custom scope that accepts two Composable functions that should fill the content in the pager and below it.
I have strong skipping mode enabled for Compose.
I've tried modelling the implementation based on LazyColumn/Row. But some things are just not working.
enum class PeriodType {
DAY,
WEEK,
MONTH,
YEAR,
}
@Composable
fun MyScreen() {
var periodType by remember { mutableStateOf(PeriodType.DAY) }
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column(
modifier = Modifier
.padding(paddingValues = innerPadding)
.padding(16.dp)
) {
Text(text = "Actual period type = $periodType")
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) {
PeriodType.entries.forEach { period ->
Button(onClick = { periodType = period }) { Text(text = period.name) }
}
}
MyCustomPagerScreen {
page1(periodType)
page2(periodType)
}
}
}
}
This is my extension function to implement pages for the pager:
fun MyCustomScope.page1(
periodType: PeriodType,
) {
page(
pagerContent = {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.background(Color.Blue.copy(alpha = 0.5f), CircleShape),
contentAlignment = Alignment.Center,
) { Text("Page 1 TOP") }
},
bottomContent = {
Column(
modifier = Modifier
.height(200.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) { Text("Page 1 BOTTOM. $periodType") }
}
)
}
The actual content that takes in MyCustomScope.() content:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyCustomPagerScreen(content: MyCustomScope.() -> Unit) {
val pages = remember(content) {
MyCustomScopeImpl().apply(content)
}
val pagerState = rememberPagerState(
initialPage = 0,
pageCount = { pages.size },
)
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
) { page ->
pages.getTopContentInPage(page)?.let {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) { it() }
}
}
Spacer(modifier = Modifier.height(12.dp))
Column(
modifier = Modifier.fillMaxWidth()
) {
pages.getBottomContentInPage(pagerState.currentPage)?.let {
Column(content = it)
}
}
}
}
This is the actual code for the Scope:
interface MyCustomScope {
fun page(pagerContent: @Composable () -> Unit, bottomContent: @Composable ColumnScope.() -> Unit)
}
private class MyCustomScopeImpl : MyCustomScope {
private val pages =
mutableListOf<Pair<@Composable () -> Unit, @Composable ColumnScope.() -> Unit>>()
val size: Int
get() = pages.size
fun getTopContentInPage(page: Int): (@Composable () -> Unit)? {
return pages.getOrNull(page)?.first
}
fun getBottomContentInPage(page: Int): (@Composable ColumnScope.() -> Unit)? {
return pages.getOrNull(page)?.second
}
override fun page(
pagerContent: @Composable () -> Unit,
bottomContent: @Composable ColumnScope.() -> Unit,
) {
pages.add(Pair(pagerContent, bottomContent))
}
}
THE ISSUE:
After enabling strong skipping mode the periodType
passed to page1
extension function does not get updated. Using the code above you can try changing the PeriodType selected, but the content below the circle does not get updated. See the GIF below:
GIF LINK
I've tried:
page1
extension inline -> Does NOT workFor a nice API, I'd like to keep the extension functions. I guess it has something to do with strong skipping mode memoizing lambdas, but I don't understand exactly what happens here.
Could someone please shed some light on why this happens? And how can I keep these extension functions, but make it actual reflect changing data?
Upvotes: 0
Views: 174