Flutter Web, toByteData, await never returns - flutter

I have this demostrative code:
import 'dart:convert';
import 'dart:ui';
void main() async {
final pictureRecorder = PictureRecorder();
Canvas(pictureRecorder).drawCircle(
const Offset(100, 100),
100,
Paint()..color = const Color.fromARGB(255, 255, 0, 0),
);
final picture = pictureRecorder.endRecording();
final image = await picture.toImage(200, 200);
print('//1 before toByteData await');
final rawByteData = await image.toByteData(format: ImageByteFormat.rawRgba);
print('//2 after toByteData await');
final encoded = base64.encode(rawByteData.buffer.asUint8List());
print('//3 base64: $encoded');
}
The result on a mobile is, as expected:
I/flutter (18489): //1 before toByteData await
I/flutter (18489): //2 after toByteData await
I/flutter (18489): //3 base64: AAAAAAAAAAAAAAAAAAAAAA.. (all the base64 string)
The result on a web:
//1 before toByteData await
And never return the result, can somebody explainme how can I get the result in web?
Thank you very much for your time sharing your knowledge!

toByteData doesn't work for Flutter web on beta channel yet.
So workaround is to use html canvas to generate image.
import 'package:universal_html/html.dart' as html;
final htmlCanvas = html.CanvasElement(width: size.width.floor(), height: size.height.floor());
final html.CanvasRenderingContext2D context = htmlCanvas.getContext("2d");
context.fillStyle = 'white';
context.fillRect(0, 0, size.width, size.height);
for (int i = 0 ; i < _paths.length ; i++) {
context.strokeStyle = 'red';
context.lineWidth = _paths[i].key.strokeWidth;
context.beginPath();
context.moveTo(caculatedOffset.dx, caculatedOffset.dy);
for (int j = 1 ; j < _paths[i].value.length ; j ++) {
context.lineTo(caculatedOffset.dx, caculatedOffset.dy);
}
context.stroke();
}
final blob = await htmlCanvas.toBlob('image/jpeg', 0.8);

Related

Flutter Google-Ml-Kit-plugin - FaceDetector not working in iPhone cameras

If we take photos in iPhone
final XFile? image = await picker.pickImage(
source: ImageSource.camera,
maxWidth: 300,
maxHeight: 300,
preferredCameraDevice: CameraDevice.front,
);
final List faces = await faceDetector.processImage(inputImage);
always give empty list
final FaceDetector faceDetector = FaceDetector(
options: FaceDetectorOptions(
enableTracking: true,
enableContours: true,
enableClassification: true,
enableLandmarks: true,
performanceMode: FaceDetectorMode.accurate));
final inputImage = InputImage.fromFilePath(image.path);
final List faces = await faceDetector.processImage(inputImage);
print(faces.length); // IS ALWAYS ZERO
Works perfectly fine if source: ImageSource.gallery in iPhone
Fixed by Giving
import 'dart:math' as Math;
import 'dart:math';
import 'package:image/image.dart' as Im;
class CompressObject {
File imageFile;
String path;
int rand;
CompressObject(this.imageFile, this.path, this.rand);
}
Future<String> compressImage(CompressObject object) async {
return compute(decodeImage, object);
}
String decodeImage(CompressObject object) {
Im.Image? image = Im.decodeImage(object.imageFile.readAsBytesSync());
I.m.Image smallerImage = Im.copyResize(
image!,width: 200,height: 200
); // choose the size here, it will maintain aspect ratio
var decodedImageFile = File(object.path + '/img_${object.rand}.jpg');
decodedImageFile.writeAsBytesSync(Im.encodeJpg(smallerImage, quality: 85));
return decodedImageFile.path;
}
Then:
void chooseImage(bool isFromGallery) async {
final XFile? image = await picker.pickImage(
source: isFromGallery ? ImageSource.gallery : ImageSource.camera,
preferredCameraDevice: CameraDevice.front,
);
if (image != null) {
if (Platform.isIOS) {
final tempDir = await getTemporaryDirectory();
final rand = Math.Random().nextInt(10000);
CompressObject compressObject = CompressObject(File(image.path), tempDir.path, rand);
String filePath = await compressImage(compressObject);
print('new path: ' + filePath);
file = File(filePath);
} else {
file = File(image.path);
}
}
final inputImage = InputImage.fromFilePath(i file.path);
final List<Face> faces = await faceDetector.processImage(inputImage);
/.........../
}

Live text recognition ( region of interest)

i have live text recognition i used library https://pub.dev/packages/google_mlkit_text_recognition and https://pub.dev/packages/camera,
but i have some problem...
i need detect text only in marked part...
Get live preview function:
//
Future _processCameraImage(CameraImage image) async {
final WriteBuffer allBytes = WriteBuffer();
for (final Plane plane in image.planes) {
allBytes.putUint8List(plane.bytes);
}
final bytes = allBytes.done().buffer.asUint8List();
final Size imageSize =
Size(image.width.toDouble(), image.height.toDouble());
//
final camera = cameras[_cameraIndex];
final imageRotation =
InputImageRotationValue.fromRawValue(camera.sensorOrientation) ??
InputImageRotation.rotation0deg;
final inputImageFormat =
InputImageFormatValue.fromRawValue(image.format.raw) ??
InputImageFormat.nv21;
final planeData = image.planes.map(
(Plane plane) {
return InputImagePlaneMetadata(
bytesPerRow: plane.bytesPerRow,
height: plane.height,
width: plane.width,
);
},
).toList();
//
final inputImageData = InputImageData(
size: imageSize,
imageRotation: imageRotation,
inputImageFormat: inputImageFormat,
planeData: planeData,
);
final inputImage =
InputImage.fromBytes(bytes: bytes, inputImageData: inputImageData);
//
widget.onImage(inputImage);
}
Processing image function:
//
Future<void> processImage(InputImage inputImage) async {
if (!_canProcess) return;
if (_isBusy) return;
_isBusy = true;
final recognizedText = await _textRecognizer.processImage(inputImage);
//
if (mounted) {
for (var element in recognizedText.blocks) {
for (var line in element.lines) {
for (var txt in line.elements) {
if (txt.text.length == 17) {
setState(() {
_text = txt.text;
});
}
}
}
}
}
_isBusy = false;
}
}
I had a similar task, I used the module mask_for_camera_view
create your own frame and find the values ​​of the cropped picture
more details & photo example in github

Request for clarification using Canvas() and Paint() in flutter

I use this method for custom markers on a map. I added a custom round background on the marker by using Canvas and Paint and just put those 2 together before returning it back to BitmapDescriptor using code I found which I don't understand very much in depth. I believe that is the reason my background and marker is getting clipped into this square box no matter how I try to adjust it.
My goal is to slightly increase the round background behind the marker. But, it just takes the limits of the marker/icon. If I increase its size, it gets clipped. Also wondering how canvas operations still works even if this class does not have a CustomPainter mixin
class IconLoader {
var vd = VendorDatabase();
late var iconPath;
var convertedIcon;
int iconSize = 200;
Future <BitmapDescriptor?> getIconWithBackground(vendorId) async {
...fetch icon path....
return processIconWithBackground(iconPath, iconSize).then((onValue) async {
return convertedIcon = BitmapDescriptor.fromBytes(onValue!);
});
Future<Uint8List?> processIconWithBackground(String path, int width) async {
final imageData = await rootBundle.load(path); //original code: loads the image into ByteData
// Performs Witchcraft
ui.Codec codec = await ui.instantiateImageCodec(imageData.buffer.asUint8List(), targetWidth: width);
// Something about animation if image is gif? <-- I'm not dealing with animations, how can i get rid of this?
ui.FrameInfo fi = await codec.getNextFrame();
// Converts ByteData to Png format?
var process = (await fi.image.toByteData(format: ui.ImageByteFormat.png))?.buffer.asUint8List();
// finally makes an Image Type Object
final iconImage = await decodeImageFromList(process!);
final pictureRecorder = ui.PictureRecorder(); // starts to record
final canvas = ui.Canvas(pictureRecorder);
canvas.scale(1.5);
_paintCircleStroke(canvas);
final imagePaint = ui.Paint();
canvas.drawImage(iconImage, Offset.zero, Paint());
final drawing = pictureRecorder.endRecording();// ends recording the "drawing"
final image = await drawing.toImage(iconSize.round(), iconSize.round());//converts this drawing to an image
final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
return bytes?.buffer.asUint8List();}
void _paintCircleStroke (ui.Canvas canvas) {
double _circleOffset = iconSize /2;
double _circleStrokeWidth = iconSize / 10;
double _outlineCircleWidth = iconSize /2 ;// (_circleStrokeWidth / 2) ;
// canvas.scale(1.0);
final paint = ui.Paint()
..style = ui.PaintingStyle.fill
..color = ui.Color .fromRGBO(132, 123, 123, 1);
// ..strokeWidth = _circleStrokeWidth;
canvas.drawCircle(ui.Offset(_circleOffset, _circleOffset), _outlineCircleWidth, paint);

Can I use Async inside Dart Isolate ??? its not working

I am using ImageEditor package to merge different images. below is my code. its working perfectly fine without using Isolate, when i use it with isolate, i get null error.
Working code without Isolate
startEditing() async {
for (var i = 0; i < image1.length || i == 0; i++) {
if (image1.isNotEmpty) {
img1 = await File(image1[i].path).readAsBytes();
}
for (var i = 0; i < image2.length || i == 0; i++) {
if (image2.isNotEmpty) {
img2 = await File(image2[i].path).readAsBytes();
}
final ImageEditorOption optionGroup = ImageEditorOption();
optionGroup.outputFormat = const OutputFormat.png(100);
optionGroup.addOptions([
MixImageOption(
x: 0,
y: 0,
width: 1000,
height: 1000,
target: MemoryImageSource(img1),
),
MixImageOption(
x: 0,
y: 0,
width: 1000,
height: 1000,
target: MemoryImageSource(img2),
),
]);
try {
final Uint8List? result = await ImageEditor.editImage(
image: mainImg, imageEditorOption: optionGroup);
if (result == null) {
image = null;
} else {
await saveImage(result, index);
setState(() {
image = MemoryImage(result);
index++;
});
}
} catch (e) {
print(e);
}
}
}
}
Code with Isolate not working
startEditing(SendPort sendPort) async {
for (var i = 0; i < image1.length || i == 0; i++) {
if (image1.isNotEmpty) {
img1 = await File(image1[i].path).readAsBytes();
}
for (var i = 0; i < image2.length || i == 0; i++) {
if (image2.isNotEmpty) {
img2 = await File(image2[i].path).readAsBytes();
}
final ImageEditorOption optionGroup = ImageEditorOption();
optionGroup.outputFormat = const OutputFormat.png(100);
optionGroup.addOptions([
MixImageOption(
x: 0,
y: 0,
width: 1000,
height: 1000,
target: MemoryImageSource(img1),
),
MixImageOption(
x: 0,
y: 0,
width: 1000,
height: 1000,
target: MemoryImageSource(img2),
),
]);
try {
final Uint8List? result = await ImageEditor.editImage(
image: mainImg, imageEditorOption: optionGroup);
if (result == null) {
image = null;
} else {
await saveImage(result, index);
image = MemoryImage(result);
index++;
sendPort.send(image);
}
} catch (e) {
print(e);
}
}
}
}
saveImage method
Future<String> saveImage(Uint8List bytes, int i) async {
final name = '${filenames[i]}';
final result = await ImageGallerySaver.saveImage(bytes, name: name);
print(result);
return result['filePath'];
}
Receiving in main thread
getImageas() async {
ReceivePort receiverPort =
ReceivePort();
final isolate =
await Isolate.spawn(startEditing, receiverPort.sendPort);
receiverPort.listen((data) {
print('Receiving: ' + data + ', ');
});
}
I get this error:
I/flutter (21937): Null check operator used on a null value
in this line:
final Uint8List? result = await ImageEditor.editImage(
image: mainImg, imageEditorOption: optionGroup);
I am sure that img1, img2, mainImg, image1, image2 values are not null... check 1000 times. I have also used flutter compute, and same result.
Flutter plugins that call into native code (such as image_editor) do not work in isolates spawned by Isolate.spawn.
The flutter_isolate package spawns isolates from native code for this reason. You should be able to use it to call image_editor in an isolate. (Disclaimer: I've never used flutter_isolate.)

upload multi Image Picker with dio (package is out of date) problem

I used to upload Images with this code below but due to package update it doesn't work anymore I read the documentation but it didn't work and I didn't know how to use multipart
here is the code
import 'package:multi_image_picker/multi_image_picker.dart';
import 'package:dio/dio.dart';
Future getImage() async {
files.clear();
List<Asset> resultList = List<Asset>();
resultList = await MultiImagePicker.pickImages(
maxImages: 2,
enableCamera: false,
);
for (var asset in resultList) {
int MAX_WIDTH = 500; //keep ratio
int height = ((500 * asset.originalHeight) / asset.originalWidth).round();
ByteData byteData =
await asset.requestThumbnail(MAX_WIDTH, height, quality: 80);
if (byteData != null) {
List<int> imageData = byteData.buffer.asUint8List();
UploadFileInfo u = UploadFileInfo.fromBytes(imageData, asset.name);
files.add(u);
}
}
setState(() {
_isUploading = true;
});
}
List<UploadFileInfo> files = new List<UploadFileInfo>();
Future<List<String>> uploadImage() async {
FormData formData = new FormData.from({"files": files});
Dio dio = new Dio();
var response =
await dio.post("http://localhost:3004/upload", data: formData);
UploadImage image = UploadImage.fromJson(response.data);
return image.images;
}
the errors are in the following lines
UploadFileInfo u = UploadFileInfo.fromBytes(imageData,asset.name);
List<UploadFileInfo> files = new List<UploadFileInfo>();
FormData formData = new FormData.from({"files": files});
so what do I need to change to make it work and what is the multipart ??
Any help will be appreciated
Thanks in advance
if (images.isEmpty || images[0] != null) {
for (int i = 0; i < images.length; i++) {
ByteData byteData = await images[i].getByteData();
List<int> imageData = byteData.buffer.asUint8List();
http.MultipartFile multipartFile =
http.MultipartFile.fromBytes('image', imageData,
filename: images[i].name,
contentType: MediaType('image', 'jpg'));
imagestoEdit.add(multipartFile);
print(imagestoEdit.length);
}
}
Dio.post(url,formdata:{images:imagestoEdit})