blink_shot
A Flutter package for blink-triggered photo capture using camera and Google ML Kit face detection.
blink_shot provides a camera preview widget with blink detection and auto-capture capabilities. Use BlinkShotController for lifecycle control and event handling.
Features
- Blink detection based on left/right eye-open probability history
- Ready-to-use camera preview widget with face alignment feedback
- Automatic capture when a blink is detected
- Manual capture mode when auto-capture is disabled
- Low-level APIs for custom camera or detection pipelines
Platform support
- Android
- iOS
Desktop and web are not primary targets for this package because the blink flow depends on the mobile camera stack and ML Kit face detection.
Installation
flutter pub add blink_shot
Platform setup
blink_shot uses the device camera and face detection. The host app must provide the required platform permissions.
Android
Add camera permission to your app manifest:
<uses-permission android:name="android.permission.CAMERA" />
Path: android/app/src/main/AndroidManifest.xml
iOS
Add a camera usage description to your app Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required to capture photos.</string>
Path: ios/Runner/Info.plist
This project is currently configured around iOS 13.0 in the example app and iOS project files.
Quick start
Use BlinkShotController with BlinkShotView for automatic capture, state management, and event handling:
import 'package:blink_shot/blink_shot.dart';
import 'package:flutter/material.dart';
class BlinkCapturePage extends StatefulWidget {
const BlinkCapturePage({super.key});
@override
State<BlinkCapturePage> createState() => _BlinkCapturePageState();
}
class _BlinkCapturePageState extends State<BlinkCapturePage> {
late final BlinkShotController _controller;
String _status = 'Initializing camera...';
@override
void initState() {
super.initState();
_controller = BlinkShotController(
config: const BlinkShotConfig(
autoCapture: true,
showDefaultOverlay: true,
showDefaultStatusText: true,
),
onCapture: (file) {
debugPrint('Captured file: ${file.path}');
},
onError: (error) {
if (!mounted) return;
setState(() {
_status = 'Error: $error';
});
},
onStateChanged: (state) {
if (!mounted) return;
setState(() {
_status = switch (state) {
BlinkShotState.initializing => 'Initializing camera...',
BlinkShotState.ready => 'Blink to capture',
BlinkShotState.capturing => 'Capturing...',
BlinkShotState.paused => 'Paused',
BlinkShotState.error => 'Camera error',
BlinkShotState.disposed => 'Disposed',
};
});
},
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Blink Shot')),
body: Column(
children: [
Expanded(
child: BlinkShotView(
controller: _controller,
instructionText: 'Align your face and blink once',
),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(_status),
),
],
),
);
}
}
Configuration
BlinkShotConfig controls capture and UI behavior:
| Property | Default | Description |
|---|---|---|
openThreshold |
0.7 |
Average eye-open score required to treat both eyes as open |
closedThreshold |
0.25 |
Average eye-open score required to treat both eyes as closed |
historyLength |
8 |
Number of frames used when evaluating a blink |
autoCapture |
true |
Automatically take a picture when a blink is detected |
cameraLensDirection |
CameraLensDirection.front |
Preferred camera |
showDefaultOverlay |
true |
Shows the built-in face alignment frame |
showDefaultStatusText |
true |
Shows the built-in status label |
Example:
final controller = BlinkShotController(
config: const BlinkShotConfig(
autoCapture: false,
openThreshold: 0.8,
closedThreshold: 0.2,
historyLength: 10,
showDefaultOverlay: false,
),
);
When autoCapture is false, BlinkShotView shows a capture button instead of taking a photo automatically.
Direct detector usage
If you already have your own face or eye-tracking pipeline, you can use BlinkDetector directly:
final detector = BlinkDetector(
historyLength: 8,
openThreshold: 0.6,
closedThreshold: 0.25,
);
void onFrame(double? leftEyeOpenProbability, double? rightEyeOpenProbability) {
detector.add(leftEyeOpenProbability, rightEyeOpenProbability);
if (detector.didBlink) {
capturePhoto();
detector.reset();
}
}
Lower-level APIs
BlinkDetectortracks blink state from eye-open probabilitiesFaceBlinkProcessorcombines ML Kit face detection with blink detectionBlinkShotControllermanages camera initialization, preview, frame processing, capture, pause, and resumeBlinkShotViewrenders the preview and optional default UI
Controller state
BlinkShotController can report these states through onStateChanged:
initializingreadycapturingpausederrordisposed
Useful controller methods:
initialize()capture()pause()resume()resetBlinkDetector()
Notes
- The built-in blink flow is intended for front-camera selfie capture.
BlinkDetectorandBlinkShotConfigdo not share the same defaultopenThreshold. The controller usesBlinkShotConfig, whose default is0.7.- In controller mode, handle captured files through
BlinkShotController.onCapture.
Example app
See example/lib/main.dart for a complete integration example.
License
MIT License. See LICENSE.