Skip to main content

Migrating to 0.11.0

Upgrade to Kotlin 2.0

Doodle now requires Kotlin 2.0. So you'll need to update your app to use the newer version of Kotlin. See installation for instructions.

Adjust to the new layout paradigm

A View's bounds is no longer editable directly as it was in previous versions of Doodle. This is a major change to the way Doodle layout functions; but it is important to avoid some major pitfalls of the previous approach. Namely, it was very easy to write code that would not produce the expected layout. This is a good example of something that would cause issues before:

0.10.x
import io.nacular.doodle.controls.text.Label import io.nacular.doodle.core.view import io.nacular.doodle.layout.constraints.constrain import io.nacular.doodle.layout.constraints.fill fun labelFitText() { //sampleStart view { + Label("Hello") // Label fits its text by default layout = constrain(children.first(), fill) // ❌ Label would ignore layout } //sampleEnd }

Now it just works as expected since Views cannot override the Layout they are managed by.

0.11.0
import io.nacular.doodle.controls.text.Label import io.nacular.doodle.core.view import io.nacular.doodle.layout.constraints.constrain import io.nacular.doodle.layout.constraints.fill fun labelFitText() { //sampleStart view { + Label("Hello") // Label fits its text by default layout = constrain(children.first(), fill) // ✅ works as expected } //sampleEnd }

More specifically, a View is given a min and max size it can take by its parent's Layout. It then picks a size it would like to take in that range and reports back to the Layout, which uses that information to position/size it and the other Views it manages.

This means you cannot directly change a View's bounds like before; and can only suggest changes (see suggestBounds etc.) that the View may use to determine its final size. Therefore, code like the following no longer works, and cannot be directly converted to this new bounds-suggestion system; since bounds suggestions are not guaranteed in the way a normal setter would be.

0.10.x
import io.nacular.doodle.core.view import io.nacular.doodle.geometry.Size fun invalidSize() { //sampleStart val view1 = view {}.apply { size = Size(10, 50) } val view2 = view {}.apply { size = view1.size } // view2.size == Size(10, 50) //sampleEnd }
0.11.0
import io.nacular.doodle.core.view fun invalidSize() { //sampleStart val view1 = view {}.apply { suggestSize(10.0, 50.0) } val view2 = view {}.apply { suggestSize(view1.size) } // view2.size == Size.Empty since suggestion usually async //sampleEnd }

Update custom Layouts

Your custom Layouts will need to change because the APIs have evolved in the latest version. Layouts are no longer aware of the View they are laying out. Instead, they now work with a sequence of Views directly and have all other necessary context provided in the call to layout.

0.10.x
import io.nacular.doodle.core.Layout import io.nacular.doodle.core.PositionableContainer import io.nacular.doodle.geometry.Rectangle //sampleStart class SimpleLayout: Layout { override fun layout(container: PositionableContainer) { container.children.forEach { it.bounds = Rectangle(0.0, 0.0, container.width, container.height) } } } //sampleEnd
0.11.0
import io.nacular.doodle.core.Layout import io.nacular.doodle.core.Positionable import io.nacular.doodle.geometry.Rectangle import io.nacular.doodle.geometry.Size import io.nacular.doodle.layout.Insets //sampleStart class SimpleLayout: Layout { override fun layout(views: Sequence<Positionable>, min: Size, current: Size, max: Size, insets: Insets): Size { views.forEach { it.updateBounds(Rectangle(0.0, 0.0, current.width, current.height)) } return current } } //sampleEnd