# Matisse is a Compose picker that treats permissions and the camera as policy

> Matisse is an Android image and video picker framework written in Kotlin with Jetpack Compose. Its interesting decisions are not the grid of thumbnails but the surrounding ones: permissions are requested when they are needed rather than up front, the camera flow is a policy the application supplies, the image loader is pluggable, and the same screen can return images, video, or both.

**leavesCZY/Matisse** — An Android image and video picker framework built with Jetpack Compose.（一个基于 Jetpack Compose 实现的 Android 图片和视频选择框架）

- Repository: https://github.com/leavesCZY/Matisse
- Website: https://github.com/leavesCZY/Matisse/wiki
- Stars: 1,002 · Forks: 74
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/leavesczy-matisse

## The problem is the fragmentation, not the thumbnail grid

Every Android application that lets a user pick a photo eventually learns that the platform picker is not one thing. The system component differs between Android versions, between manufacturers, and between the photo and video paths, and the behaviour a user gets depends on the device they happen to own. Matisse describes its first advantage as broad compatibility, solving the compatibility problems between different system versions, which is the honest framing of what a picker framework is for, since building a grid of thumbnails is easy and building one that behaves the same everywhere is not. The second advantage is the permission practice, following the Android permission model and asking for permissions on demand rather than at startup, which is both a policy requirement and a user experience one, since a permission dialog before the user has said what they want is the fastest way to a permanent denial. The third is that the whole thing is Kotlin and Compose, with no view interop layer in the middle.

## Permissions are requested at the moment they are justified

The on demand permission model is worth a second look, because it changes where the code that asks lives. A picker that needs storage permission on old Android versions and a photo picker permission on newer ones, and which also needs the camera permission when the user chooses to take a picture rather than choose an existing one, has three different permission situations. Handling them at the point of use means the camera path only asks for camera access when the user actually opens the camera, and the media path only asks for what that path needs, and the user sees a prompt that is connected to the action they just took. The consequence for the integrating developer is that the framework owns the request flow rather than exposing a permission helper, so the application code is about choosing a mode and handling the result. The trade is that the application has less control over the exact wording and timing of the prompt, which is a real consideration when a product has its own permission copy.

## The camera is a policy the application supplies

The fourth feature is flexible capture strategy, with support for several shooting strategies and the ability to customise the capture logic. This is a bigger decision than it sounds, because a camera inside a picker is where an opinionated library usually imposes its own screen. The usual pattern is that the framework owns the whole flow, the preview, the shutter button, the confirmation step, and the application gets a file back, which is simple but rigid, since a product that wants a different capture experience, a different confirmation step or a mirrored preview has to fight the library. Matisse's stated position is that the strategy is a parameter, and that you can supply your own logic, which moves the work into the application and makes the library smaller. The cost is that the application now has to implement and maintain a camera screen, and the framework's own camera experience is only there for the cases where you do not need one.

## Image loading is pluggable, which matters more than it sounds

The fifth feature is custom image loading, with support for plugging in image loading frameworks such as Coil or Glide. The reason this matters is that a picker shows a lot of images at once, and the loader is the part of the pipeline that decides whether the grid scrolls smoothly on a low end device. A library that ships its own loader, or that assumes one particular library, forces the application into a decision about its whole image stack, and the usual outcome is two loaders in one application, one for the picker and one for the rest of the app, with duplicated caches and duplicated disk usage. Letting the application pass in its own loader means the picker shares the cache with the rest of the application, and the thumbnails it shows are the same objects the application already has. The cost of that choice is an integration point, a callback or an interface the application has to implement, and a little more work before the first grid appears.

## The selection mode is part of the contract

The sixth feature is the selection mode, and the description is precise about it: the framework supports selecting images and video at the same time, and also supports selecting only one media type. That sounds trivial until you have written one, because the two modes have different result types, different counts, and different behaviour on the selection limit. An application that offers a gallery of photos and a separate video capture usually wants one type at a time, while an application that builds a post with mixed media wants both. Making that a first class parameter rather than a consequence of how the developer wired the callbacks means the same screen serves both, and the result the application receives tells it what came back. The related constraint is the selection limit, which the feature list does not describe in detail, so an application that needs a hard cap on the number of items, or a different cap per media type, should check the integration guide rather than assume.

## Theming goes beyond light and dark

The seventh feature is a theming system with deep customisation, and the README shows three screenshots side by side, the day theme, the night theme and a custom theme. Having the two standard themes built in is table stakes for a Compose library, but the third screenshot is the interesting one, because it is evidence that the theming is meant to be changed rather than merely switched. For a picker that sits inside a product, the difference between a library that offers a dark mode and one that lets you set the accent colour, the corner radius, the spacing and the selection indicator is the difference between a component you can ship and one you can only ship in a certain product. The practical caution is that a custom theme is a place where the library's own layout assumptions can show, so a team that intends to restyle it should plan on the wiki page that documents the theme parameters rather than discovering them from the sample application.

## The repository is a Gradle project with a sample app and a published artifact

The tree shows a conventional Gradle setup: the `matisse/` directory is the library module, `app/` is the sample application, `build-logic/` holds the convention plugins, and there is a `jitpack.yml` along with the Gradle wrapper scripts and the settings and build files in the Kotlin DSL. The library is published on Maven Central under the `io.github.leavesczy` group, and the dependency block in the README shows how it is consumed:

```kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

dependencies {
    implementation("io.github.leavesczy:matisse:latestVersion")
}
```

The `latestVersion` placeholder in that snippet is the one thing to be careful about, since a floating version in a build file is a reproducibility problem in a project that has to be audited. The repository also ships a `key.jks` for signing releases, and the licence is Apache-2.0 with the text in the LICENSE file. On the release cadence, the versions are 3.0.0 on 2026-10-05, 2.3.2 on 2026-05-01 and 2.3.1 on 2026-04-13, so the project is active and the major version has just moved, which is the point at which a migration note is worth reading.

## Conclusion

Matisse suits an Android team that wants a picker whose behaviour and look are under its own control, particularly one that needs the camera flow to match the product and the permission prompts to appear at the moment they are justified. It is a poor fit if you want the smallest possible dependency, since a framework that draws its own screens and themes brings its own opinions about layout and colour, and it is a poor fit if you need a feature the library does not model, since the selection modes and the camera policies are the two places where it is opinionated. Before you adopt it, check three things: which version you take, with 3.0.0 published on 2026-10-05 after 2.3.2 in May 2026, how the picker behaves on the oldest Android version you support, since cross version compatibility is the problem the library exists to solve, and whether the wiki covers the call you need, since the README deliberately keeps the integration guide there rather than inline.

## FAQ

### What is Matisse and what does it replace?

Matisse is an Android image and video picker framework written in Kotlin with Jetpack Compose. It exists to give consistent behaviour across Android versions, to request permissions on demand rather than at startup, and to let the application control the camera flow and the image loading rather than inherit them.

### How do I add Matisse to an Android project?

Add the dependency to your module, with `implementation("io.github.leavesczy:matisse:latestVersion")`, and make sure the repositories block includes google() and mavenCentral(). The README keeps the integration steps in the wiki rather than inline, and the repository ships a sample app in the app/ directory that shows a working setup.

### Can Matisse pick images and videos at the same time?

Yes. The feature list says it supports selecting images and video simultaneously, and also supports selecting only one media type, so an application can offer a single type picker or a mixed one from the same screen.

### Which image loading library does Matisse use?

It does not insist on one. The README states support for plugging in an image loading framework such as Coil or Glide, so the application supplies the loader and the picker shares the application's cache instead of shipping a second one.

### How do I customise the appearance of the Matisse picker?

The library provides a theming system with deep customisation and ships light and dark themes by default, and the README shows day, night and custom theme screenshots. The theme parameters are documented in the wiki rather than in the README, so check the integration guide for the values you can change.

## Sources

- [leavesCZY/Matisse on GitHub](https://github.com/leavesCZY/Matisse)
- [License: Apache-2.0](https://github.com/leavesCZY/Matisse/blob/master/LICENSE)
- [Project website](https://github.com/leavesCZY/Matisse/wiki)
- [README](https://github.com/leavesCZY/Matisse/blob/master/README.md)
- [Releases](https://github.com/leavesCZY/Matisse/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/leavesczy-matisse
