Classes: Text, Image, Text.Range, Dimension, FixedSize etc.
- Responsible for storing the layout description
- Responsible for serialization to json format
- Fundamental types for all library signatures
- Can be created using factory methods (see below)
Classes: Text, Image, Container, Pager, Indicator etc.
- Subset of node types
- Conforms to
Divnode type - Have extended syntax (compared with other node types)
- Can be the root elements of the layout
- Similar to
Viewin Android andUIViewin iOS.
Classes: Text.Properties, Image.Properties etc.
- Auxiliary entities for operations with node properties
- Contains all non-constant node properties
- Can be created using factory methods
Classes: DivScope, TemplateScope
- Provides access to factory methods
- Provides access to enum values
- Accumulates templates used while layouting
- Accumulates supplements used while layouting (see below)
Functions: text(...), image(...) etc.
- Available for all node types except abstract types
- Accessible only within
DivScopeorTemplateScope - Signature contains all non-constant node properties
- Each argument should be literal property value (
String,Int,Text.Range,FixedSizeetc.) - Requires call with named arguments (except forced properties)
- Some methods supports calls with partially unnamed parameters (ex.
text("Hello, world!")
Functions: column, row etc.
Some node types has alternative factories with adjusted semantic.
For example type Container has factory column means container with vertical orientation and type EdgeInsets has same named factory edgeInsets with single argument all.
Functions: textProps(...), imageProps(...) etc.
- Available for all node property types
- Accessible only within
DivScopeorTemplateScope - Signature contains all non-constant node properties
- Each argument should be literal property value (
String,Int,Text.Range,FixedSizeetc.) - Requires call with named arguments (no exceptions)
Functions: textRefs(...), imageRefs(...) etc.
- Available for all node property types
- Accessible only within
TemplateScope - Signature contains all non-constant node properties
- Each argument should be a reference property value (
Reference<String>,Reference<Size>etc.) - Requires call with named arguments (no exceptions)
Read only properties: horizontal, verical, start, bottom etc.
- The names matches with values (
start-> "start" in json) - Accessible only within
DivScopeorTemplateScope
Class: Template
- Incapsulates layout invariants
- Body must be invariant (pure randomless function)
- Name must be unique among all templates used in actual div-json
- Can be created using method
template(...)
Class: Component
- Holds template reference and template properties
- Conforms to
Divnode type - Can be created using method
render(...)
Interface: Supplement
- Allows to accumulate supplementary data while layouting
- Can be provided using
supplement(...)method insideDivScopeorTemplateScope
Class: Divan
- Holds card data, templates used in card and layout supplements
- Responsible for div-json serialization
- Can be created using method
divan { ... }
Method divan provides access to DivScope so all syntax available only within divan { ... } body.
The result of divan method call is object of type Divan with fields card: Div (layout), templates: Map<String, Div> (used templates) and supplements: Map<SupplementKey<*>, Supplement>.
val divan = divan {
// Your layout data here
}Root of layout tree is data method with required logId and list of layout states. List of states can be created via listOf(root(...), root(...)) for multiple state layout with explicit stateId or via singleRoot(...) for single state layout with implicit stateId = 0.
val divan = divan {
data(
logId = "my-layout-id",
states = listOf(
root(stateId = 1, div = ...),
root(stateId = 2, div = ...)
)
)
}or
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = ...
)
)
}Layout is a tree of DSL or extension method calls with arguments.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
width = wrapContentSize(),
height = wrapContentSize(),
margins = edgeInsets(left = 10, right = 10, top = 5, bottom = 5),
items = listOf(
text("Hello, world!", fontSize = 18),
image("https://my-image-link")
)
)
)
)
}Decomposition is a great technique for working with complex layout. To carry out part of the layout into a method, it is enough to create an extension for DivScope.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
items = listOf(
header("Hello, world!"),
body(...)
)
)
)
)
}
fun DivScope.header(title: String) = text(
text = title,
fontWeight = bold,
fontSize = 24,
lineHeight = 28
)
fun DivScope.body(...) = ...Composition is a great technique for reusing parts of layout. Each node can be composed with relevant properties. It's convenient to set the element location parameters at the element integration point, and it's style in a separate method. To allow this approach DSL provides syntax div + divProps.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
items = listOf(
header("Hello, world") + textProps(margins = edgeInsets(top = 10)),
body(...) + containerProps(margins = edgeInsets(all = 8))
)
)
)
)
}Alternative way to change node properties is override extension method. Signature of this method contains all non-constant properties of node.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
items = listOf(
header("Hello, world").override(margins = edgeInsets(top = 10)),
body(...).override(margins = edgeInsets(all = 8))
)
)
)
)
}DivKit implements templates approach to allow deduplication of repeated layout parts. Type Component<T : Div> allows to inline templates in layout. Component can be created by method render from specified template and bindings.
- Method
referenceallows to create deferred layout properties which should be resolved withrendercall. - Method
<node>Refsallows to create property references. - Method
deferallows to override node properties by specified property references. - Method
renderallows to "bake" template with bindings intoComponent.
// Template attributes
private val titleRef = reference<String>("title")
// Template layout
private val headerTemplate = template("header-template") {
text(
fontWeight = bold,
fontSize = 24,
lineHeight = 28
) + textRefs(text = titleRef) // or .defer(text = titleRef)
}
// Component signature for usage inside layout tree
fun DivScope.header(title: String) = render(headerTemplate, titleRef bind title)
// Component usage example
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
items = listOf(
header("Hello, world!") + textProps(width = matchParentSize()),
body(...)
)
)
)
)
}DSL allows to inline enum values in layout. You can just start typing value of enum (and even select value from suggest). Check available enum values in documentation.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = container(
orientation = horizontal, // Enum value = "horizontal"
items = listOf(
text("Hello, world!", fontWeight = bold) // Enum value = "bold"
body(...)
)
)
)
)
}DSL contains 3 types of properties: literals, references and expressions. Base class of all properties is Property<T>.
- Methods
valueandvalueOrNullallows to create literal. - Method
referenceallows to create reference (can be resolver later in layout). - Method
expressionallows to create expression (evaluates in runtime).
Method expression<T> allows to create evaluable property. Most types has evaluable properties. If type has no evaluate method, there is no evaluable properties. In common case expressions are used together with variables. Variables can be declared in data object (property variables).
val divan = divan {
data(
logId = "my-layout-id",
states = singleState(
div = column(
items = listOf(
text("Hello, world!").evaluate(alpha = "@{toNumber(alpha_var) / 255.0}"),
slider(
minValue = 0,
maxValue = 255,
thumbValueVariable = "alpha_var",
...
)
)
)
),
variables = listOf(
integerVariable(name = "alpha_var", value = 0)
)
)
}Developer wants to understand structure of layout as fast as possible. The most influential to structure of layout type is container. But "container" don't suggests how layout looks like (it depends on orientation parameter). To enhance readability DSL provides shortcuts column, row and stack.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column( // Container
items = listOf(
row(...), // Container
stack(...) // Container
)
)
)
)
}Container shortcuts has vararg signature for items property so the children can be specified without named parameter.
val divan = divan {
data(
logId = "my-layout-id",
states = singleRoot(
div = column(
row(...),
stack(...),
// Set other properties below children
margins = ...,
background = ...
)
)
)
}A lot of properties in DivKit is a collections (for example background: List<Background>). However property background has more than one element in rare cases. Method listOf() in starting part of value distracts from matter part of value. To solve this issue DSL provides method asList() for each node type.
val divan = divan {
data(
logId = "my-layout-id",
states = singleState(
div = text(
text = "Hello, world!",
background = solidBackground(color("#ffffff")).asList()
)
)
)
}