Types of Props
The Components page details how to declare a component and its props as standard val
properties. This page covers two more ways for a parent component to configure its children with props: Common props and Tree props.
Common Propsβ
A common prop is one of a set of properties that can be applied to any primitive component.
Common props are supported by all primitive components. They include View properties like onClick
and background
, visibility events like onVisible
, as well as layout parameters like margin
.
Common props are specified using the Style
API, which enables one or more common props to be chained together into a Style object that is passed to the components as a prop, as shown in the following example:
val style = Style.margin(all = 4.px)
.backgroundColor(Color.RED)
.viewTag("my_view")
For a component to be configured with a Style, it should declare a Style prop (such as val style: Style? = null
). It should then pass that Style object to one of the components it renders to, usually the root component it returns from render
. In this way, the Style will eventually end up on a primitive component (for example, Text
or Row)
. All common props end up being materialized by primitive components, as shown in the following example:
class StyledHelloComponent(private val style: Style? = null, private val name: String) :
KComponent() {
override fun ComponentScope.render(): Component? {
return Text(style = style, text = "Hello $name!")
}
}
val componentWithOnClick =
StyledHelloComponent(style = Style.onClick { log("clicked!") }, name = "Common Props")
Combining a Style from above with a local Styleβ
The following 'more advanced' example shows how you can combine a Style taken from above with the Style the component itself wants to define:
class OuterTextComponent : KComponent() {
override fun ComponentScope.render(): Component? {
return InnerTextComponent(style = Style.margin(all = 8.dp))
}
}
class InnerTextComponent(private val style: Style? = null) : KComponent() {
override fun ComponentScope.render(): Component? {
return Text(
style = Style.padding(all = 8.dp).alpha(.5f) + style,
text = "I accept style from a parent!")
}
}
Working with Styleβ
In the above example, you may have noticed the use of +
to combine the Style passed from OuterTextComponent
and the styles that InnerTextComponent
defines. The +
operator combines two Styles into a single Style without mutating either:
val alphaStyle = Style.alpha(1f)
val combinedStyle = alphaStyle + Style.padding(all = 8.dp).margin(all = 8.dp)
// Result:
// alphaStyle: (alpha: 1f)
// combinedStyle: (alpha: 1f) <- (padding-all: 8.dp) <- (margin-all: 8.dp)
Note that ordering around +
matters: if a Style property is defined twice, the last definition takes precendence:
val alphaStyle = Style.alpha(1f)
val combinedStyle = alphaStyle + Style.padding(all = 8.dp).alpha(.5f)
// Result:
// combinedStyle will apply padding of 8.dp and alpha of .5f
Generally, Style objects are immutable: any time you combine Styles or add new properties to a Style, you get a new Style instance that contains all the properties of the previous Style plus the new properties.
Java - Kotlin compatibilityβ
If you need to pass down a Style from a Java class to a Kotlin KComponent
, use StyleCompat
:
@LayoutSpec
class OuterStyleComponentSpec {
@OnCreateLayout
static Component onCreateLayout(ComponentContext c) {
return new InnerTextComponent(StyleCompat.marginDip(YogaEdge.ALL, 8).build());
}
}
For passing Style from Kotlin code to a Java Component Spec
, you can use .kotlinStyle()
, which is equivalent to setting all the common props the Style defines:
class OuterStyleKComponent : KComponent() {
override fun ComponentScope.render(): Component? {
val style = Style.margin(all = 8.dp)
return OuterStyleComponent.create(context).kotlinStyle(style).build()
}
}
Tree Propsβ
A tree prop is a special type of prop that is transparently passed from a parent component to its children.
A TreeProp is a special type of prop which is transparently passed from a parent component to its children. It provides a convenient way to share contextual data or utilities in a tree without having to explicitly pass val
properties to every component in your hierarchy.
Declaring a Tree Propβ
In order to declare a TreeProp you need to use TreePropProvider
:
return TreePropProvider(
Typeface::class.java to Typeface.DEFAULT_BOLD,
String::class.java to getTextTitle(),
Int::class.java to Color.RED) {
TreePropsChildComponent()
}
You can only declare one TreeProp for any one given type. If a child of ParentComponent also defines a TreeProp of the given type, it will override the value of that TreeProp for all its children (but not for itself).
Using a Tree Propβ
The child component can access the TreeProp value through a ComponentScope.getTreeProp<>()
method that has the same type that was declared in the parents TreePropProvider
call:
val color = getTreeProp<Int>()
val typeface = getTreeProp<Typeface>()
val title = getTreeProp<String>()
When to Use Tree Propsβ
Tree Props are powerful, but if overused, they can make your component code more difficult to understand. The best practice is to only use tree props for properties that the whole tree needs to know about (such as theming information or loggers) and not just as a more convenient way to get props to the leaves of a tree of components.