adds multiple file selection option on iOS
This commit is contained in:
parent
a745cdb333
commit
a33ece96ed
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue