Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending Bitmap to Flutter from Android Platform

I am creating a image processing app so first using android platform apis to process the bitmap ad then sending it to flutter. I am using below code to convert bitmap to byte[]

  Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath());
        ByteBuffer allocate = ByteBuffer.allocate(bitmap.getByteCount());
        bitmap.copyPixelsToBuffer(allocate);
        byte[] array = allocate.array();

and then sending it to flutter using Method Channel as Uint8List but when i read it as Image.memory() . It is giving me error as

Failed decoding image. Data is either invalid, or it is encoded using an unsupported format. Below is code of My Main Activity

package com.rahul.flutterappdomain;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;

import com.asksira.bsimagepicker.BSImagePicker;
import com.asksira.bsimagepicker.Utils;

import java.nio.ByteBuffer;

import io.flutter.app.FlutterFragmentActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class ActivityMain extends FlutterFragmentActivity implements BSImagePicker.OnSingleImageSelectedListener{
    private static final String TAG = "DomainFilterFlutterApp";
    private static final String CHANNEL = "com.rummy.io/filter";
    MethodChannel methodChannel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        methodChannel = new MethodChannel(getFlutterView(), CHANNEL);
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                if (methodCall.method.equals("openImagePicker")) {
                    showImagePickerDialog();
                    result.success(null);
                }

            }
        });
    }

    private void showImagePickerDialog() {
        String providerAuthority = "com.rahul.ffd.fileprovider";
        BSImagePicker singleSelectionPicker = new BSImagePicker.Builder(providerAuthority)
                .setSpanCount(4)
                .setGridSpacing(Utils.dp2px(4))
                .setPeekHeight(Utils.dp2px(360))
                .build();


        singleSelectionPicker.show(getSupportFragmentManager(), "picker");

    }

    @Override
    public void onSingleImageSelected(Uri uri) {
        Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath());
        ByteBuffer allocate = ByteBuffer.allocate(bitmap.getByteCount());
        bitmap.copyPixelsToBuffer(allocate);
        byte[] array = allocate.array();

        methodChannel.invokeMethod("setImage", array);
        Log.d(TAG, "onSingleImageSelected: ------------");

    }
}

and code of my dart file

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

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'colors.dart';
//import 'package:image_picker/image_picker.dart';

void main(){runApp(MyApp());}

class MyApp extends StatefulWidget {
  static const platform = const MethodChannel('com.rummy.io/filter');
  String _image;
  Uint8List _image_data ;

  Future<Null> getImage() async {
    platform.invokeMethod("openImagePicker");
  }

  @override
  State<StatefulWidget> createState() {
    var myAppState = MyAppState();
    platform.setMethodCallHandler((MethodCall call) {
      switch (call.method) {
        case 'setImage':
          print('setState');
          myAppState.makeState(call.arguments);
          print('setState');
      }
    });
    return myAppState;
  }
}



class MyAppState extends State<MyApp> {
  void makeState( Uint8List imgData) {
    setState(() {

      widget._image_data = imgData;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: _mAppTheme,
      home: Scaffold(
        appBar: AppBar(
          actions: <Widget>[
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.save),
            )
          ],
        ),
        body: _mBody(widget._image_data),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            widget.getImage();
          },
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.elliptical(12.0, 13.0))),
          child: Icon(Icons.image),
        ),
      ),
    );
  }
  double _discreteValue = 0.0;
  double _discreteValue2 = 0.0;

  Widget _mBody(Uint8List imgData) {

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,

        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Card(

//          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
              child: imgData == null
                  ? Image.asset(
                'images/beauty.jpg',
                fit: BoxFit.contain,

              )
                  : /*Image.file(
                File(img),
                fit: BoxFit.contain,)*/
              Image.memory(imgData),


            ),
          ),
          new Slider(
              value: _discreteValue,
              min: 0.0,
              max: 200.0,
              divisions: 10,
              activeColor: Colors.greenAccent,
              label: '${_discreteValue.round()}',
              onChanged: (double value) {
                setState(() {
                  _discreteValue = value;
                });}),
          new Slider(
              value: _discreteValue2,
              min: 0.0,
              max: 200.0,
              divisions: 10,
              activeColor: Colors.greenAccent,
              label: '${_discreteValue2.round()}',
              onChanged: (double value) {
                setState(() {
                  _discreteValue2 = value;
                });}),
        ],
      ),
    );
  }

/*  Future getImage() async {
    var image = await ImagePicker.pickImage(source: ImageSource.gallery);
    setState(() {
      _image = image;
    });
  }*/
}



final ThemeData _mAppTheme = _buildAppTheme();
ThemeData _buildAppTheme() {
  final ThemeData base = ThemeData.light();
  return base.copyWith(
    accentColor: kShrineBrown900,
    primaryColor: kShrinePink100,
    buttonColor: kShrinePink100,
    scaffoldBackgroundColor: kShrineBackgroundWhite,
    cardColor: kShrineBackgroundWhite,
    textSelectionColor: kShrinePink100,
    errorColor: kShrineErrorRed,
  );
}
like image 942
Gaurav Maan Avatar asked Jul 25 '18 10:07

Gaurav Maan


People also ask

How do I display bitmap images in flutter?

Displaying. To easiest way to display an image is to getting the bitmap with header and then passing it to the widget Image. memory : // .. Uint8List headedBitmap = bitmap.

How do you turn an image into a byte in flutter?

RaisedButton( onPressed: () async { List<int> imageBytes = await sampleImage. readAsBytes(); base64Image = base64Encode(imageBytes); print(base64Image);},), SizedBox(height: 30,), Image.


3 Answers

I had the same error message. The problem is that you decode bitmap image in a wrong way, in your Java code. Here's the solution that will work

ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bitmap.recycle();

// And return byteArray through your MethodChannel
like image 132
Ihor Klimov Avatar answered Nov 04 '22 09:11

Ihor Klimov


You can't pass a raw bitmap to Image.memory because it doesn't contain enough information, notably width and height. The Image constructors need a recognized file format: JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP.

If the file that you are opening here:

Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath());

is in one of the formats above, then don't decode it into an Android bitmap, but rather read it verbatim into a byte array, send that over the channel and use that as the input for the Image constructor. (Or, if you can, simply pass the file path back to Dart and use dart:io and, if necessary path_provider, to read the file.)

If you really need to pass a raw bitmap to Image then the simplest thing is to prepend a Windows Bitmap (BMP) header. There's an example here for an unusual raw bitmap format. Yours wouldn't even need the indexed color map as you almost certainly already have ARGB pixels (test this by checking that bitmap size in bytes = 4 * width * height).

like image 3
Richard Heap Avatar answered Nov 04 '22 10:11

Richard Heap


More detail to @Ihor Klimov answer

I convert the bitmap in java from fingerprint device by:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
fingerBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
fingerBitmap.recycle();

then in Flutter Dart I just use

Uint8List fingerImages;

  void mapFingerImage(dynamic imageBytes) {
    setState(() {
      fingerImages = imageBytes;
    });
  }
    Image.memory(fingerImages,
                  width: 100,
                  height: 100,
                   fit: BoxFit.contain,)
like image 2
amorenew Avatar answered Nov 04 '22 09:11

amorenew