├── .gitignore
├── .idea
├── .gitignore
├── .name
├── libraries
│ ├── Dart_SDK.xml
│ └── Flutter_Plugins.xml
├── misc.xml
├── modules.xml
├── runConfigurations
│ └── example_lib_main_dart.xml
└── workspace.xml
├── .metadata
├── .packages
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── lijiaqi
│ └── image_editor
│ └── ImageEditorPlugin.java
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── lijiaqi
│ │ │ │ │ └── image_editor_example
│ │ │ │ │ └── MainActivity.java
│ │ │ └── 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
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── image_editor.iml
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── ImageEditorPlugin.h
│ ├── ImageEditorPlugin.m
│ └── SwiftImageEditorPlugin.swift
└── image_editor_dove.podspec
├── lib
├── extension
│ └── num_extension.dart
├── flutter_image_editor.dart
├── image_editor.dart
├── model
│ └── float_text_model.dart
└── widget
│ ├── drawing_board.dart
│ ├── editor_panel_controller.dart
│ ├── float_text_widget.dart
│ ├── image_editor_delegate.dart
│ ├── slider_widget.dart
│ └── text_editor_page.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── image_editor_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | #*.iml
41 | #.idea/workspace.xml
42 | #.idea/tasks.xml
43 | #.idea/gradle.xml
44 | #.idea/assetWizardSettings.xml
45 | #.idea/dictionaries
46 | #.idea/libraries
47 | ## Android Studio 3 in .gitignore file.
48 | #.idea/caches
49 | #.idea/modules.xml
50 | ## Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | #.idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/.idea/.gitignore
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | flutter_image_editor
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example_lib_main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 | 1642388073606
28 |
29 |
30 | 1642388073606
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.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: d79295af24c3ed621c33713ecda14ad196fd9c31
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/.packages:
--------------------------------------------------------------------------------
1 | # This file is deprecated. Tools should instead consume
2 | # `.dart_tool/package_config.json`.
3 | #
4 | # For more info see: https://dart.dev/go/dot-packages-deprecation
5 | #
6 | # Generated by pub on 2022-02-03 10:24:03.727670.
7 | archive:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/archive-3.1.11/lib/
8 | async:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/async-2.8.1/lib/
9 | boolean_selector:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/boolean_selector-2.1.0/lib/
10 | characters:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/
11 | charcode:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/charcode-1.3.1/lib/
12 | clock:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/clock-1.1.0/lib/
13 | collection:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/
14 | crypto:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/crypto-3.0.1/lib/
15 | fake_async:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/fake_async-1.2.0/lib/
16 | ffi:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/ffi-1.1.2/lib/
17 | file:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/file-6.1.2/lib/
18 | flutter:file:///I:/flutter/flutter/packages/flutter/lib/
19 | flutter_test:file:///I:/flutter/flutter/packages/flutter_test/lib/
20 | image:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/image-3.1.1/lib/
21 | matcher:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/matcher-0.12.10/lib/
22 | meta:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/meta-1.7.0/lib/
23 | path:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path-1.8.0/lib/
24 | path_provider:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider-2.0.8/lib/
25 | path_provider_android:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_android-2.0.11/lib/
26 | path_provider_ios:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_ios-2.0.7/lib/
27 | path_provider_linux:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_linux-2.1.5/lib/
28 | path_provider_macos:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_macos-2.0.5/lib/
29 | path_provider_platform_interface:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_platform_interface-2.0.3/lib/
30 | path_provider_windows:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/path_provider_windows-2.0.5/lib/
31 | petitparser:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/petitparser-4.4.0/lib/
32 | platform:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/platform-3.1.0/lib/
33 | plugin_platform_interface:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/plugin_platform_interface-2.1.2/lib/
34 | process:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/process-4.2.4/lib/
35 | screenshot:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/screenshot-1.2.3/lib/
36 | sky_engine:file:///I:/flutter/flutter/bin/cache/pkg/sky_engine/lib/
37 | source_span:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/source_span-1.8.1/lib/
38 | stack_trace:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/stack_trace-1.10.0/lib/
39 | stream_channel:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/stream_channel-2.1.0/lib/
40 | string_scanner:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/string_scanner-1.1.0/lib/
41 | term_glyph:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/term_glyph-1.2.0/lib/
42 | test_api:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/test_api-0.4.2/lib/
43 | typed_data:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/typed_data-1.3.0/lib/
44 | vector_math:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/vector_math-2.1.0/lib/
45 | win32:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/win32-2.3.6/lib/
46 | xdg_directories:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/xdg_directories-0.2.0/lib/
47 | xml:file:///H:/Users/45010/AppData/Roaming/Pub/Cache/hosted/pub.flutter-io.cn/xml-5.3.1/lib/
48 | image_editor_dove:lib/
49 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## 0.0.3
3 | feat : add interact for image and allow cancel operation
4 | ## 0.0.2
5 | fix: bugs
6 | ## 0.0.1
7 |
8 | * TODO: Describe initial release.
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 bladeofgod
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # image_editor_dove
2 |
3 | A high-performance image editor with crop, scribble, mosaic, add-text, flip, rotated functions.
4 |
5 | Support custom ui style.
6 |
7 | - drawing
8 |
9 | 
10 |
11 |
12 | - rotate
13 |
14 |
15 | 
16 |
17 |
18 | - mosaic
19 |
20 |
21 | 
22 |
23 | - add or delete text
24 |
25 |
26 | 
27 |
28 |
29 | 
30 |
31 |
32 | ## Getting Started
33 |
34 | First, add image_editor: as a
35 | [dependency in your pubspec.yaml file.](https://flutter.cn/development/packages-and-plugins/using-packages)
36 |
37 | import
38 | ```
39 | import 'package:image_editor/flutter_image_editor.dart';
40 | ```
41 |
42 | iOS
43 | Add the following keys to your Info.plist file, located in /ios/Runner/Info.plist:
44 |
45 | - NSPhotoLibraryUsageDescription - describe why your app needs permission for the photo library. This is called Privacy - Photo Library Usage Description in the visual editor.
46 | - NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.
47 | - NSMicrophoneUsageDescription - describe why your app needs access to the microphone, if you intend to record videos. This is called Privacy - Microphone Usage Description in the visual editor.
48 | Or in text format add the key:
49 | ```
50 | NSPhotoLibraryUsageDescription
51 | Used to demonstrate image picker plugin
52 | NSCameraUsageDescription
53 | Used to demonstrate image picker plugin
54 | NSMicrophoneUsageDescription
55 | Used to capture audio for image picker plugin
56 | ```
57 |
58 | Android
59 |
60 | No configuration required - the plugin should work out of the box.
61 |
62 | ## Usage
63 |
64 | pick an image that wanna edit, and pass it to image editor like this:
65 |
66 | ```
67 | Future toImageEditor(File origin) async {
68 | return Navigator.push(context, MaterialPageRoute(builder: (context) {
69 | return ImageEditor(
70 | originImage: origin,
71 | //this is nullable, you can custom new file's save postion
72 | savePath: customDirectory
73 | );
74 | })).then((result) {
75 | if (result is EditorImageResult) {
76 | setState(() {
77 | _image = result.newFile;
78 | });
79 | }
80 | }).catchError((er) {
81 | debugPrint(er);
82 | });
83 | }
84 | ```
85 |
86 | ## custom ui style
87 |
88 | You can extends ImageEditorDelegate and custom editor's widget:
89 |
90 | ```
91 | ImageEditor.uiDelegate = YouUiDelegate();
92 |
93 | class YouUiDelegate extends ImageEditorDelegate{
94 | ...
95 | }
96 | ```
97 |
98 |
99 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.lijiaqi.image_editor'
2 | version '1.0'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.1.0'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 |
24 | android {
25 | compileSdkVersion 30
26 |
27 | defaultConfig {
28 | minSdkVersion 16
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'image_editor'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/lijiaqi/image_editor/ImageEditorPlugin.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.image_editor;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import io.flutter.embedding.engine.plugins.FlutterPlugin;
6 | import io.flutter.plugin.common.MethodCall;
7 | import io.flutter.plugin.common.MethodChannel;
8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
9 | import io.flutter.plugin.common.MethodChannel.Result;
10 |
11 | /** ImageEditorPlugin */
12 | public class ImageEditorPlugin implements FlutterPlugin, MethodCallHandler {
13 | /// The MethodChannel that will the communication between Flutter and native Android
14 | ///
15 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
16 | /// when the Flutter Engine is detached from the Activity
17 | //private MethodChannel channel;
18 |
19 | @Override
20 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
21 | // channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "image_editor");
22 | // channel.setMethodCallHandler(this);
23 | }
24 |
25 | @Override
26 | public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
27 | // if (call.method.equals("getPlatformVersion")) {
28 | // result.success("Android " + android.os.Build.VERSION.RELEASE);
29 | // } else {
30 | // result.notImplemented();
31 | // }
32 | }
33 |
34 | @Override
35 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
36 | //channel.setMethodCallHandler(null);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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: d79295af24c3ed621c33713ecda14ad196fd9c31
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # image_editor_example
2 |
3 | Demonstrates how to use the image_editor plugin.
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/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 |
--------------------------------------------------------------------------------
/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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 31
29 |
30 | defaultConfig {
31 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
32 | applicationId "com.lijiaqi.image_editor_example"
33 | minSdkVersion 16
34 | targetSdkVersion 31
35 | versionCode flutterVersionCode.toInteger()
36 | versionName flutterVersionName
37 | }
38 |
39 | buildTypes {
40 | release {
41 | // TODO: Add your own signing config for the release build.
42 | // Signing with the debug keys for now, so `flutter run --release` works.
43 | signingConfig signingConfigs.debug
44 | }
45 | }
46 | }
47 |
48 | flutter {
49 | source '../..'
50 | }
51 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
14 |
18 |
22 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/lijiaqi/image_editor_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.image_editor_example;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends 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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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 | repositories {
3 | maven { url 'https://maven.aliyun.com/repository/public' }
4 | maven { url 'https://maven.aliyun.com/repository/google' }
5 | maven { url 'https://maven.aliyun.com/repository/jcenter' }
6 | maven {url 'http://download.flutter.io'}
7 | google()
8 | jcenter()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:4.1.0'
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | maven { url 'https://maven.aliyun.com/repository/public' }
19 | maven { url 'https://maven.aliyun.com/repository/google' }
20 | maven { url 'https://maven.aliyun.com/repository/jcenter' }
21 | maven {url 'http://download.flutter.io'}
22 | google()
23 | jcenter()
24 | }
25 | }
26 |
27 | rootProject.buildDir = '../build'
28 | subprojects {
29 | project.buildDir = "${rootProject.buildDir}/${project.name}"
30 | project.evaluationDependsOn(':app')
31 | }
32 |
33 | task clean(type: Delete) {
34 | delete rootProject.buildDir
35 | }
36 |
--------------------------------------------------------------------------------
/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 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/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 | 8.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.lijiaqi.imageEditorExample;
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.lijiaqi.imageEditorExample;
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.lijiaqi.imageEditorExample;
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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/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 | image_editor_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 'dart:io';
2 | import 'package:flutter/material.dart';
3 | import 'package:image_editor_dove/image_editor.dart';
4 | import 'dart:async';
5 |
6 | import 'package:image_picker/image_picker.dart';
7 |
8 |
9 | void main() {
10 | runApp(MyApp());
11 | }
12 | class MyApp extends StatefulWidget {
13 | @override
14 | _MyAppState createState() => _MyAppState();
15 | }
16 |
17 | class _MyAppState extends State {
18 |
19 | @override
20 | void initState() {
21 | super.initState();
22 | }
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return MaterialApp(
27 | home: Scaffold(
28 | appBar: AppBar(
29 | title: const Text('Plugin example app'),
30 | ),
31 | body: HomePage(),
32 | ),
33 | );
34 | }
35 | }
36 |
37 | class HomePage extends StatefulWidget {
38 | @override
39 | _HomePageState createState() => _HomePageState();
40 | }
41 |
42 | class _HomePageState extends State {
43 |
44 | File? _image;
45 |
46 | final picker = ImagePicker();
47 |
48 | Future toImageEditor(File origin) async {
49 | return Navigator.push(context, MaterialPageRoute(builder: (context) {
50 | return ImageEditor(
51 | originImage: origin,
52 | );
53 | })).then((result) {
54 | if (result is EditorImageResult) {
55 | setState(() {
56 | _image = result.newFile;
57 | });
58 | }
59 | }).catchError((er) {
60 | debugPrint(er);
61 | });
62 | }
63 |
64 | void getImage() async {
65 | PickedFile? image =
66 | await picker.getImage(source: ImageSource.gallery);
67 | if(image != null) {
68 | final File origin = File(image.path);
69 | toImageEditor(origin);
70 | }
71 | }
72 |
73 | @override
74 | Widget build(BuildContext context) {
75 | return Material(
76 | color: Colors.white,
77 | child: Container(
78 | width: double.infinity, height: double.infinity,
79 | child: Column(
80 | children: [
81 | if(_image != null)
82 | Expanded(child: Image.file(_image!)),
83 | ElevatedButton(
84 | onPressed: getImage,
85 | child: Text('edit image'),
86 | ),
87 | ],
88 | ),
89 | ),
90 | );
91 | }
92 | }
93 |
94 | Widget condition({required bool condtion, required Widget isTrue, required Widget isFalse}) {
95 | return condtion ? isTrue : isFalse;
96 | }
97 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "3.4.6"
12 | async:
13 | dependency: transitive
14 | description:
15 | name: async
16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.11.0"
20 | boolean_selector:
21 | dependency: transitive
22 | description:
23 | name: boolean_selector
24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "2.1.1"
28 | characters:
29 | dependency: transitive
30 | description:
31 | name: characters
32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.3.0"
36 | clock:
37 | dependency: transitive
38 | description:
39 | name: clock
40 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.1.1"
44 | collection:
45 | dependency: transitive
46 | description:
47 | name: collection
48 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.17.1"
52 | convert:
53 | dependency: transitive
54 | description:
55 | name: convert
56 | sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "3.1.1"
60 | cross_file:
61 | dependency: transitive
62 | description:
63 | name: cross_file
64 | sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "0.3.3+6"
68 | crypto:
69 | dependency: transitive
70 | description:
71 | name: crypto
72 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "3.0.3"
76 | cupertino_icons:
77 | dependency: "direct main"
78 | description:
79 | name: cupertino_icons
80 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "1.0.6"
84 | fake_async:
85 | dependency: transitive
86 | description:
87 | name: fake_async
88 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
89 | url: "https://pub.dev"
90 | source: hosted
91 | version: "1.3.1"
92 | ffi:
93 | dependency: transitive
94 | description:
95 | name: ffi
96 | sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
97 | url: "https://pub.dev"
98 | source: hosted
99 | version: "2.1.0"
100 | flutter:
101 | dependency: "direct main"
102 | description: flutter
103 | source: sdk
104 | version: "0.0.0"
105 | flutter_plugin_android_lifecycle:
106 | dependency: transitive
107 | description:
108 | name: flutter_plugin_android_lifecycle
109 | sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
110 | url: "https://pub.dev"
111 | source: hosted
112 | version: "2.0.17"
113 | flutter_test:
114 | dependency: "direct dev"
115 | description: flutter
116 | source: sdk
117 | version: "0.0.0"
118 | flutter_web_plugins:
119 | dependency: transitive
120 | description: flutter
121 | source: sdk
122 | version: "0.0.0"
123 | http:
124 | dependency: transitive
125 | description:
126 | name: http
127 | sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
128 | url: "https://pub.dev"
129 | source: hosted
130 | version: "1.1.0"
131 | http_parser:
132 | dependency: transitive
133 | description:
134 | name: http_parser
135 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
136 | url: "https://pub.dev"
137 | source: hosted
138 | version: "4.0.2"
139 | image:
140 | dependency: transitive
141 | description:
142 | name: image
143 | sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "3.3.0"
147 | image_editor_dove:
148 | dependency: "direct main"
149 | description:
150 | path: ".."
151 | relative: true
152 | source: path
153 | version: "0.0.2"
154 | image_picker:
155 | dependency: "direct main"
156 | description:
157 | name: image_picker
158 | sha256: b4f02353277b39f350093a2460b32b43eafaa3bfc4f92e8d90926698d8d78df6
159 | url: "https://pub.dev"
160 | source: hosted
161 | version: "0.7.5+4"
162 | image_picker_for_web:
163 | dependency: transitive
164 | description:
165 | name: image_picker_for_web
166 | sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0"
167 | url: "https://pub.dev"
168 | source: hosted
169 | version: "2.2.0"
170 | image_picker_platform_interface:
171 | dependency: transitive
172 | description:
173 | name: image_picker_platform_interface
174 | sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514
175 | url: "https://pub.dev"
176 | source: hosted
177 | version: "2.9.1"
178 | js:
179 | dependency: transitive
180 | description:
181 | name: js
182 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
183 | url: "https://pub.dev"
184 | source: hosted
185 | version: "0.6.7"
186 | matcher:
187 | dependency: transitive
188 | description:
189 | name: matcher
190 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
191 | url: "https://pub.dev"
192 | source: hosted
193 | version: "0.12.15"
194 | material_color_utilities:
195 | dependency: transitive
196 | description:
197 | name: material_color_utilities
198 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
199 | url: "https://pub.dev"
200 | source: hosted
201 | version: "0.2.0"
202 | meta:
203 | dependency: transitive
204 | description:
205 | name: meta
206 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
207 | url: "https://pub.dev"
208 | source: hosted
209 | version: "1.9.1"
210 | mime:
211 | dependency: transitive
212 | description:
213 | name: mime
214 | sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
215 | url: "https://pub.dev"
216 | source: hosted
217 | version: "1.0.4"
218 | path:
219 | dependency: transitive
220 | description:
221 | name: path
222 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
223 | url: "https://pub.dev"
224 | source: hosted
225 | version: "1.8.3"
226 | path_provider:
227 | dependency: transitive
228 | description:
229 | name: path_provider
230 | sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
231 | url: "https://pub.dev"
232 | source: hosted
233 | version: "2.1.1"
234 | path_provider_android:
235 | dependency: transitive
236 | description:
237 | name: path_provider_android
238 | sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
239 | url: "https://pub.dev"
240 | source: hosted
241 | version: "2.2.1"
242 | path_provider_foundation:
243 | dependency: transitive
244 | description:
245 | name: path_provider_foundation
246 | sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
247 | url: "https://pub.dev"
248 | source: hosted
249 | version: "2.3.1"
250 | path_provider_linux:
251 | dependency: transitive
252 | description:
253 | name: path_provider_linux
254 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
255 | url: "https://pub.dev"
256 | source: hosted
257 | version: "2.2.1"
258 | path_provider_platform_interface:
259 | dependency: transitive
260 | description:
261 | name: path_provider_platform_interface
262 | sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
263 | url: "https://pub.dev"
264 | source: hosted
265 | version: "2.1.1"
266 | path_provider_windows:
267 | dependency: transitive
268 | description:
269 | name: path_provider_windows
270 | sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
271 | url: "https://pub.dev"
272 | source: hosted
273 | version: "2.2.1"
274 | petitparser:
275 | dependency: transitive
276 | description:
277 | name: petitparser
278 | sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
279 | url: "https://pub.dev"
280 | source: hosted
281 | version: "5.4.0"
282 | platform:
283 | dependency: transitive
284 | description:
285 | name: platform
286 | sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
287 | url: "https://pub.dev"
288 | source: hosted
289 | version: "3.1.3"
290 | plugin_platform_interface:
291 | dependency: transitive
292 | description:
293 | name: plugin_platform_interface
294 | sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
295 | url: "https://pub.dev"
296 | source: hosted
297 | version: "2.1.6"
298 | pointycastle:
299 | dependency: transitive
300 | description:
301 | name: pointycastle
302 | sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
303 | url: "https://pub.dev"
304 | source: hosted
305 | version: "3.7.3"
306 | screenshot:
307 | dependency: transitive
308 | description:
309 | name: screenshot
310 | sha256: "455284ff1f5b911d94a43c25e1385485cf6b4f288293eba68f15dad711c7b81c"
311 | url: "https://pub.dev"
312 | source: hosted
313 | version: "2.1.0"
314 | sky_engine:
315 | dependency: transitive
316 | description: flutter
317 | source: sdk
318 | version: "0.0.99"
319 | source_span:
320 | dependency: transitive
321 | description:
322 | name: source_span
323 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
324 | url: "https://pub.dev"
325 | source: hosted
326 | version: "1.9.1"
327 | stack_trace:
328 | dependency: transitive
329 | description:
330 | name: stack_trace
331 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
332 | url: "https://pub.dev"
333 | source: hosted
334 | version: "1.11.0"
335 | stream_channel:
336 | dependency: transitive
337 | description:
338 | name: stream_channel
339 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
340 | url: "https://pub.dev"
341 | source: hosted
342 | version: "2.1.1"
343 | string_scanner:
344 | dependency: transitive
345 | description:
346 | name: string_scanner
347 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
348 | url: "https://pub.dev"
349 | source: hosted
350 | version: "1.2.0"
351 | term_glyph:
352 | dependency: transitive
353 | description:
354 | name: term_glyph
355 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
356 | url: "https://pub.dev"
357 | source: hosted
358 | version: "1.2.1"
359 | test_api:
360 | dependency: transitive
361 | description:
362 | name: test_api
363 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
364 | url: "https://pub.dev"
365 | source: hosted
366 | version: "0.5.1"
367 | typed_data:
368 | dependency: transitive
369 | description:
370 | name: typed_data
371 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
372 | url: "https://pub.dev"
373 | source: hosted
374 | version: "1.3.2"
375 | vector_math:
376 | dependency: transitive
377 | description:
378 | name: vector_math
379 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
380 | url: "https://pub.dev"
381 | source: hosted
382 | version: "2.1.4"
383 | win32:
384 | dependency: transitive
385 | description:
386 | name: win32
387 | sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
388 | url: "https://pub.dev"
389 | source: hosted
390 | version: "5.0.9"
391 | xdg_directories:
392 | dependency: transitive
393 | description:
394 | name: xdg_directories
395 | sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
396 | url: "https://pub.dev"
397 | source: hosted
398 | version: "1.0.3"
399 | xml:
400 | dependency: transitive
401 | description:
402 | name: xml
403 | sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
404 | url: "https://pub.dev"
405 | source: hosted
406 | version: "6.3.0"
407 | sdks:
408 | dart: ">=3.0.0 <4.0.0"
409 | flutter: ">=3.10.0"
410 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: image_editor_dove_example
2 | description: Demonstrates how to use the image_editor plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `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 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | image_editor_dove:
16 | # When depending on this package from a real application you should use:
17 | # image_editor: ^x.y.z
18 | # See https://dart.dev/tools/pub/dependencies#version-constraints
19 | # The example app is bundled with the plugin so we use a path dependency on
20 | # the parent directory to use the current plugin's version.
21 | path: ../
22 |
23 | # The following adds the Cupertino Icons font to your application.
24 | # Use with the CupertinoIcons class for iOS style icons.
25 | cupertino_icons: ^1.0.2
26 |
27 | image_picker: ^0.7.4
28 |
29 |
30 | dev_dependencies:
31 | flutter_test:
32 | sdk: flutter
33 |
34 | # For information on the generic Dart part of this file, see the
35 | # following page: https://dart.dev/tools/pub/pubspec
36 |
37 | # The following section is specific to Flutter.
38 | flutter:
39 |
40 | # The following line ensures that the Material Icons font is
41 | # included with your application, so that you can use the icons in
42 | # the material Icons class.
43 | uses-material-design: true
44 |
45 | # To add assets to your application, add an assets section, like this:
46 | # assets:
47 | # - images/a_dot_burr.jpeg
48 | # - images/a_dot_ham.jpeg
49 |
50 | # An image asset can refer to one or more resolution-specific "variants", see
51 | # https://flutter.dev/assets-and-images/#resolution-aware.
52 |
53 | # For details regarding adding assets from package dependencies, see
54 | # https://flutter.dev/assets-and-images/#from-packages
55 |
56 | # To add custom fonts to your application, add a fonts section here,
57 | # in this "flutter" section. Each entry in this list should have a
58 | # "family" key with the font family name, and a "fonts" key with a
59 | # list giving the asset and other descriptors for the font. For
60 | # example:
61 | # fonts:
62 | # - family: Schyler
63 | # fonts:
64 | # - asset: fonts/Schyler-Regular.ttf
65 | # - asset: fonts/Schyler-Italic.ttf
66 | # style: italic
67 | # - family: Trajan Pro
68 | # fonts:
69 | # - asset: fonts/TrajanPro.ttf
70 | # - asset: fonts/TrajanPro_Bold.ttf
71 | # weight: 700
72 | #
73 | # For details regarding fonts from package dependencies,
74 | # see https://flutter.dev/custom-fonts/#from-packages
75 |
--------------------------------------------------------------------------------
/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 | import 'package:image_editor_dove_example/main.dart';
11 |
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) => widget is Text &&
22 | widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/image_editor.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/ephemeral/
38 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/flutter_image_editor/9f0aeb5d025a60650e5daf2dd36329fc062e2b82/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/ImageEditorPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface ImageEditorPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/ImageEditorPlugin.m:
--------------------------------------------------------------------------------
1 | #import "ImageEditorPlugin.h"
2 | #if __has_include()
3 | #import
4 | #else
5 | // Support project import fallback if the generated compatibility header
6 | // is not copied when this plugin is created as a library.
7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
8 | #import "image_editor_dove-Swift.h"
9 | #endif
10 |
11 | @implementation ImageEditorPlugin
12 | + (void)registerWithRegistrar:(NSObject*)registrar {
13 | [SwiftImageEditorPlugin registerWithRegistrar:registrar];
14 | }
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/Classes/SwiftImageEditorPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | public class SwiftImageEditorPlugin: NSObject, FlutterPlugin {
5 | public static func register(with registrar: FlutterPluginRegistrar) {
6 | let channel = FlutterMethodChannel(name: "image_editor", binaryMessenger: registrar.messenger())
7 | let instance = SwiftImageEditorPlugin()
8 | registrar.addMethodCallDelegate(instance, channel: channel)
9 | }
10 |
11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 | result("iOS " + UIDevice.current.systemVersion)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ios/image_editor_dove.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint image_editor.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'image_editor_dove'
7 | s.version = '0.0.1'
8 | s.summary = 'An image editor with crop, scribble, mosaic, add-text, flip, rotated functions.'
9 | s.description = <<-DESC
10 | An image editor with crop, scribble, mosaic, add-text, flip, rotated functions.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '8.0'
19 |
20 | # Flutter.framework does not contain a i386 slice.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/lib/extension/num_extension.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/cupertino.dart';
4 |
5 | extension EditorNum on num{
6 |
7 | Widget get vGap => SizedBox(height: this.toDouble());
8 |
9 | Widget get hGap => SizedBox(width: this.toDouble());
10 |
11 | }
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/lib/flutter_image_editor.dart:
--------------------------------------------------------------------------------
1 |
2 | export 'image_editor.dart';
3 | export 'widget/image_editor_delegate.dart';
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/image_editor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:math' as math;
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | import 'dart:ui' as ui;
7 |
8 | import 'extension/num_extension.dart';
9 | import 'package:path_provider/path_provider.dart';
10 | import 'package:screenshot/screenshot.dart';
11 |
12 | import 'model/float_text_model.dart';
13 | import 'widget/drawing_board.dart';
14 | import 'widget/editor_panel_controller.dart';
15 | import 'widget/float_text_widget.dart';
16 | import 'widget/image_editor_delegate.dart';
17 | import 'widget/text_editor_page.dart';
18 |
19 | const CanvasLauncher _defaultLauncher = CanvasLauncher(mosaicWidth: 5.0, pStrockWidth: 5.0, pColor: Colors.red);
20 |
21 | ///The editor's result.
22 | class EditorImageResult {
23 | ///image width
24 | final int imgWidth;
25 |
26 | ///image height
27 | final int imgHeight;
28 |
29 | ///new file after edit
30 | final File newFile;
31 |
32 | EditorImageResult(this.imgWidth, this.imgHeight, this.newFile);
33 | }
34 |
35 |
36 | ///The launcher provides some initial-values for canvas;
37 | class CanvasLauncher{
38 |
39 | factory CanvasLauncher.auto() {
40 | return const CanvasLauncher(mosaicWidth: 5.0, pStrockWidth: 5.0, pColor: Colors.red);
41 | }
42 |
43 | const CanvasLauncher({required this.mosaicWidth, required this.pStrockWidth, required this.pColor});
44 |
45 | ///mosaic pixel's width
46 | final double mosaicWidth;
47 |
48 | ///painter stroke width.
49 | final double pStrockWidth;
50 |
51 | ///painter color
52 | final Color pColor;
53 |
54 | }
55 |
56 | class ImageEditor extends StatefulWidget {
57 |
58 | const ImageEditor({Key? key,
59 | required this.originImage,
60 | this.savePath,
61 | this.launcher = _defaultLauncher})
62 | : super(key: key);
63 |
64 | ///origin image
65 | /// * input for edit
66 | final File originImage;
67 |
68 | ///edited-file's save path.
69 | /// * if null will save in temporary file.
70 | final Directory? savePath;
71 |
72 | ///provide some initial value
73 | final CanvasLauncher launcher;
74 |
75 | ///[uiDelegate] is determine the editor's ui style.
76 | ///You can extends [ImageEditorDelegate] and custome it by youself.
77 | static ImageEditorDelegate uiDelegate = DefaultImageEditorDelegate();
78 |
79 | @override
80 | State createState() {
81 | return ImageEditorState();
82 | }
83 | }
84 |
85 | class ImageEditorState extends State
86 | with SignatureBinding, ScreenShotBinding, TextCanvasBinding, RotateCanvasBinding, LittleWidgetBinding, WindowUiBinding {
87 | final EditorPanelController _panelController = EditorPanelController();
88 |
89 | double get headerHeight => windowStatusBarHeight;
90 |
91 | double get bottomBarHeight => 105 + windowBottomBarHeight;
92 |
93 | ///Edit area height.
94 | double get canvasHeight => screenHeight - bottomBarHeight - headerHeight;
95 |
96 | ///Operation panel button's horizontal space.
97 | Widget get controlBtnSpacing => 5.hGap;
98 |
99 | ///Save the edited-image to [widget.savePath] or [getTemporaryDirectory()].
100 | void saveImage() {
101 | _panelController.takeShot.value = true;
102 | screenshotController.capture(pixelRatio: 1.0).then((value) async {
103 | final paths = widget.savePath ?? await getTemporaryDirectory();
104 | final file = await File('${paths.path}/' + DateTime.now().toString() + '.jpg').create();
105 | file.writeAsBytes(value ?? []);
106 | decodeImg().then((value) {
107 | if (value == null) {
108 | Navigator.pop(context);
109 | } else {
110 | Navigator.pop(context, EditorImageResult(value.width, value.height, file));
111 | }
112 | }).catchError((e) {
113 | Navigator.pop(context);
114 | });
115 | });
116 | }
117 |
118 | Future decodeImg() async {
119 | return await decodeImageFromList(widget.originImage.readAsBytesSync());
120 | }
121 |
122 | static ImageEditorState? of(BuildContext context) {
123 | return context.findAncestorStateOfType();
124 | }
125 |
126 | @override
127 | void initState() {
128 | super.initState();
129 | initPainter();
130 | }
131 |
132 | @override
133 | Widget build(BuildContext context) {
134 | _panelController.screenSize ??= windowSize;
135 |
136 | final appBar = ValueListenableBuilder(
137 | valueListenable: _panelController.showAppBar,
138 | builder: (ctx, value, child) {
139 | return AnimatedPositioned(
140 | top: value ? 0 : -headerHeight,
141 | left: 0, right: 0,
142 | child: ValueListenableBuilder(
143 | valueListenable: _panelController.takeShot,
144 | builder: (ctx, value, child) {
145 | return Opacity(
146 | opacity: value ? 0 : 1,
147 | child: AppBar(
148 | iconTheme: IconThemeData(color: Colors.white, size: 16),
149 | leading: backWidget(),
150 | backgroundColor: Colors.transparent,
151 | actions: [
152 | resetWidget(onTap: resetCanvasPlate)
153 | ],
154 | ),
155 | );
156 | }),
157 | duration: _panelController.panelDuration);
158 | });
159 |
160 | final paintCanvas = Positioned.fromRect(
161 | rect: Rect.fromLTWH(0, headerHeight, screenWidth, canvasHeight),
162 | child: RotatedBox(
163 | quarterTurns: rotateValue,
164 | child: Stack(
165 | alignment: Alignment.center,
166 | children: [
167 | InteractiveViewer(child: _buildImage()),
168 | _buildBrushCanvas(),
169 | //buildTextCanvas(),
170 | ],
171 | ),
172 | ));
173 |
174 | final textCanvas = Positioned.fromRect(
175 | rect: Rect.fromLTWH(0, headerHeight, screenWidth, screenHeight),
176 | child: RotatedBox(
177 | quarterTurns: rotateValue,
178 | child: buildTextCanvas(),
179 | ));
180 |
181 | //bottom operation(control) bar
182 | final bottomOpBar = ValueListenableBuilder(
183 | valueListenable: _panelController.showBottomBar,
184 | builder: (ctx, value, child) {
185 | return AnimatedPositioned(
186 | bottom: value ? 0 : -bottomBarHeight,
187 | child: SizedBox(
188 | width: screenWidth,
189 | child: ValueListenableBuilder(
190 | valueListenable: _panelController.takeShot,
191 | builder: (ctx, value, child) {
192 | return Opacity(
193 | opacity: value ? 0 : 1,
194 | child: _buildControlBar(),
195 | );
196 | }),
197 | ),
198 | duration: _panelController.panelDuration);
199 | });
200 |
201 | //trash bin
202 | final trashBin = ValueListenableBuilder(
203 | valueListenable: _panelController.showTrashCan,
204 | builder: (ctx, value, child) {
205 | return AnimatedPositioned(
206 | bottom: value ? _panelController.trashCanPosition.dy : -headerHeight,
207 | left: _panelController.trashCanPosition.dx,
208 | child: _buildTrashCan(),
209 | duration: _panelController.panelDuration);
210 | });
211 |
212 | return Material(
213 | color: Colors.black,
214 | child: Listener(
215 | onPointerMove: (v) {
216 | _panelController.pointerMoving(v);
217 | },
218 | child: Screenshot(
219 | controller: screenshotController,
220 | child: Stack(
221 | children: [
222 | appBar,
223 | //canvas
224 | paintCanvas,
225 | bottomOpBar,
226 | trashBin,
227 | textCanvas,
228 | ],
229 | ),
230 | ),
231 | ),
232 | );
233 | }
234 |
235 | Widget _buildControlBar() {
236 | return Container(
237 | color: Colors.black,
238 | width: screenWidth,
239 | height: bottomBarHeight,
240 | padding: EdgeInsets.only(left: 16, right: 16, bottom: windowBottomBarHeight),
241 | child: Column(
242 | children: [
243 | Expanded(
244 | child: ValueListenableBuilder(
245 | valueListenable: _panelController.operateType,
246 | builder: (ctx, value, child) {
247 | return Opacity(
248 | opacity: _panelController.show2ndPanel() ? 1 : 0,
249 | child: Row(
250 | mainAxisAlignment: value == OperateType.brush ?
251 | MainAxisAlignment.spaceAround : MainAxisAlignment.end,
252 | children: [
253 | if(value == OperateType.brush)
254 | ..._panelController.brushColor
255 | .map((e) => CircleColorWidget(
256 | color: e,
257 | valueListenable: _panelController.colorSelected,
258 | onColorSelected: (color) {
259 | if(pColor.value == color.value) return;
260 | changePainterColor(color);
261 | },
262 | ))
263 | .toList(),
264 | 35.hGap,
265 | unDoWidget(onPressed: undo),
266 | if(value == OperateType.mosaic)
267 | 7.hGap,
268 | ],
269 | ),
270 | );
271 | },
272 | ),
273 | ),
274 | Expanded(
275 | child: Row(
276 | children: [
277 | _buildButton(OperateType.brush, 'Draw', onPressed: () {
278 | switchPainterMode(DrawStyle.normal);
279 | }),
280 | controlBtnSpacing,
281 | _buildButton(OperateType.text, 'Text', onPressed: toTextEditorPage),
282 | controlBtnSpacing,
283 | _buildButton(OperateType.flip, 'Flip', onPressed: flipCanvas),
284 | controlBtnSpacing,
285 | _buildButton(OperateType.rotated, 'Rotate', onPressed: rotateCanvasPlate),
286 | controlBtnSpacing,
287 | _buildButton(OperateType.mosaic, 'Mosaic', onPressed: () {
288 | switchPainterMode(DrawStyle.mosaic);
289 | }),
290 | Expanded(child: SizedBox()),
291 | doneButtonWidget(onPressed: saveImage),
292 | ],
293 | ),
294 | )
295 | ],
296 | ),
297 | );
298 | }
299 |
300 | Widget _buildImage() {
301 | return Transform(
302 | alignment: Alignment.center,
303 | transform: Matrix4.rotationY(flipValue),
304 | child: Container(
305 | alignment: Alignment.center,
306 | child: Image.file(widget.originImage),
307 | ),
308 | );
309 | }
310 |
311 | Widget _buildTrashCan() {
312 | return ValueListenableBuilder(
313 | valueListenable: _panelController.trashColor,
314 | builder: (ctx, value, child) {
315 | final bool isActive = value.value == EditorPanelController.defaultTrashColor.value;
316 | return Container(
317 | width: _panelController.tcSize.width,
318 | height: _panelController.tcSize.height,
319 | decoration: BoxDecoration(
320 | color: value,
321 | borderRadius: BorderRadius.all(Radius.circular(8))
322 | ),
323 | child: Column(
324 | children: [
325 | 12.vGap,
326 | Icon(Icons.delete_outline, size: 32, color: Colors.white,),
327 | 4.vGap,
328 | Text(isActive ? 'move here for delete' : 'release to delete',
329 | style: TextStyle(color: Colors.white, fontSize: 12),)
330 | ],
331 | ),
332 | );
333 | });
334 | }
335 |
336 | Widget _buildButton(OperateType type, String txt, {VoidCallback? onPressed}) {
337 | return GestureDetector(
338 | onTap: () {
339 | if(type == _panelController.currentOperateType) {
340 | _panelController.cancelOperateType();
341 | } else {
342 | _panelController.switchOperateType(type);
343 | onPressed?.call();
344 | }
345 | },
346 | child: ValueListenableBuilder(
347 | valueListenable: _panelController.operateType,
348 | builder: (ctx, value, child) {
349 | return SizedBox(
350 | width: 44,
351 | height: 41,
352 | child: Column(
353 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
354 | children: [
355 | getOperateTypeRes(type, choosen: _panelController.isCurrentOperateType(type)),
356 | Text(
357 | txt,
358 | style: TextStyle(
359 | color: _panelController.isCurrentOperateType(type)
360 | ? const Color(0xFFFA4D32) : const Color(0xFF999999), fontSize: 11),
361 | )
362 | ],
363 | ),
364 | );
365 | },
366 | ),
367 | );
368 | }
369 |
370 |
371 | }
372 |
373 | ///Little widget binding is for unified manage the widgets that has common style.
374 | /// * If you wanna custom this part, see [ImageEditorDelegate]
375 | mixin LittleWidgetBinding on State {
376 |
377 | ///go back widget
378 | Widget backWidget({VoidCallback? onPressed}) {
379 | return GestureDetector(
380 | onTap: onPressed ?? () {
381 | Navigator.pop(context);
382 | },
383 | child: ImageEditor.uiDelegate.buildBackWidget(),
384 | );
385 | }
386 |
387 | ///operation button in control bar
388 | Widget getOperateTypeRes(OperateType type, {required bool choosen}) {
389 | return ImageEditor.uiDelegate.buildOperateWidget(type, choosen: choosen);
390 | }
391 |
392 | ///action done widget
393 | Widget doneButtonWidget({VoidCallback? onPressed}) {
394 | return GestureDetector(
395 | onTap: onPressed,
396 | child: ImageEditor.uiDelegate.buildDoneWidget(),
397 | );
398 | }
399 |
400 | ///undo action
401 | Widget unDoWidget({VoidCallback? onPressed}) {
402 | return GestureDetector(
403 | onTap: onPressed,
404 | child: ImageEditor.uiDelegate.buildUndoWidget(),
405 | );
406 | }
407 |
408 | ///Ignore pointer evenet by [OperateType]
409 | Widget ignoreWidgetByType(OperateType type, Widget child) {
410 | return ValueListenableBuilder(
411 | valueListenable: realState?._panelController.operateType ?? ValueNotifier(OperateType.non),
412 | builder: (ctx, type, c) {
413 | return IgnorePointer(
414 | ignoring: type != OperateType.brush && type != OperateType.mosaic,
415 | child: child,
416 | );
417 | });
418 | }
419 |
420 | ///reset button
421 | Widget resetWidget({VoidCallback? onTap}) {
422 | return Padding(
423 | padding: EdgeInsets.only(top: 6, bottom: 6 , right: 16),
424 | child: ValueListenableBuilder(
425 | valueListenable: realState?._panelController.operateType ?? ValueNotifier(OperateType.non),
426 | builder: (ctx, value, child) {
427 | return Offstage(
428 | offstage: value != OperateType.rotated,
429 | child: GestureDetector(
430 | onTap: onTap,
431 | child: ImageEditor.uiDelegate.resetWidget,
432 | ),);
433 | },
434 | ),
435 | );
436 | }
437 |
438 | }
439 |
440 | ///This binding can make editor to roatate canvas
441 | /// * for now, the paint-path,will change his relative position of canvas
442 | /// * when canvas rotated. because the paint-path area it's full canvas and
443 | /// * origin image is not maybe. if force to keep the relative path, will reduce
444 | /// * the paint-path area.
445 | mixin RotateCanvasBinding on State {
446 |
447 | ///canvas rotate value
448 | /// * 90 angle each time.
449 | int rotateValue = 0;
450 |
451 | ///canvas flip value
452 | /// * 180 angle each time.
453 | double flipValue = 0;
454 |
455 | ///flip canvas
456 | void flipCanvas() {
457 | flipValue = flipValue == 0 ? math.pi : 0;
458 | setState(() {});
459 | }
460 |
461 | ///routate canvas
462 | /// * will effect image, text, drawing board
463 | void rotateCanvasPlate() {
464 | rotateValue++;
465 | setState(() {});
466 | }
467 |
468 | ///reset canvas
469 | void resetCanvasPlate() {
470 | rotateValue = 0;
471 | setState(() {
472 |
473 | });
474 | }
475 |
476 | }
477 |
478 | ///text painting
479 | mixin TextCanvasBinding on State {
480 | late StateSetter textSetter;
481 |
482 | final List textModels = [];
483 |
484 | void addText(FloatTextModel model) {
485 | textModels.add(model);
486 | refreshTextCanvas();
487 | }
488 |
489 | ///delete a text from canvas
490 | void deleteTextWidget(FloatTextModel target) {
491 | textModels.remove(target);
492 | refreshTextCanvas();
493 | }
494 |
495 | void toTextEditorPage() {
496 | realState?._panelController.hidePanel();
497 | Navigator.of(context)
498 | .push(PageRouteBuilder(
499 | opaque: false,
500 | pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) {
501 | return TextEditorPage();
502 | }))
503 | .then((value) {
504 | realState?._panelController.showPanel();
505 | if (value is FloatTextModel) {
506 | addText(value);
507 | }
508 | });
509 | }
510 |
511 | void refreshTextCanvas() {
512 | textSetter.call(() {});
513 | }
514 |
515 | Widget buildTextCanvas() {
516 | return StatefulBuilder(builder: (tCtx, setter) {
517 | textSetter = setter;
518 | return Stack(
519 | alignment: Alignment.center,
520 | children: textModels
521 | .map((e) => Positioned(
522 | child: _wrapWithGesture(
523 | FloatTextWidget(
524 | textModel: e,
525 | ),
526 | e),
527 | left: e.left,
528 | top: e.top,
529 | ))
530 | .toList(),
531 | );
532 | });
533 | }
534 |
535 | Widget _wrapWithGesture(Widget child, FloatTextModel model) {
536 | void pointerDetach(DragEndDetails? details) {
537 | if (details != null) {
538 | //touch event up
539 | realState?._panelController.releaseText(details, model, () {
540 | deleteTextWidget(model);
541 | });
542 | } else {
543 | //touch event cancel
544 | realState?._panelController.doIdle();
545 | }
546 | model.isSelected = false;
547 | refreshTextCanvas();
548 | realState?._panelController.showPanel();
549 | }
550 |
551 | return GestureDetector(
552 | child: child,
553 | onPanStart: (_) {
554 | realState?._panelController.moveText(model);
555 | },
556 | onPanUpdate: (details) {
557 | model.isSelected = true;
558 | model.left += details.delta.dx;
559 | model.top += details.delta.dy;
560 | refreshTextCanvas();
561 | realState?._panelController.hidePanel();
562 | },
563 | onPanEnd: (d) {
564 | pointerDetach(d);
565 | },
566 | onPanCancel: () {
567 | pointerDetach(null);
568 | },
569 | );
570 | }
571 | }
572 |
573 | ///drawing board
574 | mixin SignatureBinding on State {
575 |
576 | DrawStyle get lastDrawStyle => painterController.drawStyle;
577 |
578 | ///Canvas layer for each draw action action.
579 | /// * e.g. First draw some path with white color, than change the color and draw some path again.
580 | /// * After this [pathRecord] will save 2 layes in it.
581 | final List pathRecord = [];
582 |
583 | late StateSetter canvasSetter;
584 |
585 | ///mosaic pixel's width
586 | double mosaicWidth = 5.0;
587 |
588 | ///painter stroke width.
589 | double pStrockWidth = 5;
590 |
591 | ///painter color
592 | Color pColor = Colors.redAccent;
593 |
594 | ///painter controller
595 | late SignatureController painterController;
596 |
597 | @override
598 | void initState() {
599 | super.initState();
600 | pColor = widget.launcher.pColor;
601 | mosaicWidth = widget.launcher.mosaicWidth;
602 | pStrockWidth = widget.launcher.pStrockWidth;
603 | }
604 |
605 | ///switch painter's style
606 | /// * e.g. color、mosaic
607 | void switchPainterMode(DrawStyle style) {
608 | if(lastDrawStyle == style) return;
609 | changePainterColor(pColor);
610 | painterController.drawStyle = style;
611 | }
612 |
613 | ///change painter's color
614 | void changePainterColor(Color color) async {
615 | pColor = color;
616 | realState?._panelController.selectColor(color);
617 | pathRecord.insert(
618 | 0,
619 | RepaintBoundary(
620 | child: CustomPaint(
621 | painter: SignaturePainter(painterController),
622 | child: ConstrainedBox(
623 | constraints: const BoxConstraints(
624 | minWidth: double.infinity, minHeight: double.infinity, maxWidth: double.infinity, maxHeight: double.infinity),
625 | )),
626 | ));
627 | //savePath();
628 | initPainter();
629 | _refreshBrushCanvas();
630 | }
631 |
632 | void _refreshBrushCanvas() {
633 | pathRecord.removeLast();
634 | //add new layer.
635 | pathRecord.add(Signature(
636 | controller: painterController,
637 | backgroundColor: Colors.transparent,
638 | ));
639 | _refreshCanvas();
640 | }
641 |
642 | ///undo last drawing.
643 | void undo() {
644 | painterController.undo();
645 | }
646 |
647 | ///refresh canvas.
648 | void _refreshCanvas() {
649 | canvasSetter(() {});
650 | }
651 |
652 | void initPainter() {
653 | painterController = SignatureController(penStrokeWidth: pStrockWidth, penColor: pColor, mosaicWidth: mosaicWidth);
654 | }
655 |
656 | Widget _buildBrushCanvas() {
657 | if (pathRecord.isEmpty) {
658 | pathRecord.add(Signature(
659 | controller: painterController,
660 | backgroundColor: Colors.transparent,
661 | ));
662 | }
663 | return StatefulBuilder(builder: (ctx, canvasSetter) {
664 | this.canvasSetter = canvasSetter;
665 | return realState?.ignoreWidgetByType(OperateType.brush, Stack(
666 | children: pathRecord,
667 | )) ?? SizedBox();
668 | });
669 | }
670 |
671 | @override
672 | void dispose() {
673 | pathRecord.clear();
674 | super.dispose();
675 | }
676 | }
677 |
678 | mixin ScreenShotBinding on State {
679 | final ScreenshotController screenshotController = ScreenshotController();
680 | }
681 |
682 | ///information about window
683 | mixin WindowUiBinding on State {
684 |
685 | Size get windowSize => MediaQuery.of(context).size;
686 |
687 | double get windowStatusBarHeight => View.of(context).padding.top;
688 |
689 | double get windowBottomBarHeight => View.of(context).padding.bottom;
690 |
691 | double get screenWidth => windowSize.width;
692 |
693 | double get screenHeight => windowSize.height;
694 |
695 | }
696 |
697 | extension _BaseImageEditorState on State {
698 | ImageEditorState? get realState {
699 | if (this is ImageEditorState) {
700 | return this as ImageEditorState;
701 | }
702 | return null;
703 | }
704 | }
705 |
706 | ///the color selected.
707 | typedef OnColorSelected = void Function(Color color);
708 |
709 | class CircleColorWidget extends StatefulWidget {
710 | final Color color;
711 |
712 | final ValueNotifier valueListenable;
713 |
714 | final OnColorSelected onColorSelected;
715 |
716 | const CircleColorWidget({Key? key, required this.color, required this.valueListenable, required this.onColorSelected}) : super(key: key);
717 |
718 | @override
719 | State createState() {
720 | return CircleColorWidgetState();
721 | }
722 | }
723 |
724 | class CircleColorWidgetState extends State {
725 | @override
726 | Widget build(BuildContext context) {
727 | return GestureDetector(
728 | onTap: () {
729 | widget.onColorSelected(widget.color);
730 | },
731 | child: ValueListenableBuilder(
732 | valueListenable: widget.valueListenable,
733 | builder: (ctx, value, child) {
734 | final double size = value == widget.color.value ? 25 : 21;
735 | return Container(
736 | width: size,
737 | height: size,
738 | decoration: BoxDecoration(
739 | border: Border.all(color: Colors.white, width: value == widget.color.value ? 4 : 2),
740 | shape: BoxShape.circle,
741 | color: widget.color,
742 | ),
743 | );
744 | },
745 | ),
746 | );
747 | }
748 |
749 |
750 | }
751 |
--------------------------------------------------------------------------------
/lib/model/float_text_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | ///Text widget model, for help to move text-widget.
4 | class FloatTextModel extends BaseFloatModel{
5 |
6 | String text;
7 |
8 | TextStyle? style;
9 |
10 | ///the top of position
11 | double top;
12 | ///the left of position
13 | double left;
14 | ///widget's size
15 | Size? size;
16 |
17 | bool isSelected;
18 |
19 | FloatTextModel({
20 | required this.text,
21 | this.style,
22 | required this.top,
23 | required this.left,
24 | this.isSelected = false,
25 | });
26 |
27 | @override
28 | Size? get floatSize => size;
29 | }
30 |
31 | abstract class BaseFloatModel{
32 | Size? get floatSize;
33 | }
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/lib/widget/drawing_board.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'dart:async';
4 | import 'dart:math';
5 | import 'dart:ui' as ui;
6 |
7 | import 'package:flutter/foundation.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:image/image.dart' as img;
10 |
11 |
12 | /// signature canvas. Controller is required, other parameters are optional.
13 | /// widget/canvas expands to maximum by default.
14 | /// this behaviour can be overridden using width and/or height parameters.
15 | ///
16 | /// This is from https://pub.flutter-io.cn/packages/signature
17 | class Signature extends StatefulWidget {
18 | /// constructor
19 | const Signature({
20 | required this.controller,
21 | Key? key,
22 | this.backgroundColor = Colors.grey,
23 | this.width,
24 | this.height,
25 | }) : super(key: key);
26 |
27 | /// signature widget controller
28 | final SignatureController controller;
29 |
30 | /// signature widget width
31 | final double? width;
32 |
33 | /// signature widget height
34 | final double? height;
35 |
36 | /// signature widget background color
37 | final Color backgroundColor;
38 |
39 | @override
40 | State createState() => SignatureState();
41 | }
42 |
43 | /// signature widget state
44 | class SignatureState extends State {
45 | /// Helper variable indicating that user has left the canvas so we can prevent linking next point
46 | /// with straight line.
47 | bool _isOutsideDrawField = false;
48 |
49 | /// Active pointer to prevent multitouch drawing
50 | int? activePointerId;
51 |
52 | @override
53 | Widget build(BuildContext context) {
54 | final double maxWidth = widget.width ?? double.infinity;
55 | final double maxHeight = widget.height ?? double.infinity;
56 | final GestureDetector signatureCanvas = GestureDetector(
57 | onVerticalDragUpdate: (DragUpdateDetails details) {
58 | //NO-OP
59 | },
60 | child: Container(
61 | decoration: BoxDecoration(color: widget.backgroundColor),
62 | child: Listener(
63 | onPointerDown: (PointerDownEvent event) {
64 | if (activePointerId == null || activePointerId == event.pointer) {
65 | activePointerId = event.pointer;
66 | widget.controller.onDrawStart?.call();
67 | _addPoint(event, PointType.tap);
68 | }
69 | },
70 | onPointerUp: (PointerUpEvent event) {
71 | if (activePointerId == event.pointer) {
72 | _addPoint(event, PointType.tap);
73 | widget.controller.pushCurrentStateToUndoStack();
74 | widget.controller.onDrawEnd?.call();
75 | activePointerId = null;
76 | }
77 | },
78 | onPointerCancel: (PointerCancelEvent event) {
79 | if (activePointerId == event.pointer) {
80 | _addPoint(event, PointType.tap);
81 | widget.controller.pushCurrentStateToUndoStack();
82 | widget.controller.onDrawEnd?.call();
83 | activePointerId = null;
84 | }
85 | },
86 | onPointerMove: (PointerMoveEvent event) {
87 | if (activePointerId == event.pointer) {
88 | _addPoint(event, PointType.move);
89 | widget.controller.onDrawMove?.call();
90 | }
91 | },
92 | child: RepaintBoundary(
93 | child: CustomPaint(
94 | painter: SignaturePainter(widget.controller),
95 | child: ConstrainedBox(
96 | constraints: BoxConstraints(
97 | minWidth: maxWidth,
98 | minHeight: maxHeight,
99 | maxWidth: maxWidth,
100 | maxHeight: maxHeight),
101 | ),
102 | ),
103 | )),
104 | ),
105 | );
106 |
107 | if (widget.width != null || widget.height != null) {
108 | //IF DOUNDARIES ARE DEFINED, USE LIMITED BOX
109 | return Center(
110 | child: LimitedBox(
111 | maxWidth: maxWidth,
112 | maxHeight: maxHeight,
113 | child: signatureCanvas,
114 | ),
115 | );
116 | } else {
117 | //IF NO BOUNDARIES ARE DEFINED, RETURN THE WIDGET AS IS
118 | return signatureCanvas;
119 | }
120 | }
121 |
122 | void _addPoint(PointerEvent event, PointType type) {
123 | final Offset o = event.localPosition;
124 | //SAVE POINT ONLY IF IT IS IN THE SPECIFIED BOUNDARIES
125 | if ((widget.width == null || o.dx > 0 && o.dx < widget.width!) &&
126 | (widget.height == null || o.dy > 0 && o.dy < widget.height!)) {
127 | // IF USER LEFT THE BOUNDARY AND AND ALSO RETURNED BACK
128 | // IN ONE MOVE, RETYPE IT AS TAP, AS WE DO NOT WANT TO
129 | // LINK IT WITH PREVIOUS POINT
130 |
131 | PointType t = type;
132 | if (_isOutsideDrawField) {
133 | t = PointType.tap;
134 | }
135 | setState(() {
136 | //IF USER WAS OUTSIDE OF CANVAS WE WILL RESET THE HELPER VARIABLE AS HE HAS RETURNED
137 | _isOutsideDrawField = false;
138 | widget.controller.addPoint(Point(o, t, event.pointer));
139 | });
140 | } else {
141 | //NOTE: USER LEFT THE CANVAS!!! WE WILL SET HELPER VARIABLE
142 | //WE ARE NOT UPDATING IN setState METHOD BECAUSE WE DO NOT NEED TO RUN BUILD METHOD
143 | _isOutsideDrawField = true;
144 | }
145 | }
146 | }
147 |
148 | /// type of user display finger movement
149 | enum PointType {
150 | /// one touch on specific place - tap
151 | tap,
152 |
153 | /// finger touching the display and moving around
154 | move,
155 | }
156 |
157 | /// one point on canvas represented by offset and type
158 | class Point {
159 | /// constructor
160 | Point(this.offset, this.type, this.eventId);
161 |
162 | /// x and y value on 2D canvas
163 | Offset offset;
164 |
165 | /// type of user display finger movement
166 | PointType type;
167 |
168 | int eventId;
169 | }
170 |
171 | class SignaturePainter extends CustomPainter {
172 | SignaturePainter(this._controller)
173 | : _penStyle = Paint(),
174 | super(repaint: _controller) {
175 | _penStyle
176 | ..color = _controller.penColor
177 | ..style = PaintingStyle.stroke
178 | ..strokeWidth = _controller.penStrokeWidth;
179 |
180 | }
181 |
182 | final SignatureController _controller;
183 | final Paint _penStyle;
184 |
185 |
186 | @override
187 | void paint(Canvas canvas, _) {
188 | final List points = _controller.value;
189 | if (points.isEmpty) {
190 | return;
191 | }
192 | //for draw [DrawStyle.mosaic]
193 | void paintMosaic(Offset center) {
194 | final ui.Paint paint = ui.Paint()..color = Colors.black26;
195 | final double size = _controller.mosaicWidth;
196 | final double halfSize = size/2;
197 | final ui.Rect b1 = Rect.fromCenter(center: center.translate(-halfSize, -halfSize),width: size,height: size);
198 | //0,0
199 | canvas.drawRect(b1, paint);
200 | paint.color = Colors.grey.withOpacity(0.5);
201 | //0,1
202 | canvas.drawRect(b1.translate(0, size), paint);
203 | paint.color = Colors.black38;
204 | //0,2
205 | canvas.drawRect(b1.translate(0, size*2), paint);
206 | paint.color = Colors.black12;
207 | //1,0
208 | canvas.drawRect(b1.translate(size, 0), paint);
209 | paint.color = Colors.black26;
210 | //1,1
211 | canvas.drawRect(b1.translate(size, size), paint);
212 | paint.color = Colors.black45;
213 | //1,2
214 | canvas.drawRect(b1.translate(size, size*2), paint);
215 | paint.color = Colors.grey.withOpacity(0.5);
216 | //2,0
217 | canvas.drawRect(b1.translate(size*2, 0), paint);
218 | paint.color = Colors.black12;
219 | //2,1
220 | canvas.drawRect(b1.translate(size*2, size), paint);
221 | paint.color = Colors.black26;
222 | //2,2
223 | canvas.drawRect(b1.translate(size*2, size*2), paint);
224 |
225 | }
226 |
227 | //for draw [DrawStyle.normal]
228 | Path paintPath() {
229 | final Path path = Path();
230 | final Map> pathM = {};
231 | points.forEach((element) {
232 | if(pathM[element.eventId] == null)
233 | pathM[element.eventId] = [];
234 | pathM[element.eventId]!.add(element);
235 | });
236 |
237 | pathM.forEach((key, value) {
238 | final first = value.first;
239 | path.moveTo(first.offset.dx, first.offset.dy);
240 | if(value.length <= 3) {
241 | _penStyle.style = PaintingStyle.fill;
242 | canvas.drawCircle(first.offset, _controller.penStrokeWidth,_penStyle);
243 | _penStyle.style = PaintingStyle.stroke;
244 | } else {
245 | value.forEach((e) {
246 | path.lineTo(e.offset.dx, e.offset.dy);
247 | });
248 | }
249 | });
250 | return path;
251 | }
252 |
253 | switch(_controller.drawStyle) {
254 | case DrawStyle.normal:
255 | canvas.drawPath(paintPath(), _penStyle);
256 | break;
257 | case DrawStyle.mosaic:
258 | //reduce the frequency of mosaic drawing.
259 | for(int i=0; i < points.length; i+=2) {
260 | paintMosaic(points[i].offset);
261 | }
262 | break;
263 | }
264 |
265 | }
266 |
267 | @override
268 | bool shouldRepaint(CustomPainter other) => true;
269 | }
270 |
271 |
272 | enum DrawStyle{
273 | ///use penColor
274 | normal,
275 | mosaic,
276 | }
277 |
278 | /// class for interaction with signature widget
279 | /// manages points representing signature on canvas
280 | /// provides signature manipulation functions (export, clear)
281 | class SignatureController extends ValueNotifier> {
282 | /// constructor
283 | SignatureController({
284 | List? points,
285 | this.penColor = Colors.black,
286 | this.penStrokeWidth = 3.0,
287 | this.exportBackgroundColor,
288 | this.onDrawStart,
289 | this.onDrawMove,
290 | this.onDrawEnd,
291 | this.mosaicWidth = 5.0
292 | }) : super(points ?? []);
293 |
294 | /// color of a signature line
295 | final Color penColor;
296 |
297 | /// boldness of a signature line
298 | final double penStrokeWidth;
299 |
300 | /// background color to be used in exported png image
301 | final Color? exportBackgroundColor;
302 |
303 | ///mosaic pixel's width
304 | final double mosaicWidth;
305 |
306 | ///draw style
307 | /// * [DrawStyle.normal] : paint with some color
308 | DrawStyle drawStyle = DrawStyle.normal;
309 |
310 | /// callback to notify when drawing has started
311 | VoidCallback? onDrawStart;
312 |
313 | /// callback to notify when the pointer was moved while drawing.
314 | VoidCallback? onDrawMove;
315 |
316 | /// callback to notify when drawing has stopped
317 | VoidCallback? onDrawEnd;
318 |
319 | /// getter for points representing signature on 2D canvas
320 | List get points => value;
321 |
322 | /// stack-like list of point to save user's latest action
323 | final List> _latestActions = >[];
324 |
325 | /// stack-like list that use to save points when user undo the signature
326 | final List> _revertedActions = >[];
327 |
328 | /// setter for points representing signature on 2D canvas
329 | set points(List points) {
330 | value = points;
331 | }
332 |
333 | /// add point to point collection
334 | void addPoint(Point point) {
335 | value.add(point);
336 | notifyListeners();
337 | }
338 |
339 | /// REMEMBERS CURRENT CANVAS STATE IN UNDO STACK
340 | void pushCurrentStateToUndoStack() {
341 | _latestActions.add([...points]);
342 | //CLEAR ANY UNDO-ED ACTIONS. IF USER UNDO-ED ANYTHING HE ALREADY MADE
343 | // ANOTHER CHANGE AND LEFT THAT OLD PATH.
344 | _revertedActions.clear();
345 | }
346 |
347 | /// check if canvas is empty (opposite of isNotEmpty method for convenience)
348 | bool get isEmpty {
349 | return value.isEmpty;
350 | }
351 |
352 | /// check if canvas is not empty (opposite of isEmpty method for convenience)
353 | bool get isNotEmpty {
354 | return value.isNotEmpty;
355 | }
356 |
357 | /// clear the canvas
358 | void clear() {
359 | value = [];
360 | _latestActions.clear();
361 | _revertedActions.clear();
362 | }
363 |
364 | /// It will remove last action from [_latestActions].
365 | /// The last action will be saved to [_revertedActions]
366 | /// that will be used to do redo-ing.
367 | /// Then, it will modify the real points with the last action.
368 | void undo() {
369 | if (_latestActions.isNotEmpty) {
370 | final List lastAction = _latestActions.removeLast();
371 | _revertedActions.add([...lastAction]);
372 | if (_latestActions.isNotEmpty) {
373 | points = [..._latestActions.last];
374 | return;
375 | }
376 | points = [];
377 | notifyListeners();
378 | }
379 | }
380 |
381 | /// It will remove last reverted actions and add it into [_latestActions]
382 | /// Then, it will modify the real points with the last reverted action.
383 | void redo() {
384 | if (_revertedActions.isNotEmpty) {
385 | final List lastRevertedAction = _revertedActions.removeLast();
386 | _latestActions.add([...lastRevertedAction]);
387 | points = [...lastRevertedAction];
388 | notifyListeners();
389 | return;
390 | }
391 | }
392 |
393 | /// convert to
394 | Future toImage() async {
395 | if (isEmpty) {
396 | return null;
397 | }
398 |
399 | double minX = double.infinity, minY = double.infinity;
400 | double maxX = 0, maxY = 0;
401 | for (Point point in points) {
402 | if (point.offset.dx < minX) {
403 | minX = point.offset.dx;
404 | }
405 | if (point.offset.dy < minY) {
406 | minY = point.offset.dy;
407 | }
408 | if (point.offset.dx > maxX) {
409 | maxX = point.offset.dx;
410 | }
411 | if (point.offset.dy > maxY) {
412 | maxY = point.offset.dy;
413 | }
414 | }
415 |
416 | final ui.PictureRecorder recorder = ui.PictureRecorder();
417 | final ui.Canvas canvas = Canvas(recorder)
418 | ..translate(-(minX - penStrokeWidth), -(minY - penStrokeWidth));
419 | if (exportBackgroundColor != null) {
420 | final ui.Paint paint = Paint()..color = exportBackgroundColor!;
421 | canvas.drawPaint(paint);
422 | }
423 | SignaturePainter(this).paint(canvas, Size.infinite);
424 | final ui.Picture picture = recorder.endRecording();
425 | return picture.toImage(
426 | (maxX - minX + penStrokeWidth * 2).toInt(),
427 | (maxY - minY + penStrokeWidth * 2).toInt(),
428 | );
429 | }
430 |
431 |
432 | /// convert canvas to dart:ui Image and then to PNG represented in Uint8List
433 | Future toPngBytes() async {
434 | if (!kIsWeb) {
435 | final ui.Image? image = await toImage();
436 | if (image == null) {
437 | return null;
438 | }
439 | final ByteData? bytes = await image.toByteData(
440 | format: ui.ImageByteFormat.png,
441 | );
442 | return bytes?.buffer.asUint8List();
443 | } else {
444 | return _toPngBytesForWeb();
445 | }
446 | }
447 |
448 | // 'image.toByteData' is not available for web. So we are using the package
449 | // 'image' to create an image which works on web too
450 | Uint8List? _toPngBytesForWeb() {
451 | if (isEmpty) {
452 | return null;
453 | }
454 | final int pColor = img.getColor(
455 | penColor.red,
456 | penColor.green,
457 | penColor.blue,
458 | );
459 |
460 | final Color backgroundColor = exportBackgroundColor ?? Colors.transparent;
461 | final int bColor = img.getColor(backgroundColor.red, backgroundColor.green,
462 | backgroundColor.blue, backgroundColor.alpha.toInt());
463 |
464 | double minX = double.infinity;
465 | double maxX = 0;
466 | double minY = double.infinity;
467 | double maxY = 0;
468 |
469 | for (Point point in points) {
470 | minX = min(point.offset.dx, minX);
471 | maxX = max(point.offset.dx, maxX);
472 | minY = min(point.offset.dy, minY);
473 | maxY = max(point.offset.dy, maxY);
474 | }
475 |
476 | //point translation
477 | final List translatedPoints = [];
478 | for (Point point in points) {
479 | translatedPoints.add(Point(
480 | Offset(
481 | point.offset.dx - minX + penStrokeWidth,
482 | point.offset.dy - minY + penStrokeWidth,
483 | ),
484 | point.type,
485 | point.eventId,
486 | ));
487 | }
488 |
489 | final int width = (maxX - minX + penStrokeWidth * 2).toInt();
490 | final int height = (maxY - minY + penStrokeWidth * 2).toInt();
491 |
492 | // create the image with the given size
493 | final img.Image signatureImage = img.Image(width, height);
494 | // set the image background color
495 | img.fill(signatureImage, bColor);
496 |
497 | // read the drawing points list and draw the image
498 | // it uses the same logic as the CustomPainter Paint function
499 | for (int i = 0; i < translatedPoints.length - 1; i++) {
500 | if (translatedPoints[i + 1].type == PointType.move) {
501 | img.drawLine(
502 | signatureImage,
503 | translatedPoints[i].offset.dx.toInt(),
504 | translatedPoints[i].offset.dy.toInt(),
505 | translatedPoints[i + 1].offset.dx.toInt(),
506 | translatedPoints[i + 1].offset.dy.toInt(),
507 | pColor,
508 | thickness: penStrokeWidth);
509 | } else {
510 | // draw the point to the image
511 | img.fillCircle(
512 | signatureImage,
513 | translatedPoints[i].offset.dx.toInt(),
514 | translatedPoints[i].offset.dy.toInt(),
515 | penStrokeWidth.toInt(),
516 | pColor,
517 | );
518 | }
519 | }
520 | // encode the image to PNG
521 | return Uint8List.fromList(img.encodePng(signatureImage));
522 | }
523 | }
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
--------------------------------------------------------------------------------
/lib/widget/editor_panel_controller.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:ui';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:image_editor_dove/model/float_text_model.dart';
6 |
7 | ///The object taht are moving.
8 | enum MoveStuff{
9 | non,
10 | text,
11 | }
12 |
13 |
14 | enum OperateType{
15 | non,
16 | brush,//drawing path
17 | text,//add text to canvas
18 | flip,//flip image
19 | rotated,//rotate canvas
20 | mosaic,//draw mosaic
21 | }
22 |
23 | class EditorPanelController {
24 |
25 | static const defaultTrashColor = const Color(0x26ffffff);
26 |
27 | EditorPanelController() {
28 | colorSelected = ValueNotifier(brushColor.first.value);
29 | }
30 |
31 | Size? screenSize;
32 |
33 | ///take shot action listener
34 | /// * it's for hide some non-relative ui.
35 | /// * e.g. hide status bar, hide bottom bar
36 | ValueNotifier takeShot = ValueNotifier(false);
37 |
38 | ValueNotifier showTrashCan = ValueNotifier(false);
39 |
40 | ///trash background color
41 | ValueNotifier trashColor = ValueNotifier(defaultTrashColor);
42 |
43 | ValueNotifier showAppBar = ValueNotifier(true);
44 |
45 | ValueNotifier showBottomBar = ValueNotifier(true);
46 |
47 | ValueNotifier operateType = ValueNotifier(OperateType.non);
48 |
49 | ///Is current operate type
50 | bool isCurrentOperateType(OperateType type) => type.index == operateType.value.index;
51 |
52 | /// is need to show second panel.
53 | /// * in some operate type like drawing path, it need a 2nd panel for change color.
54 | bool show2ndPanel() => operateType.value == OperateType.brush || operateType.value == OperateType.mosaic;
55 |
56 | final List brushColor = const [
57 | Color(0xFFFA4D32),
58 | Color(0xFFFF7F1E),
59 | Color(0xFF2DA24A),
60 | Color(0xFFF2F2F2),
61 | Color(0xFF222222),
62 | Color(0xFF1F8BE5),
63 | Color(0xFF4E43DB),
64 | ];
65 |
66 | late ValueNotifier colorSelected;
67 |
68 | void selectColor(Color color) {
69 | colorSelected.value = color.value;
70 | }
71 |
72 | ///switch operate type
73 | void switchOperateType(OperateType type) {
74 | operateType.value = type;
75 | }
76 |
77 | void cancelOperateType() {
78 | operateType.value = OperateType.non;
79 | }
80 |
81 | OperateType get currentOperateType => operateType.value;
82 |
83 |
84 | ///moving object
85 | /// * non : not moving.
86 | MoveStuff moveStuff = MoveStuff.non;
87 |
88 | ///trash can position
89 | Offset trashCanPosition = Offset(111, (20 + (PlatformDispatcher.instance.implicitView?.padding.bottom ?? 0)));
90 |
91 | ///trash can size.
92 | final Size tcSize = Size(153, 77);
93 |
94 | ///The top and bottom panel's slide duration.
95 | final Duration panelDuration = const Duration(milliseconds: 300);
96 |
97 | ///hide bottom and top(app) bar.
98 | void hidePanel() {
99 | showAppBar.value = false;
100 | showBottomBar.value = false;
101 | switchTrashCan(true);
102 | }
103 |
104 | ///show bottom and top(app) bar.
105 | void showPanel() {
106 | showAppBar.value = true;
107 | showBottomBar.value = true;
108 | switchTrashCan(false);
109 | }
110 |
111 | ///hide/show trash can.
112 | void switchTrashCan(bool show) {
113 | showTrashCan.value = show;
114 | }
115 |
116 | ///switch trash can's color.
117 | void switchTrashCanColor(bool isInside) {
118 | trashColor.value = isInside ? Colors.red : defaultTrashColor;
119 | }
120 |
121 | ///move text.
122 | void moveText(FloatTextModel model) {
123 | moveStuff = MoveStuff.text;
124 | movingTarget = model;
125 | }
126 |
127 | ///release the moving-text.
128 | void releaseText(DragEndDetails details, FloatTextModel model, Function throwCall) {
129 | if(isThrowText(pointerUpPosition??Offset.zero, model)) {
130 | throwCall.call();
131 | }
132 | doIdle();
133 | }
134 |
135 | ///stop moving.
136 | void doIdle() {
137 | movingTarget = null;
138 | pointerUpPosition = null;
139 | moveStuff = MoveStuff.non;
140 | switchTrashCanColor(false);
141 | }
142 |
143 | ///moving object.
144 | /// * must based on [BaseFloatModel].
145 | /// * most time it's used to find the [movingTarget] that who just realeased.
146 | BaseFloatModel? movingTarget;
147 |
148 | ///cache the target taht just released.
149 | Offset? pointerUpPosition;
150 |
151 | ///pointer moving's callback
152 | void pointerMoving(PointerMoveEvent event) {
153 | pointerUpPosition = event.localPosition;
154 | switch(moveStuff) {
155 | case MoveStuff.non:
156 | break;
157 | case MoveStuff.text:
158 | if(movingTarget is FloatTextModel) {
159 | switchTrashCanColor(isThrowText(event.localPosition, movingTarget!));
160 | }
161 | break;
162 | }
163 | }
164 |
165 | ///decided whether the text is deleted or not.
166 | bool isThrowText(Offset pointer,BaseFloatModel target) {
167 | final Rect textR = Rect.fromCenter(center: pointer,
168 | width: target.floatSize?.width??1,
169 | height: target.floatSize?.height??1);
170 | final Rect tcR = Rect.fromLTWH(
171 | screenSize!.width - trashCanPosition.dx,
172 | screenSize!.height - trashCanPosition.dy - tcSize.height,
173 | tcSize.width,
174 | tcSize.height);
175 | return textR.overlaps(tcR);
176 | }
177 |
178 | }
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/lib/widget/float_text_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math' as math;
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:image_editor_dove/model/float_text_model.dart';
5 |
6 | import '../image_editor.dart';
7 |
8 |
9 |
10 | class FloatTextWidget extends StatefulWidget{
11 |
12 | final FloatTextModel textModel;
13 |
14 | const FloatTextWidget({Key? key,
15 | required this.textModel}) : super(key: key);
16 |
17 | @override
18 | State createState() {
19 | return FloatTextWidgetState();
20 | }
21 | }
22 |
23 | class FloatTextWidgetState extends State {
24 |
25 | FloatTextModel get model => widget.textModel;
26 |
27 | @override
28 | void initState() {
29 | super.initState();
30 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
31 | if(mounted) {
32 | RenderObject? ro = context.findRenderObject();
33 | if(ro is RenderBox) {
34 | widget.textModel.size ??= ro.size;
35 | }
36 | }
37 | });
38 | }
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return Container(
43 | padding: EdgeInsets.all(4),
44 | constraints: BoxConstraints(
45 | minWidth: 10, maxWidth: 335
46 | ),
47 | decoration: BoxDecoration(
48 | border: model.isSelected ?
49 | ImageEditor.uiDelegate.textSelectedBorder
50 | : null
51 | ),
52 | child: Text(
53 | model.text,
54 | style: model.style,
55 | ),
56 | );
57 | }
58 |
59 | }
60 |
61 |
62 | ///Draw dash border.
63 | class DashBorder extends Border{
64 |
65 | DashBorder({
66 | this.gap = 4.0,
67 | this.strokeWidth = 2.0,
68 | this.dashColor = Colors.white,
69 | BorderSide top = BorderSide.none,
70 | BorderSide right = BorderSide.none,
71 | BorderSide bottom = BorderSide.none,
72 | BorderSide left = BorderSide.none,
73 | }) : super(top: top, bottom: bottom, left: left, right: right);
74 |
75 | final double gap;
76 |
77 | final double strokeWidth;
78 |
79 | final Color dashColor;
80 |
81 | @override
82 | void paint(
83 | Canvas canvas,
84 | Rect rect, {
85 | TextDirection? textDirection,
86 | BoxShape shape = BoxShape.rectangle,
87 | BorderRadius? borderRadius,
88 | }) {
89 |
90 | Path getDashedPath({
91 | required math.Point a,
92 | required math.Point b,
93 | required gap,
94 | }) {
95 | final Size size = Size(b.x - a.x, b.y - a.y);
96 | final Path path = Path();
97 | path.moveTo(a.x, a.y);
98 | bool shouldDraw = true;
99 | math.Point currentPoint = math.Point(a.x, a.y);
100 |
101 | final num radians = math.atan(size.height / size.width);
102 |
103 | final num dx = math.cos(radians) * gap < 0
104 | ? math.cos(radians) * gap * -1
105 | : math.cos(radians) * gap;
106 |
107 | final num dy = math.sin(radians) * gap < 0
108 | ? math.sin(radians) * gap * -1
109 | : math.sin(radians) * gap;
110 |
111 | while (currentPoint.x <= b.x && currentPoint.y <= b.y) {
112 | shouldDraw
113 | ? path.lineTo(currentPoint.x, currentPoint.y)
114 | : path.moveTo(currentPoint.x, currentPoint.y);
115 | shouldDraw = !shouldDraw;
116 | currentPoint = math.Point(
117 | currentPoint.x + dx,
118 | currentPoint.y + dy,
119 | );
120 | }
121 | return path;
122 | }
123 | if (isUniform) {
124 | final Paint dashedPaint = Paint()
125 | ..color = dashColor
126 | ..strokeWidth = strokeWidth
127 | ..style = PaintingStyle.stroke;
128 | // top line
129 | final Path _topPath = getDashedPath(
130 | a: math.Point(rect.topLeft.dx, rect.topLeft.dy),
131 | b: math.Point(rect.topRight.dx, rect.topRight.dy),
132 | gap: gap,
133 | );
134 | // right line
135 | final Path _rightPath = getDashedPath(
136 | a: math.Point(rect.topRight.dx, rect.topRight.dy),
137 | b: math.Point(rect.bottomRight.dx, rect.bottomRight.dy),
138 | gap: gap,
139 | );
140 | // bottom line
141 | final Path _bottomPath = getDashedPath(
142 | a: math.Point(rect.bottomLeft.dx, rect.bottomLeft.dy),
143 | b: math.Point(rect.bottomRight.dx, rect.bottomRight.dy),
144 | gap: gap,
145 | );
146 | // left line
147 | final Path _leftPath = getDashedPath(
148 | a: math.Point(rect.topLeft.dx, rect.topLeft.dy),
149 | b: math.Point(rect.bottomLeft.dx, rect.bottomLeft.dy),
150 | gap: gap,
151 | );
152 |
153 | canvas.drawPath(_topPath, dashedPaint);
154 | canvas.drawPath(_rightPath, dashedPaint);
155 | canvas.drawPath(_bottomPath, dashedPaint);
156 | canvas.drawPath(_leftPath, dashedPaint);
157 | }
158 |
159 | paintBorder(canvas, rect, top: top, right: right, bottom: bottom, left: left);
160 | }
161 |
162 | }
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/lib/widget/image_editor_delegate.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:image_editor_dove/flutter_image_editor.dart';
5 | import 'package:image_editor_dove/widget/float_text_widget.dart';
6 | import 'package:image_editor_dove/widget/text_editor_page.dart';
7 |
8 | import 'editor_panel_controller.dart';
9 | import 'slider_widget.dart';
10 |
11 |
12 | class DefaultTextConfigModel extends TextConfigModel{
13 | @override
14 | double get initSize => 14;
15 |
16 | @override
17 | double get sliderBottomLimit => 14;
18 |
19 | @override
20 | double get sliderUpLimit => 36;
21 |
22 | @override
23 | Color get cursorColor => const Color(0xFFF83112);
24 |
25 | }
26 |
27 | class DefaultImageEditorDelegate extends ImageEditorDelegate{
28 |
29 | Color operatorStatuscolor(bool choosen) => choosen ? Colors.red : Colors.white;
30 |
31 | @override
32 | List get brushColors => const [
33 | Color(0xFFFA4D32),
34 | Color(0xFFFF7F1E),
35 | Color(0xFF2DA24A),
36 | Color(0xFFF2F2F2),
37 | Color(0xFF222222),
38 | Color(0xFF1F8BE5),
39 | Color(0xFF4E43DB),
40 | ];
41 |
42 | @override
43 | List get textColors => const [
44 | Color(0xFFFA4D32),
45 | Color(0xFFFF7F1E),
46 | Color(0xFF2DA24A),
47 | Color(0xFFF2F2F2),
48 | Color(0xFF222222),
49 | Color(0xFF1F8BE5),
50 | Color(0xFF4E43DB),
51 | ];
52 |
53 | @override
54 | Widget backBtnWidget(double limitSize) {
55 | return Icon(Icons.arrow_back_ios_new, size: limitSize);
56 | }
57 |
58 | @override
59 | Widget doneWidget(BoxConstraints constraints) {
60 | return Container(
61 | constraints: constraints,
62 | alignment: Alignment.center,
63 | decoration: BoxDecoration(
64 | borderRadius: BorderRadius.all(Radius.circular(6)),
65 | gradient: const LinearGradient(colors: [Colors.green, Colors.greenAccent])),
66 | child: Text(
67 | 'Done',
68 | style: TextStyle(fontSize: 15, color: Colors.white),
69 | ),
70 | );
71 | }
72 |
73 | @override
74 | Widget brushWidget(double limitSize, OperateType type, {required bool choosen}) {
75 | return Icon(Icons.brush_outlined, size: limitSize, color: operatorStatuscolor(choosen));
76 | }
77 |
78 | @override
79 | Widget addTextWidget(double limitSize, OperateType type, {required bool choosen}) {
80 | return Icon(Icons.notes, size: limitSize, color: operatorStatuscolor(choosen));
81 | }
82 |
83 | @override
84 | Widget rotateWidget(double limitSize, OperateType type, {required bool choosen}) {
85 | return Icon(Icons.rotate_right, size: limitSize, color: operatorStatuscolor(choosen));
86 | }
87 |
88 | @override
89 | Widget flipWidget(double limitSize, OperateType type, {required bool choosen}) {
90 | return Icon(Icons.flip, size: limitSize, color: operatorStatuscolor(choosen));
91 | }
92 |
93 | @override
94 | Widget mosaicWidget(double limitSize, OperateType type, {required bool choosen}) {
95 | return Icon(Icons.auto_awesome_mosaic, size: limitSize, color: operatorStatuscolor(choosen));
96 | }
97 |
98 | @override
99 | Widget get resetWidget => Text('Reset', style: TextStyle(color: Colors.white, fontSize: 16));
100 |
101 | @override
102 | SliderThemeData sliderThemeData(BuildContext context) => SliderTheme.of(context).copyWith(
103 | trackHeight: 2,
104 | thumbColor: Colors.white,
105 | disabledThumbColor: Colors.white,
106 | activeTrackColor: const Color(0xFFF83112),
107 | inactiveTrackColor: Colors.white.withOpacity(0.5),
108 | overlayShape: CustomRoundSliderOverlayShape(),
109 | );
110 |
111 | @override
112 | TextConfigModel get textConfigModel => DefaultTextConfigModel();
113 |
114 | @override
115 | Border get textSelectedBorder => DashBorder();
116 |
117 | @override
118 | Widget undoWidget(double limitSize) => Icon(Icons.undo, size: limitSize, color: Colors.white);
119 |
120 | @override
121 | Widget get boldTagWidget => Text(
122 | 'Bold',
123 | style: TextStyle(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
124 | );
125 |
126 | @override
127 | Widget get sliderLeftWidget => _txtFlatWidget('small');
128 |
129 | @override
130 | Widget get sliderRightWidget => _txtFlatWidget('big');
131 |
132 | Widget _txtFlatWidget(String txt) {
133 | return Text(
134 | txt,
135 | style: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 13),
136 | );
137 | }
138 |
139 | }
140 |
141 |
142 | ///This model for [TextEditorPage] to initial text style.
143 | abstract class TextConfigModel{
144 | ///slider up limit
145 | double get sliderUpLimit;
146 |
147 | ///slider bottom limit
148 | double get sliderBottomLimit;
149 |
150 | ///initial size
151 | double get initSize;
152 |
153 | ///input field's cursor color.
154 | Color get cursorColor;
155 |
156 | bool get isLegal => initSize >= sliderBottomLimit && initSize <= sliderUpLimit;
157 |
158 |
159 | }
160 |
161 |
162 | ///For delegate [ImageEditor]'s ui style.
163 | abstract class ImageEditorDelegate{
164 |
165 | final BoxConstraints _doneWidgetCons = BoxConstraints(maxHeight: 32, maxWidth: 54);
166 |
167 | final double _operateBtnSize = 24;
168 |
169 | final double _unDoWidgetSize = 20;
170 |
171 | final double _backWidgetSize = 22;
172 |
173 | Widget buildDoneWidget() => doneWidget(_doneWidgetCons);
174 |
175 | Widget buildOperateWidget(OperateType type, {required bool choosen}) {
176 | switch(type) {
177 | case OperateType.non:
178 | return SizedBox();
179 | case OperateType.brush:
180 | return brushWidget(_operateBtnSize, type, choosen: choosen);
181 | case OperateType.text:
182 | return addTextWidget(_operateBtnSize, type, choosen: choosen);
183 | case OperateType.flip:
184 | return flipWidget(_operateBtnSize, type, choosen: choosen);
185 | case OperateType.rotated:
186 | return rotateWidget(_operateBtnSize, type, choosen: choosen);
187 | case OperateType.mosaic:
188 | return mosaicWidget(_operateBtnSize, type, choosen: choosen);
189 | }
190 | }
191 |
192 | Widget buildUndoWidget() => undoWidget(_unDoWidgetSize);
193 |
194 | Widget buildBackWidget() => backBtnWidget(_backWidgetSize);
195 |
196 | ///Brush colors
197 | /// * color's amount in [1,7]
198 | List get brushColors;
199 |
200 | ///Text Colors
201 | /// * color's amount in [1,7]
202 | List get textColors;
203 |
204 | ///Slider's theme data
205 | SliderThemeData sliderThemeData(BuildContext context);
206 |
207 | ///Slider's tag
208 | /// * left-flag(small) - Slider - right-flag(big)
209 | Widget get sliderLeftWidget;
210 |
211 | ///Slider's tag
212 | /// * left-flag(small) - Slider - right-flag(big)
213 | Widget get sliderRightWidget;
214 |
215 | ///To control the text style of bold.
216 | Widget get boldTagWidget;
217 |
218 | ///Text config model
219 | /// * see also: [TextEditorPage]
220 | TextConfigModel get textConfigModel;
221 |
222 | ///Back button on appbar
223 | Widget backBtnWidget(double limitSize);
224 |
225 | ///Brush widget
226 | /// * for paint color on canvas.
227 | Widget brushWidget(double limitSize, OperateType type, {required bool choosen});
228 |
229 | ///Add text widget
230 | /// * for add some text on canvas.
231 | Widget addTextWidget(double limitSize, OperateType type, {required bool choosen});
232 |
233 | ///Flip widget
234 | /// * for flip the canvas.
235 | Widget flipWidget(double limitSize, OperateType type, {required bool choosen});
236 |
237 | ///Rotate widget
238 | /// * for rotate the canvas(90 angle each tap).
239 | Widget rotateWidget(double limitSize, OperateType type, {required bool choosen});
240 |
241 | ///Mosaic widget
242 | /// * for paint some mosaic on canvas.
243 | Widget mosaicWidget(double limitSize, OperateType type, {required bool choosen});
244 |
245 | ///Done widget
246 | /// * for save the edit action and generate a new image as result.
247 | Widget doneWidget(BoxConstraints constraints);
248 |
249 | ///Undo widget
250 | /// * for undo last edit action.
251 | Widget undoWidget(double limitSize);
252 |
253 | ///Reset widget
254 | /// * for reset rotated action.
255 | Widget get resetWidget;
256 |
257 | ///Text selected border
258 | /// * for draw a border the text selected.
259 | Border get textSelectedBorder;
260 |
261 |
262 |
263 | }
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
--------------------------------------------------------------------------------
/lib/widget/slider_widget.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 |
4 |
5 | class CustomRoundSliderOverlayShape extends SliderComponentShape {
6 | /// Create a slider thumb overlay that draws a circle.
7 | const CustomRoundSliderOverlayShape({ this.overlayRadius = 0.0 });
8 |
9 | /// The preferred radius of the round thumb shape when enabled.
10 | ///
11 | /// If it is not provided, then half of the [SliderThemeData.trackHeight] is
12 | /// used.
13 | final double overlayRadius;
14 |
15 | @override
16 | Size getPreferredSize(bool isEnabled, bool isDiscrete) {
17 | return Size.fromRadius(overlayRadius);
18 | }
19 |
20 | @override
21 | void paint(
22 | PaintingContext context,
23 | Offset center, {
24 | required Animation activationAnimation,
25 | required Animation enableAnimation,
26 | required bool isDiscrete,
27 | required TextPainter labelPainter,
28 | required RenderBox parentBox,
29 | required SliderThemeData sliderTheme,
30 | required TextDirection textDirection,
31 | required double value,
32 | required double textScaleFactor,
33 | required Size sizeWithOverflow,
34 | }) {
35 |
36 | final Canvas canvas = context.canvas;
37 | final Tween radiusTween = Tween(
38 | begin: 0.0,
39 | end: overlayRadius,
40 | );
41 |
42 | canvas.drawCircle(
43 | center,
44 | radiusTween.evaluate(activationAnimation),
45 | Paint()..color = sliderTheme.overlayColor!,
46 | );
47 | }
48 | }
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/lib/widget/text_editor_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:image_editor_dove/extension/num_extension.dart';
5 | import 'package:image_editor_dove/model/float_text_model.dart';
6 |
7 | import '../image_editor.dart';
8 | import 'image_editor_delegate.dart';
9 |
10 |
11 | ///A page for input some text to canvas.
12 | class TextEditorPage extends StatefulWidget {
13 | @override
14 | State createState() {
15 | return TextEditorPageState();
16 | }
17 | }
18 |
19 | class TextEditorPageState extends State with LittleWidgetBinding, WindowUiBinding{
20 |
21 | static TextConfigModel get configModel => ImageEditor.uiDelegate.textConfigModel;
22 |
23 | final FocusNode _node = FocusNode();
24 |
25 | final GlobalKey filedKey = GlobalKey();
26 |
27 | final TextEditingController _controller = TextEditingController();
28 |
29 | final List textColorList = ImageEditor.uiDelegate.textColors;
30 |
31 | ///text's color
32 | late ValueNotifier selectedColor;
33 |
34 | Color get _textColor => Color(selectedColor.value);
35 |
36 | ///text's size
37 | double _size = configModel.initSize;
38 |
39 | ///text font weight
40 | FontWeight _fontWeight = FontWeight.normal;
41 |
42 | FloatTextModel buildModel() {
43 | RenderObject? ro = filedKey.currentContext?.findRenderObject();
44 | Offset offset = Offset(100, 200);
45 | if (ro is RenderBox) {
46 | //adjust text's dy value
47 | offset = ro.localToGlobal(Offset.zero).translate(0, -(44 + windowStatusBarHeight));
48 | }
49 | return FloatTextModel(text: _controller.text, top: offset.dy, left: offset.dx, style: TextStyle(fontSize: _size, color: _textColor));
50 | }
51 |
52 | void popWithResult() {
53 | Navigator.pop(context, buildModel());
54 | }
55 |
56 | void tapBoldBtn() {
57 | _fontWeight = _fontWeight == FontWeight.bold ? FontWeight.normal : FontWeight.bold;
58 | setState(() {
59 |
60 | });
61 | }
62 |
63 | @override
64 | void initState() {
65 | super.initState();
66 | assert(configModel.isLegal, "TextConfigModel config size is not legal : "
67 | "initSize must middle in up and bottom limit");
68 | selectedColor = ValueNotifier(textColorList.first.value);
69 | Future.delayed(const Duration(milliseconds: 160), () {
70 | if (mounted) _node.requestFocus();
71 | });
72 | }
73 |
74 | @override
75 | void dispose() {
76 | _node.dispose();
77 | _controller.dispose();
78 | super.dispose();
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | return GestureDetector(
84 | onTap: () {
85 | if(!_node.hasFocus)
86 | _node.requestFocus();
87 | },
88 | child: Material(
89 | color: Colors.transparent,
90 | child: Stack(
91 | children: [
92 | ClipRect(
93 | child: BackdropFilter(
94 | filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
95 | child: Container(
96 | width: double.infinity, height: double.infinity,
97 | color: Colors.black38,),
98 | ),
99 | ),
100 | Scaffold(
101 | backgroundColor: Colors.transparent,
102 | resizeToAvoidBottomInset: true,
103 | appBar: AppBar(
104 | backgroundColor: Colors.transparent,
105 | leading: GestureDetector(
106 | onTap: () {
107 | Navigator.pop(context);
108 | },
109 | child: Container(
110 | height: 44,
111 | alignment: Alignment.center,
112 | padding: EdgeInsets.only(top: 12, bottom: 12),
113 | child: Text('Cancel', style: TextStyle(color: Colors.white, fontSize: 16),),
114 | ),
115 | ),
116 | actions: [
117 | Padding(
118 | padding: EdgeInsets.only(top: 12, bottom: 12 , right: 16),
119 | child: doneButtonWidget(onPressed: popWithResult),
120 | ),
121 | ],
122 | ),
123 | body: Column(
124 | mainAxisAlignment: MainAxisAlignment.center,
125 | children: [
126 | Expanded(child: SizedBox()),
127 | //text area
128 | Container(
129 | width: screenWidth,
130 | padding: EdgeInsets.symmetric(horizontal: 20),
131 | child: TextField(
132 | key: filedKey,
133 | maxLines: 50,
134 | minLines: 1,
135 | controller: _controller,
136 | focusNode: _node,
137 | cursorColor: configModel.cursorColor,
138 | style: TextStyle(color: _textColor, fontSize: _size, fontWeight: _fontWeight),
139 | decoration: InputDecoration(
140 | isCollapsed: true,
141 | border: InputBorder.none
142 | ),
143 | ),
144 | ),
145 | Expanded(child: SizedBox()),
146 | //slider
147 | Container(
148 | height: 36,
149 | padding: EdgeInsets.symmetric(horizontal: 20),
150 | width: double.infinity,
151 | //color: Colors.white,
152 | child: Row(
153 | children: [
154 | GestureDetector(
155 | onTap: tapBoldBtn,
156 | child: ImageEditor.uiDelegate.boldTagWidget,
157 | ),
158 | 24.hGap,
159 | ImageEditor.uiDelegate.sliderLeftWidget,
160 | 8.hGap,
161 | Expanded(child: _buildSlider()),
162 | 8.hGap,
163 | ImageEditor.uiDelegate.sliderRightWidget,
164 | 2.hGap,
165 | ],
166 | ),
167 | ),
168 | //color selector
169 | Container(
170 | height: 41,
171 | padding: EdgeInsets.symmetric(horizontal: 20),
172 | width: double.infinity,
173 | //color: Colors.white,
174 | child: Row(
175 | mainAxisAlignment: MainAxisAlignment.spaceAround,
176 | children: textColorList
177 | .map((e) => CircleColorWidget(
178 | color: e,
179 | valueListenable: selectedColor,
180 | onColorSelected: (color) {
181 | setState(() {
182 | selectedColor.value = color.value;
183 | });
184 | },
185 | ))
186 | .toList(),
187 | ),
188 | ),
189 | ],
190 | ),
191 | ),
192 | ],
193 | ),
194 | ),
195 | );
196 | }
197 |
198 | Widget _buildSlider() {
199 | return SliderTheme(
200 | data: ImageEditor.uiDelegate.sliderThemeData(context),
201 | child: Slider(
202 | value: _size,
203 | max: configModel.sliderUpLimit,
204 | min: configModel.sliderBottomLimit,
205 | onChanged: (v) {
206 | _size = v;
207 | setState(() {});
208 | },
209 | ));
210 | }
211 |
212 |
213 |
214 | }
215 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "3.4.6"
12 | async:
13 | dependency: transitive
14 | description:
15 | name: async
16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.11.0"
20 | boolean_selector:
21 | dependency: transitive
22 | description:
23 | name: boolean_selector
24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "2.1.1"
28 | characters:
29 | dependency: transitive
30 | description:
31 | name: characters
32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.3.0"
36 | clock:
37 | dependency: transitive
38 | description:
39 | name: clock
40 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.1.1"
44 | collection:
45 | dependency: transitive
46 | description:
47 | name: collection
48 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.17.1"
52 | convert:
53 | dependency: transitive
54 | description:
55 | name: convert
56 | sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "3.1.1"
60 | crypto:
61 | dependency: transitive
62 | description:
63 | name: crypto
64 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "3.0.3"
68 | fake_async:
69 | dependency: transitive
70 | description:
71 | name: fake_async
72 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "1.3.1"
76 | ffi:
77 | dependency: transitive
78 | description:
79 | name: ffi
80 | sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "2.1.0"
84 | flutter:
85 | dependency: "direct main"
86 | description: flutter
87 | source: sdk
88 | version: "0.0.0"
89 | flutter_test:
90 | dependency: "direct dev"
91 | description: flutter
92 | source: sdk
93 | version: "0.0.0"
94 | image:
95 | dependency: "direct main"
96 | description:
97 | name: image
98 | sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "3.3.0"
102 | js:
103 | dependency: transitive
104 | description:
105 | name: js
106 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "0.6.7"
110 | matcher:
111 | dependency: transitive
112 | description:
113 | name: matcher
114 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "0.12.15"
118 | material_color_utilities:
119 | dependency: transitive
120 | description:
121 | name: material_color_utilities
122 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "0.2.0"
126 | meta:
127 | dependency: transitive
128 | description:
129 | name: meta
130 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "1.9.1"
134 | path:
135 | dependency: transitive
136 | description:
137 | name: path
138 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
139 | url: "https://pub.dev"
140 | source: hosted
141 | version: "1.8.3"
142 | path_provider:
143 | dependency: "direct main"
144 | description:
145 | name: path_provider
146 | sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
147 | url: "https://pub.dev"
148 | source: hosted
149 | version: "2.1.1"
150 | path_provider_android:
151 | dependency: transitive
152 | description:
153 | name: path_provider_android
154 | sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
155 | url: "https://pub.dev"
156 | source: hosted
157 | version: "2.2.1"
158 | path_provider_foundation:
159 | dependency: transitive
160 | description:
161 | name: path_provider_foundation
162 | sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
163 | url: "https://pub.dev"
164 | source: hosted
165 | version: "2.3.1"
166 | path_provider_linux:
167 | dependency: transitive
168 | description:
169 | name: path_provider_linux
170 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
171 | url: "https://pub.dev"
172 | source: hosted
173 | version: "2.2.1"
174 | path_provider_platform_interface:
175 | dependency: transitive
176 | description:
177 | name: path_provider_platform_interface
178 | sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
179 | url: "https://pub.dev"
180 | source: hosted
181 | version: "2.1.1"
182 | path_provider_windows:
183 | dependency: transitive
184 | description:
185 | name: path_provider_windows
186 | sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
187 | url: "https://pub.dev"
188 | source: hosted
189 | version: "2.2.1"
190 | petitparser:
191 | dependency: transitive
192 | description:
193 | name: petitparser
194 | sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
195 | url: "https://pub.dev"
196 | source: hosted
197 | version: "5.4.0"
198 | platform:
199 | dependency: transitive
200 | description:
201 | name: platform
202 | sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
203 | url: "https://pub.dev"
204 | source: hosted
205 | version: "3.1.3"
206 | plugin_platform_interface:
207 | dependency: transitive
208 | description:
209 | name: plugin_platform_interface
210 | sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
211 | url: "https://pub.dev"
212 | source: hosted
213 | version: "2.1.6"
214 | pointycastle:
215 | dependency: transitive
216 | description:
217 | name: pointycastle
218 | sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
219 | url: "https://pub.dev"
220 | source: hosted
221 | version: "3.7.3"
222 | screenshot:
223 | dependency: "direct main"
224 | description:
225 | name: screenshot
226 | sha256: "455284ff1f5b911d94a43c25e1385485cf6b4f288293eba68f15dad711c7b81c"
227 | url: "https://pub.dev"
228 | source: hosted
229 | version: "2.1.0"
230 | sky_engine:
231 | dependency: transitive
232 | description: flutter
233 | source: sdk
234 | version: "0.0.99"
235 | source_span:
236 | dependency: transitive
237 | description:
238 | name: source_span
239 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
240 | url: "https://pub.dev"
241 | source: hosted
242 | version: "1.9.1"
243 | stack_trace:
244 | dependency: transitive
245 | description:
246 | name: stack_trace
247 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
248 | url: "https://pub.dev"
249 | source: hosted
250 | version: "1.11.0"
251 | stream_channel:
252 | dependency: transitive
253 | description:
254 | name: stream_channel
255 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
256 | url: "https://pub.dev"
257 | source: hosted
258 | version: "2.1.1"
259 | string_scanner:
260 | dependency: transitive
261 | description:
262 | name: string_scanner
263 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
264 | url: "https://pub.dev"
265 | source: hosted
266 | version: "1.2.0"
267 | term_glyph:
268 | dependency: transitive
269 | description:
270 | name: term_glyph
271 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
272 | url: "https://pub.dev"
273 | source: hosted
274 | version: "1.2.1"
275 | test_api:
276 | dependency: transitive
277 | description:
278 | name: test_api
279 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
280 | url: "https://pub.dev"
281 | source: hosted
282 | version: "0.5.1"
283 | typed_data:
284 | dependency: transitive
285 | description:
286 | name: typed_data
287 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
288 | url: "https://pub.dev"
289 | source: hosted
290 | version: "1.3.2"
291 | vector_math:
292 | dependency: transitive
293 | description:
294 | name: vector_math
295 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
296 | url: "https://pub.dev"
297 | source: hosted
298 | version: "2.1.4"
299 | win32:
300 | dependency: transitive
301 | description:
302 | name: win32
303 | sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
304 | url: "https://pub.dev"
305 | source: hosted
306 | version: "5.0.9"
307 | xdg_directories:
308 | dependency: transitive
309 | description:
310 | name: xdg_directories
311 | sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
312 | url: "https://pub.dev"
313 | source: hosted
314 | version: "1.0.3"
315 | xml:
316 | dependency: transitive
317 | description:
318 | name: xml
319 | sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
320 | url: "https://pub.dev"
321 | source: hosted
322 | version: "6.3.0"
323 | sdks:
324 | dart: ">=3.0.0 <4.0.0"
325 | flutter: ">=3.10.0"
326 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: image_editor_dove
2 | description: An image editor with crop, scribble, mosaic, add-text, flip, rotated functions.
3 | version: 0.0.3
4 | homepage: https://github.com/bladeofgod/flutter_image_editor
5 |
6 | environment:
7 | sdk: '>=2.18.0 <4.0.0'
8 | flutter: ">=3.7.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | path_provider: ^2.1.1
15 | image: ^3.0.0
16 | screenshot: ^2.1.0
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 |
22 | # For information on the generic Dart part of this file, see the
23 | # following page: https://dart.dev/tools/pub/pubspec
24 |
25 | # The following section is specific to Flutter.
26 | flutter:
27 | # This section identifies this Flutter project as a plugin project.
28 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily
29 | # be modified. They are used by the tooling to maintain consistency when
30 | # adding or updating assets for this project.
31 | plugin:
32 | platforms:
33 | android:
34 | package: com.lijiaqi.image_editor
35 | pluginClass: ImageEditorPlugin
36 | ios:
37 | pluginClass: ImageEditorPlugin
38 |
39 | # To add assets to your plugin package, add an assets section, like this:
40 | # assets:
41 | # - images/a_dot_burr.jpeg
42 | # - images/a_dot_ham.jpeg
43 | #
44 | # For details regarding assets in packages, see
45 | # https://flutter.dev/assets-and-images/#from-packages
46 | #
47 | # An image asset can refer to one or more resolution-specific "variants", see
48 | # https://flutter.dev/assets-and-images/#resolution-aware.
49 |
50 | # To add custom fonts to your plugin package, add a fonts section here,
51 | # in this "flutter" section. Each entry in this list should have a
52 | # "family" key with the font family name, and a "fonts" key with a
53 | # list giving the asset and other descriptors for the font. For
54 | # example:
55 | # fonts:
56 | # - family: Schyler
57 | # fonts:
58 | # - asset: fonts/Schyler-Regular.ttf
59 | # - asset: fonts/Schyler-Italic.ttf
60 | # style: italic
61 | # - family: Trajan Pro
62 | # fonts:
63 | # - asset: fonts/TrajanPro.ttf
64 | # - asset: fonts/TrajanPro_Bold.ttf
65 | # weight: 700
66 | #
67 | # For details regarding fonts in packages, see
68 | # https://flutter.dev/custom-fonts/#from-packages
69 |
--------------------------------------------------------------------------------
/test/image_editor_test.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter_test/flutter_test.dart';
3 |
4 |
5 | void main() {
6 | //const MethodChannel channel = MethodChannel('image_editor');
7 |
8 | TestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | setUp(() {
11 | // channel.setMockMethodCallHandler((MethodCall methodCall) async {
12 | // return '42';
13 | // });
14 | });
15 |
16 | tearDown(() {
17 | //channel.setMockMethodCallHandler(null);
18 | });
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------