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.
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#
| Name | Type | Description |
|---|---|---|
sourceId | String | Asset source that stores the forced preset. |
presetId | String | Primary preset ID that should be applied. |
presetCandidates | List<ForceCropPresetCandidate> | Additional presets the helper can fall back to. Omit this if you only want to use a single preset. |
mode | ForceCropMode | Controls 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#
| Mode | Behavior |
|---|---|
Silent | Applies the preset without opening the crop UI. |
Always | Applies the preset and opens the crop sheet immediately. |
IfNeeded | Compares 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:1as the enforced force-crop preset.4:3and3:4as 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.Arrangementimport androidx.compose.runtime.Composableimport androidx.core.net.toUriimport ly.img.editor.Editorimport ly.img.editor.core.component.Dockimport ly.img.editor.core.component.data.ForceCropConfigurationimport ly.img.editor.core.component.data.ForceCropModeimport ly.img.editor.core.component.data.ForceCropPresetCandidateimport ly.img.editor.core.component.rememberimport ly.img.editor.core.component.rememberCropimport ly.img.editor.core.configuration.EditorConfigurationimport ly.img.editor.core.configuration.rememberimport ly.img.editor.core.event.EditorEventimport ly.img.engine.populateAssetSource
// Add this composable to your NavHost@Composablefun 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.