Closed rohintonc closed 2 months ago
Related issue on Android: https://github.com/miguelpruivo/flutter_file_picker/issues/1552
I got this working, by yes, you guessed it, implementing my own platform channel to show the directory picker using UIDocumentPickerViewController. I call startAccessingSecurityScopedResource
on the picked directory and cache the URL. Then I create a dispose method on my folder class on the Dart side to call stopAccessingSecurityScopedResource
on the cached URL when I am done reading the folder contents. Here is the code:
import UIKit
import Flutter
import UniformTypeIdentifiers
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, UIDocumentPickerDelegate {
var flutterResult: FlutterResult?
var directoryPath: URL!
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "com.yourCompany.app/documents", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { (call, result) in
if call.method == "getDirectoryPath" {
self.flutterResult = result
self.getDirectoryPath()
} else if call.method == "stopAccessingSecurityScopedResource" {
self.directoryPath?.stopAccessingSecurityScopedResource()
self.directoryPath = nil
result(nil)
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func getDirectoryPath() {
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.folder], asCopy: false)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
documentPicker.directoryURL = nil
documentPicker.modalPresentationStyle = .formSheet
if let rootViewController = window?.rootViewController {
rootViewController.present(documentPicker, animated: true, completion: nil)
}
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
self.directoryPath = urls.first
if self.directoryPath == nil {
flutterResult?(FlutterError(code: "NO_DIRECTORY_PICKED", message: "No directory was picked", details: nil))
return
}
let success = self.directoryPath.startAccessingSecurityScopedResource()
if success {
flutterResult?(self.directoryPath.path)
} else {
flutterResult?(FlutterError(code: "ACCESS_DENIED", message: "Unable to access security scoped resource", details: nil))
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
flutterResult?(FlutterError(code: "USER_CANCELLED", message: "User cancelled the picker", details: nil))
}
}
And on the Dart side:
static const methodChannel = MethodChannel('com.yourCompany.app/documents');
final path = await methodChannel.invokeMethod('getDirectoryPath');
final directory = Directory(path);
final items = await directory.list().toList())
methodChannel.invokeMethod('stopAccessingSecurityScopedResource');
Hope that helps someone. I also have the Android code if you need to do this on the Android platform which uses the Scoped Storage API.
@miguelpruivo Let me know if you want me to create a PR for this change. It would mean:
It is the responsibility of the Dart code to ensure that all picked folders are followed up with a call to the stopAccessingSecurityScopedResource platform channel, to avoid resource leaks.
This issue is stale because it has been open for 7 days with no activity.
This issue was closed because it has been inactive for 14 days since being marked as stale.
This issue should not have been closed. It still needs to be fixed with an improved API.
You can get a directory path, but cannot list the contents without calling
This should be matched by a call to
Reference: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
I confirmed the problem/fix by adding startAccessingSecurityScopedResource to docmentPicker:didPickDocumentsAtURLs:
However, another method channel will be required to call stopAccessingSecurityScopedResource on the selected folder when the Dart code has finished iterating the contents, as this code currently results in a resource leak.