Compare commits

..

21 commits

Author SHA1 Message Date
mike doornenbal
d8e824191a
Merge pull request #36 from Iconica-Development/feature/error_handling
feat: error handling
2024-09-05 11:25:03 +02:00
mike doornenbal
a6f50c54d8 feat: add support for handling errors 2024-09-05 10:33:26 +02:00
mike doornenbal
004f0067a4 feat: add default styling and title 2024-08-08 10:42:33 +02:00
Gorter-dev
204e375cb8
Merge pull request #34 from Iconica-Development/chore/deploy
chore: ready the package for deployment to the pub server
2024-07-22 15:00:42 +02:00
6221ed816f chore: ready the package for deployment to the pub server 2024-07-19 11:25:59 +02:00
e208c5031c chore: add fvm configuration to gitignore 2024-07-19 11:24:27 +02:00
Jacques Doeleman
a80bf1fb87
Merge pull request #29 from Iconica-Development/bugfix/customButton
fix: removed customButton
2024-02-28 13:09:46 +01:00
mike doornenbal
b3aa961e92 fix: removed customButton 2024-02-28 12:00:33 +01:00
Gorter-dev
9131a82501
Merge pull request #27 from Iconica-Development/bugfix/simple_theme
bugfix: SImplify ImagePickerTheme
2024-02-27 16:27:38 +01:00
Jacques
c7114d56ca bugfix: SImplify ImagePickerTheme 2024-02-22 16:23:09 +01:00
Freek van de Ven
39f5e82a48
Merge pull request #26 from Iconica-Development/update-component-documentation-workflow-correct
Add component-documentation.yml correct
2024-02-14 08:04:35 +01:00
Vick Top
75f3aac3f2 feat(documentation): Create component-documentation.yml workflow file 2024-02-13 13:35:27 +01:00
Vick Top
27d8fcd609 chore: Remove old component-documentation.yml 2024-02-13 13:35:27 +01:00
Freek van de Ven
f0bac29e7f
Merge pull request #25 from Iconica-Development/update-component-documentation-workflow
Add component-documentation.yml
2024-02-12 20:14:57 +01:00
Vick Top
fdc556c476 feat(documentation): Create component-documentation.yml workflow file 2024-02-12 19:07:10 +01:00
Freek van de Ven
29f08a94ea
Merge pull request #24 from Iconica-Development/fix/image_picker_version
fix: imagepicker version
2024-02-07 10:59:07 +01:00
mike doornenbal
7743745c37 fix: imagepicker version 2024-02-07 10:46:02 +01:00
Freek van de Ven
1d0efbdbd0
Merge pull request #23 from Iconica-Development/fix/add_ci_linter
fix: add ci and linter
2024-02-07 10:39:12 +01:00
mike doornenbal
9e3eaff8c2 merged add figma link in this branch 2024-02-07 10:35:26 +01:00
mike doornenbal
455fc691a2 fix: add ci and linter 2024-02-07 09:51:48 +01:00
Freek van de Ven
7bdce96cf5 chore: add figma links 2023-12-17 14:33:25 +01:00
23 changed files with 351 additions and 597 deletions

14
.github/workflows/component-ci.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Iconica Standard Component CI Workflow
# Workflow Caller version: 2.0.0
on:
pull_request:
workflow_dispatch:
jobs:
call-global-iconica-workflow:
uses: Iconica-Development/.github/.github/workflows/component-ci.yml@master
secrets: inherit
permissions: write-all
with:
subfolder: "." # add optional subfolder to run workflow in

View file

@ -0,0 +1,14 @@
name: Iconica Standard Component Documentation Workflow
# Workflow Caller version: 1.0.0
on:
release:
types: [published]
workflow_dispatch:
jobs:
call-iconica-component-documentation-workflow:
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
secrets: inherit
permissions: write-all

View file

@ -1,32 +0,0 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches:
- master
- feature/*
- bugfix/*
- hotfix/*
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.gradle/wrapper
/opt/hostedtoolcache/flutter
key: ${{ runner.OS }}-flutter-install-cache
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Flutter pub get
run: flutter pub get
- name: Flutter format
run: flutter format -o none --set-exit-if-changed .
- name: Flutter analyze
run: flutter analyze

4
.gitignore vendored
View file

@ -31,3 +31,7 @@ build/
coverage/ coverage/
/.flutter-plugins /.flutter-plugins
/.flutter-plugins-dependencies /.flutter-plugins-dependencies
# FVM Version Cache
.fvm/
.fvmrc

View file

@ -27,4 +27,24 @@
## 1.0.4 - April 4th 2023 ## 1.0.4 - April 4th 2023
- Make camera option optional - Make camera option optional
## 1.0.5 - February 7th 2024
- Added CI and linter
## 2.0.0 - February 22nd 2024
- Simplified the ImagePickerTheme
# 3.0.0 - February 22nd 2024
- Removed customButton from ImagePicker.
# 4.0.0
* Added title options to theme.
* updated iconica_analysis dependency.
* Updated default theme.
# 4.1.0
* Added support for handling errors when calling `pickImage`.

View file

@ -6,6 +6,9 @@ Image Picker that can be used to pick an image from storage or make a picture wi
![Image Picker GIF](flutter_image_picker.gif) ![Image Picker GIF](flutter_image_picker.gif)
Figma Design that defines this component (only accessible for Iconica developers): https://www.figma.com/file/4WkjwynOz5wFeFBRqTHPeP/Iconica-Design-System?type=design&node-id=357%3A3354&mode=design&t=XulkAJNPQ32ARxWh-1
Figma clickable prototype that demonstrates this component (only accessible for Iconica developers): https://www.figma.com/proto/4WkjwynOz5wFeFBRqTHPeP/Iconica-Design-System?type=design&node-id=340-611&viewport=188%2C-512%2C0.05&t=XulkAJNPQ32ARxWh-0&scaling=min-zoom&starting-point-node-id=516%3A2544&show-proto-sidebar=1
## Setup ## Setup
To use this package, add `flutter_image_picker` as a dependency in your pubspec.yaml file. To use this package, add `flutter_image_picker` as a dependency in your pubspec.yaml file.

View file

@ -1,4 +1,9 @@
include: package:flutter_lints/flutter.yaml include: package:flutter_iconica_analysis/analysis_options.yaml
# Additional information about this file can be found at # Possible to overwrite the rules from the package
# https://dart.dev/guides/language/analysis-options
analyzer:
exclude:
linter:
rules:

1
example/.gitignore vendored
View file

@ -31,6 +31,7 @@ migrate_working_dir/
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
pubspec.lock
# Web related # Web related
lib/generated_plugin_registrant.dart lib/generated_plugin_registrant.dart

View file

@ -11,7 +11,9 @@ void main() {
} }
class ImagePickerExample extends StatelessWidget { class ImagePickerExample extends StatelessWidget {
const ImagePickerExample({Key? key}) : super(key: key); const ImagePickerExample({
super.key,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -27,8 +29,10 @@ class ImagePickerExample extends StatelessWidget {
} }
class ImagePickerExampleHomePage extends StatefulWidget { class ImagePickerExampleHomePage extends StatefulWidget {
const ImagePickerExampleHomePage({Key? key, required this.title}) const ImagePickerExampleHomePage({
: super(key: key); required this.title,
super.key,
});
final String title; final String title;
@ -112,7 +116,13 @@ class ImagePickerExampleHomePageState
Uint8List? imageInBytes = await showModalBottomSheet<Uint8List?>( Uint8List? imageInBytes = await showModalBottomSheet<Uint8List?>(
context: context, context: context,
backgroundColor: Colors.white, backgroundColor: Colors.white,
builder: (BuildContext context) => const ImagePicker()); builder: (BuildContext context) => ImagePicker(
onError: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(error.message ?? "An error occurred")),
);
},
));
if (imageInBytes != null) { if (imageInBytes != null) {
if (!listEquals(uploadedImage, imageInBytes)) { if (!listEquals(uploadedImage, imageInBytes)) {
setState(() { setState(() {

View file

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
} }

View file

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View file

@ -5,6 +5,8 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import file_selector_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
} }

View file

@ -1,289 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.10.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
url: "https://pub.dev"
source: hosted
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
url: "https://pub.dev"
source: hosted
version: "1.17.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7632a2bcddc8cef4afde3c6f80e69b29a7060e176f01119c229fe4eb3a2a3d4f"
url: "https://pub.dev"
source: hosted
version: "0.3.3+1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
url: "https://pub.dev"
source: hosted
version: "1.0.5"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_image_picker:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "1.0.4"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
url: "https://pub.dev"
source: hosted
version: "2.0.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: db3060f22889f3d9d55f6a217565486737037eec3609f7f3eca4d0c67ee0d8a0
url: "https://pub.dev"
source: hosted
version: "4.0.1"
image_picker:
dependency: transitive
description:
name: image_picker
sha256: a8f2f0aed50c03230ab37e93ca2905c50b6c4097245345956eb24a88f45328cd
url: "https://pub.dev"
source: hosted
version: "0.8.6"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: "8243d3324e28806671feb358e3756528bae6f63dacafaecf896e6b45b167def9"
url: "https://pub.dev"
source: hosted
version: "0.8.5+2"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "60f306ffbdcada4bc8b2691acc420258a1b758e102c87c4f94fb568d640f0e0e"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: "1768087441bd69ca632249d212c26fa8d530552d37b4896a4dd8d6781435c147"
url: "https://pub.dev"
source: hosted
version: "0.8.6+1"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: cedb2650709f066ee83fd65a1d99b6ff33e5adf98cea376426dd33c2b22bf6ad
url: "https://pub.dev"
source: hosted
version: "2.6.1"
js:
dependency: transitive
description:
name: js
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
lints:
dependency: transitive
description:
name: lints
sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
url: "https://pub.dev"
source: hosted
version: "0.12.13"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
path:
dependency: transitive
description:
name: path
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
url: "https://pub.dev"
source: hosted
version: "1.8.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "075f927ebbab4262ace8d0b283929ac5410c0ac4e7fc123c76429564facfb757"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
url: "https://pub.dev"
source: hosted
version: "0.4.16"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
sdks:
dart: ">=2.18.0 <3.0.0"
flutter: ">=2.10.0"

View file

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
} }

View file

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View file

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: 2022 Iconica // SPDX-FileCopyrightText: 2022 Iconica
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
///
library flutter_image_picker; library flutter_image_picker;
export 'src/services/image_picker_service.dart'; export "src/models/image_picker_config.dart";
export 'src/models/image_picker_theme.dart'; export "src/models/image_picker_theme.dart";
export 'src/models/image_picker_config.dart'; export "src/services/image_picker_service.dart";
export 'src/ui/image_picker.dart'; export "src/ui/image_picker.dart";

View file

@ -10,11 +10,12 @@ class ImagePickerConfig {
/// If specified, the image will be at most `maxWidth` wide and /// If specified, the image will be at most `maxWidth` wide and
/// `maxHeight` tall. Otherwise the image will be returned at it's /// `maxHeight` tall. Otherwise the image will be returned at it's
/// original width and height. /// original width and height.
/// The `imageQuality` argument modifies the quality of the image, ranging from 0-100 /// The `imageQuality` argument modifies the quality of the image, ranging
/// where 100 is the original/max quality. If `imageQuality` is null, the image with /// from 0-100 where 100 is the original/max quality. If `imageQuality` is null, the image with
/// the original quality will be returned. Compression is only supported for certain /// the original quality will be returned. Compression is only supported
/// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked, /// for certain image types such as JPEG and on Android PNG and WebP, too.
/// a warning message will be logged. /// If compression is not supported for the image that is picked, a warning
/// message will be logged.
final double? maxWidth; final double? maxWidth;
final double? maxHeight; final double? maxHeight;
final int? imageQuality; final int? imageQuality;

View file

@ -2,95 +2,57 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
class ImagePickerTheme { class ImagePickerTheme {
/// The [ImagePickerTheme] is used to style the [ImagePicker]. /// The [ImagePickerTheme] is used to style the [ImagePicker].
const ImagePickerTheme({ const ImagePickerTheme({
this.font = "Roboto",
this.title = "Upload Image",
this.titleTextSize = 20,
this.titleColor = Colors.black,
this.titleBackgroundColor = Colors.white,
this.titleAlignment = TextAlign.left,
this.textColor = Colors.black,
this.iconColor = Colors.black, this.iconColor = Colors.black,
this.iconSize = 125, this.iconSize = 125,
this.iconTextSize = 15,
this.spaceBetweenIcons = 30, this.spaceBetweenIcons = 30,
this.makePhotoIcon, this.makePhotoIcon,
this.makePhotoText = "Take a Picture", this.makePhotoText = "TAKE PICTURE",
this.selectImageIcon, this.selectImageIcon,
this.selectImageText = "Select File", this.selectImageText = "UPLOAD FILE",
this.closeButtonText = "Close", this.iconTextStyle,
this.closeButtonTextSize = 15, this.closeButtonBuilder,
this.closeButtonTextColor = Colors.white, this.title = "Do you want to upload a file or take a picture?",
this.closeButtonWidth = 300, this.titleStyle,
this.closeButtonHeight = 40, this.titleAlignment = TextAlign.center,
this.closeButtonBackgroundColor = Colors.black,
}); });
/// The font that's used in the Image Picker
final String font;
/// The title displayed at the top of the Image Picker Dialog.
final String title;
/// The font size of the title mentioned above.
final double titleTextSize;
/// The color of the title text.
final Color titleColor;
/// The color of the title background.
final Color titleBackgroundColor;
/// The alignment of the title text.
final TextAlign titleAlignment;
/// The color of the icons /// The color of the icons
final Color iconColor; final Color iconColor;
/// The color of the text in the Image Picker Dialog
final Color textColor;
/// The size of the icons that are visible in the Image Picker Dialog. /// The size of the icons that are visible in the Image Picker Dialog.
final double iconSize; final double iconSize;
/// The font size of the text underneath the icon buttons.
final double iconTextSize;
/// The size of the space between the two icons in the Image Picker Dialog. /// The size of the space between the two icons in the Image Picker Dialog.
final double spaceBetweenIcons; final double spaceBetweenIcons;
/// The icon that is displayed for the 'Make Photo' functionality of the Image Picker Dialog. /// The icon that is displayed for the 'Make Photo' functionality of the
/// Image Picker Dialog.
final Widget? makePhotoIcon; final Widget? makePhotoIcon;
/// The text that is displayed underneath the 'Make Photo' icon. /// The text that is displayed underneath the 'Make Photo' icon.
final String makePhotoText; final String makePhotoText;
/// The icon that is displayed for the 'Select Image From Gallery' functionality of the Image Picker Dialog. /// The icon that is displayed for the 'Select Image From Gallery'
/// functionality of the Image Picker Dialog.
final Widget? selectImageIcon; final Widget? selectImageIcon;
/// The text that is displayed underneath the 'Select Image From Gallery' icon. /// The text that is displayed underneath the 'Select Image From Gallery'
/// icon.
final String selectImageText; final String selectImageText;
/// The text that is shown on the 'Close Dialog' button at the bottom of the Image Picker Dialog. final TextStyle? iconTextStyle;
final String closeButtonText;
/// The fontsize of the text of the close button of the Image Picker Dialog. final Widget Function(Function() onTap)? closeButtonBuilder;
final double closeButtonTextSize;
/// The color of the text of the close button of the Image Picker Dialog. final String title;
final Color closeButtonTextColor;
/// The width of the 'Close Dialog' button at the bottom of the Image Picker Dialog. final TextStyle? titleStyle;
final double closeButtonWidth;
/// The height of the 'Close Dialog' button at the bottom of the Image Picker Dialog. final TextAlign titleAlignment;
final double closeButtonHeight;
/// The color of the close button of the Image Picker Dialog.
final Color closeButtonBackgroundColor;
} }

View file

@ -2,14 +2,17 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'dart:typed_data'; import "dart:typed_data";
import 'package:flutter_image_picker/src/models/image_picker_config.dart'; import "package:flutter_image_picker/src/models/image_picker_config.dart";
import 'package:image_picker/image_picker.dart'; import "package:image_picker/image_picker.dart";
/// The Image Picker Service class is the functionality of the Image Picker package which uses the Image Picker package to choose an image. /// The Image Picker Service class is the functionality of the Image Picker
/// If you have your own implementation of the Image Picker you can add it to the constructor when creating the class. /// package which uses the Image Picker package to choose an image.
abstract class ImagePickerService { /// If you have your own implementation of the Image Picker you can add it to
/// [pickImage] is the function that picks the image and returns it as a [Uint8List]. /// the constructor when creating the class.
mixin ImagePickerService {
/// [pickImage] is the function that picks the image and returns it as a
/// [Uint8List].
/// The function requires [source], an [ImageSource] /// The function requires [source], an [ImageSource]
Future<Uint8List?> pickImage( Future<Uint8List?> pickImage(
ImageSource source, { ImageSource source, {
@ -17,16 +20,21 @@ abstract class ImagePickerService {
}); });
} }
/// The ImagePickerServiceDefault is the default implementation of the ImagePickerService. /// The ImagePickerServiceDefault is the default implementation of the
/// It uses the Image Picker package to pick an image and returns it as a [Uint8List]. /// ImagePickerService.
/// It uses the Image Picker package to pick an image and returns it as a
/// [Uint8List].
class ImagePickerServiceDefault implements ImagePickerService { class ImagePickerServiceDefault implements ImagePickerService {
ImagePickerServiceDefault({this.imagePicker}); ImagePickerServiceDefault({this.imagePicker});
/// It's possible to have your own implementation for the Image Picker if you don't want to use the Image Picker Package. /// It's possible to have your own implementation for the Image Picker if you
/// don't want to use the Image Picker Package.
ImagePicker? imagePicker; ImagePicker? imagePicker;
/// [pickImage] is the function that picks the image and returns it as a [Uint8List]. /// [pickImage] is the function that picks the image and returns it as a
/// The function requires [source], an [ImageSource] that's the method of how the image needs to be picked, for example gallery or camera. /// [Uint8List].
/// The function requires [source], an [ImageSource] that's the method of how
/// the image needs to be picked, for example gallery or camera.
@override @override
Future<Uint8List?> pickImage( Future<Uint8List?> pickImage(
ImageSource source, { ImageSource source, {

View file

@ -2,155 +2,165 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_image_picker/flutter_image_picker.dart'; import "package:flutter/services.dart";
import 'package:image_picker/image_picker.dart'; import "package:flutter_image_picker/flutter_image_picker.dart";
import "package:image_picker/image_picker.dart";
/// The Image Picker class generates the Image Picker Widget which can be displayed in your application. If you call the class you can give it 4 optional variables: /// The Image Picker class generates the Image Picker Widget which can be
/// The first one is the [ImagePickerTheme] which can be used to change the UI of the widget. /// displayed in your application. If you call the class you can give it 4
/// The second one is the [ImagePickerConfig] which can be used to configure the behaviour of the image picker. /// optional variables:
/// The third one is your own implementation of the ImagePickerService. Which can be used in testing for example. /// The first one is the [ImagePickerTheme] which can be used to change the UI
/// of the widget.
/// The second one is the [ImagePickerConfig] which can be used to configure the
/// behaviour of the image picker.
/// The third one is your own implementation of the ImagePickerService. Which
/// can be used in testing for example.
/// The fourth one is a custom Button widget. /// The fourth one is a custom Button widget.
class ImagePicker extends StatelessWidget { class ImagePicker extends StatelessWidget {
const ImagePicker({ const ImagePicker({
this.imagePickerTheme = const ImagePickerTheme(), this.theme = const ImagePickerTheme(),
this.imagePickerConfig = const ImagePickerConfig(), this.config = const ImagePickerConfig(),
this.imagePickerService, this.service,
this.customButton, this.onError,
super.key, super.key,
}); });
/// ImagePickerTheme can be used to change the UI of the Image Picker Widget to change the text/icons to your liking. /// ImagePickerTheme can be used to change the UI of the Image Picker Widget to change the text/icons to your liking.
final ImagePickerTheme imagePickerTheme; final ImagePickerTheme theme;
/// ImagePickerConfig can be used to define the size and quality for the uploaded image. /// ImagePickerConfig can be used to define the size and quality for the
final ImagePickerConfig imagePickerConfig; /// uploaded image.
final ImagePickerConfig config;
/// The Image Picker Dialog can have a custom button if you want to. /// The ImagePickerService can be used if you want to use your own
final Widget? customButton; /// implementation of the Image Service if you want to use it for testing or
/// add more features. If null the current implementation will be used.
final ImagePickerService? service;
/// The ImagePickerService can be used if you want to use your own implementation of the Image Service if you want to use it for testing or add more features. If null the current implementation will be used. final Function(PlatformException error)? onError;
final ImagePickerService? imagePickerService;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => SingleChildScrollView(
return SingleChildScrollView( child: Column(
child: Column( children: <Widget>[
children: <Widget>[ ListTile(
ListTile( title: Text(
tileColor: imagePickerTheme.titleBackgroundColor, theme.title,
title: Text( style:
textAlign: imagePickerTheme.titleAlignment, theme.titleStyle ?? Theme.of(context).textTheme.titleMedium,
imagePickerTheme.title, textAlign: theme.titleAlignment,
style: TextStyle(
fontFamily: imagePickerTheme.font,
fontSize: imagePickerTheme.titleTextSize,
color: imagePickerTheme.titleColor,
), ),
), ),
), Row(
const SizedBox(height: 20), mainAxisAlignment: MainAxisAlignment.center,
Row( children: [
mainAxisAlignment: MainAxisAlignment.center,
children: [
_generateIconButtonWithText(
context,
imagePickerTheme.selectImageIcon,
imagePickerTheme,
Icons.image,
ImageSource.gallery,
imagePickerTheme.selectImageText,
),
if (imagePickerConfig.cameraOption ?? true) ...[
SizedBox(
width: imagePickerTheme.spaceBetweenIcons,
),
_generateIconButtonWithText( _generateIconButtonWithText(
context, context,
imagePickerTheme.makePhotoIcon, theme.selectImageIcon,
imagePickerTheme, theme,
Icons.camera_alt_rounded, Icons.image,
ImageSource.camera, ImageSource.gallery,
imagePickerTheme.makePhotoText, theme.selectImageText,
onError,
), ),
] if (config.cameraOption ?? true) ...[
], SizedBox(
), width: theme.spaceBetweenIcons,
const SizedBox(height: 10), ),
Row( _generateIconButtonWithText(
mainAxisAlignment: MainAxisAlignment.center, context,
children: [ theme.makePhotoIcon,
SizedBox( theme,
width: imagePickerTheme.closeButtonWidth, Icons.camera_alt_rounded,
height: imagePickerTheme.closeButtonHeight, ImageSource.camera,
child: customButton ?? theme.makePhotoText,
ElevatedButton( onError,
style: ElevatedButton.styleFrom( ),
backgroundColor: ],
imagePickerTheme.closeButtonBackgroundColor, ],
), ),
onPressed: () => Navigator.of(context).pop(), if (theme.closeButtonBuilder != null) ...[
child: Text( theme.closeButtonBuilder!.call(
imagePickerTheme.closeButtonText, () => Navigator.of(context).pop(),
style: TextStyle( ),
fontFamily: imagePickerTheme.font, ] else ...[
fontSize: imagePickerTheme.closeButtonTextSize, const SizedBox(height: 30),
color: imagePickerTheme.closeButtonTextColor, Center(
), child: SizedBox(
width: 300,
height: 40,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
),
onPressed: () => Navigator.of(context).pop(),
child: const Text(
"Close",
style: TextStyle(
fontSize: 15,
color: Colors.white,
), ),
), ),
) ),
),
),
const SizedBox(height: 30),
], ],
), ],
const SizedBox(height: 30), ),
], );
),
);
}
/// The [_generateIconButtonWithText] function returns a column that includes an [IconButton] and [Text]. /// The [_generateIconButtonWithText] function returns a column that includes
/// The function requires the following parameters to be able to generate an icon with text: /// an [IconButton] and [Text].
/// [context] The build context that is required to make the [pickImage] function in [_imagePickerService] work. /// The function requires the following parameters to be able to generate an
/// [imagePickerTheme] The ImagePickerTheme that includes all default values for the Image Picker Dialog. /// icon with text:
/// [icon] The icon that needs to be displayed, requires an [IconData] as value to be used. /// [context] The build context that is required to make the [pickImage]
/// [imageSource] The type of [ImageSource] to be used to pick an image when pressed on the icon. /// function in [_imagePickerService] work.
/// [imagePickerTheme] The ImagePickerTheme that includes all default values
/// for the Image Picker Dialog.
/// [icon] The icon that needs to be displayed, requires an [IconData] as
///value to be used.
/// [imageSource] The type of [ImageSource] to be used to pick an image when
/// pressed on the icon.
/// [bottomText] The text that's displayed underneath the icon. /// [bottomText] The text that's displayed underneath the icon.
Column _generateIconButtonWithText( Column _generateIconButtonWithText(
BuildContext context, BuildContext context,
Widget? customIcon, Widget? customIcon,
ImagePickerTheme imagePickerTheme, ImagePickerTheme imagePickerTheme,
IconData icon, IconData icon,
ImageSource imageSource, ImageSource imageSource,
String bottomText) { String bottomText,
return Column( Function(PlatformException error)? onError,
mainAxisSize: MainAxisSize.min, ) =>
children: <Widget>[ Column(
InkWell( mainAxisSize: MainAxisSize.min,
key: Key(bottomText), children: <Widget>[
onTap: () async { InkWell(
final navigator = Navigator.of(context); key: Key(bottomText),
var image = onTap: () async {
await (imagePickerService ?? ImagePickerServiceDefault()) var navigator = Navigator.of(context);
.pickImage(imageSource, config: imagePickerConfig); Uint8List? image;
navigator.pop(image); try {
}, image = await (service ?? ImagePickerServiceDefault())
child: customIcon ?? .pickImage(imageSource, config: config);
Icon( } on PlatformException catch (e) {
icon, debugPrint("image_picker_error: $e");
size: imagePickerTheme.iconSize, onError?.call(e);
color: imagePickerTheme.iconColor, }
), navigator.pop(image);
), },
Text( child: customIcon ??
bottomText, Icon(
style: TextStyle( icon,
fontFamily: imagePickerTheme.font, size: imagePickerTheme.iconSize,
fontSize: imagePickerTheme.iconTextSize, color: imagePickerTheme.iconColor,
color: imagePickerTheme.textColor, ),
), ),
), Text(
const SizedBox(height: 20), bottomText,
], style: theme.iconTextStyle,
); ),
} ],
);
} }

View file

@ -1,21 +1,24 @@
name: flutter_image_picker name: flutter_image_picker
description: A Flutter Image Picking package. description: A Flutter Image Picking package.
version: 1.0.4 version: 4.1.0
repository: https://github.com/Iconica-Development/flutter_image_picker repository: https://github.com/Iconica-Development/flutter_image_picker
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
environment: environment:
sdk: ">=2.17.6 <3.0.0" sdk: ">=3.0.0 <4.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
image_picker: ^0.8.6 image_picker: ^1.1.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0 mocktail: ^1.0.3
mocktail: ^0.3.0 flutter_iconica_analysis:
git:
flutter: url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 7.0.0

View file

@ -2,22 +2,22 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'dart:typed_data'; import "dart:typed_data";
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_image_picker/flutter_image_picker.dart' import "package:flutter_image_picker/flutter_image_picker.dart"
as iconica_image_picker; as iconica_image_picker;
import 'package:image_picker/image_picker.dart' as image_picker; import "package:flutter_test/flutter_test.dart";
import 'package:flutter_test/flutter_test.dart'; import "package:image_picker/image_picker.dart" as image_picker;
import 'package:mocktail/mocktail.dart'; import "package:mocktail/mocktail.dart";
import 'mocks/image_picker_service_mock.dart'; import "mocks/image_picker_service_mock.dart";
void main() { void main() {
Uint8List cameraImage = Uint8List(10); var cameraImage = Uint8List(10);
Uint8List galleryImage = Uint8List(44); var galleryImage = Uint8List(44);
testWidgets('Image Picker Shows With Normal Theme', (tester) async { testWidgets("Image Picker Shows With Normal Theme", (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
home: Material( home: Material(
@ -26,18 +26,14 @@ void main() {
), ),
); );
final titleFinder = var makePhotoIconFinder = find.byIcon(Icons.camera_alt_rounded);
find.text(const iconica_image_picker.ImagePickerTheme().title); var makePhotoTextFinder =
final makePhotoIconFinder = find.byIcon(Icons.camera_alt_rounded);
final makePhotoTextFinder =
find.text(const iconica_image_picker.ImagePickerTheme().makePhotoText); find.text(const iconica_image_picker.ImagePickerTheme().makePhotoText);
final selectImageIconFinder = find.byIcon(Icons.image); var selectImageIconFinder = find.byIcon(Icons.image);
final selectImageTextFinder = find var selectImageTextFinder = find
.text(const iconica_image_picker.ImagePickerTheme().selectImageText); .text(const iconica_image_picker.ImagePickerTheme().selectImageText);
final closebuttonTextFinder = find var closebuttonTextFinder = find.text("Close");
.text(const iconica_image_picker.ImagePickerTheme().closeButtonText);
expect(titleFinder, findsOneWidget);
expect(makePhotoIconFinder, findsOneWidget); expect(makePhotoIconFinder, findsOneWidget);
expect(makePhotoTextFinder, findsOneWidget); expect(makePhotoTextFinder, findsOneWidget);
expect(selectImageIconFinder, findsOneWidget); expect(selectImageIconFinder, findsOneWidget);
@ -46,20 +42,26 @@ void main() {
}); });
testWidgets( testWidgets(
'Image Picker Calls Function Correctly When ImageSource Is Gallery', "Image Picker Calls Function Correctly When ImageSource Is Gallery",
(tester) async { (tester) async {
ImagePickerServiceMock serviceMock = ImagePickerServiceMock(); var serviceMock = ImagePickerServiceMock();
when(() => serviceMock.pickImage(image_picker.ImageSource.gallery)) when(() => serviceMock.pickImage(image_picker.ImageSource.gallery))
.thenAnswer((_) => Future.value(galleryImage)); .thenAnswer((_) => Future.value(galleryImage));
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(
MaterialApp(
home: Material( home: Material(
child: iconica_image_picker.ImagePicker( child: iconica_image_picker.ImagePicker(
imagePickerService: serviceMock)))); service: serviceMock,
),
),
),
);
Finder finder = find.byKey( var finder = find.byKey(
Key(const iconica_image_picker.ImagePickerTheme().selectImageText)); Key(const iconica_image_picker.ImagePickerTheme().selectImageText),
);
await tester.tap(finder); await tester.tap(finder);
@ -68,20 +70,26 @@ void main() {
}); });
testWidgets( testWidgets(
'Image Picker Calls Function Correctly When ImageSource Is Camera', "Image Picker Calls Function Correctly When ImageSource Is Camera",
(tester) async { (tester) async {
ImagePickerServiceMock serviceMock = ImagePickerServiceMock(); var serviceMock = ImagePickerServiceMock();
when(() => serviceMock.pickImage(image_picker.ImageSource.camera)) when(() => serviceMock.pickImage(image_picker.ImageSource.camera))
.thenAnswer((_) => Future.value(cameraImage)); .thenAnswer((_) => Future.value(cameraImage));
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(
MaterialApp(
home: Material( home: Material(
child: iconica_image_picker.ImagePicker( child: iconica_image_picker.ImagePicker(
imagePickerService: serviceMock)))); service: serviceMock,
),
),
),
);
Finder finder = find.byKey( var finder = find.byKey(
Key(const iconica_image_picker.ImagePickerTheme().makePhotoText)); Key(const iconica_image_picker.ImagePickerTheme().makePhotoText),
);
await tester.tap(finder); await tester.tap(finder);
@ -89,41 +97,42 @@ void main() {
.called(1); .called(1);
}); });
testWidgets('Image Picker Shows With Custom Theme', (tester) async { testWidgets("Image Picker Shows With Custom Theme", (tester) async {
String title = "title";
Widget makePhotoIcon = Container( Widget makePhotoIcon = Container(
height: 125, height: 125,
width: 125, width: 125,
color: Colors.red, color: Colors.red,
); );
String makePhotoText = "taaaake image"; var makePhotoText = "taaaake image";
Widget selectImageIcon = Container( Widget selectImageIcon = Container(
height: 125, height: 125,
width: 125, width: 125,
color: Colors.blue, color: Colors.blue,
); );
String selectImageText = "seleeeeect image"; var selectImageText = "seleeeeect image";
String closeButtonText = "Close Dialog!"; var closeButtonText = "Close";
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(
MaterialApp(
home: Material( home: Material(
child: iconica_image_picker.ImagePicker( child: iconica_image_picker.ImagePicker(
imagePickerTheme: iconica_image_picker.ImagePickerTheme( theme: iconica_image_picker.ImagePickerTheme(
title: title, makePhotoIcon: makePhotoIcon,
makePhotoIcon: makePhotoIcon, makePhotoText: makePhotoText,
makePhotoText: makePhotoText, selectImageIcon: selectImageIcon,
selectImageIcon: selectImageIcon, selectImageText: selectImageText,
selectImageText: selectImageText, ),
closeButtonText: closeButtonText))))); ),
),
),
);
final titleFinder = find.text(title); var makePhotoIconFinder = find.byWidget(makePhotoIcon);
final makePhotoIconFinder = find.byWidget(makePhotoIcon); var makePhotoTextFinder = find.text(makePhotoText);
final makePhotoTextFinder = find.text(makePhotoText); var selectImageIconFinder = find.byWidget(selectImageIcon);
final selectImageIconFinder = find.byWidget(selectImageIcon); var selectImageTextFinder = find.text(selectImageText);
final selectImageTextFinder = find.text(selectImageText); var closebuttonTextFinder = find.text(closeButtonText);
final closebuttonTextFinder = find.text(closeButtonText);
expect(titleFinder, findsOneWidget);
expect(makePhotoIconFinder, findsOneWidget); expect(makePhotoIconFinder, findsOneWidget);
expect(makePhotoTextFinder, findsOneWidget); expect(makePhotoTextFinder, findsOneWidget);
expect(selectImageIconFinder, findsOneWidget); expect(selectImageIconFinder, findsOneWidget);

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter_image_picker/src/services/image_picker_service.dart'; import "package:flutter_image_picker/src/services/image_picker_service.dart";
import 'package:mocktail/mocktail.dart'; import "package:mocktail/mocktail.dart";
class ImagePickerServiceMock extends Mock implements ImagePickerService {} class ImagePickerServiceMock extends Mock implements ImagePickerService {}