Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a35167d
README fix
stuartmorgan-g May 1, 2023
69dc81b
Direct copy of Windows
stuartmorgan-g May 1, 2023
b110acf
Remove Windows examples, add template-created macOS and Linux examples
stuartmorgan-g May 1, 2023
e311f04
Add new platform interface pieces
stuartmorgan-g May 1, 2023
1558aa2
Update Windows implementation
stuartmorgan-g May 1, 2023
9f0f245
Add macOS and Linux implementations
stuartmorgan-g May 1, 2023
e8e6c98
Add integration test config exemptions
stuartmorgan-g May 1, 2023
5d8b155
Fix some lingering references to Windows
stuartmorgan-g May 1, 2023
fd7655d
Add TODO about PHFilePicker
stuartmorgan-g May 1, 2023
e48478e
Update desktop implementation examples to use newer APIs
stuartmorgan-g May 1, 2023
047b32a
Fix macOS
stuartmorgan-g May 1, 2023
4af92e2
Add app-facing API, update examples
stuartmorgan-g May 2, 2023
1361f31
Add README notes, version bumps, and endorsement
stuartmorgan-g May 2, 2023
094b763
Adjust labels
stuartmorgan-g May 2, 2023
c384211
Add codeowners
stuartmorgan-g May 2, 2023
ba3a86b
Update OS support table in README
stuartmorgan-g May 2, 2023
3f0161a
Fix pubspec overrides
stuartmorgan-g May 2, 2023
1d99ee1
Add desktop support to app-facing example
stuartmorgan-g May 2, 2023
7a509e3
Copyright for template-generated files
stuartmorgan-g May 2, 2023
471f87d
Revert auto-changes to existing platforms
stuartmorgan-g May 2, 2023
42ac084
Sort overrides
stuartmorgan-g May 2, 2023
a2eb725
Autoformat boilerplate code
stuartmorgan-g May 2, 2023
9174b4f
Add missing version bump
stuartmorgan-g May 2, 2023
3e4b21c
Initial review comments
stuartmorgan-g May 3, 2023
0281b4c
Remove examples for ease of review
stuartmorgan-g May 3, 2023
4ada117
Reverts removing examples for ease of review
stuartmorgan-g May 3, 2023
16ab8c2
Merge branch 'main' into image-picker-macos-linux
stuartmorgan-g May 3, 2023
921e295
Merge branch 'main' into image-picker-macos-linux
stuartmorgan-g Jun 7, 2023
7b4ebf5
Address review comments
stuartmorgan-g Jun 7, 2023
d18936d
Missing awaits
stuartmorgan-g Jun 7, 2023
9296999
Merge branch 'main' into image-picker-macos-linux
stuartmorgan-g Jun 9, 2023
ff710c6
Scrub platform interface overrides
stuartmorgan-g Jun 9, 2023
c79601e
Merge branch 'main' into image-picker-macos-linux
stuartmorgan-g Jun 9, 2023
dba80d2
Remove overrides
stuartmorgan-g Jun 9, 2023
6a86c5d
Update macOS version
stuartmorgan-g Jun 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add new platform interface pieces
  • Loading branch information
stuartmorgan-g committed May 1, 2023
commit e311f04670264d91b2e9a2f76f1b4157ac7608b0
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,76 @@ abstract class ImagePickerPlatform extends PlatformInterface {
);
return pickedImages ?? <XFile>[];
}

/// Returns true if the implementation supports [source].
///
/// Defaults to true for the original image sources, `gallery` and `camera`,
/// for backwards compatibility.
bool supportsImageSource(ImageSource source) {
return source == ImageSource.gallery || source == ImageSource.camera;
}
}

/// A base class for an [ImagePickerPlatform] implementation that does not
/// directly support [ImageSource.camera], but supports delegating to a
/// provided [ImagePickerCameraDelegate].
abstract class CameraDelegatingImagePickerPlatform extends ImagePickerPlatform {
/// A delegate to respond to calls that use [ImageSource.camera].
///
/// When it is null, attempting to use [ImageSource.camera] will throw a
/// [StateError].
ImagePickerCameraDelegate? cameraDelegate;

@override
bool supportsImageSource(ImageSource source) {
if (source == ImageSource.camera) {
return cameraDelegate != null;
}
return super.supportsImageSource(source);
}

Future<XFile?> getImageFromSource({
required ImageSource source,
ImagePickerOptions options = const ImagePickerOptions(),
}) async {
if (source == ImageSource.camera) {
final ImagePickerCameraDelegate? delegate = cameraDelegate;
if (delegate == null) {
throw StateError(
'This implementation of ImagePickerPlatform requires a '
'"cameraDelegate" in order to use ImageSource.camera');
} else {
return delegate.takePhoto(
options: ImagePickerCameraDelegateOptions(
preferredCameraDevice: options.preferredCameraDevice,
));
}
}
return super.getImageFromSource(source: source, options: options);
}

@override
Future<XFile?> getVideo({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
if (source == ImageSource.camera) {
final ImagePickerCameraDelegate? delegate = cameraDelegate;
if (delegate == null) {
throw StateError(
'This implementation of ImagePickerPlatform requires a '
'"cameraDelegate" in order to use ImageSource.camera');
} else {
return delegate.takeVideo(
options: ImagePickerCameraDelegateOptions(
preferredCameraDevice: preferredCameraDevice,
maxVideoDuration: maxDuration));
}
}
return super.getVideo(
source: source,
preferredCameraDevice: preferredCameraDevice,
maxDuration: maxDuration);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cross_file/cross_file.dart';
import 'package:flutter/foundation.dart' show immutable;

import 'camera_device.dart';

/// Options for [ImagePickerCameraDelegate] methods.
///
/// New options may be added in the future.
@immutable
class ImagePickerCameraDelegateOptions {
/// Creates a new set of options for taking an image or video.
const ImagePickerCameraDelegateOptions({
this.preferredCameraDevice = CameraDevice.rear,
this.maxVideoDuration,
});

/// The camera device to default to, if available.
final CameraDevice preferredCameraDevice;

/// The maximum duration to allow when recording a video.
final Duration? maxVideoDuration;
}

/// A delegate for `ImagePickerPlatform` implementations that do not provide
/// a camera implementation, or that have a default but allow substituting an
/// alternate implementation.
abstract class ImagePickerCameraDelegate {
/// Takes a photo with the given [options] and returns an [XFile] to the
/// resulting image file.
///
/// Returns null if the photo could not be taken, or the user cancelled.
Future<XFile?> takePhoto({
ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions(),
});

/// Records a video with the given [options] and returns an [XFile] to the
/// resulting video file.
///
/// Returns null if the video could not be recorded, or the user cancelled.
Future<XFile?> takeVideo({
ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions(),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

export 'camera_delegate.dart';
export 'camera_device.dart';
export 'image_options.dart';
export 'image_picker_options.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';

void main() {
group('ImagePickerPlatform', () {
test('supportsImageSource defaults to true for original values', () async {
final ImagePickerPlatform implementation = FakeImagePickerPlatform();

expect(implementation.supportsImageSource(ImageSource.camera), true);
expect(implementation.supportsImageSource(ImageSource.gallery), true);
});
});

group('CameraDelegatingImagePickerPlatform', () {
test(
'supportsImageSource returns false for camera when there is no delegate',
() async {
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();

expect(implementation.supportsImageSource(ImageSource.camera), false);
});

test('supportsImageSource returns true for camera when there is a delegate',
() async {
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();
implementation.cameraDelegate = FakeCameraDelegate();

expect(implementation.supportsImageSource(ImageSource.camera), true);
});

test('getImageFromSource for camera throws if delegate is not set',
() async {
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();

expectLater(implementation.getImageFromSource(source: ImageSource.camera),
throwsStateError);
});

test('getVideo for camera throws if delegate is not set', () async {
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();

expectLater(implementation.getVideo(source: ImageSource.camera),
throwsStateError);
});

test('getImageFromSource for camera calls delegate if set', () async {
const String fakePath = '/tmp/foo';
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();
implementation.cameraDelegate =
FakeCameraDelegate(result: XFile(fakePath));

expect(
(await implementation.getImageFromSource(source: ImageSource.camera))!
.path,
fakePath);
});

test('getVideo for camera calls delegate if set', () async {
const String fakePath = '/tmp/foo';
final FakeCameraDelegatingImagePickerPlatform implementation =
FakeCameraDelegatingImagePickerPlatform();
implementation.cameraDelegate =
FakeCameraDelegate(result: XFile(fakePath));

expect((await implementation.getVideo(source: ImageSource.camera))!.path,
fakePath);
});
});
}

class FakeImagePickerPlatform extends ImagePickerPlatform {}

class FakeCameraDelegatingImagePickerPlatform
extends CameraDelegatingImagePickerPlatform {}

class FakeCameraDelegate extends ImagePickerCameraDelegate {
FakeCameraDelegate({this.result});

XFile? result;

@override
Future<XFile?> takePhoto(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return result;
}

@override
Future<XFile?> takeVideo(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return result;
}
}