I have seen this in another person's code:
#Composable
fun UsingFraction() {
Column(modifier = Modifier
.fillMaxSize(1f)
.background(Color(0xff888888))) {
Text(text = "Testing, fraction 123 ...")
}
}
What's the purpose of the fraction-argument "1f" in this specific case?
I know, that it is for distributing available space. But I could find no difference, when I had it in or when I removed it. Concerning the shown snippet.
Default parameter for fillMaxSize() is 1.0f, therefore you don't see any difference.
Checkout the documentation for more details;
https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier#(androidx.compose.ui.Modifier).fillMaxSize(kotlin.Float)
Related
On iOS there is EmptyView here https://developer.apple.com/documentation/swiftui/emptyview. But I don't know how to implement it on Compose. If I have it, for some code is much easier for me. For example,
myList.map { item ->
if item is XItem -> EmptyView()
....
}
Don't tell me I need not it, I just know how to implement it. Thanks.
Compose is built much different than SwiftUI.
In SwiftUI you need to use EmptyView in two cases:
When you have a genetic parameter and it should be empty in some cases - e.g. you need to define some default type in case when the parameter is not specified.
When the context requires you to return some view.
On the other side, Compose doesn't have such problems in the first place, that's why no such view exists.
In cases when SwiftUI will give you an error around an empty #ViewBuilder block, Compose will be totally fine.
In your example you can use Unit:
myList.map { item ->
if item is XItem -> Unit
....
}
Or just empty braces:
myList.map { item ->
if item is XItem -> { }
....
}
If you'll find a case when you really need some empty view, you can use Box(Modifier) - it'll be an empty view with zero size.
I think you can use a Spacer component to display an empty space.
Spacer accepts Modifier object as a parameter, you can then use this modifier to set Spacer’s width or height or both.
For instance, you can draw a Spacer in your code but this needs to be done in a composable context or inside another composable.
#Composable
fun MyComposable(){
myList.map { item ->
if item is XItem -> Spacer(modifier = Modifier.size(100.dp, 100.dp))
....
}
}
You can easily create one yourself:
#Composable
fun EmptyView() {
}
which can be replaced / inlined by
{}
Taking first steps in Jetpack Compose, which is quite amazing except one annoying issue.
I have a constant set of previews: Normal, Dark and RTL:
#Preview(
name = "Normal",
group = "Screen",
showBackground = true
)
#Preview(
name = "Dark",
group = "Screen",
showBackground = true,
uiMode = Configuration.UI_MODE_NIGHT_YES
)
#Preview(
name = "RTL",
group = "Screen",
showBackground = true,
locale = "iw"
)
#Composable
fun JustAComposable() {
...
}
Let's just say, for example that I preview 50 composable functions. I need to copy-paste this set 50 times, which is absolutely incorrect.
Annotation inheritance is forbidden, so my question is: did anybody find a way to reuse the same set of previews across all composable functions?
The only 2 solutions which I could think of are:
To use multiple custom previews only while developing.
Partially reuse the previews - use compile-time constants for name and group.
Edit:
I created a feature request to compose team to be able to create custom annotation and annotate the annotation with all of the previews I want to reuse.
This way I only need to use my custom annotation.
Can be tracked in Google Issue Tracker
The accepted feature request is now implemented and is available starting from Android Studio Dolphin and Jetpack Compose 1.2.0-beta01.
It is called Multipreview Annotation. More information about this feature can be found here.
In order to use this feature, you must create a custom annotation class.
#Preview(
name = "small font",
group = "font scales",
fontScale = 0.5f
)
#Preview(
name = "large font",
group = "font scales",
fontScale = 1.5f
)
annotation class FontScalePreviews
and now you can apply this annotation class. For example:
#FontScalePreviews
#Composable
fun HelloWorldPreview() {
Text("Hello World")
}
I have a layout which looks like this:
Row {
...
Box(
modifier = Modifier
.fillMaxHeight()
.width(50.dp)
) {
AnimatedVisibility(
visible = isSelected && selectedAnimationFinished,
enter = fadeIn(),
exit = fadeOut()
) {
...
}
}
}
But I get the compile-time error:
fun RowScope.AnimatedVisibility(visible: Boolean, modifier: Modifier = ..., enter: EnterTransition = ..., exit: ExitTransition = ..., content: AnimatedVisibilityScope.() -> Unit): Unit' can't be called in this context by implicit receiver. Use the explicit one if necessary
It appears that Kotlin finds the AnimatedVisibility function ambiguous, since Compose exposes multiple AnimatedVisibility functions with the same signature: there's a fun AnimatedVisibility with no receiver, and a fun RowScope.AnimatedVisibility which requires RowScope.
From what I can gather, Kotlin is complaining about me using the RowScope version incorrectly, but I just want to use the version with no receiver!
Using this.AnimatedVisibility also doesn't help.
The only workaround I've found that works is to fully qualify the name, like androidx.compose.animation.AnimatedVisibility(...). But I have no idea why this works.
Can anyone shed some light on this? Is there a better option I can use than fully qualifying the name?
One workaround is to use a fully qualified name:
Box {
androidx.compose.animation.AnimatedVisibility(visibile = ...) {
...
}
}
Looks like it's a bug in the language - overload resolution is not aware of #DslMarkers and such stuff. I couldn't find related issues on Kotlin bugtracker so I filed one myself - https://youtrack.jetbrains.com/issue/KT-48215.
Another workaround is creating new composable method and using it in a Row:
#Composable
fun AnimatedThings() {
Box {
AnimatedVisiblity(visible = ...) {
...
}
}
}
I had this problem too, I used StateValue for the value of the AnimatedVisibility with a default value of true.
I found out that was fixed to me by giving the StateValue a default value of false and then using LaunchedEffect to change the value to true, or by clicking on any view on the screen that changes the value to true.
Before Jetpack Compose ConstraintLayout was the recommended way of building complex layouts since it allows to flatten UI hierarchies. See Manage complexity: layouts matter documentation section.
The most common case in which layout takes an especially long time is when hierarchies of View objects are nested within one another. Each nested layout object adds cost to the layout stage. The flatter your hierarchy, the less time that it takes for the layout stage to complete.
Is it still true in the Compose world?
This is not the case in Compose. Use ConstraintLayout if it helps you to implement a layout but not because of performance concerns. See Compose ConstraintLayout documentation:
Note: In the View system, ConstraintLayout was the recommended way to create large and complex layouts, as a flat view hierarchy was better for performance than nested views are. However, this is not a concern in Compose, which is able to efficiently handle deep layout hierarchies.
It's actually the opposite. When performance is an issue you must be careful using ConstraintLayout in Jetpack Compose. ConstraintLayout calls MeasureLayout under the hood which comes with serious performance warning which is
#Composable
#UiComposable
#Deprecated(
"This API is unsafe for UI performance at scale - using it incorrectly will lead " +
"to exponential performance issues. This API should be avoided whenever possible."
)
fun MultiMeasureLayout(
modifier: Modifier = Modifier,
content: #Composable #UiComposable () -> Unit,
measurePolicy: MeasurePolicy
) {
// Rest of the code
}
#Composable
inline fun ConstraintLayout(
modifier: Modifier = Modifier,
optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
crossinline content: #Composable ConstraintLayoutScope.() -> Unit
) {
val measurer = remember { Measurer() }
val scope = remember { ConstraintLayoutScope() }
val remeasureRequesterState = remember { mutableStateOf(false) }
val (measurePolicy, onHelpersChanged) = rememberConstraintLayoutMeasurePolicy(
optimizationLevel,
scope,
remeasureRequesterState,
measurer
)
#Suppress("Deprecation")
MultiMeasureLayout(
modifier = modifier.semantics { designInfoProvider = measurer },
measurePolicy = measurePolicy,
content = {
val previousHelpersHashCode = scope.helpersHashCode
scope.reset()
scope.content()
if (scope.helpersHashCode != previousHelpersHashCode) onHelpersChanged()
}
)
}
You can use when performance is not an issue and you don't want to build nested Composables there won't be an issue with ConstraintLayout, when layout is complex take MultiMeasureLayout warning message into consideration.
I saw the new Jetpack Compose in Android and decided to check it out. I have been trying to understand some basic concepts about composables.
My question is: Can composable functions call non-composable functions?
I have searched Google to no avail.
My question is: Can composable functions call non-composable functions?
Yes. Pretty much everything in Kotlin winds up as a function call, and most functions available to you are non-composable.
Here is one of Google's bits of sample Compose UI code:
#Composable
fun NewsStory() {
val image = imageResource(R.drawable.header)
Column(
modifier = Modifier.padding(16.dp)
) {
val imageModifier = Modifier
.preferredHeight(180.dp)
.fillMaxWidth()
Image(image, modifier = imageModifier,
contentScale = ContentScale.Crop)
Spacer(Modifier.preferredHeight(16.dp))
Text("A day in Shark Fin Cove")
Text("Davenport, California")
Text("December 2018")
}
}
In that, the following functions are not #Composable:
imageResource()
Modifier.padding()
Modifier.preferredHeight()
Modifier.fillMaxWidth()
The rule is that a function marked with #Composable needs to be called by another function marked as #Composable or one of a small family of end consumers of composable functions. This is reminiscent of coroutines, where suspend functions need to be called by other suspend functions or one of a small family of end consumers of suspend functions (e.g., coroutine builders like launch()).