Onsen UI is a set of mobile friendly web components, created with native iOS and Android design standards. With Onsen UI it's possible to develop mobile sites or hybrid web applications (e.g. with Cordova), which share exactly the same code, but also automatically choose the look and feel based on the platform on which they are running.
KVision supports all Onsen UI components with fully type-safe, consistent Kotlin API and readable DSL builders, but you should get familiar with Onsen UI documentation to achieve best results. You will also find a lot of examples in the KVision Onsen UI kitchensink example project.
To use Onsen UI with KVision you have to add kvision-onsenui
module to your dependencies in build.gradle.kts
file.
{% hint style="info" %} Note: Onsen UI components are not compatible with Bootstrap, so you can't mix both Onsen UI and Bootstrap KVision components in one application. {% endhint %}
Onsen UI application layout is created with pages contained within three main types of components:
- Navigator
- Splitter
- Tabbar
KVision provides a dedicated DSL to define these layouts and to allow joining them together.
{% hint style="info" %} Note: Layout containers are managed directly by Onsen UI library and should not be modified by KVision application. In particular, you should not use KVision data bindings (e.g. with Redux store) to render Onsen UI layout containers. However you can safely render content of pages or tabs. {% endhint %}
A Navigator
component allows you to define a stack of pages, where only the top level page is visible on the screen. The first page defined (without ID parameter) is visible on start. All other pages should have unique ID values and will not be visible until pushed with pushPage
method of the Navigator
component (other methods like insertPage
or replacePage
can be used as well). You can use the BackButton
default action or popPage
method of the Navigator
component to return to the previous visible page. With Navigator
component constructor parameters or pushPage
method parameters you can specify additional effects and animations.
navigator(forceSwipeable = true) {
page {
toolbar("Page 1")
onsButton("Go to page 2").onClick {
this@navigator.pushPage("page2")
}
}
page("page2") {
toolbar("Page 2") {
left {
backButton("Page 1")
}
}
}
}
A Splitter
component enables responsive layout by implementing a two-column layout or an additional sliding menu. You need to add exactly two components to a Splitter
container - a SplitterSide
and a SplitterContent
. The first one defines a sliding menu and the second defines main content. A menu may be always visible, always collapsed or may be collapsed based on the device orientation (portrait or landscape). Within SplitterContent
component you can define one or more pages (with unique IDs). You can use load
method of the SplitterContent
class to select visible page.
splitter {
lateinit var splitterContent: SplitterContent
splitterSide(collapse = Collapse.COLLAPSE, swipeable = true) {
page {
ul {
li("First page").onClick {
splitterContent.load("1")
this@splitterSide.close()
}
li("Second page").onClick {
splitterContent.load("2")
this@splitterSide.close()
}
li("Third page").onClick {
splitterContent.load("3")
this@splitterSide.close()
}
}
}
}
splitterContent = splitterContent {
page("1") {
toolbar("First page")
}
page("2") {
toolbar("Second page")
}
page("3") {
toolbar("Third page")
}
}
}
A Tabbar
component allows you to define a list of tabs, where only one tab is visible at a time. You can define one or more Tab
components inside a Tabbar
. Every Tab
should have a label, an icon or both. By default tabbar is displayed at the bottom of the screen. You can use TabPosition.AUTO
to have it at the bottom on iOS and at the top on Android devices.
tabbar(position = TabsPosition.AUTO, swipeable = true) {
tab("First tab") {
page {
div("Page 1")
}
}
tab("Second tab") {
page {
div("Page 2")
}
}
}
Using OnsenUi
global object you get access to various methods, which allow your code to detect platform, screen orientation, customize global animations settings and define global handler for device back button.
import io.kvision.onsenui.OnsenUi
if (OnsenUi.isAndroid()) {
OnsenUi.disableAnimations()
}
OnsenUi.onOrientationChange { evt ->
// handle orientation change event
}
OnsenUi.setDefaultDeviceBackButtonListener { evt ->
// handle device back button event
}
OnsenUI provides a rich set of form components and they are fully integrated with KVision standard forms implementation. You can use all components from io.kvision.onsenui.form
package as a standalone input elements or use them together with FormPanel
container.
@Serializable
data class MyForm(
val text: String?,
val password: String?,
val number: Int?,
val number2: Int?,
val date: Date?,
val check: Boolean = false,
val select: String?,
val search: String?
)
formPanel<MyForm> {
add(MyForm::text, OnsText(placeholder = "Enter text", floatLabel = true))
add(
MyForm::password,
OnsText(
type = TextInputType.PASSWORD,
placeholder = "Enter password",
floatLabel = true
)
)
add(
MyForm::number,
OnsNumber(
placeholder = "Enter number",
floatLabel = true
), validator = {
(it.value ?: 0).toInt() > 5
}
)
add(
MyForm::number2,
OnsRange(min = 1, max = 10, label = "Range selection"),
validator = {
(it.value ?: 0).toInt() > 5
}
)
add(
MyForm::date,
OnsDateTime(mode = DateTimeMode.TIME, label = "Enter time")
)
add(
MyForm::check, OnsSwitch(label = "Check option")
)
add(
MyForm::select,
OnsSelect(
listOf("1" to "Option 1", "2" to "Option 2", "3" to "Option 3"),
emptyOption = true,
multiple = true,
label = "Select option from a list"
)
)
add(
MyForm::search,
OnsText(
TextInputType.SEARCH,
label = "Search",
placeholder = "Search"
)
)
onsButton("Show data").onClick {
console.log(this@formPanel.getData())
}
onsButton("Clear data").onClick {
this@formPanel.clearData()
}
onsButton("Validate").onClick {
this@formPanel.validate()
}
}
Dialog components include ActionSheets, Alerts, Dialogs, Modals, Popovers and Toasts. To show one of these components you need o create an instance of the corresponding class from io.kvision.onsenui.dialog
package, and call one of show*
methods.
val alertDialog = alertDialog("Alert", true, rowfooter = true) {
div("Lorem Ipsum")
alertDialogButton("OK")
alertDialogButton("Cancel")
}
alertDialog.showDialog()
val modal = modal {
div("Modal content")
onsButton("Close").onClick {
this@modal.hideModal()
}
}
modal.showModal()
val dialog = dialog(true) {
div("Dialog content")
onsButton("Close").onClick {
this@dialog.hideDialog()
}
}
dialog.showDialog()
val actionSheet = actionSheet("Test action", true) {
actionSheetButton("First", icon = "md-square-o").onClick {
this.icon = "md-check-square"
}
actionSheetButton("Second", icon = "md-square-o")
actionSheetButton("Close", icon = "md-close").onClick {
this@actionSheet.hideActionSheet()
}
}
actionSheet.showActionSheet()
val toast = toast(animation = ToastAnimation.FALL) {
div("This is a toast")
onsButton("OK").onClick {
this@toast.hideToast()
}
}
toast.showToast()
val popover = popover(cancelable = true) {
div("Test of the popover")
}
popover.showOnsPopover(widget) // attach popover to a widget
You can also use one of the top level functions: showAlert
, showConfirm
, showPrompt
, showToast
, showActionSheet
to display popup windows without creating any objects.
showToast(
"Notification message",
"OK",
timeout = 2000,
animation = ToastAnimation.FALL
)
With Onsen UI you can create infinite scroll effect in two different ways.
Use this way if you want to load content asynchronously (e.g. from a network). You should use onInfiniteScroll
event handler of the page component.
page {
onsList().bind(InfiniteScrollModel.items) { items ->
items.forEach {
item("Item #$it")
}
}
div(className = "after-list") {
icon("fa-spinner", size = "26px", spin = true)
}
onInfiniteScroll { done ->
window.setTimeout({
InfiniteScrollModel.loadMore()
done()
}, 600)
}
}
Use this way if you have very long list of items, but you don't want to add all of them to the DOM at the same time. Onsen UI will automatically unload items that are not visible.
page {
onsList {
onsLazyRepeat(3000) { index ->
OnsenUi.createElement("<ons-list-item>Item #$index</ons-list-item>")
}
}
}
{% hint style="info" %}
Note: You can't use KVision components with lazy repeat list. You need to create native DOM elements with OnsenUi.createElement
helper method.
{% endhint %}