The @imgly/camera-flutter
Flutter module is built on top of the native Android and iOS UI implementation and has no dedicated Flutter UI.
However, you still have access to the same customization options as for iOS and an interface for Android.
To use them you need to configure them natively for both platforms.
Further, the most important settings can be set from Flutter directly.
Flutter Customization#
From within Flutter directly, you can modify the most important camera behavior settings.
CameraSettings#
All the basic configuration settings are part of the EngineSettings
which are required to initialize the camera.
license
– the license to activate the Engine with.userID
– an optional unique ID tied to your application’s user. This helps us accurately calculate monthly active users (MAU). Especially useful when one person uses the app on multiple devices with a sign-in feature, ensuring they’re counted once. Providing this aids in better data accuracy. The default value isnil
.
final settings = CameraSettings(license: "YOUR_LICENSE", userId: "YOUR_USER_ID");
Reactions#
You can optionally provide a video
parameter which lets the user react to that video.
final _ = await IMGLYCamera.openCamera( settings, // Optional, if you want to react to a video video: 'https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_2mb.mp4',)
Native Customization#
In order to fully utilize the natively available customizations, the module comes with native interfaces for both iOS and Android.
iOS#
The customization layer for iOS is written in Swift and available via the IMGLYCameraModuleSwiftAdapter.shared
instance.
The camera that is opened via the IMGLYCameraModuleSwiftAdapter.openCamera()
function can be completely customized and exchanged.
For this to work you can use the IMGLYCameraModuleSwiftAdapter.builderClosure
which provides optional metadata
with which you can provide any prebuilt or custom camera view. The metadata
parameter of the openCamera
function can be utilized
to provide customization details from the React Native side of your app to the native side:
You can either use the CameraBuilder.default
implementation or in case you want a completely custom UI, you can use the CameraBuilder.custom
function that allows you to return a custom View
based on a given CameraSettings
, URL
, metadata
and an CameraBuilderResult
:
// Configure the [CaptureVideo.Input]. IMGLYCameraPlugin.configurationClosure = { /* * This closure requires returning a [CaptureVideo.Input]. * * You can access the following parameters: * - "cameraSettings": The CameraSettings passed from the Flutter side. * - "engineConfiguration": The EngineConfiguration that includes your license. * - "metadata": Metadata passed from the Flutter side. * - "videoUri": The video reaction URL passed from Flutter, or null. */
if (metadata["is_reactions"] == true) { CaptureVideo.Input( engineConfiguration = engineConfiguration, cameraMode = CameraMode.Reaction( videoUri ?: throw RuntimeException("Missing video URL."), cameraLayoutMode = CameraLayoutMode.Horizontal, positionsSwapped = false, ), ) } else { // Simply return null to use the default camera configuration. null } }
// Adding custom metadata to the CameraResult. IMGLYCameraPlugin.resultClosure = { /* * This closure allows you to add custom metadata of type [Map<String, Any?>] to the CameraResult. * You can access the following values: * - "cameraResult": The native CameraResult returned by the camera. * - "inputMetadata": Metadata passed from the Flutter side when the camera was opened. * * The return value must be a [Map<String, Any?>], which will be passed to the Dart-side * representation of the CameraResult. * * Notes: * - Use `mapOf<String, Any?>()` to return key-value pairs. * - Return `null` if no metadata should be added. */
// Example: Read the result data... val recordings = when (val result = cameraResult) { is CameraResult.Record -> result.recordings is CameraResult.Reaction -> result.reaction else -> emptyList() }
// Return a custom metadata map mapOf( "MY_CUSTOM_KEY" to "MY_CUSTOM_VALUE", ) }
Android#
On Android, the module comes with two interfaces for modifications:
CameraInputClosure#
The CameraInputClosure
can be used to provide a custom CaptureVideo.Input
based on given metadata
.
It is accessible via the IMGLYCameraModule.configurationClosure
.
// Configure the [CaptureVideo.Input]. IMGLYCameraPlugin.configurationClosure = { /* * This closure requires returning a [CaptureVideo.Input]. * * You can access the following parameters: * - "cameraSettings": The CameraSettings passed from the Flutter side. * - "engineConfiguration": The EngineConfiguration that includes your license. * - "metadata": Metadata passed from the Flutter side. * - "videoUri": The video reaction URL passed from Flutter, or null. */
if (metadata["is_reactions"] == true) { CaptureVideo.Input( engineConfiguration = engineConfiguration, cameraMode = CameraMode.Reaction( videoUri ?: throw RuntimeException("Missing video URL."), cameraLayoutMode = CameraLayoutMode.Horizontal, positionsSwapped = false, ), ) } else { // Simply return null to use the default camera configuration. null } }
CameraResultClosure#
The CameraResultClosure
can be used to return a custom CameraResult
based on the result returned by the camera.
It is accessible via the IMGLYCameraModule.resultClosure
.
// Adding custom metadata to the CameraResult. IMGLYCameraPlugin.resultClosure = { /* * This closure allows you to add custom metadata of type [Map<String, Any?>] to the CameraResult. * You can access the following values: * - "cameraResult": The native CameraResult returned by the camera. * - "inputMetadata": Metadata passed from the Flutter side when the camera was opened. * * The return value must be a [Map<String, Any?>], which will be passed to the Dart-side * representation of the CameraResult. * * Notes: * - Use `mapOf<String, Any?>()` to return key-value pairs. * - Return `null` if no metadata should be added. */
// Example: Read the result data... val recordings = when (val result = cameraResult) { is CameraResult.Record -> result.recordings is CameraResult.Reaction -> result.reaction else -> emptyList() }
// Return a custom metadata map mapOf( "MY_CUSTOM_KEY" to "MY_CUSTOM_VALUE", ) }
Full Code#
Here’s the code for all three files:
camera_flutter.dart#
import "package:imgly_camera/imgly_camera.dart";
class CameraFlutter { /// Opens the camera. void openCamera() async { final settings = CameraSettings(license: "YOUR_LICENSE", userId: "YOUR_USER_ID");
final _ = await IMGLYCamera.openCamera( settings, // Optional, if you want to react to a video video: 'https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_2mb.mp4', ) }}
ConfigurationGuide.swift#
import imgly_cameraimport imgly_editorimport SwiftUI
func useCustomEditor() { IMGLYEditorPlugin.builderClosure = { _, metadata in // Make decisions based on your own metadata. if metadata?["use_custom_editor"] as? Bool == true { // Return your custom editor. EditorBuilder.custom { settings, _, _, result in CustomEditor(settings: settings, result: result) } } else { // Return a custom or prebuilt editor. EditorBuilder.design() } }}
private struct CustomEditor: View { init(settings _: EditorSettings, result _: @escaping EditorBuilder.EditorBuilderResult) {}
var body: some View { Text("Custom Editor") }}
func configureCamera() { IMGLYCameraPlugin.builderClosure = { metadata in // Make decisions based on your own metadata. if metadata?["use_custom_camera"] as? Bool == true { // Return your custom camera. CameraBuilder.custom { settings, url, metadata, result in CustomCamera(settings: settings, url: url, metadata: metadata, result: result) } } else { // Return a custom or prebuilt camera. CameraBuilder.default() } }}
private struct CustomCamera: View { init( settings _: CameraSettings, url _: URL?, metadata _: [String: Any]?, result _: @escaping CameraBuilder.CameraBuilderResult ) {}
var body: some View { Text("Custom Camera") }}
ConfigurationGuide.kt#
package ly.img.editor.flutter.showcases.guides
import androidx.compose.runtime.Composableimport ly.img.camera.core.CameraLayoutModeimport ly.img.camera.core.CameraModeimport ly.img.camera.core.CameraResultimport ly.img.camera.core.CaptureVideoimport ly.img.camera.flutter.plugin.IMGLYCameraPluginimport ly.img.editor.flutter.plugin.IMGLYEditorPluginimport ly.img.editor.flutter.plugin.builder.EditorBuilderimport ly.img.editor.flutter.plugin.builder.EditorBuilderResultimport ly.img.editor.flutter.plugin.model.EditorSettings
private fun useCustomEditor() { IMGLYEditorPlugin.builderClosure = { _, metadata -> if (metadata?.get("custom") == true) { EditorBuilder.custom { settings, _, _, result, onClose -> @Composable { CustomEditor(settings, result, onClose) } } } else { EditorBuilder.design() } }}
private fun customizeCamera() { // Configure the [CaptureVideo.Input]. IMGLYCameraPlugin.configurationClosure = { /* * This closure requires returning a [CaptureVideo.Input]. * * You can access the following parameters: * - "cameraSettings": The CameraSettings passed from the Flutter side. * - "engineConfiguration": The EngineConfiguration that includes your license. * - "metadata": Metadata passed from the Flutter side. * - "videoUri": The video reaction URL passed from Flutter, or null. */
if (metadata["is_reactions"] == true) { CaptureVideo.Input( engineConfiguration = engineConfiguration, cameraMode = CameraMode.Reaction( videoUri ?: throw RuntimeException("Missing video URL."), cameraLayoutMode = CameraLayoutMode.Horizontal, positionsSwapped = false, ), ) } else { // Simply return null to use the default camera configuration. null } }
// Adding custom metadata to the CameraResult. IMGLYCameraPlugin.resultClosure = { /* * This closure allows you to add custom metadata of type [Map<String, Any?>] to the CameraResult. * You can access the following values: * - "cameraResult": The native CameraResult returned by the camera. * - "inputMetadata": Metadata passed from the Flutter side when the camera was opened. * * The return value must be a [Map<String, Any?>], which will be passed to the Dart-side * representation of the CameraResult. * * Notes: * - Use `mapOf<String, Any?>()` to return key-value pairs. * - Return `null` if no metadata should be added. */
// Example: Read the result data... val recordings = when (val result = cameraResult) { is CameraResult.Record -> result.recordings is CameraResult.Reaction -> result.reaction else -> emptyList() }
// Return a custom metadata map mapOf( "MY_CUSTOM_KEY" to "MY_CUSTOM_VALUE", ) }}
@Composableprivate fun CustomEditor( settings: EditorSettings, result: EditorBuilderResult, onClose: (Throwable?) -> Unit,) {}