Skip to content

Instantly share code, notes, and snippets.

@Skeptick
Last active June 1, 2025 13:33
Show Gist options
  • Save Skeptick/c5abd489e4ae87d0bea6e184c7377589 to your computer and use it in GitHub Desktop.
Save Skeptick/c5abd489e4ae87d0bea6e184c7377589 to your computer and use it in GitHub Desktop.
Decompose-Router + ChildItems
@Serializable
sealed class TestItem(val number: Int) {
@Serializable data object Test1 : TestItem(1)
@Serializable data object Test2 : TestItem(2)
@Serializable data object Test3 : TestItem(3)
@Serializable data object Test4 : TestItem(4)
@Serializable data object Test5 : TestItem(5)
}
@Composable
fun Example() {
val listState = rememberLazyListState()
val itemsRouter = rememberItemsRouter {
listOf(TestItem.Test1, TestItem.Test2, TestItem.Test3, TestItem.Test4, TestItem.Test5)
}
LazyColumn(
state = listState
) {
items(itemsRouter) {
val viewModel: TestViewModel = rememberOnRoute(key = "test_${it.number}")
Text(
text = "${viewModel.state.value.text}"
)
}
}
ItemsRouterLifecycleController(
router = itemsRouter,
lazyListState = listState,
itemIndexConverter = { it }
)
}
class ItemsRouter<C : Any> @PublishedApi internal constructor(
val navigation: ItemsNavigation<C>,
@PublishedApi internal val childItems: LazyChildItems<C, RouterContext>,
private val lifecycle: Lifecycle
) : ItemsNavigation<C> by navigation {
val items: State<ChildItems<C, RouterContext>>
get() = childItems.asState(lifecycle)
operator fun get(configuration: C): RouterContext = childItems[configuration]
}
@OptIn(InternalSerializationApi::class)
@Composable
inline fun <reified C : @Serializable Any> rememberItemsRouter(
key: Any = C::class.key,
noinline initialItems: () -> List<C>,
): ItemsRouter<C> {
val routerContext = LocalRouterContext.current
val routerKey = "$key.router"
return remember(routerKey) {
routerContext.getOrCreate(key = routerKey) {
val navigation = ItemsNavigation<C>()
ItemsRouter(
navigation = navigation,
childItems = routerContext.childItems(
source = navigation,
serializer = C::class.serializerOrNull(),
initialItems = {
Items(items = initialItems())
},
key = routerKey,
childFactory = { _, childComponentContext -> RouterContext(childComponentContext) }
),
lifecycle = routerContext.lifecycle
)
}
}
}
inline fun <C : Any> LazyListScope.items(
router: ItemsRouter<C>,
noinline key: ((item: C) -> Any)? = null,
noinline contentType: (item: C) -> Any? = { null },
crossinline itemContent: @Composable LazyItemScope.(item: C) -> Unit
) {
val childItems by router.items
items(
count = childItems.items.size,
key = if (key != null) { index: Int -> key(childItems.items[index]) } else null,
contentType = { index: Int -> contentType(childItems.items[index]) }
) {
val item = childItems.items[it]
val context = router[item]
CompositionLocalProvider(LocalRouterContext provides context) {
itemContent(item)
}
}
}
inline fun <C : Any> LazyGridScope.items(
router: ItemsRouter<C>,
noinline key: ((item: C) -> Any)? = null,
noinline span: (LazyGridItemSpanScope.(item: C) -> GridItemSpan)? = null,
noinline contentType: (item: C) -> Any? = { null },
crossinline itemContent: @Composable LazyGridItemScope.(item: C) -> Unit
) {
val childItems by router.items
items(
count = childItems.items.size,
key = if (key != null) { index: Int -> key(childItems.items[index]) } else null,
span = if (span != null) { index: Int -> span(childItems.items[index]) } else null,
contentType = { index: Int -> contentType(childItems.items[index]) }
) {
val item = childItems.items[it]
val context = router[item]
CompositionLocalProvider(LocalRouterContext provides context) {
itemContent(item)
}
}
}
@Composable
fun <C : Any> ItemsRouterLifecycleController(
router: ItemsRouter<C>,
lazyListState: LazyListState,
itemIndexConverter: (Int) -> Int,
forwardPreloadCount: Int = 0,
backwardPreloadCount: Int = 0,
) {
ChildItemsLifecycleController(
items = router.childItems,
lazyListState = lazyListState,
itemIndexConverter = itemIndexConverter,
forwardPreloadCount = forwardPreloadCount,
backwardPreloadCount = backwardPreloadCount
)
}
@Composable
fun <C : Any> ItemsRouterLifecycleController(
router: ItemsRouter<C>,
lazyGridState: LazyGridState,
itemIndexConverter: (Int) -> Int,
forwardPreloadCount: Int = 0,
backwardPreloadCount: Int = 0,
) {
ChildItemsLifecycleController(
items = router.childItems,
lazyGridState = lazyGridState,
itemIndexConverter = itemIndexConverter,
forwardPreloadCount = forwardPreloadCount,
backwardPreloadCount = backwardPreloadCount
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment