4

hii I am trying to implement the compute function. i am trying to click an image via camera using image_picker plugin. Then i a am trying to get the Hash digest of the image file. Since its a very intense operation, i am trying to use the compute function but unable to to do so. This is what i have done so far

final File picture = await ImagePicker.pickImage(
        source: ImageSource.camera);
    setState(() {
      _imageFile = picture;
    });
var result = await compute(generateImageHash(), _imageFile);
    print(result);

This is my generateImageHash function which i am trying to pass to compute function

generateImageHash() async{
    var image_bytes =  _imageFile.readAsBytesSync().toString();
    var bytes = utf8.encode(image_bytes); // data being hashed
    String digest = sha256.convert(bytes).toString();
    print("This is image Digest :  $digest");
    return digest;
  }

But when i click the image using my phone, i am getting following error

 Unhandled Exception: type 'Future<dynamic>' is not a subtype of type '(File) => FutureOr<dynamic>'

I am new to strongly typed language, Please Help. Thanks

imran
  • 626
  • 2
  • 13
  • 24

3 Answers3

6

first issue please change

var result = await compute(generateImageHash(), _imageFile); 

to

var result = await compute(generateImageHash, _imageFile);

you don't need this ()
similar https://github.com/flutter/flutter/issues/27582

second issue please remove await keyword from compute

var result = compute(generateImageHash, _imageFile);

and you need to modify

Future<String> generateImageHash(File file) async{

and put it outside of class

I just provide a full working code and demo picture. You can see digest string in this picture

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:video_player/video_player.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:crypto/crypto.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Image Picker Demo',
      home: MyHomePage(title: 'Image Picker Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

Future<String> generateImageHash(File file) async{
  var image_bytes =  file.readAsBytesSync().toString();
  var bytes = utf8.encode(image_bytes); // data being hashed
  String digest = sha256.convert(bytes).toString();
  print("This is image Digest :  $digest");
  return  digest;
}

class _MyHomePageState extends State<MyHomePage> {
  File _imageFile;
  dynamic _pickImageError;
  bool isVideo = false;
  VideoPlayerController _controller;
  String _retrieveDataError;



  void _onImageButtonPressed(ImageSource source) async {
    if (_controller != null) {
      _controller.setVolume(0.0);
      _controller.removeListener(_onVideoControllerUpdate);
    }
    if (isVideo) {
      ImagePicker.pickVideo(source: source).then((File file) {
        if (file != null && mounted) {
          setState(() {
            _controller = VideoPlayerController.file(file)
              ..addListener(_onVideoControllerUpdate)
              ..setVolume(1.0)
              ..initialize()
              ..setLooping(true)
              ..play();
          });
        }
      });
    } else {
      try {
        print("_imageFile start");
        _imageFile = await ImagePicker.pickImage(source: source);
        print("_imageFile end");

        print("compute start");
        var result = compute(generateImageHash, _imageFile);
        print("compute end");
        print(result);
      } catch (e) {
        _pickImageError = e;
      }
      setState(() {});
    }
  }

  void _onVideoControllerUpdate() {
    setState(() {});
  }

  @override
  void deactivate() {
    if (_controller != null) {
      _controller.setVolume(0.0);
      _controller.removeListener(_onVideoControllerUpdate);
    }
    super.deactivate();
  }

  @override
  void dispose() {
    if (_controller != null) {
      _controller.dispose();
    }
    super.dispose();
  }

  Widget _previewVideo(VideoPlayerController controller) {
    final Text retrieveError = _getRetrieveErrorWidget();
    if (retrieveError != null) {
      return retrieveError;
    }
    if (controller == null) {
      return const Text(
        'You have not yet picked a video',
        textAlign: TextAlign.center,
      );
    } else if (controller.value.initialized) {
      return Padding(
        padding: const EdgeInsets.all(10.0),
        child: AspectRatioVideo(controller),
      );
    } else {
      return const Text(
        'Error Loading Video',
        textAlign: TextAlign.center,
      );
    }
  }

  Widget _previewImage() {
    final Text retrieveError = _getRetrieveErrorWidget();
    if (retrieveError != null) {
      return retrieveError;
    }
    if (_imageFile != null) {
      return Image.file(_imageFile);
    } else if (_pickImageError != null) {
      return Text(
        'Pick image error: $_pickImageError',
        textAlign: TextAlign.center,
      );
    } else {
      return const Text(
        'You have not yet picked an image.',
        textAlign: TextAlign.center,
      );
    }
  }

  Future<void> retrieveLostData() async {
    final LostDataResponse response = await ImagePicker.retrieveLostData();
    if (response.isEmpty) {
      return;
    }
    if (response.file != null) {
      setState(() {
        if (response.type == RetrieveType.video) {
          isVideo = true;
          _controller = VideoPlayerController.file(response.file)
            ..addListener(_onVideoControllerUpdate)
            ..setVolume(1.0)
            ..initialize()
            ..setLooping(true)
            ..play();
        } else {
          isVideo = false;
          _imageFile = response.file;
        }
      });
    } else {
      _retrieveDataError = response.exception.code;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Platform.isAndroid
            ? FutureBuilder<void>(
          future: retrieveLostData(),
          builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.none:
              case ConnectionState.waiting:
                return const Text(
                  'You have not yet picked an image.',
                  textAlign: TextAlign.center,
                );
              case ConnectionState.done:
                return isVideo
                    ? _previewVideo(_controller)
                    : _previewImage();
              default:
                if (snapshot.hasError) {
                  return Text(
                    'Pick image/video error: ${snapshot.error}}',
                    textAlign: TextAlign.center,
                  );
                } else {
                  return const Text(
                    'You have not yet picked an image.',
                    textAlign: TextAlign.center,
                  );
                }
            }
          },
        )
            : (isVideo ? _previewVideo(_controller) : _previewImage()),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              isVideo = false;
              _onImageButtonPressed(ImageSource.gallery);
            },
            heroTag: 'image0',
            tooltip: 'Pick Image from gallery',
            child: const Icon(Icons.photo_library),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              onPressed: () {
                isVideo = false;
                _onImageButtonPressed(ImageSource.camera);
              },
              heroTag: 'image1',
              tooltip: 'Take a Photo',
              child: const Icon(Icons.camera_alt),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              backgroundColor: Colors.red,
              onPressed: () {
                isVideo = true;
                _onImageButtonPressed(ImageSource.gallery);
              },
              heroTag: 'video0',
              tooltip: 'Pick Video from gallery',
              child: const Icon(Icons.video_library),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              backgroundColor: Colors.red,
              onPressed: () {
                isVideo = true;
                _onImageButtonPressed(ImageSource.camera);
              },
              heroTag: 'video1',
              tooltip: 'Take a Video',
              child: const Icon(Icons.videocam),
            ),
          ),
        ],
      ),
    );
  }

  Text _getRetrieveErrorWidget() {
    if (_retrieveDataError != null) {
      final Text result = Text(_retrieveDataError);
      _retrieveDataError = null;
      return result;
    }
    return null;
  }
}

class AspectRatioVideo extends StatefulWidget {
  AspectRatioVideo(this.controller);

  final VideoPlayerController controller;

  @override
  AspectRatioVideoState createState() => AspectRatioVideoState();
}

class AspectRatioVideoState extends State<AspectRatioVideo> {
  VideoPlayerController get controller => widget.controller;
  bool initialized = false;

  void _onVideoControllerUpdate() {
    if (!mounted) {
      return;
    }
    if (initialized != controller.value.initialized) {
      initialized = controller.value.initialized;
      setState(() {});
    }
  }

  @override
  void initState() {
    super.initState();
    controller.addListener(_onVideoControllerUpdate);
  }

  @override
  Widget build(BuildContext context) {
    if (initialized) {
      return Center(
        child: AspectRatio(
          aspectRatio: controller.value?.aspectRatio,
          child: VideoPlayer(controller),
        ),
      );
    } else {
      return Container();
    }
  }
}

enter image description here

chunhunghan
  • 51,087
  • 5
  • 102
  • 120
  • Thanks for your reply but if i do this i m getting this error: The argument type 'dynamic Function()' can't be assigned to the parameter type 'FutureOr Function(File)'. – imran Aug 07 '19 at 01:56
  • Yeah .. i have tried followed this example..but could not implement – imran Aug 07 '19 at 02:04
  • There are too many need to explain. so I just provide a full working code to allow you to modify it directly. and remove some my unuseful comments – chunhunghan Aug 07 '19 at 02:58
  • thank you so much brother for taking the pain ..i am greatly obliged..i will try to implement your code and will tell you.. Just wanted to know how is the performance? without compute it is taking around 10 seconds to render the image – imran Aug 07 '19 at 03:02
  • the working code I provided is official image_picker example. I just add your logical. and about performance, it less than 3 second in my Android emulator – chunhunghan Aug 07 '19 at 03:14
  • Yep.. i was not aware of the fact the argument in the compute function must be a top level function...Thanks you so much brother..Its working – imran Aug 07 '19 at 03:22
  • can you help @chunhunghan https://stackoverflow.com/questions/68656090/compute-method-not-calling-in-futurebuilder – John Joe Aug 05 '21 at 06:18
1

I would additionally suggest you change the code of the function to calculate the hash to something like:

Future<String> generateImageHash(File file) async{
  Digest digest = await sha256.bind(file).openRead()).first;
  print("This is image Digest :  ${digest.toString()}");
  return  digest.toString();
}

Otherwise your digest will be false calculated.

Jose Lima
  • 11
  • 1
0

try like these code.

static  generateImageHash(String fileBytes){
print('fileBytes = $fileBytes');
var bytes = utf8.encode(fileBytes); // data being hashed
String digest = sha256.convert(bytes).toString();
print("This is image Digest :  $digest");
return digest;
}



var imageBytes =  image.readAsBytesSync().toString();
var result = await compute(generateImageHash, imageBytes);
null
  • 523
  • 2
  • 8
  • When i try to do this, this error creeps in: The argument type 'Future Function()' can't be assigned to the parameter type 'FutureOr Function(File)'. – imran Aug 07 '19 at 02:07