my_filter_camera 2.0.0
my_filter_camera: ^2.0.0 copied to clipboard
This package lets you use the camera with advanced digital filters, apply real-time effects, and customize your camera experience easily.
my_filter_camera #
A Flutter plugin for real-time camera filters, filtered photo capture, video recording, exposure control, torch control, and front/back camera switching on Android and iOS.
Platform and requirements #
| Requirement | Supported value |
|---|---|
| Flutter | >=3.47.1 |
| Dart | >=3.13.1 <4.0.0 |
| Android | API 24+ |
| iOS | 15.0+ |
| Web, desktop | Not supported |
Android uses CameraX 1.6.1 and GPUImage-compatible filters. iOS uses AVFoundation and Core Image without a third-party pod. Both CocoaPods and Swift Package Manager are supported.
Platform support #
| Capability | Android | iOS |
|---|---|---|
| Live camera preview and 13 filters | ✅ | ✅ |
| Filtered JPEG capture | ✅ | ✅ |
| MP4 recording with optional audio | ✅ | ✅ |
| Pause and resume recording | ✅ | ✅ |
| Front/back camera and torch | ✅ | ✅ |
| Exposure, contrast, gamma, and RGB controls | ✅ | ✅ |
| Automatic app lifecycle handling | ✅ | ✅ |
| Filter-processed video recording | — | — |
Camera-dependent controls such as torch and exposure are applied only when the active device supports them. Recorded video is currently unfiltered on both platforms; filters affect the live preview and JPEG capture.
Features #
- Real-time filters in an Android or iOS PlatformView.
- Capture the currently selected filter to a JPEG file.
- Record MP4 video, with optional microphone audio.
- Pause, resume, and stop an active video recording.
- Switch front/back cameras and control the torch.
- Adjust exposure compensation, contrast, gamma, and RGB values.
- Receive camera, torch, and capture events through typed Dart state/streams.
- Automatically stop/resume the camera with the application lifecycle.
Installation #
Add the package to pubspec.yaml:
dependencies:
my_filter_camera: ^2.0.0
Android permissions #
The plugin manifest declares camera and microphone permissions. Do not add legacy storage permissions: captured files use app-private storage and recordings use MediaStore.
If your application never records audio, remove the merged microphone permission in your application manifest:
<uses-permission
android:name="android.permission.RECORD_AUDIO"
tools:node="remove" />
When using tools:node, add xmlns:tools="http://schemas.android.com/tools" to the root <manifest> element.
iOS permissions #
Add usage descriptions to the consuming application's ios/Runner/Info.plist. iOS terminates applications that access these capabilities without the matching description:
<key>NSCameraUsageDescription</key>
<string>Use the camera to preview filters and capture photos and videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Use the microphone only when recording video with audio.</string>
NSMicrophoneUsageDescription is required only when the application calls startVideoRecording(enableAudio: true).
See the platform channel contract for native arguments, return types, events, and error codes.
Basic usage #
Import the package and create one controller for one camera screen:
import 'package:my_filter_camera/my_filter_camera.dart';
final controller = CameraViewController(
facing: CameraFacing.back,
ratio: Ratio.ratio_16_9,
autoResume: true,
);
Display the preview. CameraView starts the controller automatically and reports startup errors through errorBuilder:
CameraView(
controller: controller,
onDetect: (data, args) {
// Capture events sent by the native implementation.
},
errorBuilder: (context, error) {
return Center(child: Text('Camera error: $error'));
},
)
Dispose the controller when the owner of the controller is removed:
@override
void dispose() {
controller.dispose();
super.dispose();
}
If CameraView creates its own controller, it always releases that controller. For an injected controller, autoDispose: true (the default) lets the widget release it.
Start, stop, and camera state #
When using the controller without CameraView:
final granted = await controller.checkPermission();
if (granted) {
await controller.start();
}
controller.isRunningState.addListener(() {
debugPrint('running: ${controller.isRunning}');
});
await controller.stop();
Only one native camera session should be active at a time. Stop or dispose the current controller before creating a camera screen with different settings.
Filters #
[Filter type]
await controller.updateFilter(
filterType: PluginFilterEnum.GRAYSCALE.code,
);
final samples = await controller.applyImageSample();
PluginFilterEnum retains its uppercase 1.x names for source compatibility.
Capture an image #
[Capture image]
final XFile image = await controller.capture();
debugPrint(image.path);
Images are written to an app-private Images directory. No storage runtime permission is required.
Record video #
Record without audio:
await controller.startVideoRecording();
await controller.pauseVideoRecording();
await controller.resumeVideoRecording();
final XFile video = await controller.stopVideoRecording();
Record with audio (Android and iOS request microphone permission when needed):
await controller.startVideoRecording(enableAudio: true);
final XFile video = await controller.stopVideoRecording();
The returned MP4 is written to the app-specific Videos directory using a unique filename. Android also creates a MediaStore recording. On iOS, pause/resume records segments and merges them when recording stops.
Filters apply to the live preview and JPEG capture. Recorded video uses the native movie pipeline and is not filter-processed.
Camera controls #
await controller.switchCamera();
if (controller.hasTorch) {
await controller.setTorch(TorchState.on);
}
final appliedExposure = await controller.adjustBrightness(value: 2);
final contrast = await controller.adjustContrast(value: 1.2);
final gamma = await controller.adjustGamma(value: 0.9);
final rgb = await controller.adjustRGB(red: 1, green: 0.95, blue: 0.9);
The exposure value is clamped to the range supported by the active camera.
Events #
controller.faces.listen((CameraData data) {
debugPrint('captured path: ${data.faceImage}');
});
controller.torchState.addListener(() {
debugPrint('torch: ${controller.torchState.value}');
});
controller.cameraRunningResponseStream.listen((event) {
debugPrint('running: ${event.isCameraRunning}');
});
Delete plugin-created media #
removeDir() is destructive. It deletes the plugin's app-private Images and Videos directories. Android also deletes MediaStore videos whose display name starts with CameraX-recording-:
await controller.removeDir();
Error handling #
Native failures are returned as PlatformException; calls after dispose() throw StateError.
try {
await controller.start();
} on PlatformException catch (error) {
debugPrint('${error.code}: ${error.message}');
}
See MIGRATION.md when upgrading from 1.x, SECURITY.md for privacy guidance, and OPERATIONS.md for validation/release commands.
Contribute #
Issues and pull requests are welcome at GitHub. For project questions, contact Thao Doan or Duc Nguyen.