How to add a File Picker plugin in Flutter? - dart

I am creating a Flutter project in which, I have a piece of data (JSON) that I want to Import from and Export to a location the user wants to. In order to achieve this, I require a File Picker plugin in Flutter. Now, I searched the Dart Packages repository for "file picker" but didn't find one.
Is there a way to get a File Picker that looks like this:
or even this...
The first screenshot is preferable for me as it allows file selection from different sources (like Drive).
Also, since I want to Export the data, I might want a Folder Picker too. ;)
But, if there is any other alternative to Folder Picker. I'd be happy to know...

I've created a file_picker plugin some time ago in order to make it possible to pick (both on iOS and Android) absolute paths and then loaded it with Flutter.
You can check it here: https://pub.dev/packages/file_picker

I used file_picker library to pick files. you can use this for pick images as well.
Future getPdfAndUpload(int position) async {
File file = await FilePicker.getFile(
type: FileType.custom,
allowedExtensions: ['pdf','docx'], //here you can add any of extention what you need to pick
);
if(file != null) {
setState(() {
file1 = file; //file1 is a global variable which i created
});
}
}
here file_picker flutter library.

I'm in the exact same boat as you, haha!
I noticed documents_picker 0.0.2. It allows the user to pick multiple files, and it seems to fit the need!
check it out: https://pub.dartlang.org/packages/documents_picker#-readme-tab-

Here's a better document picker. It looks like the native document picker from the Storage Access Framework, which is what you have in your picture.
flutter_document_picker

Just found the FileSelector plugin from flutter.dev. Compatible with MacOS, Windows and Web.
From its pub.dev page:
Open a single file
final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']);
final file = await openFile(acceptedTypeGroups: [typeGroup]);
Open multiple files at once
final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']);
final files = await openFiles(acceptedTypeGroups: [typeGroup]);
Saving a file
final path = await getSavePath();
final name = "hello_file_selector.txt";
final data = Uint8List.fromList("Hello World!".codeUnits);
final mimeType = "text/plain";
final file = XFile.fromData(data, name: name, mimeType: mimeType);
await file.saveTo(path);
MacOS: Provide file read or/and write privileges
On target MacOS please provide sufficient rights using Xcode:
In case you don't provide file read or/and write permissions, the call to
final XFile? file =
await openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
neither shows anything not returns.

Related

How to get the current path of an electron js portable app?

Hello fellow developers/programmers. Today I come to ask for help with something, and is that I'm making an app that if or if it has to be portable, the problem I'm having is that when writing a zip file the app does not find the directories or generates the zip corruptly.
My code that writes the zip file looks like this:
ipcMain.on("report-file", async (event, data) => {
let zipFilePath = "report.zip"
let output = fs.createWriteStream(zipFilePath);
let archive = archiver("zip")
archive.pipe(output);
if (data.saves) {
archive.directory(`game/saves`, false)
archive.directory(`game/cache`, false)
}
archive.append(fs.createReadStream(`log.txt`), {
name: `log.txt`,
})
archive.append(fs.createReadStream(`traceback.txt`), {
name: `traceback.txt`,
})
archive.finalize()
});
Electron Version: 21.2.3
SO: Windows 11
electron-builder Version: 23.6.0
Looking a little more in depth, I found that the app is using the address
C:\User\User\AppData\Local\Temp\[id]\resources\app.asar
and it is trying to compress the files as if they were in that path, however, the files to compress are located where the .exe is. (G:\myapp\dist)
I already tried to fix it by following an old post on the stackoverflow page, however, it didn't solve the problem, so today I decided to ask my question here, do you have any idea how to get the actual path of an electron js portable app?
I found the solution to this, and it was to create a dialog with electron, and load the folder location. Then use resolve together with the address of the executable, this way it loads correctly the address of the folder where the exe is located.
let dir
dir = await dialog.showOpenDialog(win, {
properties: ['openDirectory'],
defaultPath: path.resolve('.', process.env.PORTABLE_EXECUTABLE_DIR)
});

How to read config file in electronjs app

It's my first time using Electron JS and nodejs. I've built a small app that reads some records from a database and updates them. Everything is working fine. I have a config file with the database credentials but when I build a portable win app, I cannot figure out how to read the config file that I would like to place next to the exe. I would like to have easy access to the file, so I could run the same app on different databases.
Can anyone tell me if what I want is possible and how? I already tried to get the exe location but I couldn't. I also read a lot of topics here but nothing seems to solve my problem (I might be doing something wrong).
I'm using electron-builder to build my app.
Thanks in advance.
Edit #1
My Config file is
{
"user" :"X",
"password" :"X",
"server":"X",
"database":"X",
"options":
{
"trustedconnection": true,
"enableArithAbort" : true,
"trustServerCertificate": true
}
}
This is what I've and works when I run the project with npm start
const configRootPath = path.resolve(__dirname,'dbConfig.json');
dbConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' }));
However, when I build it, the app is looking for the file in another location different from the one where the executable is.
Use of Electron's app.getPath(name) function will get you the path(s) you are after, irrespective of which OS (Operating System) you are using.
Unless your application writes your dbConfig.json file, it may be difficult for your user to understand exactly where they should place their database config file as each OS will run and store your application data in a different directory. You would need to be explicit to the user as to where to place their config file(s). Alternatively, your application could create the config file(s) on the user's behalf (automatically or through a html form) and save it to a location 'known' to the application.
A common place where application specific config files are stored is in the user's application data directory. With the application name automatically amended to the directory, it can be found as shown below.
const electronApp = require('electron').app;
let appUserDataPath = electronApp.getPath('userData');
console.log(appUserDataPath );
In your use case, the below would apply.
const electronApp = require('electron').app;
const nodeFs = require('fs');
const nodePath = require('path');
const configRootPath = nodePath.join(electronApp.getPath('userData'), 'dbConfig.json');
dbConfig = JSON.parse(nodeFs.readFileSync(configRootPath, 'utf-8'));
console.log(configRootPath);
console.log(dbConfig);
You can try electron-store to store config.
Electron doesn't have a built-in way to persist user preferences and other data. This module handles that for you, so you can focus on building your app. The data is saved in a JSON file named config.json in app.getPath('userData').

Copy a DriveItem but overwrite

I want to copy and overwrite the existing file but cannot do it without getting the error nameAlreadyExists .
As a workaround I seem to have to download it into a stream and then reupload it.
The code is as per the API
await graphClient.Sites.Root.Drives[documentLibraryId].Items[fileId]
.Copy(newFileName, parentReference)
.Request()
.PostAsync();
As a workaround I may have to download the file into a strea and then upload it like as per the following which seems to work
await RetryWithExponentialBackoff.RunAsync(async () =>
{
uploadSession = await graphClient.Drives[driveId]
.Root
.ItemWithPath(filePath)
.CreateUploadSession()
.Request()
.PostAsync();
});
Usually you just need to set the microsoft.graph.conflictBehavior to replace. This works fine with functions such as moving. However, the Api ignores the specified conflict behavior with the copy function. More information on the bug here: link to github issue. It does not seem to be a priority for Microsoft, since the bug was reported over a year ago and nothing happened since. So you will most likely need a workaround.
Another idea for a workaround:
Copy the DriveItem to the target document library or folder, but use for example a combination of the current date and the drive item id instead of the desired file name newFileName to make sure, that you not get a conflict with any existing file.
Change the copied DriveItem's name to the desired file name newFileName, e. g.:
PATCH https://graph.microsoft.com/v1.0/sites/siteId/drives/driveId/items/itemId
{
"name": "newFileName",
"#microsoft.graph.conflictBehavior":"replace"
}
In my tests, this replaced the existing file with the name newFileName with the copied file.
With this workaround, you do not need to download and reupload the file. Thus, the performance should be better. However, the user may wonder about weird file names.

How to read and write a text file in Flutter

How do you read text from a file and write text to a file?
I've been learning about how to read and write text to and from a file. I found another question about reading from assets, but that is not the same. I will add my answer below from what I learned from the documentation.
Setup
Add the following plugin in pubspec.yaml:
dependencies:
path_provider: ^1.6.27
Update the version number to whatever is current.
And import it in your code.
import 'package:path_provider/path_provider.dart';
You also have to import dart:io to use the File class.
import 'dart:io';
Writing to a text file
_write(String text) async {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.txt');
await file.writeAsString(text);
}
Reading from a text file
Future<String> _read() async {
String text;
try {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.txt');
text = await file.readAsString();
} catch (e) {
print("Couldn't read file");
}
return text;
}
Notes
You can also get the path string with join(directory.path, 'my_file.txt') but you need to import 'package:path/path.dart'.
Flutter's Official Documentation of Reading and Writing Files
This works for iOS, Android, Linux and MacOS but not for web.
As additional info to #Suragch's answer, if you want to find the file you created, you can do as the images show:
And then inside that data folder, go again to a folder named data and search for your package, and then go to:
If you happen to create new files, in order to be able to see them, just right click and click Synchronize.
An another way to pull the file from the device is by using adb pull command. You can find the file path by debugging the code and then use adb pull command. adb is located in Android SDK -> platform-tools directory.
./adb pull /storage/emulated/0/Android/data/com.innovate.storage.storage_sample/files/sample.txt ~/Downloads
#Suragch 's answer is right. Except the version of path_provider that you want to use now is:
path_provider: ^2.0.9

How to get the (absolute) path to the Download folder?

In a Flutter project, how to get the (absolute) path to the Download folder in my Android device?
My Download folder is next those one: Alarms, Android, data, DCIM, Documents, Movies, Music, Notifications, Pictures, ...
Device: GALAXY S8+ SM-G955F. Android 8.0. Not Rooted. Flutter beta v0.5.1. Dart 2.0.0-dev.58.0. Windows 10
File manager showing my Download folder
Using this package path_provider I got those 3 paths:
/data/user/0/com.exemple.fonzo/cache
/data/user/0/com.exemple.fonzo/app_flutter
/storage/emulated/0
I cannot find or access those 3 folders from Solid-Explorer file manager on my un-rooted device GALAXY S8+ SM-G955F. Android 8.0. I just want to find the absolute path to a folder (like Download) that:
I can access with my Android file manager app.
I can write files in this folder from my flutter project.
I personaly use this method :
path_provider: 2.0.9
Future<String?> getDownloadPath() async {
Directory? directory;
try {
if (Platform.isIOS) {
directory = await getApplicationDocumentsDirectory();
} else {
directory = Directory('/storage/emulated/0/Download');
// Put file in global download folder, if for an unknown reason it didn't exist, we fallback
// ignore: avoid_slow_async_io
if (!await directory.exists()) directory = await getExternalStorageDirectory();
}
} catch (err, stack) {
print("Cannot get download folder path");
}
return directory?.path;
}
Output :
On IOS it create a folder with the name of your app and put the file inside it. Users can easily find the folder, it's intuitive.
On Android, I test if the default download folder exist (it work most of the time), else I put inside the external storage directory (in this case, it's hard for the user to manually find the file...).
For IOS : (Mentioned by #Wai Yan)
Maybe add the follwing key in your plist.info to better see your app in document folder (https://stackoverflow.com/a/74457977/10088439).
UISupportsDocumentBrowser
I used this library to get public download directory in Android
ext_storage
import 'package:ext_storage/ext_storage.dart';
Future<String> _getPathToDownload() async {
return ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
}
final String path = await _getPathToDownload();
print(path);
You could use the downloads_path_provider package. You will have add <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> to your AndroidManifest.xml. Also if you plan to write into that folder and want your application to work for android version > 6 you must ask the user for writing permission. You could do that with https://pub.dev/packages/permission_handler.
await PermissionHandler().requestPermissions([PermissionGroup.storage]);
Change the function getApplicationDocumentsDirectory() to getExternalStorageDirectory() it should show the external directory for the app.
You should use native feature.
At time, to access phone directory is provided by path_provider package .
With it, you can acceess: temporary directory, app directory, external storage.
Doc: https://pub.dartlang.org/packages/path_provider
In Flutter
https://pub.dev/packages/ext_storage
Installation
Add ext_storage as a dipendency in your project pubspeck.yaml.
dependencies:
ext_storage:
Usage
First, you write import ext_storage package.
import 'package:ext_storage/ext_storage.dart';
void dirpath() async {
var path = await ExtStorage.getExternalStoragePublicDirectory(ExtStorage.DIRECTORY_DOWNLOADS);
}
I simplified it without plugins
I used this code and it's working like a charm
Directory dir = Directory('/storage/emulated/0/Download');
Using path provider package, you should be able to do this:
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
String? downloadDirectory;
if (Platform.isAndroid) {
final externalStorageFolder = await getExternalStorageDirectory();
if (externalStorageFolder != null) {
downloadDirectory = p.join(externalStorageFolder.path, "Downloads");
}
} else {
final downloadFolder = await getDownloadsDirectory();
if (downloadFolder != null) {
downloadDirectory = downloadFolder.path;
}
}

Resources