Skip to main content

Creating Applications

All Doodle apps run within an Application. It is the entry-point for your business logic, and often the first class you write. Doodle fully initializes your app at constructor time, so there is no additional run or start method to implement. You can provide custom tear-down logic via the shutdown method though.

import io.nacular.doodle.application.Application //sampleStart class UsefulApp: Application { init { println("Hi!") } override fun shutdown() {} } //sampleEnd

App Launch

You can either launch an app top-level, or nested within another app. The Application class does not change regardless of the launch mode. That is because apps have no knowledge of the mode they will run in, making them independent of platform concepts by default.


Doodle does not expose any browser or desktop-specific concepts to apps. The launch step is the only place where HTML elements are directly accepted for Web apps, and this is only to support running Doodle apps within a non-Doodle page.

Top-level Apps

Most apps will run independent of others and exist purely within the context of a page, or element within it (for Web apps). Use the application function to launch apps this way. The result is a full-screen experience by default, with the app taking up the entire page and control all aspects of it. You can also provide an HTML element when launching a top-level Web app. This allows you to host Doodle apps in non-Doodle contexts. The apps in this documentation are top-level within specific elements.

Closing the page cleans up any apps within it. Removing the element hosting an app or explicitly calling shutdown has the same effect.

package usefulapp import UsefulApp import io.nacular.doodle.application.application //sampleStart fun main() { // launch full-screen application { UsefulApp() } } //sampleEnd

Nested Apps

Doodle Web apps can also be run within other Doodle Web apps. This is done by placing the nested app in a View that the host app manages. An app launched this way has the same functionality as a top-level one. Its lifecycle however, is tied to the host View.


import io.nacular.doodle.application.Application import io.nacular.doodle.core.Display //sampleStart class InnerApp(display: Display): Application { init { // add stuff to display } // ... override fun shutdown() { /*...*/ } } //sampleEnd


package outerapp import InnerApp import io.nacular.doodle.application.Application import io.nacular.doodle.application.ApplicationViewFactory import io.nacular.doodle.application.ApplicationViewFactory.Companion.AppViewModule import io.nacular.doodle.application.application import io.nacular.doodle.core.Display import org.kodein.di.instance //sampleStart class OuterApp(display: Display, appView: ApplicationViewFactory): Application { init { display += appView { InnerApp(display = instance()) // inner app initialization } } override fun shutdown() {} } fun main() { application(modules = listOf(AppViewModule)) { OuterApp(display = instance(), appView = instance()) } } //sampleEnd

Use an ApplicationViewFactory to create nested apps. This class is available via the AppViewModule.


Adding a nested app's View to the Display triggers the app's initialization. Shutdown the app by removing the host View from the Display.


Doodle uses dependency injection when creating apps. The lambda provided when launching an app is actually a Kodein binding context that lets you inject instances from Doodle modules, or your own.

package applications import io.nacular.doodle.application.Modules.Companion.PointerModule import io.nacular.doodle.application.application import org.kodein.di.DI.Module import org.kodein.di.instance //sampleStart fun main() { application(modules = listOf( PointerModule, // ..., Module(name = "A Custom Module") { // custom Kodein bind statements }, /*...*/)) { MyApp(instance()) } } //sampleEnd