Image cropper not working in GetxController - flutter

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();
}
}
}

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"/>

I modified it following your instruction it worked. The following is a sample modification
Future<void> pickImage() async {
try {
ImagePicker _picker = ImagePicker();
await _picker.pickImage(source: ImageSource.gallery).then((value) async {
if (value != null) {
imageFile = File(value.path);
showAlert(imageFile.toString());
editImage(imageFile: imageFile);
// cropImage(imageFile);
} else {
showAlert("No image selected");
}
});
} catch (e) {
showAlert("$e");
}}
Future<void> editImage({required File? imageFile}) async {
final File? croppedFile = await ImageCropper().cropImage(
sourcePath: imageFile!.path,
androidUiSettings: AndroidUiSettings(
toolbarTitle: 'Advert Image',
toolbarColor: Colors.green.shade400,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
iosUiSettings: IOSUiSettings(
title: 'Cropper',
),
);
if (croppedFile != null) {
imageFile = File(croppedFile.path);
update();
} else {
// update();
}}

Related

How to download a file on flutter?

How to download a .pdf file from an URL with POST Request having headers in Flutter?
i have a different pdf files for each user and i want the link to be different depend on the user.
You can use flutter_downloader plugin for downloading your files.
dependencies:
flutter_downloader: ^1.8.4
And also you can use this codebase for that:
import 'dart:io';
import 'dart:ui';
import 'package:fimber/fimber.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadingService {
static const downloadingPortName = 'downloading';
static Future<void> createDownloadTask(String url) async {
final _storagePermission = await _permissionGranted();
Fimber.d('Current storage permission: $_storagePermission');
if (!_storagePermission) {
final _status = await Permission.storage.request();
if (!_status.isGranted) {
Fimber.d('Permission wasnt granted. Cancelling downloading');
return;
}
}
final _path = await _getPath();
Fimber.d('Downloading path $_path');
if (_path == null) {
Fimber.d('Got empty path. Cannot start downloading');
return;
}
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: _path,
showNotification: true,
// show download progress in status bar (for Android)
openFileFromNotification: true,
// click on notification to open downloaded file (for Android)
saveInPublicStorage: true);
await Future.delayed(const Duration(seconds: 1));
if (taskId != null) {
await FlutterDownloader.open(taskId: taskId);
}
}
static Future<bool> _permissionGranted() async {
return await Permission.storage.isGranted;
}
static Future<String?> _getPath() async {
if (Platform.isAndroid) {
final _externalDir = await getExternalStorageDirectory();
return _externalDir?.path;
}
return (await getApplicationDocumentsDirectory()).absolute.path;
}
static downloadingCallBack(id, status, progress) {
final _sendPort = IsolateNameServer.lookupPortByName(downloadingPortName);
if (_sendPort != null) {
_sendPort.send([id, status, progress]);
} else {
Fimber.e('SendPort is null. Cannot find isolate $downloadingPortName');
}
}
}
And in another page you can use this class to download your file:
final _receivePort = ReceivePort();
#override
void initState() {
super.initState();
IsolateNameServer.registerPortWithName(
_receivePort.sendPort, DownloadingService.downloadingPortName);
FlutterDownloader.registerCallback(DownloadingService.downloadingCallBack);
_receivePort.listen((message) {
Fimber.d('Got message from port: $message');
});
}
#override
void dispose() {
_receivePort.close();
super.dispose();
}
void _downloadFile() async {
try {
await DownloadingService.createDownloadTask(url.toString());
} catch (e) {
print("error")
}
}

How can I upload mutiple-photo in flutter app via ImgePicker

I want to add the function which can upload multiple Photo image via ImagePicker
In this code, I can just upload single photo, not mutiple.
This app operating by flutter, dart and firebase server.
[Code]
void dispose() {
textEditingController.dispose();
super.dispose();
}
File _image;
Future _getImage() async {
var image = await ImagePicker.pickImage(
source: ImageSource.gallery,
maxWidth: 1000,
maxHeight: 1000,
);
setState(() {
_image = image;
});
}
Future _uploadFile(BuildContext context) async {
if (_image != null) {
final firebaseStorageRef = FirebaseStorage.instance
.ref()
.child('post')
.child('${DateTime.now().millisecondsSinceEpoch}.png');
final task = firebaseStorageRef.putFile(
_image,
StorageMetadata(contentType: 'image/png'),
);
final storageTaskSnapshot = await task.onComplete;
final downloadUrl = await storageTaskSnapshot.ref.getDownloadURL();
await Firestore.instance.collection('post').add(
{
'contents': textEditingController.text,
'displayName': widget.user.displayName,
'email': widget.user.email,
'photoUrl': downloadUrl,
'userPhotoUrl': widget.user.photoUrl,
});
}
final images = await _picker.pickMultiImage(
maxHeight: 1024,
maxWidth: 1024,
imageQuality: 50,
);
I created here 3 functions used to pick files from imagePicker and to upload them to firebase storage.
first, pick images from gallery:
final imageFiles = await pickImages();
second, upload the images:
final path = 'path/where/you/want/to/save/your/images';
final imageUrls = uploadImages(imagesFiles, path)
print(imageUrls);
you can now use the images urls to save to firestore
Future<List<File>> pickeImages() async {
ImagePicker picker = ImagePicker();
final images = await picker.pickMultiImage(
maxHeight: 1000, maxWidth: 1000, imageQuality: 90);
List<File> files = [];
if (images == null || images.isEmpty) return [];
for (var i = 0; i < images.length; i++) {
final file = File(images[i].path);
files.add(file);
}
return files;
}
Future<String?> _uploadImageFile(File file, String path) async {
try {
final storage = FirebaseStorage.instance;
TaskSnapshot? taskSnapshot;
final storageRef = storage.ref().child(path);
final uploadTask = storageRef.putFile(file);
taskSnapshot = await uploadTask.whenComplete(() {});
final imageUrl = await taskSnapshot.ref.getDownloadURL();
return imageUrl;
} catch (e) {
throw Exception(e.toString());
}
}
Future<List<String>> uploadImages(
List<File> files,
String path,
) async {
final urls = <String>[];
try {
if (files.isNotEmpty) {
for (var i = 0; i < files.length; i++) {
final file = files[i];
final imagePath = '$path/${Random().nextInt(10000)}.jpg';
final url = await _uploadImageFile(file, imagePath);
urls.add(url!);
}
}
return urls;
} on FirebaseException {
rethrow;
}
}
Instead of using ImagePicker.pickImage, use ImagePicker.pickMultiImage. That gives you a List instead of an XFile. Then you can just upload all images in the list. For instance, add an image parameter to your _uploadFile Function so that its function signature is
Future _uploadFile(BuildContext context, XFile image)
and just upload all images like
for (final image of images) {
_uploadFile(context, image)
}

I'm having access issues when using the class I created

Like this I have a code:
final Storage storage = Storage();
// ....
onPressed: () async {
final results = await FilePicker.platform.pickFiles(
type: FileType.image,
allowMultiple: false,
allowedExtensions: ['jpg', 'png', "jpeg"],
);
if (results == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Resim seƧmediniz."),
backgroundColor: Color.fromARGB(255, 36, 36, 36),
),
);
return null;
}
final path = results.files.single.path!;
final fileName = results.files.single.name;
Storage.uploadFile(path, fileName);
},
Also I have storage_services.dart:
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
class Storage {
final FirebaseStorage _storage = FirebaseStorage.instance;
Future<void> uploadFile(String filePath, String fileName) async {
File file = File(filePath);
try {
await _storage.ref("$fileName").putFile(file);
} on Exception catch (e) {
print(e);
}
}
}
My codes were like this. I am getting an error. Error:
Instance member 'uploadFile' can't be accessed using static access.dartstatic_access_to_instance_member
I'm trying to code by looking at this video: https://www.youtube.com/watch?v=sM-WMcX66FI
How can I solve the problem? Thanks in advance for your help.

An eexception occurs when using flutter_downloader package

I'm trying to use flutter_downloader package to download some files (images/pdf). There is a listView with ListTiles each containing a button to start downloading when clicked but this error occurs when scrolling the list view.
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 30 pos 12: '!_initialized': FlutterDownloader.initialize() must be called only once!
//my code is like this:
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadFile extends StatefulWidget {
DownloadFile({this.downloadUrl});
final String downloadUrl;
#override
_DownloadFileState createState() => _DownloadFileState();
}
class _DownloadFileState extends State<DownloadFile> {
String downloadId;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState(){
super.initState();
_init();
}
Future<void> _init() async {
await FlutterDownloader.initialize();
IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
print("status: $status");
print("progress: $progress");
print("id == downloadId: ${id == downloadId}");
});
FlutterDownloader.registerCallback(downloadCallback);
_localPath = (await _findLocalPath()) + '/Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
Future<String> _findLocalPath() async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<bool> _checkPermission() async {
if (Theme.of(context).platform == TargetPlatform.android) {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.storage]);
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
//----------------------------------------------------------------
#override
void dispose() {
super.dispose();
}
//---------------------------------------------------------------
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () async {
if (await _checkPermission()) {
final taskId = await FlutterDownloader.enqueue(
url: widget.downloadUrl,
savedDir: _localPath,
showNotification:
true, // show download progress in status bar (for Android)
openFileFromNotification:
true, // click on notification to open downloaded file (for Android)
);
downloadId = taskId;
}
},
child: Text('Downloa File',style: TextStyle(color: Colors.teal),)
);
}
}
According to the Usage section in the flutter_downloader package and the error you are getting, you must call the FlutterDownloader.initialize not more than once.
You can do that in the main method of your application, just like so:
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize();

Converting of Image File created using image_picker package of flutter to AssetImage/Image.asset?

I am new to flutter, i am building an app where i need to convert the file(image) generated after using image_picker package to asset image to use in the app.
example code as follows, which creates file(Image)
final Function onSelectImage;
ImageInput(this.onSelectImage);
File _storedImage;
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
if (imageFile == null) {
return;
}
setState(() {
_storedImage = imageFile;
});
final appDir = await syspaths.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage = await imageFile.copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
}
Thanks in advance
You can create an image variable which you can rever to and update when you selected the image.
See the following code:
final Function onSelectImage;
ImageInput(this.onSelectImage);
File _storedImage;
Image _tempImage;
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
if (imageFile == null) {
return;
}
setState(() {
_storedImage = imageFile;
});
final appDir = await syspaths.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage = await imageFile.copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
setState(() {
_tempImage = imageFile;
});
}
#override
Widget build(BuildContext context) {
return _tempImage == null ? Container(child:null) : Image(image: _tempImage);
}