├── .gitignore
├── .metadata
├── .vscode
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc
└── README_ZH.md
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── 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
│ │ │ │ └── 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
│ ├── Podfile
│ ├── Podfile.lock
│ ├── 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
│ ├── crop_index.dart
│ ├── image_result_page.dart
│ ├── index.dart
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── gif
└── 5e941f8d-39a2-45da-9c8c-9eb2f6513498.gif
├── lib
├── crop_box.dart
└── src
│ ├── box_border.dart
│ ├── grid_line.dart
│ ├── image_crop.dart
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── crop_box_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | build/
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Flutter.podspec
62 | **/ios/Flutter/Generated.xcconfig
63 | **/ios/Flutter/app.flx
64 | **/ios/Flutter/app.zip
65 | **/ios/Flutter/flutter_assets/
66 | **/ios/Flutter/flutter_export_environment.sh
67 | **/ios/ServiceDefinitions.json
68 | **/ios/Runner/GeneratedPluginRegistrant.*
69 |
70 | # Exceptions to above rules.
71 | !**/ios/**/default.mode1v3
72 | !**/ios/**/default.mode2v3
73 | !**/ios/**/default.pbxuser
74 | !**/ios/**/default.perspectivev3
75 |
--------------------------------------------------------------------------------
/.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: f30b7f4db93ee747cd727df747941a28ead25ff5
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "crop_box",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "example",
14 | "cwd": "example",
15 | "request": "launch",
16 | "type": "dart"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.1.0] - 2021-01-13.
2 |
3 | * Release new package, support all kinds of material cutting operation and region calculation.
4 |
5 | ## [0.1.1] - 2021-02-03.
6 |
7 | * update doc.
8 |
9 | ## [0.1.2] - 2021-03-16.
10 |
11 | * add Circle CropBox. Now you can use `cropBoxType: CropBoxType.Circle` to change the UI of CropBox,And the return params is also `Rect` type;
12 |
13 | ## [0.1.3] - 2021-03-22.
14 |
15 | * add `borderRadius` `borderWidth` and `needInnerBorder`;
16 |
17 | ## [0.1.4] - 2021-03-23.
18 |
19 | * add `gridLine` property;
20 |
21 | ## [0.1.5] - 2021-03-24.
22 |
23 | * add `CropBoxBorder cropBoxBorder` property, remove `borderColor` `borderRadius` `borderWidth` properties;
24 |
25 | ## [0.1.6] - 2021-03-26.
26 |
27 | * add `backgroundColor` property;
28 |
29 | ## [0.1.7] - 2021-03-26.
30 |
31 | * change mask color to `rgba(0,0,0,0.5)`;
32 |
33 | ## [0.1.8] - 2021-04-03.
34 |
35 | * add `ImageCrop.getResult` to get real image crop result Uint8List;
36 |
37 | ## [0.1.9] - 2021-04-12.
38 |
39 | * add `ImageCrop.getImageSize` to get real image size;
40 |
41 | ## [0.1.10] - 2021-04-25.
42 |
43 | * bug fix;
44 |
45 | ## [0.1.13] - 2021-04-25.
46 |
47 | * add `quality` and `size` to `ImageCrop.getResult`;
48 |
49 | ## [1.0.1] - 2021-05-08.
50 |
51 | * add `quality` and `size` to `ImageCrop.getResult`;
52 |
53 | ## [1.0.2] - 2021-06-02.
54 |
55 | * add `maskColor`;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 godaangel
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
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > Background: why develop this component? At present, video synthesis technology is used in our flitter app, which involves the clipping of video or picture materials. At present, the common components in the market are based on pictures, and basically use canvas for rendering and clipping, which does not meet our business needs, so we need to develop a clipping component ourselves.
2 |
3 | [中文文档](https://github.com/godaangel/flutter_crop_box/blob/master/doc/README_ZH.md)
4 |
5 | ## Demo
6 |
7 | 
8 |
9 | ## Demand analysis
10 |
11 | At the beginning of requirement design, considering the habit of gesture and referring to most editing tools, the following requirements are defined:
12 |
13 | - The clipping Box **is fixed** in a position on the screen. The position and size of the material can be adjusted by **dragging with one finger and zooming with two fingers** to frame the clipping range
14 |
15 | - The minimum edge of the material cannot be less than the corresponding edge of the clipping box, that is, the clipping Box **can only move relatively within the range of the material**
16 |
17 | - The types of supporting materials include **pictures and videos**
18 |
19 | ## Parameter
20 |
21 | | name | type | desc | default |
22 | | --- | --- | --- | --- |
23 | | cropRect | Rect | If you do not fill in the initial clipping region, it will be filled and centered by default, which is similar to cover | - |
24 | | clipSize | Size | Size of material to be cut | Required |
25 | | cropRatio | Size | Crop box scale, default`16:9` | `Size(16, 9)` |
26 | | child | Widget | Material to be cut | Required |
27 | | maxCropSize | Size | The maximum width and height of the current scale of the clipping box is mainly used when the size of the clipping box needs to be adjusted actively. If there is no special requirement, it does not need to be configured | Calculate based on parent component |
28 | | maxScale | Double | Maximum size allowed to enlarge | `10.0` |
29 | | needInnerBorder | bool | Whether the inner border decoration is required in the square mode (if there are rounded corners, it will not be displayed) | `false` |
30 | | cropBoxType | CropBoxType | Crop box style, default square, can be changed to circle. If it is a circle, the value set by `CropRatio` will be invalid and forced to `1:1` | CropBoxType.Square |
31 | | gridLine | GridLine | Clipping gridlines | - |
32 | | backgroundColor | Color | Background Color | `Color(0xff141414)` |
33 | | maskColor | Color | Mask Color | `Color.fromRGBO(0, 0, 0, 0.5)` |
34 | | cropBoxBorder | CropBoxBorder | Crop box style, including color, width and rounded corner information | `cropBoxBorder.width = 2` `cropBoxBorder.color = Colors.white` `cropBoxBorder.radius = Radius.circular(0)` |
35 | | cropRectUpdateStart | Function | Callback when crop region begins to change | - |
36 | | cropRectUpdate | Function(Rect rect) | Callback when clipping region changes | - |
37 | | cropRectUpdateEnd | Function(Rect rect) | Callback when clipping region end | Required |
38 |
39 | ## Demo code
40 |
41 | > Can see `example` in github
42 |
43 | #### git
44 | ```yaml
45 | crop_box:
46 | git:
47 | url: https://github.com/godaangel/flutter_crop_box.git
48 | ```
49 |
50 | #### pub.dev
51 | ```yaml
52 | crop_box: ^0.1.5
53 | ```
54 |
55 | #### Code
56 | ```dart
57 | import 'package:crop_box/crop_box.dart';
58 |
59 | // ...
60 |
61 | CropBox(
62 | // cropRect: Rect.fromLTRB(1 - 0.4083, 0.162, 1, 0.3078), // 2.4倍 随机位置
63 | // cropRect: Rect.fromLTRB(0, 0, 0.4083, 0.1457), //2.4倍,都是0,0
64 | cropRect: Rect.fromLTRB(0, 0, 1, 0.3572), // 1倍
65 | clipSize: Size(200, 315),
66 | cropRatio: Size(16, 9),
67 | cropRectUpdateEnd: (rect) {
68 | print("rect final $rect");
69 | },
70 | cropRectUpdate: (rect) {
71 | print("rect change $rect");
72 | },
73 | child: Image.network(
74 | "https://img1.maka.im/materialStore/beijingshejia/tupianbeijinga/9/M_7TNT6NIM/M_7TNT6NIM_v1.jpg",
75 | width: double.infinity,
76 | height: double.infinity,
77 | fit: BoxFit.cover,
78 | loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent loadingProgress) {
79 | if (loadingProgress == null)
80 | return child;
81 | return Center(
82 | child: CircularProgressIndicator(
83 | value: loadingProgress.expectedTotalBytes != null
84 | ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
85 | : null,
86 | ),
87 | );
88 | },
89 | ),
90 | )
91 | ```
92 |
93 | ### Real Image Crop
94 |
95 | You can Use `ImageCrop.getResult` to get real image bytes.
96 |
97 | > more details in `example`
98 |
99 | ```dart
100 | /// get origin image uint8List
101 | Uint8List bytes = (await NetworkAssetBundle(Uri.parse(imageUrl))
102 | .load(imageUrl))
103 | .buffer
104 | .asUint8List();
105 | /// get result uint8List
106 | Uint8List result = await ImageCrop.getResult(
107 | clipRect: _resultRect,
108 | image: bytes
109 | );
110 | ```
111 |
112 | ## TODO
113 |
114 | * [x] Dynamically transform crop box scale
115 |
116 | * [x] Support circle clipping box drawing
117 |
118 | * [ ] Optimize boundary calculation code
119 |
120 | * [x] Support the drawing of fillet clipping box
121 |
122 | * [ ] Support rotation
123 |
124 | * [x] Support Gridlines
125 |
126 | * [x] Real image crop to Uint8List
127 |
--------------------------------------------------------------------------------
/doc/README_ZH.md:
--------------------------------------------------------------------------------
1 | > 背景:为啥要开发这个组件呢?因为目前在我们的flutter app中,用到了视频合成技术,这里就涉及到视频或者图片素材的裁剪,目前市面上普遍的组件都是基于图片的,并且基本上都是使用canvas进行渲染和裁剪,不太符合我们的业务需求,所以要自己开发一个裁剪组件。
2 |
3 | ## 效果展示
4 | 
5 |
6 | ## 需求分析
7 | 在移动端,基本都是用手势操作,所以在需求设计之初,就考虑到手势的习惯,以及参考大部分编辑工具,定义出了以下几个需求点:
8 | - 裁剪框**固定**在屏幕上的一个位置,通过**单指拖动,双指缩放**的形式调整素材位置和大小,来框定裁剪范围
9 | - 素材的最小边不能小于裁剪框上与其对应的边,即裁剪框**只能相对在素材范围内移动**
10 | - 支持素材的类型包含**图片和视频**
11 |
12 | ## 参数设计
13 |
14 | | 参数名 | 类型 | 描述 | 默认值 |
15 | | --- | --- | --- | --- |
16 | | cropRect | Rect | 初始裁剪区域,如果不填,默认会填充并居中,表现形式类似cover | - |
17 | | clipSize | Size | 待裁剪素材的尺寸 | 必填 |
18 | | cropRatio | Size | 裁剪框比例,默认`16:9` | `Size(16, 9)` |
19 | | child | Widget | 待裁剪素材 | 必填 |
20 | | maxCropSize | Size | 裁剪框当前比例下最大宽高,主要是用于需要主动调整裁剪框大小时使用 如果没有特殊需求,不需要配置 | 根据父组件计算 |
21 | | maxScale | Double | 允许放大的最大尺寸 | `10.0` |
22 | | needInnerBorder | bool | 方形模式下是否需要内边框修饰(如果有圆角,则不显示) | `false` |
23 | | cropBoxType | CropBoxType | 裁剪框样式,默认方形,可以换成圆形,如果是圆形,则`cropRatio`设置的值将失效,强制变为`1:1` | CropBoxType.Square |
24 | | gridLine | GridLine | 裁剪网格线 | - |
25 | | backgroundColor | Color | 背景色 | `Color(0xff141414)` |
26 | | maskColor | Color | 遮罩颜色 | `Color.fromRGBO(0, 0, 0, 0.5)` |
27 | | cropBoxBorder | CropBoxBorder | 裁剪框样式,包含颜色、宽度和圆角信息 | `cropBoxBorder.width = 2` `cropBoxBorder.color = Colors.white` `cropBoxBorder.radius = Radius.circular(0)` |
28 | | cropRectUpdateStart | Function | 裁剪区域开始变化时的回调 | - |
29 | | cropRectUpdate | Function(Rect rect) | 裁剪区域变化时的回调 | - |
30 | | cropRectUpdateEnd | Function(Rect rect) | 返回 | 必填 |
31 |
32 | ## 使用Demo
33 | > 可参考 `git` 的 `example`,可以直接运行
34 |
35 | #### git引入
36 | ```yaml
37 | crop_box:
38 | git:
39 | url: https://github.com/godaangel/flutter_crop_box.git
40 | ```
41 |
42 | #### pub.dev引入
43 | ```yaml
44 | crop_box: ^0.1.6
45 | ```
46 |
47 | #### 代码
48 | > 更多属性参考`example`代码
49 | ```dart
50 | import 'package:crop_box/crop_box.dart';
51 |
52 | // ...
53 |
54 | CropBox(
55 | // cropRect: Rect.fromLTRB(1 - 0.4083, 0.162, 1, 0.3078), // 2.4倍 随机位置
56 | // cropRect: Rect.fromLTRB(0, 0, 0.4083, 0.1457), //2.4倍,都是0,0
57 | // cropBoxType: CropBoxType.Circle,
58 | cropRect: Rect.fromLTRB(0, 0, 1, 0.3572), // 1倍
59 | clipSize: Size(200, 315),
60 | cropRatio: Size(16, 9),
61 | cropRectUpdateEnd: (rect) {
62 | print("裁剪区域最终确定 $rect");
63 | },
64 | cropRectUpdate: (rect) {
65 | print("裁剪区域变化 $rect");
66 | },
67 | child: Image.network(
68 | "https://img1.maka.im/materialStore/beijingshejia/tupianbeijinga/9/M_7TNT6NIM/M_7TNT6NIM_v1.jpg",
69 | width: double.infinity,
70 | height: double.infinity,
71 | fit: BoxFit.cover,
72 | loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent loadingProgress) {
73 | if (loadingProgress == null)
74 | return child;
75 | return Center(
76 | child: CircularProgressIndicator(
77 | value: loadingProgress.expectedTotalBytes != null
78 | ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
79 | : null,
80 | ),
81 | );
82 | },
83 | ),
84 | )
85 | ```
86 |
87 | ### 导出图片
88 |
89 | 可以使用 `ImageCrop.getResult` 获取裁剪后图片的数据,并且使用这些数据去展示或导出图片
90 |
91 | > more details in `example`
92 |
93 | ```dart
94 | /// get origin image uint8List
95 | Uint8List bytes = (await NetworkAssetBundle(Uri.parse(imageUrl))
96 | .load(imageUrl))
97 | .buffer
98 | .asUint8List();
99 | /// get result uint8List
100 | Uint8List result = await ImageCrop.getResult(
101 | clipRect: _resultRect,
102 | image: bytes
103 | );
104 | ```
105 |
106 | ## TODO
107 |
108 | * [x] 动态变换裁剪框比例
109 | * [x] 支持圆形裁剪框绘制
110 | * [ ] 优化边界计算代码
111 | * [x] 支持圆角裁剪框绘制
112 | * [ ] 支持旋转
113 | * [x] 支持网格线
114 | * [x] 支持导出图片
115 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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: f30b7f4db93ee747cd727df747941a28ead25ff5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/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 plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 29
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.example.example"
42 | minSdkVersion 16
43 | targetSdkVersion 29
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | }
47 |
48 | buildTypes {
49 | release {
50 | // TODO: Add your own signing config for the release build.
51 | // Signing with the debug keys for now, so `flutter run --release` works.
52 | signingConfig signingConfigs.debug
53 | }
54 | }
55 | }
56 |
57 | flutter {
58 | source '../..'
59 | }
60 |
61 | dependencies {
62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
63 | }
64 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
23 |
27 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
6 | systemProp.http.proxyHost=127.0.0.1
7 | systemProp.http.proxyPort=12639
8 | systemProp.https.proxyHost=127.0.0.1
9 | systemProp.https.proxyPort=12639
10 |
--------------------------------------------------------------------------------
/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-5.6.2-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/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - image_editor (0.0.1):
4 | - Flutter
5 | - image_gallery_saver (1.5.0):
6 | - Flutter
7 | - image_picker (0.0.1):
8 | - Flutter
9 |
10 | DEPENDENCIES:
11 | - Flutter (from `Flutter`)
12 | - image_editor (from `.symlinks/plugins/image_editor/ios`)
13 | - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
14 | - image_picker (from `.symlinks/plugins/image_picker/ios`)
15 |
16 | EXTERNAL SOURCES:
17 | Flutter:
18 | :path: Flutter
19 | image_editor:
20 | :path: ".symlinks/plugins/image_editor/ios"
21 | image_gallery_saver:
22 | :path: ".symlinks/plugins/image_gallery_saver/ios"
23 | image_picker:
24 | :path: ".symlinks/plugins/image_picker/ios"
25 |
26 | SPEC CHECKSUMS:
27 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
28 | image_editor: c1d038630eedea60d2dee9c14f36aa66c7f9cfab
29 | image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
30 | image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
31 |
32 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
33 |
34 | COCOAPODS: 1.10.1
35 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 51;
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 | F703CDBC5EA7BFA0A05AC082 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A951B5BC79AC4933DE38E18 /* Pods_Runner.framework */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXCopyFilesBuildPhase section */
20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
21 | isa = PBXCopyFilesBuildPhase;
22 | buildActionMask = 2147483647;
23 | dstPath = "";
24 | dstSubfolderSpec = 10;
25 | files = (
26 | );
27 | name = "Embed Frameworks";
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXCopyFilesBuildPhase section */
31 |
32 | /* Begin PBXFileReference section */
33 | 0A951B5BC79AC4933DE38E18 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
39 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
40 | 8DD009DC31863A0A5163208F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 9B5B2E376CDF7C30C1A9C31B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
49 | D011E50C08F5969127A22930 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | F703CDBC5EA7BFA0A05AC082 /* Pods_Runner.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 8DBC9F4CB7F9A2E29EB936F8 /* Frameworks */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 0A951B5BC79AC4933DE38E18 /* Pods_Runner.framework */,
68 | );
69 | name = Frameworks;
70 | sourceTree = "";
71 | };
72 | 9740EEB11CF90186004384FC /* Flutter */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
76 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
77 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
78 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
79 | );
80 | name = Flutter;
81 | sourceTree = "";
82 | };
83 | 97C146E51CF9000F007C117D = {
84 | isa = PBXGroup;
85 | children = (
86 | 9740EEB11CF90186004384FC /* Flutter */,
87 | 97C146F01CF9000F007C117D /* Runner */,
88 | 97C146EF1CF9000F007C117D /* Products */,
89 | DA82EE1EFC12646F4F975A61 /* Pods */,
90 | 8DBC9F4CB7F9A2E29EB936F8 /* Frameworks */,
91 | );
92 | sourceTree = "";
93 | };
94 | 97C146EF1CF9000F007C117D /* Products */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 97C146EE1CF9000F007C117D /* Runner.app */,
98 | );
99 | name = Products;
100 | sourceTree = "";
101 | };
102 | 97C146F01CF9000F007C117D /* Runner */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
106 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
107 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
108 | 97C147021CF9000F007C117D /* Info.plist */,
109 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
110 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
111 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
112 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
113 | );
114 | path = Runner;
115 | sourceTree = "";
116 | };
117 | DA82EE1EFC12646F4F975A61 /* Pods */ = {
118 | isa = PBXGroup;
119 | children = (
120 | D011E50C08F5969127A22930 /* Pods-Runner.debug.xcconfig */,
121 | 9B5B2E376CDF7C30C1A9C31B /* Pods-Runner.release.xcconfig */,
122 | 8DD009DC31863A0A5163208F /* Pods-Runner.profile.xcconfig */,
123 | );
124 | path = Pods;
125 | sourceTree = "";
126 | };
127 | /* End PBXGroup section */
128 |
129 | /* Begin PBXNativeTarget section */
130 | 97C146ED1CF9000F007C117D /* Runner */ = {
131 | isa = PBXNativeTarget;
132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
133 | buildPhases = (
134 | E297A4F05395FB4017928359 /* [CP] Check Pods Manifest.lock */,
135 | 9740EEB61CF901F6004384FC /* Run Script */,
136 | 97C146EA1CF9000F007C117D /* Sources */,
137 | 97C146EB1CF9000F007C117D /* Frameworks */,
138 | 97C146EC1CF9000F007C117D /* Resources */,
139 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
141 | EF8D9232D306D061B748AB39 /* [CP] Embed Pods Frameworks */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | );
147 | name = Runner;
148 | productName = Runner;
149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
150 | productType = "com.apple.product-type.application";
151 | };
152 | /* End PBXNativeTarget section */
153 |
154 | /* Begin PBXProject section */
155 | 97C146E61CF9000F007C117D /* Project object */ = {
156 | isa = PBXProject;
157 | attributes = {
158 | LastUpgradeCheck = 1020;
159 | ORGANIZATIONNAME = "";
160 | TargetAttributes = {
161 | 97C146ED1CF9000F007C117D = {
162 | CreatedOnToolsVersion = 7.3.1;
163 | LastSwiftMigration = 1100;
164 | };
165 | };
166 | };
167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
168 | compatibilityVersion = "Xcode 9.3";
169 | developmentRegion = en;
170 | hasScannedForEncodings = 0;
171 | knownRegions = (
172 | en,
173 | Base,
174 | );
175 | mainGroup = 97C146E51CF9000F007C117D;
176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
177 | projectDirPath = "";
178 | projectRoot = "";
179 | targets = (
180 | 97C146ED1CF9000F007C117D /* Runner */,
181 | );
182 | };
183 | /* End PBXProject section */
184 |
185 | /* Begin PBXResourcesBuildPhase section */
186 | 97C146EC1CF9000F007C117D /* Resources */ = {
187 | isa = PBXResourcesBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
191 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
193 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | /* End PBXResourcesBuildPhase section */
198 |
199 | /* Begin PBXShellScriptBuildPhase section */
200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
201 | isa = PBXShellScriptBuildPhase;
202 | buildActionMask = 2147483647;
203 | files = (
204 | );
205 | inputPaths = (
206 | );
207 | name = "Thin Binary";
208 | outputPaths = (
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | shellPath = /bin/sh;
212 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
213 | };
214 | 9740EEB61CF901F6004384FC /* Run Script */ = {
215 | isa = PBXShellScriptBuildPhase;
216 | buildActionMask = 2147483647;
217 | files = (
218 | );
219 | inputPaths = (
220 | );
221 | name = "Run Script";
222 | outputPaths = (
223 | );
224 | runOnlyForDeploymentPostprocessing = 0;
225 | shellPath = /bin/sh;
226 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
227 | };
228 | E297A4F05395FB4017928359 /* [CP] Check Pods Manifest.lock */ = {
229 | isa = PBXShellScriptBuildPhase;
230 | buildActionMask = 2147483647;
231 | files = (
232 | );
233 | inputFileListPaths = (
234 | );
235 | inputPaths = (
236 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
237 | "${PODS_ROOT}/Manifest.lock",
238 | );
239 | name = "[CP] Check Pods Manifest.lock";
240 | outputFileListPaths = (
241 | );
242 | outputPaths = (
243 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | shellPath = /bin/sh;
247 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
248 | showEnvVarsInLog = 0;
249 | };
250 | EF8D9232D306D061B748AB39 /* [CP] Embed Pods Frameworks */ = {
251 | isa = PBXShellScriptBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | inputFileListPaths = (
256 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
257 | );
258 | name = "[CP] Embed Pods Frameworks";
259 | outputFileListPaths = (
260 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
261 | );
262 | runOnlyForDeploymentPostprocessing = 0;
263 | shellPath = /bin/sh;
264 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
265 | showEnvVarsInLog = 0;
266 | };
267 | /* End PBXShellScriptBuildPhase section */
268 |
269 | /* Begin PBXSourcesBuildPhase section */
270 | 97C146EA1CF9000F007C117D /* Sources */ = {
271 | isa = PBXSourcesBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
275 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
276 | );
277 | runOnlyForDeploymentPostprocessing = 0;
278 | };
279 | /* End PBXSourcesBuildPhase section */
280 |
281 | /* Begin PBXVariantGroup section */
282 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
283 | isa = PBXVariantGroup;
284 | children = (
285 | 97C146FB1CF9000F007C117D /* Base */,
286 | );
287 | name = Main.storyboard;
288 | sourceTree = "";
289 | };
290 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
291 | isa = PBXVariantGroup;
292 | children = (
293 | 97C147001CF9000F007C117D /* Base */,
294 | );
295 | name = LaunchScreen.storyboard;
296 | sourceTree = "";
297 | };
298 | /* End PBXVariantGroup section */
299 |
300 | /* Begin XCBuildConfiguration section */
301 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ALWAYS_SEARCH_USER_PATHS = NO;
305 | CLANG_ANALYZER_NONNULL = YES;
306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
307 | CLANG_CXX_LIBRARY = "libc++";
308 | CLANG_ENABLE_MODULES = YES;
309 | CLANG_ENABLE_OBJC_ARC = YES;
310 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
311 | CLANG_WARN_BOOL_CONVERSION = YES;
312 | CLANG_WARN_COMMA = YES;
313 | CLANG_WARN_CONSTANT_CONVERSION = YES;
314 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
316 | CLANG_WARN_EMPTY_BODY = YES;
317 | CLANG_WARN_ENUM_CONVERSION = YES;
318 | CLANG_WARN_INFINITE_RECURSION = YES;
319 | CLANG_WARN_INT_CONVERSION = YES;
320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
325 | CLANG_WARN_STRICT_PROTOTYPES = YES;
326 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
327 | CLANG_WARN_UNREACHABLE_CODE = YES;
328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
330 | COPY_PHASE_STRIP = NO;
331 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
332 | ENABLE_NS_ASSERTIONS = NO;
333 | ENABLE_STRICT_OBJC_MSGSEND = YES;
334 | GCC_C_LANGUAGE_STANDARD = gnu99;
335 | GCC_NO_COMMON_BLOCKS = YES;
336 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
337 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
338 | GCC_WARN_UNDECLARED_SELECTOR = YES;
339 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
340 | GCC_WARN_UNUSED_FUNCTION = YES;
341 | GCC_WARN_UNUSED_VARIABLE = YES;
342 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
343 | MTL_ENABLE_DEBUG_INFO = NO;
344 | SDKROOT = iphoneos;
345 | SUPPORTED_PLATFORMS = iphoneos;
346 | TARGETED_DEVICE_FAMILY = "1,2";
347 | VALIDATE_PRODUCT = YES;
348 | };
349 | name = Profile;
350 | };
351 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
352 | isa = XCBuildConfiguration;
353 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
354 | buildSettings = {
355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
356 | CLANG_ENABLE_MODULES = YES;
357 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
358 | ENABLE_BITCODE = NO;
359 | FRAMEWORK_SEARCH_PATHS = (
360 | "$(inherited)",
361 | "$(PROJECT_DIR)/Flutter",
362 | );
363 | INFOPLIST_FILE = Runner/Info.plist;
364 | LD_RUNPATH_SEARCH_PATHS = (
365 | "$(inherited)",
366 | "@executable_path/Frameworks",
367 | );
368 | LIBRARY_SEARCH_PATHS = (
369 | "$(inherited)",
370 | "$(PROJECT_DIR)/Flutter",
371 | );
372 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
373 | PRODUCT_NAME = "$(TARGET_NAME)";
374 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
375 | SWIFT_VERSION = 5.0;
376 | VERSIONING_SYSTEM = "apple-generic";
377 | };
378 | name = Profile;
379 | };
380 | 97C147031CF9000F007C117D /* Debug */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | ALWAYS_SEARCH_USER_PATHS = NO;
384 | CLANG_ANALYZER_NONNULL = YES;
385 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
386 | CLANG_CXX_LIBRARY = "libc++";
387 | CLANG_ENABLE_MODULES = YES;
388 | CLANG_ENABLE_OBJC_ARC = YES;
389 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
390 | CLANG_WARN_BOOL_CONVERSION = YES;
391 | CLANG_WARN_COMMA = YES;
392 | CLANG_WARN_CONSTANT_CONVERSION = YES;
393 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
394 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
395 | CLANG_WARN_EMPTY_BODY = YES;
396 | CLANG_WARN_ENUM_CONVERSION = YES;
397 | CLANG_WARN_INFINITE_RECURSION = YES;
398 | CLANG_WARN_INT_CONVERSION = YES;
399 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
400 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
401 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
402 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
403 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
404 | CLANG_WARN_STRICT_PROTOTYPES = YES;
405 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
406 | CLANG_WARN_UNREACHABLE_CODE = YES;
407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
408 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
409 | COPY_PHASE_STRIP = NO;
410 | DEBUG_INFORMATION_FORMAT = dwarf;
411 | ENABLE_STRICT_OBJC_MSGSEND = YES;
412 | ENABLE_TESTABILITY = YES;
413 | GCC_C_LANGUAGE_STANDARD = gnu99;
414 | GCC_DYNAMIC_NO_PIC = NO;
415 | GCC_NO_COMMON_BLOCKS = YES;
416 | GCC_OPTIMIZATION_LEVEL = 0;
417 | GCC_PREPROCESSOR_DEFINITIONS = (
418 | "DEBUG=1",
419 | "$(inherited)",
420 | );
421 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
422 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
423 | GCC_WARN_UNDECLARED_SELECTOR = YES;
424 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
425 | GCC_WARN_UNUSED_FUNCTION = YES;
426 | GCC_WARN_UNUSED_VARIABLE = YES;
427 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
428 | MTL_ENABLE_DEBUG_INFO = YES;
429 | ONLY_ACTIVE_ARCH = YES;
430 | SDKROOT = iphoneos;
431 | TARGETED_DEVICE_FAMILY = "1,2";
432 | };
433 | name = Debug;
434 | };
435 | 97C147041CF9000F007C117D /* Release */ = {
436 | isa = XCBuildConfiguration;
437 | buildSettings = {
438 | ALWAYS_SEARCH_USER_PATHS = NO;
439 | CLANG_ANALYZER_NONNULL = YES;
440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
441 | CLANG_CXX_LIBRARY = "libc++";
442 | CLANG_ENABLE_MODULES = YES;
443 | CLANG_ENABLE_OBJC_ARC = YES;
444 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
445 | CLANG_WARN_BOOL_CONVERSION = YES;
446 | CLANG_WARN_COMMA = YES;
447 | CLANG_WARN_CONSTANT_CONVERSION = YES;
448 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
450 | CLANG_WARN_EMPTY_BODY = YES;
451 | CLANG_WARN_ENUM_CONVERSION = YES;
452 | CLANG_WARN_INFINITE_RECURSION = YES;
453 | CLANG_WARN_INT_CONVERSION = YES;
454 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
455 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
456 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
457 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
458 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
459 | CLANG_WARN_STRICT_PROTOTYPES = YES;
460 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
461 | CLANG_WARN_UNREACHABLE_CODE = YES;
462 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
463 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
464 | COPY_PHASE_STRIP = NO;
465 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
466 | ENABLE_NS_ASSERTIONS = NO;
467 | ENABLE_STRICT_OBJC_MSGSEND = YES;
468 | GCC_C_LANGUAGE_STANDARD = gnu99;
469 | GCC_NO_COMMON_BLOCKS = YES;
470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
472 | GCC_WARN_UNDECLARED_SELECTOR = YES;
473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
474 | GCC_WARN_UNUSED_FUNCTION = YES;
475 | GCC_WARN_UNUSED_VARIABLE = YES;
476 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
477 | MTL_ENABLE_DEBUG_INFO = NO;
478 | SDKROOT = iphoneos;
479 | SUPPORTED_PLATFORMS = iphoneos;
480 | SWIFT_COMPILATION_MODE = wholemodule;
481 | SWIFT_OPTIMIZATION_LEVEL = "-O";
482 | TARGETED_DEVICE_FAMILY = "1,2";
483 | VALIDATE_PRODUCT = YES;
484 | };
485 | name = Release;
486 | };
487 | 97C147061CF9000F007C117D /* Debug */ = {
488 | isa = XCBuildConfiguration;
489 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
490 | buildSettings = {
491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
492 | CLANG_ENABLE_MODULES = YES;
493 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
494 | DEVELOPMENT_TEAM = F3228W89AS;
495 | ENABLE_BITCODE = NO;
496 | FRAMEWORK_SEARCH_PATHS = (
497 | "$(inherited)",
498 | "$(PROJECT_DIR)/Flutter",
499 | );
500 | INFOPLIST_FILE = Runner/Info.plist;
501 | LD_RUNPATH_SEARCH_PATHS = (
502 | "$(inherited)",
503 | "@executable_path/Frameworks",
504 | );
505 | LIBRARY_SEARCH_PATHS = (
506 | "$(inherited)",
507 | "$(PROJECT_DIR)/Flutter",
508 | );
509 | PRODUCT_BUNDLE_IDENTIFIER = com.godaangel.crop.box;
510 | PRODUCT_NAME = "$(TARGET_NAME)";
511 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
512 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
513 | SWIFT_VERSION = 5.0;
514 | VERSIONING_SYSTEM = "apple-generic";
515 | };
516 | name = Debug;
517 | };
518 | 97C147071CF9000F007C117D /* Release */ = {
519 | isa = XCBuildConfiguration;
520 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
521 | buildSettings = {
522 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
523 | CLANG_ENABLE_MODULES = YES;
524 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
525 | DEVELOPMENT_TEAM = F3228W89AS;
526 | ENABLE_BITCODE = NO;
527 | FRAMEWORK_SEARCH_PATHS = (
528 | "$(inherited)",
529 | "$(PROJECT_DIR)/Flutter",
530 | );
531 | INFOPLIST_FILE = Runner/Info.plist;
532 | LD_RUNPATH_SEARCH_PATHS = (
533 | "$(inherited)",
534 | "@executable_path/Frameworks",
535 | );
536 | LIBRARY_SEARCH_PATHS = (
537 | "$(inherited)",
538 | "$(PROJECT_DIR)/Flutter",
539 | );
540 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.crop.box;
541 | PRODUCT_NAME = "$(TARGET_NAME)";
542 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
543 | SWIFT_VERSION = 5.0;
544 | VERSIONING_SYSTEM = "apple-generic";
545 | };
546 | name = Release;
547 | };
548 | /* End XCBuildConfiguration section */
549 |
550 | /* Begin XCConfigurationList section */
551 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
552 | isa = XCConfigurationList;
553 | buildConfigurations = (
554 | 97C147031CF9000F007C117D /* Debug */,
555 | 97C147041CF9000F007C117D /* Release */,
556 | 249021D3217E4FDB00AE95B9 /* Profile */,
557 | );
558 | defaultConfigurationIsVisible = 0;
559 | defaultConfigurationName = Release;
560 | };
561 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
562 | isa = XCConfigurationList;
563 | buildConfigurations = (
564 | 97C147061CF9000F007C117D /* Debug */,
565 | 97C147071CF9000F007C117D /* Release */,
566 | 249021D4217E4FDB00AE95B9 /* Profile */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | /* End XCConfigurationList section */
572 | };
573 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
574 | }
575 |
--------------------------------------------------------------------------------
/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 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/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/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | NSCameraUsageDescription
26 | App需要您的同意,才能访问相机
27 | NSMicrophoneUsageDescription
28 | App需要您的同意,才能访问麦克风
29 | NSPhotoLibraryUsageDescription
30 | App需要您的同意,才能访问相册
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UIViewControllerBasedStatusBarAppearance
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/crop_index.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:example/image_result_page.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/services.dart';
7 | import 'dart:ui';
8 | import 'package:flutter_screenutil/flutter_screenutil.dart';
9 |
10 | import 'package:crop_box/crop_box.dart';
11 | import 'package:image_picker/image_picker.dart';
12 |
13 | enum ClipType {
14 | networkImage,
15 | localImage
16 | }
17 |
18 | class CropIndex extends StatefulWidget {
19 | final double width;
20 | final double height;
21 | final ClipType clipType;
22 | final Uint8List? localImageData;
23 | final String? imageUrl;
24 | CropIndex({Key? key, required this.width, required this.height, required this.clipType, this.localImageData, this.imageUrl}) : super(key: key);
25 |
26 | @override
27 | _CropIndexState createState() => _CropIndexState();
28 | }
29 |
30 | class _CropIndexState extends State {
31 | Rect _resultRect = Rect.zero;
32 | /// 最大裁剪区域 会结合裁剪比例计算实际裁剪框范围
33 | Size _maxCropSize = Size(300, 300);
34 | /// 裁剪比例
35 | Size _cropRatio = Size(16, 9);
36 | /// 裁剪区域
37 | Rect? _cropRect;
38 |
39 | bool exportLoading = false;
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return Scaffold(
44 | appBar: AppBar(
45 | title: Text('Crop Detail'),
46 | ),
47 | body: Container(
48 | child: Column(
49 | children: [
50 | Expanded(
51 | child: CropBox(
52 | // cropRect: Rect.fromLTRB(1 - 0.4083, 0.162, 1, 0.3078), // 2.4倍 模拟随机位置
53 | // cropRect: Rect.fromLTRB(0, 0, 0.4083, 0.1457), //2.4倍,都是0,0
54 | // cropRect: Rect.fromLTRB(0, 0, 1, 0.3572), // 1倍
55 | // cropBoxType: CropBoxType.Circle,
56 | // borderColor: Colors.white,
57 | gridLine: GridLine(),
58 | cropRect: _cropRect,
59 | clipSize: Size(widget.width, widget.height),
60 | maxCropSize: _maxCropSize,
61 | cropRatio: _cropRatio,
62 | cropBoxBorder: CropBoxBorder(
63 | color: Colors.white,
64 | radius: Radius.circular(5),
65 | ),
66 | cropRectUpdateEnd: (rect) {
67 | _resultRect = rect;
68 | print("裁剪区域最终确定 $rect");
69 | setState(() {});
70 | },
71 | cropRectUpdate: (rect) {
72 | _resultRect = rect;
73 | print("裁剪区域变化 $rect");
74 | setState(() {});
75 | },
76 | child: widget.clipType == ClipType.networkImage ? Image.network(
77 | widget.imageUrl!,
78 | width: double.infinity,
79 | height: double.infinity,
80 | fit: BoxFit.contain,
81 | loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
82 | if (loadingProgress == null)
83 | return child;
84 | return Center(
85 | child: CircularProgressIndicator(
86 | value: loadingProgress.expectedTotalBytes != null
87 | ? loadingProgress.cumulativeBytesLoaded.toDouble() / loadingProgress.expectedTotalBytes!.toDouble()
88 | : null,
89 | ),
90 | );
91 | },
92 | ) : Image.memory(
93 | widget.localImageData!,
94 | width: double.infinity,
95 | height: double.infinity,
96 | fit: BoxFit.contain,
97 | ),
98 | ),
99 | ),
100 | SizedBox(
101 | height: 36.w,
102 | ),
103 | Container(
104 | padding: EdgeInsets.only(
105 | bottom: MediaQuery.of(context).padding.bottom,
106 | ),
107 | child: Container(
108 | height: 250.w,
109 | padding: EdgeInsets.all(16.w),
110 | child: Row(
111 | children: [
112 | Expanded(
113 | flex: 0,
114 | child: Container(
115 | width: 150,
116 | child: Column(
117 | crossAxisAlignment: CrossAxisAlignment.start,
118 | children: [
119 | Text(
120 | "裁剪区域: ",
121 | style: TextStyle(
122 | fontFamily: "PingFang SC",
123 | fontSize: 28.sp,
124 | fontStyle: FontStyle.normal,
125 | fontWeight: FontWeight.w500,
126 | ),
127 | ),
128 | Text(
129 | "left: ${_resultRect.left.toStringAsFixed(5)}",
130 | style: TextStyle(
131 | fontFamily: "PingFang SC",
132 | fontSize: 28.sp,
133 | fontStyle: FontStyle.normal,
134 | fontWeight: FontWeight.w500,
135 | ),
136 | ),
137 | Text(
138 | "top: ${_resultRect.top.toStringAsFixed(5)}",
139 | style: TextStyle(
140 | fontFamily: "PingFang SC",
141 | fontSize: 28.sp,
142 | fontStyle: FontStyle.normal,
143 | fontWeight: FontWeight.w500,
144 | ),
145 | ),
146 | Text(
147 | "right: ${_resultRect.right.toStringAsFixed(5)}",
148 | style: TextStyle(
149 | fontFamily: "PingFang SC",
150 | fontSize: 28.sp,
151 | fontStyle: FontStyle.normal,
152 | fontWeight: FontWeight.w500,
153 | ),
154 | ),
155 | Text(
156 | "bottom: ${_resultRect.bottom.toStringAsFixed(5)}",
157 | style: TextStyle(
158 | fontFamily: "PingFang SC",
159 | fontSize: 28.sp,
160 | fontStyle: FontStyle.normal,
161 | fontWeight: FontWeight.w500,
162 | ),
163 | ),
164 | ],
165 | ),
166 | ),
167 | ),
168 | Expanded(
169 | child: Column(
170 | children: [
171 | CupertinoButton(
172 | color: Colors.blue,
173 | child: Text(
174 | "1:1",
175 | style: TextStyle(
176 | fontFamily: "PingFang SC",
177 | fontSize: 28.sp,
178 | fontStyle: FontStyle.normal,
179 | fontWeight: FontWeight.w500,
180 | ),
181 | ),
182 | onPressed: () {
183 | setState(() {
184 | _cropRatio = Size(1, 1);
185 | });
186 | },
187 | ),
188 | SizedBox(
189 | height: 10,
190 | ),
191 | CupertinoButton(
192 | color: Colors.blue,
193 | child: Text(
194 | exportLoading ? "Exporting" : "Export",
195 | style: TextStyle(
196 | fontFamily: "PingFang SC",
197 | fontSize: 28.sp,
198 | fontStyle: FontStyle.normal,
199 | fontWeight: FontWeight.w500,
200 | ),
201 | ),
202 | onPressed: exportLoading ? null : () async {
203 | setState(() {
204 | exportLoading = true;
205 | });
206 |
207 | /// get origin image uint8List
208 | Uint8List bytes;
209 | if(widget.clipType == ClipType.networkImage) {
210 | bytes = (await NetworkAssetBundle(Uri.parse(widget.imageUrl!))
211 | .load(widget.imageUrl!))
212 | .buffer
213 | .asUint8List();
214 | }else{
215 | bytes = widget.localImageData!;
216 | }
217 | /// get result uint8List
218 | Uint8List result = (await ImageCrop.getResult(
219 | clipRect: _resultRect,
220 | image: bytes
221 | ))!;
222 |
223 | setState(() {
224 | exportLoading = false;
225 | });
226 |
227 | /// if you need to export to gallery
228 | /// you can use this https://pub.dev/packages/image_gallery_saver
229 | /// ... your export code ...
230 | ///
231 | /// my code is only to show result in other page
232 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) {
233 | return ImageResultPage(imageBytes: result,);
234 | }));
235 | },
236 | ),
237 | ],
238 | ),
239 | )
240 | ],
241 | ),
242 | ),
243 | ),
244 | ],
245 | ),
246 | ),
247 | );
248 | }
249 | }
--------------------------------------------------------------------------------
/example/lib/image_result_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 |
6 | class ImageResultPage extends StatefulWidget {
7 | final Uint8List imageBytes;
8 | ImageResultPage({Key? key, required this.imageBytes}) : super(key: key);
9 |
10 | @override
11 | _ImageResultPageState createState() => _ImageResultPageState();
12 | }
13 |
14 | class _ImageResultPageState extends State {
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | appBar: AppBar(
19 | title: Text('Result'),
20 | ),
21 | body: Center(
22 | child: Container(
23 | width: 700.w,
24 | child: Image.memory(
25 | widget.imageBytes,
26 | fit: BoxFit.contain,
27 | ),
28 | ),
29 | ),
30 | );
31 | }
32 | }
--------------------------------------------------------------------------------
/example/lib/index.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:example/crop_index.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:image_picker/image_picker.dart';
7 | import 'package:crop_box/crop_box.dart';
8 |
9 |
10 |
11 | class PageIndex extends StatefulWidget {
12 | PageIndex({Key? key}) : super(key: key);
13 |
14 | @override
15 | _PageIndexState createState() => _PageIndexState();
16 | }
17 |
18 | class _PageIndexState extends State {
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Scaffold(
23 | appBar: AppBar(
24 | title: Text('Crop Box'),
25 | ),
26 | body: Center(
27 | child: Column(
28 | children: [
29 | SizedBox(
30 | height: 100,
31 | ),
32 | /// NetWork Image
33 | TextButton(
34 | onPressed: () {
35 | Navigator.push(context, MaterialPageRoute(builder: (context) => CropIndex(
36 | width: 200,
37 | height: 315,
38 | imageUrl: "https://img1.maka.im/materialStore/beijingshejia/tupianbeijinga/9/M_7TNT6NIM/M_7TNT6NIM_v1.jpg",
39 | clipType: ClipType.networkImage,
40 | )));
41 | },
42 | child: Text('Test NetWork Image'),
43 | ),
44 | /// Local Image
45 | TextButton(
46 | onPressed: () async {
47 | PickedFile? pickedFile = await ImagePicker().getImage(source: ImageSource.gallery);
48 | if(pickedFile != null) {
49 | Uint8List bytes = await pickedFile.readAsBytes();
50 | Size imageSize = await ImageCrop.getImageSize(bytes);
51 | Navigator.push(context, MaterialPageRoute(builder: (context) => CropIndex(
52 | width: imageSize.width,
53 | height: imageSize.height,
54 | localImageData: bytes,
55 | clipType: ClipType.localImage,
56 | )));
57 | }
58 | },
59 | child: Text('Choose Local Image'),
60 | ),
61 | ],
62 | ),
63 | ),
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:example/index.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'dart:ui';
5 | import 'package:flutter_screenutil/flutter_screenutil.dart';
6 |
7 | void main() {
8 | runApp(MyApp());
9 | }
10 |
11 | class MyApp extends StatelessWidget {
12 | @override
13 | Widget build(BuildContext context) {
14 | return ScreenUtilInit(
15 | designSize: Size(750, 1334),
16 | builder: () {
17 | return MaterialApp(
18 | title: 'Flutter Demo',
19 | theme: ThemeData(
20 | primaryColor: Color(0xff44D7B6),
21 | visualDensity: VisualDensity.adaptivePlatformDensity,
22 | ),
23 | home: PageIndex(),
24 | );
25 | },
26 | );
27 | }
28 | }
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | args:
5 | dependency: transitive
6 | description:
7 | name: args
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.1.1"
11 | async:
12 | dependency: transitive
13 | description:
14 | name: async
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.6.1"
18 | boolean_selector:
19 | dependency: transitive
20 | description:
21 | name: boolean_selector
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.1.0"
25 | characters:
26 | dependency: transitive
27 | description:
28 | name: characters
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.1.0"
32 | charcode:
33 | dependency: transitive
34 | description:
35 | name: charcode
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.2.0"
39 | clock:
40 | dependency: transitive
41 | description:
42 | name: clock
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.1.0"
46 | collection:
47 | dependency: transitive
48 | description:
49 | name: collection
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.15.0"
53 | convert:
54 | dependency: transitive
55 | description:
56 | name: convert
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "3.0.0"
60 | crop_box:
61 | dependency: "direct dev"
62 | description:
63 | path: ".."
64 | relative: true
65 | source: path
66 | version: "1.0.2"
67 | cupertino_icons:
68 | dependency: "direct main"
69 | description:
70 | name: cupertino_icons
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "1.0.3"
74 | exif:
75 | dependency: transitive
76 | description:
77 | name: exif
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.2.0"
81 | fake_async:
82 | dependency: transitive
83 | description:
84 | name: fake_async
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "1.2.0"
88 | flutter:
89 | dependency: "direct main"
90 | description: flutter
91 | source: sdk
92 | version: "0.0.0"
93 | flutter_plugin_android_lifecycle:
94 | dependency: transitive
95 | description:
96 | name: flutter_plugin_android_lifecycle
97 | url: "https://pub.dartlang.org"
98 | source: hosted
99 | version: "2.0.2"
100 | flutter_screenutil:
101 | dependency: "direct main"
102 | description:
103 | name: flutter_screenutil
104 | url: "https://pub.dartlang.org"
105 | source: hosted
106 | version: "5.0.0+2"
107 | flutter_test:
108 | dependency: "direct dev"
109 | description: flutter
110 | source: sdk
111 | version: "0.0.0"
112 | flutter_web_plugins:
113 | dependency: transitive
114 | description: flutter
115 | source: sdk
116 | version: "0.0.0"
117 | hashcodes:
118 | dependency: transitive
119 | description:
120 | name: hashcodes
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "2.0.0"
124 | http:
125 | dependency: transitive
126 | description:
127 | name: http
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "0.13.3"
131 | http_parser:
132 | dependency: transitive
133 | description:
134 | name: http_parser
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "4.0.0"
138 | image_editor:
139 | dependency: transitive
140 | description:
141 | name: image_editor
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.0.0"
145 | image_gallery_saver:
146 | dependency: "direct main"
147 | description:
148 | name: image_gallery_saver
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.6.9"
152 | image_picker:
153 | dependency: "direct main"
154 | description:
155 | name: image_picker
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "0.7.5+3"
159 | image_picker_for_web:
160 | dependency: transitive
161 | description:
162 | name: image_picker_for_web
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.0.0"
166 | image_picker_platform_interface:
167 | dependency: transitive
168 | description:
169 | name: image_picker_platform_interface
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "2.1.0"
173 | image_size_getter:
174 | dependency: "direct main"
175 | description:
176 | name: image_size_getter
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.0.0"
180 | js:
181 | dependency: transitive
182 | description:
183 | name: js
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "0.6.3"
187 | matcher:
188 | dependency: transitive
189 | description:
190 | name: matcher
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "0.12.10"
194 | meta:
195 | dependency: transitive
196 | description:
197 | name: meta
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "1.3.0"
201 | path:
202 | dependency: transitive
203 | description:
204 | name: path
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "1.8.0"
208 | pedantic:
209 | dependency: transitive
210 | description:
211 | name: pedantic
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "1.11.0"
215 | plugin_platform_interface:
216 | dependency: transitive
217 | description:
218 | name: plugin_platform_interface
219 | url: "https://pub.dartlang.org"
220 | source: hosted
221 | version: "2.0.0"
222 | sky_engine:
223 | dependency: transitive
224 | description: flutter
225 | source: sdk
226 | version: "0.0.99"
227 | source_span:
228 | dependency: transitive
229 | description:
230 | name: source_span
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "1.8.1"
234 | sprintf:
235 | dependency: transitive
236 | description:
237 | name: sprintf
238 | url: "https://pub.dartlang.org"
239 | source: hosted
240 | version: "6.0.0"
241 | stack_trace:
242 | dependency: transitive
243 | description:
244 | name: stack_trace
245 | url: "https://pub.dartlang.org"
246 | source: hosted
247 | version: "1.10.0"
248 | stream_channel:
249 | dependency: transitive
250 | description:
251 | name: stream_channel
252 | url: "https://pub.dartlang.org"
253 | source: hosted
254 | version: "2.1.0"
255 | string_scanner:
256 | dependency: transitive
257 | description:
258 | name: string_scanner
259 | url: "https://pub.dartlang.org"
260 | source: hosted
261 | version: "1.1.0"
262 | term_glyph:
263 | dependency: transitive
264 | description:
265 | name: term_glyph
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "1.2.0"
269 | test_api:
270 | dependency: transitive
271 | description:
272 | name: test_api
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "0.3.0"
276 | typed_data:
277 | dependency: transitive
278 | description:
279 | name: typed_data
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "1.3.0"
283 | vector_math:
284 | dependency: transitive
285 | description:
286 | name: vector_math
287 | url: "https://pub.dartlang.org"
288 | source: hosted
289 | version: "2.1.0"
290 | sdks:
291 | dart: ">=2.12.0 <3.0.0"
292 | flutter: ">=2.0.0"
293 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.2
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | flutter_screenutil: ^5.0.0+2
27 | image_gallery_saver: ^1.6.9
28 | image_picker: ^0.7.4
29 | image_size_getter: ^1.0.0
30 |
31 |
32 | # The following adds the Cupertino Icons font to your application.
33 | # Use with the CupertinoIcons class for iOS style icons.
34 | cupertino_icons: ^1.0.3
35 |
36 | dev_dependencies:
37 | flutter_test:
38 | sdk: flutter
39 | crop_box:
40 | path: ../
41 |
42 | # For information on the generic Dart part of this file, see the
43 | # following page: https://dart.dev/tools/pub/pubspec
44 |
45 | # The following section is specific to Flutter.
46 | flutter:
47 |
48 | # The following line ensures that the Material Icons font is
49 | # included with your application, so that you can use the icons in
50 | # the material Icons class.
51 | uses-material-design: true
52 |
53 | # To add assets to your application, add an assets section, like this:
54 | # assets:
55 | # - images/a_dot_burr.jpeg
56 | # - images/a_dot_ham.jpeg
57 |
58 | # An image asset can refer to one or more resolution-specific "variants", see
59 | # https://flutter.dev/assets-and-images/#resolution-aware.
60 |
61 | # For details regarding adding assets from package dependencies, see
62 | # https://flutter.dev/assets-and-images/#from-packages
63 |
64 | # To add custom fonts to your application, add a fonts section here,
65 | # in this "flutter" section. Each entry in this list should have a
66 | # "family" key with the font family name, and a "fonts" key with a
67 | # list giving the asset and other descriptors for the font. For
68 | # example:
69 | # fonts:
70 | # - family: Schyler
71 | # fonts:
72 | # - asset: fonts/Schyler-Regular.ttf
73 | # - asset: fonts/Schyler-Italic.ttf
74 | # style: italic
75 | # - family: Trajan Pro
76 | # fonts:
77 | # - asset: fonts/TrajanPro.ttf
78 | # - asset: fonts/TrajanPro_Bold.ttf
79 | # weight: 700
80 | #
81 | # For details regarding fonts from package dependencies,
82 | # see https://flutter.dev/custom-fonts/#from-packages
83 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/gif/5e941f8d-39a2-45da-9c8c-9eb2f6513498.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/godaangel/flutter_crop_box/dffe2dc08d2c4827bbc50273dc072384c2201732/gif/5e941f8d-39a2-45da-9c8c-9eb2f6513498.gif
--------------------------------------------------------------------------------
/lib/crop_box.dart:
--------------------------------------------------------------------------------
1 | library crop_box;
2 |
3 | export 'src/main.dart';
4 | export 'src/grid_line.dart';
5 | export 'src/box_border.dart';
6 | export 'src/image_crop.dart';
--------------------------------------------------------------------------------
/lib/src/box_border.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CropBoxBorder {
4 | /// 方形模式下,边框的圆角
5 | final Radius? radius;
6 | Radius get noNullRaidus => radius ?? Radius.circular(0);
7 |
8 | /// 边框宽度
9 | ///
10 | /// 默认 [2]
11 | final double width;
12 |
13 | /// 边框颜色
14 | ///
15 | /// 默认 [Colors.white]
16 | final Color color;
17 |
18 | CropBoxBorder({this.radius, this.width = 2, this.color = Colors.white});
19 | }
--------------------------------------------------------------------------------
/lib/src/grid_line.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class GridLine {
4 | /// 网格线颜色
5 | /// 默认 `Colors.white`
6 | Color color;
7 | /// 网格线宽度
8 | double width;
9 | /// 网格线padding
10 | EdgeInsets? padding;
11 |
12 | /// 网格线
13 | GridLine({this.color = Colors.white, this.width = 0.5, this.padding});
14 | }
15 |
--------------------------------------------------------------------------------
/lib/src/image_crop.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:image_editor/image_editor.dart';
5 | import 'package:image_size_getter/image_size_getter.dart' as imageGetter;
6 | import 'package:exif/exif.dart';
7 |
8 | class ImageCropOutputFormatQuality {
9 | static const int VeryHigh = 100;
10 | static const int High = 75;
11 | static const int Middle = 50;
12 | static const int Low = 25;
13 | static const int VeryLow = 5;
14 | }
15 | class ImageCrop {
16 | /// get crop result image, type is Uint8List
17 | static Future getResult({required Rect clipRect, required Uint8List image, int? outputQuality, Size? outputSize}) async {
18 |
19 | final Size memoryImageSize = await getImageSize(image);
20 | final editorOption = ImageEditorOption();
21 |
22 | editorOption.addOption(ClipOption(
23 | x: clipRect.left * memoryImageSize.width,
24 | y: clipRect.top * memoryImageSize.height,
25 | width: clipRect.width * memoryImageSize.width,
26 | height: clipRect.height * memoryImageSize.height
27 | ));
28 |
29 | if(outputSize != null) {
30 | editorOption.addOption(ScaleOption(outputSize.width.toInt(), outputSize.height.toInt()));
31 | }
32 |
33 | editorOption.outputFormat = OutputFormat.jpeg(outputQuality ?? ImageCropOutputFormatQuality.High);
34 | final result = await ImageEditor.editImage(image: image, imageEditorOption: editorOption);
35 | return result;
36 | }
37 |
38 | /// get image size with exif
39 | static Future getImageSize(Uint8List bytes) async {
40 | try {
41 | Map? data =
42 | await readExifFromBytes(bytes);
43 | double width = data?['EXIF ExifImageWidth']?.values?[0].toDouble();
44 | double height = data?['EXIF ExifImageLength']?.values?[0].toDouble();
45 | if(width > height) {
46 | if (data!['Image Orientation']!.printable!.contains('Horizontal')) {
47 | return Size(width.toDouble(), height);
48 | }else {
49 | return Size(height, width);
50 | }
51 | }else{
52 | return Size(width, height);
53 | }
54 | } catch (e) {
55 | imageGetter.ImageInput imageInput = imageGetter.MemoryInput(bytes);
56 | double width = imageGetter.ImageSizeGetter.getSize(imageInput).width.toDouble();
57 | double height = imageGetter.ImageSizeGetter.getSize(imageInput).height.toDouble();
58 | final Size memoryImageSize = Size(width, height);
59 | return Size(memoryImageSize.width, memoryImageSize.height);
60 | }
61 |
62 | }
63 | }
--------------------------------------------------------------------------------
/lib/src/main.dart:
--------------------------------------------------------------------------------
1 | library crop_box;
2 |
3 | import 'dart:math';
4 | import 'dart:ui';
5 |
6 | import 'package:flutter/cupertino.dart';
7 | import 'package:flutter/material.dart';
8 |
9 | import 'box_border.dart';
10 | import 'grid_line.dart';
11 |
12 | enum CropBoxType {
13 | Square,
14 | Circle
15 | }
16 |
17 | /// 回调函数的类型定义
18 | typedef _CropRectUpdate = void Function(Rect rect);
19 |
20 | class CropBox extends StatefulWidget {
21 |
22 | /// 初始裁剪区域(LTRB均是 0 到 1 的double类型)
23 | ///
24 | /// 如果不填,默认会填充并居中,表现形式类似cover
25 | final Rect? cropRect;
26 | /// 待裁剪素材的尺寸
27 | final Size clipSize;
28 | /// 子组件
29 | ///
30 | /// 一般是待裁剪素材
31 | final Widget child;
32 | /// 裁剪框比例
33 | ///
34 | /// 默认 16:9
35 | final Size? cropRatio;
36 | /// 裁剪框当前比例下最大宽高
37 | ///
38 | /// 主要是用于需要主动调整裁剪框大小时使用 如果没有特殊需求,不需要配置
39 | final Size? maxCropSize;
40 | /// 最大放大尺寸
41 | ///
42 | /// 允许放大的最大尺寸,默认10.0
43 | final double maxScale;
44 | /// 裁剪区域开始变化时的回调
45 | final Function? cropRectUpdateStart;
46 | /// 裁剪区域变化时的回调
47 | ///
48 | /// 可用于初始生成裁剪区域,以及手势触发的回调
49 | final _CropRectUpdate? cropRectUpdate;
50 | /// 裁剪区域停止变化时的回调函数,可以获得最终裁剪区域
51 | ///
52 | /// 返回值 `Rect rect` 为裁剪区域在素材上的比例
53 | ///
54 | /// `rect`的LTRB值均为0到1的`double`值,代表在本轴上的百分比位置
55 | ///
56 | /// 这个百分比只是LTRB分别相对于原素材宽高的百分比,各个LTRB之间这个**百分比值没有联系**
57 | ///
58 | /// LTRB的绝对值有比例关系,比例等于裁剪比例
59 | final _CropRectUpdate cropRectUpdateEnd;
60 |
61 | /// 裁剪框类型
62 | ///
63 | /// [cropBoxType] 有两种类型
64 | ///
65 | /// [CropBoxType.Square] 表示方形框
66 | /// [CropBoxType.Circle] 表示圆形框,圆形裁剪框模式下[cropRatio]会强制为`1:1`,且`needInnerBorder`和`borderRadius`不生效
67 | ///
68 | /// [cropBoxType] 默认值为 [CropBoxType.Square]
69 | final CropBoxType cropBoxType;
70 |
71 | /// 是否需要内边框
72 | ///
73 | /// default [false]
74 | final bool needInnerBorder;
75 |
76 | /// 网格线
77 | final GridLine? gridLine;
78 |
79 | /// 裁剪框边框样式
80 | ///
81 | /// 包含颜色、宽度、圆角等信息
82 | final CropBoxBorder? cropBoxBorder;
83 |
84 | /// 裁剪框背景颜色
85 | ///
86 | /// default [Color(0xff141414)]
87 | final Color? backgroundColor;
88 |
89 | /// 遮罩层颜色
90 | ///
91 | /// default [Color.fromRGBO(0, 0, 0, 0.5)]
92 | final Color? maskColor;
93 |
94 | /// ### 裁剪素材组件
95 | ///
96 | /// 通过传入裁剪素材宽高[clipSize],裁剪区域比例[cropRatio]以及待裁剪的内容[child],就可以生成裁剪器,支持手势移动、放大缩小操作
97 | ///
98 | /// 再通过[cropRectUpdateEnd]回调拿到裁剪区域的值,对应到素材进行裁剪操作
99 | ///
100 | /// {@tool dartpad --template=stateless_widget_material}
101 | ///
102 | /// 代码示例
103 | /// ```dart
104 | /// CropBox(
105 | /// // cropRect: Rect.fromLTRB(1 - 0.4083, 0.162, 1, 0.3078), // 2.4倍 随机位置
106 | /// // cropRect: Rect.fromLTRB(0, 0, 0.4083, 0.1457), //2.4倍,都是0,0
107 | /// cropRect: Rect.fromLTRB(0, 0, 1, 0.3572), // 1倍
108 | /// clipSize: Size(200, 315),
109 | /// cropRatio: Size(16, 9),
110 | /// cropRectUpdateEnd: (rect) {
111 | /// print("裁剪区域移动 $rect");
112 | /// },
113 | /// child: Image.network(
114 | /// "https://img1.maka.im/materialStore/beijingshejia/tupianbeijinga/9/M_7TNT6NIM/M_7TNT6NIM_v1.jpg",
115 | /// width: double.infinity,
116 | /// height: double.infinity,
117 | /// fit: BoxFit.cover,
118 | /// loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent loadingProgress) {
119 | /// if (loadingProgress == null)
120 | /// return child;
121 | /// return Center(
122 | /// child: CircularProgressIndicator(
123 | /// value: loadingProgress.expectedTotalBytes != null
124 | /// ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
125 | /// : null,
126 | /// ),
127 | /// );
128 | /// },
129 | /// ),
130 | /// )
131 | /// ```
132 | /// {@end-tool}
133 | CropBox({this.cropRect, required this.clipSize, required this.child, required this.cropRectUpdateEnd, this.cropRectUpdateStart, this.cropRectUpdate, this.cropRatio, this.maxCropSize, this.maxScale = 10.0, this.cropBoxType = CropBoxType.Square, this.needInnerBorder = false, this.gridLine, this.cropBoxBorder, this.backgroundColor, this.maskColor});
134 |
135 | @override
136 | _CropBoxState createState() => _CropBoxState();
137 | }
138 |
139 | class _CropBoxState extends State {
140 | /// 临时比例缩放大小
141 | double _tmpScale = 1.0;
142 | /// 最终比例缩放大小
143 | double _scale = 1.0;
144 |
145 | /// 待裁剪素材上次偏移量
146 | Offset _lastFocalPoint = Offset(0.0, 0.0);
147 | /// 待裁剪素材初始偏移量
148 | Offset _deltaPoint = Offset(0, 0);
149 | /// 待裁剪的素材本身尺寸 - 由外部传入
150 | late Size _originClipSize;
151 | /// 待裁剪的素材计算后的初始尺寸
152 | Size _resizeClipSize = Size(0, 0);
153 |
154 | /// 组件自身宽
155 | double _containerWidth = 0;
156 | /// 组件自身高
157 | double _containerHeight = 0;
158 | /// 头部padding高度
159 | double _containerPaddingTop = 0;
160 | /// 底部padding高度
161 | double _containerPaddingBottom = 10;
162 | /// 左右两边padding高度
163 | double _containerPaddingRL = 10;
164 | /// 裁剪框最大尺寸
165 | Size _cropBoxMaxSize = Size(0, 0);
166 | /// 裁剪框实际尺寸
167 | Size _cropBoxRealSize = Size(0, 0);
168 | /// 裁剪框实际坐标
169 | Rect _cropBoxRealRect = Rect.fromLTWH(0, 0, 0, 0);
170 | /// 裁剪比例
171 | Size _cropRatio = Size(16, 9);
172 | /// 中心点坐标
173 | Offset _originPos = Offset(0, 0);
174 |
175 | /// 裁剪结果数据
176 | ///
177 | /// LTRB值均为0到1的double值,代表在本轴上的百分比位置
178 | ///
179 | /// 包含了缩放尺寸,需要自行判断计算
180 | Rect resultRect = Rect.fromLTRB(0, 0, 1, 1);
181 |
182 | // 是否绘制完毕
183 | bool isReady = false;
184 |
185 | Future? _loading;
186 |
187 | @override
188 | void initState() {
189 | super.initState();
190 | }
191 |
192 | /// 初始化裁剪
193 | ///
194 | /// 返回值 bool true表示初始化成功 false表示失败
195 | bool initCrop() {
196 | caculateCropBoxSize();
197 | caculateInitClipSize();
198 | caculateInitClipPosition();
199 | return true;
200 | }
201 |
202 | /// 计算canvas绘制裁剪框的位置
203 | ///
204 | /// 计算裁剪框位置
205 | ///
206 | /// 计算中心点位置
207 | void caculateCropBoxSize() {
208 | // 中心坐标点用组件的中心点
209 | _originPos = Offset(_containerWidth / 2, (_containerHeight) / 2);
210 | // 计算裁剪框尺寸
211 | _cropBoxRealSize = canculateInnerBoxRealSize(_cropBoxMaxSize, _cropRatio);
212 | // 计算裁剪框坐标信息(坐标轴在 0, 0 处),用于裁剪框在canvas的坐标绘制
213 | _cropBoxRealRect = Rect.fromLTWH((_containerWidth - _cropBoxRealSize.width) / 2, (_containerHeight - _cropBoxRealSize.height) / 2, _cropBoxRealSize.width, _cropBoxRealSize.height);
214 | // print("caculateCropBoxSize Result \n _cropBoxRealSize: $_cropBoxRealSize \n _cropBoxRealRect: $_cropBoxRealRect \n _originPos: $_originPos");
215 | }
216 |
217 | /// 计算初始素材尺寸
218 | ///
219 | /// 需要计算素材宽高比,判断横向还是纵向拉满
220 | void caculateInitClipSize() {
221 | double _realWidth = 0;
222 | double _realHeight = 0;
223 |
224 | double _cropAspectRatio = _cropBoxRealSize.width / _cropBoxRealSize.height; //裁剪框宽高比
225 | double _clipAspectRatio = _originClipSize.width / _originClipSize.height; //素材宽高比
226 |
227 | if (_cropAspectRatio > _clipAspectRatio) {
228 | _realWidth = _cropBoxRealSize.width;
229 | _realHeight = _realWidth / _clipAspectRatio;
230 | } else {
231 | _realHeight = _cropBoxRealSize.height;
232 | _realWidth = _realHeight * _clipAspectRatio;
233 | }
234 | _resizeClipSize = Size(_realWidth, _realHeight);
235 |
236 | print("_resizeClipSize: $_resizeClipSize");
237 | }
238 |
239 | /// 计算初始素材摆放位置
240 | ///
241 | /// 根据初始素材尺寸以及scale确定初始位置
242 | void caculateInitClipPosition() {
243 | // 根据scale和传入的裁剪区域确定具体位置
244 | Rect? _clipRect;
245 |
246 | if(resultRect == Rect.fromLTRB(0, 0, 1, 1)) {
247 | // 如果没有传入初始裁剪区域,则默认居中裁剪对应比例的区域,那么scale必然为1
248 | _scale = 1.0;
249 | _deltaPoint = Offset(_originPos.dx - _resizeClipSize.width/2, _originPos.dy - _resizeClipSize.height/2);
250 | double _clipAspectRatio = _resizeClipSize.width / _resizeClipSize.height; //素材宽高比
251 | double _cropAspectRatio = _cropBoxRealSize.width / _cropBoxRealSize.height; //裁剪区域宽高比
252 | Rect _tempRect;
253 | if(_cropAspectRatio > _clipAspectRatio) {
254 | // 如果裁剪框宽高比大于素材宽高比
255 | _tempRect = Rect.fromLTWH(0, (_resizeClipSize.height - _cropBoxRealSize.height) / 2, _cropBoxRealSize.width, _cropBoxRealSize.height);
256 | }else{
257 | _tempRect = Rect.fromLTWH((_resizeClipSize.width - _cropBoxRealSize.width) / 2, 0, _cropBoxRealSize.width, _cropBoxRealSize.height);
258 | }
259 | _clipRect = Rect.fromLTRB(_tempRect.left / _resizeClipSize.width, _tempRect.top / _resizeClipSize.height, _tempRect.right / _resizeClipSize.width, _tempRect.bottom / _resizeClipSize.height);
260 | }else{
261 | double _clipAspectRatio = _resizeClipSize.width / _resizeClipSize.height; //素材宽高比
262 | double _cropAspectRatio = _cropBoxRealSize.width / _cropBoxRealSize.height; //裁剪区域宽高比
263 | if(_cropAspectRatio > _clipAspectRatio) {
264 | // 如果裁剪框宽高比大于素材宽高比
265 | _scale = 1 / resultRect.width;
266 | }else{
267 | _scale = 1 / resultRect.height;
268 | }
269 | double _scaledWidth = _scale * _resizeClipSize.width;
270 | double _scaledHeight = _scale * _resizeClipSize.height;
271 |
272 | // 计算偏移和缩放后的位置 - 计算公式画图可得【公式一】 - 一定要注意_scale
273 | // 至于为啥是除以_scale还没整明白,猜测和缩放有关系,需要再研究 todo
274 | double _scaledLeft = _originPos.dx - (_cropBoxRealSize.width / 2 + _scaledWidth * resultRect.left) / _scale;
275 | double _scaledTop = _originPos.dy - (_cropBoxRealSize.height / 2 + _scaledHeight * resultRect.top) / _scale;
276 | _deltaPoint = Offset(_scaledLeft, _scaledTop);
277 | }
278 |
279 | print('_clipRect: $_clipRect _deltaPoint: $_deltaPoint');
280 | }
281 |
282 | /// 判断是否超出界限
283 | ///
284 | /// 如果超出界限,则自动修正位置
285 | void resizeRange() {
286 | Rect _result = transPointToCropArea();
287 | double left = _result.left;
288 | double right = _result.right;
289 | double top = _result.top;
290 | double bottom = _result.bottom;
291 |
292 | // print('resizeRange: ${_result.left} ${_result.top} ${_result.bottom} ${_result.right}');
293 |
294 | bool _isOutRange = false;
295 | // 如果边过大,导致_scale < 1,则进行缩放计算,并且重置 _scale = 1
296 | if((right - left > 1) || (bottom - top > 1)) {
297 | double _max = max(right - left, bottom - top);
298 | left = left / _max;
299 | right = right / _max;
300 | top = top / _max;
301 | bottom = bottom / _max;
302 |
303 | _scale = 1;
304 | _isOutRange = true;
305 | }
306 |
307 | if(left < 0) {
308 | right = right - left;
309 | left = 0;
310 | _isOutRange = true;
311 | }
312 |
313 | if(right > 1) {
314 | left = 1 - (right - left);
315 | right = 1;
316 | _isOutRange = true;
317 | }
318 |
319 | if(top < 0) {
320 | bottom = bottom - top;
321 | top = 0;
322 | _isOutRange = true;
323 | }
324 |
325 | if(bottom > 1) {
326 | top = 1 - (bottom - top);
327 | bottom = 1;
328 | _isOutRange = true;
329 | }
330 |
331 | if(_isOutRange) {
332 | resultRect = Rect.fromLTRB(left, top, right, bottom);
333 | try {
334 | caculateInitClipPosition();
335 | }catch(e) {
336 | print(e);
337 | }
338 | }
339 | }
340 |
341 | /// 根据当前的点,反向计算出渲染区域
342 | Rect transPointToCropArea() {
343 | double _scaledWidth = _scale * _resizeClipSize.width;
344 | double _scaledHeight = _scale * _resizeClipSize.height;
345 | // 由【公式一】反推
346 | double _left = ((_originPos.dx - _deltaPoint.dx) * _scale - _cropBoxRealSize.width / 2) / _scaledWidth;
347 | double _top = ((_originPos.dy - _deltaPoint.dy) * _scale - _cropBoxRealSize.height / 2) / _scaledHeight;
348 |
349 | double _clipAspectRatio = _resizeClipSize.width / _resizeClipSize.height; //素材宽高比
350 | double _cropAspectRatio = _cropBoxRealSize.width / _cropBoxRealSize.height; //裁剪区域宽高比
351 | if(_cropAspectRatio > _clipAspectRatio) {
352 | // 如果裁剪框宽高比大于素材宽高比
353 | // 根据left和top,以及裁剪比例和实际尺寸,计算出裁剪区域相对于素材的长宽百分比LTRB(这个百分比只是Left Top Right Bottom分别相对于原素材宽高的百分比,LTRB之间这个百分比值没有任何关系,LTRB的绝对值有比例关系,比例等于裁剪比例)
354 | double _width = _resizeClipSize.width / _scale;
355 | double _right = _left + 1 / _scale;
356 | double _bottom = _top + _width / _cropAspectRatio / _resizeClipSize.height;
357 | resultRect = Rect.fromLTRB(_left, _top, _right, _bottom);
358 | }else{
359 | double _height = _resizeClipSize.height / _scale;
360 | double _bottom = _top + 1 / _scale;
361 | double _right = _left + _height * _cropAspectRatio / _resizeClipSize.width;
362 | _scale = 1 / resultRect.height;
363 | resultRect = Rect.fromLTRB(_left, _top, _right, _bottom);
364 | }
365 |
366 | return resultRect;
367 | }
368 |
369 | /// 根据填充物最大宽高和填充物比例,计算填充物实际宽高
370 | ///
371 | /// Size [_maxSize] 最大宽高
372 | ///
373 | /// Size [_ratioSize] 宽高比尺寸
374 | ///
375 | Size canculateInnerBoxRealSize(Size _maxSize, Size _ratioSize) {
376 | double _realWidth = 0;
377 | double _realHeight = 0;
378 |
379 | double _contentAspectRatio = _maxSize.width / _maxSize.height; //容器宽高比
380 | double _renderAspectRatio = _ratioSize.width / _ratioSize.height; //渲染区域宽高比
381 |
382 | if (_contentAspectRatio > _renderAspectRatio) {
383 | //容器宽高比大于渲染区域宽高比,则保证高度统一
384 | _realHeight = _maxSize.height;
385 | _realWidth = _realHeight * _renderAspectRatio;
386 | } else {
387 | _realWidth = _maxSize.width;
388 | _realHeight = _realWidth / _renderAspectRatio;
389 | }
390 |
391 | return Size(_realWidth, _realHeight);
392 | }
393 |
394 | @override
395 | void didUpdateWidget(covariant CropBox oldWidget) {
396 | if(widget.cropRatio != oldWidget.cropRatio) {
397 | setState(() {
398 | isReady = false;
399 | });
400 | }
401 |
402 | super.didUpdateWidget(oldWidget);
403 | }
404 |
405 |
406 | @override
407 | Widget build(BuildContext context) {
408 | if(!isReady) {
409 | resultRect = widget.cropRect ?? Rect.fromLTRB(0, 0, 1, 1);
410 | assert(resultRect.left >= 0 && resultRect.left <=1);
411 | assert(resultRect.right >= 0 && resultRect.right <=1);
412 | assert(resultRect.top >= 0 && resultRect.top <=1);
413 | assert(resultRect.bottom >= 0 && resultRect.bottom <=1);
414 |
415 | _originClipSize = widget.clipSize;
416 | if(widget.cropBoxType == CropBoxType.Circle) {
417 | _cropRatio = Size(1, 1);
418 | }else{
419 | _cropRatio = widget.cropRatio ?? Size(16, 9);
420 | }
421 |
422 | _loading = Future.delayed(Duration(milliseconds: 10)).then((value) {
423 | _containerWidth = context.size!.width;
424 | _containerHeight = context.size!.height;
425 | _containerPaddingTop = MediaQuery.of(context).padding.top * 2;
426 | _cropBoxMaxSize = widget.maxCropSize ?? Size(_containerWidth - _containerPaddingRL*2, _containerHeight - _containerPaddingTop - _containerPaddingBottom);
427 | print("build init data \n _containerWidth: $_containerWidth _containerHeight: $_containerHeight _containerPaddingTop: $_containerPaddingTop");
428 | isReady = initCrop();
429 | if(widget.cropRectUpdate != null) {
430 | resultRect = transPointToCropArea();
431 | widget.cropRectUpdate!(resultRect);
432 | }
433 | setState(() {});
434 | });
435 | }
436 |
437 | return FutureBuilder(
438 | future: _loading,
439 | builder: (_, snapshot) {
440 | return ClipRect(
441 | child: Container(
442 | color: widget.backgroundColor ?? Color(0xff141414),
443 | child: GestureDetector(
444 | onScaleStart: _handleScaleStart,
445 | onScaleUpdate: (d) => _handleScaleUpdate(context.size!, d),
446 | onScaleEnd: _handleScaleEnd,
447 | child: AnimatedSwitcher(
448 | duration: Duration(milliseconds: 200),
449 | child: (isReady && snapshot.connectionState == ConnectionState.done) ? Stack(
450 | children: [
451 | Transform(
452 | transform: Matrix4.identity()
453 | ..scale(max(_scale, 1.0), max(_scale, 1.0))
454 | ..translate(_deltaPoint.dx, _deltaPoint.dy),
455 | origin: _originPos,
456 | // overflowBox解决容器尺寸问题,如果不用overflowBox,则子container过大时,会收到父级大小约束变形
457 | child: OverflowBox(
458 | alignment: Alignment.topLeft,
459 | maxWidth: double.infinity,
460 | maxHeight: double.infinity,
461 | child: Container(
462 | width: _resizeClipSize.width,
463 | height: _resizeClipSize.height,
464 | child: widget.child,
465 | ),
466 | ),
467 | ),
468 | CustomPaint(
469 | size: Size(double.infinity, double.infinity),
470 | painter: widget.cropBoxType == CropBoxType.Circle ?
471 | DrawCircleLight(clipRect: _cropBoxRealRect, centerPoint: _originPos, cropBoxBorder: widget.cropBoxBorder ?? CropBoxBorder(), maskColor: widget.maskColor)
472 | : DrawRectLight(clipRect: _cropBoxRealRect, needInnerBorder: widget.needInnerBorder, gridLine: widget.gridLine, cropBoxBorder: widget.cropBoxBorder, maskColor: widget.maskColor),
473 | ),
474 | ],
475 | ): Center(
476 | child: Container(
477 | child: Center(child: CupertinoActivityIndicator(
478 | radius: 12,
479 | )),
480 | ),
481 | ),
482 | ),
483 | ),
484 | ),
485 | );
486 | }
487 | );
488 | }
489 |
490 | void _handleScaleStart(ScaleStartDetails details) {
491 | _tmpScale = _scale;
492 | _lastFocalPoint = details.focalPoint;
493 |
494 | if(widget.cropRectUpdateStart != null) {
495 | widget.cropRectUpdateStart!();
496 | }
497 | }
498 |
499 | void _handleScaleUpdate(Size size, ScaleUpdateDetails details) {
500 | setState(() {
501 | _scale = min(widget.maxScale, max(_tmpScale * details.scale, 1.0));
502 | if (details.scale == 1) {
503 | _deltaPoint += (details.focalPoint - _lastFocalPoint); //偏移量
504 | _lastFocalPoint = details.focalPoint; //保存最有一个Point
505 | }
506 | resizeRange();
507 | });
508 | if(widget.cropRectUpdate != null) {
509 | widget.cropRectUpdate!(resultRect);
510 | }
511 | }
512 |
513 | void _handleScaleEnd(ScaleEndDetails details) {
514 | widget.cropRectUpdateEnd(resultRect);
515 | }
516 | }
517 |
518 |
519 | class DrawRectLight extends CustomPainter {
520 | final Rect clipRect;
521 | final bool needInnerBorder;
522 | final GridLine? gridLine;
523 | final CropBoxBorder? cropBoxBorder;
524 | final Color? maskColor;
525 | DrawRectLight({required this.clipRect, this.needInnerBorder = false, this.gridLine, this.cropBoxBorder, this.maskColor});
526 |
527 | @override
528 | void paint(Canvas canvas, Size size) {
529 | var paint = Paint();
530 | CropBoxBorder _cropBoxBorder = cropBoxBorder ?? CropBoxBorder();
531 | double _storkeWidth = _cropBoxBorder.width;
532 | Radius _borderRadius = _cropBoxBorder.noNullRaidus;
533 | RRect _rrect = RRect.fromRectAndRadius(clipRect, _borderRadius);
534 | RRect _borderRRect = RRect.fromRectAndRadius(Rect.fromLTWH(clipRect.left, clipRect.top - _storkeWidth / 2, clipRect.width, clipRect.height + _storkeWidth), _borderRadius);
535 |
536 | paint
537 | ..style = PaintingStyle.fill
538 | ..color = maskColor ?? Color.fromRGBO(0, 0, 0, 0.5);
539 | canvas.save();
540 |
541 | // 绘制一个圆形反选框和背景遮罩(透明部分)
542 | Path path = Path.combine(
543 | PathOperation.difference,
544 | Path()..addRect(Rect.fromLTRB(0, 0, size.width, size.height)),
545 | Path()
546 | ..addRRect(_rrect)
547 | ..close(),
548 | );
549 | canvas.drawPath(path, paint);
550 | canvas.restore();
551 |
552 | // 绘制主色调边框
553 | paint
554 | ..color = _cropBoxBorder.color
555 | ..style = PaintingStyle.stroke
556 | ..strokeWidth = _storkeWidth;
557 |
558 | canvas.drawRRect(_borderRRect, paint);
559 |
560 | if(gridLine != null) {
561 | canvas.save();
562 | // 绘制主色调边框
563 | paint
564 | ..color = gridLine!.color
565 | ..style = PaintingStyle.stroke
566 | ..strokeWidth = gridLine!.width;
567 | Path gridLinePath = new Path();
568 |
569 | EdgeInsets _padding = gridLine!.padding ?? EdgeInsets.all(0);
570 |
571 | for(int i = 1; i < 3; i ++) {
572 | // 绘制横线
573 | gridLinePath.moveTo(((clipRect.width / 3) * i + clipRect.left - gridLine!.width / 2), clipRect.top + _padding.top);
574 | gridLinePath.lineTo(((clipRect.width / 3) * i + clipRect.left - gridLine!.width / 2), clipRect.top + clipRect.height - _padding.bottom);
575 |
576 | // 绘制竖线
577 | gridLinePath.moveTo(clipRect.left + _padding.left, ((clipRect.height / 3) * i + clipRect.top - gridLine!.width / 2));
578 | gridLinePath.lineTo(clipRect.left + clipRect.width - _padding.right, ((clipRect.height / 3) * i + clipRect.top - gridLine!.width / 2));
579 | }
580 | canvas.drawPath(gridLinePath, paint);
581 | canvas.restore();
582 | }
583 |
584 | if(needInnerBorder) {
585 | // 绘制边框内的样式
586 | paint.style = PaintingStyle.fill;
587 | canvas.drawRect(Rect.fromLTWH(clipRect.left - _storkeWidth / 2, clipRect.top - _storkeWidth, 45.44 / 2, 7.57 / 2), paint);
588 | canvas.drawRect(Rect.fromLTWH(clipRect.left - _storkeWidth / 2, clipRect.top - _storkeWidth, 7.57 / 2, 45.44 / 2), paint);
589 | canvas.drawRect(Rect.fromLTWH(clipRect.left + clipRect.width + _storkeWidth / 2, clipRect.top - _storkeWidth, -45.44 / 2, 7.57 / 2), paint);
590 | canvas.drawRect(Rect.fromLTWH(clipRect.left + clipRect.width + _storkeWidth / 2, clipRect.top - _storkeWidth, -7.57 / 2, 45.44 / 2), paint);
591 | canvas.drawRect(Rect.fromLTWH(clipRect.left - _storkeWidth / 2, clipRect.top + clipRect.height + _storkeWidth, 45.44 / 2, -7.57 / 2), paint);
592 | canvas.drawRect(Rect.fromLTWH(clipRect.left - _storkeWidth / 2, clipRect.top + clipRect.height + _storkeWidth, 7.57 / 2, -45.44 / 2), paint);
593 | canvas.drawRect(Rect.fromLTWH(clipRect.left + clipRect.width + _storkeWidth / 2, clipRect.top + clipRect.height + _storkeWidth, -45.44 / 2, -7.57 / 2), paint);
594 | canvas.drawRect(Rect.fromLTWH(clipRect.left + clipRect.width + _storkeWidth / 2, clipRect.top + clipRect.height + _storkeWidth, -7.57 / 2, -45.44 / 2), paint);
595 | }
596 |
597 | }
598 |
599 | @override
600 | bool shouldRepaint(CustomPainter oldDelegate) => true;
601 | }
602 |
603 | class DrawCircleLight extends CustomPainter {
604 | final Rect clipRect;
605 | final Offset centerPoint;
606 | final CropBoxBorder? cropBoxBorder;
607 | final Color? maskColor;
608 | DrawCircleLight({required this.clipRect, required this.centerPoint, this.cropBoxBorder, this.maskColor});
609 |
610 | @override
611 | void paint(Canvas canvas, Size size) {
612 | CropBoxBorder _cropBoxBorder = cropBoxBorder ?? CropBoxBorder();
613 |
614 | var paint = Paint();
615 | double _storkeWidth = _cropBoxBorder.width;
616 | double _radius = clipRect.width / 2;
617 | paint
618 | ..style = PaintingStyle.fill
619 | ..color = maskColor ?? Color.fromRGBO(0, 0, 0, 0.5);
620 | canvas.save();
621 | // 绘制一个圆形反选框和背景遮罩(透明部分)
622 | Path path = Path.combine(
623 | PathOperation.difference,
624 | Path()..addRect(Rect.fromLTRB(0, 0, size.width, size.height)),
625 | Path()
626 | ..addOval(Rect.fromCircle(center: centerPoint, radius: _radius))
627 | ..close(),
628 | );
629 | canvas.drawPath(path, paint);
630 | canvas.restore();
631 |
632 | // 绘制主色调边框
633 | paint
634 | ..color = _cropBoxBorder.color
635 | ..style = PaintingStyle.stroke
636 | ..strokeWidth = _storkeWidth;
637 | canvas.drawCircle(centerPoint, _radius, paint);
638 | }
639 |
640 | @override
641 | bool shouldRepaint(CustomPainter oldDelegate) => true;
642 | }
643 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | args:
5 | dependency: transitive
6 | description:
7 | name: args
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.1.1"
11 | async:
12 | dependency: transitive
13 | description:
14 | name: async
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.6.1"
18 | boolean_selector:
19 | dependency: transitive
20 | description:
21 | name: boolean_selector
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.1.0"
25 | characters:
26 | dependency: transitive
27 | description:
28 | name: characters
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.1.0"
32 | charcode:
33 | dependency: transitive
34 | description:
35 | name: charcode
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.2.0"
39 | clock:
40 | dependency: transitive
41 | description:
42 | name: clock
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.1.0"
46 | collection:
47 | dependency: transitive
48 | description:
49 | name: collection
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.15.0"
53 | convert:
54 | dependency: transitive
55 | description:
56 | name: convert
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "3.0.0"
60 | exif:
61 | dependency: "direct main"
62 | description:
63 | name: exif
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "2.2.0"
67 | fake_async:
68 | dependency: transitive
69 | description:
70 | name: fake_async
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "1.2.0"
74 | flutter:
75 | dependency: "direct main"
76 | description: flutter
77 | source: sdk
78 | version: "0.0.0"
79 | flutter_test:
80 | dependency: "direct dev"
81 | description: flutter
82 | source: sdk
83 | version: "0.0.0"
84 | hashcodes:
85 | dependency: transitive
86 | description:
87 | name: hashcodes
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "2.0.0"
91 | image_editor:
92 | dependency: "direct main"
93 | description:
94 | name: image_editor
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "1.0.0"
98 | image_size_getter:
99 | dependency: "direct main"
100 | description:
101 | name: image_size_getter
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.0.0"
105 | matcher:
106 | dependency: transitive
107 | description:
108 | name: matcher
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "0.12.10"
112 | meta:
113 | dependency: transitive
114 | description:
115 | name: meta
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.3.0"
119 | path:
120 | dependency: transitive
121 | description:
122 | name: path
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.8.0"
126 | sky_engine:
127 | dependency: transitive
128 | description: flutter
129 | source: sdk
130 | version: "0.0.99"
131 | source_span:
132 | dependency: transitive
133 | description:
134 | name: source_span
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.8.1"
138 | sprintf:
139 | dependency: transitive
140 | description:
141 | name: sprintf
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "6.0.0"
145 | stack_trace:
146 | dependency: transitive
147 | description:
148 | name: stack_trace
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.10.0"
152 | stream_channel:
153 | dependency: transitive
154 | description:
155 | name: stream_channel
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "2.1.0"
159 | string_scanner:
160 | dependency: transitive
161 | description:
162 | name: string_scanner
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.1.0"
166 | term_glyph:
167 | dependency: transitive
168 | description:
169 | name: term_glyph
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "1.2.0"
173 | test_api:
174 | dependency: transitive
175 | description:
176 | name: test_api
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "0.3.0"
180 | typed_data:
181 | dependency: transitive
182 | description:
183 | name: typed_data
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "1.3.0"
187 | vector_math:
188 | dependency: transitive
189 | description:
190 | name: vector_math
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "2.1.0"
194 | sdks:
195 | dart: ">=2.12.0 <3.0.0"
196 | flutter: ">=2.0.0"
197 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: crop_box
2 | description: "A component that can be tailored for a variety of materials.Widget for Clip."
3 | version: 1.0.2
4 | author: yangdz30@gmail.com
5 | homepage: https://github.com/godaangel/flutter_crop_box
6 |
7 | environment:
8 | sdk: ">=2.12.0 <3.0.0"
9 | flutter: ">=1.17.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 | image_editor: ^1.0.0
15 | image_size_getter: ^1.0.0
16 | exif: ^2.1.0
17 | dev_dependencies:
18 | flutter_test:
19 | sdk: flutter
20 |
21 | # For information on the generic Dart part of this file, see the
22 | # following page: https://dart.dev/tools/pub/pubspec
23 |
24 | # The following section is specific to Flutter.
25 | flutter:
26 |
27 | # To add assets to your package, add an assets section, like this:
28 | # assets:
29 | # - images/a_dot_burr.jpeg
30 | # - images/a_dot_ham.jpeg
31 | #
32 | # For details regarding assets in packages, see
33 | # https://flutter.dev/assets-and-images/#from-packages
34 | #
35 | # An image asset can refer to one or more resolution-specific "variants", see
36 | # https://flutter.dev/assets-and-images/#resolution-aware.
37 |
38 | # To add custom fonts to your package, add a fonts section here,
39 | # in this "flutter" section. Each entry in this list should have a
40 | # "family" key with the font family name, and a "fonts" key with a
41 | # list giving the asset and other descriptors for the font. For
42 | # example:
43 | # fonts:
44 | # - family: Schyler
45 | # fonts:
46 | # - asset: fonts/Schyler-Regular.ttf
47 | # - asset: fonts/Schyler-Italic.ttf
48 | # style: italic
49 | # - family: Trajan Pro
50 | # fonts:
51 | # - asset: fonts/TrajanPro.ttf
52 | # - asset: fonts/TrajanPro_Bold.ttf
53 | # weight: 700
54 | #
55 | # For details regarding fonts in packages, see
56 | # https://flutter.dev/custom-fonts/#from-packages
--------------------------------------------------------------------------------
/test/crop_box_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 |
3 | import 'package:crop_box/crop_box.dart';
4 |
5 | void main() {
6 | test('adds one to input values', () {
7 | // final calculator = Calculator();
8 | // expect(calculator.addOne(2), 3);
9 | // expect(calculator.addOne(-7), -6);
10 | // expect(calculator.addOne(0), 1);
11 | // expect(() => calculator.addOne(null), throwsNoSuchMethodError);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------