Slint Android
ASecurityExpert guidance for building Android apps with Slint (Rust GUI toolkit). Use this skill whenever the user is working on a Slint UI — especially for Android targets — including .slint DSL syntax, component architecture, Cargo setup, Android backend configuration (cargo-apk, android-activity), Material Design 3 compliance, touch targets, dp/sp units, safe area insets, edge-to-edge layouts, dark mode, gestures, and responsive design for multiple Android screen densities. Trigger this skill when ...
- 4 stars
- 0 votes
- 0 copies
- 3 views
- Added September 12, 2026
Works with
Security analysis
100/100Pro scans all 2 files and shows the line behind each finding
npx -y skills add aibot88/sec_skill_store --skill slint-android --agent claude-codeAre you the author of Slint Android?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-slint-android)---
name: slint-android
description: >
Expert guidance for building Android apps with Slint (Rust GUI toolkit). Use this skill whenever
the user is working on a Slint UI — especially for Android targets — including .slint DSL syntax,
component architecture, Cargo setup, Android backend configuration (cargo-apk, android-activity),
Material Design 3 compliance, touch targets, dp/sp units, safe area insets, edge-to-edge layouts,
dark mode, gestures, and responsive design for multiple Android screen densities. Trigger this skill
when the user mentions Slint, .slint files, cargo-apk, slint Android, or asks about cross-platform
Rust UI for mobile. Also trigger for any Android UI design questions involving density-independent
pixels, Material 3 components, system bar insets, or gesture navigation.
---
# Slint Android Expert Skill
Slint ist ein deklaratives, reaktives GUI-Toolkit in Rust mit einer eigenen DSL (`.slint`-Dateien),
das nativ auf Android (ab v1.5), Desktop und Embedded läuft. Diese Skill-Anleitung deckt sowohl
die Slint-DSL als auch alle relevanten Android-UI-Designregeln ab.
---
## 1. Projekt-Setup: Cargo & Android-Backend
### Minimal Cargo.toml für Android
```toml
[package]
name = "meine-app"
version = "0.1.0"
edition = "2021"
[lib]
# PFLICHT für Android: cdylib
crate-type = ["cdylib"]
[dependencies]
# Slint mit Android-Activity-Backend (v0.6 empfohlen ab Slint 1.6+)
slint = { version = "1.9", features = ["backend-android-activity-06"] }
# Für ältere android-activity 0.5:
# slint = { version = "1.9", features = ["backend-android-activity-05"] }
[build-dependencies]
slint-build = "1.9"
```
### build.rs
```rust
fn main() {
slint_build::compile("ui/app.slint").unwrap();
}
```
### Entry Point (lib.rs / main.rs)
```rust
// Für Android MUSS der Entry Point android_main heißen (kein fn main)
#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: slint::android::AndroidApp) {
slint::android::init(app).unwrap();
let ui = AppWindow::new().unwrap();
ui.run().unwrap();
}
// Desktop-Fallback
#[cfg(not(target_os = "android"))]
fn main() {
let ui = AppWindow::new().unwrap();
ui.run().unwrap();
}
slint::include_modules!();
```
### Umgebungsvariablen (vor cargo-apk)
```bash
export ANDROID_HOME=$HOME/Android/Sdk
export ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk/<version>
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk # JDK 17 empfohlen (NICHT 21)
```
### Build & Deploy
```bash
# Targets installieren
rustup target add aarch64-linux-android # echtes Gerät
rustup target add x86_64-linux-android # Emulator
# cargo-apk installieren
cargo install cargo-apk
# Auf Gerät deployen
cargo apk run --target aarch64-linux-android --lib
# Auf Emulator
cargo apk run --target x86_64-linux-android --lib
```
> **Wichtig**: `i-slint-backend-android-activity` folgt KEIN SemVer. Immer mit `= "=x.y.z"`
> exakt pinnen, wenn direkt als Dependency. Mit dem `features`-Flag am `slint`-Crate ist das
> automatisch korrekt.
---
## 2. Das .slint DSL – Syntax & Konzepte
### Komponenten-Grundstruktur
```slint
// Imports aus der Standardbibliothek
import { Button, VerticalBox, HorizontalBox, ScrollView } from "std-widgets.slint";
// Material-Style-Widgets (für Android empfohlen)
import { Button, Switch, Slider, CheckBox, ComboBox } from "std-widgets.slint";
// Komponente deklarieren
export component MeinButton inherits Rectangle {
// Eigenschaften
in property <string> label: "Klick mich";
in property <color> button-color: #6750A4; // Material Primary
out property <bool> ist-geklickt: false;
in-out property <int> zaehler: 0;
// Callback deklarieren
callback geklickt();
// Layout & Aussehen
width: 200px;
height: 48px; // MINIMUM Touch Target!
background: button-color;
border-radius: 12px;
Text {
text: root.label;
color: white;
font-size: 14px;
horizontal-alignment: center;
vertical-alignment: center;
}
// Touch-Interaktion
TouchArea {
clicked => {
root.zaehler += 1;
root.geklickt();
}
}
}
// Hauptfenster
export component AppWindow inherits Window {
title: "Meine Android App";
// Kein festes width/height setzen – Android passt sich an
VerticalBox {
padding: 16px;
spacing: 8px;
MeinButton {
label: "Los!";
geklickt => { debug("Button geklickt!"); }
}
}
}
```
### Property-Typen
| Typ | Beispiel |
|-----|---------|
| `int` | `property <int> count: 0;` |
| `float` | `property <float> opacity: 1.0;` |
| `bool` | `property <bool> visible: true;` |
| `string` | `property <string> name: "Hallo";` |
| `color` | `property <color> bg: #FF5722;` |
| `brush` | Gradient oder Farbe |
| `length` | `property <length> size: 48px;` |
| `duration` | `property <duration> anim: 300ms;` |
| `image` | `@image-url("icon.png")` |
| `struct` | Eigene Datentypen |
| `[T]` | Arrays / Models |
### Property-Sichtbarkeit
```slint
in property <T> name; // Nur von außen setzbar
out property <T> name; // Nur von innen setzbar
in-out property <T> name; // Beide Richtungen
private property <T> name; // Nur intern
```
### Two-Way Binding
```slint
export component Wrapper inherits Rectangle {
in-out property <string> text <=> eingabe.text; // direkte Verlinkung
eingabe := TextInput {}
}
```
### Callbacks
```slint
export component Beispiel inherits Rectangle {
callback aktion(string) -> bool; // Mit Parameter und Rückgabe
aktion(text) => {
debug("Aktion: " + text);
true
}
// Alias
callback extern-geklickt <=> touch.clicked;
touch := TouchArea {}
}
```
### Layouts
```slint
// Vertikal
VerticalLayout {
spacing: 8px;
padding: 16px;
alignment: start; // start | center | end | space-between | space-around
Rectangle { height: 48px; }
Rectangle { height: 48px; }
}
// Horizontal
HorizontalLayout {
spacing: 12px;
Rectangle { width: 50%; } // Prozentangaben
Rectangle { horizontal-stretch: 1; } // Flexibel
}
// Grid
GridLayout {
Row {
Rectangle { colspan: 2; }
}
Row {
Rectangle {}
Rectangle {}
}
}
```
### Animationen & Übergänge
```slint
Rectangle {
property <bool> aktiv: false;
background: aktiv ? #6750A4 : #E8DEF8;
// Eigenschaftsanimation
animate background {
duration: 250ms;
easing: ease-in-out;
}
// Positionsanimation
x: aktiv ? 0px : 100px;
animate x { duration: 300ms; easing: ease-out; }
}
```
### States & Transitions
```slint
Button {
states [
pressed when touch.pressed: {
background: #4A3F8C;
scale: 0.97;
}
hovered when touch.has-hover: {
background: #7965AF;
}
]
transitions [
in pressed: { animate background { duration: 80ms; } }
out pressed: { animate background { duration: 200ms; } }
]
}
```
### Wiederholungen mit `for`
```slint
export component Liste inherits VerticalLayout {
in property <[string]> eintraege;
for eintrag[i] in eintraege: Rectangle {
height: 56px;
Text { text: eintrag; }
}
}
```
### Globale Singletons
```slint
export global AppTheme {
in-out property <bool> dark-mode: false;
in-out property <color> primary: #6750A4;
in-out property <color> background: dark-mode ? #1C1B1F : #FFFBFE;
}
// Nutzung in Rust:
// AppTheme::get(&ui).set_dark_mode(true);
```
---
## 3. Android-spezifisches UI-Design
### Das wichtigste Prinzip: Density-Independent Pixels (dp)
Android-Geräte haben stark unterschiedliche Pixeldichten. Slint verwendet `px`-Einheiten,
die intern auf der Logikdichte basieren — entsprechend Android dp:
| Android Dichte | DPI | Scale | Slint px ≈ |
|---------------|-----|-------|-----------|
| mdpi (Basis) | 160 | 1x | 1 dp |
| hdpi | 240 | 1.5x | 1.5 dp |
| xhdpi | 320 | 2x | 2 dp |
| xxhdpi | 480 | 3x | 3 dp |
| xxxhdpi | 640 | 4x | 4 dp |
> In Slint gibt es aktuell kein natives dp-System wie in Jetpack Compose. Verwende `self.Window.scale-factor`
> um den Skalierungsfaktor abzurufen und physische Groessen korrekt zu berechnen.
### Touch-Targets: Die goldene Regel
```slint
// RICHTIG: Mindest-Touch-Target 48x48 (logische Pixel = dp)
TouchableIcon {
width: max(48px, icon-size);
height: max(48px, icon-size);
// Sichtbarer Bereich kann kleiner sein
Image {
width: 24px; height: 24px;
horizontal-alignment: center;
vertical-alignment: center;
}
}
// FALSCH: Zu kleines Touch-Target
Image {
width: 16px; height: 16px;
// TouchArea direkt drauf -> zu klein!
}
```
**Regeln:**
- Minimum: **48x48 px** (ca. 9mm physisch, entspricht 48dp)
- Abstand zwischen Touch-Targets: mindestens **8 px**
- Icons sind typisch 24px, aber Touch-Area muss 48px sein
### Safe Area / System Insets (Android 15+ Edge-to-Edge)
Ab Android 15 (API 35) zeichnet das System Apps standardmaessig edge-to-edge.
Content darf nicht hinter Status Bar oder Navigationleiste verschwinden.
```slint
// Aktueller Workaround in Slint (natives Inset-API noch in Entwicklung):
// System-Insets über Rust-Backend einlesen und als Properties übergeben
export component SafeWindow inherits Window {
// Von Rust gesetzt: slint::android::window_insets()
in property <length> inset-top: 24px; // Status Bar
in property <length> inset-bottom: 48px; // Navigationleiste / Gesture-Bar
in property <length> inset-left: 0px;
in property <length> inset-right: 0px;
VerticalLayout {
// Oben: Status Bar Padding
Rectangle { height: root.inset-top; }
// Inhalt
VerticalLayout {
padding-left: root.inset-left + 16px;
padding-right: root.inset-right + 16px;
vertical-stretch: 1;
// ... App-Inhalt ...
}
// Unten: Navigation Bar Padding
Rectangle { height: root.inset-bottom; }
}
}
```
**Rust-Seite (Insets abrufen):**
```rust
// Vereinfachtes Beispiel – echte API variiert nach Slint-Version
#[cfg(target_os = "android")]
fn get_window_insets(app: &slint::android::AndroidApp) -> (f32, f32, f32, f32) {
// Insets über android-activity API holen
// top, bottom, left, right in logischen Pixeln
(24.0, 48.0, 0.0, 0.0) // Fallback-Werte
}
```
### Material Design 3 – die Android-Designsprache
Slint enthält einen **Material-Style** für Widgets (`import { ... } from "std-widgets.slint"`
mit gesetztem Material-Theme).
**Material 3 Farbsystem:**
```slint
// Material You Farbpalette (light mode)
export global Material3Colors {
// Primary
out property <color> primary: #6750A4;
out property <color> on-primary: #FFFFFF;
out property <color> primary-container: #EADDFF;
out property <color> on-primary-container: #21005D;
// Secondary
out property <color> secondary: #625B71;
out property <color> on-secondary: #FFFFFF;
// Surface
out property <color> surface: #FFFBFE;
out property <color> on-surface: #1C1B1F;
out property <color> surface-variant: #E7E0EC;
// Error
out property <color> error: #B3261E;
out property <color> on-error: #FFFFFF;
// Dark Mode Varianten
in property <bool> dark: false;
out property <color> background: dark ? #1C1B1F : surface;
}
```
**Typografie (Material 3 Type Scale):**
```slint
// Schriftgroessen in sp (scale-independent pixels = logische px für Text)
// Minimum: 14px für Body-Text
export global TextStyles {
// Display
out property <length> display-large: 57px;
out property <length> display-medium: 45px;
out property <length> display-small: 36px;
// Headline
out property <length> headline-large: 32px;
out property <length> headline-medium: 28px;
out property <length> headline-small: 24px;
// Title
out property <length> title-large: 22px;
out property <length> title-medium: 16px;
out property <length> title-small: 14px;
// Body (NIE unter 14px!)
out property <length> body-large: 16px;
out property <length> body-medium: 14px;
out property <length> body-small: 12px; // Nur für Captions!
// Label
out property <length> label-large: 14px;
out property <length> label-medium: 12px;
out property <length> label-small: 11px;
}
```
**Elevation & Schatten (Material 3):**
```slint
// Material 3 verwendet Tonal Elevation statt echte Schatten
component ElevatedCard inherits Rectangle {
background: #F3EFF4; // surface + primary overlay bei dp=1
border-radius: 12px;
// Slint drop-shadow für visuelle Tiefe
drop-shadow-color: #00000033;
drop-shadow-blur: 4px;
drop-shadow-offset-y: 2px;
}
```
### Android Navigation Patterns
```slint
// Bottom Navigation Bar (haeufigstes Muster auf Phones)
export component BottomNavBar inherits Rectangle {
in property <int> aktiver-tab: 0;
in property <length> nav-inset: 0px; // Navigationleisten-Inset
height: 80px + nav-inset;
background: #FFFBFE;
// Trennlinie oben
Rectangle {
height: 1px;
background: #CAC4D0;
}
HorizontalLayout {
padding-bottom: nav-inset;
for tab[i] in ["Startseite", "Suche", "Profil"]: NavItem {
aktiv: aktiver-tab == i;
label: tab;
horizontal-stretch: 1;
}
}
}
component NavItem inherits Rectangle {
in property <bool> aktiv;
in property <string> label;
callback geklickt();
height: 80px;
VerticalLayout {
alignment: center;
spacing: 4px;
// Icon-Container (48x48 Touch Target)
Rectangle {
height: 32px;
width: 64px; // "pill" shape
background: aktiv ? #EADDFF : transparent;
border-radius: 16px;
horizontal-alignment: center;
// Icon hier einfuegen
}
Text {
text: label;
font-size: 12px;
color: aktiv ? #6750A4 : #625B71;
horizontal-alignment: center;
}
}
TouchArea { clicked => { root.geklickt(); } }
}
```
### Scrollable Content
```slint
// Wichtig: ScrollView für langen Content
export component ScrollableScreen inherits Rectangle {
in property <length> top-padding: 0px;
in property <length> bottom-padding: 0px;
ScrollView {
// Wichtig: viewport-height muss gesetzt sein
viewport-height: content.preferred-height;
content := VerticalLayout {
padding-top: top-padding + 16px;
padding-bottom: bottom-padding + 16px;
padding-left: 16px;
padding-right: 16px;
spacing: 12px;
// Inhalt hier
}
}
}
```
### Responsive Design für verschiedene Bildschirmgroessen
```slint
export component AdaptivesLayout inherits Window {
// Kompakt: Phone portrait (< 600px)
// Medium: Tablet/Foldable (600-840px)
// Expanded: Tablet landscape (> 840px)
property <bool> ist-kompakt: root.width < 600px;
property <bool> ist-erweitert: root.width >= 840px;
if ist-kompakt: KompaktesLayout {}
if !ist-kompakt && !ist-erweitert: MittleresLayout {}
if ist-erweitert: ErweiterteLayout {}
}
```
### Dark Mode
```slint
export global AppTheme {
// In Rust: slint::platform::SystemTheme (falls verfügbar) abfragen
in-out property <bool> dark-mode: false;
out property <color> hintergrund: dark-mode ? #1C1B1F : #FFFBFE;
out property <color> auf-hintergrund: dark-mode ? #E6E1E5 : #1C1B1F;
out property <color> oberflaeche: dark-mode ? #2B2930 : #FFFBFE;
out property <color> primaer: dark-mode ? #D0BCFF : #6750A4;
out property <color> auf-primaer: dark-mode ? #371E73 : #FFFFFF;
}
// In Rust den Dark Mode setzen:
// AppTheme::get(&ui).set_dark_mode(
// std::env::var("SLINT_COLOR_SCHEME") == Ok("dark".into())
// );
```
---
## 4. Haeufige Muster & Best Practices
### Daten aus Rust an UI übergeben
```slint
// ui/app.slint
export struct Aufgabe {
id: int,
titel: string,
erledigt: bool,
}
export component AppWindow inherits Window {
in-out property <[Aufgabe]> aufgaben;
callback aufgabe-erledigt(int);
callback neue-aufgabe(string);
}
```
```rust
// main.rs
use slint::Model;
let ui = AppWindow::new().unwrap();
let model = std::rc::Rc::new(slint::VecModel::<Aufgabe>::from(vec![
Aufgabe { id: 1, titel: "Einkaufen".into(), erledigt: false },
]));
ui.set_aufgaben(model.clone().into());
ui.on_aufgabe_erledigt(move |id| {
// Model updaten
});
```
### Fehlervermeidung: Keine zyklischen Bindings
```slint
// FALSCH: Zyklus
property <int> a: b + 1;
property <int> b: a + 1; // Endlosschleife!
// RICHTIG: Einseitige Abhängigkeit oder Callbacks
property <int> eingabe: 0;
property <int> ergebnis: eingabe * 2; // OK
```
### Performance auf Android
- Vermeide sehr tiefe Layout-Verschachtelungen (> 10 Ebenen)
- Nutze `if`/`for` sparsam in animierten Bereichen
- Bilder mit `@image-url` werden zur Kompilierzeit eingebettet
- Für viele Listeneintraege: `VirtualModel` statt direktem Array
- Animationen: Maximal 300ms für major transitions, 80-150ms für micro-interactions
### Keyboard / Software-Tastatur
```slint
// TextInput öffnet automatisch die Soft-Keyboard
TextInput {
width: parent.width;
height: 48px;
placeholder-text: "Suche...";
input-type: text; // text | password | number | decimal | email | url | phone
// Auf Eingabe reagieren
edited => { debug("Text: " + self.text); }
accepted => { /* Return/Enter */ }
}
```
> **Bekanntes Problem**: `native-activity`-Feature hat eingeschraenkte Keyboard-Unterstützung.
> Für vollstaendige Tastatureingabe `game-activity`-Feature verwenden (benötigt Java-Stubs).
---
## 5. Checkliste vor Android-Deployment
- [ ] `[lib] crate-type = ["cdylib"]` in Cargo.toml gesetzt
- [ ] `backend-android-activity-06` Feature aktiviert
- [ ] Entry Point heisst `android_main`, nicht `main`
- [ ] `slint::android::init(app).unwrap()` als erste Zeile
- [ ] JDK 17 installiert (nicht 21 wegen bekanntem Bug)
- [ ] `ANDROID_HOME`, `ANDROID_NDK_ROOT`, `JAVA_HOME` gesetzt
- [ ] Alle Touch-Targets >= 48x48 px
- [ ] System-Insets (Status Bar, Navigation Bar) berücksichtigt
- [ ] Dark Mode getestet
- [ ] Auf echtem Geraet getestet (nicht nur Emulator)
- [ ] Schriftgroessen >= 14px für Body-Text
- [ ] Farbkontrast >= 4.5:1 (WCAG AA)
- [ ] Scrollbarer Content bei variablen Bildschirmhoehen
---
## 6. Wichtige Links
- Offizielle Doku: https://docs.slint.dev
- Android API: https://docs.slint.dev/latest/docs/rust/slint/android/
- Live Preview: https://slintpad.com
- Slint Material Widgets: `import { ... } from "std-widgets.slint"` mit `SLINT_STYLE=material`
- Beispielprojekte: https://github.com/slint-ui/slint/tree/master/examples
- Material Design 3: https://m3.material.io
Files in this skill
- SKILL.md
- source.json
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.