Skip to content

Commit b398a02

Browse files
jasonpanelliEgor
authored andcommitted
[file_selector_platform_interface] Add platform interface for new file_selector plugin (flutter#2995)
1 parent 447c257 commit b398a02

22 files changed

+1242
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ packages/camera/** @bparrishMines
1111
packages/connectivity/** @cyanglaz @matthew-carroll
1212
packages/device_info/** @matthew-carroll
1313
packages/espresso/** @collinjackson @adazh
14+
packages/file_selector/** @ditman
1415
packages/google_maps_flutter/** @cyanglaz
1516
packages/google_sign_in/** @cyanglaz @mehmetf
1617
packages/image_picker/** @cyanglaz
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
* Initial release.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2020 The Flutter Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# file_selector_platform_interface
2+
3+
A common platform interface for the `file_selector` plugin.
4+
5+
This interface allows platform-specific implementations of the `file_selector`
6+
plugin, as well as the plugin itself, to ensure they are supporting the
7+
same interface.
8+
9+
# Usage
10+
11+
To implement a new platform-specific implementation of `file_selector`, extend
12+
[`FileSelectorPlatform`][2] with an implementation that performs the
13+
platform-specific behavior, and when you register your plugin, set the default
14+
`FileSelectorPlatform` by calling
15+
`FileSelectorPlatform.instance = MyPlatformFileSelector()`.
16+
17+
# Note on breaking changes
18+
19+
Strongly prefer non-breaking changes (such as adding a method to the interface)
20+
over breaking changes for this package.
21+
22+
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
23+
on why a less-clean interface is preferable to a breaking change.
24+
25+
[1]: ../file_selector
26+
[2]: lib/file_selector_platform_interface.dart
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export 'src/platform_interface/file_selector_interface.dart';
2+
export 'src/types/types.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2020 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/services.dart';
6+
7+
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
8+
import 'package:meta/meta.dart';
9+
10+
const MethodChannel _channel =
11+
MethodChannel('plugins.flutter.io/file_selector');
12+
13+
/// An implementation of [FileSelectorPlatform] that uses method channels.
14+
class MethodChannelFileSelector extends FileSelectorPlatform {
15+
/// The MethodChannel that is being used by this implementation of the plugin.
16+
@visibleForTesting
17+
MethodChannel get channel => _channel;
18+
19+
/// Load a file from user's computer and return it as an XFile
20+
@override
21+
Future<XFile> openFile({
22+
List<XTypeGroup> acceptedTypeGroups,
23+
String initialDirectory,
24+
String confirmButtonText,
25+
}) async {
26+
final List<String> path = await _channel.invokeListMethod<String>(
27+
'openFile',
28+
<String, dynamic>{
29+
'acceptedTypeGroups':
30+
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
31+
'initialDirectory': initialDirectory,
32+
'confirmButtonText': confirmButtonText,
33+
'multiple': false,
34+
},
35+
);
36+
return path == null ? null : XFile(path?.first);
37+
}
38+
39+
/// Load multiple files from user's computer and return it as an XFile
40+
@override
41+
Future<List<XFile>> openFiles({
42+
List<XTypeGroup> acceptedTypeGroups,
43+
String initialDirectory,
44+
String confirmButtonText,
45+
}) async {
46+
final List<String> pathList = await _channel.invokeListMethod<String>(
47+
'openFile',
48+
<String, dynamic>{
49+
'acceptedTypeGroups':
50+
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
51+
'initialDirectory': initialDirectory,
52+
'confirmButtonText': confirmButtonText,
53+
'multiple': true,
54+
},
55+
);
56+
return pathList?.map((path) => XFile(path))?.toList() ?? [];
57+
}
58+
59+
/// Gets the path from a save dialog
60+
@override
61+
Future<String> getSavePath({
62+
List<XTypeGroup> acceptedTypeGroups,
63+
String initialDirectory,
64+
String suggestedName,
65+
String confirmButtonText,
66+
}) async {
67+
return _channel.invokeMethod<String>(
68+
'getSavePath',
69+
<String, dynamic>{
70+
'acceptedTypeGroups':
71+
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
72+
'initialDirectory': initialDirectory,
73+
'suggestedName': suggestedName,
74+
'confirmButtonText': confirmButtonText,
75+
},
76+
);
77+
}
78+
79+
/// Gets a directory path from a dialog
80+
@override
81+
Future<String> getDirectoryPath({
82+
String initialDirectory,
83+
String confirmButtonText,
84+
}) async {
85+
return _channel.invokeMethod<String>(
86+
'getDirectoryPath',
87+
<String, dynamic>{
88+
'initialDirectory': initialDirectory,
89+
'confirmButtonText': confirmButtonText,
90+
},
91+
);
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2020 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
8+
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
9+
10+
import '../method_channel/method_channel_file_selector.dart';
11+
12+
/// The interface that implementations of file_selector must implement.
13+
///
14+
/// Platform implementations should extend this class rather than implement it as `file_selector`
15+
/// does not consider newly added methods to be breaking changes. Extending this class
16+
/// (using `extends`) ensures that the subclass will get the default implementation, while
17+
/// platform implementations that `implements` this interface will be broken by newly added
18+
/// [FileSelectorPlatform] methods.
19+
abstract class FileSelectorPlatform extends PlatformInterface {
20+
/// Constructs a FileSelectorPlatform.
21+
FileSelectorPlatform() : super(token: _token);
22+
23+
static final Object _token = Object();
24+
25+
static FileSelectorPlatform _instance = MethodChannelFileSelector();
26+
27+
/// The default instance of [FileSelectorPlatform] to use.
28+
///
29+
/// Defaults to [MethodChannelFileSelector].
30+
static FileSelectorPlatform get instance => _instance;
31+
32+
/// Platform-specific plugins should set this with their own platform-specific
33+
/// class that extends [FileSelectorPlatform] when they register themselves.
34+
static set instance(FileSelectorPlatform instance) {
35+
PlatformInterface.verifyToken(instance, _token);
36+
_instance = instance;
37+
}
38+
39+
/// Open file dialog for loading files and return a file path
40+
Future<XFile> openFile({
41+
List<XTypeGroup> acceptedTypeGroups,
42+
String initialDirectory,
43+
String confirmButtonText,
44+
}) {
45+
throw UnimplementedError('openFile() has not been implemented.');
46+
}
47+
48+
/// Open file dialog for loading files and return a list of file paths
49+
Future<List<XFile>> openFiles({
50+
List<XTypeGroup> acceptedTypeGroups,
51+
String initialDirectory,
52+
String confirmButtonText,
53+
}) {
54+
throw UnimplementedError('openFiles() has not been implemented.');
55+
}
56+
57+
/// Open file dialog for saving files and return a file path at which to save
58+
Future<String> getSavePath({
59+
List<XTypeGroup> acceptedTypeGroups,
60+
String initialDirectory,
61+
String suggestedName,
62+
String confirmButtonText,
63+
}) {
64+
throw UnimplementedError('getSavePath() has not been implemented.');
65+
}
66+
67+
/// Open file dialog for loading directories and return a directory path
68+
Future<String> getDirectoryPath({
69+
String initialDirectory,
70+
String confirmButtonText,
71+
}) {
72+
throw UnimplementedError('getDirectoryPath() has not been implemented.');
73+
}
74+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'x_file/x_file.dart';
2+
3+
export 'x_type_group/x_type_group.dart';
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import 'dart:convert';
2+
import 'dart:typed_data';
3+
4+
/// The interface for a XFile.
5+
///
6+
/// A XFile is a container that wraps the path of a selected
7+
/// file by the user and (in some platforms, like web) the bytes
8+
/// with the contents of the file.
9+
///
10+
/// This class is a very limited subset of dart:io [File], so all
11+
/// the methods should seem familiar.
12+
abstract class XFileBase {
13+
/// Construct a XFile
14+
XFileBase(String path);
15+
16+
/// Save the XFile at the indicated file path.
17+
void saveTo(String path) async {
18+
throw UnimplementedError('saveTo has not been implemented.');
19+
}
20+
21+
/// Get the path of the picked file.
22+
///
23+
/// This should only be used as a backwards-compatibility clutch
24+
/// for mobile apps, or cosmetic reasons only (to show the user
25+
/// the path they've picked).
26+
///
27+
/// Accessing the data contained in the picked file by its path
28+
/// is platform-dependant (and won't work on web), so use the
29+
/// byte getters in the XFile instance instead.
30+
String get path {
31+
throw UnimplementedError('.path has not been implemented.');
32+
}
33+
34+
/// The name of the file as it was selected by the user in their device.
35+
///
36+
/// Use only for cosmetic reasons, do not try to use this as a path.
37+
String get name {
38+
throw UnimplementedError('.name has not been implemented.');
39+
}
40+
41+
/// For web, it may be necessary for a file to know its MIME type.
42+
String get mimeType {
43+
throw UnimplementedError('.mimeType has not been implemented.');
44+
}
45+
46+
/// Get the length of the file. Returns a `Future<int>` that completes with the length in bytes.
47+
Future<int> length() {
48+
throw UnimplementedError('.length() has not been implemented.');
49+
}
50+
51+
/// Synchronously read the entire file contents as a string using the given [Encoding].
52+
///
53+
/// By default, `encoding` is [utf8].
54+
///
55+
/// Throws Exception if the operation fails.
56+
Future<String> readAsString({Encoding encoding = utf8}) {
57+
throw UnimplementedError('readAsString() has not been implemented.');
58+
}
59+
60+
/// Synchronously read the entire file contents as a list of bytes.
61+
///
62+
/// Throws Exception if the operation fails.
63+
Future<Uint8List> readAsBytes() {
64+
throw UnimplementedError('readAsBytes() has not been implemented.');
65+
}
66+
67+
/// Create a new independent [Stream] for the contents of this file.
68+
///
69+
/// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0).
70+
///
71+
/// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file.
72+
///
73+
/// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled.
74+
Stream<Uint8List> openRead([int start, int end]) {
75+
throw UnimplementedError('openRead() has not been implemented.');
76+
}
77+
78+
/// Get the last-modified time for the XFile
79+
Future<DateTime> lastModified() {
80+
throw UnimplementedError('openRead() has not been implemented.');
81+
}
82+
}

0 commit comments

Comments
 (0)