Last active
June 1, 2025 13:33
-
-
Save Skeptick/c5abd489e4ae87d0bea6e184c7377589 to your computer and use it in GitHub Desktop.
Decompose-Router + ChildItems
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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 } | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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