Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.

Commit

Permalink
Add pull to refresh to gallery screen [866] (#934)
Browse files Browse the repository at this point in the history
* Replaced TopAppBar with CenterAlignedTopAppBar, Fix issue #857 App title moves

* Feature pull to refresh GalleryScreen issue[#866]

* Moved isRefreshing state to compose

* Remove unnecessary isLoading state

* Apply suggestions from code review

* Remove unnecessary imports

---------

Co-authored-by: Chris Arriola <[email protected]>
Co-authored-by: Chris Arriola <[email protected]>
  • Loading branch information
3 people authored Dec 27, 2023
1 parent 937fc80 commit dfa9aea
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,34 @@

package com.google.samples.apps.sunflower.compose.gallery

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
Expand All @@ -60,35 +67,66 @@ fun GalleryScreen(
plantPictures = viewModel.plantPictures,
onPhotoClick = onPhotoClick,
onUpClick = onUpClick,
onPullToRefresh = viewModel::refreshData,
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun GalleryScreen(
plantPictures: Flow<PagingData<UnsplashPhoto>>,
onPhotoClick: (UnsplashPhoto) -> Unit = {},
onUpClick: () -> Unit = {},
onPullToRefresh: () -> Unit,
) {
Scaffold(
topBar = {
GalleryTopBar(onUpClick = onUpClick)
},
) { padding ->
val pagingItems: LazyPagingItems<UnsplashPhoto> = plantPictures.collectAsLazyPagingItems()
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.padding(padding),
contentPadding = PaddingValues(all = dimensionResource(id = R.dimen.card_side_margin))

val pullToRefreshState = rememberPullToRefreshState()

if (pullToRefreshState.isRefreshing) {
onPullToRefresh()
}

val pagingItems: LazyPagingItems<UnsplashPhoto> =
plantPictures.collectAsLazyPagingItems()

LaunchedEffect(pagingItems.loadState) {
when (pagingItems.loadState.refresh) {
is LoadState.Loading -> Unit
is LoadState.Error,is LoadState.NotLoading -> {
pullToRefreshState.endRefresh()
}
}
}

Box(
modifier = Modifier
.padding(padding)
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
items(
count = pagingItems.itemCount,
key = pagingItems.itemKey { it }
) { index ->
val photo = pagingItems[index] ?: return@items
PhotoListItem(photo = photo) {
onPhotoClick(photo)

LazyVerticalGrid(
columns = GridCells.Fixed(2),
contentPadding = PaddingValues(all = dimensionResource(id = R.dimen.card_side_margin))
) {
items(
count = pagingItems.itemCount,
key = pagingItems.itemKey { it }
) { index ->
val photo = pagingItems[index] ?: return@items
PhotoListItem(photo = photo) {
onPhotoClick(photo)
}
}
}

PullToRefreshContainer(
modifier = Modifier.align(Alignment.TopCenter),
state = pullToRefreshState
)
}
}
}
Expand All @@ -107,7 +145,7 @@ private fun GalleryTopBar(
navigationIcon = {
IconButton(onClick = onUpClick) {
Icon(
Icons.Filled.ArrowBack,
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
)
}
Expand All @@ -120,7 +158,7 @@ private fun GalleryTopBar(
private fun GalleryScreenPreview(
@PreviewParameter(GalleryScreenPreviewParamProvider::class) plantPictures: Flow<PagingData<UnsplashPhoto>>
) {
GalleryScreen(plantPictures = plantPictures)
GalleryScreen(plantPictures = plantPictures, onPullToRefresh = {})
}

private class GalleryScreenPreviewParamProvider :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.google.samples.apps.sunflower.compose.home

import android.util.Log
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,43 @@ package com.google.samples.apps.sunflower.viewmodels
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.google.samples.apps.sunflower.data.UnsplashPhoto
import com.google.samples.apps.sunflower.data.UnsplashRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class GalleryViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
repository: UnsplashRepository
private val repository: UnsplashRepository
) : ViewModel() {

private var queryString: String? = savedStateHandle["plantName"]

val plantPictures =
repository.getSearchResultStream(queryString ?: "").cachedIn(viewModelScope)

private val _plantPictures = MutableStateFlow<PagingData<UnsplashPhoto>?>(null)
val plantPictures: Flow<PagingData<UnsplashPhoto>> get() = _plantPictures.filterNotNull()

init {
refreshData()
}


fun refreshData() {

viewModelScope.launch {
try {
_plantPictures.value = repository.getSearchResultStream(queryString ?: "").cachedIn(viewModelScope).first()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ktlint = "0.40.0"
ktx = "1.7.0"
lifecycle = "2.6.0-alpha04"
material = "1.8.0-rc01"
material3 = "1.0.1"
material3 = "1.2.0-alpha11"
# @keep
minSdk = "23"
monitor = "1.6.0"
Expand Down

0 comments on commit dfa9aea

Please sign in to comment.