Wrap DetailScreen into a BottomSheetScaffold.
This commit is contained in:
parent
c855e97c34
commit
7af9eb8acc
9 changed files with 153 additions and 93 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue