Merge remote-tracking branch 'origin/development' into file-tree-browse-feature

This commit is contained in:
acclorite 2024-08-04 19:00:21 +03:00
commit 5301df42ee
5 changed files with 114 additions and 118 deletions

View file

@ -1,16 +1,17 @@
package ua.acclorite.book_story.presentation.components
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.statusBars
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
@ -18,23 +19,24 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
/**
* Animated top app bar. Has up to 3 different top bars inside it. Follow [TopAppBar] for more info.
* Animated top app bar.
* Has up to 3 different top bars inside it. Follow [TopAppBar] for more info.
*
* @param containerColor Container color of the TopBar.
* @param scrolledContainerColor Scrolled container color of the TopBar.
* @param scrollBehavior [TopAppBarScrollBehavior].
* @param isTopBarScrolled Whether isScrolled state should be forced or not.
* @param content1Visibility Whether first top app bar should be shown.
* @param content2Visibility Whether second top app bar should be shown.
* @param content3Visibility Whether third top app bar should be shown.
* @param customContent Custom content below [TopAppBar].
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -58,102 +60,93 @@ fun AnimatedTopAppBar(
content3Visibility: Boolean? = null,
content3NavigationIcon: @Composable () -> Unit = {},
content3Title: @Composable () -> Unit = {},
content3Actions: @Composable RowScope.() -> Unit = {}
content3Actions: @Composable RowScope.() -> Unit = {},
customContent: @Composable ColumnScope.() -> Unit = {}
) {
Box(modifier = Modifier.fillMaxWidth()) {
val density = LocalDensity.current
val statusBarPadding = with(density) {
WindowInsets.statusBars.getTop(density).toDp()
}
val animatedContainerColor = lerp(
containerColor,
scrolledContainerColor,
animateFloatAsState(
if (isTopBarScrolled == true) 1f else 0f,
animationSpec = spring(
stiffness = Spring.StiffnessMediumLow
),
label = stringResource(id = R.string.top_app_bar_anim_content_desc)
).value
)
if (containerColor != Color.Transparent) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(64.dp + statusBarPadding)
.background(
if (isTopBarScrolled == true) scrolledContainerColor
else containerColor
Column(
Modifier
.fillMaxWidth()
.background(animatedContainerColor)
) {
Box(modifier = Modifier.fillMaxWidth()) {
CustomAnimatedVisibility(
visible = content1Visibility ?: true,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content1NavigationIcon,
title = content1Title,
actions = content1Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
)
}
val scrollableContainerColor by remember(isTopBarScrolled) {
derivedStateOf {
if (isTopBarScrolled == true) {
scrolledContainerColor
} else {
containerColor
if (content2Visibility != null) {
CustomAnimatedVisibility(
visible = content2Visibility,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content2NavigationIcon,
title = content2Title,
actions = content2Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
}
if (content3Visibility != null) {
CustomAnimatedVisibility(
visible = content3Visibility,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content3NavigationIcon,
title = content3Title,
actions = content3Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
}
}
val animatedContainerColor by animateColorAsState(
targetValue = scrollableContainerColor,
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
label = stringResource(id = R.string.top_app_bar_anim_content_desc)
)
CustomAnimatedVisibility(
visible = content1Visibility ?: true,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content1NavigationIcon,
title = content1Title,
actions = content1Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
if (content2Visibility != null) {
CustomAnimatedVisibility(
visible = content2Visibility,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content2NavigationIcon,
title = content2Title,
actions = content2Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
}
if (content3Visibility != null) {
CustomAnimatedVisibility(
visible = content3Visibility,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) {
TopAppBar(
windowInsets = WindowInsets.statusBars,
navigationIcon = content3NavigationIcon,
title = content3Title,
actions = content3Actions,
scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor,
scrolledContainerColor = scrolledContainerColor
)
)
}
}
customContent()
}
}

View file

@ -27,7 +27,6 @@ import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon
@ -35,7 +34,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@ -44,7 +42,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -109,7 +106,7 @@ fun BookInfoScreenRoot(screen: Screen.BookInfo) {
)
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun BookInfoScreen(
state: State<BookInfoState>,
@ -120,7 +117,6 @@ private fun BookInfoScreen(
onHistoryEvent: (HistoryEvent) -> Unit
) {
val context = LocalContext.current
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val listState = rememberLazyListState()
val snackbarState = remember { SnackbarHostState() }
val refreshState = rememberPullRefreshState(
@ -179,8 +175,7 @@ private fun BookInfoScreen(
.fillMaxSize()
.imePadding()
.windowInsetsPadding(WindowInsets.navigationBars)
.pullRefresh(refreshState)
.nestedScroll(scrollBehavior.nestedScrollConnection),
.pullRefresh(refreshState),
containerColor = MaterialTheme.colorScheme.surface,
topBar = {
BookInfoTopBar(
@ -189,7 +184,6 @@ private fun BookInfoScreen(
onLibraryEvent = onLibraryEvent,
onHistoryEvent = onHistoryEvent,
onNavigate = onNavigate,
scrollBehavior = scrollBehavior,
listState = listState,
snackbarState = snackbarState
)

View file

@ -12,13 +12,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import ua.acclorite.book_story.R
@ -42,7 +40,6 @@ import ua.acclorite.book_story.presentation.ui.Transitions
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryEvent [HistoryEvent] callback.
* @param onNavigate Navigator callback.
* @param scrollBehavior [TopAppBarScrollBehavior].
* @param listState [LazyListState].
* @param snackbarState [SnackbarHostState].
*/
@ -54,7 +51,6 @@ fun BookInfoTopBar(
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
onNavigate: OnNavigate,
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
snackbarState: SnackbarHostState
) {
@ -62,9 +58,9 @@ fun BookInfoTopBar(
val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
AnimatedTopAppBar(
containerColor = Color.Transparent,
scrollBehavior = scrollBehavior,
isTopBarScrolled = null,
containerColor = MaterialTheme.colorScheme.surface.copy(0f),
scrollBehavior = null,
isTopBarScrolled = listState.canScrollBackward,
content1NavigationIcon = {
if (

View file

@ -33,12 +33,14 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
* Tab row, either scrollable or static, depends on screen width.
*
* @param onEvent Event used to Scroll to Page.
* @param itemCountBackgroundColor Item count background color.
* @param books The list of all books.
* @param pagerState [PagerState].
*/
@Composable
fun LibraryTabRow(
onEvent: (LibraryEvent) -> Unit,
itemCountBackgroundColor: Color,
books: List<CategorizedBooks>,
pagerState: PagerState
) {
@ -113,7 +115,7 @@ fun LibraryTabRow(
text = tabItem.second.toString(),
modifier = Modifier
.background(
MaterialTheme.colorScheme.surfaceContainer,
itemCountBackgroundColor,
MaterialTheme.shapes.medium
)
.padding(horizontal = 8.dp, vertical = 4.dp),

View file

@ -1,5 +1,8 @@
package ua.acclorite.book_story.presentation.screens.library.components
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -168,12 +171,20 @@ fun LibraryTopBar(
},
content3Actions = {
MoreDropDown()
},
customContent = {
LibraryTabRow(
onEvent = onEvent,
books = state.value.categorizedBooks,
itemCountBackgroundColor = animateColorAsState(
if (state.value.hasSelectedItems) MaterialTheme.colorScheme.surfaceContainerHighest
else MaterialTheme.colorScheme.surfaceContainer,
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
label = stringResource(id = R.string.top_app_bar_anim_content_desc)
).value,
pagerState = pagerState
)
}
)
LibraryTabRow(
onEvent = onEvent,
books = state.value.categorizedBooks,
pagerState = pagerState
)
}
}