├── .circleci └── config.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── assets ├── Record_1.gif └── Screenshot_1.jpg ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── main.dart │ └── result.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── lib ├── easy_image_editor.dart └── src │ ├── editor_view.dart │ ├── matrix_gesture_detector.dart │ └── resizable_widget.dart ├── pubspec.lock ├── pubspec.yaml └── test └── easy_image_editor_test.dart /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Use the latest 2.1 version of CircleCI pipeline process engine. 2 | # See: https://circleci.com/docs/2.0/configuration-reference 3 | version: 2.1 4 | 5 | # Define a job to be invoked later in a workflow. 6 | # See: https://circleci.com/docs/2.0/configuration-reference/#jobs 7 | jobs: 8 | say-hello: 9 | # Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. 10 | # See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor 11 | docker: 12 | - image: cimg/base:stable 13 | # Add steps to the job 14 | # See: https://circleci.com/docs/2.0/configuration-reference/#steps 15 | steps: 16 | - checkout 17 | - run: 18 | name: "Say hello" 19 | command: "echo Hello, World!" 20 | 21 | # Invoke jobs via workflows 22 | # See: https://circleci.com/docs/2.0/configuration-reference/#workflows 23 | workflows: 24 | say-hello-workflow: 25 | jobs: 26 | - say-hello 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.3 2 | * fix bug 3 | * added EasyImageEditorController to perform operations 4 | * update example app 5 | 6 | ## 1.0.2 7 | * fix bug 8 | * now editor allow handle all actions manually like move, rotate, zoom, flip 9 | * user can enable and disable touch actions 10 | 11 | ## 1.0.1 12 | * fix issue and update README.md 13 | 14 | ## 0.0.1 15 | 16 | * TODO: Describe initial release. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 easy_image_editor 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | # Easy Image Editor 15 |

16 | 17 | pub.dev version 18 | 19 | 20 | Platform support 21 | 22 | 23 | MIT License Badge 24 | 25 | 26 | Flutter Platform Badge 27 | 28 |

29 | EasyImageEditor use for add any kind of widget over the background image or color and move that widget, resize, rotate. 30 | 31 |

32 | screenshot 33 |

34 | 35 | ## Features 36 | 37 | 1) change editor background color. 38 | 2) add any widget as background in editor. 39 | 3) add any widget over the editor. 40 | 4) move, resize, flip, zoom and rotate added widget. 41 | 5) update added widget with another widget. 42 | 6) allow undo and redo. 43 | 7) allow single and multiple selection. 44 | 8) allow change border color and remove icon. 45 | 9) remove added widget. 46 | 10) handle all action manually 47 | 48 | ## Getting started 49 | 50 | First, add `easy_image_editor` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). 51 | then add this line in your file `import 'package:easy_image_editor/easy_image_editor.dart';` 52 | 53 | ## Usage 54 | 55 | ```dart 56 | import 'package:easy_image_editor/easy_image_editor.dart'; 57 | 58 | class _MyHomePageState extends State { 59 | late EasyImageEditorController _easyImageEditorController; 60 | ... 61 | 62 | @override 63 | void initState() { 64 | super.initState(); 65 | ... 66 | } 67 | ... 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | return Scaffold( 72 | ... 73 | body: EditorView( 74 | onInitialize: (controller) { 75 | setState(() { 76 | _easyImageEditorController = controller; 77 | }); 78 | }, 79 | ), 80 | ... 81 | ); 82 | } 83 | ... 84 | } 85 | ``` 86 | for more detail and usage see /example/lib/main.dart 87 | 88 | ## Additional information 89 | 90 | 1) `borderColor` use for set border color of widget default value `Colors.black`. 91 | 2) `removeIcon` set remove icon of widget default value `Icon(Icons.cancel)`. 92 | 3) `onViewTouch` this event call when widget touch. 93 | 4) `onViewTouchOver` this event call when widget touch remove. 94 | 5) `addBackgroundColor` set background color of editor. 95 | 6) `addBackgroundView` set background color of editor. it will overlap background color. 96 | 7) `addView` add any kind of view over the editor. 97 | 8) `updateView` update added view in editor. 98 | 9) `canEditMultipleView` set edit selection mode multiple or single default value `true`. 99 | 10) `hideViewControl` it will hide borders and remove icons of all added widget. 100 | 11) `showViewControl` it will show borders and remove icons of all added widget. 101 | 12) `onClick` this event call when widget click. 102 | 13) `clickToFocusAndMove` if you set true then any widget move, rotate, zoom by touch when user click's on default value `false`. 103 | 14) `moveView` move widget over the editor programmatically. 104 | 15) `rotateView` rotate widget over the editor programmatically. 105 | 16) `zoomInOutView` zoom in or out widget over the editor programmatically. 106 | 17) `flipView` flip vertical or horizontal widget over the editor programmatically. 107 | 18) `updateMatrix` update widget matrix over the editor programmatically like your way. 108 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /assets/Record_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/assets/Record_1.gif -------------------------------------------------------------------------------- /assets/Screenshot_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/assets/Screenshot_1.jpg -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 31 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.example" 47 | minSdkVersion 16 48 | targetSdkVersion 31 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshbdabhi/easy_image_editor/57bc36fc65508d72bd6888058f8359edd598ac1c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:image_picker/image_picker.dart'; 3 | import 'package:easy_image_editor/easy_image_editor.dart'; 4 | import 'dart:io'; 5 | import 'result.dart'; 6 | 7 | void main() { 8 | runApp(const MyApp()); 9 | } 10 | 11 | class MyApp extends StatelessWidget { 12 | const MyApp({Key? key}) : super(key: key); 13 | 14 | // This widget is the root of your application. 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp( 18 | title: 'Flutter Demo', 19 | theme: ThemeData( 20 | // This is the theme of your application. 21 | // 22 | // Try running your application with "flutter run". You'll see the 23 | // application has a blue toolbar. Then, without quitting the app, try 24 | // changing the primarySwatch below to Colors.green and then invoke 25 | // "hot reload" (press "r" in the console where you ran "flutter run", 26 | // or simply save your changes to "hot reload" in a Flutter IDE). 27 | // Notice that the counter didn't reset back to zero; the application 28 | // is not restarted. 29 | primarySwatch: Colors.blue, 30 | ), 31 | home: const MyHomePage(title: 'Flutter Demo Home Page'), 32 | ); 33 | } 34 | } 35 | 36 | class MyHomePage extends StatefulWidget { 37 | const MyHomePage({Key? key, required this.title}) : super(key: key); 38 | 39 | // This widget is the home page of your application. It is stateful, meaning 40 | // that it has a State object (defined below) that contains fields that affect 41 | // how it looks. 42 | 43 | // This class is the configuration for the state. It holds the values (in this 44 | // case the title) provided by the parent (in this case the App widget) and 45 | // used by the build method of the State. Fields in a Widget subclass are 46 | // always marked "final". 47 | 48 | final String title; 49 | 50 | @override 51 | State createState() => _MyHomePageState(); 52 | } 53 | 54 | class _MyHomePageState extends State { 55 | late EasyImageEditorController _easyImageEditorController; 56 | final ImagePicker _picker = ImagePicker(); 57 | 58 | //late EditorView editorView; 59 | 60 | final List _colorArray = [ 61 | Colors.red, 62 | Colors.black, 63 | Colors.white, 64 | Colors.amber, 65 | Colors.black38, 66 | Colors.yellow, 67 | Colors.orange, 68 | Colors.deepOrange, 69 | Colors.pink, 70 | Colors.blue, 71 | Colors.cyan, 72 | Colors.deepPurple, 73 | Colors.teal, 74 | ]; 75 | 76 | @override 77 | void initState() { 78 | super.initState(); 79 | //_easyImageEditorController.canEditMultipleView(true); 80 | } 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | return Scaffold( 85 | appBar: AppBar( 86 | title: Text(widget.title), 87 | actions: [ 88 | IconButton( 89 | onPressed: () => 90 | _easyImageEditorController.canEditMultipleView(false), 91 | icon: const Icon(Icons.photo_size_select_large), 92 | ), 93 | IconButton( 94 | onPressed: () => 95 | _easyImageEditorController.canEditMultipleView(true), 96 | icon: const Icon(Icons.select_all), 97 | ), 98 | IconButton( 99 | onPressed: () => _easyImageEditorController.undo(), 100 | icon: const Icon(Icons.undo), 101 | ), 102 | IconButton( 103 | onPressed: () => _easyImageEditorController.redo(), 104 | icon: const Icon(Icons.redo), 105 | ), 106 | IconButton( 107 | onPressed: () { 108 | _easyImageEditorController.saveEditing().then((value) { 109 | if (value != null) { 110 | Navigator.push( 111 | context, 112 | MaterialPageRoute( 113 | builder: (builder) => Result(uint8list: value))); 114 | } 115 | }); 116 | }, 117 | icon: const Icon(Icons.done_all), 118 | ), 119 | ], 120 | ), 121 | body: EditorView( 122 | borderColor: Colors.red, 123 | clickToFocusAndMove: false, 124 | onInitialize: (controller) { 125 | setState(() { 126 | _easyImageEditorController = controller; 127 | }); 128 | }, 129 | removeIcon: const Icon( 130 | Icons.cancel_outlined, 131 | size: 20.0, 132 | ), 133 | onViewTouchOver: (position, widget, widgetType) { 134 | debugPrint("onViewTouch: $position, $widgetType"); 135 | }, 136 | onClick: (position, widget, widgetType) { 137 | debugPrint("onViewClick"); 138 | if (widgetType == "text") { 139 | Text _text = widget as Text; 140 | _addText(position, _text); 141 | } 142 | }, 143 | ), 144 | bottomNavigationBar: BottomNavigationBar( 145 | items: const [ 146 | BottomNavigationBarItem( 147 | icon: Icon(Icons.text_fields), 148 | label: "Add Text", 149 | ), 150 | BottomNavigationBarItem( 151 | icon: Icon(Icons.image), 152 | label: "Add Image", 153 | ), 154 | BottomNavigationBarItem( 155 | icon: Icon(Icons.color_lens), 156 | label: "Add Color", 157 | ), 158 | ], 159 | onTap: (position) { 160 | switch (position) { 161 | case 0: 162 | _addText(null, null); 163 | break; 164 | case 1: 165 | _addImage(); 166 | break; 167 | case 2: 168 | _addBg(); 169 | break; 170 | } 171 | }, 172 | ), 173 | ); 174 | } 175 | 176 | void _addText(int? position, Text? text) { 177 | final textEditController = TextEditingController(text: text?.data); 178 | Color? textColor = text?.style?.color; 179 | 180 | showModalBottomSheet( 181 | context: context, 182 | builder: (BuildContext context) { 183 | return StatefulBuilder(builder: (context, stateSetter) { 184 | return Column( 185 | children: [ 186 | Padding( 187 | padding: const EdgeInsets.all(10.0), 188 | child: TextField( 189 | controller: textEditController, 190 | keyboardType: TextInputType.text, 191 | textInputAction: TextInputAction.done, 192 | decoration: const InputDecoration( 193 | hintText: "Enter Text", 194 | ), 195 | style: TextStyle(color: textColor), 196 | ), 197 | ), 198 | SizedBox( 199 | height: 60, 200 | child: ListView.builder( 201 | shrinkWrap: true, 202 | itemCount: _colorArray.length, 203 | scrollDirection: Axis.horizontal, 204 | itemBuilder: (context, index) { 205 | return InkWell( 206 | onTap: () { 207 | stateSetter(() { 208 | textColor = _colorArray[index]; 209 | }); 210 | }, 211 | child: Container( 212 | height: 50.0, 213 | width: 50.0, 214 | color: _colorArray[index], 215 | ), 216 | ); 217 | }, 218 | ), 219 | ), 220 | InkWell( 221 | onTap: () { 222 | if (textEditController.text.isNotEmpty) { 223 | Navigator.pop(context); 224 | 225 | if (text == null) { 226 | _easyImageEditorController.addView( 227 | Text( 228 | textEditController.text, 229 | style: TextStyle( 230 | fontSize: 20.0, 231 | color: textColor, 232 | ), 233 | ), 234 | widgetType: "text", 235 | ); 236 | } else { 237 | _easyImageEditorController.updateView( 238 | position!, 239 | Text( 240 | textEditController.text, 241 | style: TextStyle( 242 | fontSize: 20.0, 243 | color: textColor, 244 | fontWeight: FontWeight.bold, 245 | ), 246 | ), 247 | ); 248 | } 249 | } 250 | }, 251 | child: Container( 252 | width: 100.0, 253 | margin: const EdgeInsets.all(20.0), 254 | padding: const EdgeInsets.all(10.0), 255 | decoration: BoxDecoration( 256 | color: Theme.of(context).primaryColor, 257 | borderRadius: 258 | const BorderRadius.all(Radius.circular(10))), 259 | child: const Center( 260 | child: Text("Add"), 261 | ), 262 | ), 263 | ), 264 | ], 265 | ); 266 | }); 267 | }); 268 | } 269 | 270 | _addBg() { 271 | showModalBottomSheet( 272 | context: context, 273 | builder: (context) { 274 | return SizedBox( 275 | height: 60, 276 | child: ListView.builder( 277 | shrinkWrap: true, 278 | itemCount: _colorArray.length, 279 | scrollDirection: Axis.horizontal, 280 | itemBuilder: (context, index) { 281 | return InkWell( 282 | onTap: () { 283 | Navigator.pop(context); 284 | _easyImageEditorController 285 | .addBackgroundColor(_colorArray[index]); 286 | }, 287 | child: Container( 288 | height: 50.0, 289 | width: 50.0, 290 | color: _colorArray[index], 291 | ), 292 | ); 293 | }, 294 | ), 295 | ); 296 | }); 297 | } 298 | 299 | _addImage() { 300 | showModalBottomSheet( 301 | context: context, 302 | builder: (context) { 303 | return Row( 304 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 305 | children: [ 306 | InkWell( 307 | onTap: () async { 308 | final XFile? image = 309 | await _picker.pickImage(source: ImageSource.gallery); 310 | if (image != null) { 311 | Navigator.pop(context); 312 | _easyImageEditorController 313 | .addBackgroundView(Image.file(File(image.path))); 314 | } 315 | }, 316 | child: const Padding( 317 | padding: EdgeInsets.all(20), 318 | child: Text( 319 | "Set Background", 320 | style: TextStyle(fontSize: 18.0), 321 | ), 322 | ), 323 | ), 324 | InkWell( 325 | onTap: () async { 326 | final XFile? image = 327 | await _picker.pickImage(source: ImageSource.gallery); 328 | if (image != null) { 329 | Navigator.pop(context); 330 | _easyImageEditorController.addView( 331 | Image.file( 332 | File(image.path), 333 | height: 200, 334 | width: 200, 335 | ), 336 | widgetType: "image"); 337 | } 338 | }, 339 | child: const Padding( 340 | padding: EdgeInsets.all(20), 341 | child: Text( 342 | "Add View", 343 | style: TextStyle(fontSize: 18.0), 344 | ), 345 | ), 346 | ), 347 | ], 348 | ); 349 | }); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /example/lib/result.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class Result extends StatefulWidget { 6 | const Result({key, required this.uint8list}) : super(key: key); 7 | 8 | final Uint8List uint8list; 9 | 10 | @override 11 | _ResultState createState() => _ResultState(); 12 | } 13 | 14 | class _ResultState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Image.memory(widget.uint8list); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | cross_file: 47 | dependency: transitive 48 | description: 49 | name: cross_file 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.3.2" 53 | cupertino_icons: 54 | dependency: "direct main" 55 | description: 56 | name: cupertino_icons 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.0.4" 60 | easy_image_editor: 61 | dependency: "direct dev" 62 | description: 63 | path: ".." 64 | relative: true 65 | source: path 66 | version: "1.0.3" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.2.0" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_lints: 80 | dependency: "direct dev" 81 | description: 82 | name: flutter_lints 83 | url: "https://pub.dartlang.org" 84 | source: hosted 85 | version: "1.0.4" 86 | flutter_plugin_android_lifecycle: 87 | dependency: transitive 88 | description: 89 | name: flutter_plugin_android_lifecycle 90 | url: "https://pub.dartlang.org" 91 | source: hosted 92 | version: "2.0.5" 93 | flutter_test: 94 | dependency: "direct dev" 95 | description: flutter 96 | source: sdk 97 | version: "0.0.0" 98 | flutter_web_plugins: 99 | dependency: transitive 100 | description: flutter 101 | source: sdk 102 | version: "0.0.0" 103 | http: 104 | dependency: transitive 105 | description: 106 | name: http 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "0.13.4" 110 | http_parser: 111 | dependency: transitive 112 | description: 113 | name: http_parser 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "4.0.0" 117 | image_picker: 118 | dependency: "direct dev" 119 | description: 120 | name: image_picker 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.8.4+11" 124 | image_picker_for_web: 125 | dependency: transitive 126 | description: 127 | name: image_picker_for_web 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "2.1.6" 131 | image_picker_platform_interface: 132 | dependency: transitive 133 | description: 134 | name: image_picker_platform_interface 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "2.4.4" 138 | js: 139 | dependency: transitive 140 | description: 141 | name: js 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "0.6.3" 145 | lints: 146 | dependency: transitive 147 | description: 148 | name: lints 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.0.1" 152 | matcher: 153 | dependency: transitive 154 | description: 155 | name: matcher 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "0.12.10" 159 | matrix4_transform: 160 | dependency: transitive 161 | description: 162 | name: matrix4_transform 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.0.1" 166 | meta: 167 | dependency: transitive 168 | description: 169 | name: meta 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "1.7.0" 173 | path: 174 | dependency: transitive 175 | description: 176 | name: path 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.8.0" 180 | plugin_platform_interface: 181 | dependency: transitive 182 | description: 183 | name: plugin_platform_interface 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "2.1.2" 187 | screenshot: 188 | dependency: transitive 189 | description: 190 | name: screenshot 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "1.2.3" 194 | sky_engine: 195 | dependency: transitive 196 | description: flutter 197 | source: sdk 198 | version: "0.0.99" 199 | source_span: 200 | dependency: transitive 201 | description: 202 | name: source_span 203 | url: "https://pub.dartlang.org" 204 | source: hosted 205 | version: "1.8.1" 206 | stack_trace: 207 | dependency: transitive 208 | description: 209 | name: stack_trace 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "1.10.0" 213 | stream_channel: 214 | dependency: transitive 215 | description: 216 | name: stream_channel 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "2.1.0" 220 | string_scanner: 221 | dependency: transitive 222 | description: 223 | name: string_scanner 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.1.0" 227 | term_glyph: 228 | dependency: transitive 229 | description: 230 | name: term_glyph 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "1.2.0" 234 | test_api: 235 | dependency: transitive 236 | description: 237 | name: test_api 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "0.4.2" 241 | typed_data: 242 | dependency: transitive 243 | description: 244 | name: typed_data 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "1.3.0" 248 | vector_math: 249 | dependency: transitive 250 | description: 251 | name: vector_math 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "2.1.0" 255 | sdks: 256 | dart: ">=2.14.0 <3.0.0" 257 | flutter: ">=2.5.0" 258 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | 38 | dev_dependencies: 39 | flutter_test: 40 | sdk: flutter 41 | easy_image_editor: 42 | path: ../ 43 | image_picker: ^0.8.4+11 44 | 45 | # The "flutter_lints" package below contains a set of recommended lints to 46 | # encourage good coding practices. The lint set provided by the package is 47 | # activated in the `analysis_options.yaml` file located at the root of your 48 | # package. See that file for information about deactivating specific lint 49 | # rules and activating additional ones. 50 | flutter_lints: ^1.0.0 51 | 52 | # For information on the generic Dart part of this file, see the 53 | # following page: https://dart.dev/tools/pub/pubspec 54 | 55 | # The following section is specific to Flutter. 56 | flutter: 57 | 58 | # The following line ensures that the Material Icons font is 59 | # included with your application, so that you can use the icons in 60 | # the material Icons class. 61 | uses-material-design: true 62 | 63 | # To add assets to your application, add an assets section, like this: 64 | # assets: 65 | # - images/a_dot_burr.jpeg 66 | # - images/a_dot_ham.jpeg 67 | 68 | # An image asset can refer to one or more resolution-specific "variants", see 69 | # https://flutter.dev/assets-and-images/#resolution-aware. 70 | 71 | # For details regarding adding assets from package dependencies, see 72 | # https://flutter.dev/assets-and-images/#from-packages 73 | 74 | # To add custom fonts to your application, add a fonts section here, 75 | # in this "flutter" section. Each entry in this list should have a 76 | # "family" key with the font family name, and a "fonts" key with a 77 | # list giving the asset and other descriptors for the font. For 78 | # example: 79 | # fonts: 80 | # - family: Schyler 81 | # fonts: 82 | # - asset: fonts/Schyler-Regular.ttf 83 | # - asset: fonts/Schyler-Italic.ttf 84 | # style: italic 85 | # - family: Trajan Pro 86 | # fonts: 87 | # - asset: fonts/TrajanPro.ttf 88 | # - asset: fonts/TrajanPro_Bold.ttf 89 | # weight: 700 90 | # 91 | # For details regarding fonts from package dependencies, 92 | # see https://flutter.dev/custom-fonts/#from-packages 93 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/easy_image_editor.dart: -------------------------------------------------------------------------------- 1 | library easy_image_editor; 2 | 3 | export 'src/editor_view.dart'; 4 | export 'src/resizable_widget.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/editor_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:matrix4_transform/matrix4_transform.dart'; 3 | import 'package:screenshot/screenshot.dart'; 4 | import 'dart:typed_data'; 5 | import 'resizable_widget.dart'; 6 | 7 | class EasyImageEditorController { 8 | _EditorViewState? _editorViewState; 9 | 10 | void _init(_EditorViewState editorViewState) { 11 | _editorViewState = editorViewState; 12 | } 13 | 14 | /// This function user for add view in editor 15 | void addView(Widget view, {String? widgetType}) => 16 | _editorViewState?._addView(view, widgetType); 17 | 18 | /// update view of given position. 19 | void updateView(int position, Widget view) => 20 | _editorViewState?._updateView(position, view); 21 | 22 | /// hide Border and Remove button from all views 23 | void hideViewControl() => _editorViewState?._disableEditMode(); 24 | 25 | /// show Border and Remove button in all views 26 | void showViewControl() => _editorViewState?._enableEditModel(); 27 | 28 | /// allow editor to move, zoom and rotate multiple views. if you set true than only one view can move, zoom and rotate default value is true. 29 | void canEditMultipleView(bool isMultipleSelection) => 30 | _editorViewState?._setSelectionMode(!isMultipleSelection); 31 | 32 | /// set editor background view it will overlap background color. 33 | void addBackgroundView(Widget? view) => _editorViewState?._addBGView(view); 34 | 35 | /// set editor background color. 36 | void addBackgroundColor(Color color) => _editorViewState?._addBGColor(color); 37 | 38 | /// redo your changes 39 | void redo() => _editorViewState?._redo(); 40 | 41 | /// undo your changes 42 | void undo() => _editorViewState?._undo(); 43 | 44 | /// move view by provide position and move type like left, right and his his value. 45 | /// Ex. position = 0, moveType = MoveType.left, value = 10 46 | void moveView(int position, MoveType moveType, double value) => 47 | _editorViewState?._moveView(position, moveType, value); 48 | 49 | /// rotate particular view 50 | void rotateView(int position, double rotateDegree) => 51 | _editorViewState?._rotateView(position, rotateDegree); 52 | 53 | /// zoom In and Out view 54 | /// for zoom view value > 1 55 | /// for zoom out view value < 0 like (0.1) 56 | void zoomInOutView(int position, double value) => 57 | _editorViewState?._zoomInOut(position, value); 58 | 59 | /// update matrix of particular view 60 | void updateMatrix(int position, Matrix4 matrix4) => 61 | _editorViewState?._updateMatrix(position, matrix4); 62 | 63 | /// flip particular view 64 | void flipView(int position, bool isHorizontal) => 65 | _editorViewState?._flipView(position, isHorizontal); 66 | 67 | /// save all edited views and his position and return Uint8List data. 68 | Future saveEditing() { 69 | assert(_editorViewState != null); 70 | 71 | return _editorViewState!._saveView(); 72 | } 73 | } 74 | 75 | class EditorView extends StatefulWidget { 76 | const EditorView({ 77 | key, 78 | required this.onInitialize, 79 | this.onViewTouch, 80 | this.onViewTouchOver, 81 | this.onClick, 82 | this.clickToFocusAndMove = false, 83 | this.borderColor = Colors.black, 84 | this.removeIcon = const Icon( 85 | Icons.cancel, 86 | size: 20.0, 87 | ), 88 | }) : super(key: key); 89 | 90 | @override 91 | _EditorViewState createState() => _EditorViewState(); 92 | 93 | final Function(EasyImageEditorController) onInitialize; 94 | 95 | /// this event fire every time you touch view. 96 | final Function(int, Widget, String?)? onViewTouch; 97 | 98 | /// this event fire every time when user remove touch from view. 99 | final Function(int, Widget, String?)? onViewTouchOver; 100 | 101 | /// this event fire every time when user click view. 102 | final Function(int, Widget, String?)? onClick; 103 | 104 | /// set border color of widget 105 | final Color borderColor; 106 | 107 | /// set remove icon 108 | final Icon removeIcon; 109 | 110 | final bool clickToFocusAndMove; 111 | } 112 | 113 | class _EditorViewState extends State { 114 | final ScreenshotController _screenshotController = ScreenshotController(); 115 | 116 | final List _widgetList = []; 117 | final List widgetList = []; 118 | 119 | bool isSingleMove = false; 120 | 121 | Color? _backgroundColor; 122 | Widget? _backgroundWidget; 123 | 124 | Future _saveView() async { 125 | _disableEditMode(); 126 | return await _screenshotController.capture(); 127 | } 128 | 129 | void _addBGView(Widget? view) { 130 | if (mounted) { 131 | setState(() { 132 | _backgroundWidget = view; 133 | }); 134 | } else { 135 | _backgroundWidget = view; 136 | } 137 | } 138 | 139 | void _addBGColor(Color color) { 140 | if (mounted) { 141 | setState(() { 142 | _backgroundColor = color; 143 | }); 144 | } else { 145 | _backgroundColor = color; 146 | } 147 | } 148 | 149 | void _setSelectionMode(bool isSingleSelection) { 150 | if (mounted) { 151 | setState(() { 152 | isSingleMove = isSingleSelection; 153 | 154 | if (isSingleMove) { 155 | _disableEditMode(); 156 | } else { 157 | _enableEditModel(); 158 | } 159 | }); 160 | } else { 161 | isSingleMove = isSingleSelection; 162 | } 163 | } 164 | 165 | void _disableEditMode() { 166 | if (mounted) { 167 | setState(() { 168 | for (var element in _widgetList) { 169 | element.showRemoveIcon = false; 170 | element.canMove = false; 171 | element.borderColor = Colors.transparent; 172 | element.updateView(); 173 | } 174 | }); 175 | } 176 | //screenshotController.capture().then((value) => null); 177 | } 178 | 179 | void _enableEditModel() { 180 | if (mounted) { 181 | setState(() { 182 | for (var element in _widgetList) { 183 | element.showRemoveIcon = true; 184 | element.canMove = true; 185 | element.borderColor = widget.borderColor; 186 | element.updateView(); 187 | } 188 | }); 189 | } 190 | } 191 | 192 | void _updateView(int position, Widget view) { 193 | assert(position >= 0 && 194 | _widgetList.isNotEmpty && 195 | _widgetList.length - 1 <= position); 196 | setState(() { 197 | debugPrint("viewUpdated"); 198 | _widgetList[position].resizableWidget = view; 199 | _widgetList[position].updateView(); 200 | }); 201 | } 202 | 203 | void _addView(Widget view, String? widgetType) { 204 | if (mounted) { 205 | setState(() { 206 | _addViewInList(view, widgetType); 207 | }); 208 | } else { 209 | _addViewInList(view, widgetType); 210 | } 211 | } 212 | 213 | void _addViewInList(Widget view, String? widgetType, {Matrix4? matrix4}) { 214 | if (_widgetList.isNotEmpty) { 215 | final lastViewItem = _widgetList.last; 216 | lastViewItem.showRemoveIcon = false; 217 | lastViewItem.borderColor = Colors.transparent; 218 | lastViewItem.canMove = false; 219 | lastViewItem.updateView(); 220 | } 221 | 222 | final resizableView = ResizableWidget( 223 | key: ObjectKey(DateTime.now().toString()), 224 | position: _widgetList.length, 225 | canMove: true, 226 | borderColor: widget.borderColor, 227 | removeIcon: widget.removeIcon, 228 | resizableWidget: view, 229 | widgetType: widgetType, 230 | matrix4: matrix4, 231 | onRemoveClick: (key, index) { 232 | setState(() { 233 | //_widgetList.removeWhere((element) => element.key == key); 234 | try { 235 | final removeView = 236 | _widgetList.firstWhere((element) => element.key == key); 237 | removeView.isVisible = false; 238 | removeView.updateView(); 239 | } catch (_) {} 240 | }); 241 | }, 242 | onClick: (key, index, widgetType) { 243 | if (widget.clickToFocusAndMove) { 244 | setState(() { 245 | final finalIndex = 246 | _widgetList.indexWhere((element) => element.key == key); 247 | 248 | final touchView = _widgetList.removeAt(finalIndex); 249 | touchView.showRemoveIcon = true; 250 | touchView.canMove = true; 251 | touchView.borderColor = widget.borderColor; 252 | touchView.updateView(); 253 | _widgetList.add(touchView); 254 | 255 | if (widget.onClick != null) { 256 | widget.onClick!(_widgetList.length - 1, touchView.resizableWidget, 257 | touchView.widgetType); 258 | } 259 | 260 | if (isSingleMove) { 261 | for (var element in _widgetList) { 262 | if (element != touchView) { 263 | element.showRemoveIcon = false; 264 | element.borderColor = Colors.transparent; 265 | element.canMove = false; 266 | element.updateView(); 267 | } 268 | } 269 | } 270 | }); 271 | } else { 272 | final finalIndex = 273 | _widgetList.indexWhere((element) => element.key == key); 274 | 275 | final touchView = _widgetList[finalIndex]; 276 | 277 | if (widget.onClick != null) { 278 | widget.onClick!(_widgetList.length - 1, touchView.resizableWidget, 279 | touchView.widgetType); 280 | } 281 | } 282 | }, 283 | onSetTop: (key, index, widgetType) { 284 | debugPrint("onTouch"); 285 | if (!widget.clickToFocusAndMove) { 286 | setState(() { 287 | final finalIndex = 288 | _widgetList.indexWhere((element) => element.key == key); 289 | 290 | final touchView = _widgetList.removeAt(finalIndex); 291 | touchView.showRemoveIcon = true; 292 | touchView.canMove = true; 293 | touchView.borderColor = widget.borderColor; 294 | touchView.updateView(); 295 | _widgetList.add(touchView); 296 | 297 | if (widget.onViewTouch != null) { 298 | widget.onViewTouch!(_widgetList.length - 1, 299 | touchView.resizableWidget, touchView.widgetType); 300 | } 301 | 302 | if (isSingleMove) { 303 | for (var element in _widgetList) { 304 | if (element != touchView) { 305 | element.showRemoveIcon = false; 306 | element.borderColor = Colors.transparent; 307 | element.canMove = false; 308 | element.updateView(); 309 | } 310 | } 311 | } 312 | }); 313 | } 314 | }, 315 | onTouchOver: (key, position, matrix) { 316 | if (widget.onViewTouchOver != null) { 317 | final touchView = 318 | _widgetList.firstWhere((element) => element.key == key); 319 | widget.onViewTouchOver!(_widgetList.length - 1, 320 | touchView.resizableWidget, touchView.widgetType); 321 | } 322 | }, 323 | ); 324 | 325 | _widgetList.add(resizableView); 326 | } 327 | 328 | void _undo() { 329 | if (_widgetList.isNotEmpty) { 330 | try { 331 | setState(() { 332 | final lastUnVisibleView = 333 | _widgetList.lastWhere((element) => element.isVisible); 334 | lastUnVisibleView.isVisible = false; 335 | lastUnVisibleView.updateView(); 336 | }); 337 | } catch (_) {} 338 | } 339 | } 340 | 341 | void _redo() { 342 | if (_widgetList.isNotEmpty) { 343 | try { 344 | setState(() { 345 | final lastUnVisibleView = 346 | _widgetList.firstWhere((element) => !element.isVisible); 347 | lastUnVisibleView.isVisible = true; 348 | lastUnVisibleView.updateView(); 349 | }); 350 | } catch (_) {} 351 | } 352 | } 353 | 354 | void _flipView(int position, bool isHorizontal) { 355 | assert(position >= 0 && 356 | _widgetList.isNotEmpty && 357 | _widgetList.length - 1 <= position); 358 | 359 | setState(() { 360 | final view = _widgetList[position]; 361 | var myTransform = Matrix4Transform.from(view.matrix4!); 362 | if (isHorizontal) { 363 | _widgetList[position] 364 | .updateMatrix(myTransform.flipHorizontally().matrix4); 365 | } else { 366 | _widgetList[position] 367 | .updateMatrix(myTransform.flipVertically().matrix4); 368 | } 369 | }); 370 | } 371 | 372 | void _updateMatrix(int position, Matrix4 matrix) { 373 | assert(position >= 0 && 374 | _widgetList.isNotEmpty && 375 | _widgetList.length - 1 <= position); 376 | setState(() { 377 | _widgetList[position].updateMatrix(matrix); 378 | }); 379 | } 380 | 381 | void _zoomInOut(int position, double value) { 382 | assert(position >= 0 && 383 | _widgetList.isNotEmpty && 384 | _widgetList.length - 1 <= position); 385 | setState(() { 386 | final view = _widgetList[position]; 387 | var myTransform = Matrix4Transform.from(view.matrix4!); 388 | _widgetList[position].updateMatrix(myTransform.scale(value).matrix4); 389 | }); 390 | } 391 | 392 | void _rotateView(int position, double rotateDegree) { 393 | assert(position >= 0 && 394 | _widgetList.isNotEmpty && 395 | _widgetList.length - 1 <= position); 396 | setState(() { 397 | final view = _widgetList[position]; 398 | var myTransform = Matrix4Transform.from(view.matrix4!); 399 | _widgetList[position].updateMatrix(myTransform 400 | .rotateByCenterDegrees( 401 | rotateDegree, Size(view.getWidth(), view.getHeight())) 402 | .matrix4); 403 | }); 404 | } 405 | 406 | void _moveView(int position, MoveType moveType, double value) { 407 | assert(position >= 0 && 408 | _widgetList.isNotEmpty && 409 | _widgetList.length - 1 <= position); 410 | 411 | setState(() { 412 | final view = _widgetList[position]; 413 | var myTransform = Matrix4Transform.from(view.matrix4!); 414 | if (moveType == MoveType.right) { 415 | _widgetList[position].updateMatrix(myTransform.right(value).matrix4); 416 | } else if (moveType == MoveType.bottom) { 417 | _widgetList[position].updateMatrix(myTransform.down(value).matrix4); 418 | } else if (moveType == MoveType.top) { 419 | _widgetList[position].updateMatrix(myTransform.up(value).matrix4); 420 | } else if (moveType == MoveType.left) { 421 | _widgetList[position].updateMatrix(myTransform.left(value).matrix4); 422 | } 423 | }); 424 | } 425 | 426 | @override 427 | void initState() { 428 | super.initState(); 429 | 430 | WidgetsBinding.instance?.addPostFrameCallback((_) { 431 | EasyImageEditorController easyImageEditorController = 432 | EasyImageEditorController(); 433 | easyImageEditorController._init(this); 434 | widget.onInitialize(easyImageEditorController); 435 | }); 436 | 437 | if (widget.clickToFocusAndMove) { 438 | _setSelectionMode(true); 439 | } 440 | } 441 | 442 | @override 443 | Widget build(BuildContext context) { 444 | return Screenshot( 445 | controller: _screenshotController, 446 | child: Container( 447 | height: MediaQuery.of(context).size.height, 448 | width: MediaQuery.of(context).size.width, 449 | color: (_backgroundColor != null) ? _backgroundColor : Colors.white, 450 | child: ClipRect( 451 | child: Stack( 452 | children: [ 453 | if (_backgroundWidget != null) 454 | Positioned( 455 | top: 0, 456 | left: 0, 457 | right: 0, 458 | bottom: 0, 459 | child: _backgroundWidget!, 460 | ), 461 | ..._widgetList, 462 | ], 463 | ), 464 | ), 465 | ), 466 | ); 467 | } 468 | } 469 | 470 | enum MoveType { 471 | left, 472 | right, 473 | top, 474 | bottom, 475 | } 476 | -------------------------------------------------------------------------------- /lib/src/matrix_gesture_detector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'dart:math'; 3 | 4 | typedef MatrixGestureDetectorCallback = void Function( 5 | Matrix4 matrix, 6 | Matrix4 translationDeltaMatrix, 7 | Matrix4 scaleDeltaMatrix, 8 | Matrix4 rotationDeltaMatrix); 9 | 10 | /// [MatrixGestureDetector] detects translation, scale and rotation gestures 11 | /// and combines them into [Matrix4] object that can be used by [Transform] widget 12 | /// or by low level [CustomPainter] code. You can customize types of reported 13 | /// gestures by passing [shouldTranslate], [shouldScale] and [shouldRotate] 14 | /// parameters. 15 | /// 16 | // ignore: must_be_immutable 17 | class MatrixGestureDetector extends StatefulWidget { 18 | /// [Matrix4] change notification callback 19 | /// 20 | final MatrixGestureDetectorCallback onMatrixUpdate; 21 | 22 | /// The [child] contained by this detector. 23 | /// 24 | /// {@macro flutter.widgets.child} 25 | /// 26 | final Widget child; 27 | 28 | /// Whether to detect translation gestures during the event processing. 29 | /// 30 | /// Defaults to true. 31 | /// 32 | final bool shouldTranslate; 33 | 34 | /// Whether to detect scale gestures during the event processing. 35 | /// 36 | /// Defaults to true. 37 | /// 38 | final bool shouldScale; 39 | 40 | /// Whether to detect rotation gestures during the event processing. 41 | /// 42 | /// Defaults to true. 43 | /// 44 | final bool shouldRotate; 45 | 46 | /// Whether [ClipRect] widget should clip [child] widget. 47 | /// 48 | /// Defaults to true. 49 | /// 50 | final bool clipChild; 51 | 52 | /// When set, it will be used for computing a "fixed" focal point 53 | /// aligned relative to the size of this widget. 54 | final Alignment? focalPointAlignment; 55 | 56 | Matrix4? matrix4Old; 57 | 58 | MatrixGestureDetector({ 59 | Key? key, 60 | required this.onMatrixUpdate, 61 | required this.child, 62 | this.matrix4Old, 63 | this.shouldTranslate = true, 64 | this.shouldScale = true, 65 | this.shouldRotate = true, 66 | this.clipChild = true, 67 | this.focalPointAlignment, 68 | }) : super(key: key); 69 | 70 | @override 71 | _MatrixGestureDetectorState createState() => _MatrixGestureDetectorState(); 72 | 73 | /// 74 | /// Compose the matrix from translation, scale and rotation matrices - you can 75 | /// pass a null to skip any matrix from composition. 76 | /// 77 | /// If [matrix] is not null the result of the composing will be concatenated 78 | /// to that [matrix], otherwise the identity matrix will be used. 79 | /// 80 | static Matrix4 compose(Matrix4? matrix, Matrix4? translationMatrix, 81 | Matrix4? scaleMatrix, Matrix4? rotationMatrix) { 82 | matrix ??= Matrix4.identity(); 83 | if (translationMatrix != null) matrix = translationMatrix * matrix; 84 | if (scaleMatrix != null) matrix = scaleMatrix * matrix; 85 | if (rotationMatrix != null) matrix = rotationMatrix * matrix; 86 | return matrix!; 87 | } 88 | 89 | /// 90 | /// Decomposes [matrix] into [MatrixDecomposedValues.translation], 91 | /// [MatrixDecomposedValues.scale] and [MatrixDecomposedValues.rotation] components. 92 | /// 93 | static MatrixDecomposedValues decomposeToValues(Matrix4 matrix) { 94 | var array = matrix.applyToVector3Array([0, 0, 0, 1, 0, 0]); 95 | Offset translation = Offset(array[0], array[1]); 96 | Offset delta = Offset(array[3] - array[0], array[4] - array[1]); 97 | double scale = delta.distance; 98 | double rotation = delta.direction; 99 | return MatrixDecomposedValues(translation, scale, rotation); 100 | } 101 | } 102 | 103 | class _MatrixGestureDetectorState extends State { 104 | Matrix4 translationDeltaMatrix = Matrix4.identity(); 105 | Matrix4 scaleDeltaMatrix = Matrix4.identity(); 106 | Matrix4 rotationDeltaMatrix = Matrix4.identity(); 107 | Matrix4 matrix = Matrix4.identity(); 108 | 109 | @override 110 | Widget build(BuildContext context) { 111 | Widget child = 112 | widget.clipChild ? ClipRect(child: widget.child) : widget.child; 113 | return GestureDetector( 114 | onScaleStart: onScaleStart, 115 | onScaleUpdate: onScaleUpdate, 116 | child: child, 117 | ); 118 | } 119 | 120 | _ValueUpdater translationUpdater = _ValueUpdater( 121 | value: Offset.zero, 122 | onUpdate: (oldVal, newVal) => newVal - oldVal, 123 | ); 124 | _ValueUpdater scaleUpdater = _ValueUpdater( 125 | value: 1.0, 126 | onUpdate: (oldVal, newVal) => newVal / oldVal, 127 | ); 128 | _ValueUpdater rotationUpdater = _ValueUpdater( 129 | value: 0.0, 130 | onUpdate: (oldVal, newVal) => newVal - oldVal, 131 | ); 132 | 133 | void onScaleStart(ScaleStartDetails details) { 134 | translationUpdater.value = details.focalPoint; 135 | scaleUpdater.value = 1.0; 136 | rotationUpdater.value = 0.0; 137 | } 138 | 139 | void onScaleUpdate(ScaleUpdateDetails details) { 140 | translationDeltaMatrix = Matrix4.identity(); 141 | scaleDeltaMatrix = Matrix4.identity(); 142 | rotationDeltaMatrix = Matrix4.identity(); 143 | 144 | if (widget.matrix4Old != null) { 145 | matrix = widget.matrix4Old!; 146 | widget.matrix4Old = null; 147 | } 148 | 149 | // handle matrix translating 150 | if (widget.shouldTranslate) { 151 | Offset translationDelta = translationUpdater.update(details.focalPoint); 152 | translationDeltaMatrix = _translate(translationDelta); 153 | matrix = translationDeltaMatrix * matrix; 154 | } 155 | 156 | final focalPointAlignment = widget.focalPointAlignment; 157 | final focalPoint = focalPointAlignment == null 158 | ? details.localFocalPoint 159 | : focalPointAlignment.alongSize(context.size!); 160 | 161 | // handle matrix scaling 162 | if (widget.shouldScale && details.scale != 1.0) { 163 | double scaleDelta = scaleUpdater.update(details.scale); 164 | scaleDeltaMatrix = _scale(scaleDelta, focalPoint); 165 | matrix = scaleDeltaMatrix * matrix; 166 | } 167 | 168 | // handle matrix rotating 169 | if (widget.shouldRotate && details.rotation != 0.0) { 170 | double rotationDelta = rotationUpdater.update(details.rotation); 171 | rotationDeltaMatrix = _rotate(rotationDelta, focalPoint); 172 | matrix = rotationDeltaMatrix * matrix; 173 | } 174 | 175 | widget.onMatrixUpdate( 176 | matrix, translationDeltaMatrix, scaleDeltaMatrix, rotationDeltaMatrix); 177 | } 178 | 179 | Matrix4 _translate(Offset translation) { 180 | var dx = translation.dx; 181 | var dy = translation.dy; 182 | 183 | // ..[0] = 1 # x scale 184 | // ..[5] = 1 # y scale 185 | // ..[10] = 1 # diagonal "one" 186 | // ..[12] = dx # x translation 187 | // ..[13] = dy # y translation 188 | // ..[15] = 1 # diagonal "one" 189 | return Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); 190 | } 191 | 192 | Matrix4 _scale(double scale, Offset focalPoint) { 193 | var dx = (1 - scale) * focalPoint.dx; 194 | var dy = (1 - scale) * focalPoint.dy; 195 | 196 | // ..[0] = scale # x scale 197 | // ..[5] = scale # y scale 198 | // ..[10] = 1 # diagonal "one" 199 | // ..[12] = dx # x translation 200 | // ..[13] = dy # y translation 201 | // ..[15] = 1 # diagonal "one" 202 | return Matrix4(scale, 0, 0, 0, 0, scale, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); 203 | } 204 | 205 | Matrix4 _rotate(double angle, Offset focalPoint) { 206 | var c = cos(angle); 207 | var s = sin(angle); 208 | var dx = (1 - c) * focalPoint.dx + s * focalPoint.dy; 209 | var dy = (1 - c) * focalPoint.dy - s * focalPoint.dx; 210 | 211 | // ..[0] = c # x scale 212 | // ..[1] = s # y skew 213 | // ..[4] = -s # x skew 214 | // ..[5] = c # y scale 215 | // ..[10] = 1 # diagonal "one" 216 | // ..[12] = dx # x translation 217 | // ..[13] = dy # y translation 218 | // ..[15] = 1 # diagonal "one" 219 | return Matrix4(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); 220 | } 221 | } 222 | 223 | typedef _OnUpdate = T Function(T oldValue, T newValue); 224 | 225 | class _ValueUpdater { 226 | final _OnUpdate onUpdate; 227 | T value; 228 | 229 | _ValueUpdater({ 230 | required this.value, 231 | required this.onUpdate, 232 | }); 233 | 234 | T update(T newValue) { 235 | T updated = onUpdate(value, newValue); 236 | value = newValue; 237 | return updated; 238 | } 239 | } 240 | 241 | class MatrixDecomposedValues { 242 | /// Translation, in most cases useful only for matrices that are nothing but 243 | /// a translation (no scale and no rotation). 244 | final Offset translation; 245 | 246 | /// Scaling factor. 247 | final double scale; 248 | 249 | /// Rotation in radians, (-pi..pi) range. 250 | final double rotation; 251 | 252 | MatrixDecomposedValues(this.translation, this.scale, this.rotation); 253 | 254 | @override 255 | String toString() { 256 | return 'MatrixDecomposedValues(translation: $translation, scale: ${scale.toStringAsFixed(3)}, rotation: ${rotation.toStringAsFixed(3)})'; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /lib/src/resizable_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'matrix_gesture_detector.dart'; 3 | import 'dart:async'; 4 | import 'dart:math' as math; 5 | 6 | // ignore: must_be_immutable 7 | class ResizableWidget extends StatefulWidget { 8 | ResizableWidget({ 9 | key, 10 | required this.position, 11 | required this.resizableWidget, 12 | this.canMove = true, 13 | this.showRemoveIcon = true, 14 | this.borderColor = Colors.black, 15 | this.widgetType, 16 | this.matrix4, 17 | this.isVisible = true, 18 | this.removeIcon = const Icon( 19 | Icons.close, 20 | size: 20.0, 21 | ), 22 | required this.onRemoveClick, 23 | required this.onSetTop, 24 | required this.onClick, 25 | required this.onTouchOver, 26 | }) : super(key: key); 27 | 28 | final int position; 29 | Widget resizableWidget; 30 | bool canMove; 31 | bool showRemoveIcon; 32 | Color borderColor; 33 | String? widgetType; 34 | Matrix4? matrix4; 35 | bool isVisible; 36 | final Icon removeIcon; 37 | 38 | /// return widget key and his position 39 | final Function(Key, int) onRemoveClick; 40 | 41 | /// return widget key, position and his type if you added 42 | final Function(Key, int, String?) onSetTop; 43 | 44 | /// return widget key, position and his type if you added 45 | final Function(Key, int, String?) onClick; 46 | 47 | /// return widget key, position and his matrix 48 | final Function(Key, int, Matrix4) onTouchOver; 49 | 50 | final resizableWidgetState = _ResizableWidgetState(); 51 | 52 | @override 53 | // ignore: no_logic_in_create_state 54 | _ResizableWidgetState createState() => resizableWidgetState; 55 | 56 | void updateMatrix(Matrix4 matrix4) => 57 | resizableWidgetState._setMatrix(matrix4); 58 | 59 | void updateView() { 60 | // ignore: invalid_use_of_protected_member 61 | resizableWidgetState.setState(() {}); 62 | } 63 | 64 | double getX() => resizableWidgetState._getX(); 65 | 66 | double getY() => resizableWidgetState._getY(); 67 | 68 | double getAngle() => resizableWidgetState._getAngle(); 69 | 70 | double getHeight() => resizableWidgetState._getHeight(); 71 | 72 | double getWidth() => resizableWidgetState._getWidth(); 73 | } 74 | 75 | class _ResizableWidgetState extends State { 76 | GlobalKey key = GlobalKey(); 77 | 78 | Matrix4 matrix = Matrix4.identity(); 79 | 80 | bool _isTouched = false; 81 | 82 | Timer? _timer; 83 | 84 | void _setMatrix(Matrix4 matrix4) { 85 | widget.matrix4 = matrix4; 86 | if (mounted) { 87 | setState(() { 88 | matrix = matrix4; 89 | }); 90 | } else { 91 | matrix = matrix4; 92 | } 93 | } 94 | 95 | double _getX() { 96 | RenderBox box = key.currentContext?.findRenderObject() as RenderBox; 97 | Offset position = box.localToGlobal(Offset.zero); 98 | return position.dx; 99 | } 100 | 101 | double _getY() { 102 | RenderBox box = key.currentContext?.findRenderObject() as RenderBox; 103 | Offset position = box.localToGlobal(Offset.zero); 104 | return position.dy - 87.6; 105 | } 106 | 107 | double _getAngle() { 108 | RenderBox box = key.currentContext?.findRenderObject() as RenderBox; 109 | Offset position = box.localToGlobal(Offset.zero); 110 | return -math.atan2(position.dy - 87.6, position.dx); 111 | } 112 | 113 | double _getWidth() { 114 | RenderBox box = key.currentContext?.findRenderObject() as RenderBox; 115 | return box.size.width; 116 | } 117 | 118 | double _getHeight() { 119 | RenderBox box = key.currentContext?.findRenderObject() as RenderBox; 120 | return box.size.height; 121 | } 122 | 123 | @override 124 | void initState() { 125 | super.initState(); 126 | if (widget.matrix4 != null) { 127 | setState(() { 128 | matrix = widget.matrix4!; 129 | }); 130 | } else { 131 | widget.matrix4 = matrix; 132 | } 133 | } 134 | 135 | @override 136 | Widget build(BuildContext context) { 137 | return (widget.isVisible) 138 | ? Transform( 139 | transform: matrix, 140 | child: Stack( 141 | key: key, 142 | children: [ 143 | MatrixGestureDetector( 144 | onMatrixUpdate: 145 | (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) { 146 | setState(() { 147 | if (widget.canMove) { 148 | matrix = m; 149 | widget.matrix4 = m; 150 | } 151 | if (!_isTouched) { 152 | _isTouched = true; 153 | widget.onSetTop( 154 | widget.key!, widget.position, widget.widgetType); 155 | } 156 | }); 157 | 158 | if (_timer?.isActive ?? false) { 159 | _timer?.cancel(); 160 | _timer = null; 161 | } 162 | 163 | _timer = Timer(const Duration(milliseconds: 600), () { 164 | widget.onTouchOver(widget.key!, widget.position, m); 165 | if (mounted) { 166 | setState(() { 167 | _isTouched = false; 168 | }); 169 | } 170 | }); 171 | }, 172 | focalPointAlignment: Alignment.center, 173 | matrix4Old: matrix, 174 | child: InkWell( 175 | onTap: () { 176 | debugPrint("click Top"); 177 | widget.onClick( 178 | widget.key!, widget.position, widget.widgetType); 179 | widget.onSetTop( 180 | widget.key!, widget.position, widget.widgetType); 181 | }, 182 | child: Container( 183 | padding: const EdgeInsets.all(10.0), 184 | decoration: BoxDecoration( 185 | border: 186 | Border.all(color: widget.borderColor, width: 1.0), 187 | borderRadius: 188 | const BorderRadius.all(Radius.circular(5.0)), 189 | ), 190 | child: widget.resizableWidget, 191 | ), 192 | ), 193 | ), 194 | if (widget.showRemoveIcon) 195 | Positioned( 196 | top: 0, 197 | left: 0, 198 | child: InkWell( 199 | onTap: () { 200 | widget.onRemoveClick(widget.key!, widget.position); 201 | }, 202 | child: widget.removeIcon, 203 | ), 204 | ), 205 | ], 206 | ), 207 | ) 208 | : Container(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "31.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.8.0" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.3.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.8.1" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.1" 53 | cli_util: 54 | dependency: transitive 55 | description: 56 | name: cli_util 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.3.5" 60 | clock: 61 | dependency: transitive 62 | description: 63 | name: clock 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.1.0" 67 | collection: 68 | dependency: transitive 69 | description: 70 | name: collection 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.15.0" 74 | convert: 75 | dependency: transitive 76 | description: 77 | name: convert 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "3.0.1" 81 | coverage: 82 | dependency: transitive 83 | description: 84 | name: coverage 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.0.3" 88 | crypto: 89 | dependency: transitive 90 | description: 91 | name: crypto 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "3.0.1" 95 | fake_async: 96 | dependency: transitive 97 | description: 98 | name: fake_async 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.2.0" 102 | file: 103 | dependency: transitive 104 | description: 105 | name: file 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "6.1.2" 109 | flutter: 110 | dependency: "direct main" 111 | description: flutter 112 | source: sdk 113 | version: "0.0.0" 114 | flutter_lints: 115 | dependency: "direct dev" 116 | description: 117 | name: flutter_lints 118 | url: "https://pub.dartlang.org" 119 | source: hosted 120 | version: "1.0.4" 121 | flutter_test: 122 | dependency: "direct dev" 123 | description: flutter 124 | source: sdk 125 | version: "0.0.0" 126 | frontend_server_client: 127 | dependency: transitive 128 | description: 129 | name: frontend_server_client 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "2.1.2" 133 | glob: 134 | dependency: transitive 135 | description: 136 | name: glob 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "2.0.2" 140 | http_multi_server: 141 | dependency: transitive 142 | description: 143 | name: http_multi_server 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "3.2.0" 147 | http_parser: 148 | dependency: transitive 149 | description: 150 | name: http_parser 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "4.0.0" 154 | io: 155 | dependency: transitive 156 | description: 157 | name: io 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "1.0.3" 161 | js: 162 | dependency: transitive 163 | description: 164 | name: js 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "0.6.3" 168 | lints: 169 | dependency: transitive 170 | description: 171 | name: lints 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "1.0.1" 175 | logging: 176 | dependency: transitive 177 | description: 178 | name: logging 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "1.0.2" 182 | matcher: 183 | dependency: transitive 184 | description: 185 | name: matcher 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "0.12.10" 189 | matrix4_transform: 190 | dependency: "direct main" 191 | description: 192 | name: matrix4_transform 193 | url: "https://pub.dartlang.org" 194 | source: hosted 195 | version: "2.0.1" 196 | meta: 197 | dependency: transitive 198 | description: 199 | name: meta 200 | url: "https://pub.dartlang.org" 201 | source: hosted 202 | version: "1.7.0" 203 | mime: 204 | dependency: transitive 205 | description: 206 | name: mime 207 | url: "https://pub.dartlang.org" 208 | source: hosted 209 | version: "1.0.1" 210 | node_preamble: 211 | dependency: transitive 212 | description: 213 | name: node_preamble 214 | url: "https://pub.dartlang.org" 215 | source: hosted 216 | version: "2.0.1" 217 | package_config: 218 | dependency: transitive 219 | description: 220 | name: package_config 221 | url: "https://pub.dartlang.org" 222 | source: hosted 223 | version: "2.0.2" 224 | path: 225 | dependency: transitive 226 | description: 227 | name: path 228 | url: "https://pub.dartlang.org" 229 | source: hosted 230 | version: "1.8.0" 231 | pedantic: 232 | dependency: transitive 233 | description: 234 | name: pedantic 235 | url: "https://pub.dartlang.org" 236 | source: hosted 237 | version: "1.11.1" 238 | pool: 239 | dependency: transitive 240 | description: 241 | name: pool 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "1.5.0" 245 | pub_semver: 246 | dependency: transitive 247 | description: 248 | name: pub_semver 249 | url: "https://pub.dartlang.org" 250 | source: hosted 251 | version: "2.1.1" 252 | screenshot: 253 | dependency: "direct main" 254 | description: 255 | name: screenshot 256 | url: "https://pub.dartlang.org" 257 | source: hosted 258 | version: "1.2.3" 259 | shelf: 260 | dependency: transitive 261 | description: 262 | name: shelf 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "1.2.0" 266 | shelf_packages_handler: 267 | dependency: transitive 268 | description: 269 | name: shelf_packages_handler 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "3.0.0" 273 | shelf_static: 274 | dependency: transitive 275 | description: 276 | name: shelf_static 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "1.1.0" 280 | shelf_web_socket: 281 | dependency: transitive 282 | description: 283 | name: shelf_web_socket 284 | url: "https://pub.dartlang.org" 285 | source: hosted 286 | version: "1.0.1" 287 | sky_engine: 288 | dependency: transitive 289 | description: flutter 290 | source: sdk 291 | version: "0.0.99" 292 | source_map_stack_trace: 293 | dependency: transitive 294 | description: 295 | name: source_map_stack_trace 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "2.1.0" 299 | source_maps: 300 | dependency: transitive 301 | description: 302 | name: source_maps 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "0.10.10" 306 | source_span: 307 | dependency: transitive 308 | description: 309 | name: source_span 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "1.8.1" 313 | stack_trace: 314 | dependency: transitive 315 | description: 316 | name: stack_trace 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "1.10.0" 320 | stream_channel: 321 | dependency: transitive 322 | description: 323 | name: stream_channel 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "2.1.0" 327 | string_scanner: 328 | dependency: transitive 329 | description: 330 | name: string_scanner 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "1.1.0" 334 | term_glyph: 335 | dependency: transitive 336 | description: 337 | name: term_glyph 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "1.2.0" 341 | test: 342 | dependency: "direct dev" 343 | description: 344 | name: test 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "1.17.10" 348 | test_api: 349 | dependency: transitive 350 | description: 351 | name: test_api 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "0.4.2" 355 | test_core: 356 | dependency: transitive 357 | description: 358 | name: test_core 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "0.4.0" 362 | typed_data: 363 | dependency: transitive 364 | description: 365 | name: typed_data 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "1.3.0" 369 | vector_math: 370 | dependency: transitive 371 | description: 372 | name: vector_math 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "2.1.0" 376 | vm_service: 377 | dependency: transitive 378 | description: 379 | name: vm_service 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "7.5.0" 383 | watcher: 384 | dependency: transitive 385 | description: 386 | name: watcher 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "1.0.1" 390 | web_socket_channel: 391 | dependency: transitive 392 | description: 393 | name: web_socket_channel 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "2.1.0" 397 | webkit_inspection_protocol: 398 | dependency: transitive 399 | description: 400 | name: webkit_inspection_protocol 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "1.0.0" 404 | yaml: 405 | dependency: transitive 406 | description: 407 | name: yaml 408 | url: "https://pub.dartlang.org" 409 | source: hosted 410 | version: "3.1.0" 411 | sdks: 412 | dart: ">=2.14.0 <3.0.0" 413 | flutter: ">=1.17.0" 414 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easy_image_editor 2 | description: Easy Image Editor is usefull for add any widget and move, resize and set over the main image or color 3 | repository: https://github.com/rajeshbdabhi/easy_image_editor 4 | homepage: https://github.com/rajeshbdabhi/easy_image_editor 5 | version: 1.0.3 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | flutter: ">=1.17.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | matrix4_transform: ^2.0.1 15 | screenshot: ^1.2.3 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | flutter_lints: ^1.0.0 21 | test: ^1.17.10 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # To add assets to your package, add an assets section, like this: 30 | # assets: 31 | # - images/a_dot_burr.jpeg 32 | # - images/a_dot_ham.jpeg 33 | # 34 | # For details regarding assets in packages, see 35 | # https://flutter.dev/assets-and-images/#from-packages 36 | # 37 | # An image asset can refer to one or more resolution-specific "variants", see 38 | # https://flutter.dev/assets-and-images/#resolution-aware. 39 | 40 | # To add custom fonts to your package, add a fonts section here, 41 | # in this "flutter" section. Each entry in this list should have a 42 | # "family" key with the font family name, and a "fonts" key with a 43 | # list giving the asset and other descriptors for the font. For 44 | # example: 45 | # fonts: 46 | # - family: Schyler 47 | # fonts: 48 | # - asset: fonts/Schyler-Regular.ttf 49 | # - asset: fonts/Schyler-Italic.ttf 50 | # style: italic 51 | # - family: Trajan Pro 52 | # fonts: 53 | # - asset: fonts/TrajanPro.ttf 54 | # - asset: fonts/TrajanPro_Bold.ttf 55 | # weight: 700 56 | # 57 | # For details regarding fonts in packages, see 58 | # https://flutter.dev/custom-fonts/#from-packages 59 | -------------------------------------------------------------------------------- /test/easy_image_editor_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import 'package:easy_image_editor/easy_image_editor.dart'; 4 | 5 | void main() { 6 | test('should editor save image', () { 7 | EditorView( 8 | onInitialize: (controller) { 9 | expect("controller.saveEditing()", null); 10 | }, 11 | ); 12 | }); 13 | } 14 | --------------------------------------------------------------------------------