diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5f2fb38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,25 @@ +--- +name: Bug Report +about: Create a report to help us improve +title: "fix: " +labels: bug +--- + +**Description** +A clear and concise description of what the bug is. + +**Steps To Reproduce** + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected Behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional Context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6b9372e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ + + +## Description + + + +## Type of Change + + + +- [ ] โœจ New feature (non-breaking change which adds functionality) +- [ ] ๐Ÿ› ๏ธ Bug fix (non-breaking change which fixes an issue) +- [ ] โŒ Breaking change (fix or feature that would cause existing functionality to change) +- [ ] ๐Ÿงน Code refactor +- [ ] โœ… Build configuration change +- [ ] ๐Ÿ“ Documentation +- [ ] ๐Ÿ—‘๏ธ Chore diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..112f425 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,10 @@ +name: ci + +on: + pull_request: + branches: + - main + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1 diff --git a/.github/workflows/flutter_document_scanner.yaml b/.github/workflows/flutter_document_scanner.yaml new file mode 100644 index 0000000..c9e928e --- /dev/null +++ b/.github/workflows/flutter_document_scanner.yaml @@ -0,0 +1,181 @@ +name: flutter_document_scanner + +on: + pull_request: + paths: + - ".github/workflows/flutter_document_scanner.yaml" + - "src/flutter_document_scanner/**" + push: + branches: + - main + paths: + - ".github/workflows/flutter_document_scanner.yaml" + - "src/flutter_document_scanner/**" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_channel: stable + flutter_version: 3.0.0 + working_directory: src/flutter_document_scanner + + android: + runs-on: macos-latest + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: "temurin" + java-version: "11" + + - uses: subosito/flutter-action@v2 + + - name: Flutter Doctor + run: flutter doctor -v + + - name: AVD Cache + uses: actions/cache@v2 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-29 + + - name: Cache AVD Snapshot + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 29 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + + - name: Integration Tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 29 + script: flutter test integration_test + working-directory: src/flutter_document_scanner/example + + ios: + runs-on: macos-latest + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v2 + + - name: Flutter Doctor + run: flutter doctor -v + + - name: Start Simulator + run: open -a Simulator.app + + - name: Integration Tests + run: flutter test integration_test -d iPhone + + linux: + runs-on: ubuntu-18.04 + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v2 + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev liblzma-dev + + - name: Enable desktop support + run: flutter config --enable-linux-desktop + + - name: Flutter Doctor + run: flutter doctor -v + + - name: Integration Tests + run: xvfb-run flutter test integration_test -d linux + + macos: + runs-on: macos-latest + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v2 + + - name: Enable desktop support + run: flutter config --enable-macos-desktop + + - name: Flutter Doctor + run: flutter doctor -v + + - name: Integration Tests + run: flutter test integration_test -d macos + + web: + runs-on: macos-latest + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v2 + + - name: Flutter Doctor + run: flutter doctor -v + + - name: Run Chromedriver + run: | + git clone https://github.com/felangel/web_installers + cd web_installers/packages/web_drivers + dart pub get + dart lib/web_driver_installer.dart chromedriver --install-only + ./chromedriver/chromedriver --port=4444 & + + - name: Integration Tests + run: flutter drive --driver test_driver/integration_test.dart --target integration_test/app_test.dart -d web-server --browser-name=chrome + + windows: + runs-on: windows-2019 + + defaults: + run: + working-directory: src/flutter_document_scanner/example + + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v2 + + - name: Enable desktop support + run: flutter config --enable-windows-desktop + + - name: Flutter Doctor + run: flutter doctor -v + + - name: Integration Tests + run: flutter test integration_test -d windows diff --git a/.github/workflows/flutter_document_scanner_android.yaml b/.github/workflows/flutter_document_scanner_android.yaml new file mode 100644 index 0000000..b766a25 --- /dev/null +++ b/.github/workflows/flutter_document_scanner_android.yaml @@ -0,0 +1,21 @@ +name: flutter_document_scanner_android + +on: + pull_request: + paths: + - ".github/workflows/flutter_document_scanner_android.yaml" + - "src/flutter_document_scanner_android/**" + push: + branches: + - main + paths: + - ".github/workflows/flutter_document_scanner_android.yaml" + - "src/flutter_document_scanner_android/**" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_channel: stable + flutter_version: 3.0.0 + working_directory: src/flutter_document_scanner_android diff --git a/.github/workflows/flutter_document_scanner_ios.yaml b/.github/workflows/flutter_document_scanner_ios.yaml new file mode 100644 index 0000000..f9d3f7f --- /dev/null +++ b/.github/workflows/flutter_document_scanner_ios.yaml @@ -0,0 +1,21 @@ +name: flutter_document_scanner_ios + +on: + pull_request: + paths: + - ".github/workflows/flutter_document_scanner_ios.yaml" + - "src/flutter_document_scanner_ios/**" + push: + branches: + - main + paths: + - ".github/workflows/flutter_document_scanner_ios.yaml" + - "src/flutter_document_scanner_ios/**" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_channel: stable + flutter_version: 3.0.0 + working_directory: src/flutter_document_scanner_ios diff --git a/.github/workflows/flutter_document_scanner_platform_interface.yaml b/.github/workflows/flutter_document_scanner_platform_interface.yaml new file mode 100644 index 0000000..e1b401b --- /dev/null +++ b/.github/workflows/flutter_document_scanner_platform_interface.yaml @@ -0,0 +1,21 @@ +name: flutter_document_scanner_platform_interface + +on: + pull_request: + paths: + - ".github/workflows/flutter_document_scanner_platform_interface.yaml" + - "src/flutter_document_scanner_platform_interface/**" + push: + branches: + - main + paths: + - ".github/workflows/flutter_document_scanner_platform_interface.yaml" + - "src/flutter_document_scanner_platform_interface/**" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_channel: stable + flutter_version: 3.0.0 + working_directory: src/flutter_document_scanner_platform_interface diff --git a/.github/workflows/flutter_document_scanner_web.yaml b/.github/workflows/flutter_document_scanner_web.yaml new file mode 100644 index 0000000..196db1c --- /dev/null +++ b/.github/workflows/flutter_document_scanner_web.yaml @@ -0,0 +1,21 @@ +name: flutter_document_scanner_web + +on: + pull_request: + paths: + - ".github/workflows/flutter_document_scanner_web.yaml" + - "src/flutter_document_scanner_web/**" + push: + branches: + - main + paths: + - ".github/workflows/flutter_document_scanner_web.yaml" + - "src/flutter_document_scanner_web/**" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_channel: stable + flutter_version: 3.0.0 + working_directory: src/flutter_document_scanner_web diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 77117e5..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: build - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@master - - - name: Install and set Flutter version - uses: subosito/flutter-action@master - with: - channel: master - - - name: Install Dependencies - run: flutter pub get - - - name: Format - run: flutter format --set-exit-if-changed . - - - name: Analyze - run: flutter analyze lib test example - - - name: Run tests - run: flutter test --no-pub --coverage --test-randomize-ordering-seed random - - - name: Upload coverage to codecov - uses: codecov/codecov-action@master - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: coverage/lcov.info diff --git a/.gitignore b/.gitignore index a247422..4aa0df8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,75 +1,48 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp .DS_Store .atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws .idea/ +.vscode/ -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies .packages -.pub-cache/ .pub/ -build/ +.dart_tool/ +pubspec.lock +flutter_export_environment.sh +coverage/ + +Podfile.lock +Pods/ +.symlinks/ +**/Flutter/App.framework/ +**/Flutter/ephemeral/ +**/Flutter/Flutter.podspec +**/Flutter/Flutter.framework/ +**/Flutter/Generated.xcconfig +**/Flutter/flutter_assets/ -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java +ServiceDefinitions.json +xcuserdata/ +**/DerivedData/ -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/ephemeral -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* +local.properties +keystore.properties +.gradle/ +gradlew +gradlew.bat +gradle-wrapper.jar +.flutter-plugins-dependencies +*.iml + +generated_plugin_registrant.cc +generated_plugin_registrant.h +generated_plugin_registrant.dart +GeneratedPluginRegistrant.java +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m +GeneratedPluginRegistrant.swift +build/ +.flutter-plugins -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 +.project +.classpath +.settings \ No newline at end of file diff --git a/.metadata b/.metadata deleted file mode 100644 index cb69e6e..0000000 --- a/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f - channel: stable - -project_type: package diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6294cd2..0000000 --- a/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright 2021 Christian Betancourt Barajas - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 8b658c2..0000000 --- a/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Flutter Document Scanner - -

-Pub -build - - - -License: MIT -

- ---- - -A Flutter plugin that allows the management of taking, cropping and applying filters to an image, using -the [Camera](https://pub.dev/packages/camera) plugin and [OpenCV](https://opencv.org/) - - -![Demo](https://raw.githubusercontent.com/criistian14/flutter_document_scanner/master/demo_scanner.gif) - ---- - -## Usage - -First, add flutter_document_scanner as -a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/packages-and-plugins/using-packages). - -## Example - -### Import libraries. - -```dart -import 'package:flutter_document_scanner/flutter_document_scanner.dart'; -``` - -### Initialize the DocumentScannerController - -```dart - final _controller = DocumentScannerController(); -``` - -### Display widget - -```dart -DocumentScanner( - controller: _controller, - onSave: (Uint8List imageBytes) { - print("image bytes: $imageBytes"); - }, -); -``` - ---- - -## Controller Document uses - -### Actions - -```dart -_controller.takePhoto( - minContourArea: 80000.0, -); - -_controller.cropPhoto(); - -_controller.applyFilter(FilterType.gray); - -_controller.savePhotoDocument(); - -_controller.changePage(AppPages.cropPhoto); -``` - -### Can listen to the changes - -```dart -_controller.statusTakePhotoPage.listen((AppStatus event) { - print("Changes when taking the picture"); - print("[initial, loading, success, failure]"); -}); - - -_controller.statusCropPhoto.listen((AppStatus event) { - print("Changes while cutting the image and adding warp perspective"); - print("[initial, loading, success, failure]"); -}); - - -_controller.statusEditPhoto.listen((AppStatus event) { - print("Changes when editing the image (applying filters)"); - print("[initial, loading, success, failure]"); -}); - - -_controller.currentFilterType.listen((FilterType event) { - print("Listen to the current filter applied on the image"); - print("[ natural, gray, eco]"); -}); - - -_controller.statusSavePhotoDocument.listen((AppStatus event) { - print("Changes while the document image is being saved"); - print("[initial, loading, success, failure]"); -}); -``` - ---- - -## Customizations - -### Camera - -```dart -DocumentScanner( - controller: _controller, - onSave: (Uint8List imageBytes) { - print("image bytes: $imageBytes"); - }, - resolutionCamera: ResolutionPreset.high, - initialCameraLensDirection: CameraLensDirection.front, -); -``` - - - -### Page transitions - -```dart -DocumentScanner( - controller: _controller, - onSave: (Uint8List imageBytes) { - print("image bytes: $imageBytes"); - }, - pageTransitionBuilder: (child, animation) { - final tween = Tween(begin: 0.0, end: 1.0); - - final curvedAnimation = CurvedAnimation( - parent: animation, - curve: Curves.easeOutCubic, - ); - - return FadeTransition( - opacity: tween.animate(curvedAnimation), - child: child, - ); - }, -); - -``` - -### General Styles - -The properties are listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/general_styles.dart) - - -#### Page to take the photo - -The properties are listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/take_photo_document_style.dart) - - -#### Page to crop the image - -The properties are listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/crop_photo_document_style.dart) - - -#### Page to edit image - -The properties are listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/edit_photo_document_style.dart) - - diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index a5744c1..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 19f09c5..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'flutter_document_scanner' diff --git a/demo_scanner.gif b/demo_scanner.gif deleted file mode 100644 index 7b8e353..0000000 Binary files a/demo_scanner.gif and /dev/null differ diff --git a/example/README.md b/example/README.md deleted file mode 100644 index ea22ba2..0000000 --- a/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# flutter_document_scanner_example - -Demonstrates how to use the flutter_document_scanner plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 61b6c4d..0000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index e2c63a9..0000000 --- a/example/lib/main.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_document_scanner/flutter_document_scanner.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - final _controller = DocumentScannerController(); - - @override - Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData.light().copyWith( - primaryColor: Colors.teal, - ), - home: Builder( - builder: (context) { - return Scaffold( - body: DocumentScanner( - controller: _controller, - generalStyles: const GeneralStyles( - baseColor: Colors.white, - ), - cropPhotoDocumentStyle: CropPhotoDocumentStyle( - top: MediaQuery.of(context).padding.top, - ), - onSave: (Uint8List imageBytes) { - // ? Bytes of the document/image already processed - }, - ), - ); - }, - ), - ); - } -} diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 49e57b9..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,285 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.2" - bloc: - dependency: transitive - description: - name: bloc - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.2" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - camera: - dependency: transitive - description: - name: camera - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.4+8" - camera_platform_interface: - dependency: transitive - description: - name: camera_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.5" - camera_web: - dependency: transitive - description: - name: camera_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.1+1" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - cross_file: - dependency: transitive - description: - name: cross_file - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" - equatable: - dependency: transitive - description: - name: equatable - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.3" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_bloc: - dependency: transitive - description: - name: flutter_bloc - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.1" - flutter_document_scanner: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.1.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" - 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" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.3" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.11" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.0" - nested: - dependency: transitive - description: - name: nested - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.1" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" - provider: - dependency: transitive - description: - name: provider - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.2" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1+1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - stream_transform: - dependency: transitive - description: - name: stream_transform - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.3" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" -sdks: - dart: ">=2.15.1 <3.0.0" - flutter: ">=2.5.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml deleted file mode 100644 index 1211fdf..0000000 --- a/example/pubspec.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: flutter_document_scanner_example -description: Demonstrates how to use the flutter_document_scanner plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -environment: - sdk: ">=2.15.1 <3.0.0" - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - flutter_document_scanner: - # When depending on this package from a real application you should use: - # flutter_document_scanner: ^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: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^1.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 356a50a..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:flutter_document_scanner_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => - widget is Text && widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} diff --git a/CHANGELOG.md b/flutter_document_scanner/CHANGELOG.md similarity index 50% rename from CHANGELOG.md rename to flutter_document_scanner/CHANGELOG.md index 164e06d..f8dc73d 100644 --- a/CHANGELOG.md +++ b/flutter_document_scanner/CHANGELOG.md @@ -1,7 +1,10 @@ +## 1.0.0 +* Stable version with basic functionality and new structure + ## 0.1.0 * Removing packages and adjusting opencv functionality (findContours) ## 0.0.1 -* Initial release. +* Initial release. \ No newline at end of file diff --git a/flutter_document_scanner/LICENSE b/flutter_document_scanner/LICENSE new file mode 100644 index 0000000..96ff66a --- /dev/null +++ b/flutter_document_scanner/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Christian Betancourt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flutter_document_scanner/README.md b/flutter_document_scanner/README.md new file mode 100644 index 0000000..5ba488e --- /dev/null +++ b/flutter_document_scanner/README.md @@ -0,0 +1,235 @@ +# Flutter Document Scanner + +![pub_version] +![coverage][coverage_badge] +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] +[![License: MIT][license_badge]][license_link] + +Generated by the [Very Good CLI][very_good_cli_link] ๐Ÿค– + +--- + +๐Ÿšง Under Development! ๐Ÿšง + +A Flutter plugin that allows the management of taking, cropping and applying filters to an image, using +the [Camera][camera_link] plugin. + +For Android use [OpenCV][opencv_link]. + +For iOS use [Vision Kit][vision_kit_link]. + + +
+ +## Basic +![demo] + + +## With Customizations +![demo_customizations] + + +--- + +## TODO + +| Feature | Android | iOS | +|-------------------------------|:-------:|:---:| +| Adjust perspective | โœ… | โœ… | +| Apply filters | โœ… | โœ… | +| Select image from gallery | โŒ | โŒ | +| Add Unit Test | โŒ | โŒ | +| Find contours in real time | โŒ | โŒ | +| Taking multiple photos at once | โŒ | โŒ | +| Share photos taken | โŒ | โŒ | +| Generate PDF | โŒ | โŒ | + +## Usage + +First, add flutter_document_scanner as +a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/packages-and-plugins/using-packages). + +Later add [camera permissions](https://pub.dev/packages/camera#installation) in the AndroidManifest.xml and Info.plist files. + + +## Example + +### Import libraries. + +```dart +import 'package:flutter_document_scanner/flutter_document_scanner.dart'; +``` + +### Initialize the DocumentScannerController + +```dart +final _controller = DocumentScannerController(); +``` + +### Display widget + +```dart +DocumentScanner( + controller: _controller, + onSave: (Uint8List imageBytes) { + print("image bytes: $imageBytes"); + }, +); +``` + +--- + +## Controller Document uses + +### Actions + +```dart +_controller.takePhoto(minContourArea: 80000.0,); + +_controller.cropPhoto(); + +_controller.applyFilter(FilterType.gray); + +_controller.savePhotoDocument(); + +_controller.changePage(AppPages.cropPhoto); +``` + +### Can listen to the changes + +```dart +_controller.statusTakePhotoPage.listen((AppStatus event) { + print("Changes when taking the picture"); + print("[initial, loading, success, failure]"); +}); + + +_controller.statusCropPhoto.listen((AppStatus event) { + print("Changes while cutting the image and adding warp perspective"); + print("[initial, loading, success, failure]"); +}); + + +_controller.statusEditPhoto.listen((AppStatus event) { + print("Changes when editing the image (applying filters)"); + print("[initial, loading, success, failure]"); +}); + + +_controller.currentFilterType.listen((FilterType event) { + print("Listen to the current filter applied on the image"); + print("[ natural, gray, eco]"); +}); + + +_controller.statusSavePhotoDocument.listen((AppStatus event) { + print("Changes while the document image is being saved"); + print("[initial, loading, success, failure]"); +}); +``` + +--- + +## Customizations + +### Change messsages dialogs + +```dart +DocumentScanner( + controller: _controller, + onSave: (Uint8List imageBytes) { + print("image bytes: $imageBytes"); + }, + generalStyles: const GeneralStyles( + messageTakingPicture: 'Capturando documento', + messageCroppingPicture: 'Recortando documento', + messageEditingPicture: 'Editando documento', + messageSavingPicture: 'Guardando documento', + ), +); +``` + +### Camera + +```dart +DocumentScanner( + controller: _controller, + onSave: (Uint8List imageBytes) { + print("image bytes: $imageBytes"); + }, + resolutionCamera: ResolutionPreset.high, + initialCameraLensDirection: CameraLensDirection.front, +); +``` + +### Page transitions + +```dart +DocumentScanner( + controller: _controller, + onSave: (Uint8List imageBytes) { + print("image bytes: $imageBytes"); + }, + pageTransitionBuilder: (child, animation) { + final tween = Tween(begin: 0.0, end: 1.0); + + final curvedAnimation = CurvedAnimation( + parent: animation, + curve: Curves.easeOutCubic, + ); + + return FadeTransition( + opacity: tween.animate(curvedAnimation), + child: child, + ); + }, +); + +``` + +### General Styles + +The properties are +listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/general_styles.dart) + +#### Page to take the photo + +The properties are +listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/take_photo_document_style.dart) + +#### Page to crop the image + +The properties are +listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/crop_photo_document_style.dart) + +#### Page to edit image + +The properties are +listed [here](https://github.com/criistian14/flutter_document_scanner/blob/master/lib/src/utils/edit_photo_document_style.dart) + + +[pub_version]: https://img.shields.io/pub/v/flutter_document_scanner.svg + +[workflow_badge]: https://github.com/criistian14/flutter_document_scanner/actions/workflows/main.yml/badge.svg + +[demo]: https://media.giphy.com/media/flQWXeHif35IsSQOOO/giphy.gif + +[demo_customizations]: https://media.giphy.com/media/rS6qYbtuuRbo8Fra3d/giphy.gif + +[coverage_badge]: https://codecov.io/gh/criistian14/flutter_document_scanner/branch/master/graph/badge.svg?token=2U7891NVMO + +[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg + +[license_link]: https://opensource.org/licenses/MIT + +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg + +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis + +[very_good_cli_link]: https://github.com/VeryGoodOpenSource/very_good_cli + +[camera_link]: https://pub.dev/packages/camera + +[opencv_link]: https://opencv.org/ + +[vision_kit_link]: https://developer.apple.com/documentation/visionkit diff --git a/flutter_document_scanner/analysis_options.yaml b/flutter_document_scanner/analysis_options.yaml new file mode 100644 index 0000000..359af7f --- /dev/null +++ b/flutter_document_scanner/analysis_options.yaml @@ -0,0 +1,7 @@ +include: package:very_good_analysis/analysis_options.3.0.1.yaml + +linter: + rules: + sort_pub_dependencies: false + omit_local_variable_types: false + lines_longer_than_80_chars: false diff --git a/example/.gitignore b/flutter_document_scanner/example/.gitignore similarity index 100% rename from example/.gitignore rename to flutter_document_scanner/example/.gitignore diff --git a/example/.metadata b/flutter_document_scanner/example/.metadata similarity index 82% rename from example/.metadata rename to flutter_document_scanner/example/.metadata index fd70cab..0a999ee 100644 --- a/example/.metadata +++ b/flutter_document_scanner/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b + revision: db747aa1331bd95bc9b3874c842261ca2d302cd5 channel: stable project_type: app diff --git a/flutter_document_scanner/example/README.md b/flutter_document_scanner/example/README.md new file mode 100644 index 0000000..f8fdcec --- /dev/null +++ b/flutter_document_scanner/example/README.md @@ -0,0 +1,3 @@ +# flutter_document_scanner_example + +Demonstrates how to use the flutter_document_scanner plugin. diff --git a/flutter_document_scanner/example/analysis_options.yaml b/flutter_document_scanner/example/analysis_options.yaml new file mode 100644 index 0000000..77fb8e1 --- /dev/null +++ b/flutter_document_scanner/example/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:very_good_analysis/analysis_options.3.0.1.yaml +linter: + rules: + public_member_api_docs: false diff --git a/example/android/.gitignore b/flutter_document_scanner/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to flutter_document_scanner/example/android/.gitignore diff --git a/example/android/app/build.gradle b/flutter_document_scanner/example/android/app/build.gradle similarity index 95% rename from example/android/app/build.gradle rename to flutter_document_scanner/example/android/app/build.gradle index e5593fc..800240c 100644 --- a/example/android/app/build.gradle +++ b/flutter_document_scanner/example/android/app/build.gradle @@ -43,7 +43,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.flutter_document_scanner_example" + applicationId "com.christian.flutter.document.flutterDocumentScanner.example" minSdkVersion 21 targetSdkVersion 31 versionCode flutterVersionCode.toInteger() diff --git a/example/android/app/src/profile/AndroidManifest.xml b/flutter_document_scanner/example/android/app/src/debug/AndroidManifest.xml similarity index 82% rename from example/android/app/src/profile/AndroidManifest.xml rename to flutter_document_scanner/example/android/app/src/debug/AndroidManifest.xml index 476c586..20ed663 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/flutter_document_scanner/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.christian.flutter.document.scanner.example"> diff --git a/example/android/app/src/main/AndroidManifest.xml b/flutter_document_scanner/example/android/app/src/main/AndroidManifest.xml similarity index 96% rename from example/android/app/src/main/AndroidManifest.xml rename to flutter_document_scanner/example/android/app/src/main/AndroidManifest.xml index d96d9e8..28badc4 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/flutter_document_scanner/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.christian.flutter.document.scanner.example"> + package="com.christian.flutter.document.scanner"> diff --git a/example/android/build.gradle b/flutter_document_scanner/example/android/build.gradle similarity index 94% rename from example/android/build.gradle rename to flutter_document_scanner/example/android/build.gradle index 24047dc..4256f91 100644 --- a/example/android/build.gradle +++ b/flutter_document_scanner/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() diff --git a/example/android/gradle.properties b/flutter_document_scanner/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to flutter_document_scanner/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter_document_scanner/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to flutter_document_scanner/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/flutter_document_scanner/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to flutter_document_scanner/example/android/settings.gradle diff --git a/flutter_document_scanner/example/integration_test/app_test.dart b/flutter_document_scanner/example/integration_test/app_test.dart new file mode 100644 index 0000000..5078c09 --- /dev/null +++ b/flutter_document_scanner/example/integration_test/app_test.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:io'; + +import 'package:flutter_document_scanner_example/main.dart' as app; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('E2E', () { + testWidgets('getPlatformName', (tester) async { + app.main(); + await tester.pumpAndSettle(); + await tester.tap(find.text('Get Platform Name')); + await tester.pumpAndSettle(); + final expected = expectedPlatformName(); + await tester.ensureVisible(find.text('Platform Name: $expected')); + }); + }); +} + +String expectedPlatformName() { + if (isWeb) return 'Web'; + if (Platform.isAndroid) return 'Android'; + if (Platform.isIOS) return 'iOS'; + throw UnsupportedError('Unsupported platform ${Platform.operatingSystem}'); +} + +bool get isWeb => identical(0, 0.0); diff --git a/example/ios/.gitignore b/flutter_document_scanner/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to flutter_document_scanner/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/flutter_document_scanner/example/ios/Flutter/AppFrameworkInfo.plist similarity index 96% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to flutter_document_scanner/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/flutter_document_scanner/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/flutter_document_scanner/example/ios/Flutter/Debug.xcconfig b/flutter_document_scanner/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/flutter_document_scanner/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter_document_scanner/example/ios/Flutter/Release.xcconfig b/flutter_document_scanner/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/flutter_document_scanner/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter_document_scanner/example/ios/Podfile b/flutter_document_scanner/example/ios/Podfile new file mode 100644 index 0000000..313ea4a --- /dev/null +++ b/flutter_document_scanner/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/flutter_document_scanner/example/ios/Runner.xcodeproj/project.pbxproj similarity index 80% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to flutter_document_scanner/example/ios/Runner.xcodeproj/project.pbxproj index 0e1a15e..4efe72c 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_document_scanner/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,12 +3,13 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 6578EEA2C83B6388B886968C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D6986157739D9687AB6AFD6 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -31,10 +32,12 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1D6986157739D9687AB6AFD6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 92B43DD0AD58E6356D15892C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,6 +45,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C32DC939DE75A0BDF7B647A4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + FC95DB849BF641CAD26B8123 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +54,31 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6578EEA2C83B6388B886968C /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 71A9A1607E036E391BA6A301 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D6986157739D9687AB6AFD6 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9567CFDD8F7714B0D403105A /* Pods */ = { + isa = PBXGroup; + children = ( + 92B43DD0AD58E6356D15892C /* Pods-Runner.debug.xcconfig */, + C32DC939DE75A0BDF7B647A4 /* Pods-Runner.release.xcconfig */, + FC95DB849BF641CAD26B8123 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +96,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 9567CFDD8F7714B0D403105A /* Pods */, + 71A9A1607E036E391BA6A301 /* Frameworks */, ); sourceTree = ""; }; @@ -105,12 +131,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 0266454444A641F4EB110FC5 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 40760E14F3E664153DAD2ABC /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -127,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -169,12 +197,36 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0266454444A641F4EB110FC5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -183,8 +235,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 40760E14F3E664153DAD2ABC /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -272,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -288,13 +358,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = WHL7MM7SCY; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDocumentScannerExample; + PRODUCT_BUNDLE_IDENTIFIER = com.christian.flutter.document.scanner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -349,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -398,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -416,13 +488,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = WHL7MM7SCY; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDocumentScannerExample; + PRODUCT_BUNDLE_IDENTIFIER = com.christian.flutter.document.scanner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -438,13 +512,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = WHL7MM7SCY; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDocumentScannerExample; + PRODUCT_BUNDLE_IDENTIFIER = com.christian.flutter.document.scanner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to flutter_document_scanner/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_document_scanner/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 99% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to flutter_document_scanner/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a..a6b826d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter_document_scanner/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_document_scanner/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to flutter_document_scanner/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_document_scanner/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to flutter_document_scanner/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.swift b/flutter_document_scanner/example/ios/Runner/AppDelegate.swift similarity index 100% rename from example/ios/Runner/AppDelegate.swift rename to flutter_document_scanner/example/ios/Runner/AppDelegate.swift diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to flutter_document_scanner/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter_document_scanner/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to flutter_document_scanner/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/flutter_document_scanner/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to flutter_document_scanner/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/flutter_document_scanner/example/ios/Runner/Info.plist similarity index 86% rename from example/ios/Runner/Info.plist rename to flutter_document_scanner/example/ios/Runner/Info.plist index 9db8694..43a8c5d 100644 --- a/example/ios/Runner/Info.plist +++ b/flutter_document_scanner/example/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Flutter Document Scanner + Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - flutter_document_scanner_example + example CFBundlePackageType APPL CFBundleShortVersionString @@ -42,6 +42,12 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + + NSCameraUsageDescription + To scan documents diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/flutter_document_scanner/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example/ios/Runner/Runner-Bridging-Header.h rename to flutter_document_scanner/example/ios/Runner/Runner-Bridging-Header.h diff --git a/flutter_document_scanner/example/lib/main.dart b/flutter_document_scanner/example/lib/main.dart new file mode 100644 index 0000000..d46563d --- /dev/null +++ b/flutter_document_scanner/example/lib/main.dart @@ -0,0 +1,67 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner_example/pages/basic_page.dart'; +import 'package:flutter_document_scanner_example/pages/custom_page.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData.light().copyWith( + primaryColor: Colors.teal, + ), + title: 'Flutter Document Scanner', + home: Builder( + builder: (context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // * Basic example page + ElevatedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => const BasicPage(), + ), + ), + child: const Text( + 'Basic example', + ), + ), + + // * Custom example page + ElevatedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const CustomPage(), + ), + ), + child: const Text( + 'Custom example', + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/flutter_document_scanner/example/lib/pages/basic_page.dart b/flutter_document_scanner/example/lib/pages/basic_page.dart new file mode 100644 index 0000000..3d342c2 --- /dev/null +++ b/flutter_document_scanner/example/lib/pages/basic_page.dart @@ -0,0 +1,30 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner/flutter_document_scanner.dart'; + +class BasicPage extends StatefulWidget { + const BasicPage({super.key}); + + @override + State createState() => _BasicPageState(); +} + +class _BasicPageState extends State { + final _controller = DocumentScannerController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: DocumentScanner( + controller: _controller, + cropPhotoDocumentStyle: CropPhotoDocumentStyle( + top: MediaQuery.of(context).padding.top, + ), + onSave: (Uint8List imageBytes) { + // ? Bytes of the document/image already processed + }, + ), + ); + } +} diff --git a/flutter_document_scanner/example/lib/pages/custom_page.dart b/flutter_document_scanner/example/lib/pages/custom_page.dart new file mode 100644 index 0000000..d5df580 --- /dev/null +++ b/flutter_document_scanner/example/lib/pages/custom_page.dart @@ -0,0 +1,109 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner/flutter_document_scanner.dart'; + +class CustomPage extends StatefulWidget { + const CustomPage({super.key}); + + @override + State createState() => _CustomPageState(); +} + +class _CustomPageState extends State { + final _controller = DocumentScannerController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: DocumentScanner( + controller: _controller, + generalStyles: const GeneralStyles( + hideDefaultBottomNavigation: true, + messageTakingPicture: 'Taking picture of document', + messageCroppingPicture: 'Cropping picture of document', + messageEditingPicture: 'Editing picture of document', + messageSavingPicture: 'Saving picture of document', + baseColor: Colors.teal, + ), + takePhotoDocumentStyle: TakePhotoDocumentStyle( + top: MediaQuery.of(context).padding.top + 25, + hideDefaultButtonTakePicture: true, + onLoading: const CircularProgressIndicator( + color: Colors.white, + ), + children: [ + // * AppBar + Positioned( + top: 0, + left: 0, + right: 0, + child: Container( + color: Colors.teal, + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + 10, + bottom: 15, + ), + child: const Center( + child: Text( + 'Take a picture of the document', + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + ), + ), + ), + + // * Button to take picture + Positioned( + bottom: MediaQuery.of(context).padding.bottom + 10, + left: 0, + right: 0, + child: Center( + child: ElevatedButton( + onPressed: _controller.takePhoto, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + ), + child: const Text( + 'Take picture', + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + ), + ), + ), + ], + ), + cropPhotoDocumentStyle: CropPhotoDocumentStyle( + top: MediaQuery.of(context).padding.top, + maskColor: Colors.teal.withOpacity(0.2), + ), + editPhotoDocumentStyle: EditPhotoDocumentStyle( + top: MediaQuery.of(context).padding.top, + ), + resolutionCamera: ResolutionPreset.ultraHigh, + pageTransitionBuilder: (child, animation) { + final tween = Tween(begin: 0, end: 1); + + final curvedAnimation = CurvedAnimation( + parent: animation, + curve: Curves.easeOutCubic, + ); + + return ScaleTransition( + scale: tween.animate(curvedAnimation), + child: child, + ); + }, + onSave: (Uint8List imageBytes) { + // ? Bytes of the document/image already processed + }, + ), + ); + } +} diff --git a/flutter_document_scanner/example/pubspec.yaml b/flutter_document_scanner/example/pubspec.yaml new file mode 100644 index 0000000..b5491b2 --- /dev/null +++ b/flutter_document_scanner/example/pubspec.yaml @@ -0,0 +1,31 @@ +name: flutter_document_scanner_example +description: Demonstrates how to use the flutter_document_scanner plugin. +version: 0.5.0 +publish_to: none + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_document_scanner: + # When depending on this package from a real application you should use: + # flutter_document_scanner: ^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: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + very_good_analysis: ^3.0.1 + +flutter: + uses-material-design: true diff --git a/flutter_document_scanner/example/test_driver/integration_test.dart b/flutter_document_scanner/example/test_driver/integration_test.dart new file mode 100644 index 0000000..5f39da2 --- /dev/null +++ b/flutter_document_scanner/example/test_driver/integration_test.dart @@ -0,0 +1,10 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/flutter_document_scanner/example/web/favicon.png b/flutter_document_scanner/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/flutter_document_scanner/example/web/favicon.png differ diff --git a/flutter_document_scanner/example/web/icons/Icon-192.png b/flutter_document_scanner/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/flutter_document_scanner/example/web/icons/Icon-192.png differ diff --git a/flutter_document_scanner/example/web/icons/Icon-512.png b/flutter_document_scanner/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/flutter_document_scanner/example/web/icons/Icon-512.png differ diff --git a/flutter_document_scanner/example/web/icons/Icon-maskable-192.png b/flutter_document_scanner/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/flutter_document_scanner/example/web/icons/Icon-maskable-192.png differ diff --git a/flutter_document_scanner/example/web/icons/Icon-maskable-512.png b/flutter_document_scanner/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/flutter_document_scanner/example/web/icons/Icon-maskable-512.png differ diff --git a/flutter_document_scanner/example/web/index.html b/flutter_document_scanner/example/web/index.html new file mode 100644 index 0000000..b6b9dd2 --- /dev/null +++ b/flutter_document_scanner/example/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + diff --git a/flutter_document_scanner/example/web/manifest.json b/flutter_document_scanner/example/web/manifest.json new file mode 100644 index 0000000..096edf8 --- /dev/null +++ b/flutter_document_scanner/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/flutter_document_scanner/lib/flutter_document_scanner.dart b/flutter_document_scanner/lib/flutter_document_scanner.dart new file mode 100644 index 0000000..ab76edc --- /dev/null +++ b/flutter_document_scanner/lib/flutter_document_scanner.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +library flutter_document_scanner; + +export 'package:camera/camera.dart'; +export 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +export 'src/bloc/app/app_state.dart'; +export 'src/document_scanner_controller.dart'; +export 'src/models/area.dart'; +export 'src/ui/pages/document_scanner.dart'; +export 'src/utils/crop_photo_document_style.dart'; +export 'src/utils/edit_photo_document_style.dart'; +export 'src/utils/general_styles.dart'; +export 'src/utils/take_photo_document_style.dart'; diff --git a/flutter_document_scanner/lib/src/bloc/app/app.dart b/flutter_document_scanner/lib/src/bloc/app/app.dart new file mode 100644 index 0000000..1417f49 --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/app/app.dart @@ -0,0 +1,3 @@ +export 'app_bloc.dart'; +export 'app_event.dart'; +export 'app_state.dart'; diff --git a/flutter_document_scanner/lib/src/bloc/app/app_bloc.dart b/flutter_document_scanner/lib/src/bloc/app/app_bloc.dart new file mode 100644 index 0000000..1d0398f --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/app/app_bloc.dart @@ -0,0 +1,252 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; +import 'dart:io'; + +import 'package:bloc/bloc.dart'; +import 'package:camera/camera.dart'; +import 'package:flutter_document_scanner/src/bloc/app/app.dart'; +import 'package:flutter_document_scanner/src/bloc/crop/crop.dart'; +import 'package:flutter_document_scanner/src/bloc/edit/edit.dart'; +import 'package:flutter_document_scanner/src/document_scanner_controller.dart'; +import 'package:flutter_document_scanner/src/utils/image_utils.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +/// Controls interactions throughout the application by means +/// of the [DocumentScannerController] +class AppBloc extends Bloc { + /// Create instance AppBloc + AppBloc({ + required ImageUtils imageUtils, + }) : _imageUtils = imageUtils, + super(AppState.init()) { + on(_cameraInitialized); + on(_photoTaken); + on(_pageChanged); + on(_photoCropped); + on(_loadCroppedPhoto); + on(_filterApplied); + on(_newEditedImageLoaded); + on(_startedSavingDocument); + on(_documentSaved); + } + + final ImageUtils _imageUtils; + + CameraController? _cameraController; + late XFile? _pictureTaken; + + /// Initialize [CameraController] + /// based on the parameters sent by [AppCameraInitialized] + /// + /// [AppCameraInitialized.cameraLensDirection] for [CameraLensDirection] + /// [AppCameraInitialized.resolutionCamera] for the [ResolutionPreset] camera + Future _cameraInitialized( + AppCameraInitialized event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusCamera: AppStatus.loading, + ), + ); + + final cameras = await availableCameras(); + final camera = cameras.firstWhere( + (camera) => camera.lensDirection == event.cameraLensDirection, + orElse: () => cameras.first, + ); + + _cameraController = CameraController( + camera, + event.resolutionCamera, + enableAudio: false, + ); + + await _cameraController!.initialize(); + + emit( + state.copyWith( + statusCamera: AppStatus.success, + cameraController: _cameraController, + ), + ); + } + + /// Take a photo with the [CameraController.takePicture] + /// + /// Then [ImageUtils.findContourPhoto] with the largest area by + /// [AppPhotoTaken.minContourArea] in the image + Future _photoTaken( + AppPhotoTaken event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusTakePhotoPage: AppStatus.loading, + ), + ); + + if (_cameraController == null) { + // TODO(bloc): add validation and error handling + return; + } + + _pictureTaken = await _cameraController!.takePicture(); + + final byteData = await _pictureTaken!.readAsBytes(); + final response = await _imageUtils.findContourPhoto( + byteData, + minContourArea: event.minContourArea, + ); + + final fileImage = File(_pictureTaken!.path); + + emit( + state.copyWith( + statusTakePhotoPage: AppStatus.success, + pictureInitial: fileImage, + contourInitial: response, + ), + ); + + emit( + state.copyWith( + currentPage: AppPages.cropPhoto, + ), + ); + } + + /// When changing the page, the state will be initialized. + Future _pageChanged( + AppPageChanged event, + Emitter emit, + ) async { + switch (event.newPage) { + case AppPages.takePhoto: + emit( + state.copyWith( + currentPage: event.newPage, + statusTakePhotoPage: AppStatus.initial, + statusCropPhoto: AppStatus.initial, + contourInitial: null, + ), + ); + break; + + case AppPages.cropPhoto: + emit( + state.copyWith( + currentPage: event.newPage, + currentFilterType: FilterType.natural, + ), + ); + break; + + case AppPages.editDocument: + emit( + state.copyWith( + currentPage: event.newPage, + statusEditPhoto: AppStatus.initial, + statusSavePhotoDocument: AppStatus.initial, + ), + ); + break; + } + } + + /// It will change the state and + /// execute the event [CropPhotoByAreaCropped] to crop the image that is in + /// the [CropBloc]. + Future _photoCropped( + AppPhotoCropped event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusCropPhoto: AppStatus.loading, + ), + ); + } + + /// It will change the state and then change page to [AppPages.editDocument] + Future _loadCroppedPhoto( + AppLoadCroppedPhoto event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusCropPhoto: AppStatus.success, + pictureCropped: event.image, + contourInitial: event.area, + ), + ); + + emit( + state.copyWith( + currentPage: AppPages.editDocument, + ), + ); + } + + /// It will change the state and + /// execute the event [EditFilterChanged] to crop the image that is + /// in the [EditBloc]. + Future _filterApplied( + AppFilterApplied event, + Emitter emit, + ) async { + if (event.filter == state.currentFilterType) return; + + emit( + state.copyWith( + currentFilterType: event.filter, + statusEditPhoto: AppStatus.loading, + ), + ); + } + + /// It is called when the image filter changes + Future _newEditedImageLoaded( + AppNewEditedImageLoaded event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusEditPhoto: + event.isSuccess ? AppStatus.success : AppStatus.failure, + ), + ); + } + + /// It will change the state and + /// validate if image edited is valid. + Future _startedSavingDocument( + AppStartedSavingDocument event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusSavePhotoDocument: AppStatus.loading, + ), + ); + } + + /// Change state after saved the document + Future _documentSaved( + AppDocumentSaved event, + Emitter emit, + ) async { + emit( + state.copyWith( + statusSavePhotoDocument: + event.isSuccess ? AppStatus.success : AppStatus.failure, + ), + ); + } +} diff --git a/lib/src/bloc/app/app_event.dart b/flutter_document_scanner/lib/src/bloc/app/app_event.dart similarity index 60% rename from lib/src/bloc/app/app_event.dart rename to flutter_document_scanner/lib/src/bloc/app/app_event.dart index df78825..391c54e 100644 --- a/lib/src/bloc/app/app_event.dart +++ b/flutter_document_scanner/lib/src/bloc/app/app_event.dart @@ -1,22 +1,35 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:typed_data'; import 'package:camera/camera.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_document_scanner/src/bloc/app/app_state.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; -import 'package:flutter_document_scanner/src/models/filter_type.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +/// Class to create events abstract class AppEvent extends Equatable {} +/// Event to initialize the app class AppCameraInitialized extends AppEvent { - final CameraLensDirection cameraLensDirection; - final ResolutionPreset resolutionCamera; - + /// Create an event instance AppCameraInitialized({ required this.cameraLensDirection, required this.resolutionCamera, }); + /// Camera library [CameraLensDirection] + final CameraLensDirection cameraLensDirection; + + /// Camera library [ResolutionPreset] + final ResolutionPreset resolutionCamera; + @override List get props => [ cameraLensDirection, @@ -24,46 +37,59 @@ class AppCameraInitialized extends AppEvent { ]; } +/// Event to take a photo class AppPhotoTaken extends AppEvent { - final double? minContourArea; - + /// Create an event instance AppPhotoTaken({ this.minContourArea, }); + /// Minimum area to detect a contour + final double? minContourArea; + @override List get props => [ minContourArea, ]; } +/// Event to change the page class AppPageChanged extends AppEvent { - final AppPages newPage; - + /// Create an event instance AppPageChanged(this.newPage); + /// New page to show + final AppPages newPage; + @override List get props => [ newPage, ]; } +/// Event to crop the photo class AppPhotoCropped extends AppEvent { + /// Create an event instance AppPhotoCropped(); @override List get props => []; } +/// Event to load the cropped photo in page [AppPages.editDocument] class AppLoadCroppedPhoto extends AppEvent { - final Uint8List image; - final Area area; - + /// Create an event instance AppLoadCroppedPhoto({ required this.image, required this.area, }); + /// Image to load + final Uint8List image; + + /// + final Area area; + @override List get props => [ image, @@ -71,48 +97,61 @@ class AppLoadCroppedPhoto extends AppEvent { ]; } +/// Event to change the filter type in page [AppPages.editDocument] class AppFilterApplied extends AppEvent { - final FilterType filter; - + /// Create an event instance AppFilterApplied({ required this.filter, }); + /// Filter type to apply + /// + /// default: [FilterType.natural] + final FilterType filter; + @override List get props => [ filter, ]; } +/// class AppNewEditedImageLoaded extends AppEvent { - final bool isSucces; - + /// Create an event instance AppNewEditedImageLoaded({ - required this.isSucces, + required this.isSuccess, }); + /// + final bool isSuccess; + @override List get props => [ - isSucces, + isSuccess, ]; } +/// class AppStartedSavingDocument extends AppEvent { + /// Create an event instance AppStartedSavingDocument(); @override List get props => []; } +/// class AppDocumentSaved extends AppEvent { - final bool isSucces; - + /// Create an event instance AppDocumentSaved({ - required this.isSucces, + required this.isSuccess, }); + /// + final bool isSuccess; + @override List get props => [ - isSucces, + isSuccess, ]; } diff --git a/lib/src/bloc/app/app_state.dart b/flutter_document_scanner/lib/src/bloc/app/app_state.dart similarity index 64% rename from lib/src/bloc/app/app_state.dart rename to flutter_document_scanner/lib/src/bloc/app/app_state.dart index 19ae4d2..efec01f 100644 --- a/lib/src/bloc/app/app_state.dart +++ b/flutter_document_scanner/lib/src/bloc/app/app_state.dart @@ -1,38 +1,53 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:io'; import 'dart:typed_data'; import 'package:camera/camera.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; -import 'package:flutter_document_scanner/src/models/filter_type.dart'; +import 'package:flutter_document_scanner/src/ui/pages/crop_photo_document_page.dart'; +import 'package:flutter_document_scanner/src/ui/pages/edit_document_photo_page.dart'; +import 'package:flutter_document_scanner/src/ui/pages/take_photo_document_page.dart'; +import 'package:flutter_document_scanner/src/utils/image_utils.dart'; import 'package:flutter_document_scanner/src/utils/model_utils.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +/// Status of the app enum AppStatus { + /// Is initializing initial, + + /// Is loading loading, + + /// Completed without errors success, + + /// An error occurred failure, } +/// Pages of the app enum AppPages { + /// Reference to the page [TakePhotoDocumentPage] takePhoto, + + /// Reference to the page [CropPhotoDocumentPage] cropPhoto, + + /// Reference to the page [EditDocumentPhotoPage] editDocument, } +/// Controls the status general of the app class AppState extends Equatable { - final AppPages currentPage; - final AppStatus statusCamera; - final CameraController? cameraController; - final AppStatus statusTakePhotoPage; - final File? pictureInitial; - final AppStatus statusCropPhoto; - final Area? contourInitial; - final Uint8List? pictureCropped; - final AppStatus statusEditPhoto; - final FilterType currentFilterType; - final AppStatus statusSavePhotoDocument; - + /// Create an state instance const AppState({ this.currentPage = AppPages.takePhoto, this.statusCamera = AppStatus.initial, @@ -47,10 +62,44 @@ class AppState extends Equatable { this.statusSavePhotoDocument = AppStatus.initial, }); + /// Initial state factory AppState.init() { return const AppState(); } + /// Current page being displayed + final AppPages currentPage; + + /// Status of when the [cameraController] is being created + final AppStatus statusCamera; + + /// Camera controller from Camera library + final CameraController? cameraController; + + /// Status when the photo was captured + final AppStatus statusTakePhotoPage; + + /// Picture that was taken + final File? pictureInitial; + + /// Status when the photo was cropped + final AppStatus statusCropPhoto; + + /// Contour found with [ImageUtils.findContourPhoto] + final Area? contourInitial; + + /// Picture that was cropped + final Uint8List? pictureCropped; + + /// Status when the photo was edited + final AppStatus statusEditPhoto; + + /// Current filter type + final FilterType currentFilterType; + + /// Status when the photo was saved + final AppStatus statusSavePhotoDocument; + @override List get props => [ currentPage, @@ -66,6 +115,8 @@ class AppState extends Equatable { statusSavePhotoDocument, ]; + /// Creates a copy of this state but with the given fields replaced with + /// the new values. AppState copyWith({ AppPages? currentPage, AppStatus? statusCamera, diff --git a/flutter_document_scanner/lib/src/bloc/crop/crop.dart b/flutter_document_scanner/lib/src/bloc/crop/crop.dart new file mode 100644 index 0000000..1908091 --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/crop/crop.dart @@ -0,0 +1,3 @@ +export 'crop_bloc.dart'; +export 'crop_event.dart'; +export 'crop_state.dart'; diff --git a/lib/src/bloc/crop/crop_bloc.dart b/flutter_document_scanner/lib/src/bloc/crop/crop_bloc.dart similarity index 83% rename from lib/src/bloc/crop/crop_bloc.dart rename to flutter_document_scanner/lib/src/bloc/crop/crop_bloc.dart index 438cf69..4dd6373 100644 --- a/lib/src/bloc/crop/crop_bloc.dart +++ b/flutter_document_scanner/lib/src/bloc/crop/crop_bloc.dart @@ -1,20 +1,25 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:async'; import 'dart:math'; import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner/src/bloc/crop/crop_event.dart'; +import 'package:flutter_document_scanner/src/bloc/crop/crop_state.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; -import 'package:flutter_document_scanner/src/models/contour.dart'; import 'package:flutter_document_scanner/src/utils/dot_utils.dart'; import 'package:flutter_document_scanner/src/utils/image_utils.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; -import 'crop_event.dart'; -import 'crop_state.dart'; - +/// Control everything related to image cropping and perspective adjustment class CropBloc extends Bloc { - final DotUtils _dotUtils; - final ImageUtils _imageUtils; - + /// Create an instance of the bloc CropBloc({ required DotUtils dotUtils, required ImageUtils imageUtils, @@ -26,10 +31,16 @@ class CropBloc extends Bloc { on(_photoByAreaCropped); } + final DotUtils _dotUtils; + final ImageUtils _imageUtils; + late Rect _imageRect; + + /// Screen size by adjusting the screen image position late Size newScreenSize; - /// + /// Position the dots according to the + /// sent contour [CropAreaInitialized.areaInitial] Future _areaInitialized( CropAreaInitialized event, Emitter emit, @@ -75,12 +86,14 @@ class CropBloc extends Bloc { ); } - emit(state.copyWith( - area: area, - )); + emit( + state.copyWith( + area: area, + ), + ); } - /// + /// Move dot and update cutting area Future _dotMoved( CropDotMoved event, Emitter emit, @@ -162,7 +175,9 @@ class CropBloc extends Bloc { ); } + /// Crop the image and then adjust the perspective /// + /// lastly change the page Future _photoByAreaCropped( CropPhotoByAreaCropped event, Emitter emit, @@ -207,9 +222,11 @@ class CropBloc extends Bloc { contour, ); - emit(state.copyWith( - imageCropped: response, - areaParsed: area, - )); + emit( + state.copyWith( + imageCropped: response ?? event.image.readAsBytesSync(), + areaParsed: area, + ), + ); } } diff --git a/lib/src/bloc/crop/crop_event.dart b/flutter_document_scanner/lib/src/bloc/crop/crop_event.dart similarity index 56% rename from lib/src/bloc/crop/crop_event.dart rename to flutter_document_scanner/lib/src/bloc/crop/crop_event.dart index f91f833..cc42f89 100644 --- a/lib/src/bloc/crop/crop_event.dart +++ b/flutter_document_scanner/lib/src/bloc/crop/crop_event.dart @@ -1,18 +1,22 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:io'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; +/// Class to create events abstract class CropEvent extends Equatable {} +/// Initializes page and cropping area with the values class CropAreaInitialized extends CropEvent { - final Area? areaInitial; - final Area defaultAreaInitial; - final File image; - final Size screenSize; - final Rect positionImage; - + /// Create an event instance CropAreaInitialized({ this.areaInitial, required this.image, @@ -21,6 +25,21 @@ class CropAreaInitialized extends CropEvent { required this.defaultAreaInitial, }); + /// Image contour in case it is found + final Area? areaInitial; + + /// Default area defined in case image contour is not found + final Area defaultAreaInitial; + + /// Image to crop + final File image; + + /// Screen size + final Size screenSize; + + /// Position of the image in the screen + final Rect positionImage; + @override List get props => [ areaInitial, @@ -30,25 +49,42 @@ class CropAreaInitialized extends CropEvent { ]; } +/// Define which dot is the one being moved enum DotPosition { + /// The top right dot topRight, + + /// The top left dot topLeft, + + /// The bottom right dot bottomRight, + + /// The bottom left dot bottomLeft, + + /// The all dots all, } +/// Move the dot to the new position class CropDotMoved extends CropEvent { - final double deltaX; - final double deltaY; - final DotPosition dotPosition; - + /// Create an event instance CropDotMoved({ required this.deltaX, required this.deltaY, required this.dotPosition, }); + /// The delta of the movement in X axis + final double deltaX; + + /// The delta of the movement in Y axis + final double deltaY; + + /// Define which dot moved + final DotPosition dotPosition; + @override List get props => [ deltaX, @@ -57,11 +93,14 @@ class CropDotMoved extends CropEvent { ]; } +/// To the cropped image apply the perspective adjustment class CropPhotoByAreaCropped extends CropEvent { - final File image; - + /// Create an event instance CropPhotoByAreaCropped(this.image); + /// Image cropped + final File image; + @override List get props => [image]; } diff --git a/lib/src/bloc/crop/crop_state.dart b/flutter_document_scanner/lib/src/bloc/crop/crop_state.dart similarity index 63% rename from lib/src/bloc/crop/crop_state.dart rename to flutter_document_scanner/lib/src/bloc/crop/crop_state.dart index ba5e041..b7828d1 100644 --- a/lib/src/bloc/crop/crop_state.dart +++ b/flutter_document_scanner/lib/src/bloc/crop/crop_state.dart @@ -1,27 +1,26 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:math'; import 'dart:typed_data'; import 'package:equatable/equatable.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; +/// Controls the status when cropping the image class CropState extends Equatable { - final Area area; - final Uint8List? imageCropped; - final Area? areaParsed; - + /// Create an state instance const CropState({ required this.area, this.imageCropped, this.areaParsed, }); - @override - List get props => [ - area, - imageCropped, - areaParsed, - ]; - + /// Initial state factory CropState.init() { return const CropState( area: Area( @@ -33,6 +32,24 @@ class CropState extends Equatable { ); } + /// The area to crop the image + final Area area; + + /// The cropped image + final Uint8List? imageCropped; + + /// The area is parsed based on the resolution of the original image + final Area? areaParsed; + + @override + List get props => [ + area, + imageCropped, + areaParsed, + ]; + + /// Creates a copy of this state but with the given fields replaced with + /// the new values. CropState copyWith({ Area? area, Uint8List? imageCropped, diff --git a/flutter_document_scanner/lib/src/bloc/edit/edit.dart b/flutter_document_scanner/lib/src/bloc/edit/edit.dart new file mode 100644 index 0000000..9f74845 --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/edit/edit.dart @@ -0,0 +1,3 @@ +export 'edit_bloc.dart'; +export 'edit_event.dart'; +export 'edit_state.dart'; diff --git a/flutter_document_scanner/lib/src/bloc/edit/edit_bloc.dart b/flutter_document_scanner/lib/src/bloc/edit/edit_bloc.dart new file mode 100644 index 0000000..7793d49 --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/edit/edit_bloc.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bloc/bloc.dart'; +import 'package:flutter_document_scanner/flutter_document_scanner.dart'; +import 'package:flutter_document_scanner/src/bloc/edit/edit_event.dart'; +import 'package:flutter_document_scanner/src/bloc/edit/edit_state.dart'; +import 'package:flutter_document_scanner/src/utils/image_utils.dart'; + +/// Control image editing and filter application +class EditBloc extends Bloc { + /// Create an instance of the bloc + EditBloc({ + required ImageUtils imageUtils, + }) : _imageUtils = imageUtils, + super(EditState.init()) { + on(_started); + on(_filterChanged); + } + + final ImageUtils _imageUtils; + + /// Base image in case of undoing a filter + late Uint8List imageBase; + + /// Load cropped image from previous page + Future _started( + EditStarted event, + Emitter emit, + ) async { + imageBase = event.image; + + emit( + state.copyWith( + image: event.image, + ), + ); + } + + /// Apply [FilterType] with OpenCV library + Future _filterChanged( + EditFilterChanged event, + Emitter emit, + ) async { + final newImage = await _imageUtils.applyFilter(imageBase, event.filter); + + emit( + state.copyWith( + image: newImage, + ), + ); + } +} diff --git a/flutter_document_scanner/lib/src/bloc/edit/edit_event.dart b/flutter_document_scanner/lib/src/bloc/edit/edit_event.dart new file mode 100644 index 0000000..f5df31e --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/edit/edit_event.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +/// Class to create events +abstract class EditEvent extends Equatable {} + +/// Initialize the page with the image +class EditStarted extends EditEvent { + /// Create an event instance + EditStarted(this.image); + + /// Bytes of the image base + final Uint8List image; + + @override + List get props => [ + image, + ]; +} + +/// Apply the filter to the image +class EditFilterChanged extends EditEvent { + /// Create an event instance + EditFilterChanged(this.filter); + + /// Filter to apply + final FilterType filter; + + @override + List get props => [ + filter, + ]; +} diff --git a/flutter_document_scanner/lib/src/bloc/edit/edit_state.dart b/flutter_document_scanner/lib/src/bloc/edit/edit_state.dart new file mode 100644 index 0000000..d7dd929 --- /dev/null +++ b/flutter_document_scanner/lib/src/bloc/edit/edit_state.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; + +/// Controls the status when editing the image +class EditState extends Equatable { + /// Create an state instance + const EditState({ + this.image, + }); + + /// Initial state + factory EditState.init() { + return const EditState(); + } + + /// The bytes of the edited image + final Uint8List? image; + + @override + List get props => [ + image, + ]; + + /// Creates a copy of this state but with the given fields replaced with + /// the new values. + EditState copyWith({ + Uint8List? image, + }) { + return EditState( + image: image ?? this.image, + ); + } +} diff --git a/flutter_document_scanner/lib/src/document_scanner_controller.dart b/flutter_document_scanner/lib/src/document_scanner_controller.dart new file mode 100644 index 0000000..bbee2fd --- /dev/null +++ b/flutter_document_scanner/lib/src/document_scanner_controller.dart @@ -0,0 +1,119 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter_document_scanner/flutter_document_scanner.dart'; +import 'package:flutter_document_scanner/src/bloc/app/app_bloc.dart'; +import 'package:flutter_document_scanner/src/bloc/app/app_event.dart'; +import 'package:flutter_document_scanner/src/ui/pages/crop_photo_document_page.dart'; +import 'package:flutter_document_scanner/src/ui/pages/take_photo_document_page.dart'; +import 'package:flutter_document_scanner/src/utils/image_utils.dart'; + +/// This class is responsible for controlling the scanning process +class DocumentScannerController { + /// Creates a new instance of the [AppBloc] + final AppBloc _appBloc = AppBloc( + imageUtils: ImageUtils(), + ); + + /// Return the [AppBloc] created + AppBloc get bloc => _appBloc; + + /// Stream [AppStatus] to know the status while taking the picture + Stream get statusTakePhotoPage { + return _appBloc.stream.transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.statusTakePhotoPage), + ), + ); + } + + /// Stream [AppStatus] to know the status while the document is being cropped + Stream get statusCropPhoto { + return _appBloc.stream.transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.statusCropPhoto), + ), + ); + } + + /// Stream [AppStatus] to know the status while editing the document + /// with filters + Stream get statusEditPhoto { + return _appBloc.stream.transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.statusEditPhoto), + ), + ); + } + + /// Stream [FilterType] the current filtering of the document + Stream get currentFilterType { + return _appBloc.stream.transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.currentFilterType), + ), + ); + } + + /// Stream [AppStatus] to know the status while saving the document + /// with all filters and cropping area + Stream get statusSavePhotoDocument { + return _appBloc.stream.transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.statusSavePhotoDocument), + ), + ); + } + + /// Will return the picture taken on the [TakePhotoDocumentPage]. + File? get pictureTaken => _appBloc.state.pictureInitial; + + /// Will return the picture cropped on the [CropPhotoDocumentPage]. + Uint8List? get pictureCropped => _appBloc.state.pictureCropped; + + /// Taking the photo + /// + /// Then find the contour with the largest area only when + /// it exceeds [minContourArea] + /// + /// [minContourArea] is default 80000.0 + Future takePhoto({ + double? minContourArea, + }) async { + _appBloc.add( + AppPhotoTaken( + minContourArea: minContourArea, + ), + ); + } + + /// Change current page by [AppPages] + Future changePage(AppPages page) async { + _appBloc.add(AppPageChanged(page)); + } + + /// Cutting the photo and adjusting the perspective + /// then change page to [AppPages.editDocument] + Future cropPhoto() async { + _appBloc.add(AppPhotoCropped()); + } + + /// Apply [FilterType] using OpenCV + Future applyFilter(FilterType type) async { + _appBloc.add(AppFilterApplied(filter: type)); + } + + /// Save the document with filter and cropping area + /// It will return it as [Uint8List] in [DocumentScanner] + Future savePhotoDocument() async { + _appBloc.add(AppStartedSavingDocument()); + } +} diff --git a/lib/src/models/area.dart b/flutter_document_scanner/lib/src/models/area.dart similarity index 63% rename from lib/src/models/area.dart rename to flutter_document_scanner/lib/src/models/area.dart index c090445..bcf95ef 100644 --- a/lib/src/models/area.dart +++ b/flutter_document_scanner/lib/src/models/area.dart @@ -1,13 +1,17 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:math'; import 'package:equatable/equatable.dart'; +/// Area composed of 4 points class Area extends Equatable { - final Point topLeft; - final Point topRight; - final Point bottomLeft; - final Point bottomRight; - + /// Create a new area const Area({ required this.topLeft, required this.topRight, @@ -15,6 +19,18 @@ class Area extends Equatable { required this.bottomRight, }); + /// The top left dot + final Point topLeft; + + /// The top right dot + final Point topRight; + + /// The bottom left dot + final Point bottomLeft; + + /// The bottom right dot + final Point bottomRight; + @override List get props => [ topLeft, @@ -23,6 +39,8 @@ class Area extends Equatable { bottomRight, ]; + /// Creates a copy of this Area but with the given fields replaced with + /// the new values. Area copyWith({ Point? topLeft, Point? topRight, diff --git a/lib/src/ui/pages/crop_photo_document_page.dart b/flutter_document_scanner/lib/src/ui/pages/crop_photo_document_page.dart similarity index 89% rename from lib/src/ui/pages/crop_photo_document_page.dart rename to flutter_document_scanner/lib/src/ui/pages/crop_photo_document_page.dart index 3507f9b..af43736 100644 --- a/lib/src/ui/pages/crop_photo_document_page.dart +++ b/flutter_document_scanner/lib/src/ui/pages/crop_photo_document_page.dart @@ -1,3 +1,10 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:io'; import 'dart:math'; @@ -15,13 +22,16 @@ import 'package:flutter_document_scanner/src/utils/border_crop_area_painter.dart import 'package:flutter_document_scanner/src/utils/dot_utils.dart'; import 'package:flutter_document_scanner/src/utils/image_utils.dart'; +/// Page to crop a photo class CropPhotoDocumentPage extends StatelessWidget { - final CropPhotoDocumentStyle cropPhotoDocumentStyle; - + /// Create a page with style const CropPhotoDocumentPage({ - Key? key, + super.key, required this.cropPhotoDocumentStyle, - }) : super(key: key); + }); + + /// Style of the page + final CropPhotoDocumentStyle cropPhotoDocumentStyle; @override Widget build(BuildContext context) { @@ -34,26 +44,30 @@ class CropPhotoDocumentPage extends StatelessWidget { builder: (context, state) { if (state == null) { return const Center( - child: Text("NO IMAGE"), + child: Text('NO IMAGE'), ); } return BlocProvider( create: (context) => CropBloc( - dotUtils: DotUtils(), + dotUtils: DotUtils( + minDistanceDots: cropPhotoDocumentStyle.minDistanceDots, + ), imageUtils: ImageUtils(), - )..add(CropAreaInitialized( - areaInitial: context.read().state.contourInitial, - defaultAreaInitial: cropPhotoDocumentStyle.defaultAreaInitial, - image: state, - screenSize: screenSize, - positionImage: Rect.fromLTRB( - cropPhotoDocumentStyle.left, - cropPhotoDocumentStyle.top, - cropPhotoDocumentStyle.right, - cropPhotoDocumentStyle.bottom, + )..add( + CropAreaInitialized( + areaInitial: context.read().state.contourInitial, + defaultAreaInitial: cropPhotoDocumentStyle.defaultAreaInitial, + image: state, + screenSize: screenSize, + positionImage: Rect.fromLTRB( + cropPhotoDocumentStyle.left, + cropPhotoDocumentStyle.top, + cropPhotoDocumentStyle.right, + cropPhotoDocumentStyle.bottom, + ), ), - )), + ), child: _CropView( cropPhotoDocumentStyle: cropPhotoDocumentStyle, image: state, @@ -65,20 +79,20 @@ class CropPhotoDocumentPage extends StatelessWidget { } Future _onPop(BuildContext context) async { - context.read().changePage(AppPages.takePhoto); + await context + .read() + .changePage(AppPages.takePhoto); return false; } } class _CropView extends StatelessWidget { - final CropPhotoDocumentStyle cropPhotoDocumentStyle; - final File image; - const _CropView({ - Key? key, required this.cropPhotoDocumentStyle, required this.image, - }) : super(key: key); + }); + final CropPhotoDocumentStyle cropPhotoDocumentStyle; + final File image; @override Widget build(BuildContext context) { @@ -98,10 +112,12 @@ class _CropView extends StatelessWidget { current.imageCropped != previous.imageCropped, listener: (context, state) { if (state.imageCropped != null) { - context.read().add(AppLoadCroppedPhoto( - image: state.imageCropped!, - area: state.areaParsed!, - )); + context.read().add( + AppLoadCroppedPhoto( + image: state.imageCropped!, + area: state.areaParsed!, + ), + ); } }, ), @@ -143,6 +159,8 @@ class _CropView extends StatelessWidget { return CustomPaint( painter: BorderCropAreaPainter( area: state, + colorBorderArea: cropPhotoDocumentStyle.colorBorderArea, + widthBorderArea: cropPhotoDocumentStyle.widthBorderArea, ), child: const SizedBox.expand(), ); diff --git a/lib/src/ui/pages/document_scanner.dart b/flutter_document_scanner/lib/src/ui/pages/document_scanner.dart similarity index 75% rename from lib/src/ui/pages/document_scanner.dart rename to flutter_document_scanner/lib/src/ui/pages/document_scanner.dart index e217b24..77a9f2e 100644 --- a/lib/src/ui/pages/document_scanner.dart +++ b/flutter_document_scanner/lib/src/ui/pages/document_scanner.dart @@ -1,10 +1,21 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_document_scanner/src/bloc/app/app_bloc.dart'; import 'package:flutter_document_scanner/src/bloc/app/app_event.dart'; import 'package:flutter_document_scanner/src/bloc/app/app_state.dart'; import 'package:flutter_document_scanner/src/document_scanner_controller.dart'; +import 'package:flutter_document_scanner/src/ui/pages/crop_photo_document_page.dart'; +import 'package:flutter_document_scanner/src/ui/pages/edit_document_photo_page.dart'; +import 'package:flutter_document_scanner/src/ui/pages/take_photo_document_page.dart'; import 'package:flutter_document_scanner/src/utils/crop_photo_document_style.dart'; import 'package:flutter_document_scanner/src/utils/dialogs.dart'; import 'package:flutter_document_scanner/src/utils/edit_photo_document_style.dart'; @@ -12,49 +23,57 @@ import 'package:flutter_document_scanner/src/utils/general_styles.dart'; import 'package:flutter_document_scanner/src/utils/model_utils.dart'; import 'package:flutter_document_scanner/src/utils/take_photo_document_style.dart'; -import 'crop_photo_document_page.dart'; -import 'edit_document_photo_page.dart'; -import 'take_photo_document_page.dart'; - +/// This class is the main page of the application class DocumentScanner extends StatelessWidget { - /// + /// Create a main page with properties and methods + /// to manage the document scanner. + const DocumentScanner({ + super.key, + this.controller, + this.generalStyles = const GeneralStyles(), + this.pageTransitionBuilder, + this.initialCameraLensDirection = CameraLensDirection.back, + this.resolutionCamera = ResolutionPreset.high, + this.takePhotoDocumentStyle = const TakePhotoDocumentStyle(), + this.cropPhotoDocumentStyle = const CropPhotoDocumentStyle(), + this.editPhotoDocumentStyle = const EditPhotoDocumentStyle(), + required this.onSave, + }); + + /// Controller to execute the different functionalities + /// using the [DocumentScannerController] final DocumentScannerController? controller; - /// + /// [generalStyles] is the [GeneralStyles] that will be used to style the + /// [DocumentScanner] widget. final GeneralStyles generalStyles; - /// + /// To change the animation performed when switching between screens + /// by using the [AnimatedSwitcherTransitionBuilder] final AnimatedSwitcherTransitionBuilder? pageTransitionBuilder; - /// Config Camera + /// Camera library [CameraLensDirection] final CameraLensDirection initialCameraLensDirection; + + /// Camera library [ResolutionPreset] final ResolutionPreset resolutionCamera; - /// + /// It is used to change the style of the [TakePhotoDocumentPage] page + /// using the [TakePhotoDocumentStyle] class. final TakePhotoDocumentStyle takePhotoDocumentStyle; - /// + /// It is used to change the style of the [CropPhotoDocumentPage] page + /// using the [CropPhotoDocumentStyle] class. final CropPhotoDocumentStyle cropPhotoDocumentStyle; - /// + /// It is used to change the style of the [EditDocumentPhotoPage] page + /// using the [EditPhotoDocumentStyle] class. final EditPhotoDocumentStyle editPhotoDocumentStyle; - /// + /// After performing the whole process of capturing the document + /// It will return it as [Uint8List]. final OnSave onSave; - const DocumentScanner({ - Key? key, - this.controller, - this.generalStyles = const GeneralStyles(), - this.pageTransitionBuilder, - this.initialCameraLensDirection = CameraLensDirection.back, - this.resolutionCamera = ResolutionPreset.high, - this.takePhotoDocumentStyle = const TakePhotoDocumentStyle(), - this.cropPhotoDocumentStyle = const CropPhotoDocumentStyle(), - this.editPhotoDocumentStyle = const EditPhotoDocumentStyle(), - required this.onSave, - }) : super(key: key); - @override Widget build(BuildContext context) { final Dialogs dialogs = Dialogs(); @@ -84,7 +103,10 @@ class DocumentScanner extends StatelessWidget { if (generalStyles.hideDefaultDialogs) return; if (state.statusTakePhotoPage == AppStatus.loading) { - dialogs.defaultDialog(context, "Taking picture"); + dialogs.defaultDialog( + context, + generalStyles.messageTakingPicture, + ); } if (state.statusTakePhotoPage == AppStatus.success) { @@ -101,7 +123,10 @@ class DocumentScanner extends StatelessWidget { if (generalStyles.hideDefaultDialogs) return; if (state.statusCropPhoto == AppStatus.loading) { - dialogs.defaultDialog(context, "Cropping picture"); + dialogs.defaultDialog( + context, + generalStyles.messageCroppingPicture, + ); } if (state.statusCropPhoto == AppStatus.success) { @@ -118,7 +143,10 @@ class DocumentScanner extends StatelessWidget { if (generalStyles.hideDefaultDialogs) return; if (state.statusEditPhoto == AppStatus.loading) { - dialogs.defaultDialog(context, "Editting picture"); + dialogs.defaultDialog( + context, + generalStyles.messageEditingPicture, + ); } if (state.statusEditPhoto == AppStatus.success) { @@ -136,17 +164,19 @@ class DocumentScanner extends StatelessWidget { if (generalStyles.hideDefaultDialogs) return; if (state.statusSavePhotoDocument == AppStatus.loading) { - dialogs.defaultDialog(context, "Saving Document"); + dialogs.defaultDialog( + context, + generalStyles.messageSavingPicture, + ); } if (state.statusSavePhotoDocument == AppStatus.success) { Navigator.pop(context); - dialogs.defaultDialog(context, "Saved Document"); } }, ), ], - child: Container( + child: ColoredBox( color: generalStyles.baseColor, child: _View( pageTransitionBuilder: pageTransitionBuilder, @@ -163,23 +193,26 @@ class DocumentScanner extends StatelessWidget { } class _View extends StatelessWidget { - final AnimatedSwitcherTransitionBuilder? pageTransitionBuilder; - final TakePhotoDocumentStyle takePhotoDocumentStyle; - final CropPhotoDocumentStyle cropPhotoDocumentStyle; - final EditPhotoDocumentStyle editPhotoDocumentStyle; - final OnSave onSave; - const _View({ - Key? key, this.pageTransitionBuilder, required this.takePhotoDocumentStyle, required this.cropPhotoDocumentStyle, required this.editPhotoDocumentStyle, required this.onSave, - }) : super(key: key); + }); + + final AnimatedSwitcherTransitionBuilder? pageTransitionBuilder; + final TakePhotoDocumentStyle takePhotoDocumentStyle; + final CropPhotoDocumentStyle cropPhotoDocumentStyle; + final EditPhotoDocumentStyle editPhotoDocumentStyle; + final OnSave onSave; @override Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + ]); + return BlocSelector( selector: (state) => state.currentPage, builder: (context, state) { @@ -212,7 +245,7 @@ class _View extends StatelessWidget { duration: const Duration(milliseconds: 400), transitionBuilder: pageTransitionBuilder ?? (child, animation) { - const begin = Offset(-1.0, 0.0); + const begin = Offset(-1, 0); const end = Offset.zero; final tween = Tween(begin: begin, end: end); diff --git a/lib/src/ui/pages/edit_document_photo_page.dart b/flutter_document_scanner/lib/src/ui/pages/edit_document_photo_page.dart similarity index 88% rename from lib/src/ui/pages/edit_document_photo_page.dart rename to flutter_document_scanner/lib/src/ui/pages/edit_document_photo_page.dart index 21ae39b..123c2d0 100644 --- a/lib/src/ui/pages/edit_document_photo_page.dart +++ b/flutter_document_scanner/lib/src/ui/pages/edit_document_photo_page.dart @@ -1,3 +1,10 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:typed_data'; import 'package:flutter/material.dart'; @@ -10,19 +17,23 @@ import 'package:flutter_document_scanner/src/bloc/edit/edit_event.dart'; import 'package:flutter_document_scanner/src/bloc/edit/edit_state.dart'; import 'package:flutter_document_scanner/src/ui/widgets/app_bar_edit_photo.dart'; import 'package:flutter_document_scanner/src/ui/widgets/bottom_bar_edit_photo.dart'; -import 'package:flutter_document_scanner/src/utils/edit_photo_document_style.dart'; import 'package:flutter_document_scanner/src/utils/image_utils.dart'; import 'package:flutter_document_scanner/src/utils/model_utils.dart'; +/// Page to edit a photo class EditDocumentPhotoPage extends StatelessWidget { - final EditPhotoDocumentStyle editPhotoDocumentStyle; - final OnSave onSave; - + /// Create a page with style const EditDocumentPhotoPage({ - Key? key, + super.key, required this.editPhotoDocumentStyle, required this.onSave, - }) : super(key: key); + }); + + /// Style of the page + final EditPhotoDocumentStyle editPhotoDocumentStyle; + + /// Callback to save the photo + final OnSave onSave; @override Widget build(BuildContext context) { @@ -33,7 +44,7 @@ class EditDocumentPhotoPage extends StatelessWidget { builder: (context, state) { if (state == null) { return const Center( - child: Text("NO IMAGE"), + child: Text('NO IMAGE'), ); } @@ -52,20 +63,21 @@ class EditDocumentPhotoPage extends StatelessWidget { } Future _onPop(BuildContext context) async { - context.read().changePage(AppPages.cropPhoto); + await context + .read() + .changePage(AppPages.cropPhoto); return false; } } class _EditView extends StatelessWidget { - final EditPhotoDocumentStyle editPhotoDocumentStyle; - final OnSave onSave; - const _EditView({ - Key? key, required this.editPhotoDocumentStyle, required this.onSave, - }) : super(key: key); + }); + + final EditPhotoDocumentStyle editPhotoDocumentStyle; + final OnSave onSave; @override Widget build(BuildContext context) { @@ -90,7 +102,7 @@ class _EditView extends StatelessWidget { if (image == null) { context.read().add( AppDocumentSaved( - isSucces: false, + isSuccess: false, ), ); return; @@ -98,7 +110,7 @@ class _EditView extends StatelessWidget { context.read().add( AppDocumentSaved( - isSucces: true, + isSuccess: true, ), ); onSave(image); @@ -112,7 +124,7 @@ class _EditView extends StatelessWidget { if (state.image != null) { context.read().add( AppNewEditedImageLoaded( - isSucces: true, + isSuccess: true, ), ); } diff --git a/lib/src/ui/pages/take_photo_document_page.dart b/flutter_document_scanner/lib/src/ui/pages/take_photo_document_page.dart similarity index 75% rename from lib/src/ui/pages/take_photo_document_page.dart rename to flutter_document_scanner/lib/src/ui/pages/take_photo_document_page.dart index bfa1f3c..38b0163 100644 --- a/lib/src/ui/pages/take_photo_document_page.dart +++ b/flutter_document_scanner/lib/src/ui/pages/take_photo_document_page.dart @@ -1,3 +1,10 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -6,13 +13,16 @@ import 'package:flutter_document_scanner/src/bloc/app/app_state.dart'; import 'package:flutter_document_scanner/src/ui/widgets/button_take_photo.dart'; import 'package:flutter_document_scanner/src/utils/take_photo_document_style.dart'; +/// Page to take a photo class TakePhotoDocumentPage extends StatelessWidget { - final TakePhotoDocumentStyle takePhotoDocumentStyle; - + /// Create a page with style const TakePhotoDocumentPage({ - Key? key, + super.key, required this.takePhotoDocumentStyle, - }) : super(key: key); + }); + + /// Style of the page + final TakePhotoDocumentStyle takePhotoDocumentStyle; @override Widget build(BuildContext context) { @@ -40,12 +50,11 @@ class TakePhotoDocumentPage extends StatelessWidget { } class _CameraPreview extends StatelessWidget { - final TakePhotoDocumentStyle takePhotoDocumentStyle; - const _CameraPreview({ - Key? key, required this.takePhotoDocumentStyle, - }) : super(key: key); + }); + + final TakePhotoDocumentStyle takePhotoDocumentStyle; @override Widget build(BuildContext context) { @@ -55,7 +64,7 @@ class _CameraPreview extends StatelessWidget { if (state == null) { return const Center( child: Text( - "No Camera", + 'No Camera', ), ); } @@ -65,10 +74,10 @@ class _CameraPreview extends StatelessWidget { children: [ // * Camera Positioned( - top: takePhotoDocumentStyle.top ?? 0, - bottom: takePhotoDocumentStyle.bottom ?? 0, - left: takePhotoDocumentStyle.left ?? 0, - right: takePhotoDocumentStyle.right ?? 0, + top: takePhotoDocumentStyle.top, + bottom: takePhotoDocumentStyle.bottom, + left: takePhotoDocumentStyle.left, + right: takePhotoDocumentStyle.right, child: CameraPreview(state), ), @@ -77,7 +86,9 @@ class _CameraPreview extends StatelessWidget { ...takePhotoDocumentStyle.children!, /// Default - const ButtonTakePhoto(), + ButtonTakePhoto( + takePhotoDocumentStyle: takePhotoDocumentStyle, + ), ], ); }, diff --git a/lib/src/ui/widgets/app_bar_crop_photo.dart b/flutter_document_scanner/lib/src/ui/widgets/app_bar_crop_photo.dart similarity index 73% rename from lib/src/ui/widgets/app_bar_crop_photo.dart rename to flutter_document_scanner/lib/src/ui/widgets/app_bar_crop_photo.dart index 6fe1d11..0b00852 100644 --- a/lib/src/ui/widgets/app_bar_crop_photo.dart +++ b/flutter_document_scanner/lib/src/ui/widgets/app_bar_crop_photo.dart @@ -1,18 +1,30 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_document_scanner/flutter_document_scanner.dart'; +/// Default AppBar of the Crop Photo page class AppBarCropPhoto extends StatelessWidget { - final CropPhotoDocumentStyle cropPhotoDocumentStyle; - + /// Create a widget with style const AppBarCropPhoto({ - Key? key, + super.key, required this.cropPhotoDocumentStyle, - }) : super(key: key); + }); + + /// The style of the page + final CropPhotoDocumentStyle cropPhotoDocumentStyle; @override Widget build(BuildContext context) { - if (cropPhotoDocumentStyle.hideAppBarDefault) return Container(); + if (cropPhotoDocumentStyle.hideAppBarDefault) { + return const SizedBox.shrink(); + } return Positioned( top: 0, @@ -40,12 +52,12 @@ class AppBarCropPhoto extends StatelessWidget { TextButton( onPressed: () => context.read().cropPhoto(), + style: TextButton.styleFrom( + foregroundColor: Colors.white, + ), child: Text( cropPhotoDocumentStyle.textButtonSave, ), - style: TextButton.styleFrom( - primary: Colors.white, - ), ), ], ), diff --git a/lib/src/ui/widgets/app_bar_edit_photo.dart b/flutter_document_scanner/lib/src/ui/widgets/app_bar_edit_photo.dart similarity index 74% rename from lib/src/ui/widgets/app_bar_edit_photo.dart rename to flutter_document_scanner/lib/src/ui/widgets/app_bar_edit_photo.dart index a359079..b99c329 100644 --- a/lib/src/ui/widgets/app_bar_edit_photo.dart +++ b/flutter_document_scanner/lib/src/ui/widgets/app_bar_edit_photo.dart @@ -1,19 +1,30 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_document_scanner/flutter_document_scanner.dart'; -import 'package:flutter_document_scanner/src/utils/edit_photo_document_style.dart'; +/// Default AppBar of the Edit Photo page class AppBarEditPhoto extends StatelessWidget { - final EditPhotoDocumentStyle editPhotoDocumentStyle; - + /// Create a widget with style const AppBarEditPhoto({ - Key? key, + super.key, required this.editPhotoDocumentStyle, - }) : super(key: key); + }); + + /// The style of the page + final EditPhotoDocumentStyle editPhotoDocumentStyle; @override Widget build(BuildContext context) { - if (editPhotoDocumentStyle.hideAppBarDefault) return Container(); + if (editPhotoDocumentStyle.hideAppBarDefault) { + return const SizedBox.shrink(); + } return Positioned( top: 0, @@ -42,12 +53,12 @@ class AppBarEditPhoto extends StatelessWidget { TextButton( onPressed: () => context.read().savePhotoDocument(), + style: TextButton.styleFrom( + foregroundColor: Colors.white, + ), child: Text( editPhotoDocumentStyle.textButtonSave, ), - style: TextButton.styleFrom( - primary: Colors.white, - ), ), ], ), diff --git a/lib/src/ui/widgets/bottom_bar_edit_photo.dart b/flutter_document_scanner/lib/src/ui/widgets/bottom_bar_edit_photo.dart similarity index 71% rename from lib/src/ui/widgets/bottom_bar_edit_photo.dart rename to flutter_document_scanner/lib/src/ui/widgets/bottom_bar_edit_photo.dart index 31cd8a4..e2364e7 100644 --- a/lib/src/ui/widgets/bottom_bar_edit_photo.dart +++ b/flutter_document_scanner/lib/src/ui/widgets/bottom_bar_edit_photo.dart @@ -1,25 +1,36 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_document_scanner/flutter_document_scanner.dart'; -import 'package:flutter_document_scanner/src/utils/edit_photo_document_style.dart'; +/// Default BottomBar of the Edit Photo page class BottomBarEditPhoto extends StatelessWidget { - final EditPhotoDocumentStyle editPhotoDocumentStyle; - + /// Create a widget with style const BottomBarEditPhoto({ - Key? key, + super.key, required this.editPhotoDocumentStyle, - }) : super(key: key); + }); + + /// The style of the page + final EditPhotoDocumentStyle editPhotoDocumentStyle; @override Widget build(BuildContext context) { - if (editPhotoDocumentStyle.hideBottomBarDefault) return Container(); + if (editPhotoDocumentStyle.hideBottomBarDefault) { + return const SizedBox.shrink(); + } return Positioned( bottom: MediaQuery.of(context).padding.bottom, left: 0, right: 0, - child: Container( + child: DecoratedBox( decoration: BoxDecoration( color: Theme.of(context).primaryColor, ), @@ -32,11 +43,11 @@ class BottomBarEditPhoto extends StatelessWidget { context.read().applyFilter( FilterType.natural, ), - child: const Text( - "Natural", - ), style: TextButton.styleFrom( - primary: Colors.white, + foregroundColor: Colors.white, + ), + child: const Text( + 'Natural', ), ), @@ -46,11 +57,11 @@ class BottomBarEditPhoto extends StatelessWidget { context.read().applyFilter( FilterType.gray, ), - child: const Text( - "GRAY", - ), style: TextButton.styleFrom( - primary: Colors.white, + foregroundColor: Colors.white, + ), + child: const Text( + 'GRAY', ), ), @@ -60,11 +71,11 @@ class BottomBarEditPhoto extends StatelessWidget { context.read().applyFilter( FilterType.eco, ), - child: const Text( - "ECO", - ), style: TextButton.styleFrom( - primary: Colors.white, + foregroundColor: Colors.white, + ), + child: const Text( + 'ECO', ), ), ], diff --git a/lib/src/ui/widgets/button_take_photo.dart b/flutter_document_scanner/lib/src/ui/widgets/button_take_photo.dart similarity index 64% rename from lib/src/ui/widgets/button_take_photo.dart rename to flutter_document_scanner/lib/src/ui/widgets/button_take_photo.dart index c0ef8fa..7edc815 100644 --- a/lib/src/ui/widgets/button_take_photo.dart +++ b/flutter_document_scanner/lib/src/ui/widgets/button_take_photo.dart @@ -1,19 +1,30 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_document_scanner/src/document_scanner_controller.dart'; +import 'package:flutter_document_scanner/src/utils/take_photo_document_style.dart'; +/// Default button that takes the photo class ButtonTakePhoto extends StatelessWidget { - final bool hide; - + /// Create a widget const ButtonTakePhoto({ - Key? key, - this.hide = false, - }) : super(key: key); + super.key, + required this.takePhotoDocumentStyle, + }); + + /// The style of the page + final TakePhotoDocumentStyle takePhotoDocumentStyle; @override Widget build(BuildContext context) { - if (hide) { - return Container(); + if (takePhotoDocumentStyle.hideDefaultButtonTakePicture) { + return const SizedBox.shrink(); } return Positioned( diff --git a/lib/src/ui/widgets/mask_crop.dart b/flutter_document_scanner/lib/src/ui/widgets/mask_crop.dart similarity index 65% rename from lib/src/ui/widgets/mask_crop.dart rename to flutter_document_scanner/lib/src/ui/widgets/mask_crop.dart index a1bacde..cf2fc1b 100644 --- a/lib/src/ui/widgets/mask_crop.dart +++ b/flutter_document_scanner/lib/src/ui/widgets/mask_crop.dart @@ -1,3 +1,10 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:ui' as ui; import 'package:flutter/material.dart'; @@ -5,15 +12,20 @@ import 'package:flutter_document_scanner/src/models/area.dart'; import 'package:flutter_document_scanner/src/utils/crop_area_clipper.dart'; import 'package:flutter_document_scanner/src/utils/crop_photo_document_style.dart'; +/// Mask that is superimposed on the image by applying color and/or filter to it class MaskCrop extends StatelessWidget { - final Area area; - final CropPhotoDocumentStyle cropPhotoDocumentStyle; - + /// Create a widget with style const MaskCrop({ - Key? key, + super.key, required this.area, required this.cropPhotoDocumentStyle, - }) : super(key: key); + }); + + /// Area to be occupied by the mask when cropping the image + final Area area; + + /// The style of the page + final CropPhotoDocumentStyle cropPhotoDocumentStyle; @override Widget build(BuildContext context) { @@ -25,7 +37,7 @@ class MaskCrop extends StatelessWidget { sigmaX: 5, sigmaY: 5, ), - child: Container( + child: ColoredBox( color: cropPhotoDocumentStyle.maskColor ?? const Color(0xffb9c2d5).withOpacity(0.1), ), diff --git a/flutter_document_scanner/lib/src/utils/border_crop_area_painter.dart b/flutter_document_scanner/lib/src/utils/border_crop_area_painter.dart new file mode 100644 index 0000000..dda0e2f --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/border_crop_area_painter.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner/src/models/area.dart'; +import 'package:flutter_document_scanner/src/utils/crop_photo_document_style.dart'; + +/// Border +class BorderCropAreaPainter extends CustomPainter { + /// Create a painter for the given [Area]. + const BorderCropAreaPainter({ + required this.area, + required this.colorBorderArea, + required this.widthBorderArea, + }); + + /// The area to paint + final Area area; + + /// Color of the border covering the clipping mask + /// + /// Can be modified from [CropPhotoDocumentStyle.colorBorderArea] + final Color colorBorderArea; + + /// Width of the border covering the clipping mask + /// + /// Can be modified from [CropPhotoDocumentStyle.widthBorderArea] + final double widthBorderArea; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = widthBorderArea + ..color = colorBorderArea; + + final path = Path() + ..moveTo(area.topLeft.x, area.topLeft.y) + ..lineTo(area.topRight.x, area.topRight.y) + ..lineTo(area.bottomRight.x, area.bottomRight.y) + ..lineTo(area.bottomLeft.x, area.bottomLeft.y) + ..close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/src/utils/crop_area_clipper.dart b/flutter_document_scanner/lib/src/utils/crop_area_clipper.dart similarity index 63% rename from lib/src/utils/crop_area_clipper.dart rename to flutter_document_scanner/lib/src/utils/crop_area_clipper.dart index b05ca99..9df5bd6 100644 --- a/lib/src/utils/crop_area_clipper.dart +++ b/flutter_document_scanner/lib/src/utils/crop_area_clipper.dart @@ -1,11 +1,21 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'package:flutter/material.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; +/// Clipper for the crop area class CropAreaClipper extends CustomClipper { - final Area area; - + /// Create a clipper for the given [Area]. const CropAreaClipper(this.area); + /// The area to clip + final Area area; + @override Path getClip(Size size) { return Path() @@ -18,7 +28,7 @@ class CropAreaClipper extends CustomClipper { ..close(), Offset.zero, ) - ..addRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height)) + ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) ..fillType = PathFillType.evenOdd; } diff --git a/flutter_document_scanner/lib/src/utils/crop_photo_document_style.dart b/flutter_document_scanner/lib/src/utils/crop_photo_document_style.dart new file mode 100644 index 0000000..aa75af8 --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/crop_photo_document_style.dart @@ -0,0 +1,89 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter_document_scanner/src/models/area.dart'; + +/// The style of the crop photo document. +@immutable +class CropPhotoDocumentStyle { + /// Create a instance of [CropPhotoDocumentStyle]. + const CropPhotoDocumentStyle({ + this.hideAppBarDefault = false, + this.textButtonSave = 'CROP', + this.children, + this.top = 0, + this.bottom = 0, + this.left = 0, + this.right = 0, + this.maskColor, + this.maskFilter, + this.dotSize = 18, + this.dotRadius = 30, + this.defaultAreaInitial = const Area( + topRight: Point(300, 80), + topLeft: Point(40, 80), + bottomLeft: Point(40, 450), + bottomRight: Point(300, 450), + ), + this.minDistanceDots = 30, + this.colorBorderArea = Colors.white, + this.widthBorderArea = 3, + }); + + /// Hide the app bar default. + final bool hideAppBarDefault; + + /// Text of save button + final String textButtonSave; + + /// Widget to be displayed on the page + final List? children; + + /// The distance that the top edge of the image is inserted from + /// the top of the stack. + final double top; + + /// The distance that the bottom edge of the image is inserted from + /// the bottom of the stack. + final double bottom; + + /// The distance that the left edge of the image is inserted from + /// the left of the stack. + final double left; + + /// The distance that the right edge of the image is inserted from + /// the right of the stack. + final double right; + + /// Mask color shown for cropping + final Color? maskColor; + + /// Mask filter shown for cropping + final ui.ImageFilter? maskFilter; + + /// Size of the dots + final double dotSize; + + /// Radius of the dots + final double dotRadius; + + /// Default area to be occupied by the mask when cropping the image + final Area defaultAreaInitial; + + /// Minimum distance between the 4 dots + final int minDistanceDots; + + /// Color of the border covering the clipping mask + final Color colorBorderArea; + + /// Width of the border covering the clipping mask + final double widthBorderArea; +} diff --git a/flutter_document_scanner/lib/src/utils/dialogs.dart b/flutter_document_scanner/lib/src/utils/dialogs.dart new file mode 100644 index 0000000..acd3aab --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/dialogs.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +/// Define the dialogs to be displayed in the app +class Dialogs { + /// Show a basic dialog with a [message] + void defaultDialog( + BuildContext context, + String message, + ) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + title: Text( + message, + ), + ); + }, + ); + } +} diff --git a/lib/src/utils/dot_utils.dart b/flutter_document_scanner/lib/src/utils/dot_utils.dart similarity index 58% rename from lib/src/utils/dot_utils.dart rename to flutter_document_scanner/lib/src/utils/dot_utils.dart index 6848464..b9909e8 100644 --- a/lib/src/utils/dot_utils.dart +++ b/flutter_document_scanner/lib/src/utils/dot_utils.dart @@ -1,9 +1,29 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_document_scanner/src/models/area.dart'; +import 'package:flutter_document_scanner/src/utils/crop_photo_document_style.dart'; +/// Dots utilities class DotUtils { + /// Create a dot utils + DotUtils({ + required this.minDistanceDots, + }); + + /// Minimum distance between the dots that can be + /// + /// Can be modified from [CropPhotoDocumentStyle.minDistanceDots] + final int minDistanceDots; + + /// Move the entire area by the given delta values. Area moveArea({ required Area original, required double deltaX, @@ -14,41 +34,41 @@ class DotUtils { topRight: Point( max( min(original.topRight.x + deltaX, imageRect.right), - original.topLeft.x + 30, + original.topLeft.x + minDistanceDots, ), min( max(original.topRight.y + deltaY, imageRect.top), - original.bottomLeft.y - 30, + original.bottomLeft.y - minDistanceDots, ), ), topLeft: Point( min( max(original.topLeft.x + deltaX, imageRect.left), - original.topRight.x - 30, + original.topRight.x - minDistanceDots, ), min( max(original.topLeft.y + deltaY, imageRect.top), - original.bottomLeft.y - 30, + original.bottomLeft.y - minDistanceDots, ), ), bottomLeft: Point( min( max(original.bottomLeft.x + deltaX, imageRect.left), - original.bottomRight.x - 30, + original.bottomRight.x - minDistanceDots, ), max( min(original.bottomLeft.y + deltaY, imageRect.bottom), - original.topLeft.y + 30, + original.topLeft.y + minDistanceDots, ), ), bottomRight: Point( max( min(original.bottomRight.x + deltaX, imageRect.right), - original.bottomLeft.x + 30, + original.bottomLeft.x + minDistanceDots, ), max( min(original.bottomRight.y + deltaY, imageRect.bottom), - original.topRight.y + 30, + original.topRight.y + minDistanceDots, ), ), ); @@ -56,6 +76,8 @@ class DotUtils { return newArea; } + /// Move dot top left by the given delta values. + /// and respecting a space of [minDistanceDots] between the other dots. Point moveTopLeft({ required Point original, required double deltaX, @@ -65,16 +87,18 @@ class DotUtils { }) { final newX = min( max(imageRect.left, original.x + deltaX), - originalArea.topRight.x - 30, + originalArea.topRight.x - minDistanceDots, ); final newY = min( max(original.y + deltaY, imageRect.top), - originalArea.bottomLeft.y - 30, + originalArea.bottomLeft.y - minDistanceDots, ); return Point(newX, newY); } + /// Move dot top right by the given delta values + /// and respecting a space of [minDistanceDots] between the other dots. Point moveTopRight({ required Point original, required double deltaX, @@ -84,16 +108,18 @@ class DotUtils { }) { final newX = max( min(imageRect.right, original.x + deltaX), - originalArea.topLeft.x + 30, + originalArea.topLeft.x + minDistanceDots, ); final newY = min( max(original.y + deltaY, imageRect.top), - originalArea.bottomLeft.y - 30, + originalArea.bottomLeft.y - minDistanceDots, ); return Point(newX, newY); } + /// Move dot bottom left by the given delta values + /// and respecting a space of [minDistanceDots] between the other dots. Point moveBottomLeft({ required Point original, required double deltaX, @@ -103,16 +129,18 @@ class DotUtils { }) { final newX = min( max(imageRect.left, original.x + deltaX), - originalArea.bottomRight.x - 30, + originalArea.bottomRight.x - minDistanceDots, ); final newY = max( min(original.y + deltaY, imageRect.bottom), - originalArea.topLeft.y + 30, + originalArea.topLeft.y + minDistanceDots, ); return Point(newX, newY); } + /// Move the bottom dot to the right by the given delta values + /// and respecting a space of [minDistanceDots] between the other dots. Point moveBottomRight({ required Point original, required double deltaX, @@ -122,11 +150,11 @@ class DotUtils { }) { final newX = max( min(imageRect.right, original.x + deltaX), - originalArea.bottomLeft.x + 30, + originalArea.bottomLeft.x + minDistanceDots, ); final newY = max( min(original.y + deltaY, imageRect.bottom), - originalArea.topRight.y + 30, + originalArea.topRight.y + minDistanceDots, ); return Point(newX, newY); diff --git a/flutter_document_scanner/lib/src/utils/edit_photo_document_style.dart b/flutter_document_scanner/lib/src/utils/edit_photo_document_style.dart new file mode 100644 index 0000000..c138b3f --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/edit_photo_document_style.dart @@ -0,0 +1,52 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +/// The style of the edit photo document. +@immutable +class EditPhotoDocumentStyle { + /// Create a instance of [EditPhotoDocumentStyle]. + const EditPhotoDocumentStyle({ + this.hideAppBarDefault = false, + this.hideBottomBarDefault = false, + this.textButtonSave = 'SAVE', + this.children, + this.top = 0, + this.bottom = 0, + this.left = 0, + this.right = 0, + }); + + /// Hide the app bar default + final bool hideAppBarDefault; + + /// Hide the bottom bar default + final bool hideBottomBarDefault; + + /// Text of save button + final String textButtonSave; + + /// Widget to be displayed on the page + final List? children; + + /// The distance that the top edge of the image is inserted from + /// the top of the stack. + final double top; + + /// The distance that the bottom edge of the image is inserted from + /// the bottom of the stack. + final double bottom; + + /// The distance that the left edge of the image is inserted from + /// the left of the stack. + final double left; + + /// The distance that the right edge of the image is inserted from + /// the right of the stack. + final double right; +} diff --git a/flutter_document_scanner/lib/src/utils/general_styles.dart b/flutter_document_scanner/lib/src/utils/general_styles.dart new file mode 100644 index 0000000..11ffc9f --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/general_styles.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +/// The style of the document scanner. +@immutable +class GeneralStyles { + /// Create a instance of [GeneralStyles]. + const GeneralStyles({ + this.hideDefaultBottomNavigation = false, + this.hideDefaultDialogs = false, + this.baseColor = Colors.white, + this.messageTakingPicture = 'Taking picture', + this.messageCroppingPicture = 'Cropping picture', + this.messageEditingPicture = 'Editing picture', + this.messageSavingPicture = 'Saving picture', + }); + + /// Hide the default bottom navigation. + final bool hideDefaultBottomNavigation; + + /// Hide the default dialogs of the app. + final bool hideDefaultDialogs; + + /// The base color of the app. + final Color baseColor; + + /// Message to be displayed when taking picture + /// (only if [hideDefaultDialogs] is false) + final String messageTakingPicture; + + /// Message to be displayed when cropping picture + /// (only if [hideDefaultDialogs] is false) + final String messageCroppingPicture; + + /// Message to be displayed when editing picture + /// (only if [hideDefaultDialogs] is false) + final String messageEditingPicture; + + /// Message to be displayed when saving picture + /// (only if [hideDefaultDialogs] is false) + final String messageSavingPicture; +} diff --git a/lib/src/utils/image_utils.dart b/flutter_document_scanner/lib/src/utils/image_utils.dart similarity index 64% rename from lib/src/utils/image_utils.dart rename to flutter_document_scanner/lib/src/utils/image_utils.dart index 6e685e4..f1cadcb 100644 --- a/lib/src/utils/image_utils.dart +++ b/flutter_document_scanner/lib/src/utils/image_utils.dart @@ -1,15 +1,20 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:math'; import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_document_scanner/flutter_document_scanner.dart'; -import 'package:flutter_document_scanner/src/models/contour.dart'; +/// ImageUtils class class ImageUtils { - final _methodChannel = const MethodChannel( - "christian.com/flutter_document_scanner", - ); + FlutterDocumentScannerPlatform get _platform => + FlutterDocumentScannerPlatform.instance; /// Calculates the rect of the image Rect imageRect(Size screenSize) { @@ -31,30 +36,31 @@ class ImageUtils { double? minContourArea, }) async { try { - final contour = await _methodChannel.invokeMethod("findContourPhoto", { - "byteData": byteData, - "minContourArea": minContourArea ?? 80000.0, - }); + final contour = await _platform.findContourPhoto( + byteData: byteData, + minContourArea: minContourArea ?? 80000.0, + ); + if (contour == null) return null; - final contourParsed = Contour.fromMap(Map.from(contour)); - if (contourParsed.points.isEmpty) return null; - if (contourParsed.points.length != 4) return null; + // final contourParsed = Contour.fromMap(contour); + if (contour.points.isEmpty) return null; + if (contour.points.length != 4) return null; // Identify each side of the contour int numTopFound = 0; int numBottomFound = 0; - Point top1 = const Point(0, 0); - Point top2 = const Point(0, 0); + Point top1 = const Point(0, 0); + Point top2 = const Point(0, 0); - Point bottom1 = const Point(0, 0); - Point bottom2 = const Point(0, 0); + Point bottom1 = const Point(0, 0); + Point bottom2 = const Point(0, 0); - Point lastTopFound = const Point(0, 1000000); - Point lastBottomFound = const Point(0, 0); + Point lastTopFound = const Point(0, 1000000); + Point lastBottomFound = const Point(0, 0); for (int i = 0; i < 4; i++) { - for (final point in contourParsed.points) { + for (final point in contour.points) { if (point.y > lastBottomFound.y) { if (bottom1.y == 0 || point.y != bottom1.y) { lastBottomFound = point; @@ -90,11 +96,11 @@ class ImageUtils { lastBottomFound = const Point(0, 0); } - Point topLeft = const Point(0, 0); - Point topRight = const Point(0, 0); + Point topLeft = const Point(0, 0); + Point topRight = const Point(0, 0); - Point bottomLeft = const Point(0, 0); - Point bottomRight = const Point(0, 0); + Point bottomLeft = const Point(0, 0); + Point bottomRight = const Point(0, 0); if (top1.x < top2.x) { topLeft = top1; @@ -129,8 +135,7 @@ class ImageUtils { bottomRight: bottomRight, ); } catch (e) { - // TODO: add error handler - // print(e); + // TODO(utils): add error handler return null; } } @@ -142,21 +147,14 @@ class ImageUtils { Contour contour, ) async { try { - final newImage = - await _methodChannel.invokeMethod("adjustingPerspective", { - "byteData": byteData, - "points": contour.points - .map((e) => { - "x": e.x, - "y": e.y, - }) - .toList(), - }); + final newImage = await _platform.adjustingPerspective( + byteData: byteData, + contour: contour, + ); return newImage; } catch (e) { - // TODO: add error handler - // print(e); + // TODO(utils): add error handler return null; } } @@ -167,10 +165,10 @@ class ImageUtils { FilterType filter, ) async { try { - final newImage = await _methodChannel.invokeMethod("applyFilter", { - "byteData": byteData, - "filter": filter.name, - }); + final newImage = await _platform.applyFilter( + byteData: byteData, + filter: filter, + ); if (newImage == null) { return byteData; @@ -178,7 +176,7 @@ class ImageUtils { return newImage; } catch (e) { - // TODO: add error handler + // TODO(utils): add error handler // print(e); return byteData; } diff --git a/flutter_document_scanner/lib/src/utils/model_utils.dart b/flutter_document_scanner/lib/src/utils/model_utils.dart new file mode 100644 index 0000000..f4f72bd --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/model_utils.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +/// Value to be able to assign null in the copyWith of the models +const valueNull = 'valueNull'; + +/// Define how the saving function is to be used +typedef OnSave = void Function(Uint8List imageBytes); diff --git a/flutter_document_scanner/lib/src/utils/take_photo_document_style.dart b/flutter_document_scanner/lib/src/utils/take_photo_document_style.dart new file mode 100644 index 0000000..599b972 --- /dev/null +++ b/flutter_document_scanner/lib/src/utils/take_photo_document_style.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +/// The style of the take photo document. +@immutable +class TakePhotoDocumentStyle { + /// Create a instance of [TakePhotoDocumentStyle]. + const TakePhotoDocumentStyle({ + this.onLoading = const Center( + child: CircularProgressIndicator(), + ), + this.children, + this.top = 0, + this.bottom = 0, + this.left = 0, + this.right = 0, + this.hideDefaultButtonTakePicture = false, + }); + + /// Widget to be displayed while loading the camera + final Widget onLoading; + + /// Widget to be displayed on the page + final List? children; + + /// The distance that the top edge of the image is inserted from + /// the top of the stack. + final double top; + + /// The distance that the bottom edge of the image is inserted from + /// the bottom of the stack. + final double bottom; + + /// The distance that the left edge of the image is inserted from + /// the left of the stack. + final double left; + + /// The distance that the right edge of the image is inserted from + /// the right of the stack. + final double right; + + /// Hide the default button to take picture + final bool hideDefaultButtonTakePicture; +} diff --git a/flutter_document_scanner/pubspec.yaml b/flutter_document_scanner/pubspec.yaml new file mode 100644 index 0000000..aea7f2d --- /dev/null +++ b/flutter_document_scanner/pubspec.yaml @@ -0,0 +1,38 @@ +name: flutter_document_scanner +description: A Flutter plugin that allows the management of taking, cropping and applying filters to an image, using the camera plugin +repository: https://github.com/criistian14/flutter_document_scanner/tree/master/flutter_document_scanner +version: 1.0.0 + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +flutter: + plugin: + platforms: + android: + default_package: flutter_document_scanner_android + ios: + default_package: flutter_document_scanner_ios + +dependencies: + flutter: + sdk: flutter + bloc: ^8.0.3 + camera: ^0.10.5+5 + equatable: ^2.0.3 + flutter_bloc: ^8.0.1 + flutter_document_scanner_android: + path: ../flutter_document_scanner_android + flutter_document_scanner_ios: + path: ../flutter_document_scanner_ios + flutter_document_scanner_platform_interface: + path: ../flutter_document_scanner_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + mocktail: ^0.3.0 + plugin_platform_interface: ^2.0.0 + very_good_analysis: ^3.0.1 + bloc_test: ^9.0.3 diff --git a/flutter_document_scanner/test/flutter_document_scanner_test.dart b/flutter_document_scanner/test/flutter_document_scanner_test.dart new file mode 100644 index 0000000..b7d4aad --- /dev/null +++ b/flutter_document_scanner/test/flutter_document_scanner_test.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockFlutterDocumentScannerPlatform extends Mock + with MockPlatformInterfaceMixin + implements FlutterDocumentScannerPlatform {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('FlutterDocumentScanner', () { + late FlutterDocumentScannerPlatform flutterDocumentScannerPlatform; + + setUp(() { + flutterDocumentScannerPlatform = MockFlutterDocumentScannerPlatform(); + FlutterDocumentScannerPlatform.instance = flutterDocumentScannerPlatform; + }); + + group('getPlatformName', () { + // test('returns correct name when platform implementation exists', + // () async { + // const platformName = '__test_platform__'; + // when( + // () => flutterDocumentScannerPlatform.getPlatformName(), + // ).thenAnswer((_) async => platformName); + // + // // final actualPlatformName = await getPlatformName(); + // // expect(actualPlatformName, equals(platformName)); + // }); + // + // test('throws exception when platform implementation is missing', + // () async { + // when( + // () => flutterDocumentScannerPlatform.getPlatformName(), + // ).thenAnswer((_) async => null); + // + // // expect(getPlatformName, throwsException); + // }); + }); + }); +} diff --git a/flutter_document_scanner_android/CHANGELOG.md b/flutter_document_scanner_android/CHANGELOG.md new file mode 100644 index 0000000..53c9e57 --- /dev/null +++ b/flutter_document_scanner_android/CHANGELOG.md @@ -0,0 +1,6 @@ +## 1.0.0 +* Stable version with basic functionality and new structure + + +# 0.5.0 +- Initial release of this plugin. \ No newline at end of file diff --git a/flutter_document_scanner_android/LICENSE b/flutter_document_scanner_android/LICENSE new file mode 100644 index 0000000..96ff66a --- /dev/null +++ b/flutter_document_scanner_android/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Christian Betancourt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flutter_document_scanner_android/README.md b/flutter_document_scanner_android/README.md new file mode 100644 index 0000000..75b2fbb --- /dev/null +++ b/flutter_document_scanner_android/README.md @@ -0,0 +1,15 @@ +# flutter_document_scanner_android + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] + +The Android implementation of [`flutter_document_scanner`][pub_link]. + +## Usage + +This package is [endorsed][endorsed_link], which means you can simply use `flutter_document_scanner` +normally. This package will be automatically included in your app when you do. + +[pub_link]: https://pub.dev/packages/flutter_document_scanner +[endorsed_link]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/flutter_document_scanner_android/analysis_options.yaml b/flutter_document_scanner_android/analysis_options.yaml new file mode 100644 index 0000000..d0fffaf --- /dev/null +++ b/flutter_document_scanner_android/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.3.0.1.yaml diff --git a/android/.gitignore b/flutter_document_scanner_android/android/.gitignore similarity index 89% rename from android/.gitignore rename to flutter_document_scanner_android/android/.gitignore index c6cbe56..2665975 100644 --- a/android/.gitignore +++ b/flutter_document_scanner_android/android/.gitignore @@ -5,4 +5,4 @@ /.idea/libraries .DS_Store /build -/captures +/captures \ No newline at end of file diff --git a/android/build.gradle b/flutter_document_scanner_android/android/build.gradle similarity index 74% rename from android/build.gradle rename to flutter_document_scanner_android/android/build.gradle index b6093e5..eb94034 100644 --- a/android/build.gradle +++ b/flutter_document_scanner_android/android/build.gradle @@ -1,4 +1,4 @@ -group 'com.christian.flutter_document_scanner' +group 'com.christian.flutterDocumentScanner' version '1.0-SNAPSHOT' buildscript { @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -24,9 +24,8 @@ rootProject.allprojects { apply plugin: 'com.android.library' apply plugin: 'kotlin-android' - android { - compileSdkVersion 31 + compileSdkVersion 30 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -42,14 +41,11 @@ android { } defaultConfig { - minSdkVersion 21 + minSdkVersion 16 } - ndkVersion '22.1.7171670' - buildToolsVersion '30.0.2' } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.quickbirdstudios:opencv:4.5.1' - implementation 'com.android.support:support-annotations:28.0.0' } \ No newline at end of file diff --git a/flutter_document_scanner_android/android/settings.gradle b/flutter_document_scanner_android/android/settings.gradle new file mode 100644 index 0000000..578a163 --- /dev/null +++ b/flutter_document_scanner_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'flutter_document_scanner_android' diff --git a/android/src/main/AndroidManifest.xml b/flutter_document_scanner_android/android/src/main/AndroidManifest.xml similarity index 60% rename from android/src/main/AndroidManifest.xml rename to flutter_document_scanner_android/android/src/main/AndroidManifest.xml index d7c74bd..3d7ceb8 100644 --- a/android/src/main/AndroidManifest.xml +++ b/flutter_document_scanner_android/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="com.christian.flutterDocumentScanner"> diff --git a/android/src/main/kotlin/com/christian/flutter_document_scanner/FlutterDocumentScannerPlugin.kt b/flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/FlutterDocumentScannerPlugin.kt similarity index 63% rename from android/src/main/kotlin/com/christian/flutter_document_scanner/FlutterDocumentScannerPlugin.kt rename to flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/FlutterDocumentScannerPlugin.kt index ae29a87..8e544d7 100644 --- a/android/src/main/kotlin/com/christian/flutter_document_scanner/FlutterDocumentScannerPlugin.kt +++ b/flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/FlutterDocumentScannerPlugin.kt @@ -1,6 +1,9 @@ -package com.christian.flutter_document_scanner +package com.christian.flutterDocumentScanner import android.util.Log +import android.content.Context +import androidx.annotation.NonNull + import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @@ -8,36 +11,30 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import org.opencv.android.OpenCVLoader -/** FlutterDocumentScannerPlugin */ + class FlutterDocumentScannerPlugin : FlutterPlugin, MethodCallHandler { - private var OpenCVFLag = false + private lateinit var channel: MethodChannel + private var context: Context? = null + private var openCVFLag = false companion object { const val TAG = "com.christian.Log.Tag" - const val PLUGIN_ID = "christian.com/flutter_document_scanner" + const val PLUGIN_ID = "flutter_document_scanner_android" } - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel: MethodChannel - - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel( - flutterPluginBinding.binaryMessenger, - FlutterDocumentScannerPlugin.PLUGIN_ID - ) + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, PLUGIN_ID) channel.setMethodCallHandler(this) + context = flutterPluginBinding.applicationContext } - override fun onMethodCall(call: MethodCall, result: Result) { - if (!OpenCVFLag) { + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (!openCVFLag) { if (!OpenCVLoader.initDebug()) { - Log.i(FlutterDocumentScannerPlugin.TAG, "Unable to load OpenCV") + Log.i(TAG, "Unable to load OpenCV") } else { - OpenCVFLag = true - Log.i(FlutterDocumentScannerPlugin.TAG, "OpenCV loaded Successfully") + openCVFLag = true + Log.i(TAG, "OpenCV loaded Successfully") } } @@ -48,7 +45,7 @@ class FlutterDocumentScannerPlugin : FlutterPlugin, MethodCallHandler { OpenCVPlugin.findContourPhoto( result, call.argument("byteData") as ByteArray, - call.argument("minContourArea") as Double + call.argument("minContourArea") as Double ) } catch (e: Exception) { result.error("FlutterDocumentScanner-Error", "Android: " + e.message, e) @@ -72,7 +69,7 @@ class FlutterDocumentScannerPlugin : FlutterPlugin, MethodCallHandler { OpenCVPlugin.applyFilter( result, call.argument("byteData") as ByteArray, - call.argument>>("filter") as String + call.argument("filter") as Int ) } catch (e: Exception) { result.error("FlutterDocumentScanner-Error", "Android: " + e.message, e) @@ -84,7 +81,8 @@ class FlutterDocumentScannerPlugin : FlutterPlugin, MethodCallHandler { } } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) + context = null } } diff --git a/android/src/main/kotlin/com/christian/flutter_document_scanner/OpenCVPlugin.kt b/flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/OpenCVPlugin.kt similarity index 89% rename from android/src/main/kotlin/com/christian/flutter_document_scanner/OpenCVPlugin.kt rename to flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/OpenCVPlugin.kt index 3cf8c81..a37b0c0 100644 --- a/android/src/main/kotlin/com/christian/flutter_document_scanner/OpenCVPlugin.kt +++ b/flutter_document_scanner_android/android/src/main/kotlin/com/christian/flutterDocumentScanner/OpenCVPlugin.kt @@ -1,6 +1,5 @@ -package com.christian.flutter_document_scanner +package com.christian.flutterDocumentScanner -import android.util.Log import io.flutter.plugin.common.MethodChannel import org.opencv.core.* import org.opencv.imgcodecs.Imgcodecs @@ -9,7 +8,11 @@ import org.opencv.imgproc.Imgproc class OpenCVPlugin { companion object { - fun findContourPhoto(result: MethodChannel.Result, byteData: ByteArray, minContourArea: Double) { + fun findContourPhoto( + result: MethodChannel.Result, + byteData: ByteArray, + minContourArea: Double + ) { try { val src = Imgcodecs.imdecode(MatOfByte(*byteData), Imgcodecs.IMREAD_UNCHANGED) @@ -149,7 +152,9 @@ class OpenCVPlugin { val isLessCurrentArea = Imgproc.contourArea(approx) > maxArea val isLessMaxArea = maxArea < maxContourArea - if (approx.total().toInt() == 4 && isContour && isLessCurrentArea && isLessMaxArea) { + if (approx.total() + .toInt() == 4 && isContour && isLessCurrentArea && isLessMaxArea + ) { maxArea = Imgproc.contourArea(approx) documentContour = approx } @@ -163,7 +168,11 @@ class OpenCVPlugin { } - fun adjustingPerspective(byteData: ByteArray, points: List>, result: MethodChannel.Result) { + fun adjustingPerspective( + byteData: ByteArray, + points: List>, + result: MethodChannel.Result + ) { try { val src = Imgcodecs.imdecode(MatOfByte(*byteData), Imgcodecs.IMREAD_UNCHANGED) val documentContour = MatOfPoint( @@ -212,22 +221,25 @@ class OpenCVPlugin { } - fun applyFilter(result: MethodChannel.Result, byteData: ByteArray, filter: String) { + fun applyFilter(result: MethodChannel.Result, byteData: ByteArray, filter: Int) { try { + val filterType: FilterType = when (filter) { + 1 -> FilterType.Natural + 2 -> FilterType.Gray + 3 -> FilterType.Eco + + else -> FilterType.Natural + } val src = Imgcodecs.imdecode(MatOfByte(*byteData), Imgcodecs.IMREAD_UNCHANGED) var dstEnd = Mat() - when (filter) { - "natural" -> { - dstEnd = src - } + when (filterType) { + FilterType.Natural -> dstEnd = src - "gray" -> { - Imgproc.cvtColor(src, dstEnd, Imgproc.COLOR_BGR2GRAY) - } + FilterType.Gray -> Imgproc.cvtColor(src, dstEnd, Imgproc.COLOR_BGR2GRAY) - "eco" -> { + FilterType.Eco -> { val dstColor = Mat() Imgproc.cvtColor(src, dstColor, Imgproc.COLOR_BGR2GRAY) @@ -247,9 +259,6 @@ class OpenCVPlugin { Imgproc.medianBlur(dstThreshold, dstEnd, 3) } - - - else -> result.notImplemented() } @@ -267,4 +276,10 @@ class OpenCVPlugin { } } } -} \ No newline at end of file + + private enum class FilterType { + Natural, + Gray, + Eco, + } +} diff --git a/flutter_document_scanner_android/lib/flutter_document_scanner_android.dart b/flutter_document_scanner_android/lib/flutter_document_scanner_android.dart new file mode 100644 index 0000000..6ab6f48 --- /dev/null +++ b/flutter_document_scanner_android/lib/flutter_document_scanner_android.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +/// The Android implementation of [FlutterDocumentScannerPlatform]. +class FlutterDocumentScannerAndroid extends FlutterDocumentScannerPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('flutter_document_scanner_android'); + + /// Registers this class as the default instance + /// of [FlutterDocumentScannerPlatform] + static void registerWith() { + FlutterDocumentScannerPlatform.instance = FlutterDocumentScannerAndroid(); + } + + @override + Future findContourPhoto({ + required Uint8List byteData, + required double minContourArea, + }) async { + final contour = await methodChannel.invokeMapMethod( + 'findContourPhoto', + { + 'byteData': byteData, + 'minContourArea': minContourArea, + }, + ); + + if (contour != null) { + return Contour.fromMap(contour); + } + + return null; + } + + @override + Future adjustingPerspective({ + required Uint8List byteData, + required Contour contour, + }) async { + return methodChannel.invokeMethod( + 'adjustingPerspective', + { + 'byteData': byteData, + 'points': contour.points + .map( + (e) => { + 'x': e.x, + 'y': e.y, + }, + ) + .toList(), + }, + ).then((value) => value); + } + + @override + Future applyFilter({ + required Uint8List byteData, + required FilterType filter, + }) async { + return methodChannel.invokeMethod( + 'applyFilter', + { + 'byteData': byteData, + 'filter': filter.value, + }, + ).then((value) => value); + } +} diff --git a/flutter_document_scanner_android/pubspec.yaml b/flutter_document_scanner_android/pubspec.yaml new file mode 100644 index 0000000..3f5e050 --- /dev/null +++ b/flutter_document_scanner_android/pubspec.yaml @@ -0,0 +1,29 @@ +name: flutter_document_scanner_android +description: Android implementation of the flutter_document_scanner plugin +repository: https://github.com/criistian14/flutter_document_scanner/tree/master/flutter_document_scanner_android +version: 1.0.0 + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" + +flutter: + plugin: + implements: flutter_document_scanner + platforms: + android: + package: com.christian.flutterDocumentScanner + pluginClass: FlutterDocumentScannerPlugin + dartPluginClass: FlutterDocumentScannerAndroid + +dependencies: + flutter: + sdk: flutter + flutter_document_scanner_platform_interface: + path: ../flutter_document_scanner_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + plugin_platform_interface: ^2.0.0 + very_good_analysis: ^3.0.1 diff --git a/flutter_document_scanner_android/test/flutter_document_scanner_android_test.dart b/flutter_document_scanner_android/test/flutter_document_scanner_android_test.dart new file mode 100644 index 0000000..b51e0f2 --- /dev/null +++ b/flutter_document_scanner_android/test/flutter_document_scanner_android_test.dart @@ -0,0 +1,55 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/services.dart'; +import 'package:flutter_document_scanner_android/flutter_document_scanner_android.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('FlutterDocumentScannerAndroid', () { + const kPlatformName = 'Android'; + late FlutterDocumentScannerAndroid flutterDocumentScanner; + late List log; + + setUp(() async { + flutterDocumentScanner = FlutterDocumentScannerAndroid(); + + log = []; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(flutterDocumentScanner.methodChannel, + (methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'getPlatformName': + return kPlatformName; + default: + return null; + } + }); + }); + + test('can be registered', () { + FlutterDocumentScannerAndroid.registerWith(); + expect( + FlutterDocumentScannerPlatform.instance, + isA(), + ); + }); + + test('getPlatformName returns correct name', () async { + // final name = await flutterDocumentScanner.getPlatformName(); + // expect( + // log, + // [isMethodCall('getPlatformName', arguments: null)], + // ); + // expect(name, equals(kPlatformName)); + }); + }); +} diff --git a/flutter_document_scanner_ios/CHANGELOG.md b/flutter_document_scanner_ios/CHANGELOG.md new file mode 100644 index 0000000..53c9e57 --- /dev/null +++ b/flutter_document_scanner_ios/CHANGELOG.md @@ -0,0 +1,6 @@ +## 1.0.0 +* Stable version with basic functionality and new structure + + +# 0.5.0 +- Initial release of this plugin. \ No newline at end of file diff --git a/flutter_document_scanner_ios/LICENSE b/flutter_document_scanner_ios/LICENSE new file mode 100644 index 0000000..96ff66a --- /dev/null +++ b/flutter_document_scanner_ios/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Christian Betancourt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flutter_document_scanner_ios/README.md b/flutter_document_scanner_ios/README.md new file mode 100644 index 0000000..f94309b --- /dev/null +++ b/flutter_document_scanner_ios/README.md @@ -0,0 +1,15 @@ +# flutter_document_scanner_ios + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] + +The ios implementation of [`flutter_document_scanner`][pub_link]. + +## Usage + +This package is [endorsed][endorsed_link], which means you can simply use `flutter_document_scanner` +normally. This package will be automatically included in your app when you do. + +[pub_link]: https://pub.dev/packages/flutter_document_scanner +[endorsed_link]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/flutter_document_scanner_ios/analysis_options.yaml b/flutter_document_scanner_ios/analysis_options.yaml new file mode 100644 index 0000000..d0fffaf --- /dev/null +++ b/flutter_document_scanner_ios/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.3.0.1.yaml diff --git a/ios/Assets/.gitkeep b/flutter_document_scanner_ios/ios/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to flutter_document_scanner_ios/ios/Assets/.gitkeep diff --git a/flutter_document_scanner_ios/ios/Classes/FlutterDocumentScannerPlugin.swift b/flutter_document_scanner_ios/ios/Classes/FlutterDocumentScannerPlugin.swift new file mode 100644 index 0000000..fdc26dc --- /dev/null +++ b/flutter_document_scanner_ios/ios/Classes/FlutterDocumentScannerPlugin.swift @@ -0,0 +1,103 @@ +// +// FlutterDocumentScanner.swift +// flutter_document_scanner_ios +// +// Created by Christian Betancourt Barajas on 28/04/23. +// + +import Flutter +import UIKit + +enum ErrorsPlugin : Error { + case stringError(String ) +} + +public class FlutterDocumentScannerPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "flutter_document_scanner_ios", binaryMessenger: registrar.messenger()) + let instance = FlutterDocumentScannerPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch (call.method) { + case "findContourPhoto": + do { + guard let arguments = call.arguments as? Dictionary + + else { + throw ErrorsPlugin.stringError("Invalid Arguments") + } + + let visionPlugin = VisionPlugin() + + visionPlugin.findContourPhoto( + result: result, + byteData: arguments["byteData"] as! FlutterStandardTypedData, + minContourArea: arguments["minContourArea"] as! Double + ) + + } catch { + result(FlutterError( + code: "FlutterDocumentScanner-Error", + message: "findContourPhoto \(error.localizedDescription)", + details: error + )) + } + break; + + case "adjustingPerspective": + do { + guard let arguments = call.arguments as? Dictionary + + else { + throw ErrorsPlugin.stringError("Invalid Arguments") + } + + let visionPlugin = VisionPlugin() + + visionPlugin.adjustingPerspective( + result: result, + byteData: arguments["byteData"] as! FlutterStandardTypedData, + points: arguments["points"] as! Array> + ) + + } catch { + result(FlutterError( + code: "FlutterDocumentScanner-Error", + message: "adjustingPerspective \(error.localizedDescription)", + details: error + )) + } + break; + + case "applyFilter": + do { + guard let arguments = call.arguments as? Dictionary + + else { + throw ErrorsPlugin.stringError("Invalid Arguments") + } + + let visionPlugin = VisionPlugin() + + visionPlugin.applyFilter( + result: result, + byteData: arguments["byteData"] as! FlutterStandardTypedData, + filter: arguments["filter"] as! Int + ) + + } catch { + result(FlutterError( + code: "FlutterDocumentScanner-Error", + message: "applyFilter \(error.localizedDescription)", + details: error + )) + } + break; + + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/flutter_document_scanner_ios/ios/Classes/VisionPlugin.swift b/flutter_document_scanner_ios/ios/Classes/VisionPlugin.swift new file mode 100644 index 0000000..32132fb --- /dev/null +++ b/flutter_document_scanner_ios/ios/Classes/VisionPlugin.swift @@ -0,0 +1,292 @@ +// +// VisionPlugin.swift +// flutter_document_scanner_ios +// +// Created by Christian Betancourt Barajas on 6/11/23. +// + +import Flutter +import Foundation +import Vision +import UIKit + + +class VisionPlugin { + func findContourPhoto(result: @escaping FlutterResult, byteData: FlutterStandardTypedData, minContourArea: Double) { + guard let image = UIImage(data: byteData.data) else { + result(FlutterError(code: "FIND_CONTOUR_PHOTO", message: "Invalid ByteData", details: nil)) + return + } + + guard let cgImage = image.cgImage else { + result(FlutterError(code: "FIND_CONTOUR_PHOTO", message: "Invalid CGImage", details: nil)) + return + } + + let request = VNDetectRectanglesRequest { request, error in + DispatchQueue.main.async { + guard let results = request.results as? [VNRectangleObservation], let rectangle = results.first else { + result(nil) + return + } + + let topLeft = self.convertToPointOfInterest(from: rectangle.topLeft, imageSize: image.size) + let topRight = self.convertToPointOfInterest(from: rectangle.topRight, imageSize: image.size) + let bottomLeft = self.convertToPointOfInterest(from: rectangle.bottomLeft, imageSize: image.size) + let bottomRight = self.convertToPointOfInterest(from: rectangle.bottomRight, imageSize: image.size) + + + let resultEnd = [ + "height": NSNumber(value: Int(image.size.height)), + "width": NSNumber(value: Int(image.size.width)), + "points": [ + [ + "x": NSNumber(value: topLeft.x), + "y": NSNumber(value: topLeft.y), + ], + [ + "x": NSNumber(value: topRight.x), + "y": NSNumber(value: topRight.y), + ], + [ + "x": NSNumber(value: bottomRight.x), + "y": NSNumber(value: bottomRight.y), + ], + [ + "x": NSNumber(value: bottomLeft.x), + "y": NSNumber(value: bottomLeft.y), + ], + ], + "image": nil, + ] + + result (resultEnd) + } + } + + // TODO: add this config in flutter + request.minimumConfidence = 0.5 + request.maximumObservations = 1 // Solo el contorno mรกs grande + request.quadratureTolerance = 25.0 + + let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) + DispatchQueue.global(qos: .userInitiated).async { + do { + try handler.perform([request]) + } catch { + DispatchQueue.main.async { + result(nil) + } + } + } + } + + private func convertToPointOfInterest(from point: CGPoint, imageSize: CGSize) -> CGPoint { + let x = point.y * imageSize.width + let y = point.x * imageSize.height + + return CGPoint(x: x, y: y) + } + + func adjustingPerspective( + result: @escaping FlutterResult, + byteData: FlutterStandardTypedData, + points: Array> + ) { + guard let image = UIImage(data: byteData.data) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Invalid ByteData", details: nil)) + return + } + + guard let ciImage = CIImage(image: image)?.oriented(.rightMirrored) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Invalid CIImage", details: nil)) + return + } + + + guard points.count == 4, + let topLeft = CGPoint(dictionary: points[0]), + let topRight = CGPoint(dictionary: points[1]), + let bottomRight = CGPoint(dictionary: points[2]), + let bottomLeft = CGPoint(dictionary: points[3]) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Invalid Points", details: nil)) + return + } + + guard let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection") else { + result(FlutterError( + code: "ADJUSTING_PERSPECTIVE", + message: "Could not create perspective correction filter", + details: nil + )) + return + } + + perspectiveCorrection.setValue(ciImage, forKey: kCIInputImageKey) + perspectiveCorrection.setValue(CIVector(cgPoint: topLeft), forKey: "inputTopLeft") + perspectiveCorrection.setValue(CIVector(cgPoint: topRight), forKey: "inputTopRight") + perspectiveCorrection.setValue(CIVector(cgPoint: bottomLeft), forKey: "inputBottomLeft") + perspectiveCorrection.setValue(CIVector(cgPoint: bottomRight), forKey: "inputBottomRight") + + + guard let outputImage = perspectiveCorrection.outputImage else { + result(FlutterError( + code: "ADJUSTING_PERSPECTIVE", + message: "Could not get output image from filter", + details: nil + )) + return + } + + + let context = CIContext(options: nil) + guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Could not create CGImage from CIImage", details: nil)) + return + } + + let uiImage = UIImage(cgImage: cgImage) + guard let imageData = uiImage.jpegData(compressionQuality: 1) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Could not get JPEG data from UIImage", details: nil)) + return + } + + + result(FlutterStandardTypedData(bytes: imageData)) + } + + func applyFilter( + result: @escaping FlutterResult, + byteData: FlutterStandardTypedData, + filter: Int + ) { + guard let image = UIImage(data: byteData.data) else { + result(FlutterError(code: "APPLY_FILTER", message: "Invalid ByteData", details: nil)) + return + } + + guard let ciImage = CIImage(image: image) else { + result(FlutterError(code: "ADJUSTING_PERSPECTIVE", message: "Invalid CIImage", details: nil)) + return + } + + switch filter { + // Gray + case 2: + guard let grayFilter = CIFilter(name: "CIColorControls") else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not make filter", details: nil)) + break + } + + grayFilter.setValue(ciImage, forKey: kCIInputImageKey) + grayFilter.setValue(0, forKey: kCIInputSaturationKey) + + guard let output = grayFilter.outputImage else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not apply filter", details: nil)) + break + } + + let context = CIContext(options: nil) + guard let cgimg = context.createCGImage(output, from: output.extent) else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not create CGImage", details: nil)) + break + } + + let uiImage = UIImage(cgImage: cgimg) + guard let imageData = uiImage.jpegData(compressionQuality: 1) else { + result(FlutterError( + code: "APPLY_FILTER", + message: "Could not get JPEG data from UIImage", + details: nil + )) + return + } + + result(FlutterStandardTypedData(bytes: imageData)) + break + + // Eco + case 3: + guard let colorMonochromeFilter = CIFilter(name: "CIColorMonochrome") else { + result(FlutterError( + code: "APPLY_FILTER", + message: "Could not make color mono chrome filter", + details: nil + )) + break + } + + colorMonochromeFilter.setValue(ciImage, forKey: kCIInputImageKey) + colorMonochromeFilter.setValue(CIColor(color: UIColor.white), forKey: "inputColor") + colorMonochromeFilter.setValue(1, forKey: "inputIntensity") + + guard let monochromeImage = colorMonochromeFilter.outputImage else { + result(FlutterError( + code: "APPLY_FILTER", + message: "Could not apply color mono chrome filter", + details: nil + )) + break + } + + + guard let colorControlsFilter = CIFilter(name: "CIColorControls") else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not make color control filter", details: nil)) + break + } + + colorControlsFilter.setValue(monochromeImage, forKey: kCIInputImageKey) + colorControlsFilter.setValue(0, forKey: "inputBrightness") + colorControlsFilter.setValue(1, forKey: "inputContrast") + + guard let colorControlsImage = colorControlsFilter.outputImage else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not apply color control filter", details: nil)) + break + } + + + guard let unsharpMaskFilter = CIFilter(name: "CIUnsharpMask") else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not make unsharp filter", details: nil)) + break + } + + unsharpMaskFilter.setValue(colorControlsImage, forKey: kCIInputImageKey) + unsharpMaskFilter.setValue(1, forKey: kCIInputRadiusKey) + unsharpMaskFilter.setValue(2, forKey: kCIInputIntensityKey) + + guard let unsharpMaskImage = unsharpMaskFilter.outputImage else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not apply unsharp filter", details: nil)) + break + } + + + let context = CIContext(options: nil) + guard let cgimg = context.createCGImage(unsharpMaskImage, from: unsharpMaskImage.extent) else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not create CGImage", details: nil)) + break + } + + let uiImage = UIImage(cgImage: cgimg) + guard let imageData = uiImage.jpegData(compressionQuality: 1) else { + result(FlutterError(code: "APPLY_FILTER", message: "Could not get JPEG data from UIImage", details: nil)) + return + } + + result(FlutterStandardTypedData(bytes: imageData)) + break + + default: + result(byteData) + } + } +} + +extension CGPoint { + init?(dictionary: [String: Double]) { + guard let x = dictionary["x"], let y = dictionary["y"] else { + return nil + } + self.init(x: x, y: y) + } +} + diff --git a/flutter_document_scanner_ios/ios/flutter_document_scanner_ios.podspec b/flutter_document_scanner_ios/ios/flutter_document_scanner_ios.podspec new file mode 100644 index 0000000..3b33a2a --- /dev/null +++ b/flutter_document_scanner_ios/ios/flutter_document_scanner_ios.podspec @@ -0,0 +1,27 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'flutter_document_scanner_ios' + s.version = '0.5.0' + s.summary = 'An iOS implementation of the flutter_document_scanner plugin.' + s.description = <<-DESC + A Flutter plugin that allows the management of taking, cropping and applying filters to an image, using the camera plugin + DESC + s.homepage = 'https://github.com/criistian14/flutter_document_scanner' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Christian Betancourt' => 'criistian-14@hotmail.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*.{swift,h,m,mm,c,cpp}' + s.dependency 'Flutter' + s.static_framework = true + + s.platform = :ios, '9.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + } + s.swift_version = '5.0' +end diff --git a/flutter_document_scanner_ios/lib/flutter_document_scanner_ios.dart b/flutter_document_scanner_ios/lib/flutter_document_scanner_ios.dart new file mode 100644 index 0000000..55f8c57 --- /dev/null +++ b/flutter_document_scanner_ios/lib/flutter_document_scanner_ios.dart @@ -0,0 +1,85 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +/// The iOS implementation of [FlutterDocumentScannerPlatform]. +class FlutterDocumentScannerIOS extends FlutterDocumentScannerPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('flutter_document_scanner_ios'); + + /// Registers this class as the default instance + /// of [FlutterDocumentScannerPlatform] + static void registerWith() { + FlutterDocumentScannerPlatform.instance = FlutterDocumentScannerIOS(); + } + + @override + Future getVersionOpenCV() async { + return methodChannel.invokeMethod('getVersionOpenCV'); + } + + @override + Future findContourPhoto({ + required Uint8List byteData, + required double minContourArea, + }) async { + final contour = await methodChannel.invokeMapMethod( + 'findContourPhoto', + { + 'byteData': byteData, + 'minContourArea': minContourArea, + }, + ); + + if (contour != null) { + return Contour.fromMap(contour); + } + + return null; + } + + @override + Future adjustingPerspective({ + required Uint8List byteData, + required Contour contour, + }) async { + final newImage = await methodChannel.invokeMethod( + 'adjustingPerspective', + { + 'byteData': byteData, + 'points': contour.points + .map( + (e) => { + 'x': e.x, + 'y': e.y, + }, + ) + .toList(), + }, + ); + + return newImage; + } + + @override + Future applyFilter({ + required Uint8List byteData, + required FilterType filter, + }) async { + return methodChannel.invokeMethod( + 'applyFilter', + { + 'byteData': byteData, + 'filter': filter.value, + }, + ).then((value) => value); + } +} diff --git a/flutter_document_scanner_ios/pubspec.yaml b/flutter_document_scanner_ios/pubspec.yaml new file mode 100644 index 0000000..3559600 --- /dev/null +++ b/flutter_document_scanner_ios/pubspec.yaml @@ -0,0 +1,28 @@ +name: flutter_document_scanner_ios +description: iOS implementation of the flutter_document_scanner plugin +repository: https://github.com/criistian14/flutter_document_scanner/tree/master/flutter_document_scanner_ios +version: 1.0.0 + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" + +flutter: + plugin: + implements: flutter_document_scanner + platforms: + ios: + pluginClass: FlutterDocumentScannerPlugin + dartPluginClass: FlutterDocumentScannerIOS + +dependencies: + flutter: + sdk: flutter + flutter_document_scanner_platform_interface: + path: ../flutter_document_scanner_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + plugin_platform_interface: ^2.0.0 + very_good_analysis: ^3.0.1 diff --git a/flutter_document_scanner_ios/test/flutter_document_scanner_ios_test.dart b/flutter_document_scanner_ios/test/flutter_document_scanner_ios_test.dart new file mode 100644 index 0000000..b72bf1f --- /dev/null +++ b/flutter_document_scanner_ios/test/flutter_document_scanner_ios_test.dart @@ -0,0 +1,55 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/services.dart'; +import 'package:flutter_document_scanner_ios/flutter_document_scanner_ios.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('FlutterDocumentScannerIOS', () { + const kPlatformName = 'iOS'; + late FlutterDocumentScannerIOS flutterDocumentScanner; + late List log; + + setUp(() async { + flutterDocumentScanner = FlutterDocumentScannerIOS(); + + log = []; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(flutterDocumentScanner.methodChannel, + (methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'getPlatformName': + return kPlatformName; + default: + return null; + } + }); + }); + + test('can be registered', () { + FlutterDocumentScannerIOS.registerWith(); + expect( + FlutterDocumentScannerPlatform.instance, + isA(), + ); + }); + + test('getPlatformName returns correct name', () async { + // final name = await flutterDocumentScanner.getPlatformName(); + // expect( + // log, + // [isMethodCall('getPlatformName', arguments: null)], + // ); + // expect(name, equals(kPlatformName)); + }); + }); +} diff --git a/flutter_document_scanner_platform_interface/CHANGELOG.md b/flutter_document_scanner_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..7476527 --- /dev/null +++ b/flutter_document_scanner_platform_interface/CHANGELOG.md @@ -0,0 +1,5 @@ +## 1.0.0 +* Stable version with basic functionality and new structure + +# 0.5.0 +* Initial release. diff --git a/flutter_document_scanner_platform_interface/LICENSE b/flutter_document_scanner_platform_interface/LICENSE new file mode 100644 index 0000000..96ff66a --- /dev/null +++ b/flutter_document_scanner_platform_interface/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Christian Betancourt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flutter_document_scanner_platform_interface/README.md b/flutter_document_scanner_platform_interface/README.md new file mode 100644 index 0000000..35fa56b --- /dev/null +++ b/flutter_document_scanner_platform_interface/README.md @@ -0,0 +1,15 @@ +# flutter_document_scanner_platform_interface + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] + +A common platform interface for the [`flutter_document_scanner`][pub_link] plugin. + +This interface allows platform-specific implementations of the `flutter_document_scanner` plugin, as well as the plugin itself, to ensure they are supporting the same interface. + +# Usage + +To implement a new platform-specific implementation of `flutter_document_scanner`, extend `FlutterDocumentScannerPlatform` with an implementation that performs the platform-specific behavior. + +[pub_link]: https://pub.dev/packages/flutter_document_scanner +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/flutter_document_scanner_platform_interface/analysis_options.yaml b/flutter_document_scanner_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..d0fffaf --- /dev/null +++ b/flutter_document_scanner_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.3.0.1.yaml diff --git a/flutter_document_scanner_platform_interface/lib/flutter_document_scanner_platform_interface.dart b/flutter_document_scanner_platform_interface/lib/flutter_document_scanner_platform_interface.dart new file mode 100644 index 0000000..bc9cb4b --- /dev/null +++ b/flutter_document_scanner_platform_interface/lib/flutter_document_scanner_platform_interface.dart @@ -0,0 +1,82 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:flutter_document_scanner_platform_interface/src/contour.dart'; +import 'package:flutter_document_scanner_platform_interface/src/filter_type.dart'; +import 'package:flutter_document_scanner_platform_interface/src/method_channel_flutter_document_scanner.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +export 'package:flutter_document_scanner_platform_interface/src/contour.dart'; +export 'package:flutter_document_scanner_platform_interface/src/filter_type.dart'; + +/// The interface that implementations of flutter_document_scanner +/// must implement. +/// +/// Platform implementations should extend this class +/// rather than implement it as `FlutterDocumentScanner`. +/// Extending this class (using `extends`) ensures that the subclass will get +/// the default implementation, while platform implementations that `implements` +/// this interface will be broken by newly added +/// [FlutterDocumentScannerPlatform] methods. +abstract class FlutterDocumentScannerPlatform extends PlatformInterface { + /// Constructs a FlutterDocumentScannerPlatform. + FlutterDocumentScannerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FlutterDocumentScannerPlatform _instance = + MethodChannelFlutterDocumentScanner(); + + /// The default instance of [FlutterDocumentScannerPlatform] to use. + /// + /// Defaults to [MethodChannelFlutterDocumentScanner]. + static FlutterDocumentScannerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FlutterDocumentScannerPlatform] when + /// they register themselves. + static set instance(FlutterDocumentScannerPlatform instance) { + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// Get the version of the opencv library + Future getVersionOpenCV() { + throw UnimplementedError('getVersionOpenCV() has not been implemented.'); + } + + /// Apply filters to the image with opencv + /// Then get the contours and return only the largest one that has four sides + /// (this is done from native code) + Future findContourPhoto({ + required Uint8List byteData, + required double minContourArea, + }) { + throw UnimplementedError('findContourPhoto() has not been implemented.'); + } + + /// Based on the given [Contour.points], the perspective is created + /// and a new image is returned [Uint8List] + Future adjustingPerspective({ + required Uint8List byteData, + required Contour contour, + }) { + throw UnimplementedError( + 'adjustingPerspective() has not been implemented.', + ); + } + + /// Apply the selected [filter] with the opencv library + Future applyFilter({ + required Uint8List byteData, + required FilterType filter, + }) { + throw UnimplementedError('applyFilter() has not been implemented.'); + } +} diff --git a/lib/src/models/contour.dart b/flutter_document_scanner_platform_interface/lib/src/contour.dart similarity index 58% rename from lib/src/models/contour.dart rename to flutter_document_scanner_platform_interface/lib/src/contour.dart index 618b599..c1dd3f1 100644 --- a/lib/src/models/contour.dart +++ b/flutter_document_scanner_platform_interface/lib/src/contour.dart @@ -1,9 +1,42 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + import 'dart:math'; import 'dart:typed_data'; import 'package:equatable/equatable.dart'; +/// Contour class class Contour extends Equatable { + /// Create a contour instance + const Contour({ + this.height, + this.width, + required this.points, + this.image, + }); + + /// Construct class from the json map + factory Contour.fromMap(Map map) { + return Contour( + height: map['height'] as int?, + width: map['width'] as int?, + points: (map['points'] as List) + .map( + (e) => Point( + Map.from(e as Map)['x']!, + Map.from(e)['y']!, + ), + ) + .toList(), + image: map['image'] as Uint8List?, + ); + } + /// image [height] final int? height; @@ -16,13 +49,6 @@ class Contour extends Equatable { /// bytes of the returned image (maybe eliminated in the future) final Uint8List? image; - const Contour({ - this.height, - this.width, - required this.points, - this.image, - }); - @override List get props => [ height, @@ -31,22 +57,8 @@ class Contour extends Equatable { image, ]; - factory Contour.fromMap(Map map) { - return Contour( - height: map['height'] as int, - width: map['width'] as int, - points: (map['points'] as List) - .map( - (e) => Point( - Map.from(e)["x"]!, - Map.from(e)["y"]!, - ), - ) - .toList(), - image: map['image'] as Uint8List, - ); - } - + /// Creates a copy of this Contour but with the given fields replaced with + /// the new values. Contour copyWith({ int? height, int? width, @@ -60,4 +72,10 @@ class Contour extends Equatable { image: image ?? this.image, ); } + + /// Convert the class to String + @override + String toString() { + return 'Contour(height: $height, width: $width, points: $points, image: $image)'; + } } diff --git a/flutter_document_scanner_platform_interface/lib/src/filter_type.dart b/flutter_document_scanner_platform_interface/lib/src/filter_type.dart new file mode 100644 index 0000000..a900ee2 --- /dev/null +++ b/flutter_document_scanner_platform_interface/lib/src/filter_type.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +/// Filter types +enum FilterType { + /// Without filter + natural, + + /// Gray scale filter + gray, + + /// Threshold filter + eco, +} + +/// Extension with utilities to FilterType +extension FilterTypeExt on FilterType { + /// Return value of the enum + int get value { + switch (this) { + case FilterType.natural: + return 1; + + case FilterType.gray: + return 2; + + case FilterType.eco: + return 3; + } + } +} diff --git a/flutter_document_scanner_platform_interface/lib/src/method_channel_flutter_document_scanner.dart b/flutter_document_scanner_platform_interface/lib/src/method_channel_flutter_document_scanner.dart new file mode 100644 index 0000000..5cc71b6 --- /dev/null +++ b/flutter_document_scanner_platform_interface/lib/src/method_channel_flutter_document_scanner.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; + +/// An implementation of [FlutterDocumentScannerPlatform] +/// that uses method channels. +class MethodChannelFlutterDocumentScanner + extends FlutterDocumentScannerPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('flutter_document_scanner'); + + @override + Future findContourPhoto({ + required Uint8List byteData, + required double minContourArea, + }) async { + final contour = await methodChannel.invokeMapMethod( + 'findContourPhoto', + { + 'byteData': byteData, + 'minContourArea': minContourArea, + }, + ); + + if (contour != null) { + return Contour.fromMap(contour); + } + + return null; + } + + @override + Future adjustingPerspective({ + required Uint8List byteData, + required Contour contour, + }) async { + return methodChannel.invokeMethod( + 'adjustingPerspective', + { + 'byteData': byteData, + 'points': contour.points + .map( + (e) => { + 'x': e.x, + 'y': e.y, + }, + ) + .toList(), + }, + ).then((value) => value); + } + + @override + Future applyFilter({ + required Uint8List byteData, + required FilterType filter, + }) async { + return methodChannel.invokeMethod( + 'applyFilter', + { + 'byteData': byteData, + 'filter': filter.value, + }, + ).then((value) => value); + } +} diff --git a/flutter_document_scanner_platform_interface/pubspec.yaml b/flutter_document_scanner_platform_interface/pubspec.yaml new file mode 100644 index 0000000..9e200c8 --- /dev/null +++ b/flutter_document_scanner_platform_interface/pubspec.yaml @@ -0,0 +1,19 @@ +name: flutter_document_scanner_platform_interface +description: A common platform interface for the flutter_document_scanner plugin. +repository: https://github.com/criistian14/flutter_document_scanner/tree/master/flutter_document_scanner_platform_interface +version: 1.0.0 + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" + +dependencies: + equatable: ^2.0.3 + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + very_good_analysis: ^3.0.1 diff --git a/flutter_document_scanner_platform_interface/test/flutter_document_scanner_platform_interface_test.dart b/flutter_document_scanner_platform_interface/test/flutter_document_scanner_platform_interface_test.dart new file mode 100644 index 0000000..d2eeb0b --- /dev/null +++ b/flutter_document_scanner_platform_interface/test/flutter_document_scanner_platform_interface_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter_document_scanner_platform_interface/flutter_document_scanner_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class FlutterDocumentScannerMock extends FlutterDocumentScannerPlatform {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + group('FlutterDocumentScannerPlatformInterface', () { + late FlutterDocumentScannerPlatform flutterDocumentScannerPlatform; + + setUp(() { + flutterDocumentScannerPlatform = FlutterDocumentScannerMock(); + FlutterDocumentScannerPlatform.instance = flutterDocumentScannerPlatform; + }); + + // group('getPlatformName', () { + // test('returns correct name', () async { + // expect( + // await FlutterDocumentScannerPlatform.instance.getPlatformName(), + // equals(FlutterDocumentScannerMock.mockPlatformName), + // ); + // }); + // }); + }); +} diff --git a/flutter_document_scanner_platform_interface/test/src/method_channel_flutter_document_scanner_test.dart b/flutter_document_scanner_platform_interface/test/src/method_channel_flutter_document_scanner_test.dart new file mode 100644 index 0000000..a5ff063 --- /dev/null +++ b/flutter_document_scanner_platform_interface/test/src/method_channel_flutter_document_scanner_test.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2021, Christian Betancourt +// https://github.com/criistian14 +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + // const kPlatformName = 'platformName'; + + // group('$MethodChannelFlutterDocumentScanner', () { + // late MethodChannelFlutterDocumentScanner + // methodChannelFlutterDocumentScanner; + // final log = []; + // + // setUp(() async { + // methodChannelFlutterDocumentScanner = + // MethodChannelFlutterDocumentScanner() + // ..methodChannel + // .setMockMethodCallHandler((MethodCall methodCall) async { + // log.add(methodCall); + // switch (methodCall.method) { + // case 'getPlatformName': + // return kPlatformName; + // default: + // return null; + // } + // }); + // }); + // + // tearDown(log.clear); + // + // test('getPlatformName', () async { + // final platformName = + // await methodChannelFlutterDocumentScanner.getPlatformName(); + // expect( + // log, + // [isMethodCall('getPlatformName', arguments: null)], + // ); + // expect(platformName, equals(kPlatformName)); + // }); + // }); +} diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 0c88507..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig -/Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Classes/FlutterDocumentScannerPlugin.h b/ios/Classes/FlutterDocumentScannerPlugin.h deleted file mode 100644 index c3b5e11..0000000 --- a/ios/Classes/FlutterDocumentScannerPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface FlutterDocumentScannerPlugin : NSObject -@end diff --git a/ios/Classes/FlutterDocumentScannerPlugin.m b/ios/Classes/FlutterDocumentScannerPlugin.m deleted file mode 100644 index 31b5f50..0000000 --- a/ios/Classes/FlutterDocumentScannerPlugin.m +++ /dev/null @@ -1,15 +0,0 @@ -#import "FlutterDocumentScannerPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "flutter_document_scanner-Swift.h" -#endif - -@implementation FlutterDocumentScannerPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftFlutterDocumentScannerPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/ios/Classes/SwiftFlutterDocumentScannerPlugin.swift b/ios/Classes/SwiftFlutterDocumentScannerPlugin.swift deleted file mode 100644 index 7f2c511..0000000 --- a/ios/Classes/SwiftFlutterDocumentScannerPlugin.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Flutter -import UIKit - -public class SwiftFlutterDocumentScannerPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "flutter_document_scanner", binaryMessenger: registrar.messenger()) - let instance = SwiftFlutterDocumentScannerPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } -} diff --git a/ios/flutter_document_scanner.podspec b/ios/flutter_document_scanner.podspec deleted file mode 100644 index fdc1e42..0000000 --- a/ios/flutter_document_scanner.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint flutter_document_scanner.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'flutter_document_scanner' - s.version = '0.0.1' - s.summary = 'A new flutter plugin project.' - s.description = <<-DESC -A new flutter plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/lib/flutter_document_scanner.dart b/lib/flutter_document_scanner.dart deleted file mode 100644 index cc72a26..0000000 --- a/lib/flutter_document_scanner.dart +++ /dev/null @@ -1,12 +0,0 @@ -library flutter_document_scanner; - -export 'package:camera/camera.dart'; - -export 'src/bloc/app/app_state.dart'; -export 'src/document_scanner_controller.dart'; -export 'src/models/area.dart'; -export 'src/models/filter_type.dart'; -export 'src/ui/pages/document_scanner.dart'; -export 'src/utils/crop_photo_document_style.dart'; -export 'src/utils/general_styles.dart'; -export 'src/utils/take_photo_document_style.dart'; diff --git a/lib/src/bloc/app/app_bloc.dart b/lib/src/bloc/app/app_bloc.dart deleted file mode 100644 index b8d37c1..0000000 --- a/lib/src/bloc/app/app_bloc.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:bloc/bloc.dart'; -import 'package:camera/camera.dart'; -import 'package:flutter_document_scanner/src/models/filter_type.dart'; -import 'package:flutter_document_scanner/src/utils/image_utils.dart'; - -import 'app_event.dart'; -import 'app_state.dart'; - -class AppBloc extends Bloc { - final ImageUtils _imageUtils; - - AppBloc({ - required ImageUtils imageUtils, - }) : _imageUtils = imageUtils, - super(AppState.init()) { - on(_cameraInitialized); - on(_photoTaken); - on(_pageChanged); - on(_photoCropped); - on(_loadCroppedPhoto); - on(_filterApplied); - on(_newEditedImageLoaded); - on(_startedSavingDocument); - on(_documentSaved); - } - - CameraController? _cameraController; - XFile? _pictureTaken; - - /// - void _cameraInitialized( - AppCameraInitialized event, - Emitter emit, - ) async { - emit(state.copyWith( - statusCamera: AppStatus.loading, - )); - - List cameras = await availableCameras(); - CameraDescription camera = cameras.firstWhere( - (camera) => camera.lensDirection == event.cameraLensDirection, - orElse: () => cameras.first, - ); - - _cameraController = CameraController( - camera, - event.resolutionCamera, - enableAudio: false, - ); - - await _cameraController!.initialize(); - - emit(state.copyWith( - statusCamera: AppStatus.success, - cameraController: _cameraController, - )); - } - - /// - Future _photoTaken( - AppPhotoTaken event, - Emitter emit, - ) async { - emit(state.copyWith( - statusTakePhotoPage: AppStatus.loading, - )); - - if (_cameraController == null) { - // TODO: agregar validacion - return; - } - - _pictureTaken = await _cameraController!.takePicture(); - - final byteData = await _pictureTaken!.readAsBytes(); - final response = await _imageUtils.findContourPhoto( - byteData, - minContourArea: event.minContourArea, - ); - - final fileImage = File(_pictureTaken!.path); - - emit(state.copyWith( - statusTakePhotoPage: AppStatus.success, - pictureInitial: fileImage, - contourInitial: response, - )); - - emit(state.copyWith( - currentPage: AppPages.cropPhoto, - )); - } - - /// - Future _pageChanged( - AppPageChanged event, - Emitter emit, - ) async { - switch (event.newPage) { - case AppPages.takePhoto: - emit(state.copyWith( - currentPage: event.newPage, - statusTakePhotoPage: AppStatus.initial, - statusCropPhoto: AppStatus.initial, - contourInitial: null, - )); - break; - - case AppPages.cropPhoto: - emit(state.copyWith( - currentPage: event.newPage, - currentFilterType: FilterType.natural, - )); - break; - - case AppPages.editDocument: - emit(state.copyWith( - currentPage: event.newPage, - statusEditPhoto: AppStatus.initial, - statusSavePhotoDocument: AppStatus.initial, - )); - break; - } - } - - /// - Future _photoCropped( - AppPhotoCropped event, - Emitter emit, - ) async { - emit(state.copyWith( - statusCropPhoto: AppStatus.loading, - )); - } - - /// - Future _loadCroppedPhoto( - AppLoadCroppedPhoto event, - Emitter emit, - ) async { - emit(state.copyWith( - statusCropPhoto: AppStatus.success, - pictureCropped: event.image, - contourInitial: event.area, - )); - - emit(state.copyWith( - currentPage: AppPages.editDocument, - )); - } - - /// - Future _filterApplied( - AppFilterApplied event, - Emitter emit, - ) async { - emit(state.copyWith( - currentFilterType: event.filter, - statusEditPhoto: AppStatus.loading, - )); - } - - /// - Future _newEditedImageLoaded( - AppNewEditedImageLoaded event, - Emitter emit, - ) async { - emit(state.copyWith( - statusEditPhoto: event.isSucces ? AppStatus.success : AppStatus.failure, - )); - } - - /// - Future _startedSavingDocument( - AppStartedSavingDocument event, - Emitter emit, - ) async { - emit(state.copyWith( - statusSavePhotoDocument: AppStatus.loading, - )); - } - - /// - Future _documentSaved( - AppDocumentSaved event, - Emitter emit, - ) async { - emit(state.copyWith( - statusSavePhotoDocument: - event.isSucces ? AppStatus.success : AppStatus.failure, - )); - } -} diff --git a/lib/src/bloc/edit/edit_bloc.dart b/lib/src/bloc/edit/edit_bloc.dart deleted file mode 100644 index ae4566d..0000000 --- a/lib/src/bloc/edit/edit_bloc.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:bloc/bloc.dart'; -import 'package:flutter_document_scanner/src/utils/image_utils.dart'; - -import 'edit_event.dart'; -import 'edit_state.dart'; - -class EditBloc extends Bloc { - final ImageUtils _imageUtils; - - EditBloc({ - required ImageUtils imageUtils, - }) : _imageUtils = imageUtils, - super(EditState.init()) { - on(_started); - on(_filterChanged); - } - - late Uint8List imageBase; - - /// - Future _started( - EditStarted event, - Emitter emit, - ) async { - imageBase = event.image; - - emit(state.copyWith( - image: event.image, - )); - } - - /// - Future _filterChanged( - EditFilterChanged event, - Emitter emit, - ) async { - final newImage = await _imageUtils.applyFilter(imageBase, event.filter); - - emit(state.copyWith( - image: newImage, - )); - } -} diff --git a/lib/src/bloc/edit/edit_event.dart b/lib/src/bloc/edit/edit_event.dart deleted file mode 100644 index f4e3b1c..0000000 --- a/lib/src/bloc/edit/edit_event.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:typed_data'; - -import 'package:equatable/equatable.dart'; -import 'package:flutter_document_scanner/src/models/filter_type.dart'; - -abstract class EditEvent extends Equatable {} - -class EditStarted extends EditEvent { - final Uint8List image; - - EditStarted(this.image); - - @override - List get props => [ - image, - ]; -} - -class EditFilterChanged extends EditEvent { - final FilterType filter; - - EditFilterChanged(this.filter); - - @override - List get props => [ - filter, - ]; -} diff --git a/lib/src/bloc/edit/edit_state.dart b/lib/src/bloc/edit/edit_state.dart deleted file mode 100644 index 3139a8d..0000000 --- a/lib/src/bloc/edit/edit_state.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:typed_data'; - -import 'package:equatable/equatable.dart'; - -class EditState extends Equatable { - final Uint8List? image; - - const EditState({ - this.image, - }); - - @override - List get props => [ - image, - ]; - - factory EditState.init() { - return const EditState(); - } - - EditState copyWith({ - Uint8List? image, - }) { - return EditState( - image: image ?? this.image, - ); - } -} diff --git a/lib/src/document_scanner_controller.dart b/lib/src/document_scanner_controller.dart deleted file mode 100644 index 041022b..0000000 --- a/lib/src/document_scanner_controller.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'dart:async'; - -import 'package:flutter_document_scanner/flutter_document_scanner.dart'; -import 'package:flutter_document_scanner/src/utils/image_utils.dart'; - -import 'bloc/app/app_bloc.dart'; -import 'bloc/app/app_event.dart'; - -class DocumentScannerController { - final AppBloc _appBloc = AppBloc( - imageUtils: ImageUtils(), - ); - - AppBloc get bloc => _appBloc; - - // * Streams - Stream get statusTakePhotoPage { - return _appBloc.stream.transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.statusTakePhotoPage), - ), - ); - } - - Stream get statusCropPhoto { - return _appBloc.stream.transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.statusCropPhoto), - ), - ); - } - - Stream get statusEditPhoto { - return _appBloc.stream.transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.statusEditPhoto), - ), - ); - } - - Stream get currentFilterType { - return _appBloc.stream.transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.currentFilterType), - ), - ); - } - - Stream get statusSavePhotoDocument { - return _appBloc.stream.transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.statusSavePhotoDocument), - ), - ); - } - - /// Taking the photo - /// - /// Then find the contour with the largest area only when it exceeds [minContourArea] - /// - /// [minContourArea] is default 80000.0 - Future takePhoto({ - double? minContourArea, - }) async { - _appBloc.add(AppPhotoTaken( - minContourArea: minContourArea, - )); - } - - /// - Future changePage(AppPages page) async { - _appBloc.add(AppPageChanged(page)); - } - - /// - Future cropPhoto() async { - _appBloc.add(AppPhotoCropped()); - } - - /// - Future applyFilter(FilterType type) async { - _appBloc.add(AppFilterApplied(filter: type)); - } - - /// - Future savePhotoDocument() async { - _appBloc.add(AppStartedSavingDocument()); - } -} diff --git a/lib/src/models/filter_type.dart b/lib/src/models/filter_type.dart deleted file mode 100644 index 54ee4b5..0000000 --- a/lib/src/models/filter_type.dart +++ /dev/null @@ -1,5 +0,0 @@ -enum FilterType { - natural, - gray, - eco, -} diff --git a/lib/src/utils/border_crop_area_painter.dart b/lib/src/utils/border_crop_area_painter.dart deleted file mode 100644 index a5eba77..0000000 --- a/lib/src/utils/border_crop_area_painter.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_document_scanner/src/models/area.dart'; - -class BorderCropAreaPainter extends CustomPainter { - final Area area; - final Color? colorBorderArea; - final double? widthBorderArea; - - const BorderCropAreaPainter({ - required this.area, - this.colorBorderArea, - this.widthBorderArea, - }); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = widthBorderArea ?? 3 - ..color = colorBorderArea ?? Colors.white; - - Path path = Path() - ..moveTo(area.topLeft.x, area.topLeft.y) - ..lineTo(area.topRight.x, area.topRight.y) - ..lineTo(area.bottomRight.x, area.bottomRight.y) - ..lineTo(area.bottomLeft.x, area.bottomLeft.y) - ..close(); - - canvas.drawPath(path, paint); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => true; -} diff --git a/lib/src/utils/crop_photo_document_style.dart b/lib/src/utils/crop_photo_document_style.dart deleted file mode 100644 index bbc78e9..0000000 --- a/lib/src/utils/crop_photo_document_style.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'dart:math'; -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; -import 'package:flutter_document_scanner/src/models/area.dart'; - -@immutable -class CropPhotoDocumentStyle { - /// - final bool hideAppBarDefault; - - /// - final String textButtonSave; - - /// - final List? children; - - /// - final double top; - final double bottom; - final double left; - final double right; - - /// - final Color? maskColor; - - /// - final ui.ImageFilter? maskFilter; - - /// - final double dotSize; - final double dotRadius; - - /// - final Area defaultAreaInitial; - - const CropPhotoDocumentStyle({ - this.hideAppBarDefault = false, - this.textButtonSave = "CROP", - this.children, - this.top = 0, - this.bottom = 0, - this.left = 0, - this.right = 0, - this.maskColor, - this.maskFilter, - this.dotSize = 18, - this.dotRadius = 30, - this.defaultAreaInitial = const Area( - topRight: Point(300.0, 80.0), - topLeft: Point(40.0, 80.0), - bottomLeft: Point(40.0, 450.0), - bottomRight: Point(300.0, 450.0), - ), - }); -} diff --git a/lib/src/utils/dialogs.dart b/lib/src/utils/dialogs.dart deleted file mode 100644 index e9fb354..0000000 --- a/lib/src/utils/dialogs.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; - -class Dialogs { - void defaultDialog( - BuildContext context, - String message, - ) async { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) { - return AlertDialog( - title: Text( - message, - ), - ); - }, - ); - } -} diff --git a/lib/src/utils/edit_photo_document_style.dart b/lib/src/utils/edit_photo_document_style.dart deleted file mode 100644 index cba3d66..0000000 --- a/lib/src/utils/edit_photo_document_style.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -@immutable -class EditPhotoDocumentStyle { - /// - final bool hideAppBarDefault; - - /// - final bool hideBottomBarDefault; - - /// - final String textButtonSave; - - /// - final List? children; - - /// - final double top; - final double bottom; - final double left; - final double right; - - const EditPhotoDocumentStyle({ - this.hideAppBarDefault = false, - this.hideBottomBarDefault = false, - this.textButtonSave = "SAVE", - this.children, - this.top = 0, - this.bottom = 0, - this.left = 0, - this.right = 0, - }); -} diff --git a/lib/src/utils/general_styles.dart b/lib/src/utils/general_styles.dart deleted file mode 100644 index 720547e..0000000 --- a/lib/src/utils/general_styles.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; - -@immutable -class GeneralStyles { - final bool hideDefaultBottomNavigation; - final bool hideDefaultDialogs; - final Color baseColor; - - const GeneralStyles({ - this.hideDefaultBottomNavigation = false, - this.hideDefaultDialogs = false, - this.baseColor = Colors.white, - }); -} diff --git a/lib/src/utils/model_utils.dart b/lib/src/utils/model_utils.dart deleted file mode 100644 index 7e22010..0000000 --- a/lib/src/utils/model_utils.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'dart:typed_data'; - -const valueNull = "valueNull"; - -typedef OnSave = void Function(Uint8List imageBytes); diff --git a/lib/src/utils/take_photo_document_style.dart b/lib/src/utils/take_photo_document_style.dart deleted file mode 100644 index 9f303dd..0000000 --- a/lib/src/utils/take_photo_document_style.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -@immutable -class TakePhotoDocumentStyle { - /// - final Widget onLoading; - - /// - final List? children; - - /// - final double? top; - final double? bottom; - final double? left; - final double? right; - - const TakePhotoDocumentStyle({ - this.onLoading = const Center( - child: CircularProgressIndicator(), - ), - this.children, - this.top, - this.bottom, - this.left, - this.right, - }); -} diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 6cc11ce..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,516 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - url: "https://pub.dartlang.org" - source: hosted - version: "31.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.0" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.0" - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.2" - bloc: - dependency: "direct main" - description: - name: bloc - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.2" - bloc_test: - dependency: "direct dev" - description: - name: bloc_test - url: "https://pub.dartlang.org" - source: hosted - version: "9.0.2" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - camera: - dependency: "direct main" - description: - name: camera - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.4+8" - camera_platform_interface: - dependency: transitive - description: - name: camera_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.5" - camera_web: - dependency: transitive - description: - name: camera_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.1+1" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.5" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - coverage: - dependency: transitive - description: - name: coverage - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - cross_file: - dependency: transitive - description: - name: cross_file - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.2" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - diff_match_patch: - dependency: transitive - description: - name: diff_match_patch - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.1" - equatable: - dependency: "direct main" - description: - name: equatable - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.3" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - file: - dependency: transitive - description: - name: file - url: "https://pub.dartlang.org" - source: hosted - version: "6.1.2" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_bloc: - dependency: "direct main" - description: - name: flutter_bloc - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" - 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" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" - glob: - dependency: transitive - description: - name: glob - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.0" - io: - dependency: transitive - description: - name: io - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.3" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.11" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.0" - mime: - dependency: transitive - description: - name: mime - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - mocktail: - dependency: transitive - description: - name: mocktail - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.0" - nested: - dependency: transitive - description: - name: nested - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - package_config: - dependency: transitive - description: - name: package_config - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.1" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" - pool: - dependency: transitive - description: - name: pool - url: "https://pub.dartlang.org" - source: hosted - version: "1.5.0" - provider: - dependency: transitive - description: - name: provider - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1+1" - shelf: - dependency: transitive - description: - name: shelf - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - shelf_static: - dependency: transitive - description: - name: shelf_static - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - source_maps: - dependency: transitive - description: - name: source_maps - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.10" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - stream_transform: - dependency: transitive - description: - name: stream_transform - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test: - dependency: transitive - description: - name: test - url: "https://pub.dartlang.org" - source: hosted - version: "1.17.12" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.3" - test_core: - dependency: transitive - description: - name: test_core - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.2" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - vm_service: - dependency: transitive - description: - name: vm_service - url: "https://pub.dartlang.org" - source: hosted - version: "7.5.0" - watcher: - dependency: transitive - description: - name: watcher - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - yaml: - dependency: transitive - description: - name: yaml - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" -sdks: - dart: ">=2.15.1 <3.0.0" - flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index 14955bc..0000000 --- a/pubspec.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: flutter_document_scanner -description: A Flutter plugin that allows the management of taking, cropping and applying filters to an image, using the camera plugin -version: 0.1.0 -homepage: https://github.com/criistian14/flutter_document_scanner - -environment: - sdk: ">=2.15.1 <3.0.0" - flutter: ">=2.5.0" - -dependencies: - flutter: - sdk: flutter - - camera: ^0.9.4+8 - bloc: ^8.0.2 - flutter_bloc: ^8.0.1 - equatable: ^2.0.3 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^1.0.4 - bloc_test: ^9.0.2 - -# The following section is specific to Flutter. -flutter: - plugin: - platforms: - android: - package: com.christian.flutter_document_scanner - pluginClass: FlutterDocumentScannerPlugin - ios: - pluginClass: FlutterDocumentScannerPlugin \ No newline at end of file diff --git a/test/src/utils/image_utils_test.dart b/test/src/utils/image_utils_test.dart deleted file mode 100644 index 7ed4daf..0000000 --- a/test/src/utils/image_utils_test.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_document_scanner/src/utils/image_utils.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - late ImageUtils imageUtils; - - setUp(() { - imageUtils = ImageUtils(); - }); - - test( - "should return the rect of the image", - () async { - // arrange - const screenSize = Size(720, 1280); - - // act - final imageRect = imageUtils.imageRect(screenSize); - - // assert - expect(imageRect, const Rect.fromLTWH(0, 0, 720, 1280)); - }, - ); -}