fluttercontrollerflutter-getximage-cropperflutter-image-picker

Image cropper not working in GetxController


The following is the code i was trying to implement in. The future method pickImage i guess it is the one having the problem. I am extending the class with GetxController. The method is expected to pick and crop the selected image using the image cropper, and then set the cropped image to the imageFile variable if the cropping was successful.

import 'dart:io';

import 'package:pamoja/app/data/const.dart';
import 'package:pamoja/app/data/firebase/firebase_functions.dart';
import 'package:pamoja/app/data/global_widgets/indicator.dart';
import 'package:pamoja/app/models/advert_model.dart';
import 'package:pamoja/app/modules/my_adverts/controllers/my_adverts_controller.dart';
import 'package:pamoja/app/routes/app_pages.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_cropper/image_cropper.dart';

class UploadBlogController extends GetxController {
  TextEditingController title = TextEditingController();
  TextEditingController description = TextEditingController();
  TextEditingController location = TextEditingController();
  TextEditingController category = TextEditingController();
  TextEditingController price = TextEditingController();
  TextEditingController phone = TextEditingController();
  final FirebaseFunctions _functions = FirebaseFunctions();
  File? imageFile;

  Future<void> pickImage() async {
    try {
      ImagePicker _picker = ImagePicker();

      await _picker.pickImage(source: ImageSource.gallery).then((value) async {
        if (value != null) {
          File? croppedFile = await ImageCropper().cropImage(
            sourcePath: value.path,
            aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),
            compressQuality: 100,
            maxWidth: 700,
            maxHeight: 700,
            // saveCircleCroppedImage: true,
          );

          if (croppedFile != null) {
            imageFile = croppedFile;
            update();
          }
        }
      });
    } catch (e) {
      showAlert("$e");
    }
  }

  void createBlog() async {
    if (title.text.isNotEmpty && description.text.isNotEmpty) {
      if (imageFile != null && imageFile != "") {
        Indicator.showLoading();

        await _functions
            .uploadBlog(
          title.text,
          description.text,
          imageFile!,
          price.text,
          category.text,
          location.text,
          phone.text,
        )
            .then((value) {
          Indicator.closeLoading();
          showAlert("Your advert created sucessfully");
          // Get.back();
          Get.toNamed(Routes.HOME);
        });
      } else {
        showAlert("Image is required");
      }
    } else {
      showAlert("All fields are required");
    }
  }

  void editBlog(BlogsModel model) async {
    Indicator.showLoading();

    if (title.text.isNotEmpty && description.text.isNotEmpty) {
      if (imageFile == null) {
        Map<String, dynamic> map = {
          'title': title.text,
          'description': description.text,
        };

        await _functions.editBlog(model.id, map).then((value) {
          Get.toNamed(Routes.HOME);
          showAlert("Your ads Updated Sucessfully");
        });
      } else {
        String imageUrl = await _functions.uploadImage(imageFile!);

        Map<String, dynamic> map = {
          'title': title.text,
          'description': description.text,
          'img': imageUrl,
        };

        await _functions.editBlog(model.id, map).then((value) {
          Get.toNamed(Routes.HOME);
          showAlert("Your Advert Updated Sucessfully");
        });
      }
    } else {
      showAlert("All fields are required");
    }

    Indicator.closeLoading();
    updateData();
  }

  void updateData() {
    Get.back();
    Get.toNamed(Routes.HOME);
    if (Get.isRegistered<MyBlogsController>()) {
      final controller = Get.find<MyBlogsController>();

      controller.myBlogs = [];
      Indicator.showLoading();
      controller.getMyBlogData();
    }
  }
}


Solution

  • this i created reusable single-tone code for my entire app to pick media image or video. you can try or modify according your requirement.

    // Dart imports:
    import 'dart:io';
    
    // Package imports:
    import 'package:image_cropper/image_cropper.dart';
    import 'package:image_picker/image_picker.dart';
    
    // Project imports:
    import 'package:app/utils/export_utils.dart';
    
    class MediaPicker {
      MediaPicker._();
    
      static const supportedImageFormates = [".jpeg", ".png"];
      static const supportedVedioFormates = [".mp4"];
      static final picker = ImagePicker();
    
      // Single image / video selection
      static Future<File?> pickMedia({
        required ImageSource source,
        bool isVideo = false,
        bool isEditing = false,
      }) async {
        try {
          final pickMedia = !isVideo
              ? await picker.pickImage(source: source)
              : await picker.pickVideo(source: source);
    
          if (pickMedia != null) {
            return isEditing
                ? await editImage(file: File(pickMedia.path))
                : File(pickMedia.path);
          } else {
            return null;
          }
        } catch (ex) {
          "Pick Media error: $ex".printLog();
          return null;
        }
      }
    
      static Future<File> editImage({required File file}) async {
        final CroppedFile? croppedFile = await ImageCropper().cropImage(
          sourcePath: file.path,
          uiSettings: [
            AndroidUiSettings(
              toolbarTitle: 'Cropper',
              toolbarColor: ColorConst.themePrimaryColor,
              toolbarWidgetColor: ColorConst.white,
              initAspectRatio: CropAspectRatioPreset.original,
              lockAspectRatio: false,
            ),
            IOSUiSettings(
              title: 'Cropper',
            ),
          ],
        );
    
        if (croppedFile != null) {
          return File(croppedFile.path);
        } else {
          return file;
        }
      }
    }
    

    Usage add call method on button tap with Future & async method

    Future<void> btnPickImageTap() async {
        final File? selectImageFile = await MediaPicker.pickMedia(
          source: ImageSource.gallery, 
          isEditing: true, // Default false
        );
    }
    

    source: ImageSource.camera - to pick image from camera. 
    isEditing: false - avoid image cropping functionality.
    isVideo: to pick video from gallery / camera.
    

    Make sure following permission added for both platforms

    Andriod- AndroidManifest.xml

        <uses-permission android:name="android.permission.CAMERA"/> //Camera
       <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> // Read Storage 
    

    iOS Runner/info.plist

    <key>NSCameraUsageDescription</key>
        <string>App require camera permission to update profile pic</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>App require gallery permission to update profile pic</string>
    

    As package mentioned in image_cropper add bellow code in AndroidManifest.xml under <applcaiton> TAG

    <activity
      android:name="com.yalantis.ucrop.UCropActivity"
      android:screenOrientation="portrait"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>