Flutter Video Editor in 6 Steps: Banuba SDK Setup, Entry Screens and Export
A Flutter video editor built on Banuba's ve_sdk_flutter plugin is one Dart call that opens a native recording and editing screen and returns the exported file paths to your code. The setup is six steps: pin the package, configure iOS, configure Android, launch the editor, pick an entry screen, and set the export.
Banuba Video Editor SDK is a production-ready mobile video editing engine for iOS, Android, Flutter and React Native. We build it, so this guide uses our plugin, and the comparison near the end also covers the open-source Flutter packages we do not sell.
What do you need before you add a video editor to Flutter?
You need four things:
- A license token. Banuba issues it with a 14-day free trial of the Video Editor SDK, and the plugin's pub.dev page says no credit card information is needed. Without a token, every call fails with an error code you will meet in Step 4.
- A Flutter toolchain at or above the plugin's floor. The pub.dev page lists Dart 3.3.0, Flutter 3.19.2, Android 8.0+ and iOS 15.0+.
- Native tools for both platforms. Our requirements pages list Kotlin 2.1+ or Java 17, the Camera 2 API and OpenGL ES 3.0 on Android, and Swift 5.9, Xcode 26.0 and iOS 15.0 on Apple devices.
- Physical devices. Our Android requirements mark x86-64 as limited support and x86 as not supported, so an emulator image is a weak place to judge the editor.
Step 1: How do you install the Flutter video editor plugin?
Add the package with the version pinned:
dependencies:
ve_sdk_flutter: 0.48.1
Then run flutter pub get. The ve_sdk_flutter package depends only on flutter and plugin_platform_interface, so it does not pull a media library into your Dart dependency tree. The media engine arrives through CocoaPods and Gradle in the next two steps.
Pin the exact version on purpose, and read the changelog rather than the version number. Some releases track native SDK versions: 0.43.0 supports Video Editor 1.52.0 and 0.44.0 supports 1.53.0, so a minor bump can change native behavior under the same Dart code. Others touch nothing functional at all, which is what 0.48.0 and 0.48.1 did, updating the package description, repository link, topics and homepage.
Step 2: What does iOS need?
Two edits. First, add Banuba's spec repositories at the top of ios/Podfile, as the plugin integration guide shows:
platform :ios, '15.0'
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/Banuba/specs.git'
source 'https://github.com/sdk-banuba/banuba-sdk-podspecs.git'
Second, add the four usage descriptions to ios/Runner/Info.plist:
<key>NSAppleMusicUsageDescription</key>
<string>This app requires access to the media library</string>
<key>NSCameraUsageDescription</key>
<string>This app requires access to the camera.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires access to the microphone.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library.</string>
Run pod install inside ios, then select your Development Team under Signing & Capabilities. Replace the sample strings with wording that tells your users why you need each permission.
Step 3: What does Android need, and how do you keep the APK small?
On Android, the size decision is the main one. Our documentation gives these additions to your app's download size, and we did not re-measure them for this article:
| What you ship | Android | iOS |
| :-- | :-- | :-- |
| Core editor | 25 MB | 14 MB |
| Full bundle | 45 MB | 45 MB |
| Editor with face AR features | 45 to 90 MB | 45 to 140 MB |
Two switches move you between those rows.
Turn off face AR if you do not need masks. Add ENABLE_FACE_AR=false to android/gradle.properties, and this line to the Podfile:
ENV['ENABLE_FACE_AR'] = 'false'
Filter processor architectures. The integration guide notes that each architecture adds megabytes. Ship ARM only:
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
Upload an Android App Bundle as well. Our Android docs state that with a bundle, the per-device library comes out 24 to 26 MB smaller than the documented figure, because Google Play serves each device only its own architecture.
Step 4: How do you open the video editor from Dart?
This is a complete lib/main.dart. One button opens the camera screen, and the result or the error comes back to the same widget:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ve_sdk_flutter/errors.dart';
import 'package:ve_sdk_flutter/export_data.dart';
import 'package:ve_sdk_flutter/export_result.dart';
import 'package:ve_sdk_flutter/features_config.dart';
import 'package:ve_sdk_flutter/ve_sdk_flutter.dart';
// Passed at build time: flutter run --dart-define=BANUBA_TOKEN=your_token
const _licenseToken = String.fromEnvironment('BANUBA_TOKEN');
void main() => runApp(const MaterialApp(home: EditorLauncher()));
class EditorLauncher extends StatefulWidget {
const EditorLauncher({super.key});
@override
State<EditorLauncher> createState() => _EditorLauncherState();
}
class _EditorLauncherState extends State<EditorLauncher> {
final _veSdk = VeSdkFlutter();
String _status = 'No clip yet';
// Step 6: a 720p file in H.264 plus a file at device-chosen quality
static const _exportData = ExportData(exportedVideos: [
ExportedVideo(
fileName: 'clip_720p',
videoResolution: VideoResolution.hd720p,
useHevcIfPossible: false,
),
ExportedVideo(
fileName: 'clip_auto',
videoResolution: VideoResolution.auto,
),
]);
Future<void> _openEditor() async {
final config = FeaturesConfigBuilder().build();
try {
final ExportResult? result = await _veSdk.openCameraScreen(
_licenseToken,
config,
exportData: _exportData,
);
if (!mounted) return;
setState(() {
_status = result == null
? 'Editor closed before export'
: 'Exported: ${result.videoSources.join(', ')}';
});
} on PlatformException catch (e) {
if (!mounted) return;
setState(() => _status = _describe(e));
}
}
String _describe(PlatformException e) {
switch (e.code) {
case errCodeSdkNotInitialized:
return 'License token is empty or invalid';
case errCodeSdkLicenseRevoked:
return 'License revoked or expired';
case errCodeMissingHost:
return 'No Activity or ViewController to show the editor';
case errCodeInvalidParams:
return 'The editor received invalid input';
case errCodeMissingExportResult:
return 'The editor could not export the video';
default:
return 'Error ${e.code}: ${e.message}';
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(_status),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _openEditor,
child: const Text('Open video editor'),
),
],
),
),
);
}
}
openCameraScreen returns Future<ExportResult?>. A null result means the user closed the editor without exporting, so a user who backs out halfway still leaves your screen with a defined status.
The plugin defines five error codes in errors.dart. The three below are the ones that stop a first run, and a fourth failure mode after them throws nothing at all.
ERR_SDK_NOT_INITIALIZED
The plugin's comment on this code reads: "Error occurs when the sdk cannot be initialized with provided license token: invalid, empty". With the snippet above, it usually means the app was started without --dart-define=BANUBA_TOKEN=..., so the constant is an empty string.
ERR_SDK_LICENSE_REVOKED
The license has been revoked, and the plugin's own comment says to contact Banuba. In the integration sample, the same code also covers an expired license, which is what a finished 14-day trial looks like from Dart.
ERR_MISSING_HOST
The editor is a native screen, so it needs an Activity on Android or a ViewController on iOS to present from. This code means none was available, which points at calling the plugin before the first frame has rendered, for example from main() or initState().
"New UI is not available from Trimmer screen"
This one is not an error code, and that is what makes it expensive to debug. FeaturesConfigBuilder sets enableEditorV2 to true by default, and with the new UI enabled the plugin refuses the trimmer entry point: it writes that line to the debug console and returns null without throwing.
From the snippet above, the only thing you see is "Editor closed before export", as though the user had backed out. If you want openTrimmerScreen, turn the new UI off for that call:
final config = FeaturesConfigBuilder()
.enableEditorV2(false)
.build();
Step 5: Which entry screen should the editor open with?
VeSdkFlutter in 0.48.1 exposes eight entry points. Each takes the token and a FeaturesConfig, and each returns the same ExportResult:
| Method | Extra input | Starts the user on |
| :-- | :-- | :-- |
| openCameraScreen | none | Recording |
| openPipScreen | one video path | Picture-in-picture over an existing clip |
| openTrimmerScreen | list of video paths | Trimming the clips you pass |
| openEditorScreen | list of video paths | The editing timeline |
| openAiClippingScreen | none | AI clipping, starting from the gallery |
| openTemplatesScreen | none | Video templates |
| openDraftsScreen | none | Saved drafts |
| openGalleryScreen | none | The media gallery |
openPipScreen takes one video path on top of the token and config, and openTrimmerScreen and openEditorScreen take a list of paths, which you get from a picker such as image_picker. The other five need nothing else.
Two things in the 0.48.1 source are worth knowing before you build on this table. openTrimmerScreen is the one that returns null under the default config, as described above. And openEditorScreen accepts an optional trackData argument that goes nowhere: the plugin serializes track data and sends it to the native side from its internal open method, but the editor entry point does not forward the argument to it. Passing a track there is a silent no-op today, so preload audio another way until a release says otherwise.
Each screen is trimmed through FeaturesConfigBuilder. Setting supportsBeauty and supportsMasks to false in CameraConfig removes the beauty and mask controls from the camera, and supportsVoiceOver: false in EditorConfig removes voice-over, which our docs mark as Android only.
Step 6: How do you control the exported file?
Without ExportData, the plugin exports one video at auto quality, which our docs describe as chosen from the device's hardware. With it, you name each file and pick a VideoResolution. The enum has nine values: seven resolutions from vga360p through vga480p, qhd540p, hd720p, fhd1080p and qhd1440p to uhd2160p, plus auto and original.
The file type differs by platform. Our docs state that the video is exported as .mp4 on Android and .mov on iOS, so do not hard-code an extension when you upload.
Three settings change what lands on disk:
1. useHevcIfPossible: false switches the file to H.264 (AVC), for upload or playback pipelines that expect AVC.
2. Watermark(imagePath: 'assets/watermark.png', alignment: WatermarkAlignment.bottomRight) adds your logo. Our docs state that no watermark is added by default.
3. Each ExportedVideo entry produces its own file, so two entries mean two encodes on the user's phone.
The result carries more than video paths. ExportResult also returns previewFilePath, metaFilePath and audioMeta, and the plugin's example describes the meta file as short data used in the exported video. To play the exported file inside your own screen, the official video_player plugin is enough.
Encoding time depends on the phone, so measure the export on the slowest Android model you support. Our video editor SDK performance benchmark shows how results spread across devices.
Which Flutter video editor route fits your app?
Video editing in Flutter has four realistic routes, and each is the right call for some team.
| Question | Banuba plugin (ve_sdk_flutter 0.48.1) | Banuba integration sample | video_editor 3.0.0 | ffmpeg_kit_flutter |
| :-- | :-- | :-- | :-- | :-- |
| What it is | Ready editor UI behind one Dart call | Full app source using a platform channel | Dart widgets for trim, crop, rotate and cover | FFmpeg and FFprobe command runner |
| Editing UI | Included, native screens | Included, editable native code | You assemble it from widgets | None |
| Export | Included, mp4 or mov | Included | Builds FFmpeg commands, does not export | Any FFmpeg command you write |
| Access | Trial token on request | Trial token on request | Open | Open |
| License | Commercial | Commercial | MIT | LGPL 3.0 by default, some packages GPL v3.0 |
| Maintenance state | Released against current native SDK versions | Tracks the plugin | Latest release is 3.0.0, no release since | Upstream FFmpegKit retired; successor and community forks exist |
Four ways to read that table:
- Fastest to a complete editor: the Banuba plugin. Recording, timeline, effects and export arrive behind the call in Step 4.
- Most control over native behavior: the Banuba integration sample. The Flutter integration sample on GitHub ships the app source, so your team can change the native layer rather than wait for a plugin release.
- Cost and UI ownership: video_editor. It is free under MIT and every widget is yours to style. You still need an export path, because its README says the library does not handle exportation.
- Raw codec control: the FFmpeg route. No other option lets you write arbitrary FFmpeg commands, and the licensing is the thing to settle first, since the default build is LGPL 3.0 and some variants are GPL v3.0.
The FFmpeg route needs one more sentence, because the package most guides still point at is not where the work happens now. The upstream project posted a retirement notice in July 2026 stating that FFmpegKit has been officially retired, and names FFmpegKitNext as the actively maintained continuation, distributed as source only, alongside community-maintained forks published for Android, Flutter and React Native through the usual package managers. A team going this way is picking a fork and a build process, not just adding a dependency.
One more limit of our own: the Banuba editor does not run on the web, so a Flutter web target needs a different route for that platform.
Why use Banuba for a Flutter video editor?
Banuba Video Editor SDK covers recording, a multi-track timeline, effects, AI clipping, AI subtitles and export in one plugin. Our docs state that it works offline and does not send, store or process user information. They also publish agent skills for Claude Code, Codex and Qwen Code, plus an LLM-friendly documentation file for AI-assisted coding.
It does not hand you Dart widgets to restyle line by line. The screens are native, and you shape them through FeaturesConfig, so a team that wants a pixel-level custom UI in Flutter is better served by video_editor plus its own export path.
For the native side of the same editor, see our walkthrough of integrating the iOS video editor SDK in Swift. For other vendors, our comparison of five video editor SDKs for social apps puts Banuba next to the alternatives, and the original Flutter guide on our blog covers the feature list in more depth.
Six steps take a Flutter app from pubspec to an exported clip
Pin ve_sdk_flutter at 0.48.1, add the Podfile sources and permissions, set the Android size switches, and call one open...Screen method from a mounted widget. Then decide the export: which resolutions, which codec, and whether a watermark goes on.
Check the size table in Step 3 and the four failure modes in Step 4 before the first device run. Three of them throw, and the fourth returns null and looks like a user walking away.
FAQ
Is the Banuba Flutter video editor free?
The Banuba Flutter video editor is free for a 14-day trial with the full feature set, and the plugin's pub.dev page says no credit card information is needed. Production use needs a commercial Banuba license.
Does the Banuba Flutter video editor work offline?
Yes. Banuba's documentation states that the Video Editor SDK works offline and does not send, store or process user information.
Does the Banuba Video Editor SDK support Flutter web?
No. The Banuba Video Editor SDK runs on iOS and Android, and pub.dev lists only those two platforms for ve_sdk_flutter.
How do I fix ERR_SDK_NOT_INITIALIZED in the Banuba Flutter plugin?
The Banuba plugin returns ERR_SDK_NOT_INITIALIZED when the license token is empty or invalid. Check that the token reaches the call, for example that you passed --dart-define=BANUBA_TOKEN=... when you started the app.
Why does openTrimmerScreen return null in the Banuba Flutter plugin?
Because the new editor UI is on by default. FeaturesConfigBuilder sets enableEditorV2 to true, and the plugin blocks the trimmer entry point under it, logging "New UI is not available from Trimmer screen" and returning null rather than throwing. Call .enableEditorV2(false) on the builder if you need that screen.
How much does the Banuba video editor add to a Flutter app's size?
Banuba documents 25 MB on Android and 14 MB on iOS for the core editor, 45 MB for the full bundle, and 45 to 90 MB on Android or 45 to 140 MB on iOS with face AR. Turning face AR off and filtering architectures moves a build toward the core figures.
Mentions
Flutter Video Editor in 6 Steps: Banuba SDK Setup, Entry Screens and Export A Flutter video editor built on Banuba's ve_sdk_flutter plugin is one Dart call that opens a native recording and editing screen and returns the exported file paths to your code. The setup is six steps: pin the package, configure iOS, configure Android, launch the editor, pick an entry screen, and set the export. Banuba Video Editor SDK is a production-ready mobile video editing engine for iOS, Android, Flutter and React Native. We build it, so this guide uses our plugin, and the comparison near the end also covers the open-source Flutter packages we do not sell. What do you need before you add a video editor to Flutter? You need four things: - A license token. Banuba issues it with a 14-day free trial of the Video Editor SDK, and the plugin's pub.dev page says no credit card information is needed. Without a token, every call fails with an error code you will meet in Step 4. - A Flutter toolchain at or above the plugin's floor. The pub.dev page lists Dart 3.3.0, Flutter 3.19.2, Android 8.0+ and iOS 15.0+. - Native tools for both platforms. Our requirements pages list Kotlin 2.1+ or Java 17, the Camera 2 API and OpenGL ES 3.0 on Android, and Swift 5.9, Xcode 26.0 and iOS 15.0 on Apple devices. - Physical devices. Our Android requirements mark x86-64 as limited support and x86 as not supported, so an emulator image is a weak place to judge the editor. Step 1: How do you install the Flutter video editor plugin? Add the package with the version pinned: dependencies: ve_sdk_flutter: 0.48.1 Then run flutter pub get. The ve_sdk_flutter package depends only on flutter and plugin_platform_interface, so it does not pull a media library into your Dart dependency tree. The media engine arrives through CocoaPods and Gradle in the next two steps. Pin the exact version on purpose, and read the changelog rather than the version number. Some releases track native SDK versions: 0.43.0 supports Video Editor 1.52.0 and 0.44.0 supports 1.53.0, so a minor bump can change native behavior under the same Dart code. Others touch nothing functional at all, which is what 0.48.0 and 0.48.1 did, updating the package description, repository link, topics and homepage. Step 2: What does iOS need? Two edits. First, add Banuba's spec repositories at the top of ios/Podfile, as the plugin integration guide shows: platform :ios, '15.0' source 'https://github.com/CocoaPods/Specs.git' source 'https://github.com/Banuba/specs.git' source 'https://github.com/sdk-banuba/banuba-sdk-podspecs.git' Second, add the four usage descriptions to ios/Runner/Info.plist: <key>NSAppleMusicUsageDescription</key> <string>This app requires access to the media library</string> <key>NSCameraUsageDescription</key> <string>This app requires access to the camera.</string> <key>NSMicrophoneUsageDescription</key> <string>This app requires access to the microphone.</string> <key>NSPhotoLibraryUsageDescription</key> <string>This app requires access to the photo library.</string> Run pod install inside ios, then select your Development Team under Signing & Capabilities. Replace the sample strings with wording that tells your users why you need each permission. Step 3: What does Android need, and how do you keep the APK small? On Android, the size decision is the main one. Our documentation gives these additions to your app's download size, and we did not re-measure them for this article: | What you ship | Android | iOS | | :-- | :-- | :-- | | Core editor | 25 MB | 14 MB | | Full bundle | 45 MB | 45 MB | | Editor with face AR features | 45 to 90 MB | 45 to 140 MB | Two switches move you between those rows. Turn off face AR if you do not need masks. Add ENABLE_FACE_AR=false to android/gradle.properties, and this line to the Podfile: ENV['ENABLE_FACE_AR'] = 'false' Filter processor architectures. The integration guide notes that each architecture adds megabytes. Ship ARM only: ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' } Upload an Android App Bundle as well. Our Android docs state that with a bundle, the per-device library comes out 24 to 26 MB smaller than the documented figure, because Google Play serves each device only its own architecture. Step 4: How do you open the video editor from Dart? This is a complete lib/main.dart. One button opens the camera screen, and the result or the error comes back to the same widget: import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:ve_sdk_flutter/errors.dart'; import 'package:ve_sdk_flutter/export_data.dart'; import 'package:ve_sdk_flutter/export_result.dart'; import 'package:ve_sdk_flutter/features_config.dart'; import 'package:ve_sdk_flutter/ve_sdk_flutter.dart'; // Passed at build time: flutter run --dart-define=BANUBA_TOKEN=your_token const _licenseToken = String.fromEnvironment('BANUBA_TOKEN'); void main() => runApp(const MaterialApp(home: EditorLauncher())); class EditorLauncher extends StatefulWidget { const EditorLauncher({super.key}); @override State<EditorLauncher> createState() => _EditorLauncherState(); } class _EditorLauncherState extends State<EditorLauncher> { final _veSdk = VeSdkFlutter(); String _status = 'No clip yet'; // Step 6: a 720p file in H.264 plus a file at device-chosen quality static const _exportData = ExportData(exportedVideos: [ ExportedVideo( fileName: 'clip_720p', videoResolution: VideoResolution.hd720p, useHevcIfPossible: false, ), ExportedVideo( fileName: 'clip_auto', videoResolution: VideoResolution.auto, ), ]); Future<void> _openEditor() async { final config = FeaturesConfigBuilder().build(); try { final ExportResult? result = await _veSdk.openCameraScreen( _licenseToken, config, exportData: _exportData, ); if (!mounted) return; setState(() { _status = result == null ? 'Editor closed before export' : 'Exported: ${result.videoSources.join(', ')}'; }); } on PlatformException catch (e) { if (!mounted) return; setState(() => _status = _describe(e)); } } String _describe(PlatformException e) { switch (e.code) { case errCodeSdkNotInitialized: return 'License token is empty or invalid'; case errCodeSdkLicenseRevoked: return 'License revoked or expired'; case errCodeMissingHost: return 'No Activity or ViewController to show the editor'; case errCodeInvalidParams: return 'The editor received invalid input'; case errCodeMissingExportResult: return 'The editor could not export the video'; default: return 'Error ${e.code}: ${e.message}'; } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(_status), const SizedBox(height: 16), ElevatedButton( onPressed: _openEditor, child: const Text('Open video editor'), ), ], ), ), ); } } openCameraScreen returns Future<ExportResult?>. A null result means the user closed the editor without exporting, so a user who backs out halfway still leaves your screen with a defined status. The plugin defines five error codes in errors.dart. The three below are the ones that stop a first run, and a fourth failure mode after them throws nothing at all. ERR_SDK_NOT_INITIALIZED The plugin's comment on this code reads: "Error occurs when the sdk cannot be initialized with provided license token: invalid, empty". With the snippet above, it usually means the app was started without --dart-define=BANUBA_TOKEN=..., so the constant is an empty string. ERR_SDK_LICENSE_REVOKED The license has been revoked, and the plugin's own comment says to contact Banuba. In the integration sample, the same code also covers an expired license, which is what a finished 14-day trial looks like from Dart. ERR_MISSING_HOST The editor is a native screen, so it needs an Activity on Android or a ViewController on iOS to present from. This code means none was available, which points at calling the plugin before the first frame has rendered, for example from main() or initState(). "New UI is not available from Trimmer screen" This one is not an error code, and that is what makes it expensive to debug. FeaturesConfigBuilder sets enableEditorV2 to true by default, and with the new UI enabled the plugin refuses the trimmer entry point: it writes that line to the debug console and returns null without throwing. From the snippet above, the only thing you see is "Editor closed before export", as though the user had backed out. If you want openTrimmerScreen, turn the new UI off for that call: final config = FeaturesConfigBuilder() .enableEditorV2(false) .build(); Step 5: Which entry screen should the editor open with? VeSdkFlutter in 0.48.1 exposes eight entry points. Each takes the token and a FeaturesConfig, and each returns the same ExportResult: | Method | Extra input | Starts the user on | | :-- | :-- | :-- | | openCameraScreen | none | Recording | | openPipScreen | one video path | Picture-in-picture over an existing clip | | openTrimmerScreen | list of video paths | Trimming the clips you pass | | openEditorScreen | list of video paths | The editing timeline | | openAiClippingScreen | none | AI clipping, starting from the gallery | | openTemplatesScreen | none | Video templates | | openDraftsScreen | none | Saved drafts | | openGalleryScreen | none | The media gallery | openPipScreen takes one video path on top of the token and config, and openTrimmerScreen and openEditorScreen take a list of paths, which you get from a picker such as image_picker. The other five need nothing else. Two things in the 0.48.1 source are worth knowing before you build on this table. openTrimmerScreen is the one that returns null under the default config, as described above. And openEditorScreen accepts an optional trackData argument that goes nowhere: the plugin serializes track data and sends it to the native side from its internal open method, but the editor entry point does not forward the argument to it. Passing a track there is a silent no-op today, so preload audio another way until a release says otherwise. Each screen is trimmed through FeaturesConfigBuilder. Setting supportsBeauty and supportsMasks to false in CameraConfig removes the beauty and mask controls from the camera, and supportsVoiceOver: false in EditorConfig removes voice-over, which our docs mark as Android only. Step 6: How do you control the exported file? Without ExportData, the plugin exports one video at auto quality, which our docs describe as chosen from the device's hardware. With it, you name each file and pick a VideoResolution. The enum has nine values: seven resolutions from vga360p through vga480p, qhd540p, hd720p, fhd1080p and qhd1440p to uhd2160p, plus auto and original. The file type differs by platform. Our docs state that the video is exported as .mp4 on Android and .mov on iOS, so do not hard-code an extension when you upload. Three settings change what lands on disk: 1. useHevcIfPossible: false switches the file to H.264 (AVC), for upload or playback pipelines that expect AVC. 2. Watermark(imagePath: 'assets/watermark.png', alignment: WatermarkAlignment.bottomRight) adds your logo. Our docs state that no watermark is added by default. 3. Each ExportedVideo entry produces its own file, so two entries mean two encodes on the user's phone. The result carries more than video paths. ExportResult also returns previewFilePath, metaFilePath and audioMeta, and the plugin's example describes the meta file as short data used in the exported video. To play the exported file inside your own screen, the official video_player plugin is enough. Encoding time depends on the phone, so measure the export on the slowest Android model you support. Our video editor SDK performance benchmark shows how results spread across devices. Which Flutter video editor route fits your app? Video editing in Flutter has four realistic routes, and each is the right call for some team. | Question | Banuba plugin (ve_sdk_flutter 0.48.1) | Banuba integration sample | video_editor 3.0.0 | ffmpeg_kit_flutter | | :-- | :-- | :-- | :-- | :-- | | What it is | Ready editor UI behind one Dart call | Full app source using a platform channel | Dart widgets for trim, crop, rotate and cover | FFmpeg and FFprobe command runner | | Editing UI | Included, native screens | Included, editable native code | You assemble it from widgets | None | | Export | Included, mp4 or mov | Included | Builds FFmpeg commands, does not export | Any FFmpeg command you write | | Access | Trial token on request | Trial token on request | Open | Open | | License | Commercial | Commercial | MIT | LGPL 3.0 by default, some packages GPL v3.0 | | Maintenance state | Released against current native SDK versions | Tracks the plugin | Latest release is 3.0.0, no release since | Upstream FFmpegKit retired; successor and community forks exist | Four ways to read that table: - Fastest to a complete editor: the Banuba plugin. Recording, timeline, effects and export arrive behind the call in Step 4. - Most control over native behavior: the Banuba integration sample. The Flutter integration sample on GitHub ships the app source, so your team can change the native layer rather than wait for a plugin release. - Cost and UI ownership: video_editor. It is free under MIT and every widget is yours to style. You still need an export path, because its README says the library does not handle exportation. - Raw codec control: the FFmpeg route. No other option lets you write arbitrary FFmpeg commands, and the licensing is the thing to settle first, since the default build is LGPL 3.0 and some variants are GPL v3.0. The FFmpeg route needs one more sentence, because the package most guides still point at is not where the work happens now. The upstream project posted a retirement notice in July 2026 stating that FFmpegKit has been officially retired, and names FFmpegKitNext as the actively maintained continuation, distributed as source only, alongside community-maintained forks published for Android, Flutter and React Native through the usual package managers. A team going this way is picking a fork and a build process, not just adding a dependency. One more limit of our own: the Banuba editor does not run on the web, so a Flutter web target needs a different route for that platform. Why use Banuba for a Flutter video editor? Banuba Video Editor SDK covers recording, a multi-track timeline, effects, AI clipping, AI subtitles and export in one plugin. Our docs state that it works offline and does not send, store or process user information. They also publish agent skills for Claude Code, Codex and Qwen Code, plus an LLM-friendly documentation file for AI-assisted coding. It does not hand you Dart widgets to restyle line by line. The screens are native, and you shape them through FeaturesConfig, so a team that wants a pixel-level custom UI in Flutter is better served by video_editor plus its own export path. For the native side of the same editor, see our walkthrough of integrating the iOS video editor SDK in Swift. For other vendors, our comparison of five video editor SDKs for social apps puts Banuba next to the alternatives, and the original Flutter guide on our blog covers the feature list in more depth. Six steps take a Flutter app from pubspec to an exported clip Pin ve_sdk_flutter at 0.48.1, add the Podfile sources and permissions, set the Android size switches, and call one open...Screen method from a mounted widget. Then decide the export: which resolutions, which codec, and whether a watermark goes on. Check the size table in Step 3 and the four failure modes in Step 4 before the first device run. Three of them throw, and the fourth returns null and looks like a user walking away. FAQ Is the Banuba Flutter video editor free? The Banuba Flutter video editor is free for a 14-day trial with the full feature set, and the plugin's pub.dev page says no credit card information is needed. Production use needs a commercial Banuba license. Does the Banuba Flutter video editor work offline? Yes. Banuba's documentation states that the Video Editor SDK works offline and does not send, store or process user information. Does the Banuba Video Editor SDK support Flutter web? No. The Banuba Video Editor SDK runs on iOS and Android, and pub.dev lists only those two platforms for ve_sdk_flutter. How do I fix ERR_SDK_NOT_INITIALIZED in the Banuba Flutter plugin? The Banuba plugin returns ERR_SDK_NOT_INITIALIZED when the license token is empty or invalid. Check that the token reaches the call, for example that you passed --dart-define=BANUBA_TOKEN=... when you started the app. Why does openTrimmerScreen return null in the Banuba Flutter plugin? Because the new editor UI is on by default. FeaturesConfigBuilder sets enableEditorV2 to true, and the plugin blocks the trimmer entry point under it, logging "New UI is not available from Trimmer screen" and returning null rather than throwing. Call .enableEditorV2(false) on the builder if you need that screen. How much does the Banuba video editor add to a Flutter app's size? Banuba documents 25 MB on Android and 14 MB on iOS for the core editor, 45 MB for the full bundle, and 45 to 90 MB on Android or 45 to 140 MB on iOS with face AR. Turning face AR off and filtering architectures moves a build toward the core figures.