commit
4d068d2747
|
@ -1,13 +1,33 @@
|
|||
.DS_Store
|
||||
.atom/
|
||||
.idea
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
.packages
|
||||
.dart_tool/
|
||||
.pub/
|
||||
build/
|
||||
ios/.generated/
|
||||
packages
|
||||
.dart_tool/
|
||||
pubspec.lock
|
||||
.iml
|
||||
|
||||
Podfile
|
||||
Podfile.lock
|
||||
file_picker.iml
|
||||
Pods/
|
||||
.symlinks/
|
||||
**/Flutter/App.framework/
|
||||
**/Flutter/Flutter.framework/
|
||||
**/Flutter/Generated.xcconfig
|
||||
**/Flutter/flutter_assets/
|
||||
ServiceDefinitions.json
|
||||
xcuserdata/
|
||||
|
||||
local.properties
|
||||
.gradle/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle-wrapper.jar
|
||||
*.iml
|
||||
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
GeneratedPluginRegistrant.java
|
||||
build/
|
||||
.flutter-plugins
|
||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
|||
## 1.1.0
|
||||
|
||||
**Breaking changes**
|
||||
* `FileType.PDF` was removed since now it can be used along with custom file types by using the `FileType.CUSTOM` and providing the file extension (e.g. PDF, SVG, ZIP, etc.).
|
||||
* `FileType.CAPTURE` is now `FileType.CAMERA`
|
||||
|
||||
**New features**
|
||||
* Now it is possible to provide a custom file extension to filter file picking options by using `FileType.CUSTOM`
|
||||
|
||||
**Bug fixes and updates**
|
||||
* Fixes file names from cloud on Android. Previously it would always display **Document**
|
||||
* Fixes an issue on iOS where an exception was being thrown after canceling and re-opening the picker.
|
||||
* Fixes an issue where collision could happen with request codes on Android.
|
||||
* Adds public documentation to `file_picker`
|
||||
* Example app updated.
|
||||
* Updates .gitignore
|
||||
|
||||
## 1.0.3
|
||||
|
||||
* Fixes `build.gradle`.
|
||||
|
|
14
README.md
14
README.md
|
@ -4,17 +4,17 @@
|
|||
</a>
|
||||
# file_picker
|
||||
|
||||
File picker plugin alows you to use a native file explorer to load absolute file path from different types of files.
|
||||
File picker plugin alows you to use a native file explorer to load absolute file path from different file types.
|
||||
|
||||
## Installation
|
||||
|
||||
First, add *file_picker* as a dependency in [your pubspec.yaml file](https://flutter.io/platform-plugins/).
|
||||
|
||||
```
|
||||
file_picker: ^1.0.2
|
||||
file_picker: ^1.1.0
|
||||
```
|
||||
## Android
|
||||
Add `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />` to your app `AndroidManifest.xml` file.
|
||||
Add `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>` to your app `AndroidManifest.xml` file.
|
||||
|
||||
## iOS
|
||||
Since we are using *image_picker* as a dependency from this plugin to load paths from gallery and camera, we need the following keys to your _Info.plist_ file, located in `<project root>/ios/Runner/Info.plist`:
|
||||
|
@ -22,19 +22,19 @@ Since we are using *image_picker* as a dependency from this plugin to load paths
|
|||
* `NSPhotoLibraryUsageDescription` - describe why your app needs permission for the photo library. This is called _Privacy - Photo Library Usage Description_ in the visual editor.
|
||||
* `NSCameraUsageDescription` - describe why your app needs access to the camera. This is called _Privacy - Camera Usage Description_ in the visual editor.
|
||||
* `NSMicrophoneUsageDescription` - describe why your app needs access to the microphone, if you intend to record videos. This is called _Privacy - Microphone Usage Description_ in the visual editor.
|
||||
* `UIBackgroundModes` with the `fetch` and `remote-notifications` keys - describe why your app needs to access background taks, such downloading files (from cloud services) when not cached to locate path. This is called _Required background modes_, with the keys _App download content from network_ and _App downloads content in response to push notifications_ respectively in the visual editor (since both methods aren't actually overriden, not adding this property/keys may only display a warning, but shouldn't prevent its correct usage).
|
||||
|
||||
## To-do
|
||||
## Currently supported features
|
||||
* [X] Load paths from **cloud files** (GDrive, Dropbox, iCloud)
|
||||
* [X] Load path from **PDF**
|
||||
* [X] Load path from **gallery**
|
||||
* [X] Load path from **camera**
|
||||
* [X] Load path from **video**
|
||||
* [X] Load path from **any** type of file (without filtering)
|
||||
* [ ] Load path from a **custom format**
|
||||
* [X] Load path from a **custom format** by providing a file extension (pdf, svg, zip, etc.)
|
||||
|
||||
## Demo App
|
||||
|
||||
![Demo](https://github.com/miguelpruivo/plugins_flutter_file_picker/blob/master/example/demo.png)
|
||||
![Demo](https://github.com/miguelpruivo/plugins_flutter_file_picker/blob/master/example/example.gif)
|
||||
|
||||
## Example
|
||||
```
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
*.iml
|
||||
*.class
|
||||
.gradle
|
||||
.idea/
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
|
|
|
@ -6,11 +6,14 @@ import android.content.Intent;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -25,10 +28,11 @@ import io.flutter.plugin.common.PluginRegistry.Registrar;
|
|||
/** FilePickerPlugin */
|
||||
public class FilePickerPlugin implements MethodCallHandler {
|
||||
|
||||
private static final int REQUEST_CODE = 43;
|
||||
private static final int REQUEST_CODE = FilePickerPlugin.class.hashCode() + 43;
|
||||
private static final int PERM_CODE = FilePickerPlugin.class.hashCode() + 50;
|
||||
private static final String TAG = "FilePicker";
|
||||
|
||||
private static final String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
|
||||
private static Result result;
|
||||
private static Registrar instance;
|
||||
private static String fileType;
|
||||
|
@ -54,7 +58,7 @@ public class FilePickerPlugin implements MethodCallHandler {
|
|||
if(fullPath == null)
|
||||
{
|
||||
FileOutputStream fos = null;
|
||||
cloudFile = instance.activeContext().getCacheDir().getAbsolutePath() + "/Document";
|
||||
cloudFile = instance.activeContext().getCacheDir().getAbsolutePath() + "/" + FileUtils.getFileName(uri, instance.activeContext());
|
||||
|
||||
try {
|
||||
fos = new FileOutputStream(cloudFile);
|
||||
|
@ -78,7 +82,7 @@ public class FilePickerPlugin implements MethodCallHandler {
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.i(TAG, "Loaded file from cloud created on:" + cloudFile);
|
||||
Log.i(TAG, "Cloud file loaded and cached on:" + cloudFile);
|
||||
fullPath = cloudFile;
|
||||
}
|
||||
|
||||
|
@ -94,7 +98,7 @@ public class FilePickerPlugin implements MethodCallHandler {
|
|||
instance.addRequestPermissionsResultListener(new PluginRegistry.RequestPermissionsResultListener() {
|
||||
@Override
|
||||
public boolean onRequestPermissionsResult(int requestCode, String[] strings, int[] grantResults) {
|
||||
if (requestCode == 0 && grantResults.length > 0
|
||||
if (requestCode == PERM_CODE && grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
startFileExplorer(fileType);
|
||||
return true;
|
||||
|
@ -128,11 +132,20 @@ public class FilePickerPlugin implements MethodCallHandler {
|
|||
Activity activity = instance.activity();
|
||||
Log.i(TAG, "Requesting permission: " + permission);
|
||||
String[] perm = { permission };
|
||||
ActivityCompat.requestPermissions(activity, perm, 0);
|
||||
ActivityCompat.requestPermissions(activity, perm, PERM_CODE);
|
||||
}
|
||||
|
||||
private String resolveType(String type) {
|
||||
|
||||
final boolean isCustom = type.contains("__CUSTOM_");
|
||||
|
||||
if(isCustom) {
|
||||
final String extension = type.split("__CUSTOM_")[1].toLowerCase();
|
||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
Log.i(TAG, "Custom file type: " + mime);
|
||||
return mime;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "PDF":
|
||||
return "application/pdf";
|
||||
|
@ -152,14 +165,19 @@ public class FilePickerPlugin implements MethodCallHandler {
|
|||
Intent intent;
|
||||
|
||||
if (checkPermission()) {
|
||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
|
||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
intent = new Intent(Intent.ACTION_PICK);
|
||||
} else {
|
||||
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
}
|
||||
|
||||
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + File.separator);
|
||||
intent.setDataAndType(uri, type);
|
||||
intent.setType(type);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
|
||||
Log.d(TAG, "Intent: " + intent.toString());
|
||||
|
||||
instance.activity().startActivityForResult(intent, REQUEST_CODE);
|
||||
} else {
|
||||
requestPermission();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
package com.mr.flutter.plugin.filepicker;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
|
@ -10,50 +11,42 @@ import android.provider.DocumentsContract;
|
|||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
/**
|
||||
* Credits to NiRRaNjAN from package in.gauriinfotech.commons;.
|
||||
* Credits to NiRRaNjAN from utils extracted of in.gauriinfotech.commons;.
|
||||
**/
|
||||
public class FileUtils
|
||||
{
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
private static final String tag = "FilePathPicker";
|
||||
|
||||
public static String getPath(final Uri uri, Context context)
|
||||
{
|
||||
public static String getPath(final Uri uri, Context context) {
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
if (isKitKat)
|
||||
{
|
||||
if (isKitKat) {
|
||||
return getForApi19(context, uri);
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme()))
|
||||
{
|
||||
if (isGooglePhotosUri(uri))
|
||||
{
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
if (isGooglePhotosUri(uri)) {
|
||||
return uri.getLastPathSegment();
|
||||
}
|
||||
return getDataColumn(context, uri, null, null);
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme()))
|
||||
{
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
private static String getForApi19(Context context, Uri uri)
|
||||
{
|
||||
private static String getForApi19(Context context, Uri uri) {
|
||||
Log.e(tag, "+++ API 19 URI :: " + uri);
|
||||
if (DocumentsContract.isDocumentUri(context, uri))
|
||||
{
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
Log.e(tag, "+++ Document URI");
|
||||
if (isExternalStorageDocument(uri))
|
||||
{
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
Log.e(tag, "+++ External Document URI");
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
if ("primary".equalsIgnoreCase(type))
|
||||
{
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
Log.e(tag, "+++ Primary External Document URI");
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
|
@ -65,13 +58,25 @@ public class FileUtils
|
|||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:", "");
|
||||
}
|
||||
try {
|
||||
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
} catch (Exception e) {
|
||||
Log.e(tag, "+++ Something went wrong while retrieving document path: " + e.toString());
|
||||
|
||||
String[] contentUriPrefixesToTry = new String[]{
|
||||
"content://downloads/public_downloads",
|
||||
"content://downloads/my_downloads",
|
||||
"content://downloads/all_downloads"
|
||||
};
|
||||
for (String contentUriPrefix : contentUriPrefixesToTry) {
|
||||
Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
|
||||
try {
|
||||
String path = getDataColumn(context, contentUri, null, null);
|
||||
if (path != null) {
|
||||
return path;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(tag, "+++ Something went wrong while retrieving document path: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else if (isMediaDocument(uri)) {
|
||||
Log.e(tag, "+++ Media Document URI");
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
|
@ -79,16 +84,13 @@ public class FileUtils
|
|||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type))
|
||||
{
|
||||
if ("image".equals(type)) {
|
||||
Log.e(tag, "+++ Image Media Document URI");
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type))
|
||||
{
|
||||
} else if ("video".equals(type)) {
|
||||
Log.e(tag, "+++ Video Media Document URI");
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type))
|
||||
{
|
||||
} else if ("audio".equals(type)) {
|
||||
Log.e(tag, "+++ Audio Media Document URI");
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
@ -100,15 +102,13 @@ public class FileUtils
|
|||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme()))
|
||||
{
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
Log.e(tag, "+++ No DOCUMENT URI :: CONTENT ");
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
|
||||
return getDataColumn(context, uri, null, null);
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme()))
|
||||
{
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
Log.e(tag, "+++ No DOCUMENT URI :: FILE ");
|
||||
return uri.getPath();
|
||||
}
|
||||
|
@ -116,47 +116,74 @@ public class FileUtils
|
|||
}
|
||||
|
||||
private static String getDataColumn(Context context, Uri uri, String selection,
|
||||
String[] selectionArgs)
|
||||
{
|
||||
String[] selectionArgs) {
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {
|
||||
column
|
||||
};
|
||||
try
|
||||
{
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
{
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(index);
|
||||
}
|
||||
} finally
|
||||
{
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isExternalStorageDocument(Uri uri)
|
||||
{
|
||||
public static String getFileName(Uri uri, Context context) {
|
||||
String result = null;
|
||||
|
||||
//if uri is content
|
||||
if (uri.getScheme() != null && uri.getScheme().equals("content")) {
|
||||
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
//local filesystem
|
||||
int index = cursor.getColumnIndex("_data");
|
||||
if (index == -1)
|
||||
//google drive
|
||||
index = cursor.getColumnIndex("_display_name");
|
||||
result = cursor.getString(index);
|
||||
if (result != null)
|
||||
uri = Uri.parse(result);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
if(uri.getPath() != null) {
|
||||
result = uri.getPath();
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1)
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isDownloadsDocument(Uri uri)
|
||||
{
|
||||
private static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isMediaDocument(Uri uri)
|
||||
{
|
||||
private static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static boolean isGooglePhotosUri(Uri uri)
|
||||
{
|
||||
private static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# file_picker_example
|
||||
|
||||
Demonstrates how to use the file_picker plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
For help getting started with Flutter, view our online
|
||||
[documentation](https://flutter.io/).
|
BIN
example/demo.png
BIN
example/demo.png
Binary file not shown.
Before Width: | Height: | Size: 129 KiB |
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
|
@ -38,6 +38,11 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
|
|
@ -13,20 +13,31 @@ class MyApp extends StatefulWidget {
|
|||
class _MyAppState extends State<MyApp> {
|
||||
String _fileName = '...';
|
||||
String _path = '...';
|
||||
String _extension;
|
||||
bool _hasValidMime = false;
|
||||
FileType _pickingType;
|
||||
TextEditingController _controller = new TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.addListener(() => _extension = _controller.text);
|
||||
}
|
||||
|
||||
void _openFileExplorer() async {
|
||||
try {
|
||||
_path = await FilePicker.getFilePath(type: _pickingType);
|
||||
} on PlatformException catch (e) {
|
||||
print(e.toString());
|
||||
if (_pickingType != FileType.CUSTOM || _hasValidMime) {
|
||||
try {
|
||||
_path = await FilePicker.getFilePath(type: _pickingType, fileExtension: _extension);
|
||||
} on PlatformException catch (e) {
|
||||
print("Unsupported operation" + e.toString());
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_fileName = _path != null ? _path.split('/').last : '...';
|
||||
});
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_fileName = _path != null ? _path.split('/').last : '...';
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -36,78 +47,97 @@ class _MyAppState extends State<MyApp> {
|
|||
appBar: new AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: new Center(
|
||||
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.all(20.0),
|
||||
child: new DropdownButton(
|
||||
hint: new Text('LOAD FILE PATH FROM...'),
|
||||
value: _pickingType,
|
||||
items: <DropdownMenuItem>[
|
||||
new DropdownMenuItem(
|
||||
child: new Text('FROM CAMERA'),
|
||||
value: FileType.CAPTURE,
|
||||
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 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
|
||||
? new TextFormField(
|
||||
maxLength: 20,
|
||||
autovalidate: true,
|
||||
controller: _controller,
|
||||
decoration: InputDecoration(labelText: 'File type'),
|
||||
keyboardType: TextInputType.text,
|
||||
textCapitalization: TextCapitalization.none,
|
||||
validator: (value) {
|
||||
RegExp reg = new RegExp(r'[^a-zA-Z0-9]');
|
||||
if (reg.hasMatch(value)) {
|
||||
_hasValidMime = false;
|
||||
return 'Invalid format';
|
||||
}
|
||||
_hasValidMime = true;
|
||||
},
|
||||
)
|
||||
: 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 DropdownMenuItem(
|
||||
child: new Text('FROM GALLERY'),
|
||||
value: FileType.IMAGE,
|
||||
),
|
||||
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 ',
|
||||
textAlign: TextAlign.center,
|
||||
style: new TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
new DropdownMenuItem(
|
||||
child: new Text('FROM PDF'),
|
||||
value: FileType.PDF,
|
||||
),
|
||||
new DropdownMenuItem(
|
||||
child: new Text('FROM VIDEO'),
|
||||
value: FileType.VIDEO,
|
||||
),
|
||||
new DropdownMenuItem(
|
||||
child: new Text('FROM ANY'),
|
||||
value: FileType.ANY,
|
||||
)
|
||||
],
|
||||
onChanged: (value) {
|
||||
setState(
|
||||
() {
|
||||
_pickingType = value;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
new Text(
|
||||
_fileName,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
new Padding(
|
||||
padding: const EdgeInsets.all(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(
|
||||
_path ?? '...',
|
||||
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,
|
||||
),
|
||||
],
|
||||
)),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
@interface FilePickerPlugin : NSObject<FlutterPlugin, UIDocumentPickerDelegate, UITabBarDelegate, UINavigationControllerDelegate,UIImagePickerControllerDelegate>
|
||||
@interface FilePickerPlugin : NSObject<FlutterPlugin, UIDocumentPickerDelegate, UITabBarDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>
|
||||
@end
|
||||
|
|
|
@ -83,7 +83,6 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls{
|
|||
|
||||
|
||||
// VideoPicker delegate
|
||||
|
||||
- (void) resolvePickVideo{
|
||||
|
||||
UIImagePickerController *videoPicker = [[UIImagePickerController alloc] init];
|
||||
|
@ -102,7 +101,13 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls{
|
|||
_result([videoURL path]);
|
||||
}
|
||||
|
||||
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
|
||||
_result = nil;
|
||||
[controller dismissViewControllerAnimated:YES completion:NULL];
|
||||
}
|
||||
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
||||
_result = nil;
|
||||
[picker dismissViewControllerAnimated:YES completion:NULL];
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Miguel Ruivo on 05/12/2018.
|
||||
//
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
@interface FileUtils : NSObject
|
||||
+ (NSString*) resolveType:(NSString*)type;
|
||||
+ (NSString*) resolvePath:(NSArray<NSURL *> *)urls;
|
||||
|
|
|
@ -11,6 +11,18 @@
|
|||
|
||||
+ (NSString*) resolveType:(NSString*)type {
|
||||
|
||||
BOOL isCustom = [type containsString:@"__CUSTOM_"];
|
||||
|
||||
if(isCustom) {
|
||||
type = [type stringByReplacingOccurrencesOfString:@"__CUSTOM_" withString:@""];
|
||||
NSString * format = [NSString stringWithFormat:@"dummy.%@", type];
|
||||
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[format pathExtension], NULL);
|
||||
NSString * UTIString = (__bridge NSString *)(UTI);
|
||||
CFRelease(UTI);
|
||||
NSLog(@"Custom file type: %@", UTIString);
|
||||
return [UTIString containsString:@"dyn."] ? nil : UTIString;
|
||||
}
|
||||
|
||||
if ([type isEqualToString:@"PDF"]) {
|
||||
return @"com.adobe.pdf";
|
||||
}
|
||||
|
|
|
@ -6,36 +6,55 @@ import 'package:image_picker/image_picker.dart';
|
|||
/// Supported file types, [ANY] should be used if the file you need isn't listed
|
||||
enum FileType {
|
||||
ANY,
|
||||
PDF,
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
CAPTURE,
|
||||
CAMERA,
|
||||
CUSTOM,
|
||||
}
|
||||
|
||||
class FilePicker {
|
||||
static const MethodChannel _channel = const MethodChannel('file_picker');
|
||||
static const String _tag = 'FilePicker';
|
||||
|
||||
static Future<String> _getPath(String type) async => await _channel.invokeMethod(type);
|
||||
|
||||
static Future<String> _getImage(ImageSource type) async {
|
||||
var image = await ImagePicker.pickImage(source: type);
|
||||
|
||||
return image?.path;
|
||||
static Future<String> _getPath(String type) async {
|
||||
try {
|
||||
return await _channel.invokeMethod(type);
|
||||
} on PlatformException catch (e) {
|
||||
print("[$_tag] Platform exception: " + e.toString());
|
||||
} catch (e) {
|
||||
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;
|
||||
}
|
||||
|
||||
/// Returns a [String] with the absolute path for the selected file
|
||||
static Future<String> getFilePath({FileType type = FileType.ANY}) async {
|
||||
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 absolute file path from the calling platform
|
||||
///
|
||||
/// A [type] must be provided to filter the picking results.
|
||||
/// 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 {
|
||||
switch (type) {
|
||||
case FileType.IMAGE:
|
||||
return _getImage(ImageSource.gallery);
|
||||
case FileType.CAPTURE:
|
||||
case FileType.CAMERA:
|
||||
return _getImage(ImageSource.camera);
|
||||
case FileType.PDF:
|
||||
return _getPath('PDF');
|
||||
case FileType.VIDEO:
|
||||
return _getPath('VIDEO');
|
||||
case FileType.ANY:
|
||||
return _getPath('ANY');
|
||||
case FileType.CUSTOM:
|
||||
return _getPath('__CUSTOM_' + (fileExtension ?? ''));
|
||||
default:
|
||||
return _getPath('ANY');
|
||||
}
|
||||
|
|
32
pubspec.yaml
32
pubspec.yaml
|
@ -1,6 +1,6 @@
|
|||
name: file_picker
|
||||
description: A plugin that allows you to pick absolute paths from diferent file types.
|
||||
version: 1.0.3
|
||||
version: 1.1.0
|
||||
author: Miguel Ruivo <miguelpruivo@outlook.com>
|
||||
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
|
||||
|
||||
|
@ -23,33 +23,3 @@ flutter:
|
|||
androidPackage: com.mr.flutter.plugin.filepicker
|
||||
pluginClass: FilePickerPlugin
|
||||
|
||||
# To add assets to your plugin package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.io/assets-and-images/#from-packages
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.io/assets-and-images/#resolution-aware.
|
||||
|
||||
# To add custom fonts to your plugin package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.io/custom-fonts/#from-packages
|
||||
|
|
Loading…
Reference in New Issue