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
Div
node type - Have extended syntax (compared with other node types)
- Can be the root elements of the layout
- Similar to
View
in Android andUIView
in 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
DivScope
orTemplateScope
- Signature contains all non-constant node properties
- Each argument should be literal property value (
String
,Int
,Text.Range
,FixedSize
etc.) - 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
DivScope
orTemplateScope
- Signature contains all non-constant node properties
- Each argument should be literal property value (
String
,Int
,Text.Range
,FixedSize
etc.) - 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
DivScope
orTemplateScope
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
Div
node type - Can be created using method
render(...)
Interface: Supplement
- Allows to accumulate supplementary data while layouting
- Can be provided using
supplement(...)
method insideDivScope
orTemplateScope
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
reference
allows to create deferred layout properties which should be resolved withrender
call. - Method
<node>Refs
allows to create property references. - Method
defer
allows to override node properties by specified property references. - Method
render
allows 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
value
andvalueOrNull
allows to create literal. - Method
reference
allows to create reference (can be resolver later in layout). - Method
expression
allows 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()
)
)
)
}