Merge branch 'development'

This commit is contained in:
acclorite 2024-08-07 15:18:46 +03:00
commit 6cdc7d69d4
5 changed files with 223 additions and 231 deletions

View file

@ -1,16 +1,17 @@
package ua.acclorite.book_story.presentation.components package ua.acclorite.book_story.presentation.components
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box 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.RowScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBars
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -18,23 +19,24 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.graphics.Color 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.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R 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 isTopBarScrolled Whether isScrolled state should be forced or not.
* @param content1Visibility Whether first top app bar should be shown. * @param content1Visibility Whether first top app bar should be shown.
* @param content2Visibility Whether second 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 content3Visibility Whether third top app bar should be shown.
* @param customContent Custom content below [TopAppBar].
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -58,42 +60,28 @@ fun AnimatedTopAppBar(
content3Visibility: Boolean? = null, content3Visibility: Boolean? = null,
content3NavigationIcon: @Composable () -> Unit = {}, content3NavigationIcon: @Composable () -> Unit = {},
content3Title: @Composable () -> Unit = {}, content3Title: @Composable () -> Unit = {},
content3Actions: @Composable RowScope.() -> Unit = {} content3Actions: @Composable RowScope.() -> Unit = {},
customContent: @Composable ColumnScope.() -> Unit = {}
) { ) {
Box(modifier = Modifier.fillMaxWidth()) { val animatedContainerColor = lerp(
val density = LocalDensity.current containerColor,
val statusBarPadding = with(density) { scrolledContainerColor,
WindowInsets.statusBars.getTop(density).toDp() animateFloatAsState(
} if (isTopBarScrolled == true) 1f else 0f,
animationSpec = spring(
if (containerColor != Color.Transparent) { stiffness = Spring.StiffnessMediumLow
Box( ),
modifier = Modifier
.fillMaxWidth()
.height(64.dp + statusBarPadding)
.background(
if (isTopBarScrolled == true) scrolledContainerColor
else containerColor
)
)
}
val scrollableContainerColor by remember(isTopBarScrolled) {
derivedStateOf {
if (isTopBarScrolled == true) {
scrolledContainerColor
} else {
containerColor
}
}
}
val animatedContainerColor by animateColorAsState(
targetValue = scrollableContainerColor,
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
label = stringResource(id = R.string.top_app_bar_anim_content_desc) label = stringResource(id = R.string.top_app_bar_anim_content_desc)
).value
) )
Column(
Modifier
.fillMaxWidth()
.background(animatedContainerColor)
) {
Box(modifier = Modifier.fillMaxWidth()) {
CustomAnimatedVisibility( CustomAnimatedVisibility(
visible = content1Visibility ?: true, visible = content1Visibility ?: true,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
@ -107,7 +95,8 @@ fun AnimatedTopAppBar(
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor, containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor scrolledContainerColor = scrolledContainerColor
) )
) )
@ -127,7 +116,8 @@ fun AnimatedTopAppBar(
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor, containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor scrolledContainerColor = scrolledContainerColor
) )
) )
@ -148,13 +138,16 @@ fun AnimatedTopAppBar(
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = animatedContainerColor, containerColor = if (isTopBarScrolled != null) Color.Transparent
else containerColor,
scrolledContainerColor = scrolledContainerColor 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.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -35,7 +34,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -44,7 +42,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -109,7 +106,7 @@ fun BookInfoScreenRoot(screen: Screen.BookInfo) {
) )
} }
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
private fun BookInfoScreen( private fun BookInfoScreen(
state: State<BookInfoState>, state: State<BookInfoState>,
@ -120,7 +117,6 @@ private fun BookInfoScreen(
onHistoryEvent: (HistoryEvent) -> Unit onHistoryEvent: (HistoryEvent) -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val listState = rememberLazyListState() val listState = rememberLazyListState()
val snackbarState = remember { SnackbarHostState() } val snackbarState = remember { SnackbarHostState() }
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
@ -179,8 +175,7 @@ private fun BookInfoScreen(
.fillMaxSize() .fillMaxSize()
.imePadding() .imePadding()
.windowInsetsPadding(WindowInsets.navigationBars) .windowInsetsPadding(WindowInsets.navigationBars)
.pullRefresh(refreshState) .pullRefresh(refreshState),
.nestedScroll(scrollBehavior.nestedScrollConnection),
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
topBar = { topBar = {
BookInfoTopBar( BookInfoTopBar(
@ -189,7 +184,6 @@ private fun BookInfoScreen(
onLibraryEvent = onLibraryEvent, onLibraryEvent = onLibraryEvent,
onHistoryEvent = onHistoryEvent, onHistoryEvent = onHistoryEvent,
onNavigate = onNavigate, onNavigate = onNavigate,
scrollBehavior = scrollBehavior,
listState = listState, listState = listState,
snackbarState = snackbarState snackbarState = snackbarState
) )

View file

@ -12,13 +12,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
@ -42,7 +40,6 @@ import ua.acclorite.book_story.presentation.ui.Transitions
* @param onLibraryEvent [LibraryEvent] callback. * @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryEvent [HistoryEvent] callback. * @param onHistoryEvent [HistoryEvent] callback.
* @param onNavigate Navigator callback. * @param onNavigate Navigator callback.
* @param scrollBehavior [TopAppBarScrollBehavior].
* @param listState [LazyListState]. * @param listState [LazyListState].
* @param snackbarState [SnackbarHostState]. * @param snackbarState [SnackbarHostState].
*/ */
@ -54,7 +51,6 @@ fun BookInfoTopBar(
onLibraryEvent: (LibraryEvent) -> Unit, onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit, onHistoryEvent: (HistoryEvent) -> Unit,
onNavigate: OnNavigate, onNavigate: OnNavigate,
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState, listState: LazyListState,
snackbarState: SnackbarHostState snackbarState: SnackbarHostState
) { ) {
@ -62,9 +58,9 @@ fun BookInfoTopBar(
val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } } val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
AnimatedTopAppBar( AnimatedTopAppBar(
containerColor = Color.Transparent, containerColor = MaterialTheme.colorScheme.surface.copy(0f),
scrollBehavior = scrollBehavior, scrollBehavior = null,
isTopBarScrolled = null, isTopBarScrolled = listState.canScrollBackward,
content1NavigationIcon = { content1NavigationIcon = {
if ( 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. * Tab row, either scrollable or static, depends on screen width.
* *
* @param onEvent Event used to Scroll to Page. * @param onEvent Event used to Scroll to Page.
* @param itemCountBackgroundColor Item count background color.
* @param books The list of all books. * @param books The list of all books.
* @param pagerState [PagerState]. * @param pagerState [PagerState].
*/ */
@Composable @Composable
fun LibraryTabRow( fun LibraryTabRow(
onEvent: (LibraryEvent) -> Unit, onEvent: (LibraryEvent) -> Unit,
itemCountBackgroundColor: Color,
books: List<CategorizedBooks>, books: List<CategorizedBooks>,
pagerState: PagerState pagerState: PagerState
) { ) {
@ -113,7 +115,7 @@ fun LibraryTabRow(
text = tabItem.second.toString(), text = tabItem.second.toString(),
modifier = Modifier modifier = Modifier
.background( .background(
MaterialTheme.colorScheme.surfaceContainer, itemCountBackgroundColor,
MaterialTheme.shapes.medium MaterialTheme.shapes.medium
) )
.padding(horizontal = 8.dp, vertical = 4.dp), .padding(horizontal = 8.dp, vertical = 4.dp),

View file

@ -1,10 +1,11 @@
package ua.acclorite.book_story.presentation.screens.library.components 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.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
@ -55,7 +56,6 @@ fun LibraryTopBar(
) { ) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
Column(Modifier.fillMaxWidth()) {
AnimatedTopAppBar( AnimatedTopAppBar(
scrollBehavior = null, scrollBehavior = null,
isTopBarScrolled = state.value.hasSelectedItems, isTopBarScrolled = state.value.hasSelectedItems,
@ -168,12 +168,19 @@ fun LibraryTopBar(
}, },
content3Actions = { content3Actions = {
MoreDropDown() MoreDropDown()
} },
) customContent = {
LibraryTabRow( LibraryTabRow(
onEvent = onEvent, onEvent = onEvent,
books = state.value.categorizedBooks, 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 pagerState = pagerState
) )
} }
)
} }