Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 1.3.0

* Adds `videoQuality` parameter to `pickVideo` for specifying video recording quality.
* Updates minimum supported SDK version to Flutter 3.35/Dart 3.9.
* Updates README to reflect currently supported OS versions for the latest
versions of the endorsed platform implementations.
Expand Down
40 changes: 39 additions & 1 deletion packages/image_picker/image_picker/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class _MyHomePageState extends State<MyHomePage> {
required BuildContext context,
bool allowMultiple = false,
bool isMedia = false,
VideoQuality? videoQuality,
}) async {
if (_controller != null) {
await _controller!.setVolume(0.0);
Expand All @@ -100,6 +101,7 @@ class _MyHomePageState extends State<MyHomePage> {
final XFile? file = await _picker.pickVideo(
source: source,
maxDuration: const Duration(seconds: 10),
quality: videoQuality ?? VideoQuality.high,
);
files = <XFile>[if (file != null) file];
}
Expand Down Expand Up @@ -481,7 +483,43 @@ class _MyHomePageState extends State<MyHomePage> {
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'takeVideo',
tooltip: 'Take a video',
tooltip: 'Take a video (high quality)',
child: const Icon(Icons.videocam),
),
),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.orange,
onPressed: () {
isVideo = true;
_onImageButtonPressed(
ImageSource.camera,
context: context,
videoQuality: VideoQuality.medium,
);
},
heroTag: 'takeVideoMedium',
tooltip: 'Take a video (medium quality)',
child: const Icon(Icons.videocam),
),
),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.blue,
onPressed: () {
isVideo = true;
_onImageButtonPressed(
ImageSource.camera,
context: context,
videoQuality: VideoQuality.low,
);
},
heroTag: 'takeVideoLow',
tooltip: 'Take a video (low quality)',
child: const Icon(Icons.videocam),
),
),
Comment on lines +490 to 525

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's significant code duplication for creating the FloatingActionButtons for different video qualities. This can be refactored into a helper method to improve maintainability and readability.

For example, you could create a helper widget/method like this:

Widget _buildVideoButton({
  required VideoQuality quality,
  required Color color,
  required String heroTag,
  required String tooltip,
}) {
  return Padding(
    padding: const EdgeInsets.only(top: 16.0),
    child: FloatingActionButton(
      backgroundColor: color,
      onPressed: () {
        isVideo = true;
        _onImageButtonPressed(
          ImageSource.camera,
          context: context,
          videoQuality: quality,
        );
      },
      heroTag: heroTag,
      tooltip: tooltip,
      child: const Icon(Icons.videocam),
    ),
  );
}

And then use it for all three buttons.

Expand Down
10 changes: 10 additions & 0 deletions packages/image_picker/image_picker/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ dev_dependencies:

flutter:
uses-material-design: true
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
image_picker_android: {path: ../../../../packages/image_picker/image_picker_android}
image_picker_for_web: {path: ../../../../packages/image_picker/image_picker_for_web}
image_picker_ios: {path: ../../../../packages/image_picker/image_picker_ios}
image_picker_linux: {path: ../../../../packages/image_picker/image_picker_linux}
image_picker_macos: {path: ../../../../packages/image_picker/image_picker_macos}
image_picker_platform_interface: {path: ../../../../packages/image_picker/image_picker_platform_interface}
image_picker_windows: {path: ../../../../packages/image_picker/image_picker_windows}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class FakeImagePicker extends ImagePickerPlatform {
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
VideoQuality quality = VideoQuality.high,
}) async {
return XFile(source == ImageSource.camera ? 'cameraVideo' : 'galleryVideo');
}
Expand Down
5 changes: 5 additions & 0 deletions packages/image_picker/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export 'package:image_picker_platform_interface/image_picker_platform_interface.
LostDataResponse,
PickedFile,
RetrieveType,
VideoQuality,
XFile,
kTypeImage,
kTypeVideo;
Expand Down Expand Up @@ -277,6 +278,8 @@ class ImagePicker {
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
/// Defaults to [CameraDevice.rear].
///
/// The [quality] argument specifies the video quality for recording/picking. Defaults to [VideoQuality.high].
///
/// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
/// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data.
///
Expand All @@ -289,11 +292,13 @@ class ImagePicker {
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
VideoQuality quality = VideoQuality.high,
}) {
return platform.getVideo(
source: source,
preferredCameraDevice: preferredCameraDevice,
maxDuration: maxDuration,
quality: quality,
);
}

Expand Down
14 changes: 12 additions & 2 deletions packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
repository: https://github.com/flutter/packages/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: 1.2.1
version: 1.3.0

environment:
sdk: ^3.9.0
Expand Down Expand Up @@ -33,7 +33,7 @@ dependencies:
image_picker_ios: ^0.8.13
image_picker_linux: ^0.2.2
image_picker_macos: ^0.2.2
image_picker_platform_interface: ^2.11.0
image_picker_platform_interface: ^2.12.0
image_picker_windows: ^0.2.2

dev_dependencies:
Expand All @@ -49,3 +49,13 @@ topics:
- image-picker
- files
- file-selection
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
image_picker_android: {path: ../../../packages/image_picker/image_picker_android}
image_picker_for_web: {path: ../../../packages/image_picker/image_picker_for_web}
image_picker_ios: {path: ../../../packages/image_picker/image_picker_ios}
image_picker_linux: {path: ../../../packages/image_picker/image_picker_linux}
image_picker_macos: {path: ../../../packages/image_picker/image_picker_macos}
image_picker_platform_interface: {path: ../../../packages/image_picker/image_picker_platform_interface}
image_picker_windows: {path: ../../../packages/image_picker/image_picker_windows}
51 changes: 50 additions & 1 deletion packages/image_picker/image_picker/test/image_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ void main() {
source: anyNamed('source'),
preferredCameraDevice: anyNamed('preferredCameraDevice'),
maxDuration: anyNamed('maxDuration'),
quality: anyNamed('quality'),
),
).thenAnswer((Invocation _) async => null);
});
Expand Down Expand Up @@ -390,6 +391,49 @@ void main() {
]);
});

test('video quality defaults to high', () async {
final picker = ImagePicker();
await picker.pickVideo(source: ImageSource.camera);

verify(
mockPlatform.getVideo(
source: ImageSource.camera,
quality: VideoQuality.high,
),
);
});

test('passes the video quality argument correctly', () async {
final picker = ImagePicker();
await picker.pickVideo(
source: ImageSource.camera,
quality: VideoQuality.low,
);
await picker.pickVideo(
source: ImageSource.camera,
quality: VideoQuality.medium,
);
await picker.pickVideo(
source: ImageSource.camera,
quality: VideoQuality.high,
);

verifyInOrder(<Object>[
mockPlatform.getVideo(
source: ImageSource.camera,
quality: VideoQuality.low,
),
mockPlatform.getVideo(
source: ImageSource.camera,
quality: VideoQuality.medium,
),
mockPlatform.getVideo(
source: ImageSource.camera,
quality: VideoQuality.high,
),
]);
});

test('handles a null video file response gracefully', () async {
final picker = ImagePicker();

Expand All @@ -401,7 +445,12 @@ void main() {
final picker = ImagePicker();
await picker.pickVideo(source: ImageSource.camera);

verify(mockPlatform.getVideo(source: ImageSource.camera));
verify(
mockPlatform.getVideo(
source: ImageSource.camera,
quality: VideoQuality.high,
),
);
});

test('camera position can set to front', () async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
// ignore_for_file: invalid_use_of_internal_member

class _FakeLostData_0 extends _i1.SmartFake implements _i2.LostData {
_FakeLostData_0(Object parent, Invocation parentInvocation)
Expand Down Expand Up @@ -156,12 +157,14 @@ class MockImagePickerPlatform extends _i1.Mock
required _i2.ImageSource? source,
_i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear,
Duration? maxDuration,
_i2.VideoQuality? quality = _i2.VideoQuality.high,
}) =>
(super.noSuchMethod(
Invocation.method(#getVideo, [], {
#source: source,
#preferredCameraDevice: preferredCameraDevice,
#maxDuration: maxDuration,
#quality: quality,
}),
returnValue: _i4.Future<_i5.XFile?>.value(),
)
Expand Down
4 changes: 4 additions & 0 deletions packages/image_picker/image_picker_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.8.14

* Adds support for `videoQuality` parameter in `getVideo`.

## 0.8.13+14

* Bumps androidx.activity:activity from 1.12.2 to 1.12.4.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ private void launchTakeVideoWithCameraIntent() {
int maxSeconds = localVideoOptions.getMaxDurationSeconds().intValue();
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, maxSeconds);
}
if (localVideoOptions != null && localVideoOptions.getVideoQuality() != null) {
int videoQuality = localVideoOptions.getVideoQuality().intValue();
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality);
}
if (cameraDevice == CameraDevice.FRONT) {
useFrontCamera(intent);
}
Expand Down
Loading