fluttercandies / extended_image

A powerful official extension library of image, which support placeholder(loading)/ failed state, cache network, zoom pan image, photo view, slide out page, editor(crop,rotate,flip), paint custom etc.
https://fluttercandies.github.io/extended_image/
MIT License
1.94k stars 505 forks source link

[Bug report] Crop Area Changed Unexpectedly #713

Open Peng-Qian opened 4 days ago

Peng-Qian commented 4 days ago

Version

^9.0.6

Platforms

Android, iOS

Device Model

emulator, iPhone 15 Pro Plus

flutter info

[✓] Flutter (Channel stable, 3.24.4, on macOS 15.1.1 24B91 darwin-arm64, locale en-NZ)
    • Flutter version 3.24.4 on channel stable at /Users/qian/Developer/SDK/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 603104015d (4 weeks ago), 2024-10-24 08:01:25 -0700
    • Engine revision db49896cf2
    • Dart version 3.5.4
    • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
    • Android SDK at /Users/qian/Library/Android/sdk
    • Platform android-35, build-tools 35.0.0
    • ANDROID_SDK_ROOT = /Users/qian/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16B40
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)

[✓] IntelliJ IDEA Community Edition (version 2024.1.4)
    • IntelliJ at /Users/qian/Applications/IntelliJ IDEA Community Edition.app
    • Flutter plugin version 82.1.2
    • Dart plugin version 241.18808

[✓] VS Code (version 1.77.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.62.0

[✓] Connected device (5 available)
    • iPhone 15 Q (mobile)            • 00008130-000C58C90E8B803A            • ios            • iOS 18.0.1 22A3370
    • iPhone 16 Pro Max (mobile)      • 292E7C90-95EA-4FA4-BFD8-7612F9E8D2E5 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-18-1 (simulator)
    • macOS (desktop)                 • macos                                • darwin-arm64   • macOS 15.1.1 24B91 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad                • darwin         • macOS 15.1.1 24B91 darwin-arm64
    • Chrome (web)                    • chrome                               • web-javascript • Google Chrome 131.0.6778.86

[✓] Network resources
    • All expected network resources are available.

How to reproduce?

1.  Run the code.
2.  Move the crop selector’s upper edge to the image boundary and beyond it.
3.  Observe that the left crop edge changes unexpectedly.
4.  Move the crop selector’s lower edge to the image boundary and beyond it.
5.  Observe that the right crop edge changes unexpectedly.

Logs

No response

Example code (optional)

import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

final _key = GlobalKey();
final _editorKey = GlobalKey<ExtendedImageEditorState>();
final _gestureKey = GlobalKey<ExtendedImageGestureState>();

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _controller = ImageEditorController();
  String info = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme
            .of(context)
            .colorScheme
            .inversePrimary,
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Text(info),
          Flexible(
            child: ExtendedImage.network(
              'https://gratisography.com/wp-content/uploads/2024/03/gratisography-funflower-1170x780.jpg',
              key: _key,
              extendedImageGestureKey: _gestureKey,
              extendedImageEditorKey: _editorKey,
              fit: BoxFit.contain,
              mode: ExtendedImageMode.editor,
              enableLoadState: true,
              enableMemoryCache: true,
              enableSlideOutPage: true,
              cache: true,
              imageCacheName: 'image_editor',
              borderRadius: BorderRadius.circular(33),
              clipBehavior: Clip.antiAlias,
              initEditorConfigHandler: (state) {
                return EditorConfig(
                  maxScale: 8.0,
                  cropRectPadding: const EdgeInsets.all(20),
                  hitTestSize: 20,
                  controller: _controller,
                  initCropRectType: InitCropRectType.imageRect,
                  cornerColor: Colors.white,
                  lineColor: Colors.white,
                  editActionDetailsIsChanged: (details) {
                    setState(() {
                      info = 'rotateDegrees: ${_controller.rotateDegrees}\n'
                          'cropAspectRatio: ${_controller.cropAspectRatio}\n'
                          'cropRect: ${_controller.editActionDetails?.cropRect}\n';
                    });
                  },
                );
              },
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () =>
            setState(() {
              info = 'rotateDegrees: ${_controller.rotateDegrees}\n'
                  'cropAspectRatio: ${_controller.cropAspectRatio}\n'
                  'cropRect: ${_controller.editActionDetails?.cropRect}\n';
            }),
        tooltip: 'refresh',
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

Contact

No response

Peng-Qian commented 4 days ago

https://github.com/user-attachments/assets/a5ef83ce-65ee-48fc-9753-96c670897081

https://github.com/user-attachments/assets/1013ed9b-0d14-410b-9d72-0bf8a13e943d

zmtzawqlp commented 4 days ago

This is auto center function, you can check android / ios album

Peng-Qian commented 4 days ago

This issue is not related to picture zooming or recentering but rather to the unexpected behavior of the selection area. For instance, when I drag the selection box upward, the selection area unexpectedly shrinks to the right. Additionally, this functionality is not present on either iOS or Android.

Peng-Qian commented 4 days ago

上面的视频可能有误导,上面的视频我并没有调节左右的裁剪区域,我只是调节上下的裁剪区域,第一个视频是我向上拉到顶后,左手边开始缩小。第二个视频是向下拉到顶后,右手边开始缩小,如下图所示

Simulator Screenshot - iPhone 16 Pro Max - 2024-11-25 at 15 24 53

zmtzawqlp commented 4 days ago

拖动到边界的时候继续拖拽,会试图缩放图片来满足条件的,这也是系统相册的效果

Peng-Qian commented 2 days ago

拖动到边界的时候继续拖拽,会试图缩放图片来满足条件的,这也是系统相册的效果

我似乎记得好像以前系统相册是有过类似的功能,但是目前我检查了最新的系统相册,这些功能已经被弃用了,我个人觉得是因为会导致错误操作和误导。我尝试修复了问题,你可以在example中测试一下效果,是否用户体验会更好~

我的pull request中,还修复了一些已知问题和加入了一些新功能:

  1. 修复了旋转图片并要剪裁框同时旋转时候,连续旋转会导致图片变小。
  2. 修复了只能向右旋转才能触发剪裁框同时旋转,现在向左向右都可以触发
  3. 改变crop aspect ratio的时候,提供动画,这里我觉得可以提供参数以让用户选择是否要动画,但是目前我的用例都要动画,所有我没有加入过多代码,也减轻你code review的工作量
  4. 改变crop aspect ratio的时候,正确的提供图片大小改变。
  5. 我同时还修复了强制重置设置和剪裁动作,我个人觉得这个设计违背了stateful widget设计模式,就是这样的设计widget并没有保持state的能力了,而且widget的update也没有按照预期实现。我目前的所有用例和测试中,并没有出现需要强制重置的问题。所以我觉得可以安全的弃用强制重置

简而言之,更新后的crop基础功能和基础动画与ios相册最新crop设计的几乎一样~ 希望你喜欢这些更新,如果可以的话,请merge到官方版本中,这样,我也就不用维护个人版本啦~哈哈~

请注意,我并没有做代码清理和优化,为了方便你快速阅读,我尽可能使用已有的方法和代码来实现新功能和修复bug~

Peng-Qian commented 2 days ago

https://github.com/user-attachments/assets/47b33cd5-8257-4cea-b443-bb168771948c

https://github.com/user-attachments/assets/30589cea-da6f-4307-b7fb-bbc7d2dd3aa2

zmtzawqlp commented 2 days ago

首先,很感谢你使用该组件,也感谢你的 pr。

scaleToFit 有多个地方使用到了,小心修改。

修改为动画,需要注意历史状态的保存问题。

正确的情况是因为根据 新旧 widget 的参数,对当前状态进行修改。当前直接重置,只是因为情况太多了,没法一一进行处理。

updateCropRect 方法,你要考虑图片已经发生了旋转的情况,不是简单跟 imageRect 比较就可以了的

zmtzawqlp commented 2 days ago

除了 修复了只能向右旋转才能触发剪裁框同时旋转,现在向左向右都可以触发 ,其他改进,建议保持在您的个人分支上面。

zmtzawqlp commented 2 days ago

强制重置设置 , 这个问题,我空了再看下。

Peng-Qian commented 20 hours ago

updateCropRect 方法,你要考虑图片已经发生了旋转的情况,不是简单跟 imageRect 比较就可以了的

感谢你的tips,确实很快发现并解决了两个bug~