Updates docs

This commit is contained in:
Miguel Ruivo 2020-09-11 18:37:45 +01:00
parent 1239c39113
commit 4da8c0f180
8 changed files with 131 additions and 49 deletions

View File

@ -1,3 +1,20 @@
## 2.0.0
**Breaking Changes**
- Unifies all platforms (IO, Web and Desktop) in a single plugin (file_picker) that can be used seamlessly across all. Both [file_picker_interface](https://pub.dev/packages/file_picker_platform_interface) and [file_picker_web](https://pub.dev/packages/file_picker_web) are no longer mantained from now on.
- You'll now have to use `FilePicker.platform.pickFiles()` and extract the files from `FilePickerResult`;
- Because platforms are unified and mixing `File` instances from dart:io and dart:html required stubbing and bloated code, it's no longer available as helper method so you'll have to instanciate a `File` with the picked paths;
**New features**
- Simplified usage, now you only need to use `FilePicker.platform.pickFiles()` with desired parameters which will return a `FilePickerResult` with a `List<PlatformFile>` containing the picked data;
- Added classes `FilePickerResult` and `PlatformFile` classes with helper getters;
- On Android all picked files are scoped cached which should result in most of files being available. Caching process is only made once, so once done, the picked instance should be the same;
- On iOS picking audio now supports multiple and cloud picks;
- Added parameter `withData` that allows file data to be immediately available on memory as `Uint8List` (part of `PlatformFile` instance). This is particularly helpful on web or if you are going to upload to somehwere else;
- Major refactor with some clean-up and improvements;
**Removed**
- Single methods such as `getFilePath()`, `getMultiFilePath()`, `getFile()` and `getMultiFile()` are no longer availble in favor o `pickFiles()`;
## 1.13.3
Go: Updates MacOS directory picker to applescript (thank you @trister1997).

View File

@ -18,16 +18,17 @@
A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extensions filtering support.
## Currently supported features
* Load paths from **cloud files** (GDrive, Dropbox, iCloud)
* Load path from a **custom format** by providing a list of file extensions (pdf, svg, zip, etc.)
* Load path from **multiple files** optionally, supplying file extensions
* Load path from **media** (video & image only)
* Load path from **audio** only
* Load path from **image** only
* Load path from **video** only
* Load path from **directory**
* Load path from **any**
* Create a `File` or `List<File>` objects from **any** selected file(s)
* Load files from **cloud files** (GDrive, Dropbox, iCloud)
* Load files from a **custom format** by providing a list of file extensions (pdf, svg, zip, etc.)
* Load files from **multiple files** optionally, supplying file extensions
* Load files from **media** (video & image only)
* Load files from **audio** only
* Load files from **image** only
* Load files from **video** only
* Load files from **directory**
* Load files from **any**
* Load files data immediately to memory (`Uint8List`);
* Supports web;
* Supports desktop through **go-flutter** (MacOS, Windows, Linux)
If you have any feature that you want to see in this package, please feel free to issue a suggestion. 🎉
@ -54,19 +55,42 @@ Quick simple usage example:
#### Single file
```
File file = await FilePicker.getFile();
FilePickerResult result = await FilePicker.platform.pickFiles();
if(result != null) {
File file = File(result.files.single.path);
}
```
#### Multiple files
```
List<File> files = await FilePicker.getMultiFile();
FilePickerResult result = await FilePicker.platform.pickFiles(allowMultiple: true);
if(result != null) {
List<File> files = result.paths.map((path) => File(path));
}
```
#### Multiple files with extension filter
```
List<File> files = await FilePicker.getMultiFile(
FilePickerResult result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'pdf', 'doc'],
);
```
### Load result and file details
```
FilePickerResult result = await FilePicker.platform.pickFiles();
if(result != null) {
PlatformFile file = result.files.first;
print(file.name);
print(file.bytes);
print(file.size);
print(file.extension);
print(file.path);
}
```
For full usage details refer to the **[Wiki](https://github.com/miguelpruivo/flutter_file_picker/wiki)** above.
## Example App

View File

@ -32,7 +32,9 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
_paths = (await FilePicker.platform.pickFiles(
type: _pickingType,
allowMultiple: _multiPick,
allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null,
allowedExtensions: (_extension?.isNotEmpty ?? false)
? _extension?.replaceAll(' ', '')?.split(',')
: null,
))
?.files;
} on PlatformException catch (e) {
@ -52,7 +54,9 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
_scaffoldKey.currentState.showSnackBar(
SnackBar(
backgroundColor: result ? Colors.green : Colors.red,
content: Text((result ? 'Temporary files removed with success.' : 'Failed to clean temporary files')),
content: Text((result
? 'Temporary files removed with success.'
: 'Failed to clean temporary files')),
),
);
});
@ -124,7 +128,8 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
maxLength: 15,
autovalidate: true,
controller: _controller,
decoration: InputDecoration(labelText: 'File extension'),
decoration:
InputDecoration(labelText: 'File extension'),
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.none,
)
@ -133,8 +138,10 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: 200.0),
child: SwitchListTile.adaptive(
title: Text('Pick multiple files', textAlign: TextAlign.right),
onChanged: (bool value) => setState(() => _multiPick = value),
title:
Text('Pick multiple files', textAlign: TextAlign.right),
onChanged: (bool value) =>
setState(() => _multiPick = value),
value: _multiPick,
),
),
@ -171,15 +178,28 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
: _paths != null
? Container(
padding: const EdgeInsets.only(bottom: 30.0),
height: MediaQuery.of(context).size.height * 0.50,
height:
MediaQuery.of(context).size.height * 0.50,
child: Scrollbar(
child: ListView.separated(
itemCount: _paths != null && _paths.isNotEmpty ? _paths.length : 1,
itemBuilder: (BuildContext context, int index) {
final bool isMultiPath = _paths != null && _paths.isNotEmpty;
final String name =
'File $index: ' + (isMultiPath ? _paths.map((e) => e.name).toList()[index] : _fileName ?? '...');
final path = _paths.map((e) => e.path).toList()[index].toString();
itemCount:
_paths != null && _paths.isNotEmpty
? _paths.length
: 1,
itemBuilder:
(BuildContext context, int index) {
final bool isMultiPath =
_paths != null && _paths.isNotEmpty;
final String name = 'File $index: ' +
(isMultiPath
? _paths
.map((e) => e.name)
.toList()[index]
: _fileName ?? '...');
final path = _paths
.map((e) => e.path)
.toList()[index]
.toString();
return ListTile(
title: Text(
@ -188,7 +208,9 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
subtitle: Text(path),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
separatorBuilder:
(BuildContext context, int index) =>
const Divider(),
)),
)
: const SizedBox(),

View File

@ -43,12 +43,20 @@ abstract class FilePicker extends PlatformInterface {
/// Retrieves the file(s) from the underlying platform
///
/// Default [type] set to `FileType.any` with [allowMultiple] set to `false`
/// Optionally, [allowedExtensions] might be provided (e.g. [`pdf`, `svg`, `jpg`].).
/// Default [type] set to [FileType.any] with [allowMultiple] set to [false]
/// Optionally, [allowedExtensions] might be provided (e.g. `[pdf, svg, jpg]`.).
///
/// If [withData] is set, picked files will have its byte data immediately available on memory as [Uint8List]
/// which can be useful if you are picking it for server upload or similar.
///
/// If you want to track picking status, for example, because some files may take some time to be
/// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler
/// that will give you the current status of picking.
///
/// The result is wrapped in a [FilePickerResult] which contains helper getters
/// with useful information regarding the picked [List<PlatformFile>].
///
/// Returns [null] if aborted.
Future<FilePickerResult> pickFiles({
FileType type = FileType.any,
List<String> allowedExtensions,
@ -65,12 +73,14 @@ abstract class FilePicker extends PlatformInterface {
/// each platform and it isn't required to invoke this as the system should take care
/// of it whenever needed. However, this will force the cleanup if you want to manage those on your own.
///
/// Returns `true` if the files were removed with success, `false` otherwise.
Future<bool> clearTemporaryFiles() async => throw UnimplementedError('clearTemporaryFiles() has not been implemented.');
/// Returns [true] if the files were removed with success, [false] otherwise.
Future<bool> clearTemporaryFiles() async => throw UnimplementedError(
'clearTemporaryFiles() has not been implemented.');
/// Selects a directory and returns its absolute path.
///
/// On Android, this requires to be running on SDK 21 or above, else won't work.
/// Returns `null` if folder path couldn't be resolved.
Future<String> getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.');
/// Returns [null] if folder path couldn't be resolved.
Future<String> getDirectoryPath() async =>
throw UnimplementedError('getDirectoryPath() has not been implemented.');
}

View File

@ -7,8 +7,10 @@ import 'package:flutter/services.dart';
import 'file_picker_result.dart';
const MethodChannel _channel = MethodChannel('miguelruivo.flutter.plugins.filepicker');
const EventChannel _eventChannel = EventChannel('miguelruivo.flutter.plugins.filepickerevent');
const MethodChannel _channel =
MethodChannel('miguelruivo.flutter.plugins.filepicker');
const EventChannel _eventChannel =
EventChannel('miguelruivo.flutter.plugins.filepickerevent');
/// An implementation of [FilePicker] that uses method channels.
class FilePickerIO extends FilePicker {
@ -34,7 +36,8 @@ class FilePickerIO extends FilePicker {
);
@override
Future<bool> clearTemporaryFiles() async => _channel.invokeMethod<bool>('clear');
Future<bool> clearTemporaryFiles() async =>
_channel.invokeMethod<bool>('clear');
@override
Future<String> getDirectoryPath() async {
@ -59,13 +62,16 @@ class FilePickerIO extends FilePicker {
) async {
final String type = describeEnum(fileType);
if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) {
throw Exception('If you are using a custom extension filter, please use the FileType.custom instead.');
throw Exception(
'If you are using a custom extension filter, please use the FileType.custom instead.');
}
try {
_eventSubscription?.cancel();
if (onFileLoading != null) {
_eventSubscription = _eventChannel.receiveBroadcastStream().listen(
(data) => onFileLoading((data as bool) ? FilePickerStatus.picking : FilePickerStatus.done),
(data) => onFileLoading((data as bool)
? FilePickerStatus.picking
: FilePickerStatus.done),
onError: (error) => throw Exception(error),
);
}
@ -81,12 +87,14 @@ class FilePickerIO extends FilePicker {
return null;
}
return FilePickerResult(result.map((file) => PlatformFile.fromMap(file)).toList());
return FilePickerResult(
result.map((file) => PlatformFile.fromMap(file)).toList());
} on PlatformException catch (e) {
print('[$_tag] Platform exception: $e');
rethrow;
} catch (e) {
print('[$_tag] Unsupported operation. Method not found. The exception thrown was: $e');
print(
'[$_tag] Unsupported operation. Method not found. The exception thrown was: $e');
rethrow;
}
}

View File

@ -19,7 +19,10 @@ class FilePickerResult {
/// original files (which can be accessed through its URI property).
///
/// Only available on IO. Throws `UnsupportedError` on Web.
List<String> get paths => files.map((file) => kIsWeb ? throw UnsupportedError('Unsupported on Web') : file.path).toList();
List<String> get paths => files
.map((file) =>
kIsWeb ? throw UnsupportedError('Unsupported on Web') : file.path)
.toList();
/// A `List<String>` containing all names from picked files with its extensions.
List<String> get names => files.map((file) => file.name).toList();

View File

@ -26,7 +26,8 @@ class FilePickerWeb extends FilePicker {
bool allowCompression,
bool withData = true,
}) async {
final Completer<List<PlatformFile>> filesCompleter = Completer<List<PlatformFile>>();
final Completer<List<PlatformFile>> filesCompleter =
Completer<List<PlatformFile>>();
String accept = _fileType(type, allowedExtensions);
html.InputElement uploadInput = html.FileUploadInputElement();
@ -42,7 +43,8 @@ class FilePickerWeb extends FilePicker {
List<PlatformFile> pickedFiles = [];
reader.onLoadEnd.listen((e) {
final Uint8List bytes = Base64Decoder().convert(reader.result.toString().split(",").last);
final Uint8List bytes =
Base64Decoder().convert(reader.result.toString().split(",").last);
pickedFiles.add(
PlatformFile(
@ -83,7 +85,8 @@ class FilePickerWeb extends FilePicker {
return 'video/*|image/*';
case FileType.custom:
return allowedExtensions.fold('', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next');
return allowedExtensions.fold(
'', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next');
break;
}
return '';

View File

@ -6,15 +6,13 @@ class PlatformFile {
this.name,
this.bytes,
this.size,
this.isDirectory = false,
});
PlatformFile.fromMap(Map data)
: this.path = data['path'],
this.name = data['name'],
this.bytes = data['bytes'],
this.size = data['size'],
this.isDirectory = data['isDirectory'];
this.size = data['size'];
/// The absolute path for a cached copy of this file. It can be used to create a
/// a file instance with a descriptor for the given path.
@ -33,9 +31,6 @@ class PlatformFile {
/// The file size in KB.
final int size;
/// Whether this file references a directory or not.
final bool isDirectory;
/// File extension for this file.
String get extension => path?.split('.')?.last;
}