Issue 1:
I'm developing these features for a flutter app: loading an image from gallery or from camera and allow rotate it.

Those operations are expensive so I'm computing them in another thread.

The code is working fine in debug mode, but when I try with release or profiler mode the app crashes and stop working only when I try to rotate the image.

I have more operations working in another thread and there is no problem with those, the problem is only with this operation.

Run flutter doctor -v

[✓] Flutter (Channel beta, v0.9.4, on Linux, locale es_ES.UTF-8)
• Flutter version 0.9.4 at /home/nadia/flutter
• Framework revision f37c235c32 (hace 3 semanas), 2018-09-25 17:45:40 -0400
• Engine revision 74625aed32
• Dart version 2.1.0-dev.5.0.flutter-a2eb050044

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
• Android SDK at /home/nadia/Android/Sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-27, build-tools 27.0.3
• Java binary at: /home/nadia/android-studio/jre/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
• All Android licenses accepted.

[✓] Android Studio (version 3.2)
• Android Studio at /home/nadia/android-studio
• Flutter plugin version 29.1.1
• Dart plugin version 181.5656
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[✓] Connected devices (1 available)
• NX591J • d2ab515a • android-arm64 • Android 7.1.2 (API 25)

This is my pubspec.yaml

dependencies:
flutter:
sdk: flutter

cupertino_icons: ^0.1.2
image_picker: ^0.4.10
path_provider: ^0.4.1
path: any
image_picker_saver: ^0.1.0
uuid: ^1.0.3
simple_permissions: ^0.1.8
image: ^2.0.4

And here is my code. I have the code divided into three files. First my main.dart

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart' as imPick;
import 'package:flutter/services.dart';

import 'package:image/image.dart' as im;
import 'package:path_provider/path_provider.dart';
import 'package:simple_permissions/simple_permissions.dart';

import 'imageUtils.dart';
import 'toCompute.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
/*theme: new ThemeData(
primarySwatch: Colors.white,
),
*/
showPerformanceOverlay: true,
debugShowCheckedModeBanner: false,
home: CameraUpload(),

);
}

}

class _AkkaMark extends State<CameraUpload> {

File _imageFile;

String _path;

List<Widget> _buttons;

im.Image _finalImage;

Widget _widgetForBody;

Widget _imageForBody;

Widget _loading;

double _widthS;
double _heightS;

String _temporalDir = "/Pictures/AKKAmarkTemporal";

final _key = new GlobalKey<ScaffoldState>();

List<String> _savedImages = new List();

void _setWidgets(){

_imageForBody =
new Stack(
fit: StackFit.expand,

children: <Widget>[
_setImage(),

new Container(
alignment: Alignment.bottomCenter,
child: new Stack(
alignment: Alignment.bottomCenter,
overflow: Overflow.visible,
children: _buttons,
),
)
] ,

);

_loading =
new Container(
child: new Stack(
fit: StackFit.expand,
children: <Widget>[

_imageFile == null?
new Center(child: new Text('Pick image or upload from files', textAlign: TextAlign.center, )):
new Image.file(_imageFile, fit: BoxFit.contain,),

new Container(
alignment: Alignment.bottomCenter,
child: new Stack(
alignment: Alignment.bottomCenter,
overflow: Overflow.visible,
children: _buttons,
),
),

new Container(
alignment: AlignmentDirectional.center,
decoration: new BoxDecoration(
color: Colors.white70,
),
child: new Container(
decoration: new BoxDecoration(
color: Colors.blue[200],
borderRadius: new BorderRadius.circular(10.0)
),
width: 300.0,
height: 200.0,
alignment: AlignmentDirectional.center,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Center(
child: new SizedBox(
height: 50.0,
width: 50.0,
child: new CircularProgressIndicator(
value: null,
strokeWidth: 7.0,
),
),
),
new Container(
margin: const EdgeInsets.only(top: 25.0),
child: new Center(
child: new Text(
"Loading.. wait...",
style: new TextStyle(
color: Colors.white
),
),
),
),
],
),
),
),
],
),
);
}

@override
void initState() {
super.initState();

initData();

_widgetForBody = _imageForBody;

}

void initData() async {

await SimplePermissions.requestPermission(Permission.WriteExternalStorage);

getExternalStorageDirectory().then((Directory dir){

compute(deleteTemporal, dir.path + _temporalDir);
});

_setWidgets();

getImageCamera();
}

void _setStateImage(){
setState(() {
_setWidgets();
_widgetForBody = _imageForBody;
});
}

void _setStateLoading(){
setState(() {
_widgetForBody = _loading;
});
}

/* Update the main screen:
* - If there is no image selected, show text
* - If there is an image: preview this and show new buttons*/
Widget _setImage() {

if (_imageFile == null) {

_setInitialButtons();
return new Center(child: new Text('Pick image or upload from files', textAlign: TextAlign.center, ));

}
else {

_showExtraButtons();

return new Image.file(_imageFile, fit: BoxFit.contain,);
}

}

//Open the camera and save the image in variable
getImageCamera() async {
File image = await imPick.ImagePicker.pickImage(source: imPick.ImageSource.camera, maxWidth: 2048.0);
_loadNewImage(image);
}

//Open the gallery and save the image choose in variable
getImageFile() async {

File image = await imPick.ImagePicker.pickImage(source: imPick.ImageSource.gallery, maxWidth: 2048.0);
_loadNewImage(image);
}

//NEW
_loadNewImage(File image){

_setStateLoading();

compute(deleteImages, _savedImages);
_savedImages.clear();

_imageFile = image == null && _imageFile != null? _imageFile: image;

if(_imageFile != null ){

_savedImages.add(_imageFile.path);

compute(decodeImage, _imageFile).then((im.Image imageDecode){
_finalImage = imageDecode;

_setStateImage();

});
}
else{
_setStateImage();
}
}

/* Init the variable with the default buttons */
_setInitialButtons() {

//Initially only two buttons, centered in the screen
double iniPosW = _widthS/2;
double iniPosH = _heightS/1.2;

_buttons = [
createButton(getImageCamera, 'Pick image', new Icon(Icons.add_a_photo), new EdgeInsets.fromLTRB(iniPosW, iniPosH, 0.0, 0.0) ),

createButton(getImageFile, 'Upload from files', new Icon(Icons.file_upload), new EdgeInsets.fromLTRB(iniPosW - (_widthS * 0.18), iniPosH, 0.0, 0.0) ),
];

}

//Append the button for select the position
_showExtraButtons() {
double iniPosW = _widthS * 0.05;
double iniPosH = _heightS/1.12;

_buttons = [

//Pick image from camera
createButton(getImageCamera, 'Pick image', new Icon(Icons.add_a_photo), new EdgeInsets.fromLTRB( iniPosW, iniPosH, 0.0, 0.0) ),

//Upload image from files
createButton(getImageFile, 'Upload from files', new Icon(Icons.file_upload),
new EdgeInsets.fromLTRB( iniPosW + ( _widthS * 0.11), iniPosH - ( _heightS *0.16), 0.0, 0.0 ) ),

//To rotate image
createButton( () { _computeRotate(); },
'Rotate right', new Icon(Icons.rotate_right),
new EdgeInsets.fromLTRB( iniPosW + ( _widthS * 0.62), iniPosH - ( _heightS *0.16), 0.0, 0.0 ) ),

//To save image
createButton( () {

new ImageUtils().saveImage(_imageFile).then((String path){
_path = path;
compute(deleteImages, _savedImages);
_savedImages.clear();
_savedImages.add(_path);
});

},
'Save image',
new Icon(Icons.save), new EdgeInsets.fromLTRB(iniPosW + ( _widthS * 0.75), iniPosH, 0.0, 0.0 )),

];

//Back
if( _savedImages.length > 1)
_buttons.add(createButton(_back, 'Back', new Icon(Icons.reply), new EdgeInsets.fromLTRB( iniPosW + ( _widthS * 0.78), 0.0, 0.0, _heightS * 0.84) ) );

}

void _back(){

_setStateLoading();

int lenght = _savedImages.length;

File image = new File( _savedImages[ lenght - 2] );
print(image);

List<String> last = new List();
last.add(_savedImages[lenght - 1]);

compute(deleteImages, last);

_savedImages.removeLast();

_imageFile = image;

compute(decodeImage, _imageFile).then((im.Image imageDecode){
_finalImage = imageDecode;
_setStateImage();
});

}

void _computeRotate(){

_setStateLoading();

new ImageUtils().saveImageTemporal(_imageFile, _temporalDir).then((File file){
_path = file.path;
_savedImages.add(_path);

List<Object> arg = new List();

arg.add(_finalImage);
arg.add(_path);
print("Rotating...");

compute(rotateImage, arg).then((List<Object> objects){
_finalImage = objects[0];
_imageFile = objects[1];
_setStateImage();

});

});

}

Positioned createButton(Function onPressed, tooltip, Widget icon, EdgeInsets padding){
return new Positioned(
top: padding.top,
bottom: padding.bottom,
left: padding.left,
//right: padding.right,
child: new CircleAvatar(
radius: 26.0,
child:
FloatingActionButton(
elevation: 6.0,
onPressed: onPressed,
tooltip: tooltip,
child: icon,
),
),
);
}

@override
Widget build(BuildContext context) {

_heightS = MediaQuery.of(context).size.height;
_widthS = MediaQuery.of(context).size.width;

return Scaffold(
key: _key,
body: _widgetForBody,
backgroundColor: Color.fromRGBO(239, 239, 240, 0.9),
);
}
}

class CameraUpload extends StatefulWidget {
@override
_AkkaMark createState() => new _AkkaMark();
}

And the other two files, one that has all the code that I will use isolated toCompute.dart

import 'dart:io';

import 'package:image/image.dart' as im;

List<Object> rotateImage(List<Object> arg) {
im.Image finalImage = arg[0];
String path = arg[1];
finalImage = im.copyRotate(finalImage, 90);
List<int> png = im.encodeJpg(finalImage);
File imageFileReturn = new File(path)..writeAsBytesSync(png);
List<Object> returns = new List();
returns.add(finalImage);
returns.add(imageFileReturn);
return returns;

}

im.Image decodeImage(File file){
return im.decodeImage(file.readAsBytesSync());
}

bool deleteImages(List<String> imagesPath){
print("Deleting...");
for( int i = 1; i < imagesPath.length; i++ ){
new File(imagesPath[i]).delete();
}
return true;
}

bool deleteTemporal(String temporalDir){

new Directory( temporalDir ).exists().then((bool exists){
if ( exists )
new Directory( temporalDir ).deleteSync(recursive: true);
});

return true;
}

And other one that I am using to save the images, imageUtils.dart

import 'dart:async';
import 'dart:io';

import 'package:path_provider/path_provider.dart';
import 'package:image_picker_saver/image_picker_saver.dart';

import 'package:uuid/uuid.dart';

class ImageUtils {

Future<String> saveImage(File image) async {
var a = await ImagePickerSaver.saveFile(fileData: image.readAsBytesSync());

return a;
}

Future<File> saveImageTemporal(File image, String temporalDir) async {

temporalDir = await _createTemporalPath(temporalDir);

var file = File('$temporalDir/'+generateUuidJpg().toString())..writeAsBytesSync(image.readAsBytesSync());

File('$temporalDir/'+'.nomedia')..writeAsStringSync('');

print('saving... '+file.path);
return file;
}

Future<String> _createTemporalPath(String temporalDir) async {
Directory dirFinal;
var sdCard = await getExternalStorageDirectory();
dirFinal = await new Directory(sdCard.path + temporalDir).create(recursive: true);

return dirFinal.path;
}

String generateUuidJpg(){
Uuid _uuid = new Uuid();
return _uuid.v1().toString()+".jpg";
}

}

Flutter throws an error which is the same that I can see in my logcat

Here is the flutter error

Fatal signal 11 (SIGSEGV), code 1, fault addr 0x7f7bdffb90 in tid 21655 (m.akka.akkamark)

And here is my logcat

Fatal signal 11 (SIGSEGV), code 1, fault addr 0x7f7e9feca0 in tid 23568 (1.ui)
10-15 13:54:54.493 361 361 W : debuggerd: handling request: pid=22783 uid=10204 gid=10204 tid=23568
10-15 13:54:54.494 23570 23570 I debuggerd64: type=1400 audit(0.0:10486): avc: denied { read } for name="isolate_snapshot_instr" dev="mmcblk0p48" ino=1532120 scontext=u:r:debuggerd:s0 tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=file permissive=1
10-15 13:54:54.494 23570 23570 I debuggerd64: type=1400 audit(0.0:10487): avc: denied { open } for path="/data/data/com.akka.akkamark/app_flutter/isolate_snapshot_instr" dev="mmcblk0p48" ino=1532120 scontext=u:r:debuggerd:s0 tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=file permissive=1
10-15 13:54:54.494 23570 23570 I debuggerd64: type=1400 audit(0.0:10488): avc: denied { getattr } for path="/data/data/com.akka.akkamark/app_flutter/isolate_snapshot_instr" dev="mmcblk0p48" ino=1532120 scontext=u:r:debuggerd:s0 tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=file permissive=1

10-15 13:54:54.566 23570 23570 F DEBUG : LineageOS Version: '14.1-20180810-UNOFFICIAL-nx591j'
10-15 13:54:54.566 23570 23570 F DEBUG : Build fingerprint: 'nubia/NX591J/NX591J:7.1.1/NMF26F/eng.nubia.20170905.150740:user/release-keys'
10-15 13:54:54.566 23570 23570 F DEBUG : Revision: '0'
10-15 13:54:54.566 23570 23570 F DEBUG : ABI: 'arm64'
10-15 13:54:54.566 23570 23570 F DEBUG : pid: 22783, tid: 23568, name: 1.ui >>> com.akka.akkamark <<<
10-15 13:54:54.566 23570 23570 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7f7e9feca0
10-15 13:54:54.566 23570 23570 F DEBUG : x0 0000007fa1042a71 x1 0000000000aaa000 x2 0000000000237800 x3 000000000046f000
10-15 13:54:54.566 23570 23570 F DEBUG : x4 0000007f7e120c91 x5 0000000000237800 x6 000000000000046f x7 00000000000008de
10-15 13:54:54.566 23570 23570 F DEBUG : x8 0000007f7e38d9d1 x9 0000000000000000 x10 0000000000000001 x11 0000000000000180
10-15 13:54:54.566 23570 23570 F DEBUG : x12 0000000000000004 x13 0000000500000000 x14 0000000000000020 x15 0000007f7df7e268
10-15 13:54:54.566 23570 23570 F DEBUG : x16 0000007f7e9fec91 x17 0000007f7e4192c1 x18 0000000000000116 x19 0000007f84288c30
10-15 13:54:54.566 23570 23570 F DEBUG : x20 0000000000000000 x21 0000007f7df7f0c0 x22 0000007f7df7f0d0 x23 0000007f7df7d270
10-15 13:54:54.566 23570 23570 F DEBUG : x24 0000007f7e5b0321 x25 0000007f7df7d1c0 x26 0000007f82ec3200 x27 0000007f7e4192c0
10-15 13:54:54.567 23570 23570 F DEBUG : x28 0000000000000004 x29 0000007f7df7e278 x30 0000007f844531b0
10-15 13:54:54.567 23570 23570 F DEBUG : sp 0000007f7df7d250 pc 0000007f8445326c pstate 0000000080000000
10-15 13:54:54.567 23570 23570 F DEBUG :
10-15 13:54:54.567 23570 23570 F DEBUG : backtrace:
10-15 13:54:54.567 23570 23570 F DEBUG : #00 pc 00000000002ae26c /data/data/com.akka.akkamark/app_flutter/isolate_snapshot_instr
10-15 13:54:54.567 23570 23570 F DEBUG : #01 pc 00000000002ae1ac /data/data/com.akka.akkamark/app_flutter/isolate_snapshot_instr

I've tested with several devices, from android 6.0 to android 8.1 and I have the same problem. This logcat is with a device with android 7.1.2.

I am not sure if it is a flutter bug or maybe the problem is with image pugglin and also I am not sure if I am doing something wrong, I followed all the steps that are in flutter web page to generate the release apk. Any clue?

Thanks in advance!

Issue 2:
I'm developing these features for a flutter app: loading an image from gallery, resize and save it.

The resizing part is a cpu intensive operation so I followed the approach suggested here using isolate to gain a better user experience.

When I run the code in debug mode I have no issue, but when I try the same code in release mode my image is saved in a wrong way:

Here is the code to replicate the problem. This is a new flutter project with the following files:

In pubspec.yaml I add this section:

dependencies:
path_provider: ^0.4.1
image_picker: ^0.4.10
image: ^2.0.4
flutter:
sdk: flutter

All code is in lib/main.dart:

import 'dart:async';
import 'dart:io';
import 'dart:isolate';

import 'package:flutter/material.dart';
import 'package:image/image.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);

@override
_MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
File _file;

Future<File> _getImage() async {
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image != null) {
return image;
}
return null;
}

static decode(DecodeParam param) async {
var p = await param.file.readAsBytes();
var image = decodeImage(p);
var thumbnail = copyResize(image, 120);
param.sendPort.send(thumbnail);
}

void _displayImage() async {
setState(() {
_file = null;
});

File file = await _getImage();
ReceivePort receivePort = new ReceivePort();

await Isolate.spawn(decode, new DecodeParam(file, receivePort.sendPort));

var image = await receivePort.first;

Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;

File profilePictureFile =
File(p.join(tempPath, 'thumbnail' + _counter.toString() + '.png'))
..writeAsBytesSync(encodePng(image));

setState(() {
_counter++;
_file = profilePictureFile;
});
}

@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_file != null
? Container(
height: 200.0,
width: 200.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fitWidth, image: FileImage(_file))))
: Container(),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _displayImage,
child: new Icon(Icons.add),
),
);
}
}

class DecodeParam {
final File file;
final SendPort sendPort;
DecodeParam(this.file, this.sendPort);
}

flutter doctor -v:

[√] Flutter (Channel master, v0.9.7-pre.61, on Microsoft Windows [Versione 10.0.15063], locale it-IT)
• Flutter version 0.9.7-pre.61 at C:\src\flutter
• Framework revision 2d81adf74c (2 days ago), 2018-10-05 22:29:37 -0700
• Engine revision 572fa5646a
• Dart version 2.1.0-dev.6.0.flutter-c6254163dc

[√] Android toolchain - develop for Android devices (Android SDK 28.0.1)
• Android SDK at d:\Profiles\alarosa\AppData\Local\Android\sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-28, build-tools 28.0.1
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b02)
• All Android licenses accepted.

[√] Android Studio (version 3.1)
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin version 26.0.1
• Dart plugin version 173.4700
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b02)

[!] IntelliJ IDEA Community Edition (version 2018.1)
• IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.1
X Flutter plugin not installed; this adds Flutter specific functionality.
X Dart plugin not installed; this adds Dart specific functionality.
• For information about installing plugins, see
https://flutter.io/intellij-setup/#installing-the-plugins

[√] VS Code (version 1.27.2)
• VS Code at d:\Profiles\alarosa\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 2.19.0

[√] VS Code, 64-bit edition (version 1.27.2)
• VS Code at C:\Program Files\Microsoft VS Code
• Flutter extension version 2.19.0

[√] Connected device (1 available)
• HUAWEI VNS L31 • 4TE0216A14001341 • android-arm64 • Android 7.0 (API 24)

! Doctor found issues in 1 category.

Thank you.