I have been trying to create a backup of Sqflite databases in Flutter. Even after giving permissions, I continue to get the following error message:
E/flutter (23753): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: FileSystemException: Cannot copy file to '/data/user/0/com.maximuzindia.myapp/files/DBBackup.db', path = '/data/user/0/com.maximuzindia.myapp/databases/Database.db' (OS Error: No such file or directory, errno = 2)
I understand that with Android 11, there is a scoped storage permissions and hence have decided to save the file to the app directory itself. However, since it is a backup of the database, I would strongly prefer to store it in an external storage area. But in both the cases, I get the same error.
Below is my code to backup the database:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:maamaka/controllers/loading_controller.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sqflite/sqflite.dart';
class BackUpDatabase extends StatefulWidget {
const BackUpDatabase({Key? key}) : super(key: key);
#override
State<BackUpDatabase> createState() => _BackUpDatabaseState();
}
class _BackUpDatabaseState extends State<BackUpDatabase> {
String message = '';
#override
void initState() {
super.initState();
//loadingController.backupDatabases();
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(children: [
Text(message),
ElevatedButton(
onPressed: () async {
final dbFolder = await getDatabasesPath();
File source1 = File('$dbFolder/Database.db');
//Option 1 : To store it in external storage area.
/* Directory copyTo = Directory(
"storage/emulated/0/Android/data/com.maximuzindia.myapp/files/Download"); */
//Option 2 : To store it in the app directory in a new folder called files.
Directory copyTo =
Directory("/data/user/0/com.maximuzindia.myapp/files");
if ((await copyTo.exists())) {
print("Path exist");
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if (await Permission.storage.request().isGranted) {
print('Permission granted');
}
if (await Permission.manageExternalStorage.request().isGranted) {
print('Permission for External Storage Granted');
}
} else {
print("not exist");
if (await Permission.storage.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
await copyTo.create();
} else if (await Permission.manageExternalStorage.request().isGranted) {
await copyTo.create();
print('Permission for External Storage Granted');
}
else {
print('Please give permission');
}
}
String newPath = "${copyTo.path}/DBBackup.db";
print(copyTo.path.toString());
await source1.copy(newPath);
setState(() {
message = 'Successfully Copied DB';
});
},
child: const Text('Copy DB'),
),
]),
);
}
}
My Android Manifest has the following:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
My build.gradle has : compileSdkVersion 31
Would appreciate a solution for both the options ie., store in external storage or store in the app folder.
Related
I am trying to access storage on an Android emulator from Flutter.
Here is the code that I'm testing it with:
void test() async {
PermissionStatus storageStatus = await Permission.storage.request();
if (storageStatus == PermissionStatus.granted) {
print("granted");
}
if (storageStatus == PermissionStatus.denied) {
print("denied");
}
if (storageStatus == PermissionStatus.permanentlyDenied) {
openAppSettings();
}
}
I have included the "android.permission.WRITE_EXTERNAL_STORAGE" permission in the AndroidManifest.xml.
I have tried giving permissions for Camera and it worked, but it doesn't work for storage.
When I check for the storages permission status it is permanently denied, and when app's settings open it says "No permissions requested".
I have tried running "flutter clean" which seems to be necessary when updating the Manifest.
I have tried uninstalling from the phone and reinstalling the app
How do I fix it?
once try to do this.
Add in your manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
for requesting permission.
Future<bool> requestPermission(Permission permission) async {
if (await permission.isGranted) {
return true;
} else {
var result = await permission.request();
if (result == PermissionStatus.granted) {
return true;
}
}
return false;
}
for checking permission also you can change dialog text as per your need
Future<bool> checkPermission() async {
///For Check permission..
if (Platform.isAndroid ? !await requestPermission(Permission.storage) && !await requestPermission(Permission.manageExternalStorage) : !await requestPermission(Permission.storage)) {
await Get.dialog(CupertinoAlertDialog(
title: const Text("Photos & Videos permission"),
content: const Text(" Photos & Videos permission should be granted to connect with device, would you like to go to app settings to give Bluetooth & Location permissions?"),
actions: <Widget>[
TextButton(
child: const Text('No thanks'),
onPressed: () {
Get.back();
}),
TextButton(
child: const Text('Ok'),
onPressed: () async {
Get.back();
await openAppSettings();
})
],
));
return false;
} else {
return true;
}
}
Use like this
I hope these things solve your issue.
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
I am currently using the latest geolocator that is 'geolocator: ^9.0.1'and for some reason am not able to get any data from the position function when i run
bool isLoc = await Geolocator.isLocationServiceEnabled();
print(isLoc);
The result is true which i think means the location services are all enabled but when i run
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best);
print('position');
nothing is been returned i have tried all the accuracy values low, high, best,lowest, medium am really confused at what could be wrong i have also try it in the initState and also using a *button
Here is the full dart code
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class GeoLoc extends StatefulWidget {
const GeoLoc({Key? key}) : super(key: key);
#override
State<GeoLoc> createState() => _GeoLocState();
}
class _GeoLocState extends State<GeoLoc> {
#override
void initState() {
super.initState();
getLoc();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(onPressed: () {
getLoc();
},
child: const Text('Get Location'),
),
],
);
}
getLoc() async{
bool isLoc = await Geolocator.isLocationServiceEnabled();
print(isLoc);
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print(position);
}
}
Below is your code modified, pls try it. you forget to ask for geolocation permission
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class GeoLoc extends StatefulWidget {
const GeoLoc({Key? key}) : super(key: key);
#override
State<GeoLoc> createState() => _GeoLocState();
}
class _GeoLocState extends State<GeoLoc> {
#override
void initState() {
super.initState();
getLoc()
.then((value) => print("Location: " + value.toJson().toString()));
}
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(onPressed: () {
getLoc()
.then((value) => print("Location: " + value.toJson().toString()));
},
child: const Text('Get Location'),
),
],
);
}
getLoc() async{
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
Geolocator.openLocationSettings();
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
Geolocator.openAppSettings();
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
return await Geolocator.getCurrentPosition();
}
}
Also make sure you have below permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
currently, my apps have many assets (images, sound, font, json, SQL-lite database file, etc). All have defined in pubspec.yaml
However, due to a request to reduce APK size, I need some of them to be downloaded when Apps be launched and save it to storage, so no need to download it next time.
if assets are not yet ready, it should waiting a sec and show loading bar circle.
The question is how to do this thing, Any Example?
The easiest way to do it is to download your files as zip (archived file) and unpack them in the path of the application storage directory getApplicationDocumentsDirectory
You will use this list of packages:
archive ,
http and
path_provider
The pubspec.yaml will look like
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
path_provider: ^1.1.0
http: ^0.12.0+2
archive: ^2.0.8
dev_dependencies:
flutter_test:
sdk: flutter
uses-material-design: true
assets:
- assets/images/
The main.dart file which coronations your app will look like
Note that api is the URL of your file without the file name.
main.dart
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'data.dart';
const api =
'https://firebasestorage.googleapis.com/v0/b/playground-a753d.appspot.com/o';
enum AppTheme { candy, cocktail }
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(AppTheme.candy),
);
}
}
class MyHomePage extends StatefulWidget {
final AppTheme theme;
MyHomePage(this.theme);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
AppTheme _theme;
String _dir;
List<String> _images;
#override
void initState() {
super.initState();
_theme = widget.theme;
_images = data[_theme];
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.style),
onPressed: () async {
if (_theme == AppTheme.candy) {
await _downloadAssets('cocktail');
}
setState(() {
_theme =
_theme == AppTheme.candy ? AppTheme.cocktail : AppTheme.candy;
_images = data[_theme];
});
},
),
body: ListView.builder(
itemCount: _images.length,
itemBuilder: (BuildContext context, int index) {
return _getImage(_images[index], _dir);
}),
);
}
Widget _getImage(String name, String dir) {
if (_theme != AppTheme.candy) {
var file = _getLocalImageFile(name, dir);
return Image.file(file);
}
return Image.asset('assets/images/$name');
}
File _getLocalImageFile(String name, String dir) => File('$dir/$name');
Future<void> _downloadAssets(String name) async {
if (_dir == null) {
_dir = (await getApplicationDocumentsDirectory()).path;
}
if (!await _hasToDownloadAssets(name, _dir)) {
return;
}
var zippedFile = await _downloadFile(
'$api/$name.zip?alt=media&token=7442d067-a656-492f-9791-63e8fc082379',
'$name.zip',
_dir);
var bytes = zippedFile.readAsBytesSync();
var archive = ZipDecoder().decodeBytes(bytes);
for (var file in archive) {
var filename = '$_dir/${file.name}';
if (file.isFile) {
var outFile = File(filename);
outFile = await outFile.create(recursive: true);
await outFile.writeAsBytes(file.content);
}
}
}
Future<bool> _hasToDownloadAssets(String name, String dir) async {
var file = File('$dir/$name.zip');
return !(await file.exists());
}
Future<File> _downloadFile(String url, String filename, String dir) async {
var req = await http.Client().get(Uri.parse(url));
var file = File('$dir/$filename');
return file.writeAsBytes(req.bodyBytes);
}
}
Then you have to list all files and added them (logically to the corresponding theme)
data.dart:
import 'main.dart' show AppTheme;
const Map<AppTheme, List<String>> data = const {
AppTheme.candy: [
'art-background-blue-1289363.jpg',
'assortment-bright-candy-1043519.jpg',
'bright-candies-cherry-1405760.jpg',
'bright-candies-colorful-539447.jpg',
'bright-candy-chewy-1328885.jpg',
],
AppTheme.cocktail: [
'alcohol-alcoholic-beverage-beverage-1304540.jpg',
'alcohol-alcoholic-beverage-beverage-1723638.jpg',
'alcohol-black-background-close-up-800390.jpg',
'alcoholic-beverage-beverage-cocktail-970197.jpg',
'bar-beverage-blur-338713.jpg',
]
};
For more information check this Github project and Medium article
In android we use getFileDir() and getCacheDir() for accessing the Internal Storage. I can see that there's a path_provider plugin that I can use but I can only figure out getTemporaryDirectory() which is analogous to getCacheDir() of android. So is there any alternative way of doing what getFileDir() does in Android.
Is there any other way to do this that I'm aware of, or I'm missing something.
From Flutter sources:
/// Examples:
///
/// * iOS: `NSDocumentsDirectory`
/// * Android: The AppData directory.
static Future<Directory> getApplicationDocumentsDirectory() async {
return new Directory((await _pathProviderProxy.ptr.applicationDocumentsDirectory()).path);
To get internal storage directory path:
For Legacy Use these flutter plugin that provides external storage path and external public storage path,
ext_storage: ^latest_ver
String path = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
Details:https://pub.dev/packages/ext_storage
external_path:^latest_ver,
For NullSafety external_path is a flutter plugin that provides internal, external storage path and external public storage path,
String path=await ExternalPath.getExternalStoragePublicDirectory(
ExternalPath.DIRECTORY_DOWNLOADS);
Details:https://pub.dev/packages/external_path
Use this dart package: https://pub.dev/packages/path_provider_ex#-example-tab-
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:path_provider_ex/path_provider_ex.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<StorageInfo> _storageInfo = [];
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
List<StorageInfo> storageInfo;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
storageInfo = await PathProviderEx.getStorageInfo();
} on PlatformException {}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_storageInfo = storageInfo;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Internal Storage root:\n ${(_storageInfo.length > 0) ? _storageInfo[0].rootDir : "unavailable"}\n'),
Text(
'Internal Storage appFilesDir:\n ${(_storageInfo.length > 0) ? _storageInfo[0].appFilesDir : "unavailable"}\n'),
Text(
'Internal Storage AvailableGB:\n ${(_storageInfo.length > 0) ? _storageInfo[0].availableGB : "unavailable"}\n'),
Text(
'SD Card root: ${(_storageInfo.length > 1) ? _storageInfo[1].rootDir : "unavailable"}\n'),
Text(
'SD Card appFilesDir: ${(_storageInfo.length > 1) ? _storageInfo[1].appFilesDir : "unavailable"}\n'),
Text(
'SD Card AvailableGB:\n ${(_storageInfo.length > 1) ? _storageInfo[1].availableGB : "unavailable"}\n'),
],
) ,
),
);
}
}
Add path_provider to pubspec.yaml
import 'package:path_provider/path_provider.dart';
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
final directory = await _localPath;