RecorderState
RecorderState is a Compose state holder that makes building recording UIs simple. It handles recording lifecycle, permission requests, error states, and provides live amplitude data for waveform visualizations.
Basic usage Create a recording UI in just a few lines:
@Composable
fun VoiceRecorder() {
val recorderState = rememberRecorderState()
Button(onClick = { recorderState.toggle() }) {
Text(if (recorderState.isRecording) "⏹ Stop" else "🎙 Record")
}
}
The state automatically triggers recomposition when recording starts, stops, or errors occur.
Configuration Customize the recorder with quality presets and callbacks:
val recorderState = rememberRecorderState(
quality = AudioQuality.High,
onRecordingComplete = { recording ->
// Called when recording stops
viewModel.save(recording)
}
)
RecorderState provides liveAmplitudes—a list of normalized amplitude values (0.0 to 1.0) updated in real-time during recording:
if (recorderState.isRecording) {
AudioWaveform(
amplitudes = recorderState.liveAmplitudes,
modifier = Modifier.fillMaxWidth().height(64.dp)
)
}
See AudioWaveform for customization options like colors, bar width, and gradients.
Permission handling RecorderState tracks permission status and can request it:
@Composable
fun RecorderWithPermission() {
val recorderState = rememberRecorderState()
if (recorderState.needsPermission) {
// Show permission request UI
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("🎤 Microphone access required")
Button(onClick = { recorderState.requestPermission() }) {
Text("Grant Access")
}
}
} else {
// Show recording UI
RecordButton(recorderState)
}
}
Error handling Display errors when they occur:
recorderState.error?.let { error ->
AlertDialog(
onDismissRequest = { recorderState.clearError() },
title = { Text("Recording Error") },
text = { Text(error.message ?: "Unknown error") },
confirmButton = {
TextButton(onClick = { recorderState.clearError() }) {
Text("OK")
}
}
)
}
Complete example Here's a full recording UI with permission handling, waveform, and error display:
@Composable
fun CompleteRecorder() {
val recorderState = rememberRecorderState(
quality = AudioQuality.Voice,
onRecordingComplete = { recording ->
println("Recorded ${recording.calculatedDuration}")
}
)
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Permission check
if (recorderState.needsPermission) {
Text("🎤 Microphone access needed")
Spacer(Modifier.height(8.dp))
Button(onClick = { recorderState.requestPermission() }) {
Text("Grant Access")
}
return@Column
}
// Waveform
if (recorderState.isRecording) {
AudioWaveform(
amplitudes = recorderState.liveAmplitudes,
modifier = Modifier.fillMaxWidth().height(80.dp),
barColor = MaterialTheme.colorScheme.primary
)
Spacer(Modifier.height(16.dp))
}
// Record button
Button(
onClick = { recorderState.toggle() },
colors = ButtonDefaults.buttonColors(
containerColor = if (recorderState.isRecording)
MaterialTheme.colorScheme.error
else
MaterialTheme.colorScheme.primary
)
) {
Text(if (recorderState.isRecording) "⏹ Stop Recording" else "🎙 Start Recording")
}
// Error dialog
recorderState.error?.let { error ->
AlertDialog(
onDismissRequest = { recorderState.clearError() },
title = { Text("Error") },
text = { Text(error.message ?: "Recording failed") },
confirmButton = {
TextButton(onClick = { recorderState.clearError() }) {
Text("OK")
}
}
)
}
}
}
API reference
Properties isRecording: Boolean Currently recording audio.
isProcessing: Boolean Processing after stop (encoding, etc.).
recording: AudioRecording? The completed recording, if available.
hasRecording: Boolean true if recording is not null.
liveAmplitudes: List<Float> Real-time amplitude values (0.0-1.0) for waveform display.
error: AudioError? Current error, if any.
needsPermission: Boolean true if microphone permission hasn't been granted.
Methods start() Start recording.
stop() Stop recording.
toggle() Start if stopped, stop if recording.
reset() Discard the current recording.
requestPermission() Request microphone permission from the OS.
clearError() Clear the current error state.
Last modified: 13 January 2026