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.annotation.SuppressLint
import android.util.Log import android.util.Log
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.calculateStartPadding
@@ -52,6 +55,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll 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
@@ -126,20 +130,26 @@ fun ChatMaterialsScreen(
Log.d("ChatMaterialsScreen", "ChatMaterialsScreen: canScrollBackward: $canScrollBackward") Log.d("ChatMaterialsScreen", "ChatMaterialsScreen: canScrollBackward: $canScrollBackward")
val toolbarColorAlpha by animateFloatAsState( val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!canScrollBackward) 1f else 0f, targetValue = if (!currentTheme.usingBlur || !canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha", label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50) animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
) )
val toolbarContainerColor by animateColorAsState( val topBarContainerColor by animateColorAsState(
targetValue = targetValue =
if (currentTheme.usingBlur || !canScrollBackward) if (currentTheme.usingBlur || !canScrollBackward)
MaterialTheme.colorScheme.surface MaterialTheme.colorScheme.surface
else else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp), MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha", label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50) animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
) )
val pullToRefreshAlpha by animateFloatAsState( val pullToRefreshAlpha by animateFloatAsState(
@@ -152,10 +162,7 @@ fun ChatMaterialsScreen(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( Column(
title = {
Text(text = "Chat Materials")
},
modifier = Modifier modifier = Modifier
.then( .then(
if (currentTheme.usingBlur) { if (currentTheme.usingBlur) {
@@ -163,93 +170,99 @@ fun ChatMaterialsScreen(
state = hazeState, state = hazeState,
style = hazeStyle style = hazeStyle
) )
} else Modifier } 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
} }
) { )
Icon( .background(topBarContainerColor.copy(alpha = topBarContainerColorAlpha))
imageVector = Icons.Outlined.MoreVert, .fillMaxWidth()
contentDescription = "Options button" ) {
) TopAppBar(
} title = { Text(text = "Chat Materials") },
colors = TopAppBarDefaults.topAppBarColors(
DropdownMenu( containerColor = Color.Transparent
modifier = Modifier.defaultMinSize(minWidth = 140.dp), ),
expanded = dropDownMenuExpanded, modifier = Modifier.fillMaxWidth(),
onDismissRequest = { navigationIcon = {
dropDownMenuExpanded = false IconButton(onClick = onBack) {
}, Icon(
offset = DpOffset(x = (-4).dp, y = (-60).dp) imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
) { contentDescription = null
DropdownMenuItem( )
}
},
actions = {
IconButton(
onClick = { 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 dropDownMenuExpanded = false
}, },
text = { offset = DpOffset(x = (-4).dp, y = (-60).dp)
Text(text = stringResource(id = R.string.action_refresh)) ) {
}, DropdownMenuItem(
leadingIcon = { onClick = {
Icon( viewModel.onRefresh()
imageVector = Icons.Rounded.Refresh, dropDownMenuExpanded = false
contentDescription = null },
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 -> ) { padding ->
Box( Box(
@@ -48,7 +48,7 @@ fun FriendsList(
) { ) {
item { item {
Spacer(modifier = Modifier.height(padding.calculateTopPadding())) Spacer(modifier = Modifier.height(padding.calculateTopPadding()))
Spacer(modifier = Modifier.height(64.dp)) Spacer(modifier = Modifier.height(16.dp))
} }
items( items(
@@ -1,8 +1,10 @@
package com.meloda.app.fast.friends.presentation package com.meloda.app.fast.friends.presentation
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
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.Column
import androidx.compose.foundation.layout.WindowInsets 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.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.rememberLazyListState 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.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab 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.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
@@ -39,9 +40,11 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll 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.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle 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.LocalHazeState
import com.meloda.app.fast.designsystem.LocalTheme import com.meloda.app.fast.designsystem.LocalTheme
import com.meloda.app.fast.designsystem.TabItem 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.FullScreenLoader
import com.meloda.app.fast.designsystem.components.NoItemsView import com.meloda.app.fast.designsystem.components.NoItemsView
import com.meloda.app.fast.friends.FriendsViewModel import com.meloda.app.fast.friends.FriendsViewModel
@@ -121,32 +123,98 @@ fun FriendsScreen(
animationSpec = tween(durationMillis = 50) animationSpec = tween(durationMillis = 50)
) )
val tabsColorAlpha by animateFloatAsState( val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!listState.canScrollBackward) 1f else 0f, targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha", label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50) animationSpec = tween(
durationMillis = 200,
easing = FastOutLinearInEasing
)
) )
val tabsContainerColor by animateColorAsState( val topBarContainerColor by animateColorAsState(
targetValue = targetValue =
if (currentTheme.usingBlur || !listState.canScrollBackward) if (currentTheme.usingBlur || !listState.canScrollBackward)
MaterialTheme.colorScheme.surface MaterialTheme.colorScheme.surface
else else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp), MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha", 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( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentWindowInsets = WindowInsets.statusBars, contentWindowInsets = WindowInsets.statusBars,
topBar = { topBar = {
Column(modifier = Modifier.fillMaxWidth()) { Column(
BlurrableTopAppBar( modifier = Modifier
title = stringResource(id = UiR.string.title_friends), .then(
listState = listState, if (currentTheme.usingBlur) {
hazeState = hazeState 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 -> ) { padding ->
@@ -164,23 +232,6 @@ fun FriendsScreen(
else -> { else -> {
val pullToRefreshState = rememberPullToRefreshState() 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 } val pagerState = rememberPagerState { tabItems.size }
LaunchedEffect(selectedTabIndex) { LaunchedEffect(selectedTabIndex) {
@@ -231,7 +282,7 @@ fun FriendsScreen(
NoItemsView( NoItemsView(
modifier = Modifier modifier = Modifier
.padding(padding.calculateTopPadding()) .padding(padding.calculateTopPadding())
.padding(top = 64.dp), .padding(top = 16.dp),
customText = "No${if (index == 1) " online" else ""} friends :(" customText = "No${if (index == 1) " online" else ""} friends :("
) )
} }
@@ -252,55 +303,12 @@ fun FriendsScreen(
state = pullToRefreshState, state = pullToRefreshState,
modifier = Modifier modifier = Modifier
.padding(top = padding.calculateTopPadding()) .padding(top = padding.calculateTopPadding())
.padding(top = 46.dp)
.alpha(pullToRefreshAlpha) .alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter), .align(Alignment.TopCenter),
contentColor = MaterialTheme.colorScheme.primary 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))
}
}
)
}
}
} }
} }
} }