# Custom Attachments Picker

The `AttachmentsPicker` component allows users to pick media, files or capture media attachments. You can find more info about it [here](/chat/docs/sdk/android/compose/message-components/attachments-picker/).

By default, it looks like below:

| Default - _Images_ tab selected                                                                     | Default - _Files_ tab selected                                                                      |
| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| ![Default Message Composer](@chat-sdk/android/v7-latest/_assets/cookbook_attachments_default_1.png) | ![Default Message Composer](@chat-sdk/android/v7-latest/_assets/cookbook_attachments_default_2.png) |

When we're done, our custom attachments picker will look like this:

| Custom - Attachment type menu                                                                      | Custom - File picker with _Back_ and _Submit_                                                      |
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| ![Default Message Composer](@chat-sdk/android/v7-latest/_assets/cookbook_attachments_custom_1.png) | ![Default Message Composer](@chat-sdk/android/v7-latest/_assets/cookbook_attachments_custom_2.png) |

Although the `AttachmentsPicker` can be customized extensively via `ChatComponentFactory` overrides, as you can read [here](/chat/docs/sdk/android/compose/message-components/attachments-picker#customization/), for this example we'll create our own component. We'll use the `AttachmentsPickerViewModel` from the SDK to manage picker state and the available `AttachmentPickerMode` definitions to identify attachment types.

## Main Composable

Let's define the main composable for our custom attachment picker. Notice that it expects an `AttachmentsPickerViewModel`, which is part of our SDK. The available attachment modes are sourced from `ChatTheme.config.attachmentPicker.modes`, which provides the configured modes (_Gallery_, _Files_, _Camera_, etc.).

```kotlin
@Composable
private fun CustomAttachmentsPicker(
    attachmentsPickerViewModel: AttachmentsPickerViewModel,
    onAttachmentsSelected: (List<Attachment>) -> Unit,
    onDismiss: () -> Unit,
) {
    var shouldShowMenu by remember { mutableStateOf(true) }
    var selectedMode by remember { mutableStateOf<AttachmentPickerMode?>(null) }

    Box( // Gray overlay
        modifier = Modifier
            .fillMaxSize()
            .background(ChatTheme.colors.backgroundCoreScrim)
            .clickable(
                onClick = onDismiss,
                indication = null,
                interactionSource = remember { MutableInteractionSource() },
            ),
    ) {
        Card(
            modifier = Modifier
                .heightIn(max = 350.dp)
                .align(Alignment.BottomCenter)
                .clickable(
                    indication = null,
                    onClick = {},
                    interactionSource = remember { MutableInteractionSource() },
                ),
            shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
        ) {
            Box(modifier = Modifier.padding(vertical = 24.dp)) {
                if (shouldShowMenu) {
                    // Show the menu with Gallery, Files, Camera options
                    AttachmentsTypeMenu(
                        onClick = { mode ->
                            selectedMode = mode
                            shouldShowMenu = false
                        },
                    )
                } else {
                    // Show the selected mode's content with back and submit buttons
                    Column(
                        modifier = Modifier.padding(horizontal = 8.dp),
                    ) {
                        AttachmentsPickerToolbar(
                            onBackClick = {
                                shouldShowMenu = true
                                selectedMode = null
                            },
                            isSubmitEnabled = attachmentsPickerViewModel.attachments.any { it.isSelected },
                            onSubmitClick = {
                                onAttachmentsSelected(
                                    attachmentsPickerViewModel.getAttachmentsFromMetadata(
                                        attachmentsPickerViewModel.attachments
                                            .filter { it.isSelected }
                                            .map { it.attachmentMetaData },
                                    ),
                                )
                            },
                        )

                        // Render the default attachment picker content for the selected mode
                        selectedMode?.let { mode ->
                            ChatTheme.componentFactory.AttachmentPickerContent(
                                params = AttachmentPickerContentParams(
                                    pickerMode = mode,
                                    commands = emptyList(),
                                    attachments = attachmentsPickerViewModel.attachments,
                                    onLoadAttachments = {},
                                    onUrisSelected = {},
                                    actions = AttachmentPickerActions.pickerDefaults(
                                        attachmentsPickerViewModel = attachmentsPickerViewModel,
                                    ).copy(onDismiss = onDismiss),
                                    onAttachmentsSubmitted = {
                                        onAttachmentsSelected(
                                            attachmentsPickerViewModel.getAttachmentsFromMetadata(it),
                                        )
                                    },
                                ),
                            )
                        }
                    }
                }
            }
        }
    }
}
```

## Attachment Type Menu

The attachment type menu (_Gallery_, _Files_, _Camera_) is drawn by a simple composable that shows a menu item for each configured `AttachmentPickerMode`.

```kotlin
@Composable
private fun AttachmentsTypeMenu(
    onClick: (AttachmentPickerMode) -> Unit,
) {
    val modes = ChatTheme.config.attachmentPicker.modes
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceEvenly,
        verticalAlignment = Alignment.CenterVertically,
    ) {
        modes.forEach { mode ->
            AttachmentsTypeMenuItem(
                mode = mode,
                onClick = onClick,
            )
        }
    }
}
```

Each menu item is represented by a circle with a label underneath. So we need a `Column`, a circle-shaped `Box` with a certain background color, and a `Text`.

```kotlin
@Composable
private fun AttachmentsTypeMenuItem(
    mode: AttachmentPickerMode,
    onClick: (AttachmentPickerMode) -> Unit,
) {
    val backgroundColor: Color
    val label: String
    val icon: ImageVector

    when (mode) {
        is GalleryPickerMode -> {
            backgroundColor = Color(0xFFCCCCFF)
            label = "Images"
            icon = Icons.Default.Image
        }
        is FilePickerMode -> {
            backgroundColor = Color(0xFFFFCCCC)
            label = "Files"
            icon = Icons.Default.Description
        }
        is CameraPickerMode -> {
            backgroundColor = Color(0xFFFFCC99)
            label = "Camera"
            icon = Icons.Default.CameraAlt
        }
        else -> {
            backgroundColor = Color.LightGray
            label = "Other"
            icon = Icons.Default.MoreHoriz
        }
    }

    Column(
        modifier = Modifier.clickable { onClick(mode) },
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Box(
            modifier = Modifier
                .padding(8.dp)
                .size(48.dp)
                .background(backgroundColor, shape = CircleShape),
            contentAlignment = Alignment.Center,
        ) {
            Icon(
                imageVector = icon,
                contentDescription = label,
            )
        }
        Text(text = label)
    }
}
```

## Picker Toolbar

Above each picker, we draw a toolbar with _Back_ and _Submit_ buttons. _Back_ navigates to the menu and _Submit_ attaches the selected file to the message contents.

```kotlin
@Composable
private fun AttachmentsPickerToolbar(
    onBackClick: () -> Unit,
    isSubmitEnabled: Boolean,
    onSubmitClick: () -> Unit,
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        IconButton(onClick = onBackClick) {
            Icon(
                painter = painterResource(id = R.drawable.ic_back),
                contentDescription = "Back",
                modifier = Modifier.size(24.dp),
            )
        }
        IconButton(
            enabled = isSubmitEnabled,
            onClick = onSubmitClick
        ) {
            Icon(
                painter = painterResource(id = R.drawable.ic_check),
                contentDescription = "Submit Attachments",
                modifier = Modifier.size(24.dp),
                tint = if (isSubmitEnabled) {
                    ChatTheme.colors.accentPrimary
                } else {
                    ChatTheme.colors.textSecondary
                },
            )
        }
    }
}
```

## Usage

We'll place our custom attachments picker in a screen that contains other components, like a header, a messages list and a message composer. We'll also use the `BackHandler` standard component.

In order to show the attachments picker, we'll use an `isShowingAttachments` flag. We'll wrap all components in a `Box`, so that the attachments picker appears on top of other content.

```kotlin
fun CustomScreen(cid: String, onBackClick: () -> Unit = {}) {
    val viewModelFactory = ChannelViewModelFactory(LocalContext.current, channelId = cid)
    val listViewModel = viewModel(modelClass = MessageListViewModel::class.java, factory = viewModelFactory)
    val composerViewModel = viewModel(modelClass = MessageComposerViewModel::class.java, factory = viewModelFactory)
    val attachmentsPickerViewModel = viewModel(modelClass = AttachmentsPickerViewModel::class.java, factory = viewModelFactory)

    val isShowingAttachments = attachmentsPickerViewModel.isPickerVisible

    val backAction = remember(composerViewModel, attachmentsPickerViewModel) {
        {
            // First close the attachments picker, if visible, then call onBackClick
            when {
                attachmentsPickerViewModel.isPickerVisible -> {
                    attachmentsPickerViewModel.setPickerVisible(false)
                }
                else -> onBackClick()
            }
        }
    }
    BackHandler(enabled = true, onBack = backAction) // Standard SDK component

    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
        // Screen content
        Scaffold(
            topBar = {
                // ChannelHeader
            },
            bottomBar = {
                // MessageComposer
            },
            content = {
                // MessageList
            }
        )

        // Attachments picker
        if (isShowingAttachments) {
            CustomAttachmentsPicker(
                attachmentsPickerViewModel = attachmentsPickerViewModel,
                onAttachmentsSelected = { attachments ->
                    attachmentsPickerViewModel.setPickerVisible(false)
                    composerViewModel.addAttachments(attachments)
                },
                onDismiss = {
                    attachmentsPickerViewModel.setPickerVisible(false)
                }
            )
        }
    }
}
```

## More Resources

If you want to learn how to use our Compose UI Components, see [this](/chat/docs/sdk/android/compose/overview/) page.

Also, check the other pages in this Cookbook to find out how to create custom versions of our components.


---

This page was last updated at 2026-04-22T14:09:27.209Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/compose-cookbook/custom-attachments-picker/](https://getstream.io/chat/docs/sdk/android/compose-cookbook/custom-attachments-picker/).