🔍 Lookup and Manage Documents
Once your DocumentStore is initialized and populated, you can fetch, list, and manage documents within it.
Listing and Fetching Documents
The simplest way to render the documents in the store is to use the CardCarousel composable from multipaz-compose. It is backed by a DocumentModel, which observes the DocumentStore reactively — once you create the model from your store and document type repository, the carousel updates automatically as documents are added or removed (e.g. after a successful provisioning flow), so you don't need to manually re-fetch and diff a list yourself.
If you do need direct access to the documents (for non-UI logic), DocumentStore#listDocuments still returns them.
Example: Listing Documents
1: Define the listDocuments function
The listDocuments function is part of the AppContainer interface and implemented in AppContainerImpl (in the core module):
// core/src/commonMain/kotlin/.../core/AppContainer.kt
interface AppContainer {
suspend fun listDocuments(): MutableList<Document>
// ... rest of the implementations
}
Refer to this AppContainer code for the complete example.
// core/src/commonMain/kotlin/.../core/AppContainerImpl.kt
class AppContainerImpl : AppContainer {
// ...
override suspend fun listDocuments(): MutableList<Document> {
val documents = mutableStateListOf<Document>()
for (document in documentStore.listDocuments()) {
if (!documents.contains(document)) {
documents.add(document)
}
}
return documents
}
}
Refer to this listDocuments code for the complete example.
2: Render the documents in HomeScreen using CardCarousel
Build a DocumentModel from container.documentStore and container.documentTypeRepository (the latter is wired up alongside the document store — see Setting Up the DocumentStore) and feed its documentInfos flow to CardCarousel. We use produceState so model creation runs as a suspend block tied to the composition, and collectAsState() on the model's documentInfos to drive the carousel reactively.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(
// ...
) {
val coroutineScope = rememberCoroutineScope { AppContainer.promptModel }
Column {
// ...
val documentModel by produceState<DocumentModel?>(null, container) {
value = DocumentModel.create(
documentStore = container.documentStore,
documentTypeRepository = container.documentTypeRepository,
)
}
var selectedDocumentId by rememberSaveable { mutableStateOf<String?>(null) }
documentModel?.let { model ->
val documentInfos by model.documentInfos.collectAsState()
CardCarousel(
cardInfos = documentInfos,
onCardClicked = { selectedDocumentId = it.identifier },
)
selectedDocumentId?.let { id ->
ModalBottomSheet(onDismissRequest = { selectedDocumentId = null }) {
DocumentDetails(
documentModel = model,
documentStore = container.documentStore,
documentId = id,
onDocumentDeleted = { selectedDocumentId = null },
)
}
}
}
}
}
A few notes on the snippet:
CardCarouselrenders each entry from thedocumentInfoslist as a card art tile and exposes anonCardClickedcallback that receives theDocumentInfofor the tapped card — itsidentifiermatches the underlyingDocument's identifier.selectedDocumentIdis held withrememberSaveableso the selection survives configuration changes (e.g. rotation), and surfaces aModalBottomSheetthat hosts theDocumentDetailscomposable defined in the next section.
Refer to this code from HomeScreen.kt for the complete example.
Showing Document Details and Deleting
Tapping a card in the carousel opens a ModalBottomSheet hosting a DocumentDetails composable. It displays the document's card art, type, name, and provisioning status, and exposes a delete button. We prevent deletion of the default sample document so the store is never empty.
@Composable
private fun DocumentDetails(
documentModel: DocumentModel,
documentStore: DocumentStore,
documentId: String,
onDocumentDeleted: () -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val documentInfo = documentModel.documentInfos.collectAsState().value
.find { it.document.identifier == documentId }
if (documentInfo == null) {
Text("No document for identifier $documentId")
return
}
val document = documentInfo.document
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Image(
modifier = Modifier.height(200.dp),
contentScale = ContentScale.FillHeight,
bitmap = documentInfo.cardArt,
contentDescription = null,
)
Text(
text = document.typeDisplayName ?: "(typeDisplayName not set)",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.secondary,
)
KeyValuePair("Provisioned", if (document.provisioned) "Yes" else "No")
KeyValuePair("Document Type", document.typeDisplayName ?: "(typeDisplayName not set)")
KeyValuePair("Document Name", document.displayName ?: "(displayName not set)")
if (document.displayName != CredentialDomains.SAMPLE_DOCUMENT_DISPLAY_NAME)
Button(
onClick = {
coroutineScope.launch { documentStore.deleteDocument(documentId) }
onDocumentDeleted()
},
colors = ButtonDefaults.buttonColors(
containerColor = Color.Red,
contentColor = Color.White,
),
) {
Text("Delete document")
}
}
}
@Composable
private fun KeyValuePair(key: String, value: String) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(text = key, fontWeight = FontWeight.Bold)
Text(text = value)
}
}
Key things to note:
documentModel.documentInfosis aStateFlow<List<DocumentInfo>>, socollectAsState()keeps the bottom sheet content live — if the document is deleted from another code path, the lookup will returnnulland you fall through to the empty branch.DocumentStore#deleteDocument(identifier: String)is the underlying API for removal; on success the carousel auto-refreshes viaDocumentModel.- Calling
onDocumentDeleted()clearsselectedDocumentIdin the parent so the bottom sheet dismisses.
Refer to this code from HomeScreen.kt for the complete example.
By following these steps, the document list, detail view, and deletion flow stay consistent with the underlying DocumentStore automatically — no manual list maintenance required.