Skip to content

Commit 7c97c88

Browse files
[file_selector] Convert Linux to Pigeon (#7770)
Replaces manual method channel code with Pigeon. I made a couple of simplifications to the Dart/C boundary while converting: - The return value is a always a list now; it's easier to handle that on the Dart side than track and handle in the native code. - It's a single method with a type enum, instead of several different methods, since that mirrors the underlying SDK. These changes are consistent with our general trend toward putting more logic in Dart and less in native code, for ease of maintenance. Fixes flutter/flutter#117906
1 parent aeecebc commit 7c97c88

File tree

13 files changed

+1482
-581
lines changed

13 files changed

+1482
-581
lines changed

packages/file_selector/file_selector_linux/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 0.9.3
22

3+
* Updates method channel implementation to use Pigeon.
34
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.
45

56
## 0.9.2+1

packages/file_selector/file_selector_linux/lib/file_selector_linux.dart

Lines changed: 75 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,17 @@
44

55
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
66
import 'package:flutter/foundation.dart' show visibleForTesting;
7-
import 'package:flutter/services.dart';
87

9-
const MethodChannel _channel =
10-
MethodChannel('plugins.flutter.dev/file_selector_linux');
11-
12-
const String _typeGroupLabelKey = 'label';
13-
const String _typeGroupExtensionsKey = 'extensions';
14-
const String _typeGroupMimeTypesKey = 'mimeTypes';
15-
16-
const String _openFileMethod = 'openFile';
17-
const String _getSavePathMethod = 'getSavePath';
18-
const String _getDirectoryPathMethod = 'getDirectoryPath';
19-
20-
const String _acceptedTypeGroupsKey = 'acceptedTypeGroups';
21-
const String _confirmButtonTextKey = 'confirmButtonText';
22-
const String _initialDirectoryKey = 'initialDirectory';
23-
const String _multipleKey = 'multiple';
24-
const String _suggestedNameKey = 'suggestedName';
8+
import 'src/messages.g.dart';
259

2610
/// An implementation of [FileSelectorPlatform] for Linux.
2711
class FileSelectorLinux extends FileSelectorPlatform {
28-
/// The MethodChannel that is being used by this implementation of the plugin.
29-
@visibleForTesting
30-
MethodChannel get channel => _channel;
12+
/// Creates a new plugin implementation instance.
13+
FileSelectorLinux({
14+
@visibleForTesting FileSelectorApi? api,
15+
}) : _hostApi = api ?? FileSelectorApi();
16+
17+
final FileSelectorApi _hostApi;
3118

3219
/// Registers the Linux implementation.
3320
static void registerWith() {
@@ -40,19 +27,16 @@ class FileSelectorLinux extends FileSelectorPlatform {
4027
String? initialDirectory,
4128
String? confirmButtonText,
4229
}) async {
43-
final List<Map<String, Object>> serializedTypeGroups =
44-
_serializeTypeGroups(acceptedTypeGroups);
45-
final List<String>? path = await _channel.invokeListMethod<String>(
46-
_openFileMethod,
47-
<String, dynamic>{
48-
if (serializedTypeGroups.isNotEmpty)
49-
_acceptedTypeGroupsKey: serializedTypeGroups,
50-
'initialDirectory': initialDirectory,
51-
_confirmButtonTextKey: confirmButtonText,
52-
_multipleKey: false,
53-
},
54-
);
55-
return path == null ? null : XFile(path.first);
30+
final List<String> paths = await _hostApi.showFileChooser(
31+
PlatformFileChooserActionType.open,
32+
PlatformFileChooserOptions(
33+
allowedFileTypes:
34+
_platformTypeGroupsFromXTypeGroups(acceptedTypeGroups),
35+
currentFolderPath: initialDirectory,
36+
acceptButtonLabel: confirmButtonText,
37+
selectMultiple: false,
38+
));
39+
return paths.isEmpty ? null : XFile(paths.first);
5640
}
5741

5842
@override
@@ -61,19 +45,16 @@ class FileSelectorLinux extends FileSelectorPlatform {
6145
String? initialDirectory,
6246
String? confirmButtonText,
6347
}) async {
64-
final List<Map<String, Object>> serializedTypeGroups =
65-
_serializeTypeGroups(acceptedTypeGroups);
66-
final List<String>? pathList = await _channel.invokeListMethod<String>(
67-
_openFileMethod,
68-
<String, dynamic>{
69-
if (serializedTypeGroups.isNotEmpty)
70-
_acceptedTypeGroupsKey: serializedTypeGroups,
71-
_initialDirectoryKey: initialDirectory,
72-
_confirmButtonTextKey: confirmButtonText,
73-
_multipleKey: true,
74-
},
75-
);
76-
return pathList?.map((String path) => XFile(path)).toList() ?? <XFile>[];
48+
final List<String> paths = await _hostApi.showFileChooser(
49+
PlatformFileChooserActionType.open,
50+
PlatformFileChooserOptions(
51+
allowedFileTypes:
52+
_platformTypeGroupsFromXTypeGroups(acceptedTypeGroups),
53+
currentFolderPath: initialDirectory,
54+
acceptButtonLabel: confirmButtonText,
55+
selectMultiple: true,
56+
));
57+
return paths.map((String path) => XFile(path)).toList();
7758
}
7859

7960
@override
@@ -98,78 +79,76 @@ class FileSelectorLinux extends FileSelectorPlatform {
9879
List<XTypeGroup>? acceptedTypeGroups,
9980
SaveDialogOptions options = const SaveDialogOptions(),
10081
}) async {
101-
final List<Map<String, Object>> serializedTypeGroups =
102-
_serializeTypeGroups(acceptedTypeGroups);
10382
// TODO(stuartmorgan): Add the selected type group here and return it. See
10483
// https://github.com/flutter/flutter/issues/107093
105-
final String? path = await _channel.invokeMethod<String>(
106-
_getSavePathMethod,
107-
<String, dynamic>{
108-
if (serializedTypeGroups.isNotEmpty)
109-
_acceptedTypeGroupsKey: serializedTypeGroups,
110-
_initialDirectoryKey: options.initialDirectory,
111-
_suggestedNameKey: options.suggestedName,
112-
_confirmButtonTextKey: options.confirmButtonText,
113-
},
114-
);
115-
return path == null ? null : FileSaveLocation(path);
84+
final List<String> paths = await _hostApi.showFileChooser(
85+
PlatformFileChooserActionType.save,
86+
PlatformFileChooserOptions(
87+
allowedFileTypes:
88+
_platformTypeGroupsFromXTypeGroups(acceptedTypeGroups),
89+
currentFolderPath: options.initialDirectory,
90+
currentName: options.suggestedName,
91+
acceptButtonLabel: options.confirmButtonText,
92+
));
93+
return paths.isEmpty ? null : FileSaveLocation(paths.first);
11694
}
11795

11896
@override
11997
Future<String?> getDirectoryPath({
12098
String? initialDirectory,
12199
String? confirmButtonText,
122100
}) async {
123-
final List<String>? path = await _channel
124-
.invokeListMethod<String>(_getDirectoryPathMethod, <String, dynamic>{
125-
_initialDirectoryKey: initialDirectory,
126-
_confirmButtonTextKey: confirmButtonText,
127-
});
128-
return path?.first;
101+
final List<String> paths = await _hostApi.showFileChooser(
102+
PlatformFileChooserActionType.chooseDirectory,
103+
PlatformFileChooserOptions(
104+
currentFolderPath: initialDirectory,
105+
acceptButtonLabel: confirmButtonText,
106+
selectMultiple: false,
107+
));
108+
return paths.isEmpty ? null : paths.first;
129109
}
130110

131111
@override
132112
Future<List<String>> getDirectoryPaths({
133113
String? initialDirectory,
134114
String? confirmButtonText,
135115
}) async {
136-
final List<String>? pathList = await _channel
137-
.invokeListMethod<String>(_getDirectoryPathMethod, <String, dynamic>{
138-
_initialDirectoryKey: initialDirectory,
139-
_confirmButtonTextKey: confirmButtonText,
140-
_multipleKey: true,
141-
});
142-
return pathList ?? <String>[];
116+
return _hostApi.showFileChooser(
117+
PlatformFileChooserActionType.chooseDirectory,
118+
PlatformFileChooserOptions(
119+
currentFolderPath: initialDirectory,
120+
acceptButtonLabel: confirmButtonText,
121+
selectMultiple: true,
122+
));
143123
}
144124
}
145125

146-
List<Map<String, Object>> _serializeTypeGroups(List<XTypeGroup>? groups) {
147-
return (groups ?? <XTypeGroup>[]).map(_serializeTypeGroup).toList();
126+
List<PlatformTypeGroup>? _platformTypeGroupsFromXTypeGroups(
127+
List<XTypeGroup>? groups) {
128+
return groups?.map(_platformTypeGroupFromXTypeGroup).toList();
148129
}
149130

150-
Map<String, Object> _serializeTypeGroup(XTypeGroup group) {
151-
final Map<String, Object> serialization = <String, Object>{
152-
_typeGroupLabelKey: group.label ?? '',
153-
};
131+
PlatformTypeGroup _platformTypeGroupFromXTypeGroup(XTypeGroup group) {
132+
final String label = group.label ?? '';
154133
if (group.allowsAny) {
155-
serialization[_typeGroupExtensionsKey] = <String>['*'];
156-
} else {
157-
if ((group.extensions?.isEmpty ?? true) &&
158-
(group.mimeTypes?.isEmpty ?? true)) {
159-
throw ArgumentError('Provided type group $group does not allow '
160-
'all files, but does not set any of the Linux-supported filter '
161-
'categories. "extensions" or "mimeTypes" must be non-empty for Linux '
162-
'if anything is non-empty.');
163-
}
164-
if (group.extensions?.isNotEmpty ?? false) {
165-
serialization[_typeGroupExtensionsKey] = group.extensions
134+
return PlatformTypeGroup(
135+
label: label,
136+
extensions: <String>['*'],
137+
);
138+
}
139+
if ((group.extensions?.isEmpty ?? true) &&
140+
(group.mimeTypes?.isEmpty ?? true)) {
141+
throw ArgumentError('Provided type group $group does not allow '
142+
'all files, but does not set any of the Linux-supported filter '
143+
'categories. "extensions" or "mimeTypes" must be non-empty for Linux '
144+
'if anything is non-empty.');
145+
}
146+
return PlatformTypeGroup(
147+
label: label,
148+
// Covert to GtkFileFilter's *.<extension> format.
149+
extensions: group.extensions
166150
?.map((String extension) => '*.$extension')
167151
.toList() ??
168-
<String>[];
169-
}
170-
if (group.mimeTypes?.isNotEmpty ?? false) {
171-
serialization[_typeGroupMimeTypesKey] = group.mimeTypes ?? <String>[];
172-
}
173-
}
174-
return serialization;
152+
<String>[],
153+
mimeTypes: group.mimeTypes ?? <String>[]);
175154
}

0 commit comments

Comments
 (0)