Add Modifier.scrolledContainerBackground (#407)

This commit is contained in:
Brian Yencho 2023-12-17 20:33:17 -06:00 committed by Álison Fernandes
parent 8015fc7b5f
commit ff4eeced33
4 changed files with 154 additions and 18 deletions

View file

@ -0,0 +1,33 @@
package com.x8bit.bitwarden.ui.platform.base.util
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
/**
* Adds a performance-optimized background color specified by the given [topAppBarScrollBehavior]
* and its current scroll state.
*/
@OmitFromCoverage
@OptIn(ExperimentalMaterial3Api::class)
@Stable
@Composable
fun Modifier.scrolledContainerBackground(
topAppBarScrollBehavior: TopAppBarScrollBehavior,
): Modifier {
val expandedColor = MaterialTheme.colorScheme.surface
val collapsedColor = MaterialTheme.colorScheme.surfaceContainer
return this then drawBehind {
drawRect(
color = topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
}
}

View file

@ -0,0 +1,27 @@
package com.x8bit.bitwarden.ui.platform.base.util
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
/**
* Returns the correct color for a scrolled container based on the given [TopAppBarScrollBehavior]
* and target [expandedColor] / [collapsedColor].
*/
@OptIn(ExperimentalMaterial3Api::class)
fun TopAppBarScrollBehavior.toScrolledContainerColor(
expandedColor: Color,
collapsedColor: Color,
): Color {
val progressFraction = if (this.isPinned) {
this.state.overlappedFraction
} else {
this.state.collapsedFraction
}
return lerp(
start = expandedColor,
stop = collapsedColor,
fraction = progressFraction,
)
}

View file

@ -35,14 +35,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.lowercaseWithCurrentLocal
import com.x8bit.bitwarden.ui.platform.base.util.scrolledContainerBackground
import com.x8bit.bitwarden.ui.platform.base.util.toSafeOverlayColor
import com.x8bit.bitwarden.ui.platform.base.util.toUnscaledTextUnit
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
@ -185,8 +184,6 @@ private fun AnimatedAccountSwitcher(
topAppBarScrollBehavior: TopAppBarScrollBehavior,
currentAnimationState: (isVisible: Boolean) -> Unit,
) {
val expandedColor = MaterialTheme.colorScheme.surface
val collapsedColor = MaterialTheme.colorScheme.surfaceContainer
val transition = updateTransition(
targetState = isVisible,
label = "AnimatedAccountSwitcher",
@ -203,20 +200,7 @@ private fun AnimatedAccountSwitcher(
// bottom padding.
.padding(bottom = 24.dp)
// Match the color of the switcher the different states of the app bar.
.drawBehind {
val progressFraction = if (topAppBarScrollBehavior.isPinned) {
topAppBarScrollBehavior.state.overlappedFraction
} else {
topAppBarScrollBehavior.state.collapsedFraction
}
val contentBackgroundColor =
lerp(
start = expandedColor,
stop = collapsedColor,
fraction = progressFraction,
)
drawRect(contentBackgroundColor)
},
.scrolledContainerBackground(topAppBarScrollBehavior),
) {
items(accountSummaries) { accountSummary ->
AccountSummaryItem(

View file

@ -0,0 +1,92 @@
package com.x8bit.bitwarden.ui.platform.base.util
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.ui.graphics.Color
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
@OptIn(ExperimentalMaterial3Api::class)
class TopAppBarScrollBehaviorExtensionsTest {
@Suppress("MaxLineLength")
@Test
fun `toScrolledContainerColor for pinned states should interpolate based on the overlappedFraction`() {
val expandedColor = Color(
red = 0f,
green = 0f,
blue = 0f,
alpha = 0f,
)
val collapsedColor = Color(
red = 1f,
green = 1f,
blue = 1f,
alpha = 1f,
)
var overlappedFraction = 0f
val topAppBarScrollBehavior = mockk<TopAppBarScrollBehavior> {
every { isPinned } returns true
every { state.overlappedFraction } answers { overlappedFraction }
}
overlappedFraction = 0f
assertEquals(
expandedColor,
topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
overlappedFraction = 1f
assertEquals(
collapsedColor,
topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
}
@Suppress("MaxLineLength")
@Test
fun `toScrolledContainerColor for pinned states should interpolate based on the collapsedFraction`() {
val expandedColor = Color(
red = 0f,
green = 0f,
blue = 0f,
alpha = 0f,
)
val collapsedColor = Color(
red = 1f,
green = 1f,
blue = 1f,
alpha = 1f,
)
var collapsedFraction = 0f
val topAppBarScrollBehavior = mockk<TopAppBarScrollBehavior> {
every { isPinned } returns false
every { state.collapsedFraction } answers { collapsedFraction }
}
collapsedFraction = 0f
assertEquals(
expandedColor,
topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
collapsedFraction = 1f
assertEquals(
collapsedColor,
topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
}
}