Reputation: 969
How can I create reusable modifiers without android compose lint rules throwing a fit?
I don't want to have to copy/paste the same modifiers for every screen within my app, I would rather just create an extension function I can call like this,
Box(modifier = Modifier.defaultFillScreen())
But that extension function, shown below, keeps throwing lint errors.
@Composable
fun Modifier.defaultFillScreen() = this.then(Modifier
.fillMaxWidth()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.padding(dimensionResource(id = R.dimen.standard_padding)))
Gives me the following lint error:
ComposableModifierFactory: Modifier factory functions should not be marked as @Composable, and should use composed instead
When I make that change I then get a new lint error:
fun Modifier.defaultFillScreen() = composed { this.then(Modifier
.fillMaxWidth()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.padding(dimensionResource(id = R.dimen.standard_padding))) }
UnnecessaryComposedModifier: Unnecessary use of Modifier.composed
How can I create a reusable modifier without compose complaining about it? Writing the same 5 lines of modifier code for every screen is not an acceptable answer.
dependencies:
'androidx.activity:activity-compose:1.3.1',
'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07',
'androidx.compose.material:material:1.0.5',
'androidx.navigation:navigation-compose:2.4.0-alpha06',
'androidx.compose.ui:ui:1.0.5',
'androidx.compose.ui:ui-tooling:1.0.5'
Android studio:
Android Studio Arctic Fox | 2020.3.1 Build #AI-203.7717.56.2031.7583922, built on July 26, 2021
Upvotes: 2
Views: 2849
Reputation: 67443
I don't see the same warning with compose 1.2.0-alpha and Android Studio Bubblebee, it used to appear when i use Modifier.composed
without state
.
Purpose of Modifier.composed is having stateful modifiers which you use with remember
, LaunchedEffect
. When you don't have a state associated with your Modifier you should you Modifier.then instead
fun Modifier.composedBackground(width: Dp, height: Dp, index: Int) = composed(
// pass inspector information for debug
inspectorInfo = debugInspectorInfo {
// name should match the name of the modifier
name = "myModifier"
// add name and value of each argument
properties["width"] = width
properties["height"] = height
properties["index"] = index
},
// pass your modifier implementation that resolved per modified element
factory = {
val density = LocalDensity.current
val color: Color = remember(index) {
Color(
red = Random.nextInt(256),
green = Random.nextInt(256),
blue = Random.nextInt(256),
alpha = 255
)
}
// 🔥 Without remember this color is created every time item using this modifier composed
// val color: Color = Color(
// red = Random.nextInt(256),
// green = Random.nextInt(256),
// blue = Random.nextInt(256),
// alpha = 255
// )
// add your modifier implementation here
Modifier.drawBehind {
val widthInPx = with(density) { width.toPx() }
val heightInPx = with(density) { height.toPx() }
drawRect(color = color, topLeft = Offset.Zero, size = Size(widthInPx, heightInPx))
}
}
)
This is just a sample composed example. If you change from remember you will see that at each recomposition random color will change.
And without composed it will give error @Composable invocations can only happen from the context of a @Composable function
if you use remember like the snippet below.
fun Modifier.nonComposedBackground(width: Dp, height: Dp, index: Int) = this.then(
// add your modifier implementation here
Modifier.drawBehind {
val color: Color = remember(index) {
Color(
red = Random.nextInt(256),
green = Random.nextInt(256),
blue = Random.nextInt(256),
alpha = 255
)
}
val widthInPx = width.toPx()
val heightInPx = height.toPx()
drawRect(color = color, topLeft = Offset.Zero, size = Size(widthInPx, heightInPx))
}
)
Upvotes: 2