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

[file_selector_platform_interface] Add platform interface for new file_selector plugin #2995

Merged
merged 14 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ packages/camera/** @bparrishMines
packages/connectivity/** @cyanglaz @matthew-carroll
packages/device_info/** @matthew-carroll
packages/espresso/** @collinjackson @adazh
packages/file_selector/** @ditman
packages/google_maps_flutter/** @cyanglaz
packages/google_sign_in/** @cyanglaz @mehmetf
packages/image_picker/** @cyanglaz
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 1.0.0

* Initial release.
25 changes: 25 additions & 0 deletions packages/file_selector/file_selector_platform_interface/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright 2020 The Flutter Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 changes: 26 additions & 0 deletions packages/file_selector/file_selector_platform_interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# file_selector_platform_interface

A common platform interface for the `file_selector` plugin.

This interface allows platform-specific implementations of the `file_selector`
plugin, as well as the plugin itself, to ensure they are supporting the
same interface.

# Usage

To implement a new platform-specific implementation of `file_selector`, extend
[`FileSelectorPlatform`][2] with an implementation that performs the
platform-specific behavior, and when you register your plugin, set the default
`FileSelectorPlatform` by calling
`FileSelectorPlatform.instance = MyPlatformFileSelector()`.

# Note on breaking changes

Strongly prefer non-breaking changes (such as adding a method to the interface)
over breaking changes for this package.

See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
on why a less-clean interface is preferable to a breaking change.

[1]: ../file_selector
[2]: lib/file_selector_platform_interface.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'src/platform_interface/file_selector_interface.dart';
export 'src/types/types.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2020 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:flutter/services.dart';

import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:meta/meta.dart';

const MethodChannel _channel =
MethodChannel('plugins.flutter.io/file_selector');

/// An implementation of [FileSelectorPlatform] that uses method channels.
class MethodChannelFileSelector extends FileSelectorPlatform {
/// The MethodChannel that is being used by this implementation of the plugin.
@visibleForTesting
MethodChannel get channel => _channel;

/// Load a file from user's computer and return it as an XFile
@override
Future<XFile> openFile({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
}) async {
final List<String> path = await _channel.invokeListMethod<String>(
'openFile',
<String, dynamic>{
'acceptedTypeGroups':
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
'multiple': false,
},
);
return path == null ? null : XFile(path?.first);
}

/// Load multiple files from user's computer and return it as an XFile
@override
Future<List<XFile>> openFiles({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
}) async {
final List<String> pathList = await _channel.invokeListMethod<String>(
'openFile',
<String, dynamic>{
'acceptedTypeGroups':
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
'multiple': true,
},
);
return pathList?.map((path) => XFile(path))?.toList() ?? [];
}

/// Gets the path from a save dialog
@override
Future<String> getSavePath({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String suggestedName,
String confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getSavePath',
<String, dynamic>{
'acceptedTypeGroups':
acceptedTypeGroups?.map((group) => group.toJSON())?.toList(),
'initialDirectory': initialDirectory,
'suggestedName': suggestedName,
'confirmButtonText': confirmButtonText,
},
);
}

/// Gets a directory path from a dialog
@override
Future<String> getDirectoryPath({
String initialDirectory,
String confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getDirectoryPath',
<String, dynamic>{
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
},
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2020 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 'dart:async';

import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

import '../method_channel/method_channel_file_selector.dart';

/// The interface that implementations of file_selector must implement.
///
/// Platform implementations should extend this class rather than implement it as `file_selector`
/// does not consider newly added methods to be breaking changes. Extending this class
/// (using `extends`) ensures that the subclass will get the default implementation, while
/// platform implementations that `implements` this interface will be broken by newly added
/// [FileSelectorPlatform] methods.
abstract class FileSelectorPlatform extends PlatformInterface {
/// Constructs a FileSelectorPlatform.
FileSelectorPlatform() : super(token: _token);

static final Object _token = Object();

static FileSelectorPlatform _instance = MethodChannelFileSelector();

/// The default instance of [FileSelectorPlatform] to use.
///
/// Defaults to [MethodChannelFileSelector].
static FileSelectorPlatform get instance => _instance;

/// Platform-specific plugins should set this with their own platform-specific
/// class that extends [FileSelectorPlatform] when they register themselves.
static set instance(FileSelectorPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}

/// Open file dialog for loading files and return a file path
Future<XFile> openFile({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
}) {
throw UnimplementedError('openFile() has not been implemented.');
}

/// Open file dialog for loading files and return a list of file paths
Future<List<XFile>> openFiles({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String confirmButtonText,
}) {
throw UnimplementedError('openFiles() has not been implemented.');
}

/// Open file dialog for saving files and return a file path at which to save
Future<String> getSavePath({
List<XTypeGroup> acceptedTypeGroups,
String initialDirectory,
String suggestedName,
String confirmButtonText,
}) {
throw UnimplementedError('getSavePath() has not been implemented.');
}

/// Open file dialog for loading directories and return a directory path
Future<String> getDirectoryPath({
String initialDirectory,
String confirmButtonText,
}) {
throw UnimplementedError('getDirectoryPath() has not been implemented.');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'x_file/x_file.dart';

export 'x_type_group/x_type_group.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'dart:convert';
import 'dart:typed_data';

/// The interface for a XFile.
///
/// A XFile is a container that wraps the path of a selected
/// file by the user and (in some platforms, like web) the bytes
/// with the contents of the file.
///
/// This class is a very limited subset of dart:io [File], so all
/// the methods should seem familiar.
abstract class XFileBase {
/// Construct a XFile
XFileBase(String path);

/// Save the XFile at the indicated file path.
void saveTo(String path) async {
throw UnimplementedError('saveTo has not been implemented.');
}

/// Get the path of the picked file.
///
/// This should only be used as a backwards-compatibility clutch
/// for mobile apps, or cosmetic reasons only (to show the user
/// the path they've picked).
///
/// Accessing the data contained in the picked file by its path
/// is platform-dependant (and won't work on web), so use the
/// byte getters in the XFile instance instead.
String get path {
throw UnimplementedError('.path has not been implemented.');
}

/// The name of the file as it was selected by the user in their device.
///
/// Use only for cosmetic reasons, do not try to use this as a path.
String get name {
throw UnimplementedError('.name has not been implemented.');
}

/// For web, it may be necessary for a file to know its MIME type.
String get mimeType {
throw UnimplementedError('.mimeType has not been implemented.');
}

/// Get the length of the file. Returns a `Future<int>` that completes with the length in bytes.
Future<int> length() {
throw UnimplementedError('.length() has not been implemented.');
}

/// Synchronously read the entire file contents as a string using the given [Encoding].
///
/// By default, `encoding` is [utf8].
///
/// Throws Exception if the operation fails.
Future<String> readAsString({Encoding encoding = utf8}) {
throw UnimplementedError('readAsString() has not been implemented.');
}

/// Synchronously read the entire file contents as a list of bytes.
///
/// Throws Exception if the operation fails.
Future<Uint8List> readAsBytes() {
throw UnimplementedError('readAsBytes() has not been implemented.');
}

/// Create a new independent [Stream] for the contents of this file.
///
/// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0).
///
/// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file.
///
/// 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.
Stream<Uint8List> openRead([int start, int end]) {
throw UnimplementedError('openRead() has not been implemented.');
}

/// Get the last-modified time for the XFile
Future<DateTime> lastModified() {
throw UnimplementedError('openRead() has not been implemented.');
}
}
Loading