I am coding an application for recognizing plant species. I want to pick an image and show that on screen but i can't. Image returns null. Although i choose photo from gallery, it still says "no image selected".
I also added read_external_storage permission to android manifests.
import 'dart:io';
import 'package:tflite/tflite.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class PlantSpeciesRecognition extends StatefulWidget {
var model;
PlantSpeciesRecognition(this.model);
#override
_PlantSpeciesRecognitionState createState() =>
_PlantSpeciesRecognitionState();
}
class _PlantSpeciesRecognitionState extends State<PlantSpeciesRecognition> {
File _image;
bool _busy = false;
List _recognitions;
Future chooseImageGallery() async {
debugPrint("choose image function");
var image = await ImagePicker().getImage(source: ImageSource.gallery);
//var image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image == null) {
debugPrint("choose image if");
return;
}
//await analyzeTFLite();
setState(() {
debugPrint("choose image set state");
_busy = true;
_image = image as File;
});
}
#override
Widget build(BuildContext context) {
List<Widget> stackChildren = [];
Size size = MediaQuery.of(context).size;
//stackChildren.clear();
stackChildren.add(Positioned(
top: 0.0,
left: 0.0,
width: size.width,
child: _image == null ? Text("No Image Selected") : Image.file(_image),
));
return Scaffold(
appBar: AppBar(
title: const Text('Plant Species Recognition'),
),
floatingActionButton: FloatingActionButton(
onPressed: chooseImageGallery,
tooltip: 'Pick Image',
child: Icon(Icons.image),
),
body: Stack(
children: stackChildren,
),
);
}
}
this is normal because it is not the file that is returned. Correct the following line:
setState(() {
debugPrint("choose image set state");
_busy = true;
_image = File(image.path); //change this line.
});
Related
I am using ImagePicker in a flutter app, but we should specify the source of the Image.
How can I let the choice to the user?
File? selectedImage;
getImage() async {
XFile? file = await ImagePicker().pickImage(source: ImageSource.camera);
selectedImage = File(file!.path);
setState(() {});
}
You can show a dialog for to choose option.
getImage() async {
bool? isCamera = await showDialog(
context: context,
builder: (context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text("Camera"),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text("gallery "),
),
],
),
),
);
if (isCamera == null) return;
XFile? file = await ImagePicker()
.pickImage(source: isCamera ? ImageSource.camera : ImageSource.gallery);
selectedImage = File(file!.path);
setState(() {});
}
You need to make two Text one for gallery and one for Camera and on thier onTap() you should pass a function which will be like
void imageGalleryBottomSheet(
{BuildContext context,
VoidCallback onCameraTap,
VoidCallback onGalleryTap}) {
//your code logic
}
Preface:
I'm using the Screenshot package. In this package, there is a method captureAndSave() which saves a widget as an image to a specific location, however, when I call this function, my image is not being saved. Why?
Complete Code Example:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:screenshot/screenshot.dart';
class QrCodeScreen extends StatefulWidget {
const QrCodeScreen({Key? key}) : super(key: key);
#override
State<QrCodeScreen> createState() => _QrCodeScreenState();
}
class _QrCodeScreenState extends State<QrCodeScreen> {
final _screenshotController = ScreenshotController();
Image? image;
var doesTheImageExist = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (image == null)
TextButton(
child: Text("Save QR Code"),
onPressed: () async {
await _captureAndSaveQRCode();
image = await _loadImage();
setState(() {});
},
)
else
image!,
Text("Is the QR Code saved to your device? ${doesTheImageExist}"),
if (image == null)
Screenshot(
controller: _screenshotController,
child: _buildQRImage('_authProvider.user!.uid')),
],
),
);
}
Widget _buildQRImage(String data) {
return QrImage(
data: data,
size: 250.0,
gapless: false,
foregroundColor: Colors.black,
backgroundColor: Colors.white,
);
}
Future<String> get imagePath async {
final directory = (await getApplicationDocumentsDirectory()).path;
return '$directory/qr.png';
}
Future<Image> _loadImage() async {
return imagePath.then((imagePath) => Image.asset(imagePath));
}
Future<void> _captureAndSaveQRCode() async {
final path = await imagePath;
await _screenshotController.captureAndSave(path);
// It always returns false, although I'm saving the file using `captureAndSave` .
doesTheImageExist = File(path).existsSync();
}
}
The Question:
In the code above, when I click on the TextButton() that says "Save QR Code" it then calls _captureAndSaveQRCode() and _loadImage(). Hence my image should successfully be saved to my (Android) phone. However, I get an error:
Unable to load asset: /data/user/0/com.example.qr/app_flutter/qr.png
Full Traceback:
======== Exception caught by image resource service ================================================
The following assertion was thrown resolving an image codec:
Unable to load asset: /data/user/0/com.example.qr/app_flutter/qr.png
When the exception was thrown, this was the stack:
#0 PlatformAssetBundle.load (package:flutter/src/services/asset_bundle.dart:237:7)
<asynchronous suspension>
#1 AssetBundleImageProvider._loadAsync (package:flutter/src/painting/image_provider.dart:675:14)
<asynchronous suspension>
Image provider: AssetImage(bundle: null, name: "/data/user/0/com.example.qr/app_flutter/qr.png")
Image key: AssetBundleImageKey(bundle: PlatformAssetBundle#5986d(), name: "/data/user/0/com.example.qr/app_flutter/qr.png", scale: 1.0)
====================================================================================================
Why isn't my image being saved to the device when calling _captureAndSaveQRCode()?
Side note:
I recently posted an answer (currently in Bounty) with (almost) the same code as in this question which does work correctly, so, what's the difference?
The problem was that I had an empty setState:
onPressed: () async {
await _captureAndSaveQRCode();
image = await _loadImage();
setState(() {});
},
)
So to solve the problem, I removed the setState and also got rid of the _loadImage() function.
And then updated the image variable within the TextButton():
TextButton(
child: Text("Save QR Code"),
onPressed: () async {
await _captureAndSaveQRCode();
setState(() {
doesTheImageExist = true;
image = image;
});
},
)
Complete working example:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:screenshot/screenshot.dart';
class QrCodeScreen extends StatefulWidget {
const QrCodeScreen({Key? key}) : super(key: key);
#override
State<QrCodeScreen> createState() => _QrCodeScreenState();
}
class _QrCodeScreenState extends State<QrCodeScreen> {
final _screenshotController = ScreenshotController();
Image? image;
var doesTheImageExist = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (image == null)
TextButton(
child: Text("Save QR Code"),
onPressed: () async {
await _captureAndSaveQRCode();
setState(() {
doesTheImageExist = true;
image = image;
});
},
)
else
image!,
Row(children: [
Text('hi'),
Text("Is the QR Code saved to your device? ${doesTheImageExist}")
]),
if (image == null)
Screenshot(
controller: _screenshotController,
child: _buildQRImage('_authProvider.user!.uid')),
],
),
);
}
Widget _buildQRImage(String data) {
return QrImage(
data: data,
size: 250.0,
gapless: false,
foregroundColor: Colors.black,
backgroundColor: Colors.white,
);
}
Future<String> get imagePath async {
final directory = (await getApplicationDocumentsDirectory()).path;
return '$directory/qr.png';
}
// Future<Image> _loadImage() async {
// return imagePath.then((imagePath) => Image.asset(imagePath));
// }
Future<void> _captureAndSaveQRCode() async {
final path = await imagePath;
await _screenshotController.captureAndSave(path);
// It always returns false, although I'm saving the file using `captureAndSave` .
doesTheImageExist = File(path).existsSync();
}
}
load image with Image.file(File(imagePath))
Image.Asset is for loading images defined in pubspec.yaml
Edit:
the path in captureAndSave is directory path. it takes another optional argument fileName.
// previous code
// i create new getter for directory
Future<String> get dirPath async {
final directory = (await getApplicationDocumentsDirectory()).path;
return directory;
}
Future<String> get imagePath async {
final directory = await dirPath;
return '$directory/qr.png';
}
Future<Image> _loadImage() async {
return imagePath.then((imagePath) => Image.file(File(imagePath)));
}
Future<void> _captureAndSaveQRCode() async {
final path = await dirPath;
await _screenshotController.captureAndSave(path, fileName: "qr.png");
// It always returns false, although I'm saving the file using `captureAndSave` .
doesTheImageExist = File(path).existsSync();
}
// the rest of the code
I am coding a plant species recognition app. App takes image from gallery and predict the plant species. So i should pick image from gallery and show the accuracy score.
I did everything exactly the same from the flutter machine learning book but app doesnt show the picked image and accuracy score. I pick image from gallery but it still says "no image selected".
If i delete these lines, i can pick the image from gallery and show on the screen. But then i cant apply AI operations.
await analyzeTFLite();
await recognizeImage(File(image.path));
Screen should be as first pic. But mine is as second pic. The whole code is below. Thanks for your help.
import 'dart:io';
import 'package:tflite/tflite.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class PlantSpeciesRecognition extends StatefulWidget {
var model;
PlantSpeciesRecognition(this.model);
#override
_PlantSpeciesRecognitionState createState() =>
_PlantSpeciesRecognitionState();
}
class _PlantSpeciesRecognitionState extends State<PlantSpeciesRecognition> {
File _image;
bool _busy = false;
List _recognitions;
Future chooseImageGallery() async {
debugPrint("choose image function");
var image = await ImagePicker().getImage(source: ImageSource.gallery);
//var image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image == null) {
debugPrint("choose image if");
return;
}
setState(() {
_busy = true;
});
await analyzeTFLite();
await recognizeImage(File(image.path));
setState(() {
debugPrint("choose image set state");
_busy = false;
//_image = image as File;
_image = File(image.path);
});
}
Future analyzeTFLite() async {
Tflite.close();
String res = await Tflite.loadModel(
model: "assets/model.tflite",
labels: "assets/labels.txt"
);
print('Model Loaded: $res');}
Future recognizeImage(File image) async{
var recognitions = await Tflite.runModelOnImage(path: _image.path);
setState(() {
_recognitions = recognitions;
});
print("Recognition Result: $_recognitions");
}
#override
Widget build(BuildContext context) {
List<Widget> stackChildren = [];
Size size = MediaQuery.of(context).size;
stackChildren.clear();
stackChildren.add(Positioned(
top: 0.0,
left: 0.0,
width: size.width,
child: _image == null
? Center(child: Text("No Image Selected"))
: Image.file(_image),
));
stackChildren.add(Center(
child: Column(
children: _recognitions != null
? _recognitions.map((res) {
return Text(
"${res["label"]}: ${res["confidence"].toStringAsFixed(3)}",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
background: Paint()
..color = Colors.white,
),
);
}).toList() : [],
),
));
return Scaffold(
appBar: AppBar(
title: const Text('Plant Species Recognition'),
),
floatingActionButton: FloatingActionButton(
onPressed: chooseImageGallery,
tooltip: 'Pick Image',
child: Icon(Icons.image),
),
body: Stack(
children: stackChildren,
),
);
}
}
I am trying to choose image with gallery or camera and display with image_picker.
When I run the app in android, I am able to choose image but not displaying. In contrast I am getting following in the console for the first time.
I/HwViewRootImpl(11213): removeInvalidNode all the node in jank list is out of time
If I repeat the same, it gives following in each time while press the button instead of opening gallery or camera.
I/flutter (11213): PlatformException(already_active, Image picker is already active, null)
I found following solutions from my search but not solved my case.
flutter clean , flutter clean
changing the version of the plugin
updating all dependencies
using retrieveLostData method as stated in the plugin documentation
Following is the code I have used for retrieve image:
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
File imageFile;
#override
void initState() {
super.initState();
}
Future _getImage(int type) async {
print("Called Image Picker");
var image = await ImagePicker.pickImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
);
setState(() {
print("$image.path");
imageFile = image;
});
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await ImagePicker.retrieveLostData();
if (response == null) {
return;
}
if (response.file != null) {
setState(() {
if (response.type == RetrieveType.image) {
imageFile = response.file;
}
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Image Editor"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
imageFile != null
? Image.file(
imageFile,
height: MediaQuery.of(context).size.height / 2,
)
: Text("Image editor"),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Add Slip"),
content: Row(
children: <Widget>[
Expanded(
child: new FlatButton(
child: new Text("Camera"),
onPressed: () {
_getImage(1);
Navigator.pop(context);
},
),
),
Expanded(
child: new FlatButton(
child: new Text("Gallery"),
onPressed: () {
_getImage(2);
Navigator.pop(context);
},
),
)
],
),
);
},
);
},
tooltip: 'Pick Image',
child: Icon(Icons.camera),
),
);
}
}
I've tried the sample code that you've shared and for some reason got compiler issues but not the same issue as yours. Therefore, I've tried to debug your code. Here is the fixed code:
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File imageFile;
#override
void initState() {
super.initState();
}
Future _getImage(int type) async {
print("Called Image Picker");
var image = await await ImagePicker.platform.pickImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
);
setState(() {
print("$image.path");
imageFile = File(image.path);
});
}
Future<void> retrieveLostData() async {
final LostData response = await ImagePicker.platform.retrieveLostData();
if (response == null) {
return;
}
if (response.file != null) {
setState(() {
if (response.type == RetrieveType.image) {
imageFile = response.file as File;
}
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Image Editor"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
imageFile != null
? Image.file(
imageFile,
height: MediaQuery.of(context).size.height / 2,
)
: Text("Image editor"),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Add Slip"),
content: Row(
children: <Widget>[
Expanded(
child: new FlatButton(
child: new Text("Camera"),
onPressed: () {
_getImage(1);
Navigator.pop(context);
},
),
),
Expanded(
child: new FlatButton(
child: new Text("Gallery"),
onPressed: () {
_getImage(2);
Navigator.pop(context);
},
),
)
],
),
);
},
);
},
tooltip: 'Pick Image',
child: Icon(Icons.camera),
),
);
}
}
Few of the fixes are these lines:
var image = await await ImagePicker.pickImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
);
It's having an error in the compiler so I've changed it to this:
var image = await await ImagePicker.platform.pickImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
);
Same to these lines:
Future<void> retrieveLostData() async {
final LostData response = await ImagePicker.retrieveLostData();
if (response == null) {
return;
}
if (response.file != null) {
setState(() {
if (response.type == RetrieveType.image) {
imageFile = response;
}
});
}
}
Fixed version:
Future<void> retrieveLostData() async {
final LostData response = await ImagePicker.platform.retrieveLostData();
if (response == null) {
return;
}
if (response.file != null) {
setState(() {
if (response.type == RetrieveType.image) {
imageFile = response.file as File;
}
});
}
}
and this
setState(() {
print("$image.path");
imageFile = image;
}
to this:
setState(() {
print("$image.path");
imageFile = File(image.path);
}
The reason could be the version of image_picker that I'm using. Currently I'm using the image_picker: ^0.7.4.
Here is actual output:
I've also encounter an issue if your running this in an Android API version 30, you get this error:
Unhandled Exception: PlatformException(no_available_camera, No cameras available for taking pictures., null, null)
The workaround is to add <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> in manifest as mentioned in this GitHub post.
i need to save file eg.jpg to "internalstorage/appname/files/"
and show a notification if it does exists already in folder. when a button is pressed/activity intiated,it should download file to local storage of andorid device with dart code.
help me find solution.
**code:**
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import './landing_page.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:simple_permissions/simple_permissions.dart';
import 'package:flutter/services.dart';
class MoviesPage extends StatefulWidget {
#override
State createState() => new MoviesPageState();
}
class MoviesPageState extends State<MoviesPage> {
final dUrl ="https://cdn.putlockers.es/download/0BBCA7584749D4E741747E32E6EB588AEA03E40F";
bool downloading = false;
var progressString = "";
static const MethodChannel _channel =
const MethodChannel('flutter_downloader');
#override
void initState() {
super.initState();
downloadFile();
}
Future<void> downloadFile() async {
Dio dio = Dio();
try {
var dir = await getApplicationDocumentsDirectory();
await dio.download(dUrl, "${dir.path}/file.torrent",
onProgress: (rec, total) {
print("Rec: $rec , Total: $total");
setState(() {
downloading = true;
progressString = ((rec / total) * 100).toStringAsFixed(0) + "%";
});
});
} catch (e) {
print(e);
}
setState(() {
downloading = false;
progressString = "Completed";
});
print("Download completed");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AppBar"),
),
body: Center(
child: downloading
? Container(
height: 120.0,
width: 200.0,
child: Card(
color: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
SizedBox(
height: 20.0,
),
Text(
"Downloading File: $progressString",
style: TextStyle(
color: Colors.white,
),
)
],
),
),
)
: Text("No Data"),
),
);
}
}
thanks in advance.post your solutions in full fludged manner.
I've checked the minimal repro you've posted and it seems that you're using Flutter plugin dio to download the file. I've reused the Future<void> downloadFile() from your code and modified it a bit to check if the plugin works as expected. As of version 3.0.10 of the dio plugin, the onProgress on dio.download() is now onReceiveProgress, but it still essentially has the same function.
Here's the method for downloading the image file based from your code with a bit of modification.
Future downloadFile() async {
Dio dio = Dio();
var dir = await getApplicationDocumentsDirectory();
var imageDownloadPath = '${dir.path}/image.jpg';
await dio.download(imageSrc, imageDownloadPath,
onReceiveProgress: (received, total) {
var progress = (received / total) * 100;
debugPrint('Rec: $received , Total: $total, $progress%');
setState(() {
downloadProgress = received.toDouble() / total.toDouble();
});
});
// downloadFile function returns path where image has been downloaded
return imageDownloadPath;
}
The plugin works as expected and successfully downloads the image file. Though I'm unable to verify how you're able to determine that the image that you're trying to download fails on your repro. In my sample app, Future downloadFile() returns a String where the image path is stored. I then use this to update the Image Widget to display the downloaded image - this determines that the download has been successful.
Here's the complete sample app.
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final imageSrc = 'https://picsum.photos/250?image=9';
var downloadPath = '';
var downloadProgress = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(flex: 5, child: Image.network(imageSrc)),
Expanded(
flex: 2,
child: Row(
children: [
ElevatedButton(
// Download displayed image from imageSrc
onPressed: () {
downloadFile().catchError((onError) {
debugPrint('Error downloading: $onError');
}).then((imagePath) {
debugPrint('Download successful, path: $imagePath');
displayDownloadImage(imagePath);
});
},
child: Text('Download'),
),
ElevatedButton(
// Delete downloaded image
onPressed: () {
deleteFile().catchError((onError) {
debugPrint('Error deleting: $onError');
}).then((value) {
debugPrint('Delete successful');
});
},
child: Text('Clear'),
)
],
),
),
LinearProgressIndicator(
value: downloadProgress,
),
Expanded(
flex: 5,
child: downloadPath == ''
// Display a different image while downloadPath is empty
// downloadPath will contain an image file path on successful image download
? Icon(Icons.image)
: Image.file(File(downloadPath))),
],
),
),
);
}
displayDownloadImage(String path) {
setState(() {
downloadPath = path;
});
}
Future deleteFile() async {
final dir = await getApplicationDocumentsDirectory();
var file = File('${dir.path}/image.jpg');
await file.delete();
setState(() {
// Clear downloadPath on file deletion
downloadPath = '';
});
}
Future downloadFile() async {
Dio dio = Dio();
var dir = await getApplicationDocumentsDirectory();
var imageDownloadPath = '${dir.path}/image.jpg';
await dio.download(imageSrc, imageDownloadPath,
onReceiveProgress: (received, total) {
var progress = (received / total) * 100;
debugPrint('Rec: $received , Total: $total, $progress%');
setState(() {
downloadProgress = received.toDouble() / total.toDouble();
});
});
// downloadFile function returns path where image has been downloaded
return imageDownloadPath;
}
}
In the sample app, clicking the 'Download' button will have the network image displayed at the top portion of the screen downloaded. After the download is successful, the downloaded image will be displayed using Image.file() at the lower portion of the screen.