- Kotlin 100%
SelectionPill takes the place of the navigation while entries are selected: a check with the count, then actions for the selection. While sliding away it keeps what it showed last. RefreshBox no longer rebuilds its content when enabled changes, which cut off running gestures. |
||
|---|---|---|
| design | ||
| gradle | ||
| sample | ||
| .gitignore | ||
| build.gradle.kts | ||
| CHANGELOG.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle.kts | ||
needanapp-design
Design-Paket von needanapp.de für Android-Apps: Theme, Darstellungs-Einstellungen und Bausteine im Material-3-Expressive-Stil. Kotlin, Jetpack Compose, Material 3, minSdk 31 (Android 12).
Lizenz: Apache 2.0. Das Paket enthält keinen Code aus GPL-Projekten; alles hier ist eigener Code.
Inhalt
| Bereich | Was |
|---|---|
theme |
NeedanappTheme (Compose), NeedanappViewTheme + Theme.Needanapp (XML für Views), ThemeSettings, ThemeSettingsStore (DataStore), DesignColors (Farben des Kartenlooks), Bewegungs-Kurven |
settings |
AppearanceSettings: fertiger Bildschirm „Darstellung“ mit allen Theme-Einstellungen |
components |
AppScaffold (Kopfzeile mit Blur, Suche), NavigationPill, NavigationPillWithToolbar, NavigationPillWithMenu (Menüknopf, klappt nach oben auf), SelectionPill (Anzahl und Aktionen, während ausgewählt wird), gruppierte Listen (SettingsGroup, ListRow, SwitchRow, SliderRow, SegmentedRow, ContentRow), OptionsSheet, SheetAction, EmptyState, ColorPicker; Karten (SectionCard mit StatusBand, LoadingBar, LabeledRow, TagPill, PillButton, ActionBar, Modifier.designCard) |
sample |
Beispiel-App zum Ausprobieren aller Bausteine und Einstellungen |
Darstellungs-Einstellungen
- Modus System / Hell / Dunkel, reines Schwarz (AMOLED), Helligkeit im Dunkelmodus (hellere Flächen und Karten)
- Farbwelt je Modus: feste Grund-, Karten- und Textfarben (hell: Wolke (Standard), Papier, Nordschnee, Latte; dunkel: Graphit, Nordnacht (Standard), Mitternacht, Gedimmt) oder Getönt (aus der Akzentfarbe); dazu kräftige Welten mit eigener Akzentfarbe für „Standard“, hell und dunkel: Ozean (Petrol/Koralle), Aubergine (Pflaume/Pink), Senf (Senfgelb/Tannengrün), Feuer (Gelb/Rot)
- Akzentquelle: Standard (#3D63D8), Material You, eigene Farbe (Farbwähler, Hex, gespeicherte Farben), Vorlagen (Saphir, Smaragd, Bernstein, Violett, Koralle, Petrol, Limette, Rosé, Schiefer)
- Paletten-Stil: Tonal Spot, Neutral, Kräftig, Expressiv, Regenbogen, Obstsalat, Monochrom, Farbtreu (Standard), Inhalt
- Flächen-Tönung (0–2), Farbverlauf im Hintergrund an/aus (Standard seit 0.28.0: aus), Kartenhelligkeit (−100 bis +100 %, Hintergrund bleibt)
- Progressiver Blur, Transparenz der Pille (0–80 %), „Effekte reduzieren“ (schaltet Blur, Schatten und Übergänge ab; die System-Einstellung „Animationen entfernen“ wird ebenfalls beachtet)
- UI-Skalierung (85–115 %), Kartenecken (0–32 dp, Standard 14), eigene Schriftart (.ttf/.otf)
- „Nur ein Abschnitt offen“: einklappbare Abschnitte als exklusive Akkordeons (Standard seit 0.27.0)
- Sprache (System oder manuell, über Androids App-Sprachen), nur bei Apps mit mehr als einer Sprache
Kartenlook
Seit 0.20 hat jede App dasselbe Aussehen, ohne Schalter: Flächen und Ränder sind etwas zur Hauptfarbe getönt, im Dunkeln zusätzlich ein Stück zu Schwarz gezogen, damit nichts grell wirkt. Der Hintergrund ist einfarbig; zuschaltbar ist ein Verlauf mit einem Schein der Hauptfarbe oben (bei reinem Schwarz bleibt er schwarz, mit Flächen-Tönung 0 ohne Schein). Inhalte stehen auf Karten mit feinem Rand und weichem Schatten (ohne bei „Effekte reduzieren“). Alle Farboptionen wirken weiter; der Look folgt Akzentfarbe, Palette, Hell/Dunkel und Tönung.
NeedanappThemestellt die zusätzlichen Farben alsLocalDesignColorsbereit: Verlauf, Kartenfläche und -rand, Hinweistext, Grün für einen guten Zustand, Leisten unten.AppScaffoldzeichnet den Verlauf selbst (backgroundColorersetzt ihn); eigene Bildschirme nehmenModifier.designBackground(LocalDesignColors.current). Eine Leiste oben, die nahtlos übergehen soll, zeichnetcolors.backgroundBrush(höheDesFensters).SectionCard: Karte mit kleinem Titel in Großbuchstaben, optional Zusatz rechts, Band mit Zustand (CardStatus, TonStatusTone) und Wasserzeichen; mitloadingläuft im Band einLoadingBar.LabeledRowfür Zeilen mit Beschriftung,TagPillfür kleine Pillen (Kategorien, „Neu“),PillButtonfür die Hauptaktion,ActionBarmitActionBarButton/ActionBarSlotfür die Aktionen unten auf einer Detailseite.- Gespeicherte Werte, die noch auf den alten Standards stehen (Tonal Spot, 20 dp), stellt der
ThemeSettingsStoreeinmalig auf die neuen um; was danach gewählt wird, bleibt.
Die Einstellungen liegen im DataStore needanapp_theme der App. Einklappbare Abschnitte starten immer
eingeklappt; nur der zuletzt geöffnete Abschnitt eines Bildschirms bleibt beim nächsten Besuch offen und wird dafür
ebenfalls dort gespeichert. Einklappbare Abschnitte (SettingsGroup mit id, sectionState und icon) haben als
Kopf eine Zeile wie die übrigen, mit Pfeil am Ende; aufgeklappt hängen ihre Zeilen darunter. Aufeinanderfolgende Gruppen
mit block = RowPosition.FIRST/MIDDLE/LAST bilden einen Block ohne großen Zwischenraum.
Einbinden
Vorerst als Gradle Composite Build (später JitPack). Beide Repos liegen nebeneinander:
// settings.gradle.kts der App
includeBuild("../needanapp-design")
// app/build.gradle.kts
dependencies {
implementation("de.needanapp:design")
}
Compose
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
val store = ThemeSettingsStore(this)
setContent {
// gespeicherte Einstellungen abwarten statt kurz die Standardwerte zu zeigen
val settings by store.settings.collectAsStateWithLifecycle(initialValue = store.cachedSettings)
settings?.let { NeedanappTheme(it) { App(it) } }
}
}
}
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// liest die Einstellungen schon beim Start, dann zeigt das erste Bild die App
ThemeSettingsStore(this).preload()
}
}
@Composable
fun App(settings: ThemeSettings) {
val search = rememberSearchState()
AppScaffold(
title = stringResource(R.string.title),
search = search,
bottomBar = { hazeState ->
NavigationPill(items, selected, onSelect = { … }, hazeState = hazeState,
modifier = Modifier.align(Alignment.BottomCenter))
},
) { padding ->
// Inhalt scrollt unter Kopfzeile und Pille: padding als contentPadding verwenden
}
}
Der Darstellungs-Bildschirm ist ein einziger Aufruf:
AppearanceSettings(settings, contentPadding = …).
Die Sample-App (sample/…/MainActivity.kt) zeigt alle Bausteine im Zusammenspiel.
Views (Übergangszeit)
Noch nicht umgestellte Bildschirme bekommen dieselben Farben:
<style name="AppTheme" parent="Theme.Needanapp" />
override fun onCreate(savedInstanceState: Bundle?) {
NeedanappViewTheme.apply(this) // vor super.onCreate
super.onCreate(savedInstanceState)
}
Sprachen
Gepflegt werden nur Englisch (values/, Standard) und Deutsch (values-de/). Alle Texte liegen in
String-Ressourcen mit dem Präfix nd_. Apps liefern per androidResources.localeFilters += listOf("en", "de")
nur diese beiden Sprachen aus. Für die Sprachauswahl sollten sie generateLocaleConfig = true setzen
(siehe sample/build.gradle.kts).
Abhängigkeiten
| Bibliothek | Lizenz | Zweck |
|---|---|---|
| AndroidX, Compose, Material 3, Material Components | Apache 2.0 | Grundlage |
| MaterialKolor | MIT | Farbschemata aus Akzentfarbe und Paletten-Stil |
| Haze | Apache 2.0 | Hintergrund-Blur für Kopfzeile und Pille |
Bauen
./gradlew :sample:assembleDebug
Versionierung nach Semantic Versioning (DESIGN_VERSION in gradle.properties), Änderungen in
CHANGELOG.md.