Skip to content

Commit 75f6c0b

Browse files
authored
Merge pull request #1824 from Decodetalkers/xdppicker
feat(linux): use xdg desktop portal instead
2 parents 6edfc00 + d600672 commit 75f6c0b

14 files changed

+443
-839
lines changed

CHANGELOG.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
## 10.3.0
2+
### Desktop
3+
- Support xdg desktop portal filechooser on linux. [#1827](https://github.com/miguelpruivo/flutter_file_picker/issues/1827) [@Decodetalkers](https://github.com/Decodetalkers)
4+
5+
### Android
6+
- Fixed an issue where CSV files were not properly filtered during file selection. [#1849](https://github.com/miguelpruivo/flutter_file_picker/pull/1849) [@SoftWyer](https://github.com/SoftWyer)
7+
18
## 10.2.4
29
### Android
3-
- Fixed an issue where custom MIME types were failing to load picking files on Chromebook. [#1858](https://github.com/miguelpruivo/flutter_file_picker/issues/1858)
10+
- Fixed an issue where custom MIME types were failing to load picking files on Chromebook. [#1858](https://github.com/miguelpruivo/flutter_file_picker/issues/1858) [@vicajilau](https://github.com/vicajilau)
411

512
## 10.2.3
613
### General
7-
- Fixed build failures on Flutter 3.24 caused by changes to address Win32 deprecation warnings on Windows. [#1855](https://github.com/miguelpruivo/flutter_file_picker/issues/1855)
14+
- Fixed build failures on Flutter 3.24 caused by changes to address Win32 deprecation warnings on Windows. [#1855](https://github.com/miguelpruivo/flutter_file_picker/issues/1855) [@vicajilau](https://github.com/vicajilau)
815

916
## 10.2.2
1017
### Android
11-
- Fixed an issue where some files would incorrectly append a `.txt` extension when saving files on Android.
18+
- Fixed an issue where some files would incorrectly append a `.txt` extension when saving files on Android. [@vicajilau](https://github.com/vicajilau)
1219

1320
## 10.2.1
1421
### Android

lib/src/linux/dialog_handler.dart

Lines changed: 0 additions & 52 deletions
This file was deleted.

lib/src/linux/file_picker_linux.dart

Lines changed: 127 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,28 @@ import 'dart:typed_data';
33

44
import 'package:file_picker/src/file_picker.dart';
55
import 'package:file_picker/src/file_picker_result.dart';
6-
import 'package:file_picker/src/linux/dialog_handler.dart';
76
import 'package:file_picker/src/platform_file.dart';
87
import 'package:file_picker/src/utils.dart';
8+
import 'package:file_picker/src/linux/xdp_filechooser.dart';
9+
import 'package:file_picker/src/linux/xdp_request.dart';
10+
import 'package:file_picker/src/linux/filters.dart';
11+
import 'package:dbus/dbus.dart';
912

1013
class FilePickerLinux extends FilePicker {
1114
static void registerWith() {
1215
FilePicker.platform = FilePickerLinux();
1316
}
1417

18+
final destination = "org.freedesktop.portal.Desktop";
19+
late final DBusClient _client;
20+
late final OrgFreedesktopPortalFileChooser _xdpChooser;
21+
22+
FilePickerLinux() : super() {
23+
_client = DBusClient.session();
24+
_xdpChooser = OrgFreedesktopPortalFileChooser(_client, destination,
25+
path: DBusObjectPath("/org/freedesktop/portal/desktop"));
26+
}
27+
1528
@override
1629
Future<FilePickerResult?> pickFiles({
1730
String? dialogTitle,
@@ -29,33 +42,46 @@ class FilePickerLinux extends FilePicker {
2942
bool readSequential = false,
3043
int compressionQuality = 0,
3144
}) async {
32-
final String executable = await _getPathToExecutable();
33-
final dialogHandler = DialogHandler(executable);
45+
final filter = Filter(type, allowedExtensions);
46+
Map<String, DBusValue> xdpOption = {
47+
'handle_token': DBusString('flutter_picker'),
48+
'multiple': DBusBoolean(allowMultiple),
49+
'modal': DBusBoolean(lockParentWindow),
50+
'filters': filter.toDBusArray(),
51+
};
52+
if (initialDirectory != null) {
53+
List<int> tmp = [];
54+
for (var i = 0; i < initialDirectory.length; i++) {
55+
tmp.add(initialDirectory[i].codeUnitAt(0));
56+
}
57+
DBusArray directory = DBusArray.byte(tmp);
58+
xdpOption["current_folder"] = directory;
59+
}
60+
final replyPath = await _xdpChooser.callOpenFile(
61+
"", dialogTitle ?? "flutter picker", xdpOption);
3462

35-
final String fileFilter = dialogHandler.fileTypeToFileFilter(
36-
type,
37-
allowedExtensions,
38-
);
63+
List<Uri> uriPaths = [];
3964

40-
final List<String> arguments = dialogHandler.generateCommandLineArguments(
41-
dialogTitle ?? defaultDialogTitle,
42-
fileFilter: fileFilter,
43-
initialDirectory: initialDirectory ?? '',
44-
multipleFiles: allowMultiple,
45-
pickDirectory: false,
46-
);
65+
final request =
66+
OrgFreedesktopPortalRequest(_client, destination, path: replyPath);
4767

48-
final String? fileSelectionResult = await runExecutableWithArguments(
49-
executable,
50-
arguments,
51-
);
52-
if (fileSelectionResult == null) {
53-
return null;
68+
await for (var response in request.response) {
69+
final status = response.response;
70+
// Maybe cancelled
71+
if (status != 0) {
72+
return null;
73+
}
74+
final result = response.results;
75+
uriPaths = result["uris"]
76+
?.asArray()
77+
.map((data) => Uri.parse(data.asString()))
78+
.toList() ??
79+
[];
80+
break;
5481
}
5582

56-
final List<String> filePaths = dialogHandler.resultStringToFilePaths(
57-
fileSelectionResult,
58-
);
83+
final filePaths = uriPaths.map((uri) => uri.toFilePath()).toList();
84+
5985
final List<PlatformFile> platformFiles = await filePathsToPlatformFiles(
6086
filePaths,
6187
withReadStream,
@@ -71,14 +97,51 @@ class FilePickerLinux extends FilePicker {
7197
bool lockParentWindow = false,
7298
String? initialDirectory,
7399
}) async {
74-
final executable = await _getPathToExecutable();
75-
final List<String> arguments =
76-
DialogHandler(executable).generateCommandLineArguments(
77-
dialogTitle ?? defaultDialogTitle,
78-
initialDirectory: initialDirectory ?? '',
79-
pickDirectory: true,
100+
Map<String, DBusValue> xdpOption = {
101+
'handle_token': DBusString('flutter_picker'),
102+
'directory': DBusBoolean(true),
103+
'modal': DBusBoolean(lockParentWindow),
104+
};
105+
if (initialDirectory != null) {
106+
List<int> tmp = [];
107+
for (var i = 0; i < initialDirectory.length; i++) {
108+
tmp.add(initialDirectory[i].codeUnitAt(0));
109+
}
110+
DBusArray directory = DBusArray.byte(tmp);
111+
xdpOption["current_folder"] = directory;
112+
}
113+
final replyPath = await _xdpChooser.callOpenFile(
114+
"", dialogTitle ?? "flutter picker", xdpOption);
115+
116+
List<Uri> uriPaths = [];
117+
118+
final request =
119+
OrgFreedesktopPortalRequest(_client, destination, path: replyPath);
120+
121+
await for (var response in request.response) {
122+
final status = response.response;
123+
// Maybe cancelled
124+
if (status != 0) {
125+
return null;
126+
}
127+
final result = response.results;
128+
uriPaths = result["uris"]
129+
?.asArray()
130+
.map((data) => Uri.parse(data.asString()))
131+
.toList() ??
132+
[];
133+
break;
134+
}
135+
136+
final filePaths = uriPaths.map((uri) => uri.toFilePath()).toList();
137+
138+
final List<PlatformFile> platformFiles = await filePathsToPlatformFiles(
139+
filePaths,
140+
false,
141+
false,
80142
);
81-
return await runExecutableWithArguments(executable, arguments);
143+
144+
return platformFiles.firstOrNull?.path;
82145
}
83146

84147
@override
@@ -91,46 +154,43 @@ class FilePickerLinux extends FilePicker {
91154
Uint8List? bytes,
92155
bool lockParentWindow = false,
93156
}) async {
94-
final executable = await _getPathToExecutable();
95-
final dialogHandler = DialogHandler(executable);
96-
97-
final String fileFilter = dialogHandler.fileTypeToFileFilter(
98-
type,
99-
allowedExtensions,
100-
);
101-
102-
final List<String> arguments = dialogHandler.generateCommandLineArguments(
103-
dialogTitle ?? defaultDialogTitle,
104-
fileFilter: fileFilter,
105-
fileName: fileName ?? '',
106-
initialDirectory: initialDirectory ?? '',
107-
saveFile: true,
108-
);
157+
Map<String, DBusValue> xdpOption = {
158+
'handle_token': DBusString('flutter_picker'),
159+
'current_name': DBusString(fileName ?? ''),
160+
'modal': DBusBoolean(lockParentWindow),
161+
};
162+
if (initialDirectory != null) {
163+
List<int> tmp = [];
164+
for (var i = 0; i < initialDirectory.length; i++) {
165+
tmp.add(initialDirectory[i].codeUnitAt(0));
166+
}
167+
DBusArray directory = DBusArray.byte(tmp);
168+
xdpOption["current_folder"] = directory;
169+
}
109170

110-
final savedFilePath =
111-
await runExecutableWithArguments(executable, arguments);
112-
await saveBytesToFile(bytes, savedFilePath);
113-
return savedFilePath;
114-
}
171+
final replyPath = await _xdpChooser.callSaveFile(
172+
"", dialogTitle ?? "flutter picker", xdpOption);
173+
final request =
174+
OrgFreedesktopPortalRequest(_client, destination, path: replyPath);
115175

116-
/// Returns the path to the executables `qarma`, `zenity` or `kdialog` as a
117-
/// [String].
118-
/// On Linux, the CLI tools `qarma` or `zenity` can be used to open a native
119-
/// file picker dialog. It seems as if all Linux distributions have at least
120-
/// one of these two tools pre-installed (on Ubuntu `zenity` is pre-installed).
121-
/// On distribuitions that use KDE Plasma as their Desktop Environment,
122-
/// `kdialog` is used to achieve these functionalities.
123-
/// The future returns an error, if none of the executables was found on
124-
/// the path.
125-
Future<String> _getPathToExecutable() async {
126-
try {
127-
try {
128-
return await isExecutableOnPath('qarma');
129-
} on Exception {
130-
return await isExecutableOnPath('kdialog');
176+
List<Uri> saveUris = [];
177+
await for (var response in request.response) {
178+
final status = response.response;
179+
// Maybe cancelled
180+
if (status != 0) {
181+
return null;
131182
}
132-
} on Exception {
133-
return await isExecutableOnPath('zenity');
183+
final result = response.results;
184+
saveUris = result["uris"]
185+
?.asArray()
186+
.map((data) => Uri.parse(data.asString()))
187+
.toList() ??
188+
saveUris;
189+
break;
134190
}
191+
192+
final savedFilePaths = saveUris.map((uri) => uri.toFilePath()).toList();
193+
194+
return savedFilePaths.firstOrNull;
135195
}
136196
}

0 commit comments

Comments
 (0)