Pickers Stable
The editor never browses the user's files or shows a media library of its own. Whenever it needs something from outside, it sends your app a request, and your app returns the answer. That's how media can come from your photo picker, your own content or your backend, and how every picker can look like the rest of your app.
| Request | When | Return |
|---|---|---|
| Add media | Adding a clip to the main track | A source you added |
| Add overlay | Adding a layer above the main track | A source you added |
| Add audio | Adding music or sound | An audio source |
| Add sticker | Adding a sticker, when you give no sticker packs | An image source |
| Choose filter | Picking a look for a clip | An effect id from the effect catalog |
| Choose font | Changing a text's font | A font family |
| Choose colour | Any colour control (the request says which) | A colour |
| Edit text | Typing a text's content | The new text |
| Choose text style | Picking a text style | A style id from the text style catalog |
Your handler can take as long as it needs: show a sheet, wait for the user, download a file. The editor waits for the answer. Then:
- An answer applies it. Added media lands at the playhead, on the main track, as an overlay or on an audio track.
- Cancel (the user backed out) leaves things as they were.
- Nothing lets the SDK show its built-in picker for that request.
The built-in pickers are deliberately simple, for getting started:
| Platform | Built in |
|---|---|
| iOS, Android, React Native | Fonts, colours, text, text styles, filters. Media always needs your picker. |
| Web | All requests: a file chooser for media, the browser's colour input, and lists. |
| macOS | None: handle every request. |
How each platform spells it:
| Platform | Handler | Cancel | Built-in picker |
|---|---|---|---|
| iOS | async closure returning RequestResponse? | .cancel | nil |
| Android | suspend function returning RequestResponse? | RequestResponse.Cancel | null |
| React Native, web | function returning an answer or a promise | null | undefined |
Example: your media library
- iOS
- Android
- React Native
- Web
dock.onRequest = { request in
switch request.kind {
case .addMedia, .addOverlay:
guard let asset = await MediaLibrarySheet.present() else { return .cancel }
let media = try? editor.addMedia(id: asset.id, file: asset.localFile)
return media.map { .source(id: $0.source) } ?? .cancel
case .chooseFont:
guard let family = await BrandFontSheet.present() else { return .cancel }
return .text(value: family)
default:
return nil // built-in picker
}
}
dock.onRequest = { request ->
when (request.kind) {
RequestType.ADD_MEDIA, RequestType.ADD_OVERLAY -> {
val asset = mediaLibrarySheet.pick()
if (asset == null) RequestResponse.Cancel
else RequestResponse.Source(editor.addMedia(asset.localFile, id = asset.id).source)
}
RequestType.CHOOSE_FONT -> brandFontSheet.pick()?.let { RequestResponse.Text(it) } ?: RequestResponse.Cancel
else -> null // built-in picker
}
}
<NitrakitEditor
editor={editor}
onRequest={async (request) => {
switch (request.kind) {
case 'addMedia':
case 'addOverlay': {
const asset = await openMediaLibrary()
if (!asset) return null
return { source: editor.addMedia(asset.id, asset.localPath, asset.name).source }
}
case 'chooseFont': {
const family = await openBrandFonts()
return family ? { text: family } : null
}
}
// anything else: the built-in picker
}}
/>
const editor = await createEditor({
// …
async onRequest(request) {
switch (request.kind) {
case 'addMedia':
case 'addOverlay': {
const asset = await openMediaLibrary()
if (!asset) return null
return { source: (await editor.addMedia(asset.id, asset.url)).source }
}
case 'chooseFont': {
const family = await openBrandFonts()
return family ? { text: family } : null
}
}
// anything else: the built-in picker
},
})
Stickers
Stickers come from your library, organised in packs, as in messaging apps. Give the editor
your packs, and Sticker opens a browser of them: a row of pack tabs, each shown by an image
(the pack's icon, or its first sticker), and a grid of stickers. A tap adds the sticker at the
playhead, sized to a quarter of the video's shorter side.
Each sticker is a media id. The SDK fetches stickers through your media resolver, the same as other media: thumbnails for the ones on screen, then the file when one is picked. PNG, WebP, GIF and APNG all work, static or animated.
- iOS
- Android
- React Native
- Web
dock.stickerPacks = [
StickerPack(id: "emoji", name: "Emoji", stickers: ["st_101", "st_102", "st_103"]),
StickerPack(id: "hearts", name: "Hearts", stickers: ["st_201", "st_202"]),
]
dock.stickerPacks = listOf(
StickerPack("emoji", "Emoji", listOf("st_101", "st_102", "st_103")),
StickerPack("hearts", "Hearts", listOf("st_201", "st_202")),
)
<NitrakitEditor
editor={editor}
stickerPacks={[
{ id: 'emoji', name: 'Emoji', stickers: ['st_101', 'st_102', 'st_103'] },
{ id: 'hearts', name: 'Hearts', stickers: ['st_201', 'st_202'] },
]}
/>
editor.setStickerPacks([
{ id: 'emoji', name: 'Emoji', stickers: ['st_101', 'st_102', 'st_103'] },
{ id: 'hearts', name: 'Hearts', stickers: ['st_201', 'st_202'] },
])
Or pass stickerPacks to createEditor.
Without packs, Sticker sends your app an add sticker request instead, answered like add media.