[image_picker] Default to gallery instead of camera when picking multiple images on pre-iOS 14 devices. (#4718)

diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md
index 3dbf2c5..992c8b2 100644
--- a/packages/image_picker/image_picker/CHANGELOG.md
+++ b/packages/image_picker/image_picker/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.8.4+8
+
+* Configures the `UIImagePicker` to default to gallery instead of camera when
+picking multiple images on pre-iOS 14 devices.
+
 ## 0.8.4+7
 
 * Refactors unit test to expose private interface via a separate test header instead of the inline declaration.
diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m
index bcaf2d1..5f32874 100644
--- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m
+++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m
@@ -23,30 +23,26 @@
 @end
 
 @interface ImagePickerPluginTests : XCTestCase
-@property(readonly, nonatomic) id mockUIImagePicker;
-@property(readonly, nonatomic) id mockAVCaptureDevice;
+
 @end
 
 @implementation ImagePickerPluginTests
 
-- (void)setUp {
-  _mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
-  _mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
-}
-
 - (void)testPluginPickImageDeviceBack {
+  id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
+  id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
   // UIImagePickerControllerSourceTypeCamera is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
+              [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
       .andReturn(YES);
 
   // UIImagePickerControllerCameraDeviceRear is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
+              [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
       .andReturn(YES);
 
   // AVAuthorizationStatusAuthorized is supported
-  OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
+  OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
       .andReturn(AVAuthorizationStatusAuthorized);
 
   // Run test
@@ -54,27 +50,30 @@
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"pickImage"
                                         arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  [plugin setImagePickerControllerOverrides:@[ controller ]];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
 
-  XCTAssertEqual([plugin getImagePickerController].cameraDevice,
-                 UIImagePickerControllerCameraDeviceRear);
+  XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
 }
 
 - (void)testPluginPickImageDeviceFront {
+  id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
+  id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
   // UIImagePickerControllerSourceTypeCamera is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
+              [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
       .andReturn(YES);
 
   // UIImagePickerControllerCameraDeviceFront is supported
-  OCMStub(ClassMethod([_mockUIImagePicker
-              isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
+  OCMStub(ClassMethod(
+              [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
       .andReturn(YES);
 
   // AVAuthorizationStatusAuthorized is supported
-  OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
+  OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
       .andReturn(AVAuthorizationStatusAuthorized);
 
   // Run test
@@ -82,27 +81,30 @@
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"pickImage"
                                         arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  [plugin setImagePickerControllerOverrides:@[ controller ]];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
 
-  XCTAssertEqual([plugin getImagePickerController].cameraDevice,
-                 UIImagePickerControllerCameraDeviceFront);
+  XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
 }
 
 - (void)testPluginPickVideoDeviceBack {
+  id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
+  id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
   // UIImagePickerControllerSourceTypeCamera is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
+              [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
       .andReturn(YES);
 
   // UIImagePickerControllerCameraDeviceRear is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
+              [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
       .andReturn(YES);
 
   // AVAuthorizationStatusAuthorized is supported
-  OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
+  OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
       .andReturn(AVAuthorizationStatusAuthorized);
 
   // Run test
@@ -110,27 +112,31 @@
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"pickVideo"
                                         arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  [plugin setImagePickerControllerOverrides:@[ controller ]];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
 
-  XCTAssertEqual([plugin getImagePickerController].cameraDevice,
-                 UIImagePickerControllerCameraDeviceRear);
+  XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
 }
 
 - (void)testPluginPickVideoDeviceFront {
+  id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
+  id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
+
   // UIImagePickerControllerSourceTypeCamera is supported
   OCMStub(ClassMethod(
-              [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
+              [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
       .andReturn(YES);
 
   // UIImagePickerControllerCameraDeviceFront is supported
-  OCMStub(ClassMethod([_mockUIImagePicker
-              isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
+  OCMStub(ClassMethod(
+              [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
       .andReturn(YES);
 
   // AVAuthorizationStatusAuthorized is supported
-  OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
+  OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
       .andReturn(AVAuthorizationStatusAuthorized);
 
   // Run test
@@ -138,12 +144,40 @@
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"pickVideo"
                                         arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  [plugin setImagePickerControllerOverrides:@[ controller ]];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
 
-  XCTAssertEqual([plugin getImagePickerController].cameraDevice,
-                 UIImagePickerControllerCameraDeviceFront);
+  XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
+}
+
+- (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 {
+  if (@available(iOS 14, *)) {
+    return;
+  }
+
+  id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
+  id photoLibrary = OCMClassMock([PHPhotoLibrary class]);
+  OCMStub(ClassMethod([photoLibrary authorizationStatus]))
+      .andReturn(PHAuthorizationStatusAuthorized);
+
+  FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
+  [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]];
+  FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickMultiImage"
+                                                              arguments:@{
+                                                                @"maxWidth" : @(100),
+                                                                @"maxHeight" : @(200),
+                                                                @"imageQuality" : @(50),
+                                                              }];
+
+  [plugin handleMethodCall:call
+                    result:^(id _Nullable r){
+                    }];
+
+  OCMVerify(times(1),
+            [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]);
 }
 
 #pragma mark - Test camera devices, no op on simulators
@@ -156,15 +190,18 @@
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"pickImage"
                                         arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  plugin.imagePickerControllerOverrides = @[ controller ];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
   plugin.result = ^(id result) {
 
   };
+
   // To ensure the flow does not crash by multiple cancel call
-  [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]];
-  [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]];
+  [plugin imagePickerControllerDidCancel:controller];
+  [plugin imagePickerControllerDidCancel:controller];
 }
 
 #pragma mark - Test video duration
@@ -174,10 +211,12 @@
   FlutterMethodCall *call = [FlutterMethodCall
       methodCallWithMethodName:@"pickVideo"
                      arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}];
+  UIImagePickerController *controller = [[UIImagePickerController alloc] init];
+  [plugin setImagePickerControllerOverrides:@[ controller ]];
   [plugin handleMethodCall:call
                     result:^(id _Nullable r){
                     }];
-  XCTAssertEqual([plugin getImagePickerController].videoMaximumDuration, 95);
+  XCTAssertEqual(controller.videoMaximumDuration, 95);
 }
 
 - (void)testViewController {
diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h
index ffd23cd..c88db0b 100644
--- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h
+++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h
@@ -8,7 +8,6 @@
 @interface FLTImagePickerPlugin : NSObject <FlutterPlugin>
 
 // For testing only.
-- (UIImagePickerController *)getImagePickerController;
 - (UIViewController *)viewControllerWithWindow:(UIWindow *)window;
 
 @end
diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m
index 1301398..cc841d6 100644
--- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m
+++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m
@@ -31,12 +31,31 @@
                                     PHPickerViewControllerDelegate,
                                     UIAdaptivePresentationControllerDelegate>
 
+/**
+ * The maximum amount of images that are allowed to be picked.
+ */
 @property(assign, nonatomic) int maxImagesAllowed;
 
+/**
+ * The arguments that are passed in from the Flutter method call.
+ */
 @property(copy, nonatomic) NSDictionary *arguments;
 
+/**
+ * The PHPickerViewController instance used to pick multiple
+ * images.
+ */
 @property(strong, nonatomic) PHPickerViewController *pickerViewController API_AVAILABLE(ios(14));
 
+/**
+ * The UIImagePickerController instances that will be used when a new
+ * controller would normally be created. Each call to
+ * createImagePickerController will remove the current first element from
+ * the array.
+ */
+@property(strong, nonatomic)
+    NSMutableArray<UIImagePickerController *> *imagePickerControllerOverrides;
+
 @end
 
 static const int SOURCE_CAMERA = 0;
@@ -44,9 +63,7 @@
 
 typedef NS_ENUM(NSInteger, ImagePickerClassType) { UIImagePickerClassType, PHPickerClassType };
 
-@implementation FLTImagePickerPlugin {
-  UIImagePickerController *_imagePickerController;
-}
+@implementation FLTImagePickerPlugin
 
 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
   FlutterMethodChannel *channel =
@@ -56,8 +73,19 @@
   [registrar addMethodCallDelegate:instance channel:channel];
 }
 
-- (UIImagePickerController *)getImagePickerController {
-  return _imagePickerController;
+- (UIImagePickerController *)createImagePickerController {
+  if ([self.imagePickerControllerOverrides count] > 0) {
+    UIImagePickerController *controller = [self.imagePickerControllerOverrides firstObject];
+    [self.imagePickerControllerOverrides removeObjectAtIndex:0];
+    return controller;
+  }
+
+  return [[UIImagePickerController alloc] init];
+}
+
+- (void)setImagePickerControllerOverrides:
+    (NSArray<UIImagePickerController *> *)imagePickerControllers {
+  _imagePickerControllerOverrides = [imagePickerControllers mutableCopy];
 }
 
 - (UIViewController *)viewControllerWithWindow:(UIWindow *)window {
@@ -88,7 +116,7 @@
  * @param arguments that should be used to get cameraDevice value.
  */
 - (UIImagePickerControllerCameraDevice)getCameraDeviceFromArguments:(NSDictionary *)arguments {
-  NSInteger cameraDevice = [[arguments objectForKey:@"cameraDevice"] intValue];
+  NSInteger cameraDevice = [arguments[@"cameraDevice"] intValue];
   return (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront
                              : UIImagePickerControllerCameraDeviceRear;
 }
@@ -108,22 +136,20 @@
   [self checkPhotoAuthorizationForAccessLevel];
 }
 
-- (void)pickImageWithUIImagePicker {
-  _imagePickerController = [[UIImagePickerController alloc] init];
-  _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
-  _imagePickerController.delegate = self;
-  _imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ];
-
-  int imageSource = [[_arguments objectForKey:@"source"] intValue];
+- (void)launchUIImagePickerWithSource:(int)imageSource {
+  UIImagePickerController *imagePickerController = [self createImagePickerController];
+  imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
+  imagePickerController.delegate = self;
+  imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ];
 
   self.maxImagesAllowed = 1;
 
   switch (imageSource) {
     case SOURCE_CAMERA:
-      [self checkCameraAuthorization];
+      [self checkCameraAuthorizationWithImagePicker:imagePickerController];
       break;
     case SOURCE_GALLERY:
-      [self checkPhotoAuthorization];
+      [self checkPhotoAuthorizationWithImagePicker:imagePickerController];
       break;
     default:
       self.result([FlutterError errorWithCode:@"invalid_source"
@@ -141,10 +167,11 @@
     self.result = nil;
   }
 
+  self.result = result;
+  _arguments = call.arguments;
+
   if ([@"pickImage" isEqualToString:call.method]) {
-    self.result = result;
-    _arguments = call.arguments;
-    int imageSource = [[_arguments objectForKey:@"source"] intValue];
+    int imageSource = [call.arguments[@"source"] intValue];
 
     if (imageSource == SOURCE_GALLERY) {  // Capture is not possible with PHPicker
       if (@available(iOS 14, *)) {
@@ -152,44 +179,39 @@
         [self pickImageWithPHPicker:1];
       } else {
         // UIImagePicker is used
-        [self pickImageWithUIImagePicker];
+        [self launchUIImagePickerWithSource:imageSource];
       }
     } else {
-      [self pickImageWithUIImagePicker];
+      [self launchUIImagePickerWithSource:imageSource];
     }
   } else if ([@"pickMultiImage" isEqualToString:call.method]) {
     if (@available(iOS 14, *)) {
-      self.result = result;
-      _arguments = call.arguments;
       [self pickImageWithPHPicker:0];
     } else {
-      [self pickImageWithUIImagePicker];
+      [self launchUIImagePickerWithSource:SOURCE_GALLERY];
     }
   } else if ([@"pickVideo" isEqualToString:call.method]) {
-    _imagePickerController = [[UIImagePickerController alloc] init];
-    _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
-    _imagePickerController.delegate = self;
-    _imagePickerController.mediaTypes = @[
+    UIImagePickerController *imagePickerController = [self createImagePickerController];
+    imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
+    imagePickerController.delegate = self;
+    imagePickerController.mediaTypes = @[
       (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo,
       (NSString *)kUTTypeMPEG4
     ];
-    _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
+    imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
 
-    self.result = result;
-    _arguments = call.arguments;
-
-    int imageSource = [[_arguments objectForKey:@"source"] intValue];
-    if ([[_arguments objectForKey:@"maxDuration"] isKindOfClass:[NSNumber class]]) {
-      NSTimeInterval max = [[_arguments objectForKey:@"maxDuration"] doubleValue];
-      _imagePickerController.videoMaximumDuration = max;
+    int imageSource = [call.arguments[@"source"] intValue];
+    if ([call.arguments[@"maxDuration"] isKindOfClass:[NSNumber class]]) {
+      NSTimeInterval max = [call.arguments[@"maxDuration"] doubleValue];
+      imagePickerController.videoMaximumDuration = max;
     }
 
     switch (imageSource) {
       case SOURCE_CAMERA:
-        [self checkCameraAuthorization];
+        [self checkCameraAuthorizationWithImagePicker:imagePickerController];
         break;
       case SOURCE_GALLERY:
-        [self checkPhotoAuthorization];
+        [self checkPhotoAuthorizationWithImagePicker:imagePickerController];
         break;
       default:
         result([FlutterError errorWithCode:@"invalid_source"
@@ -202,9 +224,9 @@
   }
 }
 
-- (void)showCamera {
+- (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerController {
   @synchronized(self) {
-    if (_imagePickerController.beingPresented) {
+    if (imagePickerController.beingPresented) {
       return;
     }
   }
@@ -212,9 +234,9 @@
   // Camera is not available on simulators
   if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] &&
       [UIImagePickerController isCameraDeviceAvailable:device]) {
-    _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
-    _imagePickerController.cameraDevice = device;
-    [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController
+    imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
+    imagePickerController.cameraDevice = device;
+    [[self viewControllerWithWindow:nil] presentViewController:imagePickerController
                                                       animated:YES
                                                     completion:nil];
   } else {
@@ -238,19 +260,19 @@
   }
 }
 
-- (void)checkCameraAuthorization {
+- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController {
   AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
 
   switch (status) {
     case AVAuthorizationStatusAuthorized:
-      [self showCamera];
+      [self showCameraWithImagePicker:imagePickerController];
       break;
     case AVAuthorizationStatusNotDetermined: {
       [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
                                completionHandler:^(BOOL granted) {
                                  dispatch_async(dispatch_get_main_queue(), ^{
                                    if (granted) {
-                                     [self showCamera];
+                                     [self showCameraWithImagePicker:imagePickerController];
                                    } else {
                                      [self errorNoCameraAccess:AVAuthorizationStatusDenied];
                                    }
@@ -266,14 +288,14 @@
   }
 }
 
-- (void)checkPhotoAuthorization {
+- (void)checkPhotoAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController {
   PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
   switch (status) {
     case PHAuthorizationStatusNotDetermined: {
       [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
         dispatch_async(dispatch_get_main_queue(), ^{
           if (status == PHAuthorizationStatusAuthorized) {
-            [self showPhotoLibrary:UIImagePickerClassType];
+            [self showPhotoLibraryWithImagePicker:imagePickerController];
           } else {
             [self errorNoPhotoAccess:status];
           }
@@ -282,7 +304,7 @@
       break;
     }
     case PHAuthorizationStatusAuthorized:
-      [self showPhotoLibrary:UIImagePickerClassType];
+      [self showPhotoLibraryWithImagePicker:imagePickerController];
       break;
     case PHAuthorizationStatusDenied:
     case PHAuthorizationStatusRestricted:
@@ -301,9 +323,13 @@
                                      handler:^(PHAuthorizationStatus status) {
                                        dispatch_async(dispatch_get_main_queue(), ^{
                                          if (status == PHAuthorizationStatusAuthorized) {
-                                           [self showPhotoLibrary:PHPickerClassType];
+                                           [self
+                                               showPhotoLibraryWithPHPicker:self->
+                                                                            _pickerViewController];
                                          } else if (status == PHAuthorizationStatusLimited) {
-                                           [self showPhotoLibrary:PHPickerClassType];
+                                           [self
+                                               showPhotoLibraryWithPHPicker:self->
+                                                                            _pickerViewController];
                                          } else {
                                            [self errorNoPhotoAccess:status];
                                          }
@@ -313,7 +339,7 @@
     }
     case PHAuthorizationStatusAuthorized:
     case PHAuthorizationStatusLimited:
-      [self showPhotoLibrary:PHPickerClassType];
+      [self showPhotoLibraryWithPHPicker:_pickerViewController];
       break;
     case PHAuthorizationStatusDenied:
     case PHAuthorizationStatusRestricted:
@@ -355,21 +381,18 @@
   }
 }
 
-- (void)showPhotoLibrary:(ImagePickerClassType)imagePickerClassType {
-  // No need to check if SourceType is available. It always is.
-  switch (imagePickerClassType) {
-    case PHPickerClassType:
-      [[self viewControllerWithWindow:nil] presentViewController:_pickerViewController
-                                                        animated:YES
-                                                      completion:nil];
-      break;
-    case UIImagePickerClassType:
-      _imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
-      [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController
-                                                        animated:YES
-                                                      completion:nil];
-      break;
-  }
+- (void)showPhotoLibraryWithPHPicker:(PHPickerViewController *)pickerViewController
+    API_AVAILABLE(ios(14)) {
+  [[self viewControllerWithWindow:nil] presentViewController:pickerViewController
+                                                    animated:YES
+                                                  completion:nil];
+}
+
+- (void)showPhotoLibraryWithImagePicker:(UIImagePickerController *)imagePickerController {
+  imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
+  [[self viewControllerWithWindow:nil] presentViewController:imagePickerController
+                                                    animated:YES
+                                                  completion:nil];
 }
 
 - (NSNumber *)getDesiredImageQuality:(NSNumber *)imageQuality {
@@ -449,8 +472,8 @@
 
 - (void)imagePickerController:(UIImagePickerController *)picker
     didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info {
-  NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
-  [_imagePickerController dismissViewControllerAnimated:YES completion:nil];
+  NSURL *videoURL = info[UIImagePickerControllerMediaURL];
+  [picker dismissViewControllerAnimated:YES completion:nil];
   // The method dismissViewControllerAnimated does not immediately prevent
   // further didFinishPickingMediaWithInfo invocations. A nil check is necessary
   // to prevent below code to be unwantly executed multiple times and cause a
@@ -484,9 +507,9 @@
     self.result = nil;
     _arguments = nil;
   } else {
-    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
+    UIImage *image = info[UIImagePickerControllerEditedImage];
     if (image == nil) {
-      image = [info objectForKey:UIImagePickerControllerOriginalImage];
+      image = info[UIImagePickerControllerOriginalImage];
     }
     NSNumber *maxWidth = GetNullableValueForKey(_arguments, @"maxWidth");
     NSNumber *maxHeight = GetNullableValueForKey(_arguments, @"maxHeight");
@@ -523,7 +546,7 @@
 }
 
 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
-  [_imagePickerController dismissViewControllerAnimated:YES completion:nil];
+  [picker dismissViewControllerAnimated:YES completion:nil];
   if (!self.result) {
     return;
   }
diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h
index 94eca42..5442f7d 100644
--- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h
+++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h
@@ -40,4 +40,15 @@
  */
 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
 
+/**
+ * Sets UIImagePickerController instances that will be used when a new
+ * controller would normally be created. Each call to
+ * createImagePickerController will remove the current first element from
+ * the array.
+ *
+ * Should be used for testing purposes only.
+ */
+- (void)setImagePickerControllerOverrides:
+    (NSArray<UIImagePickerController *> *)imagePickerControllers;
+
 @end
diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml
index 7142d82..43143f1 100755
--- a/packages/image_picker/image_picker/pubspec.yaml
+++ b/packages/image_picker/image_picker/pubspec.yaml
@@ -3,7 +3,7 @@
   library, and taking new pictures with the camera.
 repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
-version: 0.8.4+7
+version: 0.8.4+8
 
 environment:
   sdk: ">=2.14.0 <3.0.0"