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,21 +16,27 @@

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
Expand Down Expand Up @@ -59,41 +65,74 @@ fun GalleryScreen(
plantPictures = viewModel.plantPictures,
onPhotoClick = onPhotoClick,
onUpClick = onUpClick,
onPullToRefresh = viewModel::refreshData,
isRefreshing = viewModel.isRefreshing.value
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun GalleryScreen(
plantPictures: Flow<PagingData<UnsplashPhoto>>,
onPhotoClick: (UnsplashPhoto) -> Unit = {},
onUpClick: () -> Unit = {},
onPullToRefresh: () -> Unit,
isRefreshing: Boolean
) {
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){
arriolac marked this conversation as resolved.
Show resolved Hide resolved
LaunchedEffect(Unit){
onPullToRefresh()
}
}
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()
}
}


Box(
modifier = Modifier
.padding(padding)
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
// 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)
val pagingItems: LazyPagingItems<UnsplashPhoto> =
plantPictures.collectAsLazyPagingItems()
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 +150,7 @@ private fun GalleryTopBar(
navigationIcon = {
IconButton(onClick = onUpClick) {
Icon(
Icons.Filled.ArrowBack,
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
)
}
Expand All @@ -124,7 +163,7 @@ private fun GalleryTopBar(
private fun GalleryScreenPreview(
@PreviewParameter(GalleryScreenPreviewParamProvider::class) plantPictures: Flow<PagingData<UnsplashPhoto>>
) {
GalleryScreen(plantPictures = plantPictures)
GalleryScreen(plantPictures = plantPictures, onPullToRefresh = {}, isRefreshing = false)
}

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 @@ -16,22 +16,56 @@

package com.google.samples.apps.sunflower.viewmodels

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
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.delay
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()

private val _isRefreshing = mutableStateOf(false)
val isRefreshing: State<Boolean> get() = _isRefreshing
arriolac marked this conversation as resolved.
Show resolved Hide resolved

init {
refreshData()
}


fun refreshData() {
arriolac marked this conversation as resolved.
Show resolved Hide resolved
_isRefreshing.value = true

viewModelScope.launch {
delay(1000)
arriolac marked this conversation as resolved.
Show resolved Hide resolved
try {
_plantPictures.value = repository.getSearchResultStream(queryString ?: "").cachedIn(viewModelScope).first()
} catch (e: Exception) {
e.printStackTrace()
} finally {
_isRefreshing.value = false
}
}
}
}
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