🛠️ Improve AnimatedTopAppBar

* Corrected shown state of the top bar (always one at a time)
This commit is contained in:
Acclorite 2024-09-28 13:15:40 +03:00
parent e04bb0aca7
commit 4e5b5bcb26
5 changed files with 111 additions and 94 deletions

View file

@ -22,9 +22,10 @@ import androidx.compose.runtime.Composable
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.graphics.lerp import androidx.compose.ui.graphics.lerp
import ua.acclorite.book_story.domain.util.ID
data class AnimatedTopAppBarData( data class AnimatedTopAppBarData(
val contentVisibility: Boolean? = null, val contentID: ID,
val contentNavigationIcon: @Composable () -> Unit, val contentNavigationIcon: @Composable () -> Unit,
val contentTitle: @Composable () -> Unit, val contentTitle: @Composable () -> Unit,
val contentActions: @Composable RowScope.() -> Unit val contentActions: @Composable RowScope.() -> Unit
@ -38,7 +39,8 @@ data class AnimatedTopAppBarData(
* @param scrolledContainerColor Scrolled container color of the TopBar. * @param scrolledContainerColor Scrolled container color of the TopBar.
* @param scrollBehavior [TopAppBarScrollBehavior]. * @param scrollBehavior [TopAppBarScrollBehavior].
* @param isTopBarScrolled Whether isScrolled state should be forced or not. * @param isTopBarScrolled Whether isScrolled state should be forced or not.
* @param animatedTopBars Pass a list of all [AnimatedTopAppBarData] to show. * @param shownTopBar [ID] of the top bar to show.
* @param topBars Pass a list of all [AnimatedTopAppBarData] to show.
* @param customContent Custom content below [TopAppBar]. * @param customContent Custom content below [TopAppBar].
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -50,7 +52,8 @@ fun AnimatedTopAppBar(
scrollBehavior: TopAppBarScrollBehavior?, scrollBehavior: TopAppBarScrollBehavior?,
isTopBarScrolled: Boolean?, isTopBarScrolled: Boolean?,
animatedTopBars: List<AnimatedTopAppBarData>, shownTopBar: ID,
topBars: List<AnimatedTopAppBarData>,
customContent: @Composable ColumnScope.() -> Unit = {} customContent: @Composable ColumnScope.() -> Unit = {}
) { ) {
val animatedContainerColor = lerp( val animatedContainerColor = lerp(
@ -70,9 +73,9 @@ fun AnimatedTopAppBar(
.background(animatedContainerColor) .background(animatedContainerColor)
) { ) {
Box(modifier = Modifier.fillMaxWidth()) { Box(modifier = Modifier.fillMaxWidth()) {
animatedTopBars.forEach { data -> topBars.forEach { data ->
CustomAnimatedVisibility( CustomAnimatedVisibility(
visible = data.contentVisibility == true, visible = data.contentID == shownTopBar,
enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)),
exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow))
) { ) {

View file

@ -60,9 +60,10 @@ fun BookInfoTopBar(
scrollBehavior = null, scrollBehavior = null,
isTopBarScrolled = listState.canScrollBackward, isTopBarScrolled = listState.canScrollBackward,
animatedTopBars = listOf( shownTopBar = 0,
topBars = listOf(
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = true, contentID = 0,
contentNavigationIcon = { contentNavigationIcon = {
if ( if (
state.value.editTitle || state.value.editTitle ||

View file

@ -78,13 +78,17 @@ fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
AnimatedTopAppBar( AnimatedTopAppBar(
scrollBehavior = null, scrollBehavior = null,
isTopBarScrolled = isScrolled.value || inNestedDirectory.value, isTopBarScrolled = isScrolled.value || state.value.hasSelectedItems,
animatedTopBars = listOf( shownTopBar = when {
state.value.hasSelectedItems -> 3
state.value.showSearch -> 2
state.value.inNestedDirectory -> 1
else -> 0
},
topBars = listOf(
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = !state.value.hasSelectedItems && contentID = 0,
!state.value.showSearch &&
!inNestedDirectory.value,
contentNavigationIcon = {}, contentNavigationIcon = {},
contentTitle = { contentTitle = {
Text( Text(
@ -120,7 +124,7 @@ fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
), ),
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = inNestedDirectory.value, contentID = 1,
contentNavigationIcon = { contentNavigationIcon = {
CustomIconButton( CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack, icon = Icons.AutoMirrored.Default.ArrowBack,
@ -165,7 +169,43 @@ fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
), ),
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = state.value.hasSelectedItems, contentID = 2,
contentNavigationIcon = {
CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = R.string.exit_search_content_desc,
disableOnClick = true
) {
onEvent(BrowseEvent.OnSearchShowHide)
}
},
contentTitle = {
CustomSearchTextField(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
onEvent(BrowseEvent.OnRequestFocus(focusRequester))
},
query = state.value.searchQuery,
onQueryChange = {
onEvent(BrowseEvent.OnSearchQueryChange(it))
},
onSearch = {
onEvent(BrowseEvent.OnSearch)
},
placeholder = stringResource(
id = R.string.search_query,
stringResource(id = R.string.files)
)
)
},
contentActions = {
MoreDropDown()
}
),
AnimatedTopAppBarData(
contentID = 3,
contentNavigationIcon = { contentNavigationIcon = {
CustomIconButton( CustomIconButton(
icon = Icons.Default.Clear, icon = Icons.Default.Clear,
@ -207,42 +247,6 @@ fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
onEvent(BrowseEvent.OnAddingDialogRequest) onEvent(BrowseEvent.OnAddingDialogRequest)
} }
} }
),
AnimatedTopAppBarData(
contentVisibility = state.value.showSearch && !state.value.hasSelectedItems,
contentNavigationIcon = {
CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = R.string.exit_search_content_desc,
disableOnClick = true
) {
onEvent(BrowseEvent.OnSearchShowHide)
}
},
contentTitle = {
CustomSearchTextField(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
onEvent(BrowseEvent.OnRequestFocus(focusRequester))
},
query = state.value.searchQuery,
onQueryChange = {
onEvent(BrowseEvent.OnSearchQueryChange(it))
},
onSearch = {
onEvent(BrowseEvent.OnSearch)
},
placeholder = stringResource(
id = R.string.search_query,
stringResource(id = R.string.files)
)
)
},
contentActions = {
MoreDropDown()
}
) )
), ),
customContent = { customContent = {

View file

@ -102,9 +102,13 @@ private fun HistoryScreen() {
scrollBehavior = null, scrollBehavior = null,
isTopBarScrolled = state.value.listState.canScrollBackward, isTopBarScrolled = state.value.listState.canScrollBackward,
animatedTopBars = listOf( shownTopBar = when {
state.value.showSearch -> 1
else -> 0
},
topBars = listOf(
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = !state.value.showSearch, contentID = 0,
contentNavigationIcon = {}, contentNavigationIcon = {},
contentTitle = { contentTitle = {
Text(stringResource(id = R.string.history_screen)) Text(stringResource(id = R.string.history_screen))
@ -133,7 +137,7 @@ private fun HistoryScreen() {
), ),
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = state.value.showSearch, contentID = 1,
contentNavigationIcon = { contentNavigationIcon = {
CustomIconButton( CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack, icon = Icons.AutoMirrored.Default.ArrowBack,

View file

@ -56,9 +56,14 @@ fun LibraryTopBar(pagerState: PagerState) {
scrollBehavior = null, scrollBehavior = null,
isTopBarScrolled = state.value.hasSelectedItems, isTopBarScrolled = state.value.hasSelectedItems,
animatedTopBars = listOf( shownTopBar = when {
state.value.hasSelectedItems -> 2
state.value.showSearch -> 1
else -> 0
},
topBars = listOf(
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = !state.value.hasSelectedItems && !state.value.showSearch, contentID = 0,
contentNavigationIcon = {}, contentNavigationIcon = {},
contentTitle = { contentTitle = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@ -90,7 +95,45 @@ fun LibraryTopBar(pagerState: PagerState) {
), ),
AnimatedTopAppBarData( AnimatedTopAppBarData(
contentVisibility = state.value.hasSelectedItems, contentID = 1,
contentNavigationIcon = {
CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = R.string.exit_search_content_desc,
disableOnClick = true
) {
onEvent(
LibraryEvent.OnSearchShowHide
)
}
},
contentTitle = {
CustomSearchTextField(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
onEvent(LibraryEvent.OnRequestFocus(focusRequester))
},
query = state.value.searchQuery,
onQueryChange = {
onEvent(LibraryEvent.OnSearchQueryChange(it))
},
onSearch = {
onEvent(LibraryEvent.OnSearch)
},
placeholder = stringResource(
id = R.string.search_query,
stringResource(id = R.string.books)
)
)
},
contentActions = {
MoreDropDown()
},
),
AnimatedTopAppBarData(
contentID = 2,
contentNavigationIcon = { contentNavigationIcon = {
CustomIconButton( CustomIconButton(
icon = Icons.Default.Clear, icon = Icons.Default.Clear,
@ -134,44 +177,6 @@ fun LibraryTopBar(pagerState: PagerState) {
} }
} }
), ),
AnimatedTopAppBarData(
contentVisibility = state.value.showSearch && !state.value.hasSelectedItems,
contentNavigationIcon = {
CustomIconButton(
icon = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = R.string.exit_search_content_desc,
disableOnClick = true
) {
onEvent(
LibraryEvent.OnSearchShowHide
)
}
},
contentTitle = {
CustomSearchTextField(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
onEvent(LibraryEvent.OnRequestFocus(focusRequester))
},
query = state.value.searchQuery,
onQueryChange = {
onEvent(LibraryEvent.OnSearchQueryChange(it))
},
onSearch = {
onEvent(LibraryEvent.OnSearch)
},
placeholder = stringResource(
id = R.string.search_query,
stringResource(id = R.string.books)
)
)
},
contentActions = {
MoreDropDown()
},
)
), ),
customContent = { customContent = {
LibraryTabRow( LibraryTabRow(