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

Feature pull to refresh gallery screen issue [866] #934

Merged
merged 12 commits into from
Dec 27, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,38 @@

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.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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 @@ -59,41 +70,85 @@ 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))
) {
// TODO update this implementation once paging Compose supports LazyGridScope
// See: https://issuetracker.google.com/issues/178087310
items(
count = pagingItems.itemCount,
key = { index ->
val photo = pagingItems[index]
"${ photo?.id ?: ""}${index}"

var isRefreshing by remember { mutableStateOf(false) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra isRefreshing state is not needed. pullToRefreshState should be the source of truth instead of having 2 different "is refreshing" states.

val pullToRefreshState = rememberPullToRefreshState()

if (pullToRefreshState.isRefreshing){
arriolac marked this conversation as resolved.
Show resolved Hide resolved
LaunchedEffect(Unit){
onPullToRefresh()
isRefreshing = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this per comment above

}
}
LaunchedEffect(isRefreshing){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block of code is not needed

if (!isRefreshing){
pullToRefreshState.endRefresh()
}
}

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

LaunchedEffect(pagingItems.loadState) {
when (pagingItems.loadState.refresh) {
is LoadState.Loading -> Unit
is LoadState.Error,is LoadState.NotLoading -> {
isRefreshing = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can call pullToRefreshState.endRefresh() directly here

}
) { index ->
val photo = pagingItems[index] ?: return@items
PhotoListItem(photo = photo) {
onPhotoClick(photo)
}
}



Box(
modifier = Modifier
.padding(padding)
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {

LazyVerticalGrid(
columns = GridCells.Fixed(2),
contentPadding = PaddingValues(all = dimensionResource(id = R.dimen.card_side_margin))
) {
// TODO update this implementation once paging Compose supports LazyGridScope
// See: https://issuetracker.google.com/issues/178087310
items(
count = pagingItems.itemCount,
key = { index ->
val photo = pagingItems[index]
"${photo?.id ?: ""}${index}"
}
) { index ->
val photo = pagingItems[index] ?: return@items
PhotoListItem(photo = photo) {
onPhotoClick(photo)
}
}
}

PullToRefreshContainer(
modifier = Modifier.align(Alignment.TopCenter),
state = pullToRefreshState
)
}


}
}

Expand All @@ -111,7 +166,7 @@ private fun GalleryTopBar(
navigationIcon = {
IconButton(onClick = onUpClick) {
Icon(
Icons.Filled.ArrowBack,
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
)
}
Expand All @@ -124,7 +179,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 Expand Up @@ -73,7 +72,8 @@ fun HomeScreen(
onPlantClick: (Plant) -> Unit = {},
viewModel: PlantListViewModel = hiltViewModel()
) {
val pagerState = rememberPagerState()
val pages : Array<SunflowerPage> = SunflowerPage.values()
val pagerState = rememberPagerState(pageCount = {pages.count()})
arriolac marked this conversation as resolved.
Show resolved Hide resolved
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()

Scaffold(
Expand All @@ -89,7 +89,8 @@ fun HomeScreen(
HomePagerScreen(
onPlantClick = onPlantClick,
pagerState = pagerState,
Modifier.padding(top = contentPadding.calculateTopPadding())
Modifier.padding(top = contentPadding.calculateTopPadding()),
pages = pages
)
}
}
Expand All @@ -100,7 +101,7 @@ fun HomePagerScreen(
onPlantClick: (Plant) -> Unit,
pagerState: PagerState,
modifier: Modifier = Modifier,
pages: Array<SunflowerPage> = SunflowerPage.values()
pages: Array<SunflowerPage>
) {
Column(modifier) {
val coroutineScope = rememberCoroutineScope()
Expand Down Expand Up @@ -129,7 +130,6 @@ fun HomePagerScreen(
// Pages
HorizontalPager(
modifier = Modifier.background(MaterialTheme.colorScheme.background),
pageCount = pages.size,
state = pagerState,
verticalAlignment = Alignment.Top
) { index ->
Expand Down Expand Up @@ -202,7 +202,8 @@ private fun HomeScreenPreview() {
SunflowerTheme {
HomePagerScreen(
onPlantClick = {},
pagerState = PagerState(),
pagerState = rememberPagerState(pageCount = {2}),
pages = SunflowerPage.values()
)
}
}
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() {
arriolac marked this conversation as resolved.
Show resolved Hide resolved

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