fix: Récupérer l'opération active depuis la table operations
- Corrige l'erreur SQL 'Unknown column fk_operation in users' - L'opération active est récupérée depuis operations.chk_active = 1 - Jointure avec users pour filtrer par entité de l'admin créateur - Query: SELECT o.id FROM operations o INNER JOIN users u ON u.fk_entite = o.fk_entite WHERE u.id = ? AND o.chk_active = 1
This commit is contained in:
@@ -1 +0,0 @@
|
||||
/home/pierre/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.1+2/
|
||||
7
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS
Executable file
7
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS
Executable file
@@ -0,0 +1,7 @@
|
||||
# Below is a list of people and organizations that have contributed
|
||||
# to the Flutter project. Names should be added to the list like so:
|
||||
#
|
||||
# Name/Organization <email address>
|
||||
|
||||
Google Inc.
|
||||
Alexandre Zollinger Chohfi <alzollin@microsoft.com>
|
||||
17
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md
Executable file
17
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md
Executable file
@@ -0,0 +1,17 @@
|
||||
## 0.2.1+2
|
||||
|
||||
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.
|
||||
* Fixes `getMedia` mime types.
|
||||
|
||||
## 0.2.1+1
|
||||
|
||||
* Adds pub topics to package metadata.
|
||||
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Adds `getMedia` method.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Implements initial Linux support.
|
||||
25
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/LICENSE
Executable file
25
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/LICENSE
Executable file
@@ -0,0 +1,25 @@
|
||||
Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
27
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/README.md
Executable file
27
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/README.md
Executable file
@@ -0,0 +1,27 @@
|
||||
# image\_picker\_linux
|
||||
|
||||
A Linux implementation of [`image_picker`][1].
|
||||
|
||||
## Limitations
|
||||
|
||||
`ImageSource.camera` is not supported unless a `cameraDelegate` is set.
|
||||
|
||||
### pickImage()
|
||||
The arguments `maxWidth`, `maxHeight`, and `imageQuality` are not currently supported.
|
||||
|
||||
### pickVideo()
|
||||
The argument `maxDuration` is not currently supported.
|
||||
|
||||
## Usage
|
||||
|
||||
### Import the package
|
||||
|
||||
This package is [endorsed][2], which means you can simply use `file_selector`
|
||||
normally. This package will be automatically included in your app when you do,
|
||||
so you do not need to add it to your `pubspec.yaml`.
|
||||
|
||||
However, if you `import` this package to use any of its APIs directly, you
|
||||
should add it to your `pubspec.yaml` as usual.
|
||||
|
||||
[1]: https://pub.dev/packages/image_picker
|
||||
[2]: https://flutter.dev/to/endorsed-federated-plugin
|
||||
@@ -0,0 +1,9 @@
|
||||
# Platform Implementation Test App
|
||||
|
||||
This is a test app for manual testing and automated integration testing
|
||||
of this platform implementation. It is not intended to demonstrate actual use of
|
||||
this package, since the intent is that plugin clients use the app-facing
|
||||
package.
|
||||
|
||||
Unless you are making changes to this implementation package, this example is
|
||||
very unlikely to be relevant.
|
||||
@@ -0,0 +1,516 @@
|
||||
// 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.
|
||||
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(
|
||||
title: 'Image Picker Demo',
|
||||
home: MyHomePage(title: 'Image Picker Example'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, this.title});
|
||||
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
List<XFile>? _mediaFileList;
|
||||
|
||||
// This must be called from within a setState() callback
|
||||
void _setImageFileListFromFile(XFile? value) {
|
||||
_mediaFileList = value == null ? null : <XFile>[value];
|
||||
}
|
||||
|
||||
dynamic _pickImageError;
|
||||
bool _isVideo = false;
|
||||
|
||||
VideoPlayerController? _controller;
|
||||
VideoPlayerController? _toBeDisposed;
|
||||
String? _retrieveDataError;
|
||||
|
||||
final ImagePickerPlatform _picker = ImagePickerPlatform.instance;
|
||||
final TextEditingController maxWidthController = TextEditingController();
|
||||
final TextEditingController maxHeightController = TextEditingController();
|
||||
final TextEditingController qualityController = TextEditingController();
|
||||
|
||||
Future<void> _playVideo(XFile? file) async {
|
||||
if (file != null && mounted) {
|
||||
await _disposeVideoController();
|
||||
final VideoPlayerController controller =
|
||||
VideoPlayerController.file(File(file.path));
|
||||
_controller = controller;
|
||||
await controller.setVolume(1.0);
|
||||
await controller.initialize();
|
||||
await controller.setLooping(true);
|
||||
await controller.play();
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onImageButtonPressed(
|
||||
ImageSource source, {
|
||||
required BuildContext context,
|
||||
bool isMultiImage = false,
|
||||
bool isMedia = false,
|
||||
}) async {
|
||||
if (_controller != null) {
|
||||
await _controller!.setVolume(0.0);
|
||||
}
|
||||
if (context.mounted) {
|
||||
if (_isVideo) {
|
||||
final XFile? file = await _picker.getVideo(
|
||||
source: source, maxDuration: const Duration(seconds: 10));
|
||||
await _playVideo(file);
|
||||
} else if (isMultiImage) {
|
||||
await _displayPickImageDialog(context,
|
||||
(double? maxWidth, double? maxHeight, int? quality) async {
|
||||
try {
|
||||
final List<XFile> pickedFileList = isMedia
|
||||
? await _picker.getMedia(
|
||||
options: MediaOptions(
|
||||
allowMultiple: isMultiImage,
|
||||
imageOptions: ImageOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: quality,
|
||||
)),
|
||||
)
|
||||
: await _picker.getMultiImageWithOptions(
|
||||
options: MultiImagePickerOptions(
|
||||
imageOptions: ImageOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: quality,
|
||||
),
|
||||
),
|
||||
);
|
||||
setState(() {
|
||||
_mediaFileList = pickedFileList;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_pickImageError = e;
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (isMedia) {
|
||||
await _displayPickImageDialog(context,
|
||||
(double? maxWidth, double? maxHeight, int? quality) async {
|
||||
try {
|
||||
final List<XFile> pickedFileList = <XFile>[];
|
||||
final XFile? media = _firstOrNull(await _picker.getMedia(
|
||||
options: MediaOptions(
|
||||
allowMultiple: isMultiImage,
|
||||
imageOptions: ImageOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: quality,
|
||||
)),
|
||||
));
|
||||
|
||||
if (media != null) {
|
||||
pickedFileList.add(media);
|
||||
setState(() {
|
||||
_mediaFileList = pickedFileList;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() => _pickImageError = e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await _displayPickImageDialog(context,
|
||||
(double? maxWidth, double? maxHeight, int? quality) async {
|
||||
try {
|
||||
final XFile? pickedFile = await _picker.getImageFromSource(
|
||||
source: source,
|
||||
options: ImagePickerOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: quality,
|
||||
),
|
||||
);
|
||||
setState(() {
|
||||
_setImageFileListFromFile(pickedFile);
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_pickImageError = e;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
if (_controller != null) {
|
||||
_controller!.setVolume(0.0);
|
||||
_controller!.pause();
|
||||
}
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposeVideoController();
|
||||
maxWidthController.dispose();
|
||||
maxHeightController.dispose();
|
||||
qualityController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _disposeVideoController() async {
|
||||
if (_toBeDisposed != null) {
|
||||
await _toBeDisposed!.dispose();
|
||||
}
|
||||
_toBeDisposed = _controller;
|
||||
_controller = null;
|
||||
}
|
||||
|
||||
Widget _previewVideo() {
|
||||
final Text? retrieveError = _getRetrieveErrorWidget();
|
||||
if (retrieveError != null) {
|
||||
return retrieveError;
|
||||
}
|
||||
if (_controller == null) {
|
||||
return const Text(
|
||||
'You have not yet picked a video',
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AspectRatioVideo(_controller),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _previewImages() {
|
||||
final Text? retrieveError = _getRetrieveErrorWidget();
|
||||
if (retrieveError != null) {
|
||||
return retrieveError;
|
||||
}
|
||||
if (_mediaFileList != null) {
|
||||
return Semantics(
|
||||
label: 'image_picker_example_picked_images',
|
||||
child: ListView.builder(
|
||||
key: UniqueKey(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final String? mime = lookupMimeType(_mediaFileList![index].path);
|
||||
return Semantics(
|
||||
label: 'image_picker_example_picked_image',
|
||||
child: mime == null || mime.startsWith('image/')
|
||||
? Image.file(
|
||||
File(_mediaFileList![index].path),
|
||||
errorBuilder: (BuildContext context, Object error,
|
||||
StackTrace? stackTrace) {
|
||||
return const Center(
|
||||
child: Text('This image type is not supported'));
|
||||
},
|
||||
)
|
||||
: _buildInlineVideoPlayer(index),
|
||||
);
|
||||
},
|
||||
itemCount: _mediaFileList!.length,
|
||||
),
|
||||
);
|
||||
} else if (_pickImageError != null) {
|
||||
return Text(
|
||||
'Pick image error: $_pickImageError',
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
} else {
|
||||
return const Text(
|
||||
'You have not yet picked an image.',
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildInlineVideoPlayer(int index) {
|
||||
final VideoPlayerController controller =
|
||||
VideoPlayerController.file(File(_mediaFileList![index].path));
|
||||
const double volume = 1.0;
|
||||
controller.setVolume(volume);
|
||||
controller.initialize();
|
||||
controller.setLooping(true);
|
||||
controller.play();
|
||||
return Center(child: AspectRatioVideo(controller));
|
||||
}
|
||||
|
||||
Widget _handlePreview() {
|
||||
if (_isVideo) {
|
||||
return _previewVideo();
|
||||
} else {
|
||||
return _previewImages();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title!),
|
||||
),
|
||||
body: Center(
|
||||
child: _handlePreview(),
|
||||
),
|
||||
floatingActionButton: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Semantics(
|
||||
label: 'image_picker_example_from_gallery',
|
||||
child: FloatingActionButton(
|
||||
key: const Key('image_picker_example_from_gallery'),
|
||||
onPressed: () {
|
||||
_isVideo = false;
|
||||
_onImageButtonPressed(ImageSource.gallery, context: context);
|
||||
},
|
||||
heroTag: 'image0',
|
||||
tooltip: 'Pick Image from gallery',
|
||||
child: const Icon(Icons.photo),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: () {
|
||||
_isVideo = false;
|
||||
_onImageButtonPressed(
|
||||
ImageSource.gallery,
|
||||
context: context,
|
||||
isMultiImage: true,
|
||||
isMedia: true,
|
||||
);
|
||||
},
|
||||
heroTag: 'multipleMedia',
|
||||
tooltip: 'Pick Multiple Media from gallery',
|
||||
child: const Icon(Icons.photo_library),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: () {
|
||||
_isVideo = false;
|
||||
_onImageButtonPressed(
|
||||
ImageSource.gallery,
|
||||
context: context,
|
||||
isMedia: true,
|
||||
);
|
||||
},
|
||||
heroTag: 'media',
|
||||
tooltip: 'Pick Single Media from gallery',
|
||||
child: const Icon(Icons.photo_library),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: () {
|
||||
_isVideo = false;
|
||||
_onImageButtonPressed(
|
||||
ImageSource.gallery,
|
||||
context: context,
|
||||
isMultiImage: true,
|
||||
);
|
||||
},
|
||||
heroTag: 'image1',
|
||||
tooltip: 'Pick Multiple Image from gallery',
|
||||
child: const Icon(Icons.photo_library),
|
||||
),
|
||||
),
|
||||
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(
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
_isVideo = true;
|
||||
_onImageButtonPressed(ImageSource.gallery, context: context);
|
||||
},
|
||||
heroTag: 'video0',
|
||||
tooltip: 'Pick Video from gallery',
|
||||
child: const Icon(Icons.video_library),
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Text? _getRetrieveErrorWidget() {
|
||||
if (_retrieveDataError != null) {
|
||||
final Text result = Text(_retrieveDataError!);
|
||||
_retrieveDataError = null;
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _displayPickImageDialog(
|
||||
BuildContext context, OnPickImageCallback onPick) async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Add optional parameters'),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
controller: maxWidthController,
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Enter maxWidth if desired'),
|
||||
),
|
||||
TextField(
|
||||
controller: maxHeightController,
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Enter maxHeight if desired'),
|
||||
),
|
||||
TextField(
|
||||
controller: qualityController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Enter quality if desired'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('CANCEL'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('PICK'),
|
||||
onPressed: () {
|
||||
final double? width = maxWidthController.text.isNotEmpty
|
||||
? double.parse(maxWidthController.text)
|
||||
: null;
|
||||
final double? height = maxHeightController.text.isNotEmpty
|
||||
? double.parse(maxHeightController.text)
|
||||
: null;
|
||||
final int? quality = qualityController.text.isNotEmpty
|
||||
? int.parse(qualityController.text)
|
||||
: null;
|
||||
onPick(width, height, quality);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
typedef OnPickImageCallback = void Function(
|
||||
double? maxWidth, double? maxHeight, int? quality);
|
||||
|
||||
class AspectRatioVideo extends StatefulWidget {
|
||||
const AspectRatioVideo(this.controller, {super.key});
|
||||
|
||||
final VideoPlayerController? controller;
|
||||
|
||||
@override
|
||||
AspectRatioVideoState createState() => AspectRatioVideoState();
|
||||
}
|
||||
|
||||
class AspectRatioVideoState extends State<AspectRatioVideo> {
|
||||
VideoPlayerController? get controller => widget.controller;
|
||||
bool initialized = false;
|
||||
|
||||
void _onVideoControllerUpdate() {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
if (initialized != controller!.value.isInitialized) {
|
||||
initialized = controller!.value.isInitialized;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller!.addListener(_onVideoControllerUpdate);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller!.removeListener(_onVideoControllerUpdate);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (initialized) {
|
||||
return Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: controller!.value.aspectRatio,
|
||||
child: VideoPlayer(controller!),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T? _firstOrNull<T>(List<T> list) {
|
||||
return list.isEmpty ? null : list.first;
|
||||
}
|
||||
@@ -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 "example")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "dev.flutter.plugins.imagePickerExample")
|
||||
|
||||
# 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()
|
||||
@@ -0,0 +1,88 @@
|
||||
# This file controls Flutter-level build steps. It should not be edited.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
|
||||
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
|
||||
# which isn't available in 3.10.
|
||||
function(list_prepend LIST_NAME PREFIX)
|
||||
set(NEW_LIST "")
|
||||
foreach(element ${${LIST_NAME}})
|
||||
list(APPEND NEW_LIST "${PREFIX}${element}")
|
||||
endforeach(element)
|
||||
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# === Flutter Library ===
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
||||
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"fl_basic_message_channel.h"
|
||||
"fl_binary_codec.h"
|
||||
"fl_binary_messenger.h"
|
||||
"fl_dart_project.h"
|
||||
"fl_engine.h"
|
||||
"fl_json_message_codec.h"
|
||||
"fl_json_method_codec.h"
|
||||
"fl_message_codec.h"
|
||||
"fl_method_call.h"
|
||||
"fl_method_channel.h"
|
||||
"fl_method_codec.h"
|
||||
"fl_method_response.h"
|
||||
"fl_plugin_registrar.h"
|
||||
"fl_plugin_registry.h"
|
||||
"fl_standard_message_codec.h"
|
||||
"fl_standard_method_codec.h"
|
||||
"fl_string_codec.h"
|
||||
"fl_value.h"
|
||||
"fl_view.h"
|
||||
"flutter_linux.h"
|
||||
)
|
||||
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
|
||||
target_link_libraries(flutter INTERFACE
|
||||
PkgConfig::GTK
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GIO
|
||||
)
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/_phony_
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
|
||||
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#include "my_application.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
g_autoptr(MyApplication) app = my_application_new();
|
||||
return g_application_run(G_APPLICATION(app), argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
#include "my_application.h"
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
struct _MyApplication {
|
||||
GtkApplication parent_instance;
|
||||
char** dart_entrypoint_arguments;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||
|
||||
// Implements GApplication::activate.
|
||||
static void my_application_activate(GApplication* application) {
|
||||
MyApplication* self = MY_APPLICATION(application);
|
||||
GtkWindow* window =
|
||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||
|
||||
// Use a header bar when running in GNOME as this is the common style used
|
||||
// by applications and is the setup most users will be using (e.g. Ubuntu
|
||||
// desktop).
|
||||
// If running on X and not using GNOME then just use a traditional title bar
|
||||
// in case the window manager does more exotic layout, e.g. tiling.
|
||||
// If running on Wayland assume the header bar will work (may need changing
|
||||
// if future cases occur).
|
||||
gboolean use_header_bar = TRUE;
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
if (GDK_IS_X11_SCREEN(screen)) {
|
||||
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
|
||||
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
|
||||
use_header_bar = FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (use_header_bar) {
|
||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||
gtk_header_bar_set_title(header_bar, "example");
|
||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||
} else {
|
||||
gtk_window_set_title(window, "example");
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 1280, 720);
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
|
||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||
fl_dart_project_set_dart_entrypoint_arguments(
|
||||
project, self->dart_entrypoint_arguments);
|
||||
|
||||
FlView* view = fl_view_new(project);
|
||||
gtk_widget_show(GTK_WIDGET(view));
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
|
||||
|
||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||
|
||||
gtk_widget_grab_focus(GTK_WIDGET(view));
|
||||
}
|
||||
|
||||
// Implements GApplication::local_command_line.
|
||||
static gboolean my_application_local_command_line(GApplication* application,
|
||||
gchar*** arguments,
|
||||
int* exit_status) {
|
||||
MyApplication* self = MY_APPLICATION(application);
|
||||
// Strip out the first argument as it is the binary name.
|
||||
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
|
||||
|
||||
g_autoptr(GError) error = nullptr;
|
||||
if (!g_application_register(application, nullptr, &error)) {
|
||||
g_warning("Failed to register: %s", error->message);
|
||||
*exit_status = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_application_activate(application);
|
||||
*exit_status = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements GObject::dispose.
|
||||
static void my_application_dispose(GObject* object) {
|
||||
MyApplication* self = MY_APPLICATION(object);
|
||||
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
|
||||
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void my_application_class_init(MyApplicationClass* klass) {
|
||||
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
|
||||
G_APPLICATION_CLASS(klass)->local_command_line =
|
||||
my_application_local_command_line;
|
||||
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
|
||||
}
|
||||
|
||||
static void my_application_init(MyApplication* self) {}
|
||||
|
||||
MyApplication* my_application_new() {
|
||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||
"application-id", APPLICATION_ID, "flags",
|
||||
G_APPLICATION_NON_UNIQUE, nullptr));
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_MY_APPLICATION_H_
|
||||
#define FLUTTER_MY_APPLICATION_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
|
||||
GtkApplication)
|
||||
|
||||
/**
|
||||
* my_application_new:
|
||||
*
|
||||
* Creates a new Flutter-based application.
|
||||
*
|
||||
* Returns: a new #MyApplication.
|
||||
*/
|
||||
MyApplication* my_application_new();
|
||||
|
||||
#endif // FLUTTER_MY_APPLICATION_H_
|
||||
@@ -0,0 +1,29 @@
|
||||
name: example
|
||||
description: Example for image_picker_linux implementation.
|
||||
publish_to: 'none'
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.4.0
|
||||
flutter: ">=3.22.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
image_picker_linux:
|
||||
# When depending on this package from a real application you should use:
|
||||
# image_picker_linux: ^x.y.z
|
||||
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ..
|
||||
image_picker_platform_interface: ^2.8.0
|
||||
mime: ^2.0.0
|
||||
video_player: ^2.1.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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:file_selector_linux/file_selector_linux.dart';
|
||||
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
||||
|
||||
/// The Linux implementation of [ImagePickerPlatform].
|
||||
///
|
||||
/// This class implements the `package:image_picker` functionality for
|
||||
/// Linux.
|
||||
class ImagePickerLinux extends CameraDelegatingImagePickerPlatform {
|
||||
/// Constructs a platform implementation.
|
||||
ImagePickerLinux();
|
||||
|
||||
/// The file selector used to prompt the user to select images or videos.
|
||||
@visibleForTesting
|
||||
static FileSelectorPlatform fileSelector = FileSelectorLinux();
|
||||
|
||||
/// Registers this class as the default instance of [ImagePickerPlatform].
|
||||
static void registerWith() {
|
||||
ImagePickerPlatform.instance = ImagePickerLinux();
|
||||
}
|
||||
|
||||
// This is soft-deprecated in the platform interface, and is only implemented
|
||||
// for compatibility. Callers should be using getImageFromSource.
|
||||
@override
|
||||
Future<PickedFile?> pickImage({
|
||||
required ImageSource source,
|
||||
double? maxWidth,
|
||||
double? maxHeight,
|
||||
int? imageQuality,
|
||||
CameraDevice preferredCameraDevice = CameraDevice.rear,
|
||||
}) async {
|
||||
final XFile? file = await getImageFromSource(
|
||||
source: source,
|
||||
options: ImagePickerOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: imageQuality,
|
||||
preferredCameraDevice: preferredCameraDevice));
|
||||
if (file != null) {
|
||||
return PickedFile(file.path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is soft-deprecated in the platform interface, and is only implemented
|
||||
// for compatibility. Callers should be using getVideo.
|
||||
@override
|
||||
Future<PickedFile?> pickVideo({
|
||||
required ImageSource source,
|
||||
CameraDevice preferredCameraDevice = CameraDevice.rear,
|
||||
Duration? maxDuration,
|
||||
}) async {
|
||||
final XFile? file = await getVideo(
|
||||
source: source,
|
||||
preferredCameraDevice: preferredCameraDevice,
|
||||
maxDuration: maxDuration);
|
||||
if (file != null) {
|
||||
return PickedFile(file.path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is soft-deprecated in the platform interface, and is only implemented
|
||||
// for compatibility. Callers should be using getImageFromSource.
|
||||
@override
|
||||
Future<XFile?> getImage({
|
||||
required ImageSource source,
|
||||
double? maxWidth,
|
||||
double? maxHeight,
|
||||
int? imageQuality,
|
||||
CameraDevice preferredCameraDevice = CameraDevice.rear,
|
||||
}) async {
|
||||
return getImageFromSource(
|
||||
source: source,
|
||||
options: ImagePickerOptions(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
imageQuality: imageQuality,
|
||||
preferredCameraDevice: preferredCameraDevice));
|
||||
}
|
||||
|
||||
// [ImagePickerOptions] options are not currently supported. If any
|
||||
// of its fields are set, they will be silently ignored.
|
||||
//
|
||||
// If source is `ImageSource.camera`, a `StateError` will be thrown
|
||||
// unless a [cameraDelegate] is set.
|
||||
@override
|
||||
Future<XFile?> getImageFromSource({
|
||||
required ImageSource source,
|
||||
ImagePickerOptions options = const ImagePickerOptions(),
|
||||
}) async {
|
||||
switch (source) {
|
||||
case ImageSource.camera:
|
||||
return super.getImageFromSource(source: source);
|
||||
case ImageSource.gallery:
|
||||
const XTypeGroup typeGroup =
|
||||
XTypeGroup(label: 'Images', mimeTypes: <String>['image/*']);
|
||||
final XFile? file = await fileSelector
|
||||
.openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
|
||||
return file;
|
||||
}
|
||||
// Ensure that there's a fallback in case a new source is added.
|
||||
// ignore: dead_code
|
||||
throw UnimplementedError('Unknown ImageSource: $source');
|
||||
}
|
||||
|
||||
// `preferredCameraDevice` and `maxDuration` arguments are not currently
|
||||
// supported. If either of these arguments are supplied, they will be silently
|
||||
// ignored.
|
||||
//
|
||||
// If source is `ImageSource.camera`, a `StateError` will be thrown
|
||||
// unless a [cameraDelegate] is set.
|
||||
@override
|
||||
Future<XFile?> getVideo({
|
||||
required ImageSource source,
|
||||
CameraDevice preferredCameraDevice = CameraDevice.rear,
|
||||
Duration? maxDuration,
|
||||
}) async {
|
||||
switch (source) {
|
||||
case ImageSource.camera:
|
||||
return super.getVideo(
|
||||
source: source,
|
||||
preferredCameraDevice: preferredCameraDevice,
|
||||
maxDuration: maxDuration);
|
||||
case ImageSource.gallery:
|
||||
const XTypeGroup typeGroup =
|
||||
XTypeGroup(label: 'Videos', mimeTypes: <String>['video/*']);
|
||||
final XFile? file = await fileSelector
|
||||
.openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
|
||||
return file;
|
||||
}
|
||||
// Ensure that there's a fallback in case a new source is added.
|
||||
// ignore: dead_code
|
||||
throw UnimplementedError('Unknown ImageSource: $source');
|
||||
}
|
||||
|
||||
// `maxWidth`, `maxHeight`, and `imageQuality` arguments are not currently
|
||||
// supported. If any of these arguments are supplied, they will be silently
|
||||
// ignored.
|
||||
@override
|
||||
Future<List<XFile>> getMultiImage({
|
||||
double? maxWidth,
|
||||
double? maxHeight,
|
||||
int? imageQuality,
|
||||
}) async {
|
||||
const XTypeGroup typeGroup =
|
||||
XTypeGroup(label: 'Images', mimeTypes: <String>['image/*']);
|
||||
final List<XFile> files = await fileSelector
|
||||
.openFiles(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
|
||||
return files;
|
||||
}
|
||||
|
||||
// `maxWidth`, `maxHeight`, and `imageQuality` arguments are not currently
|
||||
// supported. If any of these arguments are supplied, they will be silently
|
||||
// ignored.
|
||||
@override
|
||||
Future<List<XFile>> getMedia({required MediaOptions options}) async {
|
||||
const XTypeGroup typeGroup = XTypeGroup(
|
||||
label: 'Images and videos',
|
||||
mimeTypes: <String>['image/*', 'video/*'],
|
||||
);
|
||||
|
||||
List<XFile> files;
|
||||
|
||||
if (options.allowMultiple) {
|
||||
files = await fileSelector
|
||||
.openFiles(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
|
||||
} else {
|
||||
final XFile? file = await fileSelector
|
||||
.openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
|
||||
files = <XFile>[
|
||||
if (file != null) file,
|
||||
];
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
34
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml
Executable file
34
app/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml
Executable file
@@ -0,0 +1,34 @@
|
||||
name: image_picker_linux
|
||||
description: Linux platform implementation of image_picker
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_linux
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
|
||||
version: 0.2.1+2
|
||||
|
||||
environment:
|
||||
sdk: ^3.4.0
|
||||
flutter: ">=3.22.0"
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: image_picker
|
||||
platforms:
|
||||
linux:
|
||||
dartPluginClass: ImagePickerLinux
|
||||
|
||||
dependencies:
|
||||
file_selector_linux: ^0.9.1+3
|
||||
file_selector_platform_interface: ^2.2.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
image_picker_platform_interface: ^2.8.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.1.5
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.4.4
|
||||
|
||||
topics:
|
||||
- image-picker
|
||||
- files
|
||||
- file-selection
|
||||
@@ -0,0 +1,180 @@
|
||||
// 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:file_selector_platform_interface/file_selector_platform_interface.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:image_picker_linux/image_picker_linux.dart';
|
||||
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'image_picker_linux_test.mocks.dart';
|
||||
|
||||
@GenerateMocks(<Type>[FileSelectorPlatform])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Returns the captured type groups from a mock call result, assuming that
|
||||
// exactly one call was made and only the type groups were captured.
|
||||
List<XTypeGroup> capturedTypeGroups(VerificationResult result) {
|
||||
return result.captured.single as List<XTypeGroup>;
|
||||
}
|
||||
|
||||
late ImagePickerLinux plugin;
|
||||
late MockFileSelectorPlatform mockFileSelectorPlatform;
|
||||
|
||||
setUp(() {
|
||||
plugin = ImagePickerLinux();
|
||||
mockFileSelectorPlatform = MockFileSelectorPlatform();
|
||||
|
||||
when(mockFileSelectorPlatform.openFile(
|
||||
acceptedTypeGroups: anyNamed('acceptedTypeGroups')))
|
||||
.thenAnswer((_) async => null);
|
||||
|
||||
when(mockFileSelectorPlatform.openFiles(
|
||||
acceptedTypeGroups: anyNamed('acceptedTypeGroups')))
|
||||
.thenAnswer((_) async => List<XFile>.empty());
|
||||
|
||||
ImagePickerLinux.fileSelector = mockFileSelectorPlatform;
|
||||
});
|
||||
|
||||
test('registered instance', () {
|
||||
ImagePickerLinux.registerWith();
|
||||
expect(ImagePickerPlatform.instance, isA<ImagePickerLinux>());
|
||||
});
|
||||
|
||||
group('images', () {
|
||||
test('pickImage passes the accepted type groups correctly', () async {
|
||||
await plugin.pickImage(source: ImageSource.gallery);
|
||||
|
||||
final VerificationResult result = verify(mockFileSelectorPlatform
|
||||
.openFile(acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['image/*']);
|
||||
});
|
||||
|
||||
test('getImage passes the accepted type groups correctly', () async {
|
||||
await plugin.getImage(source: ImageSource.gallery);
|
||||
|
||||
final VerificationResult result = verify(mockFileSelectorPlatform
|
||||
.openFile(acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['image/*']);
|
||||
});
|
||||
|
||||
test('getImageFromSource passes the accepted type groups correctly',
|
||||
() async {
|
||||
await plugin.getImageFromSource(source: ImageSource.gallery);
|
||||
|
||||
final VerificationResult result = verify(mockFileSelectorPlatform
|
||||
.openFile(acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['image/*']);
|
||||
});
|
||||
|
||||
test('getImageFromSource calls delegate when source is camera', () async {
|
||||
const String fakePath = '/tmp/foo';
|
||||
plugin.cameraDelegate = FakeCameraDelegate(result: XFile(fakePath));
|
||||
expect(
|
||||
(await plugin.getImageFromSource(source: ImageSource.camera))!.path,
|
||||
fakePath);
|
||||
});
|
||||
|
||||
test(
|
||||
'getImageFromSource throws StateError when source is camera with no delegate',
|
||||
() async {
|
||||
await expectLater(plugin.getImageFromSource(source: ImageSource.camera),
|
||||
throwsStateError);
|
||||
});
|
||||
|
||||
test('getMultiImage passes the accepted type groups correctly', () async {
|
||||
await plugin.getMultiImage();
|
||||
|
||||
final VerificationResult result = verify(
|
||||
mockFileSelectorPlatform.openFiles(
|
||||
acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['image/*']);
|
||||
});
|
||||
});
|
||||
|
||||
group('videos', () {
|
||||
test('pickVideo passes the accepted type groups correctly', () async {
|
||||
await plugin.pickVideo(source: ImageSource.gallery);
|
||||
|
||||
final VerificationResult result = verify(mockFileSelectorPlatform
|
||||
.openFile(acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['video/*']);
|
||||
});
|
||||
|
||||
test('getVideo passes the accepted type groups correctly', () async {
|
||||
await plugin.getVideo(source: ImageSource.gallery);
|
||||
|
||||
final VerificationResult result = verify(mockFileSelectorPlatform
|
||||
.openFile(acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes, <String>['video/*']);
|
||||
});
|
||||
|
||||
test('getVideo calls delegate when source is camera', () async {
|
||||
const String fakePath = '/tmp/foo';
|
||||
plugin.cameraDelegate = FakeCameraDelegate(result: XFile(fakePath));
|
||||
expect(
|
||||
(await plugin.getVideo(source: ImageSource.camera))!.path, fakePath);
|
||||
});
|
||||
|
||||
test('getVideo throws StateError when source is camera with no delegate',
|
||||
() async {
|
||||
await expectLater(
|
||||
plugin.getVideo(source: ImageSource.camera), throwsStateError);
|
||||
});
|
||||
});
|
||||
|
||||
group('media', () {
|
||||
test('getMedia passes the accepted type groups correctly', () async {
|
||||
await plugin.getMedia(options: const MediaOptions(allowMultiple: true));
|
||||
|
||||
final VerificationResult result = verify(
|
||||
mockFileSelectorPlatform.openFiles(
|
||||
acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups')));
|
||||
expect(capturedTypeGroups(result)[0].mimeTypes,
|
||||
<String>['image/*', 'video/*']);
|
||||
});
|
||||
|
||||
test('multiple media handles an empty path response gracefully', () async {
|
||||
expect(
|
||||
await plugin.getMedia(
|
||||
options: const MediaOptions(
|
||||
allowMultiple: true,
|
||||
),
|
||||
),
|
||||
<String>[]);
|
||||
});
|
||||
|
||||
test('single media handles an empty path response gracefully', () async {
|
||||
expect(
|
||||
await plugin.getMedia(
|
||||
options: const MediaOptions(
|
||||
allowMultiple: false,
|
||||
),
|
||||
),
|
||||
<String>[]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Mocks generated by Mockito 5.4.4 from annotations
|
||||
// in image_picker_linux/test/image_picker_linux_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i3;
|
||||
|
||||
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'
|
||||
as _i2;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
|
||||
/// A class which mocks [FileSelectorPlatform].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockFileSelectorPlatform extends _i1.Mock
|
||||
implements _i2.FileSelectorPlatform {
|
||||
MockFileSelectorPlatform() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i3.Future<_i2.XFile?> openFile({
|
||||
List<_i2.XTypeGroup>? acceptedTypeGroups,
|
||||
String? initialDirectory,
|
||||
String? confirmButtonText,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#openFile,
|
||||
[],
|
||||
{
|
||||
#acceptedTypeGroups: acceptedTypeGroups,
|
||||
#initialDirectory: initialDirectory,
|
||||
#confirmButtonText: confirmButtonText,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<_i2.XFile?>.value(),
|
||||
) as _i3.Future<_i2.XFile?>);
|
||||
|
||||
@override
|
||||
_i3.Future<List<_i2.XFile>> openFiles({
|
||||
List<_i2.XTypeGroup>? acceptedTypeGroups,
|
||||
String? initialDirectory,
|
||||
String? confirmButtonText,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#openFiles,
|
||||
[],
|
||||
{
|
||||
#acceptedTypeGroups: acceptedTypeGroups,
|
||||
#initialDirectory: initialDirectory,
|
||||
#confirmButtonText: confirmButtonText,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<List<_i2.XFile>>.value(<_i2.XFile>[]),
|
||||
) as _i3.Future<List<_i2.XFile>>);
|
||||
|
||||
@override
|
||||
_i3.Future<String?> getSavePath({
|
||||
List<_i2.XTypeGroup>? acceptedTypeGroups,
|
||||
String? initialDirectory,
|
||||
String? suggestedName,
|
||||
String? confirmButtonText,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getSavePath,
|
||||
[],
|
||||
{
|
||||
#acceptedTypeGroups: acceptedTypeGroups,
|
||||
#initialDirectory: initialDirectory,
|
||||
#suggestedName: suggestedName,
|
||||
#confirmButtonText: confirmButtonText,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<String?>.value(),
|
||||
) as _i3.Future<String?>);
|
||||
|
||||
@override
|
||||
_i3.Future<_i2.FileSaveLocation?> getSaveLocation({
|
||||
List<_i2.XTypeGroup>? acceptedTypeGroups,
|
||||
_i2.SaveDialogOptions? options = const _i2.SaveDialogOptions(),
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getSaveLocation,
|
||||
[],
|
||||
{
|
||||
#acceptedTypeGroups: acceptedTypeGroups,
|
||||
#options: options,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<_i2.FileSaveLocation?>.value(),
|
||||
) as _i3.Future<_i2.FileSaveLocation?>);
|
||||
|
||||
@override
|
||||
_i3.Future<String?> getDirectoryPath({
|
||||
String? initialDirectory,
|
||||
String? confirmButtonText,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getDirectoryPath,
|
||||
[],
|
||||
{
|
||||
#initialDirectory: initialDirectory,
|
||||
#confirmButtonText: confirmButtonText,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<String?>.value(),
|
||||
) as _i3.Future<String?>);
|
||||
|
||||
@override
|
||||
_i3.Future<List<String>> getDirectoryPaths({
|
||||
String? initialDirectory,
|
||||
String? confirmButtonText,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getDirectoryPaths,
|
||||
[],
|
||||
{
|
||||
#initialDirectory: initialDirectory,
|
||||
#confirmButtonText: confirmButtonText,
|
||||
},
|
||||
),
|
||||
returnValue: _i3.Future<List<String>>.value(<String>[]),
|
||||
) as _i3.Future<List<String>>);
|
||||
}
|
||||
Reference in New Issue
Block a user