Search Docs
Loading...
Skip to content

Force Crop

Use the Android SDK’s force crop flow by preparing (or isolating) the preset in an asset source and dispatching EditorEvent.ApplyForceCrop from your editor lifecycle, typically in onCreate or onLoaded.

3 mins
estimated time
GitHub

Overview#

Force cropping ensures that a page (or any cropable block) uses a specific aspect ratio or fixed size when the editor loads. Use the EditorEvent.ApplyForceCrop event to apply force crop by providing the target design block as well as the ForceCropConfiguration that should be applied.


ForceCropConfiguration Parameters#

NameTypeDescription
sourceIdStringAsset source that stores the forced preset.
presetIdStringPrimary preset ID that should be applied.
presetCandidatesList<ForceCropPresetCandidate>Additional presets the helper can fall back to. Omit this if you only want to use a single preset.
modeForceCropModeControls whether the crop sheet opens (Always / IfNeeded) or stays silent (Silent).

ForceCropConfiguration supports optional fallback candidates so you can reuse the same block-level heuristics on Android.


Modes#

ModeBehavior
SilentApplies the preset without opening the crop UI.
AlwaysApplies the preset and opens the crop sheet immediately.
IfNeededCompares the preset with the current frame dimensions. The crop is applied and the sheet opens only if the ratio/size differs materially.

ForceCropMode.IfNeeded accepts an optional threshold to tweak the tolerance for aspect-ratio comparisons.


Usage Example#

The snippet below shows how to create a configuration object:

val configuration = ForceCropConfiguration(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-1-1",
presetCandidates = listOf(
ForceCropPresetCandidate(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-4-3",
),
ForceCropPresetCandidate(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-3-4",
),
),
mode = ForceCropMode.Silent,
)

After the onCreate flow is complete, we apply the force crop configuration to the first page in onLoaded:

editorContext.eventHandler.send(
event = EditorEvent.ApplyForceCrop(
designBlock = page,
configuration = configuration,
),
)

Isolating the Preset#

In this example we isolate the available presets by loading a dedicated asset file in onCreate:

editorContext.engine.populateAssetSource(
id = cropPresetsSourceId,
jsonUri = "file:///android_asset/force_crop_content.json".toUri(),
replaceBaseUri = editorContext.baseUri,
)

For this guide, the force_crop_content.json asset intentionally exposes only three presets:

  • 1:1 as the enforced force-crop preset.
  • 4:3 and 3:4 as fallback candidates.

This keeps the crop options focused on the exact ratios used by ForceCropConfiguration.

{
"version": "4.0.0",
"id": "ly.img.crop.presets",
"assets": [
{
"id": "aspect-ratio-1-1",
"label": {
"en": "1:1",
"de": "1:1"
},
"meta": {
"thumbUri": "{{base_url}}/ly.img.crop.presets/thumbnails/ratio-1-1.png"
},
"payload": {
"transformPreset": {
"type": "FixedAspectRatio",
"width": 1,
"height": 1
}
},
"groups": ["fixed-ratio"]
},
{
"id": "aspect-ratio-3-4",
"label": {
"en": "3:4",
"de": "3:4"
},
"meta": {
"thumbUri": "{{base_url}}/ly.img.crop.presets/thumbnails/ratio-3-4.png"
},
"payload": {
"transformPreset": {
"type": "FixedAspectRatio",
"width": 3,
"height": 4
}
},
"groups": ["fixed-ratio"]
},
{
"id": "aspect-ratio-4-3",
"label": {
"en": "4:3",
"de": "4:3"
},
"meta": {
"thumbUri": "{{base_url}}/ly.img.crop.presets/thumbnails/ratio-4-3.png"
},
"payload": {
"transformPreset": {
"type": "FixedAspectRatio",
"width": 4,
"height": 3
}
},
"groups": ["fixed-ratio"]
}
]
}

Full Code#

For demonstration purposes, this full sample adds the Crop button to the dock so you can inspect the configured presets manually. It applies force crop with ForceCropMode.Silent in onLoaded, so the crop sheet is not shown automatically on initial load.

In addition, it prepares a scene from a remote image in onCreate, loads ly.img.crop.presets from force_crop_content.json.

Here’s the full code:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.runtime.Composable
import androidx.core.net.toUri
import ly.img.editor.Editor
import ly.img.editor.core.component.Dock
import ly.img.editor.core.component.data.ForceCropConfiguration
import ly.img.editor.core.component.data.ForceCropMode
import ly.img.editor.core.component.data.ForceCropPresetCandidate
import ly.img.editor.core.component.remember
import ly.img.editor.core.component.rememberCrop
import ly.img.editor.core.configuration.EditorConfiguration
import ly.img.editor.core.configuration.remember
import ly.img.editor.core.event.EditorEvent
import ly.img.engine.populateAssetSource
// Add this composable to your NavHost
@Composable
fun ForceCropEditorSolution(
license: String,
onClose: (Throwable?) -> Unit,
) {
Editor(
license = license, // pass null or empty for evaluation mode with watermark
configuration = {
val cropPresetsSourceId = "ly.img.crop.presets"
EditorConfiguration.remember {
onCreate = {
editorContext.engine.scene.createFromImage(
imageUri = "https://img.ly/static/ubq_samples/sample_4.jpg".toUri(),
)
editorContext.engine.populateAssetSource(
id = cropPresetsSourceId,
jsonUri = "file:///android_asset/force_crop_content.json".toUri(),
replaceBaseUri = editorContext.baseUri,
)
}
onLoaded = {
val page = requireNotNull(editorContext.engine.scene.getCurrentPage())
val configuration = ForceCropConfiguration(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-1-1",
presetCandidates = listOf(
ForceCropPresetCandidate(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-4-3",
),
ForceCropPresetCandidate(
sourceId = cropPresetsSourceId,
presetId = "aspect-ratio-3-4",
),
),
mode = ForceCropMode.Silent,
)
editorContext.eventHandler.send(
event = EditorEvent.ApplyForceCrop(
designBlock = page,
configuration = configuration,
),
)
}
dock = {
Dock.remember {
horizontalArrangement = { Arrangement.Center }
listBuilder = {
Dock.ListBuilder.remember {
add { Dock.Button.rememberCrop() }
}
}
}
}
}
},
onClose = onClose,
)
}

Behavior Details#

  • The helper locks page resizing while the crop sheet is open and restores the previous setting when finished.
  • Fixed-size presets adjust the scene size and trigger a zoom recalculation automatically.
  • All preset candidates are fetched on the main engine thread. Missing sources or IDs are logged and ignored.
  • Matching logic mirrors the iOS implementation: the helper picks the preset whose ratio/size best fits the current frame, then applies it according to the selected mode.

See Also#