import 'dart:async'; import 'package:file_picker/src/file_picker.dart'; import 'package:file_picker/src/file_picker_result.dart'; import 'package:file_picker/src/platform_file.dart'; import 'package:file_picker/src/utils.dart'; class FilePickerLinux extends FilePicker { @override Future pickFiles({ String? dialogTitle, FileType type = FileType.any, List? allowedExtensions, Function(FilePickerStatus)? onFileLoading, bool allowCompression = true, bool allowMultiple = false, bool withData = false, bool withReadStream = false, }) async { final String executable = await _getPathToExecutable(); final String fileFilter = fileTypeToFileFilter( type, allowedExtensions, ); final List arguments = generateCommandLineArguments( dialogTitle ?? defaultDialogTitle, fileFilter: fileFilter, multipleFiles: allowMultiple, pickDirectory: false, ); final String? fileSelectionResult = await runExecutableWithArguments( executable, arguments, ); if (fileSelectionResult == null) { return null; } final List filePaths = resultStringToFilePaths( fileSelectionResult, ); final List platformFiles = await filePathsToPlatformFiles( filePaths, withReadStream, withData, ); return FilePickerResult(platformFiles); } @override Future getDirectoryPath({ String? dialogTitle, }) async { final executable = await _getPathToExecutable(); final arguments = generateCommandLineArguments( dialogTitle ?? defaultDialogTitle, pickDirectory: true, ); return await runExecutableWithArguments(executable, arguments); } /// Returns the path to the executables `qarma` or `zenity` as a [String]. /// /// On Linux, the CLI tools `qarma` or `zenity` can be used to open a native /// file picker dialog. It seems as if all Linux distributions have at least /// one of these two tools pre-installed (on Ubuntu `zenity` is pre-installed). /// The future returns an error, if neither of both executables was found on /// the path. Future _getPathToExecutable() async { try { return await isExecutableOnPath('qarma'); } on Exception { return await isExecutableOnPath('zenity'); } } String fileTypeToFileFilter(FileType type, List? allowedExtensions) { switch (type) { case FileType.any: return '*.*'; case FileType.audio: return '*.mp3 *.wav *.midi *.ogg *.aac'; case FileType.custom: return '*.' + allowedExtensions!.join(' *.'); case FileType.image: return '*.bmp *.gif *.jpg *.jpeg *.png'; case FileType.media: return '*.webm *.mpeg *.mkv *.mp4 *.avi *.mov *.flv *.jpg *.jpeg *.bmp *.gif *.png'; case FileType.video: return '*.webm *.mpeg *.mkv *.mp4 *.avi *.mov *.flv'; default: throw Exception('unknown file type'); } } List generateCommandLineArguments( String dialogTitle, { String fileFilter = '', bool multipleFiles = false, bool pickDirectory = false, }) { final arguments = ['--file-selection', '--title', dialogTitle]; if (fileFilter.isNotEmpty) { arguments.add('--file-filter=$fileFilter'); } if (multipleFiles) { arguments.add('--multiple'); } if (pickDirectory) { arguments.add('--directory'); } return arguments; } /// Transforms the result string (stdout) of `qarma` / `zenity` into a [List] /// of file paths. List resultStringToFilePaths(String fileSelectionResult) { if (fileSelectionResult.trim().isEmpty) { return []; } return fileSelectionResult.split('|'); } }