Migrate to ListDetailPaneScaffold

Change-Id: I5dfc70a595838ff39c40d7063f86bb8ceb810d4b
camal/after
Miłosz Moczkowski 1 year ago
parent 77356ebc63
commit 321d52cb1c

@ -73,6 +73,7 @@ dependencies {
implementation(project(":feature:interests"))
implementation(project(":feature:topic"))
implementation("androidx.compose.material3:material3-adaptive-android:1.0.0-SNAPSHOT")
implementation(libs.accompanist.systemuicontroller)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)

@ -16,58 +16,50 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.interestsRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicIdArg
@OptIn(
ExperimentalLayoutApi::class,
ExperimentalMaterial3AdaptiveApi::class,
)
@Composable
fun NiaApp() {
Scaffold { padding ->
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = interestsRoute,
modifier = Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(WindowInsets.safeDrawing),
) {
composable(route = interestsRoute) {
InterestsRoute(onTopicClick = navController::navigateToTopic)
}
composable(
route = "topic_route/{$topicIdArg}",
arguments = listOf(
navArgument(topicIdArg) { type = NavType.StringType },
),
) {
val layoutState = rememberListDetailPaneScaffoldState(
initialFocus = ListDetailPaneScaffoldRole.List
)
var selectedTopicId: String? by remember { mutableStateOf(null) }
ListDetailPaneScaffold(
layoutState = layoutState,
listPane = {
InterestsRoute(onTopicClick = { topicId ->
selectedTopicId = topicId
layoutState.navigateTo(ListDetailPaneScaffoldRole.Detail)
})
},
detailPane = {
selectedTopicId?.let {
TopicRoute(
onBackClick = navController::popBackStack,
onTopicClick = navController::navigateToTopic
topicId = it,
onTopicClick = {},
onBackClick = {
layoutState.navigateBack(true)
layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
},
//modifier = Modifier.preferredWidth(400.dp)
)
}
}
}
},
)
}

@ -36,6 +36,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -61,11 +62,16 @@ import com.google.samples.apps.nowinandroid.feature.topic.R.string
@Composable
fun TopicRoute(
topicId: String,
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: TopicViewModel = hiltViewModel(),
) {
LaunchedEffect(topicId) {
viewModel.onTopicIdChange(topicId)
}
val topicUiState: TopicUiState by viewModel.topicUiState.collectAsStateWithLifecycle()
val newsUiState: NewsUiState by viewModel.newUiState.collectAsStateWithLifecycle()

@ -32,48 +32,67 @@ import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicArgs
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class TopicViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder)
private val _topicId: MutableStateFlow<String?> = MutableStateFlow(null)
private val topicId: SharedFlow<String> = _topicId.filterNotNull().shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
)
val topicUiState: StateFlow<TopicUiState> = topicUiState(
topicId = topicArgs.topicId,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
).stateIn(
val topicUiState: StateFlow<TopicUiState> = topicId.flatMapLatest { id ->
topicUiState(
topicId = id,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TopicUiState.Loading,
)
val newUiState: StateFlow<NewsUiState> = newsUiState(
topicId = topicArgs.topicId,
userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository,
).stateIn(
val newUiState: StateFlow<NewsUiState> = topicId.flatMapLatest { id ->
newsUiState(
topicId = id,
userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsUiState.Loading,
)
fun onTopicIdChange(topicId: String) {
viewModelScope.launch {
_topicId.value = topicId
}
}
fun followTopicToggle(followed: Boolean) {
viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(topicArgs.topicId, followed)
userDataRepository.toggleFollowedTopicId(topicId.first(), followed)
}
}

@ -28,6 +28,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://androidx.dev/snapshots/builds/10531429/artifacts/repository") }
}
}
rootProject.name = "nowinandroid"

Loading…
Cancel
Save