Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add getDirectoryPaths for macos
Browse files Browse the repository at this point in the history
  • Loading branch information
VanesaOshiro committed Feb 7, 2023
1 parent d065e4e commit 419f5c8
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 28 deletions.
3 changes: 2 additions & 1 deletion packages/file_selector/file_selector_macos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.9.1

* Adds `getDirectoryPaths` implementation.
* Updates example code for `use_build_context_synchronously` lint.
* Updates minimum Flutter version to 3.0.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:flutter/material.dart';

/// Screen that allows the user to select one or more directories using `getDirectoryPaths`,
/// then displays the selected directories in a dialog.
class GetMultipleDirectoriesPage extends StatelessWidget {
/// Default Constructor
const GetMultipleDirectoriesPage({Key? key}) : super(key: key);

Future<void> _getDirectoryPaths(BuildContext context) async {
const String confirmButtonText = 'Choose';
final List<String?> directoriesPaths =
await FileSelectorPlatform.instance.getDirectoryPaths(
confirmButtonText: confirmButtonText,
);
if (directoriesPaths == null) {
// Operation was canceled by the user.
return;
}
if (context.mounted) {
await showDialog<void>(
context: context,
builder: (BuildContext context) =>
TextDisplay(directoriesPaths.join('\n')),
);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Select multiple directories'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(
// ignore: deprecated_member_use
primary: Colors.blue,
// ignore: deprecated_member_use
onPrimary: Colors.white,
),
child: const Text(
'Press to ask user to choose multiple directories'),
onPressed: () => _getDirectoryPaths(context),
),
],
),
),
);
}
}

/// Widget that displays a text file in a dialog.
class TextDisplay extends StatelessWidget {
/// Creates a `TextDisplay`.
const TextDisplay(this.directoryPaths, {Key? key}) : super(key: key);

/// The paths selected in the dialog.
final String directoryPaths;

@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Selected Directories'),
content: Scrollbar(
child: SingleChildScrollView(
child: Text(directoryPaths),
),
),
actions: <Widget>[
TextButton(
child: const Text('Close'),
onPressed: () => Navigator.pop(context),
),
],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ class HomePage extends StatelessWidget {
child: const Text('Open a get directory dialog'),
onPressed: () => Navigator.pushNamed(context, '/directory'),
),
const SizedBox(height: 10),
ElevatedButton(
style: style,
child: const Text('Open a get directories dialog'),
onPressed: () =>
Navigator.pushNamed(context, '/multi-directories'),
),
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/material.dart';

import 'get_directory_page.dart';
import 'get_multiple_directories_page.dart';
import 'home_page.dart';
import 'open_image_page.dart';
import 'open_multiple_images_page.dart';
Expand Down Expand Up @@ -36,6 +37,8 @@ class MyApp extends StatelessWidget {
'/open/text': (BuildContext context) => const OpenTextPage(),
'/save/text': (BuildContext context) => SaveTextPage(),
'/directory': (BuildContext context) => const GetDirectoryPage(),
'/multi-directories': (BuildContext context) =>
const GetMultipleDirectoriesPage()
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,54 @@ class exampleTests: XCTestCase {
XCTAssertNotNil(panelController.openPanel)
}

}
func testGetDirectoriesMultiple() throws {
let panelController = TestPanelController()
let plugin = FileSelectorPlugin(
viewProvider: TestViewProvider(),
panelController: panelController)

let returnPaths = ["/foo/bar", "/foo/test"];
panelController.openURLs = returnPaths.map({ path in URL(fileURLWithPath: path) })

let called = XCTestExpectation()
let options = OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths, returnPaths)
called.fulfill()
}

wait(for: [called], timeout: 0.5)
XCTAssertNotNil(panelController.openPanel)
if let panel = panelController.openPanel {
XCTAssertTrue(panel.canChooseDirectories)
// For consistency across platforms, file selection is disabled.
XCTAssertFalse(panel.canChooseFiles)
XCTAssertTrue(panel.allowsMultipleSelection)
}
}

func testGetDirectoryMultipleCancel() throws {
let panelController = TestPanelController()
let plugin = FileSelectorPlugin(
viewProvider: TestViewProvider(),
panelController: panelController)

let called = XCTestExpectation()
let options = OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths.count, 0)
called.fulfill()
}

wait(for: [called], timeout: 0.5)
XCTAssertNotNil(panelController.openPanel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ..
file_selector_platform_interface: ^2.2.0
file_selector_platform_interface: ^2.4.0
flutter:
sdk: flutter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ class FileSelectorMacOS extends FileSelectorPlatform {
return paths.isEmpty ? null : paths.first;
}

@override
Future<List<String>> getDirectoryPaths({
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String?> paths =
await _hostApi.displayOpenPanel(OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions(
directoryPath: initialDirectory,
prompt: confirmButtonText,
)));
return paths.isEmpty ? <String>[] : List<String>.from(paths);
}

// Converts the type group list into a flat list of all allowed types, since
// macOS doesn't support filter groups.
AllowedTypes? _allowedTypesFromTypeGroups(List<XTypeGroup>? typeGroups) {
Expand Down
4 changes: 2 additions & 2 deletions packages/file_selector/file_selector_macos/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: file_selector_macos
description: macOS implementation of the file_selector plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
version: 0.9.0+4
version: 0.9.1

environment:
sdk: ">=2.12.0 <3.0.0"
Expand All @@ -18,7 +18,7 @@ flutter:

dependencies:
cross_file: ^0.3.1
file_selector_platform_interface: ^2.2.0
file_selector_platform_interface: ^2.4.0
flutter:
sdk: flutter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,31 @@ void main() {
plugin.getSavePath(acceptedTypeGroups: <XTypeGroup>[group]),
completes);
});

test('ignores all type groups if any of them is a wildcard', () async {
await plugin.getSavePath(acceptedTypeGroups: <XTypeGroup>[
const XTypeGroup(
label: 'text',
extensions: <String>['txt'],
mimeTypes: <String>['text/plain'],
macUTIs: <String>['public.text'],
),
const XTypeGroup(
label: 'image',
extensions: <String>['jpg'],
mimeTypes: <String>['image/jpg'],
macUTIs: <String>['public.image'],
),
const XTypeGroup(
label: 'any',
),
]);

final VerificationResult result =
verify(mockApi.displaySavePanel(captureAny));
final SavePanelOptions options = result.captured[0] as SavePanelOptions;
expect(options.allowedFileTypes, null);
});
});

group('getDirectoryPath', () {
Expand Down Expand Up @@ -366,28 +391,51 @@ void main() {
});
});

test('ignores all type groups if any of them is a wildcard', () async {
await plugin.getSavePath(acceptedTypeGroups: <XTypeGroup>[
const XTypeGroup(
label: 'text',
extensions: <String>['txt'],
mimeTypes: <String>['text/plain'],
macUTIs: <String>['public.text'],
),
const XTypeGroup(
label: 'image',
extensions: <String>['jpg'],
mimeTypes: <String>['image/jpg'],
macUTIs: <String>['public.image'],
),
const XTypeGroup(
label: 'any',
),
]);

final VerificationResult result =
verify(mockApi.displaySavePanel(captureAny));
final SavePanelOptions options = result.captured[0] as SavePanelOptions;
expect(options.allowedFileTypes, null);
group('getDirectoryPaths', () {
test('works as expected with no arguments', () async {
when(mockApi.displayOpenPanel(any)).thenAnswer((_) async =>
<String>['firstDirectory', 'secondDirectory', 'thirdDirectory']);

final List<String> path = await plugin.getDirectoryPaths();

expect(path,
<String>['firstDirectory', 'secondDirectory', 'thirdDirectory']);
final VerificationResult result =
verify(mockApi.displayOpenPanel(captureAny));
final OpenPanelOptions options = result.captured[0] as OpenPanelOptions;
expect(options.allowsMultipleSelection, true);
expect(options.canChooseFiles, false);
expect(options.canChooseDirectories, true);
expect(options.baseOptions.allowedFileTypes, null);
expect(options.baseOptions.directoryPath, null);
expect(options.baseOptions.nameFieldStringValue, null);
expect(options.baseOptions.prompt, null);
});

test('handles cancel', () async {
when(mockApi.displayOpenPanel(any)).thenAnswer((_) async => <String?>[]);

final List<String> paths = await plugin.getDirectoryPaths();

expect(paths, <String>[]);
});

test('passes confirmButtonText correctly', () async {
await plugin.getDirectoryPaths(confirmButtonText: 'Select directories');

final VerificationResult result =
verify(mockApi.displayOpenPanel(captureAny));
final OpenPanelOptions options = result.captured[0] as OpenPanelOptions;
expect(options.baseOptions.prompt, 'Select directories');
});

test('passes initialDirectory correctly', () async {
await plugin.getDirectoryPaths(initialDirectory: '/example/directory');

final VerificationResult result =
verify(mockApi.displayOpenPanel(captureAny));
final OpenPanelOptions options = result.captured[0] as OpenPanelOptions;
expect(options.baseOptions.directoryPath, '/example/directory');
});
});
}

0 comments on commit 419f5c8

Please sign in to comment.