Berechtigungen und Foto-Auswahl in Compose – rememberLauncherForActivityResult
Kamera-Erlaubnis abfragen, ein Bild aus der Galerie holen, ein Dokument öffnen: Dafür gibt es in Compose genau einen Weg, und er ist erfreulich kurz. onActivityResult und startActivityForResult brauchst du nie wieder.
Fast jede App braucht das irgendwann: „Darf ich auf die Kamera?", „Such dir ein Bild aus", „Öffne diese PDF". Früher war das ein Ritual aus startActivityForResult, magischen Request-Codes und einem onActivityResult, in dem irgendwann alles zusammenlief. In Compose ist es ein Einzeiler pro Anliegen.
Eine Berechtigung anfragen
@Composable
fun KameraButton(onErlaubt: () -> Unit) {
val ctx = LocalContext.current
var abgelehnt by remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { erlaubt ->
if (erlaubt) onErlaubt() else abgelehnt = true
}
Column {
Button(onClick = {
val schon = ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA)
if (schon == PackageManager.PERMISSION_GRANTED) onErlaubt()
else launcher.launch(Manifest.permission.CAMERA)
}) { Text("Foto aufnehmen") }
if (abgelehnt) {
Text("Ohne Kamera-Erlaubnis geht das leider nicht.")
}
}
}Zwei Dinge sind hier wichtig:
- Erst prüfen, dann fragen.
checkSelfPermissionkostet nichts und erspart dem Nutzer einen Dialog, den er schon einmal beantwortet hat. - Der Launcher lebt an der Composition.
rememberLauncherForActivityResultregistriert sich beim Aufbau und meldet sich beim Verschwinden wieder ab. Genau deshalb darfst du ihn nicht in einemif-Zweig oder einer Schleife erzeugen — er muss bei jeder Recomposition an derselben Stelle stehen.
Ein Bild auswählen – ohne jede Berechtigung
Das ist der Teil, den viele übersehen: Für den Photo Picker brauchst du gar keine Speicher-Berechtigung. Das System zeigt die Auswahl, deine App bekommt nur das eine Bild.
var bild by remember { mutableStateOf<Uri?>(null) }
val picker = rememberLauncherForActivityResult(
ActivityResultContracts.PickVisualMedia()
) { uri -> bild = uri } // uri ist null, wenn abgebrochen wurde
Button(onClick = {
picker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}) { Text("Bild wählen") }
bild?.let { AsyncImage(model = it, contentDescription = null) }Weniger Code und weniger Berechtigungen — die einzige Kombination, bei der man nichts abwägen muss. Wer noch READ_EXTERNAL_STORAGE anfragt, nur um ein Profilbild auszuwählen, sollte hier umsteigen.
Mehrere Berechtigungen auf einmal
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { ergebnis: Map<String, Boolean> ->
val alleDa = ergebnis.values.all { it }
// …
}
launcher.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))Was ich dabei gelernt habe
Frag erst, wenn es einen sichtbaren Grund gibt. Ein Berechtigungsdialog beim ersten App-Start, ohne dass der Nutzer irgendetwas angetippt hat, wird sehr oft weggeklickt — und beim zweiten „Nein" ist die Tür bei Android dauerhaft zu. Frage stattdessen genau in dem Moment, in dem jemand auf „Foto aufnehmen" tippt. Dann ist der Zusammenhang klar und die Zustimmungsrate deutlich höher.
Und plane den Fall ein, dass jemand ablehnt. Eine App, die danach nur noch eine leere Fläche zeigt, wirkt kaputt. Ein Satz reicht: was fehlt, und wie man es in den Einstellungen nachholen kann.
Du baust gerade eine Android-App und bist dir bei so einer Stelle unsicher? Schreib mir — ich schaue mir das gerne an.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
kotlinx.serialization: JSON und data class ohne Handarbeit verbinden
Eine API antwortet mit JSON, deine App will eine data class. Dazwischen steht in vielen Projekten ein handgeschriebener Parser. Mit kotlinx.serialization sind es eine Annotation und eine Zeile – und ein Schalter, ohne den deine App beim nächsten API-Update abstürzt.
AnimatedVisibility in Jetpack Compose – ein- und ausblenden, das nicht springt
Ein Element einfach mit if wegzulassen, poppt hart weg. AnimatedVisibility blendet es weich ein und aus – mit sinnvollen Voreinstellungen und, wenn du willst, deiner eigenen Kombination aus Faden und Bewegung. Ich zeige dir beides.
snapshotFlow in Jetpack Compose – aus Compose-Zustand einen Flow machen
Manchmal willst du auf einen Compose-Zustand reagieren wie auf einen Datenstrom – entprellen, kombinieren, sammeln. Genau das macht snapshotFlow. Ich zeige dir das an der häufigsten Aufgabe: mehr laden, wenn der Nutzer ans Listenende scrollt.