@@ -134,13 +134,23 @@ - (UIImageOrientation)getImageRotation {
134134}
135135@end
136136
137- static AVCaptureFlashMode getFlashModeForString (NSString *mode) {
137+ // Mirrors FlashMode in flash_mode.dart
138+ typedef enum {
139+ FlashModeOff,
140+ FlashModeAuto,
141+ FlashModeAlways,
142+ FlashModeTorch,
143+ } FlashMode;
144+
145+ static FlashMode getFlashModeForString (NSString *mode) {
138146 if ([mode isEqualToString: @" off" ]) {
139- return AVCaptureFlashModeOff ;
147+ return FlashModeOff ;
140148 } else if ([mode isEqualToString: @" auto" ]) {
141- return AVCaptureFlashModeAuto ;
149+ return FlashModeAuto ;
142150 } else if ([mode isEqualToString: @" always" ]) {
143- return AVCaptureFlashModeOn;
151+ return FlashModeAlways;
152+ } else if ([mode isEqualToString: @" torch" ]) {
153+ return FlashModeTorch;
144154 } else {
145155 NSError *error = [NSError errorWithDomain: NSCocoaErrorDomain
146156 code: NSURLErrorUnknown
@@ -152,6 +162,20 @@ static AVCaptureFlashMode getFlashModeForString(NSString *mode) {
152162 }
153163}
154164
165+ static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode (FlashMode mode) {
166+ switch (mode) {
167+ case FlashModeOff:
168+ return AVCaptureFlashModeOff;
169+ case FlashModeAuto:
170+ return AVCaptureFlashModeAuto;
171+ case FlashModeAlways:
172+ return AVCaptureFlashModeOn;
173+ case FlashModeTorch:
174+ default :
175+ return -1 ;
176+ }
177+ }
178+
155179// Mirrors ResolutionPreset in camera.dart
156180typedef enum {
157181 veryLow,
@@ -219,7 +243,7 @@ @interface FLTCam : NSObject <FlutterTexture,
219243@property (assign , nonatomic ) BOOL isAudioSetup;
220244@property (assign , nonatomic ) BOOL isStreamingImages;
221245@property (assign , nonatomic ) ResolutionPreset resolutionPreset;
222- @property (assign , nonatomic ) AVCaptureFlashMode flashMode;
246+ @property (assign , nonatomic ) FlashMode flashMode;
223247@property (assign , nonatomic ) CMTime lastVideoSampleTime;
224248@property (assign , nonatomic ) CMTime lastAudioSampleTime;
225249@property (assign , nonatomic ) CMTime videoTimeOffset;
@@ -250,7 +274,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
250274 _enableAudio = enableAudio;
251275 _dispatchQueue = dispatchQueue;
252276 _captureSession = [[AVCaptureSession alloc ] init ];
253- _flashMode = AVCaptureFlashModeAuto ;
277+ _flashMode = FlashModeAuto ;
254278
255279 _captureDevice = [AVCaptureDevice deviceWithUniqueID: cameraName];
256280 NSError *localError = nil ;
@@ -303,7 +327,10 @@ - (void)captureToFile:(FlutterResult)result API_AVAILABLE(ios(10)) {
303327 if (_resolutionPreset == max) {
304328 [settings setHighResolutionPhotoEnabled: YES ];
305329 }
306- [settings setFlashMode: _flashMode];
330+ AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode (_flashMode);
331+ if (avFlashMode != -1 ) {
332+ [settings setFlashMode: avFlashMode];
333+ }
307334 NSError *error;
308335 NSString *path = [self getTemporaryFilePathWithExtension: @" jpg"
309336 subfolder: @" pictures"
@@ -694,25 +721,51 @@ - (void)resumeVideoRecordingWithResult:(FlutterResult)result {
694721}
695722
696723- (void )setFlashModeWithResult : (FlutterResult)result mode : (NSString *)modeStr {
697- AVCaptureFlashMode mode;
724+ FlashMode mode;
698725 @try {
699726 mode = getFlashModeForString (modeStr);
700727 } @catch (NSError *e) {
701728 result (getFlutterError (e));
702729 return ;
703730 }
704- if (!_captureDevice.hasFlash ) {
705- result ([FlutterError errorWithCode: @" setFlashModeFailed"
706- message: @" Device does not have flash capabilities"
707- details: nil ]);
708- return ;
709- }
710- if (![_capturePhotoOutput.supportedFlashModes
711- containsObject: [NSNumber numberWithInt: ((int )mode)]]) {
712- result ([FlutterError errorWithCode: @" setFlashModeFailed"
713- message: @" Device does not support this specific flash mode"
714- details: nil ]);
715- return ;
731+ if (mode == FlashModeTorch) {
732+ if (!_captureDevice.hasTorch ) {
733+ result ([FlutterError errorWithCode: @" setFlashModeFailed"
734+ message: @" Device does not support torch mode"
735+ details: nil ]);
736+ return ;
737+ }
738+ if (!_captureDevice.isTorchAvailable ) {
739+ result ([FlutterError errorWithCode: @" setFlashModeFailed"
740+ message: @" Torch mode is currently not available"
741+ details: nil ]);
742+ return ;
743+ }
744+ if (_captureDevice.torchMode != AVCaptureTorchModeOn) {
745+ [_captureDevice lockForConfiguration: nil ];
746+ [_captureDevice setTorchMode: AVCaptureTorchModeOn];
747+ [_captureDevice unlockForConfiguration ];
748+ }
749+ } else {
750+ if (!_captureDevice.hasFlash ) {
751+ result ([FlutterError errorWithCode: @" setFlashModeFailed"
752+ message: @" Device does not have flash capabilities"
753+ details: nil ]);
754+ return ;
755+ }
756+ AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode (mode);
757+ if (![_capturePhotoOutput.supportedFlashModes
758+ containsObject: [NSNumber numberWithInt: ((int )avFlashMode)]]) {
759+ result ([FlutterError errorWithCode: @" setFlashModeFailed"
760+ message: @" Device does not support this specific flash mode"
761+ details: nil ]);
762+ return ;
763+ }
764+ if (_captureDevice.torchMode != AVCaptureTorchModeOff) {
765+ [_captureDevice lockForConfiguration: nil ];
766+ [_captureDevice setTorchMode: AVCaptureTorchModeOff];
767+ [_captureDevice unlockForConfiguration ];
768+ }
716769 }
717770 _flashMode = mode;
718771 result (nil );
@@ -854,6 +907,12 @@ - (BOOL)setupWriterForPath:(NSString *)path {
854907 [_audioOutput setSampleBufferDelegate: self queue: _dispatchQueue];
855908 }
856909
910+ if (_flashMode == FlashModeTorch) {
911+ [self .captureDevice lockForConfiguration: nil ];
912+ [self .captureDevice setTorchMode: AVCaptureTorchModeOn];
913+ [self .captureDevice unlockForConfiguration ];
914+ }
915+
857916 [_videoWriter addInput: _videoWriterInput];
858917 [_captureVideoOutput setSampleBufferDelegate: self queue: _dispatchQueue];
859918
0 commit comments