-
Notifications
You must be signed in to change notification settings - Fork 74
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
Problem in using ReactiveImagePicker (in conversion of model object to/from form.value) #125
Comments
replace the file path with some image from internet to check if everything works fine. |
Thanks a lot. However, as I mentioned already, the images from Appwrite are already displayed properly if we use the 'Url' in SelectedFile in FormGroup. It should be possible to convert the image file data on Appwrite backend to an in-memory file and then pass this file in Can we try something like this for the conversion of Url to a file? It uses Future<File> getFileFromImageId(String id) async {
Uri uri = Uri.parse(getImageUrlFromId(id));
final http.Response responseData = await http.get(uri);
final file = XFile.fromData(responseData.bodyBytes);
return file as File; // ??????? We need File. Will it work?`
} However, this gives Future and we may not be able to use it in the But in the mean time, I have continued to use the conversion approach between Form Value and model object and got a success to a great extent. However, the logic becomes quite complex, particularly when reading multiple files and allowing users to change one or more of those images. In addition, I have been using Reactive forms in either read-only or edit mode. It complicates the things further. We have to handle data from three locations:
The current implementation of PersonName personNameFromFormValue(FormGroup form) {
Map<String, dynamic> formValue = form.value;
return PersonName(
id: formValue['id'] as String?,
first: formValue['first'] as String,
... // other fields
// profilePic gets a single image input
profilePic: formValue['profilePic'] != null
? form.control('profilePic').dirty
? formValue['profilePic'].isNotEmpty
? (formValue['profilePic'] as List<SelectedFile>)[0].file!.path
: null
: getIdFromImageUrl((formValue['profilePic'] as List<SelectedFileImage>)[0].url!)
: null,
// Just for testing...
// This friendPics field takes multiple images as input
friendPics: formValue['friendPics'] != null
? form.control('friendPics').dirty
? formValue['friendPics'].isNotEmpty
? (formValue['friendPics'] as List<SelectedFile>)
.map((e) => e.file != null ? e.file!.path : getIdFromImageUrl(e.url!))
.toList()
: null
: (formValue['friendPics'] as List<SelectedFileImage>)
.map((e) => getIdFromImageUrl(e.url!))
.toList()
: null,
);
} |
Since image_picker returns XFile, why are we using SelectedFile? On web, I can't access the picked image. inal images = formGroup.control('image').value
as List<SelectedFile>?;
MultipartFile? multiPartImageFile;
if (images != null && images.isNotEmpty) {
final selectedImageFile = images.first;
print(selectedImageFile);
XFile? file;
if (kIsWeb) {
Uint8List? imageBytes =
await selectedImageFile.file
?.readAsBytes(); // Throw error `Unsupported operation: _Namespace,` |
@rsbichkar You could probably try this https://github.com/djangoflow/reactive_forms_widgets For your case, I suppose what you can do is download the image to temp directory and get the image bytes and pass those bytes to ReactiveImagePicker as initial value? |
@vasilich6107 Could you check this PR? After that I will create the PR for File -> XFile change if that is okay. |
@adar2378 Thanks for your help. However, I could get the code working (for Linux platform at least). I am not converting image URLs and image Files anymore. Instead I am handling them separately. It is not necessary to download the file to temporary storage either. First I have used the conversion functions Map<String, dynamic> personNameToFormValue(PersonName personName) {
final map = <String, dynamic>{
'id': personName.id,
'name': personName.name,
...
'profilePic': personName.profilePic != null
? [
SelectedFileImage(
url: AppwriteConstants.getImageUrlFromId(personName.profilePic!))
]
: null,
'friendPics': personName.friendPics != null
? personName.friendPics!
.map((e) => SelectedFileImage(url: AppwriteConstants.getImageUrlFromId(e)))
.toList()
: null,
};
return map;
} // Uses any valid value(s) for image fields. Their actual values are obtained in controller code
PersonName personNameFromFormValue(FormGroup form) {
Map<String, dynamic> formValue = form.value;
return PersonName(
id: formValue['id'] as String?,
name: formValue['name'] as String,
...
// use null values for these image fields
profilePic: null,
friendPics: null,
);
} These functions are used in The controller uses the void createPersonName(
BuildContext context, PersonName personName, FormGroup form) async {
Map<String, dynamic> formValue = form.value;
state = true;
// Create image for profilePic field, if an image is entered by user
String? profilePicIds;
if (form.control('profilePic').dirty) {
// store new images, if any, to appwrite storage and create newPersonName with new image id
profilePicIds = formValue['profilePic'].isNotEmpty
? (await _storageRepo
.uploadImages([formValue['profilePic'][0].file]))[0]
: null;
}
// Create images for friendPics field, if one or more images are entered by user
List<String> friendPicsIds = [];
if (form.control('friendPics').dirty) {
// first create a list of image files added
final newImageFiles = <io.File>[]; // we can use XFile here for Platform Independence
formValue['friendPics'].forEach((e) {
if (e.file != null) newImageFiles.add(e.file);
});
// upload these files to Appwrite storage and get image ids
friendPicsIds = formValue['friendPics'].isNotEmpty
? await _storageRepo.uploadImages(newImageFiles)
: [];
}
// set new image ids for image field
final newPersonName = personName.copyWith(
profilePic: profilePicIds,
friendPics: friendPicsIds,
);
final res = await _personNameRepo.createPersonName(newPersonName);
state = false;
... void updatePersonName(BuildContext context, PersonName personName,
PersonName prevPersonName, FormGroup form) async {
Map<String, dynamic> formValue = form.value;
state = true;
// Update image for profilePic field, if an image is changed by user
String? profilePicIds = formValue['profilePic'] != null
? form.control('profilePic').dirty
? formValue['profilePic'].isNotEmpty
? (await _storageRepo.uploadImages([formValue['profilePic'][0].file]))[0]
: null
: AppwriteConstants.getIdFromImageUrl(
(formValue['profilePic'] as List<SelectedFileImage>)[0].url!)
: null;
// delete previous image, if any
if (prevPersonName.profilePic != null &&
prevPersonName.profilePic != profilePicIds) {
await _storageRepo.deleteImage(prevPersonName.profilePic!);
}
// Update images for friendPics field, if one or more images are changed by user
List<String>? friendPicsIds;
if (formValue['friendPics'] != null) {
if (form.control('friendPics').dirty) {
if (formValue['friendPics'].isNotEmpty) {
// get set of ids of previous images, if any, in friendPics field
final idSet = prevPersonName.friendPics?.toSet();
// get a set of ids of images available in formValue urls after the update operation
final urlIds = <String>[];
formValue['friendPics']!.forEach((e) {
if (e.url != null) {
urlIds.add(AppwriteConstants.getIdFromImageUrl(e.url));
}
});
final urlIdsSet = urlIds.toSet();
// difference, idSet - urlSet, gives deleted images
if (idSet != null) {
Set deletedIdsSet = idSet.difference(urlIdsSet);
// remove these images from Appwrite storage
deletedIdsSet
.forEach((e) async => await _storageRepo.deleteImage(e));
}
// now add new files, if any, in formValue 'files' to Appwrite storage
// first create a list of files added
final newImageFiles = <io.File>[]; // we can use XFile here as well
formValue['friendPics'].forEach((e) {
if (e.file != null) newImageFiles.add(e.file);
});
// upload these files to Appwrite storage and get image ids
final List<String> newImageIds =
await _storageRepo.uploadImages(newImageFiles);
// prepare list of ids of all images (previous images not yet deleted and new images added)
friendPicsIds = [...urlIds, ...newImageIds];
} else {
// delete images, if any, present earlier
prevPersonName.friendPics!
.forEach((e) => _storageRepo.deleteImage(e));
friendPicsIds = [];
}
} else {
friendPicsIds = prevPersonName.friendPics;
}
} else {
friendPicsIds = null;
}
// set new image ids for image field
final newPersonName = personName.copyWith(
profilePic: profilePicIds,
friendPics: friendPicsIds,
);
final res = await _personNameRepo.updatePersonName(newPersonName);
state = false; This code works correctly for adding new files, removing exising files, and any combination as well. |
'You could probably try this https://github.com/djangoflow/reactive_forms_widgets' @adar2378: reactive_forms_widgets:
git:
url: https://github.com/djangoflow/reactive_forms_widgets However, I am unable to use On the other hand, if I use reactive_image_picker:
git:
url: https://github.com/djangoflow/reactive_forms_widgets/tree/master/packages/reactive_image_picker it results in github error fatal: repository 'https://github.com/djangoflow/reactive_forms_widgets/tree/master/packages/reactive_image_picker/' not found
exit code: 128
exit code 69 |
I am using ReactiveForms along with ReactiveImagePicker and several other Reactive packages for the implementation of an application involving several forms. I am also using Freezed, JsonSerialization, and Riverpod packages and AppWrite for backend.
In ReactiveForms using ReactiveImagePicker, I am unable to pass 'PersonName' model class object to Reactive Form widget's form.value (for the editing mode) due to use of different types in model class and the form group (String? type in model class and List<SelectedFile> type in form group) for the field using ReactiveImagePicker.
Extract of related code in my app is given below to understand the exact problem:
The model class file (PersonName) contains other fields besides profilePic, of type String?, which is used to store the id or Url of image file stored in Appwrite Storage bucket:
The FormGroup for this class is like this:
The main problem is how to convert PersonName object to form.value data (in form display widget) as the form.value has field of type List<SelectedFile> whereas PersonName has the corresponding model class field is of type String?.
For this I am using PersonNameToFormValue (and corresponding PersonNameFromFormValue) functions:
'profilePic' field in above function uses condition to pass either a File or Url to display widget image in either read only or editing mode.
The line using 'url:' displays the image correctly in readonly mode. However, the line using 'file:' gives the following error:
Will you please help in this regard?
The text was updated successfully, but these errors were encountered: