Kotlin und UI Frameworks

Wenn es um die UI Entwicklung mit Kotlin geht, hat man als Entwickler eine Vielzahl von Frameworks zur Auswahl und kann diese mit Kotlin kombinieren, je nachdem wie groß die Anwendung ist oder welche Präferenzen und Anforderungen man hat. Deshalb werde ich in diesem Blogpost einige UI Frameworks vorstellen, die sehr gut mit Kotlin zusammenarbeiten.

React

React ist eine JavaScript-Bibliothek für die Erstellung von Benutzeroberflächen und kann als Basis bei der Entwicklung von Single-Page-Applications oder mobilen Anwendungen verwendet werden. Es erlaubt Entwicklern, große Webanwendungen zu erstellen, die Daten ändern können, ohne die Seite neu zu laden. Der Hauptzweck von React ist es, schnell, skalierbar und einfach zu sein. Es behandelt nur die Benutzeroberflächen in der Anwendung. Es kann aber auch in Kombination mit anderen JavaScript-Bibliotheken oder -Frameworks verwendet werden.

Der Code einer React-Anwendung besteht aus “Entitäten”, die Komponenten (Components) genannt werden. Komponenten können mithilfe der React-DOM-Bibliothek auf ein bestimmtes Element im DOM gerendert werden. Beim Rendering einer Komponente kann man Werte übergeben, die als “props” bezeichnet werden. React ermöglicht die Erstellung von Komponenten, die wiederverwendbar und zusammensetzbar sind, und die aus anderen Komponenten und Standard-HTML-Tags mithilfe einer XML-ähnlichen Syntax namens JSX aufgebaut werden. React ist auch wegen der Datenbindung interessant. Es verwendet einseitige Datenbindung, und eine Anwendungsarchitektur namens Flux steuert den Datenfluss zu den Komponenten über einen Kontrollpunkt – den Dispatcher. Es ist einfacher, in sich geschlossene Komponenten von großen ReactJS-Apps zu debuggen. Wenn ihr mehr darüber erfahren wollt, findet ihr hier die offizielle Seite von React. Mit den von JetBrains bereitgestellten Wrappern für React kann man React-Komponenten in Kotlin/JS definieren. Ich werde ein Beispiel für dieses Framework zeigen, weil es mein Lieblingsframework ist. Werfen wir also einen Blick auf ein kurzes, aber praktisches Beispiel:

IntelliJ bietet die Möglichkeit, ein Kotlin-Projekt mit React zu erstellen, wie in dem obigen Bild zu sehen ist. Dabei wird eine Projektstruktur erzeugt, die aus einem Kotlin-Ordner mit der Klasse client.kt und welcome.kt, dem Test-Ordner und den Gradle-Abhängigkeiten besteht:

Mit folgenden Gradle-Abhängigkeiten ist alles vorbereitet, um die gewünschte Kotlin-React App zu entwickeln:

dependencies {
    testImplementation(kotlin("test-js"))
    implementation("org.jetbrains:kotlin-react:16.13.1-pre.113-kotlin-1.4.0")
    implementation("org.jetbrains:kotlin-react-dom:16.13.1-pre.113-kotlin-1.4.0")
}

Das Gradle-Plugin für Kotlin sorgt dafür, dass unser gesamter Code und alle Abhängigkeiten in einem einzigen JavaScript-Artefakt gebündelt werden, das den gleichen Namen trägt wie unser Projekt. Das kann man in der index.html Datei sehen:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS Client</title>
</head>
<body>
<script src="KotlinWithReact.js"></script>
<div id="root"></div>
</body>
</html>

Je nachdem, wie wir unser Projekt benannt haben, hat die eingebettete js-Datei einen anderen Namen. In unserem Fall ist es KotlinWithReact.js.

Mit den von JetBrains bereitgestellten Wrappern für React kann man React-Komponenten in Kotlin/JS definieren. Außerdem bieten die Kotlin-Wrapper für React einen funktionalen Component Builder. Durch die Kombination dieser Komponenten, die wiederum auch aus anderen kleineren Komponenten zusammengesetzt werden können, bauen wir unsere Anwendung. Indem wir unsere Komponenten so strukturieren, dass sie wiederverwendbar sind, und sie generisch halten, können wir sie in mehreren Teilen unserer Anwendung verwenden, ohne Code oder Logik duplizieren zu müssen.

Welcome.kt

external interface WelcomeProps : RProps {
    var name: String
}

data class WelcomeState(val name: String) : RState

@JsExport
class Welcome(props: WelcomeProps) : RComponent<WelcomeProps, WelcomeState>(props) {

    init {
        state = WelcomeState(props.name)
    }

    override fun RBuilder.render() {
        div {
            +"Hello, ${state.name}"
        }
        button {
            attrs.onClickFunction = {
                setState(
                    WelcomeState(name = "Hi Andrada")
                )
            }
            +"Click for greeting"
        }

        input {
            attrs {
                type = InputType.text
                value = state.name
                onChangeFunction = { event ->
                    setState(
                            WelcomeState(name = (event.target as HTMLInputElement).value)
                    )
                }
            }
        }
   

Lass uns das obige Snippet erklären: Es definiert eine Funktion namens render als Erweiterungsfunktion (Extension Function) auf RBuilder ohne Parameter. Sie beginnt mit einer Begrüßung, gefolgt von dem Wert des Attributs state, der aus dem input übernommen wird. Neben der “Input” befindet sich ein button, der, wenn er angeklickt wird, die Begrüßung “Hallo Andrada” ausgibt.

Konzeptionell sind die Funktionskomponenten von React selbst nicht sehr komplex. Sie enthalten einfach die Anweisungen, wie eine React-Komponente in einer einzigen Funktion basierend auf den übergebenen Eigenschaften zu rendern ist, und sind nicht auf eine umgebende Klassenstruktur angewiesen.
Die main() Funktion befindet sich in client.kt, die unsere App referenziert. Das Einzige, was wir jetzt tun müssen, ist React zu sagen, dass es unsere App-Komponente als Kind des Root-Objekts rendern soll.

fun main() {
    window.onload = {
        render(document.getElementById("root")) {
            child(Welcome::class) {
                attrs {
                    name = "Kotlin/JS"
                }
            }
        }
    }
}

Schauen wir uns an, wie es aussieht! Wenn wir jetzt auf die Schaltfläche klicken, erscheint die Begrüßung:

Die Verwendung von Kotlin zum Schreiben von React-Applikationen erlaubt es uns, unser Wissen über Sprachparadigmen, Syntax und Werkzeuge wiederzuverwenden, um Frontend-Applikationen für moderne Browser zu bauen und die Kotlin-Bibliotheken zu nutzen, während wir die Fähigkeiten der JavaScript-Plattform und ihres Ökosystems ausschöpfen. Wenn man es im Detail analysieren will: hier findet eine sehr gute Einführung, wie Kotlin mit React zusammenarbeitet. Ich wünschte, dass Kotlin/JS + React auch mehr genutzt/verbreitet wäre. Ich denke, dass es vor allem in Verbindung mit Kotlin Multiplatform großartig ist.

Vaadin, VoK

Das nächste Framework, das wir uns anschauen ist Vaadin. Warum Vaadin?

  • Vaadin ist stark ereignisgesteuert. Man hat viel mit Event-Listenern zu tun. Glücklicherweise erlaubt es uns Kotlin, sehr prägnante Event-Listener mit Hilfe von Funktionsreferenzen und Lambdas zu schreiben.
  • Kotlins apply()-Methode ist extrem nützlich, wenn man UI-Komponenten und Layouts in Vaadin definiert. Sie erhöht die Lesbarkeit: Man kann den Initialisierungscode für eine Komponente gruppieren und die Verschachtelung des Layouts direkt im Code sehen.
  • Die genannten Punkte gelten auch für andere GUI-Frameworks wie Swing, JavaFX und GWT (wobei sich GWT nicht mit Kotlin verwenden lässt).

Aber was ist der interessante Teil mit Vaadin? Es ist Vok – Vaadin-on-Kotlin. VoK ist ein eigenes Webapplikations-Framework, das Vaadin für das Frontend und Kotlin für das Backend verwendet. Während man sich mit Vaadin nicht um HTML und JavaScript-Programmierung kümmern muss und man mit einem der Desktop-Programmierung angelehnten Paradigma (wie bei AWT, Swing oder SWT) programmiert, kann man mit Kotlin sauberen, verständlichen und sicheren Code schreiben. Richtig interessant wird es also, wenn wir Kotlin in Verbindung mit einem populären Framework wie Vaadin einsetzen wollen. Es wird weder JavaEE noch Spring benötigt; alle komplexen Features werden bewusst weggelassen, was Vaadin-on-Kotlin zu einem perfekten Ausgangspunkt für Programmieranfänger macht: Man wird nur serverseitigen Kotlin-Code schreiben. JavaScript und CSS werden nur benötigt, wenn man sich entscheidet, die Anwendung zu stylen oder eine eigene Rich Component zu schreiben. Das Web ist aus HTML-Seiten aufgebaut. Der Grundbaustein einer HTML-Seite ist das HTML-Element, wie <div> oder <a>. Zusammen mit den HTML-Elementen setzt man die Seite mit Hilfe von Web Components wie Paper Input oder Vaadin Grid zusammen, die dann selbst die notwendigen HTML-Elemente hosten.

Das Folgende ist alles, was man braucht, um einen Knopf auf einer Seite zu haben, der beim Anklicken eine Notification anzeigt:

@Route("")
class WelcomeView: VerticalLayout() {
    init {
        button("Click for greeting") {
            onLeftClick {
                Notification.show("Hi Andrada")
            }
        }
        formLayout {
            textField("Please type in your name:")
        }
    }
}

Die Funktion button() erstellt einfach den Vaadin-Button, legt eine Bezeichnung für ihn fest und fügt ihn in das übergeordnete Layout ein (in diesem Fall das VerticalLayout) und führt den Konfigurationsblock für diesen Knopf aus. Der Konfigurationsblock fügt einen Linksklick-Listener hinzu.

Die Funktion formLayout() erstellt die Vaadin FormLayout-Komponente und fügt sie in das Wurzel-Layout (VerticalLayout) ein. Dann führt sie den Konfigurationsblock aus und fungiert als übergeordnetes Layout in diesem Block. Dies ist sehr wichtig, denn dadurch kann die Funktion textField() das neu erstellte Elemnt TextField korrekt in das FormLayout selbst einfügen und nicht in das root-VerticalLayout. Diese “Magie” ist eigentlich nur ein Feature von Kotlin. Kotlin verwendet einfach das aktuelle Layout als Empfänger für die Funktion textField().

Zusammenfassend lässt sich sagen, dass Vaadin ein Single-Page Web Component Framework ist, im Gegensatz zu den traditionellen Multiple-Page Frameworks. Als solches ähnelt es der traditionellen Swing/JavaFX-Programmierung und ist näher an der GUI-Softwareentwicklung dran als an der klassischen Webentwicklung mit HTML und JavaScript. VoK fördert die Wiederverwendung von Code/UI durch die Wiederverwendung von Komponenten (die Komponenten reichen von einfachen Komponenten bis hin zu komplexen Containern und sogar Formularen) anstelle der Erstellung von Page Templates. Intern verwendet Vaadin Web Components.

VoK ist also ein “Bindemittel” zwischen Vaadin und Kotlin, das es einem ermöglicht, Web-Apps reibungslos zu schreiben. Es wurde entwickelt, um die Programmierung von Webanwendungen zu vereinfachen, indem es Annahmen über das macht, was jeder Entwickler braucht, um loszulegen. Es erlaubt einem, weniger Code zu schreiben und dabei mehr zu erreichen als mit vielen anderen Sprachen und Frameworks.

KVision

Wenn es um Kotlin und UI Frameworks geht, kann man KVision auch der Liste hinzufügen. KVision ist ein Open-Source Framework, das es ermöglicht, moderne Webanwendungen mit Kotlin zu erstellen, ohne HTML, CSS oder JavaScript anfassen zu müssen. Es handelt sich um ein relativ neues Projekt, aber es ist eines der umfassendsten und mit allen Funktionen ausgestattete Kotlin-Framework, das auf das Frontend abzielt. Ein paar Vorteile werde ich in den folgenden Punkten aufzählen:

  • Bietet typsichere DSL-Builder
  • Bietet Bootstrap Styles, Typografie und Components
  • Enthält hochentwickelte Layout-Container, einschließlich CSS-Flexbox, CSS-Gitter und Bootstrap responsive 12-Spalten-Gitter.
  • Es ist einfach Drag & Drop zu benutzen
  • Bietet eine typsichere REST-Konnektivität.
  • Bietet Integrationsschnittstellen für Ktor, Jooby und Spring Boot Frameworks auf der Serverseite.
  • Unterstützt die Internationalisierung mit Hilfe der Jed-Bibliothek und Gettext-Übersetzungen.
  • Ist so gestaltet, dass es offen und flexibel ist.

Das grundlegende Design von KVision ist vielen Nicht-Web-UI-Programmierbibliotheken sehr ähnlich, einschließlich Swing, JavaFX, QT, WinForms und Flutter. KVision unterstützt vollständig sowohl reaktive als auch imperative Programmiermodelle. Es gibt einem alles, was man für die Zustandsverwaltung der Anwendungen benötigt. Von einfachen Observables bis zu fortgeschrittenen Redux-Stores. Von einfachen Event-Callbacks bis zu funktionalen Event-Flows.
KVision ist Open Source und modular. Man kann seine eigenen Module erstellen, indem man sich ein Beispiel von den vielen bereits existierenden holt. Fast alle KVision-Klassen sind als open deklariert. Mit Vererbung und Komposition kann man die eigenen Komponenten bauen, mit allen neuen Funktionen, die man in einer Anwendung benötigt.

KVision ist für jede Art von Projekten geeignet, einschließlich responsiver, mobiler Webanwendungen oder sogar einer einfachen Website.

Die Hauptanwendungsklasse von KVision muss die Klasse pl.treksoft.kvision.Application erweitern und die Methode start überschreiben:

class App : Application() {

    override fun start() {
        root("kvapp") {
            span("Hello world!")
        }
    }
}

fun main() {
    startApplication(::App, module.hot)
}

Der interessante Teil ist, dass man KVision-Anwendungen in den meisten modernen Browsern (z.B. Chrome oder Firefox), die vom Kotlin/JS-Compiler generierte Source-Maps unterstützen, einfach debuggen kann. Source-Maps erlauben es, den Kotlin-Quellcode im “Sources”-Panel des Browsers zu sehen. Man kann Zeilennummern sehen, durch den Code schreiten, Haltepunkte setzen und Variablenwerte beobachten, genau wie bei normalem JavaScript.
KVision besteht sowohl aus erforderlicher als auch aus optionaler Funktionalität. Module können als Abhängigkeiten in der build.gradle.kts Datei hinzugefügt werden. Die aktuelle Liste der verfügbaren Module enthält das KVision Core-Modul, das für alle Anwendungen benötigt wird, bootstrap-basierte Komponenten, bootstrap-basierte erweiterte Auswahlformular-Komponente, Datumspicker, Spinner, Upload-Formular-Komponente, Diagramm-Komponente, Print-Library und viele andere. KVision kann alternativ auch ganz ohne Bootstrap verwendet werden. Man muss nur alle bootstrap Module aus der build.gradle.kts Datei ausschließen. Man wird keine Komponenten aus diesen Modulen verwenden können, die von Bootstraps JavaScript und Styling abhängen, aber man kann trotzdem eine voll funktionsfähige Anwendung erstellen.

Jedes KVision-Projekt nutzt die HMR (Hot Module Replacement) Funktion von Webpack. HMR kann die Entwicklung erheblich beschleunigen, indem der Browserinhalt automatisch aktualisiert wird, nachdem Änderungen im Quellcode vorgenommen wurden. Es erlaubt auch, den Zustand der Anwendung beizubehalten. Man muss einfach die Start-Methode mit einem State-Parameter überschreiben . KVision enthält Klassen für die Erstellung von typischem HTML-Markup einer Webseite. Die Hauptklasse für diesen Zweck ist pl.treksoft.kvision.html.Tag, mit der man jedes HTML-Element rendern kann. Diese Klasse ist auch ein Container, sodass Instanzen von Tag in anderen Tag-Objekte verschachtelt werden können. Es gibt einige Unterklassen von Tag, Div, P, Span, H1, H2, H3, H4, H5, H6, Main, Section, Header, Footer, die die am häufigsten verwendeten HTML-Elemente rendern. Wenn es um Buttons geht, kann KVision Buttons in einem von 17 Stilen darstellen. Die Klasse pl.treksoft.kvision.html.Button bietet eine onClick-Methode, die eine einfache Möglichkeit bietet, einen Handler an das Click-Ereignis zu binden.

Das Ziel ist nicht, eine Demo-Anwendung zu erstellen, sondern euch wissen zu lassen, dass KVision eine moderne und coole Art der Frontend-Erstellung bei der Programmierung mit Kotlin ist. Weitere Informationen findet man im KVision-Guide, der meiner Meinung nach sehr ausführlich ist.

TornadoFX

Eine der wichtigeren Eigenschaften von Kotlin ist seine 100%ige Interoperabilität mit bestehenden Java-Bibliotheken und Codebasen, einschließlich JavaFX.
Während JavaFX mit Kotlin auf die gleiche Weise wie Java verwendet werden kann, wurde behauptet, dass Kotlin über Sprachfunktionen verfügt, die die JavaFX-Entwicklung rationalisieren und vereinfachen könnten.

Vor der Kotlin-Beta hat Eugen Kiss mit KotlinFX JavaFX-“Builder” prototypisiert. Edvin Syse hat im Januar 2016 die Initiative neu gestartet und TornadoFX veröffentlicht.

TornadoFX zielt darauf ab, die Menge an Code, die zum Erstellen von JavaFX-Anwendungen benötigt wird, stark zu minimieren. Es enthält nicht nur typsichere Builder, um schnell Steuerelemente und Benutzeroberflächen zu erstellen, sondern bietet auch Dependency Injection, delegierte Eigenschaften, Funktionen zur Erweiterung von Steuerelementen und andere praktische Funktionen, die durch Kotlin ermöglicht werden. TornadoFX ist ein gutes Beispiel dafür, wie Kotlin Codebases vereinfachen kann, und es begegnet der Ausführlichkeit von UI-Code mit Eleganz und Einfachheit. Es kann in Verbindung mit anderen populären JavaFX-Bibliotheken wie ControlsFX und JFXtras arbeiten. Es funktioniert besonders gut mit reaktiven Frameworks wie ReactFX sowie RxJava. Um mit TornadoFX zu arbeiten, muss man das spezifische Plugin installieren:

JavaFX verwendet eine Analogie aus dem Theater, indem eine Anwendung mit Stage– und Scene-Komponenten organisiert wird.

TornadoFX baut darauf auf, indem es View-, Controller- und Fragment-Komponenten bereitstellt. Während Stage und Scene von TornadoFX verwendet werden, werden mit View, Controller und Fragment neue Konzepte eingeführt, die die Entwicklung rationalisieren. Viele dieser Komponenten werden automatisch als Singletons verwaltet und können durch die einfachen Abhängigkeitsinjektionen von TornadoFX oder durch direkte Instanziierung miteinander kommunizieren. Um eine TornadoFX-Anwendung zu erstellen, muss man mindestens eine Klasse haben, die App erweitert. Eine App ist der Einstiegspunkt in die Anwendung und legt die initiale Ansicht fest. Sie erweitert zwar JavaFX Application, aber man muss nicht unbedingt eine start()– oder main()-Methode angeben:

import tornadofx.*

class MyApp: App(MyTestView::class)

Eine View enthält sowohl Anzeigelogik als auch ein Layout von Nodes, ähnlich wie die JavaFX Stage. Sie wird automatisch als Singleton verwaltet. Wenn man eine View deklariert, muss man eine Root-Eigenschaft angeben, die ein beliebiger Parent-Typ sein kann, und die den Inhalt der View enthält. Man kann in der gleichen Kotlin-Datei oder in einer neuen Datei eine Klasse von View erweitern, die die abstrakte Root-Eigenschaft überschreibt und VBox oder einen anderen Node zuweisen:

import tornadofx.*

class MyTestView: View() {
    override val root = vbox {
        button("Click for greeting")
    }
}

Um eine TornadoFX-Anwendung zu erstellen, sollte man mit der Sprache Kotlin vertraut sein. Warum noch ein weiteres Framework? Kotlin interagiert perfekt mit Java, so dass das Schreiben von JavaFX-Anwendungen mit Kotlin bereits ein Vergnügen ist. TornadoFX nutzt jedoch einige sehr interessante Sprachfeatures von Kotlin, die noch prägnanteren und schöneren Code ermöglichen, ohne dabei irgendwelche Features der JavaFX-API zu verstecken.

Fritz2

Fritz2 verwendet Kotlin-Standardfunktionen (insbesondere Coroutinen und Flows) und hat keine externen Abhängigkeiten. Der Kernquelltext besteht aus nur etwa 1,500 Zeilen Code und ist damit eine der leichtesten UI-Libs: Sie bietet präzises Data-Binding für die UI-Elemente. Wenn sich die Daten ändern, werden nur die DOM-Elemente aktualisiert, die an Teile des Modells gebunden sind, die sich wirklich geändert haben – kein virtuelles DOM wird benötigt.
Mit fritz2 kann man sowohl kleine Anwendungen sehr schnell mit wenigen Zeilen Code erstellen als auch Anwendungen im Enterprise-Bereich mit dem Fokus auf wiederverwendbare Komponenten, sauberen Code und Struktur. Das funktionale, reaktive Konzept macht den resultierenden Code einfach zu lesen und zu pflegen. Es enthält alles, was man für den Start des Projekts benötigt: State Management (auch für komplexe verschachtelte Modelle), Validierung, Routing, REST, WebSockets, WebComponents sowie eine Reihe von maßgeschneiderten Komponenten. Unter Ausnutzung der Multiplattform-Fähigkeiten von Kotlin muss man die Datenklassen und den Validierungscode nur einmal schreiben und kann diese in Client und Server verwenden.

Um fritz2 zu nutzen, muss man ein Kotlin Multiplattform-Projekt aufsetzen. fritz2 zwingt einen nicht, die Komponenten auf eine bestimmte Art und Weise zu bauen – man kann jedes einzelne Kotlin-Sprachfeature nutzen, das einem gefällt. Um eine Komponente zu erstellen, die innerhalb von Render-Kontexten direkt aufgerufen werden kann, muss man nur den entsprechenden Empfänger RenderContext angeben. Wenn ihr es ausprobieren wollt, findet ihr hier eine recht ausführliche Dokumentation.

Ich hoffe, dass euch dieser Blogpost gefallen hat und dass ihr hier ein Lieblingsframework finden konntet. Meines ist Kotlin mit React, aber alle Frameworks sind einzigartig und sie sollten sowohl nach Präferenzen als auch nach den Anforderungen eingesetzt werden.

Views All Time
1992
Views Today
14
Short URL for this post: https://blog.oio.de/MwNA3
This entry was posted in Other languages for the Java VM and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *