Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick "Done" or "Cancel" Triggers Double Close in Cropper View #527

Open
irenegordun opened this issue Sep 15, 2024 · 1 comment
Open

Quick "Done" or "Cancel" Triggers Double Close in Cropper View #527

irenegordun opened this issue Sep 15, 2024 · 1 comment

Comments

@irenegordun
Copy link

When using the flutter_image_cropper package, an issue occurs when there is an open modal in the Flutter app and the native crop view is displayed for image cropping. If the user presses the "Done" or "Cancel" button quickly (in rapid succession), the close action is executed twice. This results in not only closing the crop view but also closing the underlying modal unintentionally.

Steps to Reproduce:
Open a modal in your Flutter app.
Launch the native image cropper view by selecting an image to crop.
Press the "Done" or "Cancel" button quickly, before the crop view fully loads or responds.
Observe that the crop view closes, but the underlying modal also closes unexpectedly.
Expected Behavior:
The crop view should close without affecting the underlying modal. Only the cropper view should be dismissed when "Done" or "Cancel" is pressed, and the first modal should remain open.

Actual Behavior:
Pressing the "Done" or "Cancel" button in the crop view rapidly causes the crop view to close twice, which also closes the parent modal unintentionally.

Version Information:
flutter_image_cropper version: 8.0.2
Platform: iOS

@irenegordun
Copy link
Author

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.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'),
    );
  }
}

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

  final String title;

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

enum CropSettings {
  planImage(1500, 750),
  userImage(500, 500);

  final int height, width;

  const CropSettings(this.height, this.width);
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextButton(
              child: Text('press to open the modal'),
              onPressed: () {
                showModalBottomSheet(
                    context: context,
                    builder: (context) {
                      return Container(
                        color: Colors.amber,
                        child: Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: <Widget>[
                              TextButton(
                                onPressed: () => openImagePicker(),
                                child: Text('Select an image'),
                              )
                            ],
                          ),
                        ),
                      );
                    });
              },
            )
          ],
        ),
      ),
    );
  }

  Future<void> openImagePicker() async {
    pickImage(ImageSource.gallery).then((pickedImage) {
      if (pickedImage != null) {
        cropImage(pickedImage.path, CropSettings.userImage).then((croppedImage) {});
      }
    }).catchError((_) {
      if (!context.mounted) return;
    });
  }

  Future<XFile?> cropImage(String path, CropSettings settings) async {
    if (!await File(path).exists()) {
      print("File does not exist at path: $path");
      return null;
    }
    final CroppedFile? croppedImage = await ImageCropper().cropImage(
      sourcePath: path,
      aspectRatio: CropAspectRatio(ratioY: settings.height.toDouble(), ratioX: settings.width.toDouble()),
      maxHeight: settings.height,
      maxWidth: settings.width,
      compressQuality: 50,
      uiSettings: [
        AndroidUiSettings(
          toolbarTitle: '',
          statusBarColor: Colors.black,
          toolbarColor: Colors.black,
          toolbarWidgetColor: Colors.white,
          activeControlsWidgetColor: Colors.yellow,
        ),
        IOSUiSettings(
          aspectRatioLockEnabled: true,
          aspectRatioPickerButtonHidden: true,
          resetButtonHidden: true,
          showCancelConfirmationDialog: true,
        ),
      ],
    );

    return croppedImage != null ? XFile(croppedImage.path) : null;
  }

 Future<XFile?> pickImage(ImageSource source) async => await ImagePicker().pickImage(source: source, requestFullMetadata: false);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant