adds multiple file selection option on iOS

This commit is contained in:
Miguel Ruivo 2019-03-06 01:16:35 +00:00
parent a745cdb333
commit a33ece96ed
5 changed files with 136 additions and 105 deletions

View File

@ -11,9 +11,11 @@ class FilePickerDemo extends StatefulWidget {
}
class _FilePickerDemoState extends State<FilePickerDemo> {
String _fileName = '...';
String _path = '...';
String _fileName;
String _path;
Map<String, String> _paths;
String _extension;
bool _multiPick = false;
bool _hasValidMime = false;
FileType _pickingType;
TextEditingController _controller = new TextEditingController();
@ -27,7 +29,12 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
void _openFileExplorer() async {
if (_pickingType != FileType.CUSTOM || _hasValidMime) {
try {
_path = await FilePicker.getFilePath(type: _pickingType, fileExtension: _extension);
if (_multiPick) {
_paths = await FilePicker.getMultiFilePath(fileExtension: _extension);
print("cenas");
} else {
_path = await FilePicker.getFilePath(type: _pickingType, fileExtension: _extension);
}
} on PlatformException catch (e) {
print("Unsupported operation" + e.toString());
}
@ -35,7 +42,7 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
if (!mounted) return;
setState(() {
_fileName = _path != null ? _path.split('/').last : '...';
_fileName = _path != null ? _path.split('/').last : _paths != null ? _paths.keys.toString() : '...';
});
}
}
@ -45,51 +52,47 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
title: const Text('File Picker example app'),
),
body: SingleChildScrollView(
child: new Center(
child: new Padding(
padding: const EdgeInsets.only(top: 50.0, left: 10.0, right: 10.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 20.0),
child: new DropdownButton(
hint: new Text('LOAD PATH FROM'),
value: _pickingType,
items: <DropdownMenuItem>[
// new DropdownMenuItem(
// child: new Text('FROM CAMERA'),
// value: FileType.CAMERA,
// ),
new DropdownMenuItem(
child: new Text('FROM AUDIO'),
value: FileType.AUDIO,
),
new DropdownMenuItem(
child: new Text('FROM GALLERY'),
value: FileType.IMAGE,
),
new DropdownMenuItem(
child: new Text('FROM VIDEO'),
value: FileType.VIDEO,
),
new DropdownMenuItem(
child: new Text('FROM ANY'),
value: FileType.ANY,
),
new DropdownMenuItem(
child: new Text('CUSTOM FORMAT'),
value: FileType.CUSTOM,
),
],
onChanged: (value) => setState(() => _pickingType = value)),
),
new ConstrainedBox(
constraints: new BoxConstraints(maxWidth: 150.0),
child: _pickingType == FileType.CUSTOM
child: new ConstrainedBox(
constraints: new BoxConstraints(maxWidth: 200.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 20.0),
child: new DropdownButton(
hint: new Text('LOAD PATH FROM'),
value: _pickingType,
items: <DropdownMenuItem>[
new DropdownMenuItem(
child: new Text('FROM AUDIO'),
value: FileType.AUDIO,
),
new DropdownMenuItem(
child: new Text('FROM GALLERY'),
value: FileType.IMAGE,
),
new DropdownMenuItem(
child: new Text('FROM VIDEO'),
value: FileType.VIDEO,
),
new DropdownMenuItem(
child: new Text('FROM ANY'),
value: FileType.ANY,
),
new DropdownMenuItem(
child: new Text('CUSTOM FORMAT'),
value: FileType.CUSTOM,
),
],
onChanged: (value) => setState(() => _pickingType = value)),
),
_pickingType == FileType.CUSTOM
? new TextFormField(
maxLength: 20,
autovalidate: true,
@ -107,38 +110,46 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
},
)
: new Container(),
),
new Padding(
padding: const EdgeInsets.only(top: 50.0, bottom: 20.0),
child: new RaisedButton(
onPressed: () => _openFileExplorer(),
child: new Text("Open file picker"),
new Visibility(
visible: _pickingType == FileType.ANY || _pickingType == FileType.CUSTOM,
child: new SwitchListTile.adaptive(
title: new Text('Pick multiple files', textAlign: TextAlign.right),
onChanged: (bool value) => setState(() => _multiPick = value),
value: _multiPick,
),
),
),
new Text(
'URI PATH ',
textAlign: TextAlign.center,
style: new TextStyle(fontWeight: FontWeight.bold),
),
new Text(
_path ?? '...',
textAlign: TextAlign.center,
softWrap: true,
textScaleFactor: 0.85,
),
new Padding(
padding: const EdgeInsets.only(top: 10.0),
child: new Text(
'FILE NAME ',
new Padding(
padding: const EdgeInsets.only(top: 50.0, bottom: 20.0),
child: new RaisedButton(
onPressed: () => _openFileExplorer(),
child: new Text("Open file picker"),
),
),
new Text(
'URI PATH ',
textAlign: TextAlign.center,
style: new TextStyle(fontWeight: FontWeight.bold),
),
),
new Text(
_fileName,
textAlign: TextAlign.center,
),
],
new Text(
_path ?? _paths?.values?.map((path) => path + '\n\n').toString() ?? '...',
textAlign: TextAlign.center,
softWrap: true,
textScaleFactor: 0.85,
),
new Padding(
padding: const EdgeInsets.only(top: 10.0),
child: new Text(
'FILE NAME ',
textAlign: TextAlign.center,
style: new TextStyle(fontWeight: FontWeight.bold),
),
),
new Text(
_fileName ?? '...',
textAlign: TextAlign.center,
),
],
),
),
)),
),

View File

@ -7,8 +7,8 @@
@property (nonatomic) UIViewController *viewController;
@property (nonatomic) UIImagePickerController *galleryPickerController;
@property (nonatomic) UIDocumentPickerViewController *pickerController;
@property (nonatomic) MPMediaPickerController *audioPickerController;
@property (nonatomic) UIDocumentInteractionController *interactionController;
@property (nonatomic) MPMediaPickerController *audioPickerController;
@property (nonatomic) NSString * fileType;
@end
@ -60,7 +60,7 @@
if(self.fileType == nil){
result(FlutterMethodNotImplemented);
} else {
[self resolvePickDocument];
[self resolvePickDocumentWithMultipleSelection:call.arguments];
}
}
@ -68,17 +68,18 @@
#pragma mark - Resolvers
- (void)resolvePickDocument {
- (void)resolvePickDocumentWithMultipleSelection:(BOOL)allowsMultipleSelection {
self.pickerController = [[UIDocumentPickerViewController alloc]
initWithDocumentTypes:@[self.fileType]
inMode:UIDocumentPickerModeImport];
if (@available(iOS 11.0, *)) {
self.pickerController.allowsMultipleSelection = NO;
} else {
// Fallback on earlier versions
self.pickerController.allowsMultipleSelection = allowsMultipleSelection;
} else if(allowsMultipleSelection) {
NSLog(@"Multiple file selection is only supported on iOS 11 and above. Single selection will be used.");
}
self.pickerController.delegate = self;
self.pickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
self.galleryPickerController.allowsEditing = NO;
@ -124,7 +125,14 @@
didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls{
[self.pickerController dismissViewControllerAnimated:YES completion:nil];
_result([FileUtils resolvePath:urls]);
NSArray * result = [FileUtils resolvePath:urls];
if([result count] > 1) {
_result(result);
} else {
_result([result objectAtIndex:0]);
}
}

View File

@ -7,7 +7,7 @@
#import <MobileCoreServices/MobileCoreServices.h>
@interface FileUtils : NSObject
+ (NSString*) resolveType:(NSString*)type;
+ (NSString*) resolvePath:(NSArray<NSURL *> *)urls;
+ (NSArray*) resolvePath:(NSArray<NSURL *> *)urls;
@end

View File

@ -30,15 +30,16 @@
}
}
+ (NSString*) resolvePath:(NSArray<NSURL *> *)urls{
+ (NSMutableArray*) resolvePath:(NSArray<NSURL *> *)urls{
NSString * uri;
NSMutableArray * paths = [[NSMutableArray alloc] init];
for (NSURL *url in urls) {
uri = (NSString *)[url path];
[paths addObject:uri];
}
return uri;
return paths;
}
@end

View File

@ -1,15 +1,14 @@
import 'dart:async';
import 'package:flutter/services.dart';
// import 'package:image_picker/image_picker.dart';
/// Supported file types, [ANY] should be used if the file you need isn't listed
String _kCustomType = '__CUSTOM_';
enum FileType {
ANY,
IMAGE,
VIDEO,
AUDIO,
// CAMERA,
CUSTOM,
}
@ -17,27 +16,34 @@ class FilePicker {
static const MethodChannel _channel = const MethodChannel('file_picker');
static const String _tag = 'FilePicker';
static Future<String> _getPath(String type) async {
static Future<dynamic> _getPath(String type, [bool multipleSelection = false]) async {
try {
return await _channel.invokeMethod(type);
dynamic result = await _channel.invokeMethod(type, multipleSelection);
if (multipleSelection) {
if (result is String) {
result = [result];
}
return Map<String, String>.fromIterable(result, key: (path) => path.split('/').last, value: (path) => path);
}
return result;
} on PlatformException catch (e) {
print("[$_tag] Platform exception: " + e.toString());
} catch (e) {
print(e.toString());
print(
"[$_tag] Unsupported operation. This probably have happened because [${type.split('_').last}] is an unsupported file type. You may want to try FileType.ALL instead.");
}
return null;
}
// static Future<String> _getImage(ImageSource type) async {
// try {
// var image = await ImagePicker.pickImage(source: type);
// return image?.path;
// } on PlatformException catch (e) {
// print("[$_tag] Platform exception: " + e.toString());
// }
// return null;
// }
/// Returns an iterable `Map<String,String>` where the `key` is the name of the file
/// and the `value` the path.
///
/// A [fileExtension] can be provided to filter the picking results.
/// If provided, it will be use the `FileType.CUSTOM` for that [fileExtension].
/// If not, `FileType.ANY` will be used and any combination of files can be multi picked at once.
static Future<Map<String, String>> getMultiFilePath({String fileExtension}) async =>
await _getPath(fileExtension != null ? (_kCustomType + fileExtension) : 'ANY', true);
/// Returns an absolute file path from the calling platform
///
@ -45,21 +51,26 @@ class FilePicker {
/// Can be used a custom file type with `FileType.CUSTOM`. A [fileExtension] must be provided (e.g. PDF, SVG, etc.)
/// Defaults to `FileType.ANY` which will display all file types.
static Future<String> getFilePath({FileType type = FileType.ANY, String fileExtension}) async {
var path;
switch (type) {
case FileType.IMAGE:
return _getPath('IMAGE');
// case FileType.CAMERA:
// return _getImage(ImageSource.camera);
path = _getPath('IMAGE');
break;
case FileType.AUDIO:
return _getPath('AUDIO');
path = _getPath('AUDIO');
break;
case FileType.VIDEO:
return _getPath('VIDEO');
path = _getPath('VIDEO');
break;
case FileType.ANY:
return _getPath('ANY');
path = _getPath('ANY');
break;
case FileType.CUSTOM:
return _getPath('__CUSTOM_' + (fileExtension ?? ''));
path = _getPath(_kCustomType + (fileExtension ?? ''));
break;
default:
return _getPath('ANY');
break;
}
return await path;
}
}