fix(sources-search): search box visibility glitch when scrolling (#1278)

* fix(sources-screen): search box visibility glitch when scrolling

* Refactor to a reusable function

* Apply to MigrateSourceScreen

* Fix warning

* suggestions

* Revert "Fix warning"

This reverts commit b1c97bcf04b9e34b3eb314fd54c45be953d2f451.

* Add comment

---------

Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
dhanuarf 2025-11-28 14:21:41 +07:00 committed by GitHub
parent 798bfc19ff
commit b24b14c73d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 153 additions and 103 deletions

View file

@ -3,6 +3,7 @@ package eu.kanade.presentation.browse
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@ -30,6 +31,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.model.installedExtension
import eu.kanade.presentation.browse.components.BaseSourceItem
@ -48,7 +50,6 @@ import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.icons.FlagEmoji
@ -126,27 +127,9 @@ private fun MigrateSourceList(
}
Column(
// Wrap around so we can use stickyHeader
modifier = Modifier.padding(contentPadding),
) {
AnimatedFloatingSearchBox(
listState = lazyListState,
searchQuery = state.searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = stringResource(KMR.strings.action_search_for_source),
modifier = Modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
)
FastScrollLazyColumn(
state = lazyListState,
// contentPadding = contentPadding + topSmallPaddingValues,
// KMK <--
) {
stickyHeader(key = STICKY_HEADER_KEY_PREFIX) {
Row(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
@ -194,8 +177,14 @@ private fun MigrateSourceList(
}
}
}
}
// KMK -->
Box {
FastScrollLazyColumn(
state = lazyListState,
contentPadding = PaddingValues(top = 65.dp),
// KMK <--
) {
items(
items = list
// KMK -->
@ -221,6 +210,23 @@ private fun MigrateSourceList(
)
}
}
// KMK -->
AnimatedFloatingSearchBox(
listState = lazyListState,
searchQuery = state.searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = stringResource(KMR.strings.action_search_for_source),
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
)
.align(Alignment.TopCenter),
)
// KMK <--
}
}
}

View file

@ -1,7 +1,9 @@
package eu.kanade.presentation.browse
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
@ -23,6 +25,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
@ -82,24 +85,12 @@ fun SourcesScreen(
modifier = Modifier.padding(contentPadding),
)
// KMK -->
else -> Column(
// Wrap around so we can use stickyHeader
else -> Box(
modifier = Modifier.padding(contentPadding),
) {
AnimatedFloatingSearchBox(
listState = lazyListState,
searchQuery = state.searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = stringResource(KMR.strings.action_search_for_source),
modifier = Modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
)
FastScrollLazyColumn(
state = lazyListState,
contentPadding = PaddingValues(top = 65.dp),
// KMK <--
) {
items(
@ -141,6 +132,22 @@ fun SourcesScreen(
}
}
}
// KMK -->
AnimatedFloatingSearchBox(
listState = lazyListState,
searchQuery = state.searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = stringResource(KMR.strings.action_search_for_source),
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
)
.align(Alignment.TopCenter),
)
// KMK <--
}
}
}

View file

@ -44,8 +44,7 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
import tachiyomi.presentation.core.util.isScrolledToStart
import tachiyomi.presentation.core.util.isScrollingDown
import tachiyomi.presentation.core.util.isItemScrollingUp
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
@ -61,7 +60,7 @@ fun AnimatedFloatingSearchBox(
keyboardController: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current,
) {
AnimatedVisibility(
visible = listState.isScrolledToStart() || !listState.isScrollingDown(),
visible = listState.isItemScrollingUp(),
enter = expandVertically(),
exit = shrinkVertically(),
modifier = modifier,

View file

@ -2,11 +2,15 @@ package tachiyomi.presentation.core.util
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun LazyListState.shouldExpandFAB(): Boolean {
@ -75,3 +79,37 @@ fun LazyListState.isScrollingDown(): Boolean {
}
}.value
}
// KMK -->
@Composable
private fun LazyListState.isItemScrolling(
initialValue: Boolean,
comparison: (Int, Int) -> Boolean,
): Boolean {
var isScrolling by remember { mutableStateOf(initialValue) }
var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) }
LaunchedEffect(this) {
snapshotFlow { firstVisibleItemIndex }
.distinctUntilChanged()
.collect { currentIndex ->
if (previousIndex != currentIndex) {
isScrolling = comparison(previousIndex, currentIndex)
previousIndex = currentIndex
}
}
}
return isScrolling
}
@Composable
fun LazyListState.isItemScrollingUp(initiallyVisible: Boolean = true): Boolean {
return isItemScrolling(initialValue = initiallyVisible) { previous, current -> previous > current }
}
@Composable
fun LazyListState.isItemScrollingDown(initiallyVisible: Boolean = false): Boolean {
return isItemScrolling(initialValue = initiallyVisible) { previous, current -> previous < current }
}
// KMK <--