Reputation: 2382
I want to apply modifier in such a way that if the width is provided, it should use the provided width, else use the max-width available.
I am applying the Modifier in the below fashion, but the result is not as expected. The view width is going haywire. Requesting guidance here.
val myModifier = Modifier.padding(
start = 4.dp, end = 4.dp, top = 8.dp, bottom = 8.dp
)
if (viewWidth == null)
myModifier.then(Modifier.fillParentMaxWidth(1f))
else
myModifier.then(Modifier.width(viewWidth))
myModifier.then(
Modifier.height(viewHeight ?: 100.dp)
.clickable(onClick = { listener.onItemClick(item) })
)
Upvotes: 60
Views: 28956
Reputation: 141
Found another method which I feel is cleaner. Say you want to add a "invoke" to the modifier based on a condition, you can use -
Modifier
.let { if (condition) it.invoke() else it}
Upvotes: 5
Reputation: 3015
I'm giving my answer based on my own implementation based on https://proandroiddev.com/jetpack-compose-tricks-conditionally-applying-modifiers-for-dynamic-uis-e3fe5a119f45
There's nothing Modifier
-specific here. This idea can be generalized to any class.
inline fun <R : Any> R.applyWhen(
condition: Boolean,
block: R.() -> R,
): R = applyChoice(condition = condition, trueBlock = block, falseBlock = { this })
inline fun <R : Any> R.applyChoice(
condition: Boolean,
trueBlock: R.() -> R,
falseBlock: R.() -> R,
): R {
return if (condition) {
trueBlock()
} else {
falseBlock()
}
}
val myModifier = Modifier
.padding(start = 4.dp, end = 4.dp, top = 8.dp, bottom = 8.dp)
.applyChoice(viewWidth == null, { fillParentMaxWidth(1f) }, { width(viewWidth!!) })
.height(viewHeight ?: 100.dp)
.clickable(onClick = { listener.onItemClick(item) })
Upvotes: 1
Reputation: 1129
Just in case if you need to run composable
@Composable
fun Modifier.conditional(condition: Boolean, modifier: @Composable Modifier.() -> Modifier) =
then(if (condition) modifier.invoke(this) else this)
Upvotes: 3
Reputation: 3748
You can create a conditional modifier via an extension function:
fun Modifier.conditional(condition : Boolean, modifier : Modifier.() -> Modifier) : Modifier {
return if (condition) {
then(modifier(Modifier))
} else {
this
}
}
This lets you chain a modifier in a conditional
block like this:
val applySpecialBackground : Boolean = [...]
Column(
modifier = Modifier
.fillMaxWidth()
.conditional(applySpecialBackground) {
background(Color.Red)
}
.padding(16.dp)
) { [...] }
It will only apply the conditional modifier when the condition is true
.
If you need a modifier for the false
case just chain a second negated conditional.
val applySpecialBackground : Boolean = [...]
Column(
modifier = Modifier
.fillMaxWidth()
.conditional(applySpecialBackground) {
background(Color.Red)
}
.conditional(!applySpecialBackground) {
background(Color.Blue)
}
.padding(16.dp)
) { [...] }
If you're feeling super fancy you can also add an optional negative case to the extension function.
fun Modifier.conditional(
condition: Boolean,
ifTrue: Modifier.() -> Modifier,
ifFalse: (Modifier.() -> Modifier)? = null
): Modifier {
return if (condition) {
then(ifTrue(Modifier))
} else if (ifFalse != null) {
then(ifFalse(Modifier))
} else {
this
}
}
This will give you a separate parameter for the case that your condition is false:
val applySpecialBackground : Boolean = [...]
Column(
modifier = Modifier
.fillMaxWidth()
.conditional(
applySpecialBackground,
ifTrue = { background(Color.Red) },
ifFalse = { background(Color.Blue) }
)
.padding(16.dp)
) { [...] }
Big thanks to mtotschnig for pointing out a critical bug in the previous implementation!
Upvotes: 86
Reputation: 683
All the above answers are good enough but i would like to add my one cent. I found this snippet more readable and clean.
ClickableText(
modifier = Modifier
.let {
if (selectedPosition == index) {
return@let it
.background(
Green200,
shape = RoundedCornerShape(12.dp)
)
}
it
}
.padding(horizontal = 12.dp, vertical = 4.dp),
text = AnnotatedString(categories[index]),
style = TextStyle(
fontSize = 14.sp,
textAlign = TextAlign.Center
),
onClick = {
selectedPosition = index
}
)
Upvotes: 8
Reputation: 596
You can also use Modifier.then
in a more compact way:
val modifier = Modifier
.padding(start = 4.dp, end = 4.dp, top = 8.dp, bottom = 8.dp)
.then(if(viewWidth == null) Modifier.fillMaxWidth(1f) else Modifier.width(viewWidth))
.height(viewHeight ?: 100.dp)
.clickable(onClick = { listener.onItemClick(item) })
See: https://jetc.dev/slack/2020-12-13-conditional-modifiers.html
Upvotes: 28
Reputation: 9388
Modifier
has a then
function to concatenate the current modifier with another modifier. This then
function returns a new modifier that you have not used it. You have to re-initialize your myModifier
variable with the returned modifier.
Check the below code:
var myModifier = Modifier.padding(
start = 4.dp, end = 4.dp, top = 8.dp, bottom = 8.dp
)
if (viewWidth == null)
myModifier = myModifier.then(Modifier.fillParentMaxWidth(1f))
else
myModifier = myModifier.then(Modifier.width(viewWidth))
myModifier = myModifier.then(
Modifier
.height(viewHeight ?: 100.dp)
.clickable(onClick = { listener.onItemClick(item) })
)
Upvotes: 31