Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Paging with Compose without Paging for Compose

Po
April 28, 2023

Paging with Compose without Paging for Compose

Po

April 28, 2023
Tweet

More Decks by Po

Other Decks in Technology

Transcript

  1. Paging with Compose Without Paging for Compose Po (Po-Hao Chen)

    App Developmentࣨ Sticker Maker AppνʔϜ
  2. Agenda - Sticker Maker and Compose - Paging for Compose

    - Self-implemented Paging management - Appendix
  3. About the Speaker @kuramu1108 • Joined LFK at 2020.11 •

    Former position: • LINE Wallet Android Dev • Current position: • LINE Sticker Maker Client Dev Lead • Hobby: • Coffee, Camp
  4. LINE Sticker Maker LINE ελϯϓϝʔΧʔ • Allow user to create

    and edit stickers • Allow user to submit sticker package to sell
  5. Screen Including Paging Data /** * Returns 10 item after

    [key]. * If [key] is null, return the 10 newest item. */ @GET() suspend fun getPackages( key: Long?, ): List<Package> • Displays the user’s submitted sticker package • Display Newest first • No local data • Originally implemented with self-managed paging with Rx
  6. Why Not? https://developer.android.com/topic/libraries/architecture/paging/v3-overview • A bit complicated setup • We

    need to setup paging data for preview • Need to be exposed as a separate data flow besides the uiState • A bit Overkill
  7. State - Implementation sealed class PagingState<T>( val key: Long?, val

    items: ImmutableList<T>, ) { data class LoadingNextPage<T>( private val currentLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(currentLoadingKey, _items.toImmutableList()) data class FailedToLoadNewPage<T>( private val failedLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(failedLoadingKey, _items.toImmutableList()) data class Loaded<T>( private val nextKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(nextKey, _items.toImmutableList()) }
  8. State - Implementation sealed class PagingState<T>( val key: Long?, val

    items: ImmutableList<T>, ) { data class LoadingNextPage<T>( private val currentLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(currentLoadingKey, _items.toImmutableList()) data class FailedToLoadNewPage<T>( private val failedLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(failedLoadingKey, _items.toImmutableList()) data class Loaded<T>( private val nextKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(nextKey, _items.toImmutableList()) } Current Items
  9. State - Implementation sealed class PagingState<T>( val key: Long?, val

    items: ImmutableList<T>, ) { data class LoadingNextPage<T>( private val currentLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(currentLoadingKey, _items.toImmutableList()) data class FailedToLoadNewPage<T>( private val failedLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(failedLoadingKey, _items.toImmutableList()) data class Loaded<T>( private val nextKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(nextKey, _items.toImmutableList()) }
  10. State - Implementation sealed class PagingState<T>( val key: Long?, val

    items: ImmutableList<T>, ) { data class LoadingNextPage<T>( private val currentLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(currentLoadingKey, _items.toImmutableList()) data class FailedToLoadNewPage<T>( private val failedLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(failedLoadingKey, _items.toImmutableList()) data class Loaded<T>( private val nextKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(nextKey, _items.toImmutableList()) }
  11. State - Implementation sealed class PagingState<T>( val key: Long?, val

    items: ImmutableList<T>, ) { data class LoadingNextPage<T>( private val currentLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(currentLoadingKey, _items.toImmutableList()) data class FailedToLoadNewPage<T>( private val failedLoadingKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(failedLoadingKey, _items.toImmutableList()) data class Loaded<T>( private val nextKey: Long? = null, private val _items: List<T> = emptyList() ) : PagingState<T>(nextKey, _items.toImmutableList()) }
  12. UI State sealed class UiState { /** * Main State

    of the Package Screen. */ data class Main( // ... val stickerPackagePagingState: PagingState<Package> = PagingState.Loaded(), // ... ) : MyPageUiState() }
  13. UI Implementation @Composable fun PackageList( pagingState: PagingState<Package>, loadNextPage: (Long) ->

    Unit, ) { val lazyListState = rememberLazyListState() LazyColumn( state = lazyListState, modifier = Modifier.fillMaxSize(), ) { } }
  14. UI Implementation fun PackageList( pagingState: PagingState<Package>, loadNextPage: (Long) -> Unit,

    ) { val lazyListState = rememberLazyListState() LazyColumn( state = lazyListState, modifier = Modifier.fillMaxSize(), ) { items(pagingState.items) { item -> PackageItem( stickerPackageViewData = item, onItemClicked = onItemClick, ) } } }
  15. LazyColumn( state = lazyListState, modifier = Modifier.fillMaxSize(), ) { items(pagingState.items)

    { item -> PackageItem( stickerPackageViewData = item, onItemClicked = onItemClick, ) } item { PackageListFooter( lazyListState = lazyListState, pagingState = pagingState, loadNextPage = loadNextPage ) } } UI Implementation Only enters the composition when the items reaches its final item
  16. UI Implementation @Composable private fun PackageListFooter( lazyListState: LazyListState, pagingState: PagingState<Package>,

    loadNextPage: (Long) -> Unit, ) { when (pagingState) { is PagingState.Loaded -> { // Start loading next page when footer is reached LaunchedEffect(pagingState.key) { pagingState.key?.let(loadNextPage) } } is PagingState.FailedToLoadNewPage -> { PackageErrorFooter( onReloadClick = { pagingState.key?.let(loadNextPage) } ) LaunchedEffect(pagingState.key) { lazyListState.animateScrollToItem(pagingState.items.size + 1) } } is PagingState.LoadingNextPage -> { PackageLoadingFooter() LaunchedEffect(pagingState.key) { lazyListState.animateScrollToItem(pagingState.items.size + 1) } } } }
  17. Conclusion - For more complicated cases or building from scratch,

    we could consider Jetpack paging - Self implemented paging is easy with the nature of Compose