improve blur on friends list & chat materials

This commit is contained in:
2024-07-13 16:32:03 +03:00
parent 93ae63fadd
commit f545a9c4e5
4 changed files with 185 additions and 245 deletions
@@ -1,81 +0,0 @@
package com.meloda.app.fast.designsystem.components
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.meloda.app.fast.designsystem.LocalTheme
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
@OptIn(
ExperimentalMaterial3Api::class,
ExperimentalHazeMaterialsApi::class
)
@Composable
fun BlurrableTopAppBar(
modifier: Modifier = Modifier,
title: String,
listState: LazyListState?,
hazeState: HazeState = remember { HazeState() }
) {
val currentTheme = LocalTheme.current
val toolbarColorAlpha by animateFloatAsState(
targetValue = if (listState == null || !listState.canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
)
val toolbarContainerColor by animateColorAsState(
targetValue =
if (currentTheme.usingBlur || listState != null && !listState.canScrollBackward)
MaterialTheme.colorScheme.surface
else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
)
TopAppBar(
title = {
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = toolbarContainerColor.copy(
alpha = if (currentTheme.usingBlur) toolbarColorAlpha else 1f
)
),
modifier = modifier
.then(
if (currentTheme.usingBlur) {
Modifier.hazeChild(
state = hazeState,
style = HazeMaterials.thick()
)
} else {
Modifier
}
)
.fillMaxWidth(),
)
}
@@ -3,10 +3,13 @@ package com.meloda.app.fast.chatmaterials.presentation
import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
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.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
@@ -52,6 +55,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -126,20 +130,26 @@ fun ChatMaterialsScreen(
Log.d("ChatMaterialsScreen", "ChatMaterialsScreen: canScrollBackward: $canScrollBackward")
val toolbarColorAlpha by animateFloatAsState(
targetValue = if (!canScrollBackward) 1f else 0f,
val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!currentTheme.usingBlur || !canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
)
val toolbarContainerColor by animateColorAsState(
val topBarContainerColor by animateColorAsState(
targetValue =
if (currentTheme.usingBlur || !canScrollBackward)
MaterialTheme.colorScheme.surface
else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
)
val pullToRefreshAlpha by animateFloatAsState(
@@ -152,10 +162,7 @@ fun ChatMaterialsScreen(
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Chat Materials")
},
Column(
modifier = Modifier
.then(
if (currentTheme.usingBlur) {
@@ -163,93 +170,99 @@ fun ChatMaterialsScreen(
state = hazeState,
style = hazeStyle
)
} else Modifier
)
.fillMaxWidth(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = toolbarContainerColor.copy(
alpha = if (currentTheme.usingBlur) toolbarColorAlpha else 1f
)
),
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = null
)
}
},
actions = {
IconButton(
onClick = {
dropDownMenuExpanded = true
} else {
Modifier
}
) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = "Options button"
)
}
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = {
dropDownMenuExpanded = false
},
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
DropdownMenuItem(
)
.background(topBarContainerColor.copy(alpha = topBarContainerColorAlpha))
.fillMaxWidth()
) {
TopAppBar(
title = { Text(text = "Chat Materials") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent
),
modifier = Modifier.fillMaxWidth(),
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = null
)
}
},
actions = {
IconButton(
onClick = {
viewModel.onRefresh()
dropDownMenuExpanded = true
}
) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = "Options button"
)
}
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = {
dropDownMenuExpanded = false
},
text = {
Text(text = stringResource(id = R.string.action_refresh))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Refresh,
contentDescription = null
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
DropdownMenuItem(
onClick = {
viewModel.onRefresh()
dropDownMenuExpanded = false
},
text = {
Text(text = stringResource(id = R.string.action_refresh))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Refresh,
contentDescription = null
)
}
)
if (currentTheme.usingBlur) {
DropdownMenuItem(
text = {
Text(text = if (moreClearBlur) "Default blur" else "Clearer blur")
},
onClick = {
moreClearBlur = !moreClearBlur
dropDownMenuExpanded = false
}
)
}
HorizontalDivider()
titles.forEachIndexed { index, title ->
DropdownMenuItem(
leadingIcon = {
RadioButton(
selected = checkedTypeIndex == index,
onClick = null
)
},
text = {
Text(text = title)
},
onClick = {
checkedTypeIndex = index
dropDownMenuExpanded = false
}
)
}
)
if (currentTheme.usingBlur) {
DropdownMenuItem(
text = {
Text(text = if (moreClearBlur) "Default blur" else "Clearer blur")
},
onClick = {
moreClearBlur = !moreClearBlur
dropDownMenuExpanded = false
}
)
}
HorizontalDivider()
titles.forEachIndexed { index, title ->
DropdownMenuItem(
leadingIcon = {
RadioButton(
selected = checkedTypeIndex == index,
onClick = null
)
},
text = {
Text(text = title)
},
onClick = {
checkedTypeIndex = index
dropDownMenuExpanded = false
}
)
}
}
}
)
)
}
}
) { padding ->
Box(
@@ -48,7 +48,7 @@ fun FriendsList(
) {
item {
Spacer(modifier = Modifier.height(padding.calculateTopPadding()))
Spacer(modifier = Modifier.height(64.dp))
Spacer(modifier = Modifier.height(16.dp))
}
items(
@@ -1,8 +1,10 @@
package com.meloda.app.fast.friends.presentation
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
@@ -10,7 +12,6 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -18,12 +19,12 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.surfaceColorAtElevation
@@ -39,9 +40,11 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -51,7 +54,6 @@ import com.meloda.app.fast.designsystem.ImmutableList
import com.meloda.app.fast.designsystem.LocalHazeState
import com.meloda.app.fast.designsystem.LocalTheme
import com.meloda.app.fast.designsystem.TabItem
import com.meloda.app.fast.designsystem.components.BlurrableTopAppBar
import com.meloda.app.fast.designsystem.components.FullScreenLoader
import com.meloda.app.fast.designsystem.components.NoItemsView
import com.meloda.app.fast.friends.FriendsViewModel
@@ -121,32 +123,98 @@ fun FriendsScreen(
animationSpec = tween(durationMillis = 50)
)
val tabsColorAlpha by animateFloatAsState(
targetValue = if (!listState.canScrollBackward) 1f else 0f,
val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
)
val tabsContainerColor by animateColorAsState(
val topBarContainerColor by animateColorAsState(
targetValue =
if (currentTheme.usingBlur || !listState.canScrollBackward)
MaterialTheme.colorScheme.surface
else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
)
var selectedTabIndex by rememberSaveable {
mutableIntStateOf(0)
}
val tabItems = listOf(
TabItem(
titleResId = UiR.string.title_friends_all,
unselectedIconResId = null,
selectedIconResId = null
),
TabItem(
titleResId = UiR.string.title_friends_online,
unselectedIconResId = null,
selectedIconResId = null
)
)
Scaffold(
modifier = Modifier.fillMaxSize(),
contentWindowInsets = WindowInsets.statusBars,
topBar = {
Column(modifier = Modifier.fillMaxWidth()) {
BlurrableTopAppBar(
title = stringResource(id = UiR.string.title_friends),
listState = listState,
hazeState = hazeState
Column(
modifier = Modifier
.then(
if (currentTheme.usingBlur) {
Modifier.hazeChild(
state = hazeState,
style = HazeMaterials.thick()
)
} else {
Modifier
}
)
.background(topBarContainerColor.copy(alpha = topBarContainerColorAlpha))
.fillMaxWidth()
) {
TopAppBar(
title = {
Text(
text = stringResource(id = UiR.string.title_friends),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent
),
modifier = Modifier.fillMaxWidth(),
)
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
modifier = Modifier,
containerColor = Color.Transparent
) {
tabItems.forEachIndexed { index, item ->
Tab(
selected = index == selectedTabIndex,
onClick = {
if (selectedTabIndex != index) {
selectedTabIndex = index
}
},
text = {
item.titleResId?.let { resId ->
Text(text = stringResource(id = resId))
}
}
)
}
}
}
}
) { padding ->
@@ -164,23 +232,6 @@ fun FriendsScreen(
else -> {
val pullToRefreshState = rememberPullToRefreshState()
var selectedTabIndex by rememberSaveable {
mutableIntStateOf(0)
}
val tabItems = listOf(
TabItem(
titleResId = UiR.string.title_friends_all,
unselectedIconResId = null,
selectedIconResId = null
),
TabItem(
titleResId = UiR.string.title_friends_online,
unselectedIconResId = null,
selectedIconResId = null
)
)
val pagerState = rememberPagerState { tabItems.size }
LaunchedEffect(selectedTabIndex) {
@@ -231,7 +282,7 @@ fun FriendsScreen(
NoItemsView(
modifier = Modifier
.padding(padding.calculateTopPadding())
.padding(top = 64.dp),
.padding(top = 16.dp),
customText = "No${if (index == 1) " online" else ""} friends :("
)
}
@@ -252,55 +303,12 @@ fun FriendsScreen(
state = pullToRefreshState,
modifier = Modifier
.padding(top = padding.calculateTopPadding())
.padding(top = 46.dp)
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter),
contentColor = MaterialTheme.colorScheme.primary
)
}
}
TabRow(
selectedTabIndex = selectedTabIndex,
modifier = Modifier
.padding(top = padding.calculateTopPadding() - 4.dp)
.height(56.dp)
.then(
if (currentTheme.usingBlur) {
Modifier.hazeChild(
state = hazeState,
style = HazeMaterials.thick()
)
} else {
Modifier
}
),
containerColor = tabsContainerColor.copy(
alpha = if (currentTheme.usingBlur) tabsColorAlpha else 1f
),
indicator = { tabPositions ->
TabRowDefaults.PrimaryIndicator(
modifier = Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
width = 48.dp
)
}
) {
tabItems.forEachIndexed { index, item ->
Tab(
selected = index == selectedTabIndex,
onClick = {
if (selectedTabIndex != index) {
selectedTabIndex = index
}
},
text = {
item.titleResId?.let { resId ->
Text(text = stringResource(id = resId))
}
}
)
}
}
}
}
}