diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..2db5f560188834959d11782beab6b447bd9d6621
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2013, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bc7d7947fe5f1974af26ebe0355887022af9f6c7
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/image_cropper/ohos/.gitignore b/image_cropper/ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..96486fd9302436d1f5334df3e5240c1ea252e7e9
--- /dev/null
+++ b/image_cropper/ohos/.gitignore
@@ -0,0 +1,30 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.packages
+build/
diff --git a/image_cropper/ohos/.metadata b/image_cropper/ohos/.metadata
new file mode 100644
index 0000000000000000000000000000000000000000..74b8ce5be9342ec82634d1c2aed4f1d45a37b0c1
--- /dev/null
+++ b/image_cropper/ohos/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+ revision: c76c05f814fa89ac912ae3355ee7fb42bd1a85ec
+ channel: unknown
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: c76c05f814fa89ac912ae3355ee7fb42bd1a85ec
+ base_revision: c76c05f814fa89ac912ae3355ee7fb42bd1a85ec
+ - platform: ohos
+ create_revision: c76c05f814fa89ac912ae3355ee7fb42bd1a85ec
+ base_revision: c76c05f814fa89ac912ae3355ee7fb42bd1a85ec
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/image_cropper/ohos/CHANGELOG.md b/image_cropper/ohos/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..fd91ecfdf17a21968366cf4c79a85b56b010e121
--- /dev/null
+++ b/image_cropper/ohos/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+* Support OpenHarmony
diff --git a/image_cropper/ohos/LICENSE b/image_cropper/ohos/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..2db5f560188834959d11782beab6b447bd9d6621
--- /dev/null
+++ b/image_cropper/ohos/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2013, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/image_cropper/ohos/README.md b/image_cropper/ohos/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d620113d715f633855597e1898b93092f35c9737
--- /dev/null
+++ b/image_cropper/ohos/README.md
@@ -0,0 +1,36 @@
+# Image Cropper
+
+[](https://pub.dartlang.org/packages/image_cropper)
+
+
+A Flutter plugin for ohos supports cropping images. This plugin is based on three different native libraries so it comes with different UI between these platforms.
+
+### Required parameters
+
+* **sourcePath**: the absolute path of an image file.
+
+## Usage
+
+```
+ yaml
+ dependencies:
+ image_cropper: 2.0.0
+ imagecropper_ohos: 1.0.0
+ ```
+
+## Example
+
+````dart
+
+import 'package:image_cropper/image_cropper.dart';
+
+
+
+````
+
+## Options
+
+| Name | Android | iOS | OpenHarmony | Default |
+|:---------------------|:-------:|:----:|:-----------:|:-----------:|
+| cropImage | ✅ | ✅ | ✅ | `File` |
+
diff --git a/image_cropper/ohos/analysis_options.yaml b/image_cropper/ohos/analysis_options.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a5744c1cfbe77ae2daba29c74156c617b5f09b77
--- /dev/null
+++ b/image_cropper/ohos/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/image_cropper/ohos/example/.gitignore b/image_cropper/ohos/example/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..24476c5d1eb55824c76d8b01a3965f94abad1ef8
--- /dev/null
+++ b/image_cropper/ohos/example/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/image_cropper/ohos/example/README.md b/image_cropper/ohos/example/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9aa3ba956da2859df1c6ff2a46d9650af429388
--- /dev/null
+++ b/image_cropper/ohos/example/README.md
@@ -0,0 +1,16 @@
+# imagecropper_ohos_example
+
+Demonstrates how to use the imagecropper_ohos plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/image_cropper/ohos/example/analysis_options.yaml b/image_cropper/ohos/example/analysis_options.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..61b6c4de17c96863d24279f06b85e01b6ebbdb34
--- /dev/null
+++ b/image_cropper/ohos/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/image_cropper/ohos/example/lib/crop_page.dart b/image_cropper/ohos/example/lib/crop_page.dart
new file mode 100644
index 0000000000000000000000000000000000000000..78d02fcc4b5c57cafedce4ddc4e9ef48e263b7e8
--- /dev/null
+++ b/image_cropper/ohos/example/lib/crop_page.dart
@@ -0,0 +1,167 @@
+import 'dart:io';
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:imagecropper_ohos/imagecropper_ohos.dart';
+import 'package:image_picker/image_picker.dart';
+import 'package:imagecropper_ohos/page/crop.dart';
+
+class CropWidget extends StatefulWidget {
+ final String filePath;
+
+ CropWidget({Key? key, required this.filePath}) : super(key: key);
+
+ @override
+ _MyAppState createState() => new _MyAppState();
+}
+
+class _MyAppState extends State {
+ final imageCropper = ImagecropperOhos();
+ final cropKey = GlobalKey();
+ File? _file;
+ File? _sample;
+ File? _lastCropped;
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ imageCropper.sampleImage(
+ path: widget.filePath,
+ maximumSize: context.size!.longestSide.ceil(),
+ ).then((value) {
+ setState(() {
+ _sample = value!;
+ _file = File(widget.filePath);
+ });
+ });
+ });
+ }
+
+
+ @override
+ void dispose() {
+ super.dispose();
+ _sample?.delete();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ debugShowCheckedModeBanner: false,
+ home: SafeArea(
+ child: Container(
+ color: Colors.black,
+ child: _sample == null ? _buildOpeningImage() : _buildCroppingImage(),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildOpeningImage() {
+ return Center(child: _buildOpenImage());
+ }
+
+ Widget _buildCroppingImage() {
+ return Column(
+ children: [
+ Expanded(
+ child: Crop.file(_sample!, key: cropKey,),
+ ),
+ Container(
+ padding: const EdgeInsets.only(top: 20.0),
+ alignment: AlignmentDirectional.center,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ TextButton(
+ child: Text(
+ '确认',
+ style: Theme
+ .of(context)
+ .textTheme
+ .labelLarge
+ ?.copyWith(color: Colors.white),
+ ),
+ onPressed: () => _cropImage(),
+ ),
+ ],
+ ),
+ )
+ ],
+ );
+ }
+
+
+ Widget _buildOpenImage() {
+ return TextButton(
+ child: Text(
+ 'Open Image',
+ style: Theme
+ .of(context)
+ .textTheme
+ .labelLarge
+ ?.copyWith(color: Colors.white),
+ ),
+ onPressed: () => _openImage(),
+ );
+ }
+
+ Future _openImage() async {
+ try {
+ final pickedFile = await ImagePicker().pickImage(
+ source: ImageSource.gallery);
+ if (pickedFile == null) return;
+ final file = File(pickedFile.path);
+ debugPrint('$file');
+ final sample = await imageCropper.sampleImage(
+ path: pickedFile.path,
+ maximumSize: context.size!.longestSide.ceil(),
+ );
+
+ _sample?.delete();
+ _file?.delete();
+
+ setState(() {
+ _sample = sample;
+ _file = file;
+ });
+ } catch (e, s) {
+ print(' _openImage $e,$s');
+ }
+ }
+
+ Future _cropImage() async {
+ final scale = cropKey.currentState?.scale;
+ final area = cropKey.currentState?.area;
+ final angle = cropKey.currentState?.angle;
+ final cx = cropKey.currentState?.cx ?? 0;
+ final cy = cropKey.currentState?.cy ?? 0;
+ if (area == null) {
+ // cannot crop, widget is not setup
+ return;
+ }
+
+ // scale up to use maximum possible number of pixels
+ // this will sample image in higher resolution to make cropped image larger
+ final sample = await imageCropper.sampleImage(
+ path: _file!.path,
+ maximumSize: (2000 / scale!).round(),
+ );
+
+ final file = await imageCropper.cropImage(
+ file: sample!,
+ area: area,
+ angle: angle,
+ cx: cx,
+ cy: cy,
+ );
+ sample.delete();
+
+ _lastCropped?.delete();
+ _lastCropped = file;
+
+ Navigator.pop(context, file.path);
+ }
+}
diff --git a/image_cropper/ohos/example/lib/main.dart b/image_cropper/ohos/example/lib/main.dart
new file mode 100644
index 0000000000000000000000000000000000000000..80f3ed9fc3b594e5ab89287d73552891a7ddba25
--- /dev/null
+++ b/image_cropper/ohos/example/lib/main.dart
@@ -0,0 +1,316 @@
+import 'dart:io';
+
+import 'package:dotted_border/dotted_border.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:image_picker/image_picker.dart';
+import 'package:imagecropper_ohos_example/crop_page.dart';
+
+void main() {
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Demo',
+ theme: ThemeData(
+ highlightColor: const Color(0xFFD0996F),
+ canvasColor: const Color(0xFFFDF5EC),
+ textTheme: TextTheme(
+ headlineSmall: ThemeData
+ .light()
+ .textTheme
+ .headlineSmall!
+ .copyWith(color: const Color(0xFFBC764A)),
+ ),
+ iconTheme: IconThemeData(
+ color: Colors.grey[600],
+ ),
+ appBarTheme: const AppBarTheme(
+ backgroundColor: Color(0xFFBC764A),
+ centerTitle: false,
+ foregroundColor: Colors.white,
+ actionsIconTheme: IconThemeData(color: Colors.white),
+ ),
+ colorScheme: ColorScheme.fromSwatch().copyWith(
+ background: const Color(0xFFFDF5EC),
+ primary: const Color(0xFFD0996F),
+ ),
+ ),
+ home: const HomePage(title: 'Image Cropper Demo'),
+ );
+ }
+}
+
+class HomePage extends StatefulWidget {
+ final String title;
+
+ const HomePage({
+ Key? key,
+ required this.title,
+ }) : super(key: key);
+
+ @override
+ _HomePageState createState() => _HomePageState();
+}
+
+class _HomePageState extends State {
+ String? _pickedFile;
+ String? _croppedFile;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: !kIsWeb ? AppBar(title: Text(widget.title)) : null,
+ body: Column(
+ mainAxisSize: MainAxisSize.max,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (kIsWeb)
+ Padding(
+ padding: const EdgeInsets.all(kIsWeb ? 24.0 : 16.0),
+ child: Text(
+ widget.title,
+ style: Theme
+ .of(context)
+ .textTheme
+ .displayMedium!
+ .copyWith(color: Theme
+ .of(context)
+ .highlightColor),
+ ),
+ ),
+ Expanded(child: _body()),
+ ],
+ ),
+ );
+ }
+
+ Widget _body() {
+ if (_croppedFile != null || _pickedFile != null) {
+ return _imageCard();
+ } else {
+ return _uploaderCard();
+ }
+ }
+
+ Widget _imageCard() {
+ return Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Padding(
+ padding:
+ const EdgeInsets.symmetric(horizontal: kIsWeb ? 24.0 : 16.0),
+ child: Card(
+ elevation: 4.0,
+ child: Padding(
+ padding: const EdgeInsets.all(kIsWeb ? 24.0 : 16.0),
+ child: _image(),
+ ),
+ ),
+ ),
+ const SizedBox(height: 24.0),
+ _menu(),
+ ],
+ ),
+ );
+ }
+
+ Widget _image() {
+ final screenWidth = MediaQuery
+ .of(context)
+ .size
+ .width;
+ final screenHeight = MediaQuery
+ .of(context)
+ .size
+ .height;
+ if (_croppedFile != null) {
+ final path = _croppedFile!;
+ return ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: 0.8 * screenWidth,
+ maxHeight: 0.7 * screenHeight,
+ ),
+ child: kIsWeb ? Image.network(path) : Image.file(File(path)),
+ );
+ } else if (_pickedFile != null) {
+ final path = _pickedFile!;
+ return ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: 0.8 * screenWidth,
+ maxHeight: 0.7 * screenHeight,
+ ),
+ child: kIsWeb ? Image.network(path) : Image.file(File(path)),
+ );
+ } else {
+ return const SizedBox.shrink();
+ }
+ }
+
+ Widget _menu() {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ FloatingActionButton(
+ onPressed: () {
+ _clear();
+ },
+ backgroundColor: Colors.redAccent,
+ tooltip: 'Delete',
+ child: const Icon(Icons.delete),
+ ),
+ if (_croppedFile == null)
+ Padding(
+ padding: const EdgeInsets.only(left: 32.0),
+ child: FloatingActionButton(
+ onPressed: () {
+ _cropImage();
+ },
+ backgroundColor: const Color(0xFFBC764A),
+ tooltip: 'Crop',
+ child: const Icon(Icons.crop),
+ ),
+ )
+ ],
+ );
+ }
+
+ Widget _uploaderCard() {
+ return Center(
+ child: Card(
+ elevation: 4.0,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(16.0),
+ ),
+ child: SizedBox(
+ width: kIsWeb ? 380.0 : 320.0,
+ height: 300.0,
+ child: Column(
+ mainAxisSize: MainAxisSize.max,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: DottedBorder(
+ radius: const Radius.circular(12.0),
+ borderType: BorderType.RRect,
+ dashPattern: const [8, 4],
+ color: Theme
+ .of(context)
+ .highlightColor
+ .withOpacity(0.4),
+ child: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Icon(
+ Icons.image,
+ color: Theme
+ .of(context)
+ .highlightColor,
+ size: 80.0,
+ ),
+ const SizedBox(height: 24.0),
+ Text(
+ 'Upload an image to start',
+ style: kIsWeb
+ ? Theme
+ .of(context)
+ .textTheme
+ .headlineSmall!
+ .copyWith(
+ color: Theme
+ .of(context)
+ .highlightColor)
+ : Theme
+ .of(context)
+ .textTheme
+ .bodyMedium!
+ .copyWith(
+ color:
+ Theme
+ .of(context)
+ .highlightColor),
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 24.0),
+ child: ElevatedButton(
+ onPressed: () {
+ _uploadImage();
+ },
+ style:
+ ElevatedButton.styleFrom(foregroundColor: Colors.white),
+ child: const Text('Upload',style: TextStyle(color: Colors.red),),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Future _cropImage() async {
+ if (_pickedFile != null) {
+ String? croppedFile = await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => CropWidget(filePath: _pickedFile!,)),
+ );
+ if (croppedFile != null) {
+ setState(() {
+ _croppedFile = croppedFile;
+ });
+ }
+ }
+ }
+
+ Future _uploadImage() async {
+ final pickedFile =
+ await ImagePicker().pickImage(source: ImageSource.gallery);
+ if (pickedFile != null) {
+ setState(() {
+ _pickedFile = pickedFile.path;
+ });
+ }
+ }
+
+
+ void _clear() {
+ setState(() {
+ if (_croppedFile != null) {
+ File(_croppedFile!).delete();
+ }
+ _pickedFile = null;
+ _croppedFile = null;
+ });
+ }
+
+ @override
+ void dispose() {
+ if (_croppedFile != null) {
+ File(_croppedFile!).delete();
+ }
+ if (_pickedFile != null) {
+ File(_pickedFile!).delete();
+ }
+ super.dispose();
+ }
+}
+
diff --git a/image_cropper/ohos/example/ohos/.gitignore b/image_cropper/ohos/example/ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6ca13b3170eec5dd5ac5ad7f1c4dd0118845f473
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/.gitignore
@@ -0,0 +1,19 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
+*.har
+**/BuildProfile.ets
+**/oh-package-lock.json5
+
+**/src/main/resources/rawfile/flutter_assets/
+**/libs/arm64-v8a/libapp.so
+**/libs/arm64-v8a/libflutter.so
+**/libs/arm64-v8a/libvmservice_snapshot.so
diff --git a/image_cropper/ohos/example/ohos/AppScope/app.json5 b/image_cropper/ohos/example/ohos/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..2ba2590b93e739cdb29a484d816527865f1b9754
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "vn.hunghd.flutter.plugins.imagecropper.imagecropper_ohos_example",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:app_icon",
+ "label": "$string:app_name"
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/AppScope/resources/base/element/string.json b/image_cropper/ohos/example/ohos/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..f07f5854204675ed4c2282b6b3f1aa492586f578
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "imagecropper_ohos_example"
+ }
+ ]
+}
diff --git a/image_cropper/ohos/example/ohos/AppScope/resources/base/media/app_icon.png b/image_cropper/ohos/example/ohos/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/image_cropper/ohos/example/ohos/AppScope/resources/base/media/app_icon.png differ
diff --git a/image_cropper/ohos/example/ohos/build-profile.json5 b/image_cropper/ohos/example/ohos/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..e21e683bc07558354812fdc5db9507ebae0dcb25
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/build-profile.json5
@@ -0,0 +1,26 @@
+{
+ "app": {
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compatibleSdkVersion": "5.0.0(12)",
+ "runtimeOS": "HarmonyOS"
+ }
+ ],
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/.gitignore b/image_cropper/ohos/example/ohos/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/.gitignore
@@ -0,0 +1,7 @@
+
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/build-profile.json5 b/image_cropper/ohos/example/ohos/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/build-profile.json5
@@ -0,0 +1,29 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+{
+ "apiType": 'stageMode',
+ "buildOption": {
+ },
+ "targets": [
+ {
+ "name": "default",
+ "runtimeOS": "HarmonyOS"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/hvigorfile.ts b/image_cropper/ohos/example/ohos/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/hvigorfile.ts
@@ -0,0 +1,17 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+export { hapTasks } from '@ohos/hvigor-ohos-plugin';
diff --git a/image_cropper/ohos/example/ohos/entry/oh-package.json5 b/image_cropper/ohos/example/ohos/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..003cdb2c961558521e310bf0ae54b0b0526b1c29
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/oh-package.json5
@@ -0,0 +1,13 @@
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {
+ "@ohos/flutter_ohos": "file:../har/flutter.har",
+ "imagecropper_ohos": "file:..\\har\\imagecropper_ohos.har",
+ "image_picker_ohos": "file:..\\har\\image_picker_ohos.har"
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/image_cropper/ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..8bc48be8773196f34cccb15cf517f87f5c6b94d2
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,24 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
+import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
+
+export default class EntryAbility extends FlutterAbility {
+ configureFlutterEngine(flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+ GeneratedPluginRegistrant.registerWith(flutterEngine)
+ }
+}
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/ets/pages/Index.ets b/image_cropper/ohos/example/ohos/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,38 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import common from '@ohos.app.ability.common';
+import { FlutterPage } from '@ohos/flutter_ohos'
+
+let storage = LocalStorage.getShared()
+const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'
+
+@Entry(storage)
+@Component
+struct Index {
+ private context = getContext(this) as common.UIAbilityContext
+ @LocalStorageLink('viewId') viewId: string = "";
+
+ build() {
+ Column() {
+ FlutterPage({ viewId: this.viewId })
+ }
+ }
+
+ onBackPress(): boolean {
+ this.context.eventHub.emit(EVENT_BACK_PRESS)
+ return true
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets b/image_cropper/ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets
new file mode 100644
index 0000000000000000000000000000000000000000..ee71d568257612b30a226f42fc5d72348e756a3b
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FlutterEngine, Log } from '@ohos/flutter_ohos';
+import ImagePickerPlugin from 'image_picker_ohos';
+import ImagecropperOhosPlugin from 'imagecropper_ohos';
+
+/**
+ * Generated file. Do not edit.
+ * This file is generated by the Flutter tool based on the
+ * plugins that support the Ohos platform.
+ */
+
+const TAG = "GeneratedPluginRegistrant";
+
+export class GeneratedPluginRegistrant {
+
+ static registerWith(flutterEngine: FlutterEngine) {
+ try {
+ flutterEngine.getPlugins()?.add(new ImagePickerPlugin());
+ flutterEngine.getPlugins()?.add(new ImagecropperOhosPlugin());
+ } catch (e) {
+ Log.e(
+ TAG,
+ "Tried to register plugins with FlutterEngine ("
+ + flutterEngine
+ + ") failed.");
+ Log.e(TAG, "Received exception while registering", e);
+ }
+ }
+}
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/module.json5 b/image_cropper/ohos/example/ohos/entry/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..7bbf78b18f39991b1404061c7437538c7d532bb7
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/module.json5
@@ -0,0 +1,53 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "phone"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ],
+ "requestPermissions": [
+ {"name" : "ohos.permission.INTERNET"},
+ ]
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/color.json b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/string.json b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..de9d6a9330c67debfbf9d0b349200af6f22e51b7
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "imagecropper_ohos_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/base/media/icon.png b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/media/icon.png differ
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "pages/Index"
+ ]
+}
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/en_US/element/string.json b/image_cropper/ohos/example/ohos/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..de9d6a9330c67debfbf9d0b349200af6f22e51b7
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/resources/en_US/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "imagecropper_ohos_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/image_cropper/ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..7e6e9e4b20ee3cbdf2d99de1cd82272d2f33c8c6
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "模块描述"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "imagecropper_ohos_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..25d4c71ff3cd584f5d64f6f8c0ac864928c234c4
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
@@ -0,0 +1,50 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import hilog from '@ohos.hilog';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
+
+export default function abilityTest() {
+ describe('ActsAbilityTest', function () {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(function () {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ })
+ beforeEach(function () {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ })
+ afterEach(function () {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ })
+ afterAll(function () {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ })
+ it('assertContain',0, function () {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
+ let a = 'abc'
+ let b = 'b'
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b)
+ expect(a).assertEqual(a)
+ })
+ })
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..f4140030e65d20df6af30a6bf51e464dea8f8aa6
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,20 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import abilityTest from './Ability.test'
+
+export default function testsuite() {
+ abilityTest()
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..4ca645e6013cfce8e7dbb728313cb8840c4da660
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
@@ -0,0 +1,63 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import UIAbility from '@ohos.app.ability.UIAbility';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import hilog from '@ohos.hilog';
+import { Hypium } from '@ohos/hypium';
+import testsuite from '../test/List.test';
+import window from '@ohos.window';
+
+export default class TestAbility extends UIAbility {
+ onCreate(want, launchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
+ var abilityDelegator: any
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ var abilityDelegatorArguments: any
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ windowStage.loadContent('testability/pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
+ JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
+
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
+
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..cef0447cd2f137ef82d223ead2e156808878ab90
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import hilog from '@ohos.hilog';
+
+@Entry
+@Component
+struct Index {
+ aboutToAppear() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
+ }
+ @State message: string = 'Hello World'
+ build() {
+ Row() {
+ Column() {
+ Text(this.message)
+ .fontSize(50)
+ .fontWeight(FontWeight.Bold)
+ Button() {
+ Text('next page')
+ .fontSize(20)
+ .fontWeight(FontWeight.Bold)
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .width('35%')
+ .height('5%')
+ .onClick(()=>{
+ })
+ }
+ .width('100%')
+ }
+ .height('100%')
+ }
+ }
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1def08f2e9dcbfa3454a07b7a3b82b173bb90d02
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts
@@ -0,0 +1,64 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import hilog from '@ohos.hilog';
+import TestRunner from '@ohos.application.testRunner';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+
+var abilityDelegator = undefined
+var abilityDelegatorArguments = undefined
+
+async function onAbilityCreateCallback() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
+}
+
+async function addAbilityMonitorCallback(err: any) {
+ hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
+}
+
+export default class OpenHarmonyTestRunner implements TestRunner {
+ constructor() {
+ }
+
+ onPrepare() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
+ }
+
+ async onRun() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'
+ let lMonitor = {
+ abilityName: testAbilityName,
+ onAbilityCreate: onAbilityCreateCallback,
+ };
+ abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
+ var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName
+ var debug = abilityDelegatorArguments.parameters['-D']
+ if (debug == 'true')
+ {
+ cmd += ' -D'
+ }
+ hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
+ abilityDelegator.executeShellCommand(cmd,
+ (err: any, d: any) => {
+ hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
+ hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
+ hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
+ })
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/module.json5 b/image_cropper/ohos/example/ohos/entry/src/ohosTest/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/module.json5
@@ -0,0 +1,51 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+{
+ "module": {
+ "name": "entry_test",
+ "type": "feature",
+ "description": "$string:module_test_desc",
+ "mainElement": "TestAbility",
+ "deviceTypes": [
+ "phone"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:test_pages",
+ "abilities": [
+ {
+ "name": "TestAbility",
+ "srcEntry": "./ets/testability/TestAbility.ets",
+ "description": "$string:TestAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:TestAbility_label",
+ "exported": true,
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "skills": [
+ {
+ "actions": [
+ "action.system.home"
+ ],
+ "entities": [
+ "entity.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_test_desc",
+ "value": "test ability description"
+ },
+ {
+ "name": "TestAbility_desc",
+ "value": "the test ability"
+ },
+ {
+ "name": "TestAbility_label",
+ "value": "test label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ
diff --git a/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "testability/pages/Index"
+ ]
+}
diff --git a/image_cropper/ohos/example/ohos/hvigor/hvigor-config.json5 b/image_cropper/ohos/example/ohos/hvigor/hvigor-config.json5
new file mode 100644
index 0000000000000000000000000000000000000000..3bcb859e88b72cc2dcc6ec1bc89d26a6411cb2d8
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/hvigor/hvigor-config.json5
@@ -0,0 +1,23 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+{
+ "modelVersion": "5.0.0",
+ "dependencies": {
+ },
+ "properties": {
+ "ohos.nativeResolver": false
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/hvigorfile.ts b/image_cropper/ohos/example/ohos/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f2d2aafe6d6a3a71a9944ebd0c91fbc308ac9d1
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/hvigorfile.ts
@@ -0,0 +1,21 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { appTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/ohos/oh-package.json5 b/image_cropper/ohos/example/ohos/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..07c4aaf2bea8f33efcce4840a796690d7cc81af6
--- /dev/null
+++ b/image_cropper/ohos/example/ohos/oh-package.json5
@@ -0,0 +1,21 @@
+{
+ "modelVersion": "5.0.0",
+ "name": "imagecropper_ohos_example",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {
+ "@ohos/flutter_ohos": "file:./har/flutter.har"
+ },
+ "devDependencies": {
+ "@ohos/hypium": "1.0.6"
+ },
+ "overrides": {
+ "@ohos/flutter_ohos": "file:./har/flutter.har",
+ "imagecropper_ohos": "file:./har/imagecropper_ohos.har",
+ "@ohos/flutter_module": "file:./entry",
+ "image_picker_ohos": "file:./har/image_picker_ohos.har"
+ }
+}
\ No newline at end of file
diff --git a/image_cropper/ohos/example/pubspec.yaml b/image_cropper/ohos/example/pubspec.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..94e36a387992dd7a95e0198f4f545fc8e8c45a63
--- /dev/null
+++ b/image_cropper/ohos/example/pubspec.yaml
@@ -0,0 +1,96 @@
+name: imagecropper_ohos_example
+description: Demonstrates how to use the imagecropper_ohos plugin.
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: '>=2.19.6 <3.0.0'
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+ imagecropper_ohos:
+ # When depending on this package from a real application you should use:
+ # imagecropper_ohos: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+ dotted_border: ^2.1.0
+ image_picker:
+ git:
+ url: https://gitee.com/openharmony-sig/flutter_packages.git
+ path: packages/image_picker/image_picker
+
+dependency_overrides:
+ image_picker_ohos:
+ git:
+ url: https://gitee.com/openharmony-sig/flutter_packages.git
+ path: packages/image_picker/image_picker_ohos
+ ref: br_optimize_har_structure
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^2.0.0
+
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+# assets:
+# - images/a_dot_burr.jpeg
+# - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/image_cropper/ohos/example/test/widget_test.dart b/image_cropper/ohos/example/test/widget_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d575f3332bf06b4111cb9894684e7a65a9bd24be
--- /dev/null
+++ b/image_cropper/ohos/example/test/widget_test.dart
@@ -0,0 +1,27 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:imagecropper_ohos_example/main.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(const MyApp());
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) => widget is Text &&
+ widget.data!.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/image_cropper/ohos/lib/imagecropper_ohos.dart b/image_cropper/ohos/lib/imagecropper_ohos.dart
new file mode 100644
index 0000000000000000000000000000000000000000..50c1895fd13cbbc58ee580b9c4490a1ed0402898
--- /dev/null
+++ b/image_cropper/ohos/lib/imagecropper_ohos.dart
@@ -0,0 +1,38 @@
+import 'dart:io';
+import 'dart:ui';
+
+import 'imagecropper_ohos_platform_interface.dart';
+
+class ImagecropperOhos {
+
+ Future sampleImage({
+ required String path,
+ required int maximumSize,
+ }) async {
+ String? filePath = await ImagecropperOhosPlatform.instance.sampleImage(path: path,maximumSize: maximumSize);
+ return File(filePath!);
+ }
+
+ Future cropImage({
+ required File file,
+ required Rect area,
+ double? scale,
+ double? angle,
+ double? cx,
+ double? cy,
+ }) async {
+ String? path = await ImagecropperOhosPlatform.instance.cropImage(
+ sourcePath: file.path,
+ area: area,
+ scale: scale,
+ angle: angle,
+ cx: cx,
+ cy: cy,
+ );
+ return File(path!);
+ }
+
+ Future recoverImage() async {
+ return await ImagecropperOhosPlatform.instance.recoverImage();
+ }
+}
diff --git a/image_cropper/ohos/lib/imagecropper_ohos_method_channel.dart b/image_cropper/ohos/lib/imagecropper_ohos_method_channel.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d3ab7e4cb8ebd5aec74342892def972a4ba0e322
--- /dev/null
+++ b/image_cropper/ohos/lib/imagecropper_ohos_method_channel.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+
+import 'imagecropper_ohos_platform_interface.dart';
+
+/// An implementation of [ImagecropperOhosPlatform] that uses method channels.
+class MethodChannelImagecropperOhos extends ImagecropperOhosPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ final methodChannel = const MethodChannel('imagecropper');
+
+
+ @override
+ Future sampleImage({
+ required String path,
+ required int maximumSize,
+ }) async {
+ String? sampleFile = await methodChannel.invokeMethod('sampleImage',{
+ 'path':path,
+ 'maximumWidth':maximumSize,
+ 'maximumHeight':maximumSize,
+ });
+ return sampleFile;
+ }
+
+ @override
+ Future cropImage(
+ {required String sourcePath,
+ required Rect area,
+ double? scale,
+ double? angle,
+ double? cx,
+ double? cy,
+ }) async {
+
+ final path = await methodChannel.invokeMethod('cropImage',{
+ 'path': sourcePath,
+ 'left': area.left,
+ 'top': area.top,
+ 'right': area.right,
+ 'bottom': area.bottom,
+ 'scale': scale ?? 1.0,
+ 'angle': angle ?? 0,
+ 'cx': cx ?? 0,
+ 'cy':cy ?? 0,
+ });
+ return path;
+ }
+
+ @override
+ Future recoverImage() async {
+ final path = await methodChannel.invokeMethod('recoverImage');
+ return path;
+ }
+}
diff --git a/image_cropper/ohos/lib/imagecropper_ohos_platform_interface.dart b/image_cropper/ohos/lib/imagecropper_ohos_platform_interface.dart
new file mode 100644
index 0000000000000000000000000000000000000000..794a98567a673fbfeed072886792e47181dfb752
--- /dev/null
+++ b/image_cropper/ohos/lib/imagecropper_ohos_platform_interface.dart
@@ -0,0 +1,49 @@
+import 'dart:ui';
+
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+import 'imagecropper_ohos_method_channel.dart';
+
+abstract class ImagecropperOhosPlatform extends PlatformInterface {
+ /// Constructs a ImagecropperOhosPlatform.
+ ImagecropperOhosPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static ImagecropperOhosPlatform _instance = MethodChannelImagecropperOhos();
+
+ /// The default instance of [ImagecropperOhosPlatform] to use.
+ ///
+ /// Defaults to [MethodChannelImagecropperOhos].
+ static ImagecropperOhosPlatform get instance => _instance;
+
+ /// Platform-specific implementations should set this with their own
+ /// platform-specific class that extends [ImagecropperOhosPlatform] when
+ /// they register themselves.
+ static set instance(ImagecropperOhosPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ Future sampleImage({
+ required String path,
+ required int maximumSize,
+ }) {
+ throw UnimplementedError('sampleImage() has not been implemented.');
+ }
+
+ Future cropImage({
+ required String sourcePath,
+ required Rect area,
+ double? scale,
+ double? angle,
+ double? cx,
+ double? cy,
+ }) {
+ throw UnimplementedError('platformVersion() has not been implemented.');
+ }
+
+ Future recoverImage() {
+ throw UnimplementedError('platformVersion() has not been implemented.');
+ }
+}
diff --git a/image_cropper/ohos/lib/page/crop.dart b/image_cropper/ohos/lib/page/crop.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5447d4c6a85d8057f753c6f6f2ab81ce30501785
--- /dev/null
+++ b/image_cropper/ohos/lib/page/crop.dart
@@ -0,0 +1,849 @@
+import 'dart:io';
+import 'dart:math';
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'dart:ui' as ui;
+
+const _kCropGridColumnCount = 3;
+const _kCropGridRowCount = 3;
+const _kCropGridColor = Color.fromRGBO(0xd0, 0xd0, 0xd0, 0.9);
+const _kCropOverlayActiveOpacity = 0.3;
+const _kCropOverlayInactiveOpacity = 0.7;
+const _kCropHandleColor = Color.fromRGBO(0xd0, 0xd0, 0xd0, 1.0);
+const _kCropHandleSize = 10.0;
+const _kCropHandleHitSize = 48.0;
+const _kCropMinFraction = 0.1;
+
+enum _CropAction { none, moving, cropping, scaling }
+
+enum _CropHandleSide { none, topLeft, topRight, bottomLeft, bottomRight }
+
+class RotateController {
+ Function(double angle)? rotate;
+
+ void dispose() {
+ rotate = null;
+ }
+}
+
+class Crop extends StatefulWidget {
+ final ImageProvider image;
+ final double? aspectRatio;
+ final double maximumScale;
+ final bool alwaysShowGrid;
+ final ImageErrorListener? onImageError;
+ final RotateController? rotateController;
+
+ const Crop({
+ Key? key,
+ required this.image,
+ this.aspectRatio,
+ this.maximumScale = 2.0,
+ this.alwaysShowGrid = true,
+ this.onImageError,
+ this.rotateController,
+ }) : super(key: key);
+
+ Crop.file(
+ File file, {
+ Key? key,
+ double scale = 1.0,
+ this.aspectRatio,
+ this.maximumScale = 2.0,
+ this.alwaysShowGrid = true,
+ this.onImageError,
+ this.rotateController,
+ }) : image = FileImage(file, scale: scale),
+ super(key: key);
+
+ Crop.asset(
+ String assetName, {
+ Key? key,
+ AssetBundle? bundle,
+ String? package,
+ this.aspectRatio,
+ this.maximumScale = 2.0,
+ this.alwaysShowGrid = true,
+ this.onImageError,
+ this.rotateController,
+ }) : image = AssetImage(assetName, bundle: bundle, package: package),
+ super(key: key);
+
+ @override
+ State createState() => CropState();
+
+ static CropState? of(BuildContext context) =>
+ context.findAncestorStateOfType();
+}
+
+class CropState extends State with TickerProviderStateMixin {
+ final _surfaceKey = GlobalKey();
+
+ late final AnimationController _activeController;
+ late final AnimationController _settleController;
+
+ double _scale = 1.0;
+ double _ratio = 1.0;
+ double angle = 0;
+ double cx = 0;
+ double cy = 0;
+ Rect _view = Rect.zero;
+ Rect _area = Rect.zero;
+ Offset _lastFocalPoint = Offset.zero;
+ _CropAction _action = _CropAction.none;
+ _CropHandleSide _handle = _CropHandleSide.none;
+
+ late double _startAngle;
+ late double _startScale;
+ late Rect _startView;
+ late Tween _viewTween;
+ late Tween _scaleTween;
+
+ ImageStream? _imageStream;
+ ui.Image? _image;
+ ImageStreamListener? _imageListener;
+
+ double get scale => _area.shortestSide / _scale;
+
+ Rect? get area => _view.isEmpty
+ ? null
+ : Rect.fromLTWH(
+ max(_area.left * _view.width / _scale - _view.left, 0),
+ max(_area.top * _view.height / _scale - _view.top, 0),
+ _area.width * _view.width / _scale,
+ _area.height * _view.height / _scale,
+ );
+
+ bool get _isEnabled => _view.isEmpty == false && _image != null;
+
+ // Saving the length for the widest area for different aspectRatio's
+ final Map _maxAreaWidthMap = {};
+
+ // Counting pointers(number of user fingers on screen)
+ int pointers = 0;
+
+ @override
+ void initState() {
+ super.initState();
+
+ _activeController = AnimationController(
+ vsync: this,
+ value: widget.alwaysShowGrid ? 1.0 : 0.0,
+ )..addListener(() => setState(() {}));
+ _settleController = AnimationController(vsync: this)
+ ..addListener(_settleAnimationChanged);
+ }
+
+ @override
+ void dispose() {
+ final listener = _imageListener;
+ if (listener != null) {
+ _imageStream?.removeListener(listener);
+ }
+ _activeController.dispose();
+ _settleController.dispose();
+
+ super.dispose();
+ }
+
+ @override
+ void didChangeDependencies() {
+ super.didChangeDependencies();
+
+ _getImage();
+ }
+
+ @override
+ void didUpdateWidget(Crop oldWidget) {
+ super.didUpdateWidget(oldWidget);
+
+ if (widget.image != oldWidget.image) {
+ _getImage();
+ } else if (widget.aspectRatio != oldWidget.aspectRatio) {
+ _area = _calculateDefaultArea(
+ viewWidth: _view.width,
+ viewHeight: _view.height,
+ imageWidth: _image?.width,
+ imageHeight: _image?.height,
+ );
+ }
+ if (widget.alwaysShowGrid != oldWidget.alwaysShowGrid) {
+ if (widget.alwaysShowGrid) {
+ _activate();
+ } else {
+ _deactivate();
+ }
+ }
+ }
+
+ void _getImage({bool force = false}) {
+ final oldImageStream = _imageStream;
+ final newImageStream =
+ widget.image.resolve(createLocalImageConfiguration(context));
+ _imageStream = newImageStream;
+ if (newImageStream.key != oldImageStream?.key || force) {
+ final oldImageListener = _imageListener;
+ if (oldImageListener != null) {
+ oldImageStream?.removeListener(oldImageListener);
+ }
+ final newImageListener =
+ ImageStreamListener(_updateImage, onError: widget.onImageError);
+ _imageListener = newImageListener;
+ newImageStream.addListener(newImageListener);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) => ConstrainedBox(
+ constraints: const BoxConstraints.expand(),
+ child: Listener(
+ onPointerDown: (event) => pointers++,
+ onPointerUp: (event) => pointers = 0,
+ child: GestureDetector(
+ key: _surfaceKey,
+ behavior: HitTestBehavior.opaque,
+ onScaleStart: _isEnabled ? _handleScaleStart : null,
+ onScaleUpdate: _isEnabled ? _handleScaleUpdate : null,
+ onScaleEnd: _isEnabled ? _handleScaleEnd : null,
+ child: CustomPaint(
+ painter: _CropPainter(
+ image: _image,
+ ratio: _ratio,
+ view: _view,
+ area: _area,
+ scale: _scale,
+ angle: angle,
+ active: _activeController.value,
+ callback: (x, y) {
+ cx = x;
+ cy = y;
+ }),
+ ),
+ ),
+ ),
+ );
+
+ void _activate() {
+ _activeController.animateTo(
+ 1.0,
+ curve: Curves.fastOutSlowIn,
+ duration: const Duration(milliseconds: 250),
+ );
+ }
+
+ void _deactivate() {
+ if (widget.alwaysShowGrid == false) {
+ _activeController.animateTo(
+ 0.0,
+ curve: Curves.fastOutSlowIn,
+ duration: const Duration(milliseconds: 250),
+ );
+ }
+ }
+
+ Size? get _boundaries {
+ final context = _surfaceKey.currentContext;
+ if (context == null) {
+ return null;
+ }
+
+ final size = context.size;
+ if (size == null) {
+ return null;
+ }
+
+ return size - const Offset(_kCropHandleSize, _kCropHandleSize) as Size;
+ }
+
+ Offset? _getLocalPoint(Offset point) {
+ final context = _surfaceKey.currentContext;
+ if (context == null) {
+ return null;
+ }
+
+ final box = context.findRenderObject() as RenderBox;
+
+ return box.globalToLocal(point);
+ }
+
+ void _settleAnimationChanged() {
+ setState(() {
+ _scale = _scaleTween.transform(_settleController.value);
+ final nextView = _viewTween.transform(_settleController.value);
+ if (nextView != null) {
+ _view = nextView;
+ }
+ });
+ }
+
+ Rect _calculateDefaultArea({
+ required int? imageWidth,
+ required int? imageHeight,
+ required double viewWidth,
+ required double viewHeight,
+ double? defaultSize,
+ }) {
+ if (imageWidth == null || imageHeight == null) {
+ return Rect.zero;
+ }
+
+ double height;
+ double width;
+ if ((widget.aspectRatio ?? 1.0) < 1) {
+ height = defaultSize ?? 1.0;
+ width =
+ ((widget.aspectRatio ?? 1.0) * imageHeight * viewHeight * height) /
+ imageWidth /
+ viewWidth;
+ if (width > 1.0) {
+ width = 1.0;
+ height = (imageWidth * viewWidth * width) /
+ (imageHeight * viewHeight * (widget.aspectRatio ?? 1.0));
+ }
+ } else {
+ width = defaultSize ?? 1.0;
+ height = (imageWidth * viewWidth * width) /
+ (imageHeight * viewHeight * (widget.aspectRatio ?? 1.0));
+ if (height > 1.0) {
+ height = 1.0;
+ width =
+ ((widget.aspectRatio ?? 1.0) * imageHeight * viewHeight * height) /
+ imageWidth /
+ viewWidth;
+ }
+ }
+ final aspectRatio = _maxAreaWidthMap[widget.aspectRatio];
+ if (aspectRatio != null) {
+ _maxAreaWidthMap[aspectRatio] = width;
+ }
+
+ return Rect.fromLTWH((1.0 - width) / 2, (1.0 - height) / 2, width, height);
+ }
+
+ void _updateImage(ImageInfo imageInfo, bool synchronousCall) {
+ final boundaries = _boundaries;
+ if (boundaries == null) {
+ return;
+ }
+
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ final image = imageInfo.image;
+
+ setState(() {
+ _image = image;
+ _scale = imageInfo.scale;
+ _ratio = max(
+ boundaries.width / image.width,
+ boundaries.height / image.height,
+ );
+
+ final viewWidth = boundaries.width / (image.width * _scale * _ratio);
+ final viewHeight = boundaries.height / (image.height * _scale * _ratio);
+ _area = _calculateDefaultArea(
+ viewWidth: viewWidth,
+ viewHeight: viewHeight,
+ imageWidth: image.width,
+ imageHeight: image.height,
+ defaultSize: 0.9,
+ );
+ _view = Rect.fromLTWH(
+ (viewWidth - 1.0) / 2,
+ (viewHeight - 1.0) / 2,
+ viewWidth,
+ viewHeight,
+ );
+ });
+ });
+
+ WidgetsBinding.instance.ensureVisualUpdate();
+ }
+
+ _CropHandleSide _hitCropHandle(Offset? localPoint) {
+ final boundaries = _boundaries;
+ if (localPoint == null || boundaries == null) {
+ return _CropHandleSide.none;
+ }
+
+ final viewRect = Rect.fromLTWH(
+ boundaries.width * _area.left,
+ boundaries.height * _area.top,
+ boundaries.width * _area.width,
+ boundaries.height * _area.height,
+ ).deflate(_kCropHandleSize / 2);
+
+ if (Rect.fromLTWH(
+ viewRect.left - _kCropHandleHitSize / 2,
+ viewRect.top - _kCropHandleHitSize / 2,
+ _kCropHandleHitSize,
+ _kCropHandleHitSize,
+ ).contains(localPoint)) {
+ return _CropHandleSide.topLeft;
+ }
+
+ if (Rect.fromLTWH(
+ viewRect.right - _kCropHandleHitSize / 2,
+ viewRect.top - _kCropHandleHitSize / 2,
+ _kCropHandleHitSize,
+ _kCropHandleHitSize,
+ ).contains(localPoint)) {
+ return _CropHandleSide.topRight;
+ }
+
+ if (Rect.fromLTWH(
+ viewRect.left - _kCropHandleHitSize / 2,
+ viewRect.bottom - _kCropHandleHitSize / 2,
+ _kCropHandleHitSize,
+ _kCropHandleHitSize,
+ ).contains(localPoint)) {
+ return _CropHandleSide.bottomLeft;
+ }
+
+ if (Rect.fromLTWH(
+ viewRect.right - _kCropHandleHitSize / 2,
+ viewRect.bottom - _kCropHandleHitSize / 2,
+ _kCropHandleHitSize,
+ _kCropHandleHitSize,
+ ).contains(localPoint)) {
+ return _CropHandleSide.bottomRight;
+ }
+
+ return _CropHandleSide.none;
+ }
+
+ void _handleScaleStart(ScaleStartDetails details) {
+ _activate();
+ _settleController.stop(canceled: false);
+ _lastFocalPoint = details.focalPoint;
+ _action = _CropAction.none;
+ _handle = _hitCropHandle(_getLocalPoint(details.focalPoint));
+ _startScale = _scale;
+ _startView = _view;
+ _startAngle = angle;
+ }
+
+ Rect _getViewInBoundaries(double scale) =>
+ Offset(
+ max(
+ min(
+ _view.left,
+ _area.left * _view.width / scale,
+ ),
+ _area.right * _view.width / scale - 1.0,
+ ),
+ max(
+ min(
+ _view.top,
+ _area.top * _view.height / scale,
+ ),
+ _area.bottom * _view.height / scale - 1.0,
+ ),
+ ) &
+ _view.size;
+
+ double get _maximumScale => widget.maximumScale;
+
+ double? get _minimumScale {
+ final boundaries = _boundaries;
+ final image = _image;
+ if (boundaries == null || image == null) {
+ return null;
+ }
+
+ final scaleX = boundaries.width * _area.width / (image.width * _ratio);
+ final scaleY = boundaries.height * _area.height / (image.height * _ratio);
+ return min(_maximumScale, max(scaleX, scaleY));
+ }
+
+ void _handleScaleEnd(ScaleEndDetails details) {
+ _deactivate();
+ final minimumScale = _minimumScale;
+ if (minimumScale == null) {
+ return;
+ }
+
+ final targetScale = _scale.clamp(minimumScale, _maximumScale);
+ _scaleTween = Tween(
+ begin: _scale,
+ end: targetScale,
+ );
+
+ _startView = _view;
+ _viewTween = RectTween(
+ begin: _view,
+ end: _getViewInBoundaries(targetScale),
+ );
+
+ _settleController.value = 0.0;
+ _settleController.animateTo(
+ 1.0,
+ curve: Curves.fastOutSlowIn,
+ duration: const Duration(milliseconds: 350),
+ );
+ }
+
+ void _updateArea({
+ required _CropHandleSide cropHandleSide,
+ double? left,
+ double? top,
+ double? right,
+ double? bottom,
+ }) {
+ final image = _image;
+ if (image == null) {
+ return;
+ }
+
+ var areaLeft = _area.left + (left ?? 0.0);
+ var areaBottom = _area.bottom + (bottom ?? 0.0);
+ var areaTop = _area.top + (top ?? 0.0);
+ var areaRight = _area.right + (right ?? 0.0);
+ double width = areaRight - areaLeft;
+ double height = (image.width * _view.width * width) /
+ (image.height * _view.height * (widget.aspectRatio ?? 1.0));
+ final maxAreaWidth = _maxAreaWidthMap[widget.aspectRatio];
+ if ((height >= 1.0 || width >= 1.0) && maxAreaWidth != null) {
+ height = 1.0;
+
+ if (cropHandleSide == _CropHandleSide.bottomLeft ||
+ cropHandleSide == _CropHandleSide.topLeft) {
+ areaLeft = areaRight - maxAreaWidth;
+ } else {
+ areaRight = areaLeft + maxAreaWidth;
+ }
+ }
+
+ // ensure minimum rectangle
+ if (areaRight - areaLeft < _kCropMinFraction) {
+ if (left != null) {
+ areaLeft = areaRight - _kCropMinFraction;
+ } else {
+ areaRight = areaLeft + _kCropMinFraction;
+ }
+ }
+
+ if (areaBottom - areaTop < _kCropMinFraction) {
+ if (top != null) {
+ areaTop = areaBottom - _kCropMinFraction;
+ } else {
+ areaBottom = areaTop + _kCropMinFraction;
+ }
+ }
+
+ // adjust to aspect ratio if needed
+ final aspectRatio = widget.aspectRatio;
+ if (aspectRatio != null && aspectRatio > 0.0) {
+ if (top != null) {
+ areaTop = areaBottom - height;
+ if (areaTop < 0.0) {
+ areaTop = 0.0;
+ areaBottom = height;
+ }
+ } else {
+ areaBottom = areaTop + height;
+ if (areaBottom > 1.0) {
+ areaTop = 1.0 - height;
+ areaBottom = 1.0;
+ }
+ }
+ }
+
+ // ensure to remain within bounds of the view
+ if (areaLeft < 0.0) {
+ areaLeft = 0.0;
+ areaRight = _area.width;
+ } else if (areaRight > 1.0) {
+ areaLeft = 1.0 - _area.width;
+ areaRight = 1.0;
+ }
+
+ if (areaTop < 0.0) {
+ areaTop = 0.0;
+ areaBottom = _area.height;
+ } else if (areaBottom > 1.0) {
+ areaTop = 1.0 - _area.height;
+ areaBottom = 1.0;
+ }
+
+ setState(() {
+ _area = Rect.fromLTRB(areaLeft, areaTop, areaRight, areaBottom);
+ });
+ }
+
+ void _handleScaleUpdate(ScaleUpdateDetails details) {
+ if (_action == _CropAction.none) {
+ if (_handle == _CropHandleSide.none) {
+ _action = pointers == 2 ? _CropAction.scaling : _CropAction.moving;
+ } else {
+ _action = _CropAction.cropping;
+ }
+ }
+
+ if (_action == _CropAction.cropping) {
+ final boundaries = _boundaries;
+ if (boundaries == null) {
+ return;
+ }
+
+ final delta = details.focalPoint - _lastFocalPoint;
+ _lastFocalPoint = details.focalPoint;
+
+ final dx = delta.dx / boundaries.width;
+ final dy = delta.dy / boundaries.height;
+
+ if (_handle == _CropHandleSide.topLeft) {
+ _updateArea(left: dx, top: dy, cropHandleSide: _CropHandleSide.topLeft);
+ } else if (_handle == _CropHandleSide.topRight) {
+ _updateArea(
+ top: dy, right: dx, cropHandleSide: _CropHandleSide.topRight);
+ } else if (_handle == _CropHandleSide.bottomLeft) {
+ _updateArea(
+ left: dx, bottom: dy, cropHandleSide: _CropHandleSide.bottomLeft);
+ } else if (_handle == _CropHandleSide.bottomRight) {
+ _updateArea(
+ right: dx, bottom: dy, cropHandleSide: _CropHandleSide.bottomRight);
+ }
+ } else if (_action == _CropAction.moving) {
+ final image = _image;
+ if (image == null) {
+ return;
+ }
+
+ final delta = details.focalPoint - _lastFocalPoint;
+ _lastFocalPoint = details.focalPoint;
+
+ final an = angle / 180 * pi;
+
+ var dx1 = delta.dy * sin(an);
+ var dx2 = delta.dx * cos(an);
+
+ var dy1 = delta.dy * cos(an);
+ var dy2 = -delta.dx * sin(an);
+
+ setState(() {
+ _view = _view.translate(
+ (dx1 + dx2) / (image.width * _scale * _ratio),
+ (dy1 + dy2) / (image.height * _scale * _ratio),
+ );
+ });
+ } else if (_action == _CropAction.scaling) {
+ final image = _image;
+ final boundaries = _boundaries;
+ if (image == null || boundaries == null) {
+ return;
+ }
+
+ setState(() {
+ _scale = _startScale * details.scale;
+ angle = _startAngle + details.rotation / pi * 180;
+
+ final dx = boundaries.width *
+ (1.0 - details.scale) /
+ (image.width * _scale * _ratio);
+ final dy = boundaries.height *
+ (1.0 - details.scale) /
+ (image.height * _scale * _ratio);
+
+ _view = Rect.fromLTWH(
+ _startView.left + dx / 2,
+ _startView.top + dy / 2,
+ _startView.width,
+ _startView.height,
+ );
+ });
+ }
+ }
+}
+
+class _CropPainter extends CustomPainter {
+ final ui.Image? image;
+ final Rect view;
+ final double ratio;
+ final Rect area;
+ final double scale;
+ final double active;
+ final double angle;
+ final Function(double cx, double cy) callback;
+
+ _CropPainter({
+ required this.image,
+ required this.view,
+ required this.ratio,
+ required this.area,
+ required this.scale,
+ required this.active,
+ required this.angle,
+ required this.callback,
+ });
+
+ @override
+ bool shouldRepaint(_CropPainter oldDelegate) {
+ return oldDelegate.image != image ||
+ oldDelegate.view != view ||
+ oldDelegate.ratio != ratio ||
+ oldDelegate.area != area ||
+ oldDelegate.active != active ||
+ oldDelegate.scale != scale;
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final rect = Rect.fromLTWH(
+ _kCropHandleSize / 2,
+ _kCropHandleSize / 2,
+ size.width - _kCropHandleSize,
+ size.height - _kCropHandleSize,
+ );
+ final boundaries = Rect.fromLTWH(
+ rect.width * area.left,
+ rect.height * area.top,
+ rect.width * area.width,
+ rect.height * area.height,
+ );
+
+ canvas.save();
+ canvas.translate(rect.left, rect.top);
+
+ final paint = Paint()..isAntiAlias = false;
+
+ final image = this.image;
+ if (image != null) {
+ final src = Rect.fromLTWH(
+ 0.0,
+ 0.0,
+ image.width.toDouble(),
+ image.height.toDouble(),
+ );
+ final dst = Rect.fromLTWH(
+ view.left * image.width * scale * ratio,
+ view.top * image.height * scale * ratio,
+ image.width * scale * ratio,
+ image.height * scale * ratio,
+ );
+
+ //--任意中心
+ var cx = (boundaries.right + boundaries.left) * 0.5; //旋转中心=画布中心
+ var cy = (boundaries.bottom + boundaries.top) * 0.5; //旋转中心=画布中心
+
+ var cyr = cy - dst.top;
+ var cxr = cx - dst.left;
+
+ callback(cxr, cyr);
+
+ canvas.save();
+ canvas.translate(cx, cy);
+ canvas.rotate(angle * pi / 180);
+ canvas.translate(-cx, -cy);
+ canvas.drawImageRect(image, src, dst, paint);
+ canvas.restore();
+ }
+
+ paint.color = Color.fromRGBO(
+ 0x0,
+ 0x0,
+ 0x0,
+ _kCropOverlayActiveOpacity * active +
+ _kCropOverlayInactiveOpacity * (1.0 - active));
+
+ canvas.drawRect(Rect.fromLTRB(0.0, 0.0, rect.width, boundaries.top), paint);
+ canvas.drawRect(
+ Rect.fromLTRB(0.0, boundaries.bottom, rect.width, rect.height), paint);
+ canvas.drawRect(
+ Rect.fromLTRB(0.0, boundaries.top, boundaries.left, boundaries.bottom),
+ paint);
+ canvas.drawRect(
+ Rect.fromLTRB(
+ boundaries.right, boundaries.top, rect.width, boundaries.bottom),
+ paint);
+
+ if (boundaries.isEmpty == false) {
+ _drawGrid(canvas, boundaries);
+ _drawHandles(canvas, boundaries);
+ }
+
+ canvas.restore();
+ }
+
+ void _drawHandles(Canvas canvas, Rect boundaries) {
+ final paint = Paint()
+ ..isAntiAlias = true
+ ..color = _kCropHandleColor;
+
+ canvas.drawOval(
+ Rect.fromLTWH(
+ boundaries.left - _kCropHandleSize / 2,
+ boundaries.top - _kCropHandleSize / 2,
+ _kCropHandleSize,
+ _kCropHandleSize,
+ ),
+ paint,
+ );
+
+ canvas.drawOval(
+ Rect.fromLTWH(
+ boundaries.right - _kCropHandleSize / 2,
+ boundaries.top - _kCropHandleSize / 2,
+ _kCropHandleSize,
+ _kCropHandleSize,
+ ),
+ paint,
+ );
+
+ canvas.drawOval(
+ Rect.fromLTWH(
+ boundaries.right - _kCropHandleSize / 2,
+ boundaries.bottom - _kCropHandleSize / 2,
+ _kCropHandleSize,
+ _kCropHandleSize,
+ ),
+ paint,
+ );
+
+ canvas.drawOval(
+ Rect.fromLTWH(
+ boundaries.left - _kCropHandleSize / 2,
+ boundaries.bottom - _kCropHandleSize / 2,
+ _kCropHandleSize,
+ _kCropHandleSize,
+ ),
+ paint,
+ );
+ }
+
+ void _drawGrid(Canvas canvas, Rect boundaries) {
+ if (active == 0.0) return;
+
+ final paint = Paint()
+ ..isAntiAlias = false
+ ..color = _kCropGridColor.withOpacity(_kCropGridColor.opacity * active)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 1.0;
+
+ final path = Path()
+ ..moveTo(boundaries.left, boundaries.top)
+ ..lineTo(boundaries.right, boundaries.top)
+ ..lineTo(boundaries.right, boundaries.bottom)
+ ..lineTo(boundaries.left, boundaries.bottom)
+ ..lineTo(boundaries.left, boundaries.top);
+
+ for (var column = 1; column < _kCropGridColumnCount; column++) {
+ path
+ ..moveTo(
+ boundaries.left + column * boundaries.width / _kCropGridColumnCount,
+ boundaries.top)
+ ..lineTo(
+ boundaries.left + column * boundaries.width / _kCropGridColumnCount,
+ boundaries.bottom);
+ }
+
+ for (var row = 1; row < _kCropGridRowCount; row++) {
+ path
+ ..moveTo(boundaries.left,
+ boundaries.top + row * boundaries.height / _kCropGridRowCount)
+ ..lineTo(boundaries.right,
+ boundaries.top + row * boundaries.height / _kCropGridRowCount);
+ }
+
+ canvas.drawPath(path, paint);
+ }
+}
diff --git a/image_cropper/ohos/ohos/.gitignore b/image_cropper/ohos/ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b6d28399b850023ef4b353c50a119b2250b21099
--- /dev/null
+++ b/image_cropper/ohos/ohos/.gitignore
@@ -0,0 +1,10 @@
+/node_modules
+/oh_modules
+/.preview
+/.idea
+/build
+/.cxx
+/.test
+/BuildProfile.ets
+/oh-package-lock.json5
+/local.properties
diff --git a/image_cropper/ohos/ohos/build-profile.json5 b/image_cropper/ohos/ohos/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..79961f96a6fe0507354b7952a378c3be2ae4bfab
--- /dev/null
+++ b/image_cropper/ohos/ohos/build-profile.json5
@@ -0,0 +1,10 @@
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ },
+ "targets": [
+ {
+ "name": "default"
+ }
+ ]
+}
diff --git a/image_cropper/ohos/ohos/hvigorfile.ts b/image_cropper/ohos/ohos/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47e6e1f81d365872f101585f5dbf816bcad65864
--- /dev/null
+++ b/image_cropper/ohos/ohos/hvigorfile.ts
@@ -0,0 +1,2 @@
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+export { harTasks } from '@ohos/hvigor-ohos-plugin';
\ No newline at end of file
diff --git a/image_cropper/ohos/ohos/index.ets b/image_cropper/ohos/ohos/index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..29cdcacbbbade8e51535a310d32f265d9175c7a0
--- /dev/null
+++ b/image_cropper/ohos/ohos/index.ets
@@ -0,0 +1,17 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import ImagecropperOhosPlugin from './src/main/ets/components/plugin/ImagecropperOhosPlugin';
+export default ImagecropperOhosPlugin;
diff --git a/image_cropper/ohos/ohos/oh-package.json5 b/image_cropper/ohos/ohos/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..e3d9e32211b39567a649816d47ca870999f4516e
--- /dev/null
+++ b/image_cropper/ohos/ohos/oh-package.json5
@@ -0,0 +1,11 @@
+{
+ "name": "imagecropper_ohos",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "index.ets",
+ "author": "",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ohos/flutter_ohos": "file:./har/flutter.har"
+ }
+}
diff --git a/image_cropper/ohos/ohos/src/main/ets/components/plugin/ImagecropperOhosPlugin.ets b/image_cropper/ohos/ohos/src/main/ets/components/plugin/ImagecropperOhosPlugin.ets
new file mode 100644
index 0000000000000000000000000000000000000000..89903cf9b696a94e43a22bac8ed5e930b2d45bcb
--- /dev/null
+++ b/image_cropper/ohos/ohos/src/main/ets/components/plugin/ImagecropperOhosPlugin.ets
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ FlutterPlugin,
+ FlutterPluginBinding
+} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
+import MethodChannel, {
+ MethodCallHandler,
+ MethodResult
+} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
+import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
+import { AbilityAware, AbilityPluginBinding } from '@ohos/flutter_ohos';
+import image from '@ohos.multimedia.image';
+import { BusinessError } from '@ohos.base';
+import fs from '@ohos.file.fs';
+import { HashMap } from '@kit.ArkTS';
+
+import { photoAccessHelper } from '@kit.MediaLibraryKit';
+
+class ImageOptions {
+ private width: number = 0;
+ private height: number = 0;
+ private degrees: number = 0;
+
+ constructor(width: number, height: number, degrees: number) {
+ this.width = width;
+ this.height = height;
+ this.degrees = degrees;
+ }
+
+ getHeight(): number {
+ return (this.isFlippedDimensions() && this.degrees != 180) ? this.width : this.height;
+ }
+
+ getWidth(): number {
+ return (this.isFlippedDimensions() && this.degrees != 180) ? this.height : this.width;
+ }
+
+ getDegrees(): number {
+ return this.degrees;
+ }
+
+ isFlippedDimensions(): boolean {
+ return this.degrees == 90 || this.degrees == 270 || this.degrees == 180;
+ }
+
+ isRotated(): boolean {
+ return this.degrees != 0;
+ }
+}
+
+export default class ImagecropPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware {
+ private channel?: MethodChannel = undefined;
+ private abilityBinding?: AbilityPluginBinding = undefined;
+
+ constructor() {
+ }
+
+ onAttachedToAbility(binding: AbilityPluginBinding): void {
+ this.abilityBinding = binding;
+ }
+
+ onDetachedFromAbility(): void {
+
+ }
+
+ getUniqueClassName(): string {
+ return "ImagecropPlugin1"
+ }
+
+ onAttachedToEngine(binding: FlutterPluginBinding): void {
+ this.channel = new MethodChannel(binding.getBinaryMessenger(), "imagecropper");
+ this.channel.setMethodCallHandler(this)
+ }
+
+ onDetachedFromEngine(binding: FlutterPluginBinding): void {
+ if (this.channel != null) {
+ this.channel.setMethodCallHandler(null)
+ }
+ }
+
+ onMethodCall(call: MethodCall, result: MethodResult): void {
+ if ("cropImage" == call.method) {
+ let path: string = call.argument("path");
+ let scale: number = call.argument("scale");
+ let left: number = call.argument("left");
+ let top: number = call.argument("top");
+ let right: number = call.argument("right");
+ let bottom: number = call.argument("bottom");
+ let angle: number = call.argument("angle");
+ let cx: number = call.argument("cx");
+ let cy: number = call.argument("cy");
+ try {
+ this.cropImage(result, path, scale, left, top, right, bottom,angle,cx,cy);
+ } catch (e) {
+ result.error("INVALID", "Image source cannot be opened", null);
+ }
+ } else if ("sampleImage" == call.method) {
+ let path: string = call.argument("path");
+ let maximumWidth: number = call.argument("maximumWidth");
+ let maximumHeight: number = call.argument("maximumHeight");
+ try {
+ this.sampleImage(result, path, maximumWidth, maximumHeight);
+ } catch (e) {
+ result.error("INVALID", "SampleImage error", null);
+ }
+ } else if ("getImageOptions" == call.method) {
+ let path: string = call.argument("path");
+ this.getImageOptions(path, result);
+ } else if ("requestPermissions" == call.method) {
+ this.requestPermissions(result);
+ } else {
+ result.notImplemented();
+ }
+ }
+
+ private async cropImage(result: MethodResult, path: string, scale: number, left: number, top: number, right: number, bottom: number,angle: number,cx: number,cy:number) {
+ try {
+ let imageSource = image.createImageSource(path);
+ let pixelMapData = await imageSource.createPixelMap();
+ let options = await this.decodeImageOptions(imageSource);
+ await pixelMapData.rotate(angle);
+
+ let width = options.getWidth() * (right - left) * scale;
+ let height = options.getHeight() * (bottom - top) * scale;
+ let rAngle = 2 * Math.PI - angle/180*Math.PI;
+ let originX = options.getWidth() * left;
+ let originY = -options.getHeight() * top;
+
+ //第三象限旋转中心坐标
+ let x0 = (options.getWidth() * left + width/2);
+ let y0 = - (options.getHeight() * top + height/2);
+
+ //获取图片4个角旋转后的坐标
+ let leftTopX = 0;
+ let leftTopY = 0;
+ let leftTopX2 = (leftTopX - x0) * Math.cos(rAngle) - (leftTopY - y0) * Math.sin(rAngle) + x0;
+ let leftTopY2 = (leftTopX - x0) * Math.sin(rAngle) + (leftTopY - y0) * Math.cos(rAngle) + y0;
+
+ let leftBottomX = 0;
+ let leftBottomY = -options.getHeight();
+ let leftBottomX2 = (leftBottomX - x0) * Math.cos(rAngle) - (leftBottomY - y0) * Math.sin(rAngle) + x0;
+ let leftBottomY2 = (leftBottomX - x0) * Math.sin(rAngle) + (leftBottomY - y0) * Math.cos(rAngle) + y0;
+
+ let rightTopX = options.getWidth();
+ let rightTopY = 0;
+ let rightTopX2 = (rightTopX - x0) * Math.cos(rAngle) - (rightTopY - y0) * Math.sin(rAngle) + x0;
+ let rightTopY2 = (rightTopX - x0) * Math.sin(rAngle) + (rightTopY - y0) * Math.cos(rAngle) + y0;
+
+ let rightBottomX = options.getWidth();
+ let rightBottomY = -options.getHeight();
+ let rightBottomX2 = (rightBottomX - x0) * Math.cos(rAngle) - (rightBottomY - y0) * Math.sin(rAngle) + x0;
+ let rightBottomY2 = (rightBottomX - x0) * Math.sin(rAngle) + (rightBottomY - y0) * Math.cos(rAngle) + y0;
+
+ //比较旋转后的4个角的坐标,取出x轴上的最小值和y轴上的最大值,用来做初始点的偏移量
+ let verifyX = Math.min(leftTopX2,leftBottomX2,rightTopX2,rightBottomX2);
+ let verifyY = Math.max(leftTopY2,leftBottomY2,rightTopY2,rightBottomY2);
+
+ let region: image.Region = {
+ x: originX - verifyX, y: Math.abs(originY - verifyY), size: {
+ height: height, width: width
+ }
+ };
+
+ await pixelMapData.crop(region)
+ let dstFile = this.createTemporaryImageFilePath();
+ await this.savaPixelMap(pixelMapData, dstFile);
+ result.success(dstFile);
+ } catch (e) {
+ }
+ }
+
+ private async sampleImage(result: MethodResult, path: string, maximumWidth: number, maximumHeight: number) {
+ let imageSource = image.createImageSource(path);
+ let options = await this.decodeImageOptions(imageSource);
+ // 缩略图采样大小,当前只能取1。
+ let inSampleSize = 1;//this.calculateInSampleSize(options.getWidth(), options.getHeight(), maximumWidth, maximumHeight);
+ let pixelMapData = await imageSource.createPixelMap({sampleSize: inSampleSize});
+ if (options.getWidth() > maximumWidth && options.getHeight() > maximumHeight) {
+ let ratio = Math.max(maximumWidth / options.getWidth(), maximumHeight / options.getHeight());
+ await pixelMapData.scale(ratio, ratio);
+ }
+ let dstFile = this.createTemporaryImageFilePath();
+ await this.savaPixelMap(pixelMapData, dstFile);
+ await this.copyExif(path, dstFile);
+ result.success(dstFile);
+ }
+
+ private async getImageOptions(path: string, result: MethodResult) {
+ let options = await this.decodeImageOptions(image.createImageSource(path));
+ let properties = new HashMap();
+ properties.set("width", options.getWidth());
+ properties.set("height", options.getHeight());
+ result.success(properties);
+ }
+
+ private requestPermissions(result: MethodResult) {
+ result.success(true);
+ }
+
+ private decodeImageOptions(imageSource: image.ImageSource): Promise {
+ return new Promise((resolve, reject) => {
+ let imageInfo: image.ImageInfo;
+ imageSource.getImageInfo().then((info: image.ImageInfo) => {
+ imageInfo = info
+ return imageSource.getImageProperty(image.PropertyKey.ORIENTATION)
+ }).then((result: string) => {
+ let value = Number.parseInt(result)
+ resolve(new ImageOptions(imageInfo.size.width, imageInfo.size.height, value ? value : 0));
+ }).catch((err: BusinessError) => {
+ resolve(new ImageOptions(imageInfo.size.width, imageInfo.size.height, 0));
+ })
+ })
+ }
+
+ private createTemporaryImageFilePath(): string {
+ let directory = this.abilityBinding?.getAbility().context.cacheDir
+ let name = "/image_crop_" + new Date().getTime().toString() + ".jpg";
+ return directory + name
+ }
+
+ private async savaPixelMap(pixelMap: image.PixelMap, path: string) {
+ let options: image.PackingOption = {
+ format: 'image/jpeg',
+ quality: 100
+ };
+ let packer = image.createImagePacker();
+ let imageBuffer = await packer.packing(pixelMap, options);
+ let stream = fs.createStreamSync(path, "a+");
+ stream.writeSync(imageBuffer);
+ stream.closeSync();
+ await pixelMap.release()
+ await packer.release()
+ }
+
+ private async copyExif(source: string, destination: string) {
+ let tags = Array();
+ tags.push(image.PropertyKey.F_NUMBER);
+ tags.push(image.PropertyKey.EXPOSURE_TIME);
+ tags.push(image.PropertyKey.FOCAL_LENGTH);
+ tags.push(image.PropertyKey.GPS_DATE_STAMP);
+ tags.push(image.PropertyKey.WHITE_BALANCE);
+ tags.push(image.PropertyKey.GPS_TIME_STAMP);
+ tags.push(image.PropertyKey.DATE_TIME);
+ tags.push(image.PropertyKey.FLASH);
+ tags.push(image.PropertyKey.GPS_LATITUDE);
+ tags.push(image.PropertyKey.GPS_LATITUDE_REF);
+ tags.push(image.PropertyKey.GPS_LONGITUDE);
+ tags.push(image.PropertyKey.GPS_LONGITUDE_REF);
+ tags.push(image.PropertyKey.MAKE);
+ tags.push(image.PropertyKey.MODEL);
+ tags.push(image.PropertyKey.ORIENTATION);
+
+ let sourceExif = image.createImageSource(source);
+ let destinationExif = image.createImageSource(destination);
+
+ sourceExif.getImageProperty(tags[0]).then(async (data:string)=>{
+ if (data != null) {
+ await destinationExif.modifyImageProperty(tags[0], data);
+ }
+ }).catch((error: BusinessError)=>{
+ console.info(`error: ${error}`);
+ })
+ }
+
+ private calculateInSampleSize(width: number, height: number, maximumWidth: number, maximumHeight: number): number {
+ let inSampleSize = 1;
+ if (height > maximumHeight || width > maximumWidth) {
+ let halfHeight = height / 2;
+ let halfWidth = width / 2;
+ while ((halfHeight / inSampleSize) >= maximumHeight && (halfWidth / inSampleSize) >= maximumWidth) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+ }
+}
diff --git a/image_cropper/ohos/ohos/src/main/module.json5 b/image_cropper/ohos/ohos/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..5c235b630941a775484569f631a6b6a70ac74b67
--- /dev/null
+++ b/image_cropper/ohos/ohos/src/main/module.json5
@@ -0,0 +1,10 @@
+{
+ "module": {
+ "name": "imagecropper_ohos",
+ "type": "har",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ]
+ }
+}
diff --git a/image_cropper/ohos/pubspec.yaml b/image_cropper/ohos/pubspec.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..661db14e263b68f08354a90d3bd01da18e9920a7
--- /dev/null
+++ b/image_cropper/ohos/pubspec.yaml
@@ -0,0 +1,71 @@
+name: imagecropper_ohos
+description: A Flutter plugin for OHOS supports cropping images
+version: 1.0.0
+homepage: https://gitee.com/openharmony-sig/fluttertpc_image_cropper
+
+environment:
+ sdk: '>=2.19.6 <4.0.0'
+ flutter: ">=2.5.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ plugin_platform_interface: ^2.0.2
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^2.0.0
+
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+ # This section identifies this Flutter project as a plugin project.
+ # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
+ # which should be registered in the plugin registry. This is required for
+ # using method channels.
+ # The Android 'package' specifies package in which the registered class is.
+ # This is required for using method channels on Android.
+ # The 'ffiPlugin' specifies that native code should be built and bundled.
+ # This is required for using `dart:ffi`.
+ # All these are used by the tooling to maintain consistency when
+ # adding or updating assets for this project.
+ plugin:
+ platforms:
+ ohos:
+ package: vn.hunghd.flutter.plugins.imagecropper
+ pluginClass: ImagecropperOhosPlugin
+
+ # To add assets to your plugin package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.dev/assets-and-images/#from-packages
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware
+
+ # To add custom fonts to your plugin package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.dev/custom-fonts/#from-packages
diff --git a/image_cropper/ohos/test/imagecropper_ohos_test.dart b/image_cropper/ohos/test/imagecropper_ohos_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a00aabb77a18b981f1293370b8342e6b2eec2972
--- /dev/null
+++ b/image_cropper/ohos/test/imagecropper_ohos_test.dart
@@ -0,0 +1,45 @@
+import 'dart:io';
+import 'dart:ui';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:imagecropper_ohos/imagecropper_ohos.dart';
+import 'package:imagecropper_ohos/imagecropper_ohos_platform_interface.dart';
+import 'package:imagecropper_ohos/imagecropper_ohos_method_channel.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+class MockImagecropperOhosPlatform
+ with MockPlatformInterfaceMixin
+ implements ImagecropperOhosPlatform {
+
+
+ @override
+ Future cropImage({required String sourcePath, required Rect area, double? scale, double? angle, double? cx, double? cy}) async {
+ return "cropImage";
+ }
+
+ @override
+ Future recoverImage() async{
+ return "recoverImage";
+ }
+
+ @override
+ Future sampleImage({required String path, required int maximumSize}) async{
+ return "sampleImage";
+ }
+}
+
+void main() {
+ final ImagecropperOhosPlatform initialPlatform = ImagecropperOhosPlatform.instance;
+
+ test('$MethodChannelImagecropperOhos is the default instance', () {
+ expect(initialPlatform, isInstanceOf());
+ });
+
+ test('cropImage', () async {
+ ImagecropperOhos imagecropperOhosPlugin = ImagecropperOhos();
+ MockImagecropperOhosPlatform fakePlatform = MockImagecropperOhosPlatform();
+ ImagecropperOhosPlatform.instance = fakePlatform;
+
+ expect((await imagecropperOhosPlugin.cropImage(file: File("filePath"), area: Rect.zero)).path, 'cropImage');
+ });
+}