I am trying to download a file from the server using flutter. It was working perfectly but I uninstalled app from the android emulator and now it is not asking for the permission and generating the following error.
Unhandled Exception: FileSystemException: Cannot create file, path = '/storage/emulated/0/Download/Salary-Sheet.xlsx'
code
Future<bool> getStoragePermission() async {
return await Permission.storage.request().isGranted;
}
Future<String> getDownloadPath() async {
return await ExternalPath.getExternalStoragePublicDirectory(
ExternalPath.DIRECTORY_DOWNLOADS);
}
downloaddd(String downloadPath) async {
var path = '$downloadPath/Salary-Sheet.xlsx';
String url =
"http://salary/export/${widget.masterID}";
await dio.download(url, path, onReceiveProgress: (received, total) {
if (total != -1) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: const [
Icon(Icons.cloud_download),
SizedBox(
width: 10,
),
Text("Salary sheet has been downloaded !"),
],
),
),
);
//you can build progressbar feature too
}
});
}
doDownloadFile() async {
if (await getStoragePermission()) {
String downloadDirectory = await getDownloadPath();
await downloaddd(downloadDirectory);
}
}
Please check the emulator path again.
you also follow this question
Flutter - save file to download folder - downloads_path_provider
Related
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 created a Pdf viewer screen and I added a button which should download the PDF from the URL when it's pressed. I display the pdf from an URL, it is displayed correctly but the download function is not working. I tried different examples and implementations found on the internet but nothing worked.
Let me know if you have any solution for this. Thanks
This is my dart class:
class _PdfViewerScreenState extends State<PdfViewerScreen> {
late PdfViewerController _pdfViewerController;
String uri = 'https://file-examples.com/wp-content/uploads/2017/10/file-example_PDF_1MB.pdf';
String filename = 'test.pdf'; // file name that you desire to keep
// downloading logic is handled by this method
Future<void> downloadFile(uri, fileName) async {
String savePath = await getFilePath(fileName);
Dio dio = Dio();
dio.download(
uri,
savePath,
);
}
Future<String> getFilePath(uniqueFileName) async {
String path = '';
Directory dir = await getApplicationDocumentsDirectory();
path = '${dir.path}/$uniqueFileName.pdf';
return path;
}
#override
void initState() {
_pdfViewerController = PdfViewerController();
super.initState();
}
#override
Widget build(BuildContext context) {
var theme = HolidayApp.of(context).theme;
return Scaffold(
appBar: AppBar(
title: Text(
Constants.downloadRequest,
style: theme.textTheme.heading3.copyWith(
fontWeight: FontWeight.w400,
),
),
actions: [_downloadButton(downloadFile(uri, filename))]),
body: SafeArea(
child: SfPdfViewer.network(
widget.pdfUrl,
controller: _pdfViewerController,
),
),
);
}
}
IconButton _downloadButton(Future f) {
return IconButton(
icon: const Icon(
Icons.download,
),
onPressed: () {
f;
},
);
}
I have created a simple note app using Hive, the app works perfectly on the emulator but after building the app with flutter build apk --release and installing it on my android device I face this problem :
if I choose an image from the gallery everything works normally ✔️
When I try to pick an image from the camera then it doesn't work ✖️
After trying to access the camera then also accessing the gallery stop working ✖️.
This is a screenshot from my mobile : screenshot_from_app
this is the code where I'm accessing the camera and gallery :
//Camera Functions
//Phot from Camera
getImageCamera() async {
final pickedimage =
await ImagePicker.platform.getImage(source: ImageSource.camera);
if (pickedimage != null) {
setState(() {
_image = File(pickedimage.path);
});
Navigator.of(context).pop();
}
}
//Photo From the gallery
getImageGallery() async {
final pickedimage =
await ImagePicker.platform.getImage(source: ImageSource.gallery);
if (pickedimage != null) {
setState(() {
_image = File(pickedimage.path);
});
Navigator.of(context).pop();
}
}
//AlertDialog Appear when pressing add phot buttom
showBottomSheet(context) {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Choose photo from",
style: TextStyle(fontWeight: FontWeight.bold),
),
actions: [
IconButton(
onPressed: () async {
await getImageCamera();
},
icon: const Icon(Icons.photo_camera),
),
IconButton(
onPressed: () async {
await getImageGallery();
},
icon: const Icon(Icons.image),
)
],
);
},
);
}
//End Camera Function
it seems you didn't add permissions read about this package and
Add this to pubspec.yaml File:
dependencies:
permission_handler: ^9.1.0
i fixed my problem by adding this permission
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
I am creating a dynamic link to navigate me to a specified page, when trying it on emulator it work fine but when trying it on actual device the link only open the app
Creating the dynamic link
Future<void> createDynamicLink(bool short, int lecID, String lecName) async{
setState(() {
_isCreatingLink = true;
});
final DynamicLinkParameters parameters = DynamicLinkParameters(
uriPrefix: 'https://geneapp.page.link',
link: Uri.parse('https://www.geneapp.com/lecture?lecID=$lecID&lecName=$lecName'),
androidParameters: AndroidParameters(packageName: 'com.example.teams',minimumVersion: 0),
dynamicLinkParametersOptions: DynamicLinkParametersOptions(
shortDynamicLinkPathLength: ShortDynamicLinkPathLength.short
),
socialMetaTagParameters: SocialMetaTagParameters(
title: "$lecName",
description: "new"
)
);
print("https://geneapp.page.link/lecture?lecID=$lecID&lecName=$lecName");
Uri url;
if (short) {
final ShortDynamicLink shortLink = await parameters.buildShortLink();
url = shortLink.shortUrl;
} else {
url = await parameters.buildUrl();
}
setState(() {
_linkMessage = url.toString();
_isCreatingLink = false;
});
print(url);
Clipboard.setData(new ClipboardData(text: '$url'));
FocusScope.of(context).requestFocus(new FocusNode());
scaffoldKey.currentState?.removeCurrentSnackBar();
scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("copied",style: TextStyle(fontSize: 16)),
Padding(
padding: EdgeInsets.fromLTRB(10.0,0.0,0.0,0.0),
child: Icon(Icons.check_circle,color: Colors.grey,),
)
],
),
backgroundColor: Colors.green,
duration: Duration(seconds: 3),
));
}
Handling the deep link:
void initDynamicLinks() async{
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
var isLec = deepLink.pathSegments.contains('lecture');
if(isLec){
var lecName = deepLink.queryParameters['lecName'];
var lecID = int.parse(deepLink.queryParameters['lecID']);
print("initDynamicLinks | lecName : $lecName lecID : $lecID");
await _openPDF(lecName, lecID);
}
}
else
print('its null');
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deeplink = data?.link;
if(deeplink != null){
var lecName = deeplink.queryParameters['lecName'];
var lecID = int.parse(deeplink.queryParameters['lecID']);
print("initDynamicLinks | lecName : $lecName lecID : $lecID");
await _openPDF(lecName, lecID);
}
}
_openPDF(String lecName, int lecID) async{
final filename = lecName;
String dir = (await getApplicationDocumentsDirectory()).path;
if (await File('$dir/$filename').exists()){
navigatorKey.currentState.pushNamed('/pdf', arguments: '$dir/$filename');
}
else{
//..
//
navigatorKey.currentState.pushNamed('/pdf', arguments: '$dir/$filename');
}
}
in my main build function I am setting initialRoute to a splash screen,
then in my splash screen initstate I am calling the initDynamicLinks():
There can be various reasons for app not launching, the android device your using, the browser used to launch the link (if a link is copy pasted in chrome they do not work), DynamicLinks setup.
You can try few things
encode your lecName variable in the Long URL.
Send the dynamic-short link over message and try to click and launch the app.
I'm working on a project, that requires me to download a pdf file from URL, once a button is tapped, and store it to phone storage (probably downloads folder).
Any ideas on how to do this? The file that is being downloaded is also not always the same and can be anything from an pdf to image.
You can use the Dio package to download files to your local storage using Dio().download
response = await dio.download("https://www.google.com/", "./xx.html");
Also you can check out this open source project as reference
I hope this would help you. Check if file is already present, if not then use the URL to fetch the file and save it in application directory.
Future<File> createFile() async {
try {
/// setting filename
final filename = widget.docPath;
/// getting application doc directory's path in dir variable
String dir = (await getApplicationDocumentsDirectory()).path;
/// if `filename` File exists in local system then return that file.
/// This is the fastest among all.
if (await File('$dir/$filename').exists()) return File('$dir/$filename');
///if file not present in local system then fetch it from server
String url = widget.documentUrl;
/// requesting http to get url
var request = await HttpClient().getUrl(Uri.parse(url));
/// closing request and getting response
var response = await request.close();
/// getting response data in bytes
var bytes = await consolidateHttpClientResponseBytes(response);
/// generating a local system file with name as 'filename' and path as '$dir/$filename'
File file = new File('$dir/$filename');
/// writing bytes data of response in the file.
await file.writeAsBytes(bytes);
/// returning file.
return file;
}
/// on catching Exception return null
catch (err) {
errorMessage = "Error";
print(errorMessage);
print(err);
return null;
}
}
The below code works on both iOS and Android. Replace ".pdf" with ".jpg" if you are looking for downloading an image.
In pubspec.yaml, paste the below code under dependencies
dio: any
path_provider: any
file_utils: any
permission_handler: any
In android/app/src/main/AndroidManifest.xml paste the below lines outside the <application> tags
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Below is the code :
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:file_utils/file_utils.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:intl/intl.dart';
void main() => runApp(Downloader());
class Downloader extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
title: "File Downloader",
debugShowCheckedModeBanner: false,
home: FileDownloader(),
theme: ThemeData(primarySwatch: Colors.blue),
);
}
class FileDownloader extends StatefulWidget {
#override
_FileDownloaderState createState() => _FileDownloaderState();
}
class _FileDownloaderState extends State<FileDownloader> {
final pdfUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
bool downloading = false;
var progress = "";
var path = "No Data";
var platformVersion = "Unknown";
var _onPressed;
Directory externalDir;
#override
void initState() {
super.initState();
downloadFile();
}
String convertCurrentDateTimeToString() {
String formattedDateTime =
DateFormat('yyyyMMdd_kkmmss').format(DateTime.now()).toString();
return formattedDateTime;
}
Future<void> downloadFile() async {
Dio dio = Dio();
final status = await Permission.storage.request();
if (status.isGranted) {
String dirloc = "";
if (Platform.isAndroid) {
dirloc = "/sdcard/download/";
} else {
dirloc = (await getApplicationDocumentsDirectory()).path;
}
try {
FileUtils.mkdir([dirloc]);
await dio.download(pdfUrl, dirloc + convertCurrentDateTimeToString() + ".pdf",
onReceiveProgress: (receivedBytes, totalBytes) {
print('here 1');
setState(() {
downloading = true;
progress = ((receivedBytes / totalBytes) * 100).toStringAsFixed(0) + "%";
print(progress);
});
print('here 2');
});
} catch (e) {
print('catch catch catch');
print(e);
}
setState(() {
downloading = false;
progress = "Download Completed.";
path = dirloc + convertCurrentDateTimeToString() + ".pdf";
});
print(path);
print('here give alert-->completed');
} else {
setState(() {
progress = "Permission Denied!";
_onPressed = () {
downloadFile();
};
});
}
}
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text('File Downloader'),
),
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: 10.0,
),
Text(
'Downloading File: $progress',
style: TextStyle(color: Colors.white),
),
],
),
),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(path),
MaterialButton(
child: Text('Request Permission Again.'),
onPressed: _onPressed,
disabledColor: Colors.blueGrey,
color: Colors.pink,
textColor: Colors.white,
height: 40.0,
minWidth: 100.0,
),
],
)));
}