Is there a way to alpha mask a composable? - kotlin

I need to apply an alpha mask/blend (Porter-Duff destination-in composition) to a composable, from another composable, not just clip it.
Is there a way to do this?

There's an alpha(Float) modifier. If the composable accepts the modifier parameter, we can just append Modifier.alpha(value) to it.
All standard composables accept the modifier parameter. It's generally useful to add the modifier parameter to our composables.
#Composable
fun MyComposable(modifier: Modifier, alpha: Float) {
// Any base composable which accepts a modifier
Column(modifier = modifier.alpha(alpha)) {
// content...
}
}
MyComposable(modifier = Modifier.fillMaxWidth(), alpha = 0.5f)
However, if it's not possible then we can always wrap the composable within a Box and apply the alpha modifier to it. There can be a wrapper composable (let's say AlphaMask()) which does this.
/**
* #param alpha: Alpha value between 0f..1f
*/
#Composable
fun AlphaMask(modifier: Modifier = Modifier, alpha: Float = 1f, content: #Composable () -> Unit){
Box(modifier.alpha(alpha)){
content()
}
}
#Composable
fun Usage(){
AlphaMask(alpha = 0.5f) {
AnyGeneralComposable()
}
}

Related

Jetpack compose remember function not working

I'm trying to create a scrollable background in Jetpack Compose.
The problem is that the variable "currentPadding" isn't updating it's state after the value "padding" is modified after recomposition. In the first composition (loading state) the "padding" value is set to 112.dp and after load the value changes to 160.dp.
It's strange because I have used the remember function this way multiple times in other places in the app and it's the first time that this happens.
Could you help me out?
Thanks a lot.
#Composable
fun ScrollableBackground(
scrollState: ScrollState,
composable: ComposableFun,
modifier: Modifier = Modifier,
isBannerListEmpty: Boolean,
) {
val padding = if (isBannerListEmpty) {
112.dp
} else {
160.dp
}
val minPadding: Dp = 29.dp
val dp0 = dimensionResource(id = R.dimen.no_dp)
var currentPadding: Dp by remember { mutableStateOf(padding) }
val state: Dp by animateDpAsState(targetValue = currentPadding)
val nestedScrollConnection: NestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val percent = scrollState.value.toFloat() / scrollState.maxValue.toFloat() * 100f
val delta = available.y.dp
val newSize = currentPadding + delta / 3
currentPadding = when {
percent > 20f -> minPadding
newSize < minPadding -> minPadding
newSize > padding -> padding
else -> newSize
}
return Offset.Zero
}
}
}
Box(
modifier = modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
Surface(
color = White,
modifier = Modifier
.padding(top = state)
.fillMaxSize()
.clip(
CircleShape.copy(
topStart = Shapes.medium.topStart,
topEnd = Shapes.medium.topEnd,
bottomEnd = CornerSize(dp0),
bottomStart = CornerSize(dp0)
)
)
) {}
composable.invoke()
}
}
I tried sending other kind of parameters from the view model to the composable (instead of a boolean, in this case "isBannerListEmpty"), like the current desired padding value, and nothing seems to work.
You have put nestedScrollConnection in remember. It also remembers the padding variable when first encountered. So when the actual value of padding is changed, this change is not propagated in remember of nestedScrollConnection.
Put padding inside onPreScroll.
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val padding = if (...) { //Don't use isBannerListEmpty here as neither this will update on recomposition
112.dp
} else {
160.dp
}
...

Using Kotlin, Compose and a timer to update a custom Floating Action Bar

I'm trying to learn compose and I wanted to make a custom Floating Action Button with the following requirements:
A composeable function that updates every second
A main function that has a state which holds the number of seconds elapsed
A timer function that runs and updates the composeable function every second
Unfortunately the timer Floating Action Button seems to update every millisecond. #Preview only works if the first parameter of CustomFab is initialized to 0 (or I assume any other integer) but in that case the timer is never greater than zero.
Here is my attempt using Kotlin and Compose:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var seconds by mutableStateOf(0)
setContent {
CustomFab(fseconds=seconds)
timer(period = 10000) { seconds++
}
}
}
}
#Composable
fun CustomFab(fseconds: Int, modifier: Modifier = Modifier) {
Box {
Surface(
modifier = Modifier
.size(128.dp)
.align(Alignment.Center),
color = androidx.compose.ui.graphics.Color.Green,
shape = CircleShape,
content = { })
Text(
text = fseconds.toString(),
modifier = Modifier.align(Alignment.Center)
)
}
}
What do I need to do to make this work? Thank you.

JETPACK COMPOSE "Cannot find parameter with this name: contentAlignment"

Am i missing an import or something? Why is this basic function giving me errors all of a sudden
No, you didn't miss anything.
You need only to add your content parameter, and your alignment parameter would be normal.
Example:
Box(modifier = Modifier,
contentAlignment = Alignment.TopStart,
content = {}
)
It happens because exists a Box constructor with no content as in your example code:
#Composable
fun Box(modifier: Modifier): Unit
The contentAlignment doesn't exist in this constructor.
You can use the constructor with the contentAlignment parameter and in this case you have to pass also the content parameter:
#Composable
inline fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: #Composable #ExtensionFunctionType BoxScope.() -> Unit
): Unit
For example:
Box(
modifier = Modifier,
contentAlignment = Alignment.Center
){
//content
}
I have also something to add . After what you have typed just open the semicolons and the error goes away , like i mentioned below
Box(modifier = Modifier,
contentAlignment = Alignment.TopStart
){
// Semicolon opening
}

Extending Material Theme with Kotlin extension property doesn't work

The official documentations gave this code example:
// Use with MaterialTheme.typography.textFieldInput
val Typography.textFieldInput: TextStyle
get() = TextStyle(/* ... */)
So I defined my own h1 style as follows
val Typography.h1: TextStyle
get() = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
and use it
#Preview
#Composable
fun GenericEmptyState(
#PreviewParameter(EmptyStatePreviewParameters::class) data: EmptyStateData
) {
MaterialTheme {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize(1f)
.background(Color.White)
.padding(24.dp)
) {
Image(
painterResource(data.image),
contentDescription = "illustration",
modifier = Modifier
.size(150.dp)
.padding(6.dp)
)
Text(data.title, Modifier.padding(6.dp), style = MaterialTheme.typography.h1)
//...
But the result text style I got is definitely not size 20 or bold font weight
It looks like the default h1 style in Material Theme
Can't see what's wrong myself
Can anyone?
Well, the Typography class itself has a member named h1 which will be used instead of your extension. You should actually get this warning in Android studio which explains it pretty well:
Extension is shadowed by a member: public final val h1: TextStyle
The documentation you mention is called "Custom design systems in Compose" - this is meant for someone who doesn't want to use the default Typography properties - either needs more text styles than provided or simply wants to name them differently. You should probably look at this section first - it shows how to create your own theme and override the default Typography styles. It should look something like this:
#Composable
fun MyTheme(
content: #Composable () -> Unit,
) {
MaterialTheme(
typography = Typography(
h1 = TextStyle(...),
),
content = content,
)
}
#Composable
fun MyContent() {
MyTheme {
// MaterialTheme.typography.h1 will be your defined TextStyle here
}
}

Should I use remember with animateDpAsState?

The Code A is from the official sample code here.
I know that in order to preserve state across recompositions, remember the mutable state using remember.
I think that the code val extraPadding by animateDpAsState(...) should be val extraPadding by remember { animateDpAsState(...) }, is it right?
BTW, val extraPadding by remember { animateDpAsState(...) } will cause the error
Composable calls are not allowed inside the calculation parameter of inline fun remember(calculation: () -> TypeVariable(T)): TypeVariable(T)
Code A
#Composable
private fun Greeting(name: String) {
var expanded by remember { mutableStateOf(false) }
val extraPadding by animateDpAsState( //Should I add remember
if (expanded) 48.dp else 0.dp
)
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello, ")
Text(text = name)
}
OutlinedButton(
onClick = { expanded = !expanded }
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
No, you shouldn't. This function is marked with #Composable so it should be used directly in the view builder.
animateDpAsState will calculate its value depending on targetValue on each recomposition.
If you check it source code you'll see, that it uses remember inside, that's why it's marked with #Composable, and that's why you shouldn't bother about remembering some values manually.
For anyone that comes across this in the future, instead of attempting to remember the animateDpAsState it would be better to remember the expansion state. Because in this case we're trying to remember the state in an item of a list we use rememberSaveable:
var expanded by rememberSaveable { mutableStateOf(false) }
A very similar case of storing indexes across list items is shown in the official documentation here