-
Notifications
You must be signed in to change notification settings - Fork 10
Update 1.0.0 #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Update 1.0.0 #66
Changes from all commits
52e1ae4
93a64fb
3f57c8f
d852165
8ee9547
d2f34e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,15 +15,14 @@ This is a Kotlin MultiPlatform library that contains pagination logic for kotlin | |
| - [License](#license) | ||
|
|
||
| ## Features | ||
| - **Pagination** implements pagination logic for the data from abstract `PagedListDataSource`. | ||
| - Managing a data loading process using **Pagination** asynchronous functions: `loadFirstPage`, `loadNextPage`, | ||
| `refresh` or their duplicates with `suspend` modifier. | ||
| - Observing states of **Pagination** using `LiveData` from **moko-mvvm**. | ||
| - **Pagination** implements pagination logic for the data from `PagingDataSource`. | ||
| - Managing data loading using `loadFirstPage`, `reloadFirstPage`, `loadNextPage`, `refresh`. | ||
| - Observing states using `StateFlow` and `RemoteState`. | ||
|
|
||
| ## Requirements | ||
| - Gradle version 6.8+ | ||
| - Gradle 8.10+ | ||
| - Android API 16+ | ||
| - iOS version 11.0+ | ||
| - iOS 11.0+ | ||
|
|
||
| ## Installation | ||
| root build.gradle | ||
|
|
@@ -35,10 +34,11 @@ allprojects { | |
| } | ||
| ``` | ||
|
|
||
| project build.gradle | ||
| ```groovy | ||
| project build.gradle.kts | ||
| ```kotlin | ||
| dependencies { | ||
| commonMainApi("dev.icerock.moko:paging:0.7.1") | ||
| commonMainApi("dev.icerock.moko:paging:0.8.0") | ||
| commonMainApi("dev.icerock.moko:remotestate:0.1.0") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's should be another lib at all |
||
| } | ||
| ``` | ||
|
|
||
|
|
@@ -49,30 +49,23 @@ You can use **Pagination** in `commonMain` sourceset. | |
| **Pagination** creation: | ||
|
|
||
| ```kotlin | ||
| val pagination: Pagination<Int> = Pagination( | ||
| parentScope = coroutineScope, | ||
| dataSource = LambdaPagedListDataSource { currentList -> | ||
| extrenalRepository.loadPage(currentList) | ||
| }, | ||
| comparator = Comparator { a: Int, b: Int -> | ||
| a - b | ||
| val pagination: Pagination<Item> = Pagination( | ||
| dataSource = PageSizePagingDataSource( | ||
| pageSize = 20, | ||
| calculateNextPage = { currentList -> | ||
| // your logic for calculating the next page | ||
| }, | ||
| nextPageListener = { result: Result<List<Int>> -> | ||
| if (result.isSuccess) { | ||
| println("Next page successful loaded") | ||
| } else { | ||
| println("Next page loading failed") | ||
| } | ||
| }, | ||
| refreshListener = { result: Result<List<Int>> -> | ||
| if (result.isSuccess) { | ||
| println("Refresh successful") | ||
| } else { | ||
| println("Refresh failed") | ||
| } | ||
| }, | ||
| initValue = listOf(1, 2, 3) | ||
| ) | ||
| loadPage = { page, pageSize -> repository.load(page = page, pageSize = pageSize) } | ||
| ), | ||
| itemKey = { item -> item.id }, | ||
| refreshStrategy = RefreshStrategy.ReplaceEverything, | ||
| nextPageListener = { result -> | ||
| result.onFailure { println("Next page loading failed: $it") } | ||
| }, | ||
| refreshListener = { result -> | ||
| result.onFailure { println("Refresh failed: $it") } | ||
| } | ||
| ) | ||
| ``` | ||
|
|
||
| Managing data loading: | ||
|
|
@@ -94,19 +87,20 @@ pagination.setData(itemsList) | |
| Observing **Pagination** states: | ||
|
|
||
| ```kotlin | ||
| // Observing the state of the pagination | ||
| pagination.state.addObserver { state: ResourceState<List<ItemClass>, Throwable> -> | ||
| // ... | ||
| } | ||
| val state: RemoteState<PagingState<YourItem>, Throwable> by viewModel.pagination.state.collectAsState() | ||
|
|
||
| // Observing the next page loading process | ||
| pagination.nextPageLoading.addObserver { isLoading: Boolean -> | ||
| // ... | ||
| } | ||
| when (state) { | ||
| RemoteState.Loading -> { | ||
| // ... | ||
| } | ||
|
|
||
| // Observing the refresh process | ||
| pagination.refreshLoading.addObserver { isRefreshing: Boolean -> | ||
| // ... | ||
| is RemoteState.Error -> { | ||
| // ... | ||
| } | ||
|
|
||
| is RemoteState.Success<PagingState<YourItem>> -> { | ||
| // ... | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,33 @@ | ||
| [versions] | ||
| kotlinVersion = "1.6.20" | ||
| androidAppCompatVersion = "1.2.0" | ||
| androidLifecycleVersion = "2.1.0" | ||
| androidCoreTestingVersion = "2.1.0" | ||
| recyclerViewVersion = "1.1.0" | ||
| swipeRefreshLayoutVersion = "1.1.0" | ||
| ktorClientVersion = "2.0.0" | ||
| coroutinesVersion = "1.6.0-native-mt" | ||
| mokoMvvmVersion = "0.12.0" | ||
| mokoResourcesVersion = "0.18.0" | ||
| mokoUnitsVersion = "0.8.0" | ||
| mokoPagingVersion = "0.7.2" | ||
| kotlinVersion = "2.1.10" | ||
| lifecycleVersion = "2.8.7" | ||
| androidCoreTestingVersion = "2.2.0" | ||
| composeBomVersion = "2025.09.00" | ||
| activityComposeVersion = "1.10.1" | ||
| ktorClientVersion = "3.4.0" | ||
| coroutinesVersion = "1.10.2" | ||
| mokoMvvmVersion = "0.16.1" | ||
| mokoPagingVersion = "0.8.0" | ||
|
|
||
| [libraries] | ||
| appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } | ||
| recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerViewVersion" } | ||
| lifecycle = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "androidLifecycleVersion" } | ||
| swipeRefreshLayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swipeRefreshLayoutVersion" } | ||
| lifecycleViewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleVersion" } | ||
| lifecycleRuntimeCompose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleVersion" } | ||
| activityCompose = { module = "androidx.activity:activity-compose", version.ref = "activityComposeVersion" } | ||
| composeBom = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" } | ||
| composeUi = { module = "androidx.compose.ui:ui" } | ||
| composeUiTooling = { module = "androidx.compose.ui:ui-tooling" } | ||
| composeUiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" } | ||
| composeMaterial3 = { module = "androidx.compose.material3:material3" } | ||
| coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" } | ||
| coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutinesVersion" } | ||
| ktorClient = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientVersion" } | ||
| mokoResources = { module = "dev.icerock.moko:resources", version.ref = "mokoResourcesVersion" } | ||
| mokoUnits = { module = "dev.icerock.moko:units", version.ref = "mokoUnitsVersion" } | ||
| mokoUnitsDataBinding = { module = "dev.icerock.moko:units-databinding", version.ref = "mokoUnitsVersion" } | ||
| mokoMvvmLiveData = { module = "dev.icerock.moko:mvvm-livedata", version.ref = "mokoMvvmVersion" } | ||
| mokoMvvmState = { module = "dev.icerock.moko:mvvm-state", version.ref = "mokoMvvmVersion" } | ||
| kotlinTestJUnit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinVersion" } | ||
| mokoMvvmFlow = { module = "dev.icerock.moko:mvvm-flow", version.ref = "mokoMvvmVersion" } | ||
| androidCoreTesting = { module = "androidx.arch.core:core-testing", version.ref = "androidCoreTestingVersion" } | ||
| ktorClientMock = { module = "io.ktor:ktor-client-mock", version.ref = "ktorClientVersion" } | ||
|
|
||
| kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" } | ||
| androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.0.4" } | ||
| googleServicesGradlePlugin = { module = "com.google.gms:google-services", version = "4.3.8" } | ||
| firebaseGradlePlugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version = "2.2.0" } | ||
| mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" } | ||
| mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.1" } | ||
| kotlinSerializationGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlinVersion" } | ||
| mokoUnitsGeneratorGradlePlugin = { module = "dev.icerock.moko:units-generator", version.ref = "mokoUnitsVersion" } | ||
| androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "8.6.1" } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why so high?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All Compose dependencies (1.9.1) and lifecycle-runtime-compose (2.9.0) require AGP ≥ 8.6.0 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Project doesn't need AGP 8.6, 8.1 more comfortable version, you need separate sample and library versions |
||
| mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.6.0" } | ||
| mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.4" } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| #Thu Feb 19 11:14:28 MSK 2026 | ||
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip | ||
| zipStoreBase=GRADLE_USER_HOME | ||
| zipStorePath=wrapper/dists |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| * Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. | ||
| */ | ||
|
|
||
| plugins { | ||
| id("dev.icerock.moko.gradle.multiplatform.mobile") | ||
| id("dev.icerock.moko.gradle.publication") | ||
| id("dev.icerock.moko.gradle.detekt") | ||
| id("org.jetbrains.kotlin.plugin.compose") | ||
| } | ||
|
|
||
| kotlin { | ||
| jvm() | ||
| } | ||
|
|
||
| android { | ||
| namespace = "dev.icerock.moko.paging.compose" | ||
|
|
||
| compileSdk = 35 | ||
|
|
||
| defaultConfig { | ||
| minSdk = 21 | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| commonMainApi(projects.paging) | ||
| implementation(libs.composeMaterial3) | ||
| androidMainImplementation(platform(libs.composeBom)) | ||
| androidMainImplementation(libs.composeUi) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /* | ||
| * Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. | ||
| */ | ||
|
|
||
| package dev.icerock.moko.paging.compose | ||
|
|
||
| import androidx.compose.foundation.layout.BoxScope | ||
| import androidx.compose.foundation.lazy.LazyListLayoutInfo | ||
| import androidx.compose.foundation.lazy.LazyListState | ||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||
| import androidx.compose.material3.pulltorefresh.PullToRefreshBox | ||
| import androidx.compose.material3.pulltorefresh.PullToRefreshState | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.LaunchedEffect | ||
| import androidx.compose.runtime.derivedStateOf | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.ui.Modifier | ||
| import dev.icerock.moko.paging.PagingState | ||
|
|
||
| /** | ||
| * Composable wrapper for paged content with pull-to-refresh and auto-load-next. | ||
| * | ||
| * @param state paging state with items and loading flags | ||
| * @param onLoadNextRequested callback for requesting the next page | ||
| * @param onRefresh callback for pull-to-refresh | ||
| * @param listState LazyListState used to detect proximity to the end | ||
| * @param pullToRefreshState PullToRefreshState for the indicator | ||
| * @param pullToRefreshIndicator composable for the pull-to-refresh indicator UI | ||
| * @param modifier modifier applied to the PullToRefreshBox | ||
| * @param content renders the list items | ||
| */ | ||
| @Suppress("LongMethod", "MagicNumber") | ||
| @OptIn(ExperimentalMaterial3Api::class) | ||
| @Composable | ||
| fun <T> PagingContent( | ||
| state: PagingState<T>, | ||
| onLoadNextRequested: () -> Unit, | ||
| onRefresh: () -> Unit, | ||
| listState: LazyListState, | ||
| pullToRefreshState: PullToRefreshState, | ||
| pullToRefreshIndicator: @Composable BoxScope.() -> Unit, | ||
| modifier: Modifier = Modifier, | ||
| content: @Composable (List<T>) -> Unit, | ||
| ) { | ||
| val shouldLoadMore: Boolean by remember { | ||
| derivedStateOf { | ||
| val layoutInfo: LazyListLayoutInfo = listState.layoutInfo | ||
| val lastVisibleIndex: Int? = layoutInfo.visibleItemsInfo.lastOrNull()?.index | ||
| val totalCount: Int = layoutInfo.totalItemsCount | ||
| val threshold: Int = PAGING_LOAD_THRESHOLD | ||
| val isCloseToEnd: Boolean = lastVisibleIndex != null && | ||
| lastVisibleIndex >= totalCount - threshold | ||
| lastVisibleIndex != 0 && !state.isEndOfList && isCloseToEnd | ||
| } | ||
| } | ||
|
|
||
| LaunchedEffect(shouldLoadMore) { | ||
| if (shouldLoadMore && !state.isNextPageLoading) { | ||
| onLoadNextRequested() | ||
| } | ||
| } | ||
|
|
||
| PullToRefreshBox( | ||
| modifier = modifier, | ||
| isRefreshing = state.isRefreshing, | ||
| onRefresh = onRefresh, | ||
| state = pullToRefreshState, | ||
| indicator = pullToRefreshIndicator | ||
| ) { | ||
| content.invoke(state.items) | ||
| } | ||
| } | ||
|
|
||
| private const val PAGING_LOAD_THRESHOLD: Int = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why so highest version???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supports AGP Version 8.6.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
library must support a wide range of versions (e.g., ensuring compatibility with AGP versions older than 8.6.1)