Skip to content

[image_picker] Add desktop support #3882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Jun 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz

# - Linux
packages/file_selector/file_selector_linux/** @cbracken
packages/image_picker/image_picker_linux/** @cbracken
packages/path_provider/path_provider_linux/** @cbracken
packages/shared_preferences/shared_preferences_linux/** @cbracken
packages/url_launcher/url_launcher_linux/** @cbracken

# - macOS
packages/file_selector/file_selector_macos/** @cbracken
packages/image_picker/image_picker_macos/** @cbracken
packages/url_launcher/url_launcher_macos/** @cbracken

# - Windows
Expand Down
7 changes: 7 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.8.8

* Adds initial support for Windows, macOS, and Linux.
* See README for current desktop limitations.
* Adds `supportsImageSource` to allow runtime checks for whether a given source
is supported by the current platform's implementation.

## 0.8.7+5

* Fixes `BuildContext` handling in example.
Expand Down
61 changes: 58 additions & 3 deletions packages/image_picker/image_picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
A Flutter plugin for iOS and Android for picking images from the image library,
and taking new pictures with the camera.

| | Android | iOS | Web |
|-------------|---------|---------|---------------------------------|
| **Support** | SDK 21+ | iOS 11+ | [See `image_picker_for_web`][1] |
| | Android | iOS | Linux | macOS | Web | Windows |
|-------------|---------|---------|-------|--------|---------------------------------|-------------|
| **Support** | SDK 21+ | iOS 11+ | Any | 10.14+ | [See `image_picker_for_web`][1] | Windows 10+ |

## Installation

Expand Down Expand Up @@ -109,6 +109,61 @@ As activities cannot communicate between tasks, the image picker activity cannot
send back its eventual result to the calling activity.
To work around this problem, consider using `launchMode: singleTask` instead.

### Windows, macOS, and Linux

This plugin currently has limited support for the three desktop platforms,
serving as a wrapper around the [`file_selector`](https://pub.dev/packages/file_selector)
plugin with appropriate file type filters set. Selection modification options,
such as max width and height, are not yet supported.

By default, `ImageSource.camera` is not supported, since unlike on Android and
iOS there is no system-provided UI for taking photos. However, the desktop
implementations allow delegating to a camera handler by setting a
`cameraDelegate` before using `image_picker`, such as in `main()`:

<?code-excerpt "readme_excerpts.dart (CameraDelegate)"?>
``` dart
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
// ···
class MyCameraDelegate extends ImagePickerCameraDelegate {
@override
Future<XFile?> takePhoto(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return _takeAPhoto(options.preferredCameraDevice);
}

@override
Future<XFile?> takeVideo(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return _takeAVideo(options.preferredCameraDevice);
}
}
// ···
void setUpCameraDelegate() {
final ImagePickerPlatform instance = ImagePickerPlatform.instance;
if (instance is CameraDelegatingImagePickerPlatform) {
instance.cameraDelegate = MyCameraDelegate();
}
}
```

Once you have set a `cameraDelegate`, `image_picker` calls with
`ImageSource.camera` will work as normal, calling your provided delegate. We
encourage the community to build packages that implement
`ImagePickerCameraDelegate`, to provide options for desktop camera UI.

#### macOS installation

Since the macOS implementation uses `file_selector`, you will need to
add a filesystem access
[entitlement][https://docs.flutter.dev/platform-integration/macos/building#entitlements-and-the-app-sandbox]:
```xml
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
```

### Example

<?code-excerpt "readme_excerpts.dart (Pick)"?>
Expand Down
48 changes: 25 additions & 23 deletions packages/image_picker/image_picker/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -316,18 +316,19 @@ class _MyHomePageState extends State<MyHomePage> {
child: const Icon(Icons.photo_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'image2',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'image2',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
Expand All @@ -341,19 +342,20 @@ class _MyHomePageState extends State<MyHomePage> {
child: const Icon(Icons.video_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
),
),
),
],
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@

import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
// #docregion CameraDelegate
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
// #enddocregion CameraDelegate

/// Example of a camera delegate
// #docregion CameraDelegate
class MyCameraDelegate extends ImagePickerCameraDelegate {
@override
Future<XFile?> takePhoto(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return _takeAPhoto(options.preferredCameraDevice);
}

@override
Future<XFile?> takeVideo(
{ImagePickerCameraDelegateOptions options =
const ImagePickerCameraDelegateOptions()}) async {
return _takeAVideo(options.preferredCameraDevice);
}
}
// #enddocregion CameraDelegate

/// Example function for README demonstration of various pick* calls.
Future<List<XFile?>> readmePickExample() async {
Expand Down Expand Up @@ -49,6 +71,20 @@ Future<void> getLostData() async {
}
// #enddocregion LostData

/// Example of camera delegate setup.
// #docregion CameraDelegate
void setUpCameraDelegate() {
final ImagePickerPlatform instance = ImagePickerPlatform.instance;
if (instance is CameraDelegatingImagePickerPlatform) {
instance.cameraDelegate = MyCameraDelegate();
}
}
// #enddocregion CameraDelegate

// Stubs for the getLostData function.
void _handleLostFiles(List<XFile> file) {}
void _handleError(PlatformException? exception) {}

// Stubs for MyCameraDelegate.
Future<XFile?> _takeAPhoto(CameraDevice device) async => null;
Future<XFile?> _takeAVideo(CameraDevice device) async => null;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flutter/ephemeral
138 changes: 138 additions & 0 deletions packages/image_picker/image_picker/example/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "image_picker_example")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "dev.flutter.plugins.image_picker_example")

# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)

# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()

# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()

# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()

# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})

# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)

# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)


# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()

# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)

set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")

install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)

foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)

# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
Loading