Wrap DetailScreen into a BottomSheetScaffold.

This commit is contained in:
Thomas Andres Gomez 2022-04-24 17:31:43 +02:00
parent c855e97c34
commit 7af9eb8acc
9 changed files with 153 additions and 93 deletions

View file

@ -10,7 +10,6 @@ import androidx.compose.material.Surface
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.systemuicontroller.SystemUiController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.pixelized.biblib.ui.composable.SystemThemeColor
@ -25,7 +24,6 @@ class MainActivity : ComponentActivity() {
private val launcherViewModel: LauncherViewModel by viewModels()
@OptIn(ExperimentalMaterialNavigationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -0,0 +1,82 @@
package com.pixelized.biblib.ui.navigation
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.ui.screen.detail.BookDetailViewModel
import com.pixelized.biblib.ui.screen.detail.DetailScreen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
val LocalBottomDetailController = staticCompositionLocalOf<BottomDetailStateController> {
error("LocalBottomDetailController is not ready yet")
}
@OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class)
@Composable
fun BottomDetailScaffold(
bottomStateController: BottomDetailStateController = rememberBottomDetailStateController(),
content: @Composable (PaddingValues) -> Unit,
) {
CompositionLocalProvider(
LocalBottomDetailController provides bottomStateController
) {
BottomSheetScaffold(
scaffoldState = bottomStateController.scaffoldState,
sheetPeekHeight = 0.dp,
sheetGesturesEnabled = false,
sheetContent = {
var detail by remember { bottomStateController.detail }
val state = bottomStateController.scaffoldState.bottomSheetState
if (state.currentValue == BottomSheetValue.Collapsed) {
detail = null
}
detail?.let {
val viewModel: BookDetailViewModel = hiltViewModel()
viewModel.getDetail(id = it)
DetailScreen(viewModel)
}
},
content = content
)
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun rememberBottomDetailStateController(
scope: CoroutineScope = rememberCoroutineScope(),
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
): BottomDetailStateController {
val controller = BottomDetailStateController(
scope = scope,
scaffoldState = scaffoldState
)
return remember(scope, scaffoldState) { controller }
}
@OptIn(ExperimentalMaterialApi::class)
@Stable
class BottomDetailStateController constructor(
private val scope: CoroutineScope,
val scaffoldState: BottomSheetScaffoldState,
) {
val detail = mutableStateOf<Int?>(null)
fun expandBookDetail(bookId: Int) {
scope.launch {
detail.value = bookId
scaffoldState.bottomSheetState.expand()
}
}
fun collapse() {
scope.launch {
scaffoldState.bottomSheetState.collapse()
detail.value = null
}
}
}

View file

@ -8,42 +8,32 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.navigation.material.*
import com.pixelized.biblib.ui.screen.authentication.AuthenticationScreen
import com.pixelized.biblib.ui.screen.detail.DetailScreen
import com.pixelized.biblib.ui.screen.home.HomeScreen
val LocalScreenNavHostController = compositionLocalOf<NavHostController> {
error("LocalFullScreenNavHostController is not ready yet.")
}
@OptIn(ExperimentalMaterialNavigationApi::class)
@Composable
fun ScreenNavHost(
modifier: Modifier = Modifier,
bottomSheetNavigator: BottomSheetNavigator = rememberBottomSheetNavigator(),
navHostController: NavHostController = rememberNavController(bottomSheetNavigator),
navHostController: NavHostController = rememberNavController(),
startDestination: Screen = Screen.Authentication
) {
CompositionLocalProvider(LocalScreenNavHostController provides navHostController) {
ModalBottomSheetLayout(bottomSheetNavigator) {
NavHost(
modifier = modifier,
navController = navHostController,
startDestination = startDestination.route,
) {
composable(Screen.Authentication.route) {
AuthenticationScreen()
}
composable(Screen.Home.route) {
HomeScreen()
}
bottomSheet(
route = Screen.BookDetail.route,
arguments = Screen.BookDetail.arguments,
) {
DetailScreen()
}
CompositionLocalProvider(
LocalScreenNavHostController provides navHostController,
) {
NavHost(
modifier = modifier,
navController = navHostController,
startDestination = startDestination.route,
) {
composable(Screen.Authentication.route) {
AuthenticationScreen()
}
composable(Screen.Home.route) {
HomeScreen()
}
}
}
@ -54,8 +44,4 @@ fun NavHostController.navigateToHome() {
launchSingleTop = true
popUpTo(0) { inclusive = true }
}
}
fun NavHostController.navigateToBookDetail(bookId: Int) {
navigate(Screen.BookDetail(id = bookId).route)
}

View file

@ -44,10 +44,9 @@ class BookDetailViewModel @Inject constructor(
state.value.let { if (it is StateUio.Success<BookUio>) it.value else null }
}
init {
fun getDetail(id: Int) {
viewModelScope.launch(Dispatchers.IO) {
try {
val id = savedStateHandle.bookId
val book = getBookDetail(id = id)
_state.value = StateUio.Success(book)
} catch (exception: Exception) {
@ -64,7 +63,7 @@ class BookDetailViewModel @Inject constructor(
return book.toUio()
}
private fun Book.toUio() : BookUio {
private fun Book.toUio(): BookUio {
val thumbnailCover by cover(
placeHolder = CoverUio(
type = CoverUio.Type.PLACE_HOLDER,

View file

@ -34,7 +34,7 @@ import com.pixelized.biblib.ui.composable.SpannedText
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
import com.pixelized.biblib.ui.composable.animation.Delay
import com.pixelized.biblib.ui.navigation.screen.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.LocalBottomDetailController
import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
import com.pixelized.biblib.ui.theme.BibLibTheme
@ -44,14 +44,15 @@ import com.pixelized.biblib.utils.extention.bibLib
fun DetailScreen(
viewModel: BookDetailViewModel = hiltViewModel()
) {
val screenNavHostController = LocalScreenNavHostController.current
val bottomDetailState = LocalBottomDetailController.current
Box {
val book by viewModel.book
book?.let {
DetailScreenContent(
book = it,
onClose = {
screenNavHostController.popBackStack()
bottomDetailState.collapse()
},
onMobi = {},
onEpub = {},
@ -76,24 +77,13 @@ private fun DetailScreenContent(
modifier = Modifier
.verticalScroll(rememberScrollState())
.systemBarsPadding()
.padding(horizontal = MaterialTheme.bibLib.dimen.medium)
.padding(all = MaterialTheme.bibLib.dimen.medium)
.then(modifier)
) {
AnimatedOffset(
modifier = Modifier.align(Alignment.End),
) {
IconButton(onClick = onClose) {
Icon(
imageVector = Icons.Default.Close,
tint = MaterialTheme.colors.onSurface,
contentDescription = null
)
}
}
AnimatedOffset(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = MaterialTheme.bibLib.dimen.medium)
.height(MaterialTheme.bibLib.dimen.detail.cover),
contentAlignment = Alignment.Center
) {
@ -143,7 +133,7 @@ private fun DetailScreenContent(
}
}
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.small))
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.extraSmall))
AnimatedOffset(
modifier = Modifier.weight(1f),
@ -158,7 +148,7 @@ private fun DetailScreenContent(
}
}
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.small))
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.extraSmall))
AnimatedOffset(
modifier = Modifier.weight(1f),
@ -189,7 +179,7 @@ private fun DetailScreenContent(
AnimatedOffset(
modifier = Modifier
.align(alignment = Alignment.CenterHorizontally)
.padding(bottom = 16.dp),
.padding(bottom = MaterialTheme.bibLib.dimen.medium),
) {
Text(
fontWeight = FontWeight.Bold,
@ -199,7 +189,9 @@ private fun DetailScreenContent(
)
}
Row(modifier = Modifier.padding(bottom = 8.dp)) {
Row(
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.small),
) {
AnimatedOffset(modifier = Modifier.weight(1f)) {
TitleLabel(
title = stringResource(id = R.string.detail_rating),
@ -224,18 +216,17 @@ private fun DetailScreenContent(
}
}
AnimatedOffset {
Row(modifier = Modifier.padding(bottom = 16.dp)) {
TitleLabel(
title = stringResource(id = R.string.detail_series),
label = book.series ?: "-",
)
}
AnimatedOffset(
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.medium),
) {
TitleLabel(
title = stringResource(id = R.string.detail_series),
label = book.series ?: "-",
)
}
AnimatedOffset {
SpannedText(
modifier = Modifier.padding(bottom = 16.dp),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
text = HtmlCompat.fromHtml(

View file

@ -3,6 +3,7 @@ package com.pixelized.biblib.ui.screen.home
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -14,35 +15,39 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.insets.systemBarsPadding
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.navigation.BottomDetailScaffold
import com.pixelized.biblib.ui.navigation.page.Page
import com.pixelized.biblib.ui.navigation.page.PageNavHost
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun HomeScreen() {
val pageNavHostController = rememberNavController()
Scaffold(
modifier = Modifier.systemBarsPadding(),
topBar = {
Column {
TopAppBar(
title = {
Text(text = stringResource(id = R.string.app_name))
}
)
BottomBarNavigation(
homePageNavController = pageNavHostController
BottomDetailScaffold {
Scaffold(
modifier = Modifier.systemBarsPadding(),
topBar = {
Column {
TopAppBar(
title = {
Text(text = stringResource(id = R.string.app_name))
}
)
BottomBarNavigation(
homePageNavController = pageNavHostController
)
}
},
content = {
PageNavHost(
modifier = Modifier.padding(it),
navHostController = pageNavHostController,
)
}
},
content = {
PageNavHost(
modifier = Modifier.padding(it),
navHostController = pageNavHostController,
)
}
)
)
}
}
@Composable

View file

@ -7,9 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
@ -34,6 +32,7 @@ fun LazyBookThumbnailColumn(
books: LazyPagingItems<BookThumbnailUio>,
onItemClick: (BookThumbnailUio) -> Unit = {},
) {
val currentOnItemClick by rememberUpdatedState(newValue = onItemClick)
LazyColumn(
modifier = modifier,
verticalArrangement = verticalArrangement,
@ -43,7 +42,7 @@ fun LazyBookThumbnailColumn(
items(books) { thumbnail ->
BookThumbnail(
thumbnail = thumbnail,
onClick = onItemClick,
onClick = currentOnItemClick,
)
}
}

View file

@ -7,8 +7,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.ui.navigation.screen.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.screen.navigateToBookDetail
import com.pixelized.biblib.ui.navigation.LocalBottomDetailController
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage
import com.pixelized.biblib.ui.theme.BibLibTheme
@ -18,13 +17,14 @@ import com.pixelized.biblib.utils.extention.bibLib
fun BooksPage(
booksViewModel: BooksViewModel = hiltViewModel()
) {
val navHostController = LocalScreenNavHostController.current
val bottomDetailState = LocalBottomDetailController.current
LazyBookThumbnailColumn(
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding),
books = booksViewModel.books,
onItemClick = {
navHostController.navigateToBookDetail(bookId = it.id)
bottomDetailState.expandBookDetail(bookId = it.id)
},
)
}

View file

@ -7,8 +7,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.ui.navigation.screen.LocalScreenNavHostController
import com.pixelized.biblib.ui.navigation.screen.navigateToBookDetail
import com.pixelized.biblib.ui.navigation.LocalBottomDetailController
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
@ -17,13 +16,14 @@ import com.pixelized.biblib.utils.extention.bibLib
fun NewsPage(
booksViewModel: NewsBookViewModel = hiltViewModel()
) {
val navHostController = LocalScreenNavHostController.current
val bottomDetail = LocalBottomDetailController.current
LazyBookThumbnailColumn(
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding),
books = booksViewModel.news,
onItemClick = {
navHostController.navigateToBookDetail(bookId = it.id)
bottomDetail.expandBookDetail(bookId = it.id)
},
)
}