mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-06-01 22:19:18 +02:00
f4b6b02ace
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4.8 KiB
4.8 KiB
Skill: Compose Multiplatform (CMP) UI
Description
Guidelines for building shared UI, adaptive layouts, and handling strings/resources in Meshtastic-Android. The codebase uses Material 3 Adaptive.
1. UI Components & Layouts
- Material 3 / Adaptive: Use
currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)to support Large (1200dp) and XL (1600dp) breakpoints. Investigate 3-pane "Power User" scenes using Navigation 3 Scenes and draggable dividers for desktopApp/tablets. - Dialogs & Alerts: Use centralized components like
AlertHost(alertManager)fromcore:ui/commonMain. Do NOT trigger alerts inline or duplicate alert logic. UseSharedDialogs(uiViewModel)for general popups. - Placeholders: Use
PlaceholderScreen(name)fromcore:ui/commonMainfor unimplemented desktopApp/JVM features. - Theme Picker: Use
ThemePickerDialogfromfeature:settings/commonMain. - Platform Implementations: Inject platform-specific behavior (e.g., Map providers) via
CompositionLocalfrom theappordesktopshells. Do not tightly couple Google Maps/osmdroid dependencies tocommonMain.
2. Strings & Resources
- Multiplatform Resources: MUST use
core:resources(e.g.,stringResource(Res.string.your_key)). Never use hardcoded strings. - ViewModels/Coroutines: Use the asynchronous
getStringSuspend(Res.string.your_key). NEVER use blockinggetString()in a coroutine context. - Formatting Constraints: CMP
stringResourceonly supports%N$s(string) and%N$d(integer).- No Float formatting: Formats like
%N$.1fpass through unsubstituted. Pre-format in Kotlin usingNumberFormatter.format(value, decimalPlaces)fromcore:commonand pass as a string argument (%N$s):val formatted = NumberFormatter.format(batteryLevel, 1) // "73.5" stringResource(Res.string.battery_percent, formatted) // uses %1$s - Percent Literals: Use bare
%(not%%) for literal percent signs in CMP-consumed strings.
- No Float formatting: Formats like
String Formatting Decision Tree
Choose the right tool for the job:
| Scenario | Tool | Example |
|---|---|---|
| Metric display (temp, voltage, %, signal) | MetricFormatter.* |
MetricFormatter.temperature(25.0f, isFahrenheit) → "77.0°F" |
| Simple number + unit | NumberFormatter + interpolation |
"${NumberFormatter.format(val, 1)} dB" |
| Localized template from strings.xml | stringResource(Res.string.key, preFormattedArgs) |
stringResource(Res.string.battery, formatted) |
| Non-composable template (notifications, plain functions) | formatString(template, args) |
formatString(template, label, value) |
| Hex formatting | formatString |
formatString("!%08x", nodeNum) |
| Date/time | DateFormatter |
DateFormatter.format(instant) |
Rules:
- NEVER use
%.Nfin strings.xml — CMP cannot substitute them. Use%N$sand pre-format floats. - Prefer
MetricFormatterover scatteredformatString("%.1f°C", temp)calls. formatString(pure Kotlin) is a pure-KotlincommonMainimplementation for: hex formats, multi-arg templates fetched at runtime, and chart axis formatters. Located incore:commonFormatter.kt.NumberFormatteralways uses.as decimal separator — intentional for mesh networking precision.
- Workflow to Add a String:
- Add to
core/resources/src/commonMain/composeResources/values/strings.xml. - Use the generated
org.meshtastic.core.resources.<key>symbol. - Validate UI presentation.
- Add to
3. Tooling & Capabilities
- Image Loading: Use
libs.coil(Coil Compose) in feature modules. Configuration/Networking for Coil (coil-network-ktor3) happens strictly in theappanddesktophost modules. - QR Codes: Use
rememberQrCodePainterfromcore:ui/commonMainpowered byqrcode-kotlin. No ZXing or Android Bitmap APIs in shared code.
4. Compose Previews
- Preview in commonMain: CMP 1.11+ supports
@PreviewincommonMainviacompose-multiplatform-ui-tooling-preview. Place preview functions alongside their composables. - Import: Use
androidx.compose.ui.tooling.preview.Preview. The JetBrains-prefixed import (org.jetbrains.compose.ui.tooling.preview.Preview) is deprecated.
5. Dialog & State Patterns
- Dialog State Preservation: Use
rememberSaveablefor dialog state (search queries, selected tabs, expanded flags) to preserve across configuration changes. Boolean and String types are auto-saveable — no customSaverneeded.
Reference Anchors
- Shared Strings:
core/resources/src/commonMain/composeResources/values/strings.xml - Platform abstraction contract:
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt - Provider wiring:
androidApp/src/main/kotlin/org/meshtastic/app/MainActivity.kt