Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bb0683c

Browse files
author
Miguel Ruivo
authored
Merge pull request #971 from miguelpruivo/feature/#915-modern-directory-picker-on-windows
#915 modern directory picker on Windows
2 parents 03cedea + 3fb350e commit bb0683c

File tree

5 files changed

+95
-70
lines changed

5 files changed

+95
-70
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 4.5.0
2+
3+
#### Desktop (Windows)
4+
Changes the implementation of `getDirectoryPath()` on Windows to provide a modern dialog that looks the same as a file picker dialog ([#915](https://github.com/miguelpruivo/flutter_file_picker/issues/915)).
5+
16
## 4.4.0
27

38
#### Desktop (Linux, macOS, and Windows)

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ A package that allows you to use the native file explorer to pick single or mult
3636

3737
If you have any feature that you want to see in this package, please feel free to issue a suggestion. 🎉
3838

39+
## Compatibility Chart
40+
41+
| API | Android | iOS | Linux | macOS | Windows | Web |
42+
| --------------------- | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ |
43+
| clearTemporaryFiles() | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: |
44+
| getDirectoryPath() | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
45+
| pickFiles() | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
46+
| saveFile() | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
47+
48+
See the [API section of the File Picker Wiki](https://github.com/miguelpruivo/flutter_file_picker/wiki/api) or the [official API reference on pub.dev](https://pub.dev/documentation/file_picker/latest/file_picker/FilePicker-class.html) for further details.
49+
50+
3951
## Documentation
4052
See the **[File Picker Wiki](https://github.com/miguelpruivo/flutter_file_picker/wiki)** for every detail on about how to install, setup and use it.
4153

lib/src/file_picker.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,14 @@ abstract class FilePicker extends PlatformInterface {
136136
/// window). This parameter works only on Windows desktop.
137137
///
138138
/// [initialDirectory] can be optionally set to an absolute path to specify
139-
/// where the dialog should open. Only supported on Linux, macOS, and Windows.
139+
/// where the dialog should open. Only supported on Linux and macOS.
140140
///
141-
/// Returns `null` if aborted or if the folder path couldn't be resolved.
141+
/// Returns a [Future<String?>] which resolves to the absolute path of the selected directory,
142+
/// if the user selected a directory. Returns `null` if the user aborted the dialog or if the
143+
/// folder path couldn't be resolved.
142144
///
145+
/// Note: on Windows, throws a `WindowsException` with a detailed error message, if the dialog
146+
/// could not be instantiated or the dialog result could not be interpreted.
143147
/// Note: Some Android paths are protected, hence can't be accessed and will return `/` instead.
144148
Future<String?> getDirectoryPath({
145149
String? dialogTitle,

lib/src/windows/file_picker_windows.dart

Lines changed: 70 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:file_picker/src/utils.dart';
88
import 'package:file_picker/src/exceptions.dart';
99
import 'package:file_picker/src/windows/file_picker_windows_ffi_types.dart';
1010
import 'package:path/path.dart';
11+
import 'package:win32/win32.dart';
1112

1213
FilePicker filePickerWithFFI() => FilePickerWindows();
1314

@@ -59,20 +60,82 @@ class FilePickerWindows extends FilePicker {
5960
return returnValue;
6061
}
6162

63+
/// See API spec:
64+
/// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog
65+
/// See example implementation:
66+
/// https://github.com/timsneath/win32/blob/main/example/dialogshow.dart
6267
@override
6368
Future<String?> getDirectoryPath({
6469
String? dialogTitle,
6570
bool lockParentWindow = false,
6671
String? initialDirectory,
6772
}) {
68-
final pathIdPointer =
69-
_pickDirectory(dialogTitle ?? defaultDialogTitle, lockParentWindow);
70-
if (pathIdPointer == null) {
71-
return Future.value(null);
73+
int hr = CoInitializeEx(
74+
nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
75+
76+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
77+
78+
final fileDialog = FileOpenDialog.createInstance();
79+
80+
final optionsPointer = calloc<Uint32>();
81+
hr = fileDialog.GetOptions(optionsPointer);
82+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
83+
84+
final options = optionsPointer.value |
85+
FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS |
86+
FILEOPENDIALOGOPTIONS.FOS_FORCEFILESYSTEM;
87+
hr = fileDialog.SetOptions(options);
88+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
89+
90+
final title = TEXT(dialogTitle ?? defaultDialogTitle);
91+
hr = fileDialog.SetTitle(title);
92+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
93+
free(title);
94+
95+
// TODO: figure out how to set the initial directory via SetDefaultFolder / SetFolder
96+
// if (initialDirectory != null) {
97+
// final folder = TEXT(initialDirectory);
98+
// final riid = calloc<COMObject>();
99+
// final item = IShellItem(riid);
100+
// final location = item.ptr;
101+
// SHCreateItemFromParsingName(folder, nullptr, riid.cast(), item.ptr.cast());
102+
// hr = fileDialog.AddPlace(item.ptr, FDAP.FDAP_TOP);
103+
// if (!SUCCEEDED(hr)) throw WindowsException(hr);
104+
// hr = fileDialog.SetFolder(location);
105+
// if (!SUCCEEDED(hr)) throw WindowsException(hr);
106+
// free(folder);
107+
// }
108+
109+
final hwndOwner = lockParentWindow ? GetForegroundWindow() : NULL;
110+
hr = fileDialog.Show(hwndOwner);
111+
if (!SUCCEEDED(hr)) {
112+
fileDialog.Release();
113+
CoUninitialize();
114+
115+
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
116+
return Future.value(null);
117+
}
118+
throw WindowsException(hr);
72119
}
73-
return Future.value(
74-
_getPathFromItemIdentifierList(pathIdPointer),
75-
);
120+
121+
final ppsi = calloc<COMObject>();
122+
hr = fileDialog.GetResult(ppsi.cast());
123+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
124+
125+
final item = IShellItem(ppsi);
126+
final pathPtr = calloc<Pointer<Utf16>>();
127+
hr = item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, pathPtr);
128+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
129+
130+
final path = pathPtr.value.toDartString();
131+
132+
hr = item.Release();
133+
if (!SUCCEEDED(hr)) throw WindowsException(hr);
134+
135+
hr = fileDialog.Release();
136+
CoUninitialize();
137+
138+
return Future.value(path);
76139
}
77140

78141
@override
@@ -138,66 +201,6 @@ class FilePickerWindows extends FilePicker {
138201
}
139202
}
140203

141-
/// Uses the Win32 API to display a dialog box that enables the user to select a folder.
142-
///
143-
/// Returns a PIDL that specifies the location of the selected folder relative to the root of the
144-
/// namespace. Returns null, if the user clicked on the "Cancel" button in the dialog box.
145-
Pointer? _pickDirectory(String dialogTitle, bool lockParentWindow) {
146-
final shell32 = DynamicLibrary.open('shell32.dll');
147-
148-
final shBrowseForFolderW =
149-
shell32.lookupFunction<SHBrowseForFolderW, SHBrowseForFolderW>(
150-
'SHBrowseForFolderW');
151-
152-
final Pointer<BROWSEINFOA> browseInfo = calloc<BROWSEINFOA>();
153-
if (lockParentWindow) {
154-
browseInfo.ref.hwndOwner = _getWindowHandle();
155-
}
156-
browseInfo.ref.pidlRoot = nullptr;
157-
browseInfo.ref.pszDisplayName = calloc.allocate<Utf16>(maximumPathLength);
158-
browseInfo.ref.lpszTitle = dialogTitle.toNativeUtf16();
159-
browseInfo.ref.ulFlags =
160-
bifEditBox | bifNewDialogStyle | bifReturnOnlyFsDirs;
161-
162-
final Pointer<NativeType> itemIdentifierList =
163-
shBrowseForFolderW(browseInfo);
164-
165-
calloc.free(browseInfo.ref.pszDisplayName);
166-
calloc.free(browseInfo.ref.lpszTitle);
167-
calloc.free(browseInfo);
168-
169-
if (itemIdentifierList == nullptr) {
170-
return null;
171-
}
172-
return itemIdentifierList;
173-
}
174-
175-
/// Uses the Win32 API to convert an item identifier list to a file system path.
176-
///
177-
/// [lpItem] must contain the address of an item identifier list that specifies a
178-
/// file or directory location relative to the root of the namespace (the desktop).
179-
/// Returns the file system path as a [String]. Throws an exception, if the
180-
/// conversion wasn't successful.
181-
String _getPathFromItemIdentifierList(Pointer lpItem) {
182-
final shell32 = DynamicLibrary.open('shell32.dll');
183-
184-
final shGetPathFromIDListW =
185-
shell32.lookupFunction<SHGetPathFromIDListW, SHGetPathFromIDListWDart>(
186-
'SHGetPathFromIDListW');
187-
188-
final Pointer<Utf16> pszPath = calloc.allocate<Utf16>(maximumPathLength);
189-
190-
final int result = shGetPathFromIDListW(lpItem, pszPath);
191-
if (result == 0x00000000) {
192-
throw Exception(
193-
'Failed to convert item identifier list to a file system path.');
194-
}
195-
196-
final path = pszPath.toDartString();
197-
calloc.free(pszPath);
198-
return path;
199-
}
200-
201204
/// Extracts the list of selected files from the Win32 API struct [OPENFILENAMEW].
202205
///
203206
/// After the user has closed the file picker dialog, Win32 API sets the property

pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: A package that allows you to use a native file explorer to pick sin
33
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
44
repository: https://github.com/miguelpruivo/flutter_file_picker
55
issue_tracker: https://github.com/miguelpruivo/flutter_file_picker/issues
6-
version: 4.4.0
6+
version: 4.5.0
77

88
dependencies:
99
flutter:
@@ -15,6 +15,7 @@ dependencies:
1515
plugin_platform_interface: ^2.0.0
1616
ffi: ^1.1.2
1717
path: ^1.8.0
18+
win32: ^2.4.1
1819

1920
dev_dependencies:
2021
lints: ^1.0.1

0 commit comments

Comments
 (0)