Refactors plugin to use single package with addition of PlatformFile and FilePickerResult classes
This commit is contained in:
parent
0f25c14fd6
commit
1916a9acce
|
@ -18,6 +18,7 @@ import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import io.flutter.plugin.common.EventChannel;
|
import io.flutter.plugin.common.EventChannel;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
@ -100,11 +101,9 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener
|
||||||
Log.i(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath());
|
Log.i(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath());
|
||||||
currentItem++;
|
currentItem++;
|
||||||
}
|
}
|
||||||
if (paths.size() > 1) {
|
|
||||||
finishWithSuccess(paths);
|
finishWithSuccess(paths);
|
||||||
} else {
|
|
||||||
finishWithSuccess(paths.get(0));
|
|
||||||
}
|
|
||||||
} else if (data.getData() != null) {
|
} else if (data.getData() != null) {
|
||||||
Uri uri = data.getData();
|
Uri uri = data.getData();
|
||||||
String fullPath;
|
String fullPath;
|
||||||
|
@ -125,7 +124,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener
|
||||||
|
|
||||||
if (fullPath != null) {
|
if (fullPath != null) {
|
||||||
Log.i(FilePickerDelegate.TAG, "Absolute file path:" + fullPath);
|
Log.i(FilePickerDelegate.TAG, "Absolute file path:" + fullPath);
|
||||||
finishWithSuccess(fullPath);
|
finishWithSuccess(Arrays.asList(fullPath));
|
||||||
} else {
|
} else {
|
||||||
finishWithError("unknown_path", "Failed to retrieve path.");
|
finishWithError("unknown_path", "Failed to retrieve path.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# example
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter, view our
|
||||||
|
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
|
@ -22,7 +22,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.mr.flutter.plugin.filepickerexample"
|
applicationId "com.mr.flutter.plugin.filepicker.example"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.mr.flutter.plugin.filepicker.example">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
|
@ -1,5 +1,5 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.mr.flutter.plugin.filepickerexample"
|
package="com.mr.flutter.plugin.filepicker.example"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.mr.flutter.plugin.filepickerexample;
|
package com.mr.flutter.plugin.filepicker.example;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import io.flutter.app.FlutterActivity;
|
import io.flutter.app.FlutterActivity;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.mr.flutter.plugin.filepicker.example
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.mr.flutter.plugin.filepicker.example">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
|
@ -0,0 +1 @@
|
||||||
|
0915ff87e81a3bfb122df4ced418a2b0
|
|
@ -310,7 +310,6 @@
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
@ -366,7 +365,6 @@
|
||||||
};
|
};
|
||||||
97C147041CF9000F007C117D /* Release */ = {
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,13 @@
|
||||||
|
import UIKit
|
||||||
|
import Flutter
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
override func application(
|
||||||
|
_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
|
) -> Bool {
|
||||||
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
#import "GeneratedPluginRegistrant.h"
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// ignore: unused_import
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:file_picker/src/file_picker_web.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||||
|
|
||||||
|
// ignore: public_member_api_docs
|
||||||
|
void registerPlugins(PluginRegistry registry) {
|
||||||
|
FilePickerWeb.registerWith(registry.registrarFor(FilePickerWeb));
|
||||||
|
registry.registerMessageHandler();
|
||||||
|
}
|
|
@ -5,19 +5,18 @@ import 'package:file_picker/file_picker.dart';
|
||||||
|
|
||||||
class FilePickerDemo extends StatefulWidget {
|
class FilePickerDemo extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_FilePickerDemoState createState() => new _FilePickerDemoState();
|
_FilePickerDemoState createState() => _FilePickerDemoState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FilePickerDemoState extends State<FilePickerDemo> {
|
class _FilePickerDemoState extends State<FilePickerDemo> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
String _fileName;
|
String _fileName;
|
||||||
String _path;
|
List<PlatformFile> _paths;
|
||||||
Map<String, String> _paths;
|
|
||||||
String _extension;
|
String _extension;
|
||||||
bool _loadingPath = false;
|
bool _loadingPath = false;
|
||||||
bool _multiPick = false;
|
bool _multiPick = false;
|
||||||
FileType _pickingType = FileType.any;
|
FileType _pickingType = FileType.any;
|
||||||
TextEditingController _controller = new TextEditingController();
|
TextEditingController _controller = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -28,97 +27,84 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
|
||||||
void _openFileExplorer() async {
|
void _openFileExplorer() async {
|
||||||
setState(() => _loadingPath = true);
|
setState(() => _loadingPath = true);
|
||||||
try {
|
try {
|
||||||
if (_multiPick) {
|
_paths = (await FilePicker.instance.pickFiles(
|
||||||
_path = null;
|
type: _pickingType,
|
||||||
_paths = await FilePicker.getMultiFilePath(
|
allowMultiple: _multiPick,
|
||||||
type: _pickingType,
|
allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null,
|
||||||
allowedExtensions: (_extension?.isNotEmpty ?? false)
|
))
|
||||||
? _extension?.replaceAll(' ', '')?.split(',')
|
?.files;
|
||||||
: null,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_paths = null;
|
|
||||||
_path = await FilePicker.getFilePath(
|
|
||||||
type: _pickingType,
|
|
||||||
allowedExtensions: (_extension?.isNotEmpty ?? false)
|
|
||||||
? _extension?.replaceAll(' ', '')?.split(',')
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
print("Unsupported operation" + e.toString());
|
print("Unsupported operation" + e.toString());
|
||||||
|
} catch (ex) {
|
||||||
|
print(ex);
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_loadingPath = false;
|
_loadingPath = false;
|
||||||
_fileName = _path != null
|
_fileName = _paths != null ? _paths.map((e) => e.name).toString() : '...';
|
||||||
? _path.split('/').last
|
|
||||||
: _paths != null ? _paths.keys.toString() : '...';
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearCachedFiles() {
|
void _clearCachedFiles() {
|
||||||
FilePicker.clearTemporaryFiles().then((result) {
|
FilePicker.instance.clearTemporaryFiles().then((result) {
|
||||||
_scaffoldKey.currentState.showSnackBar(
|
_scaffoldKey.currentState.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
backgroundColor: result ? Colors.green : Colors.red,
|
backgroundColor: result ? Colors.green : Colors.red,
|
||||||
content: Text((result
|
content: Text((result ? 'Temporary files removed with success.' : 'Failed to clean temporary files')),
|
||||||
? 'Temporary files removed with success.'
|
|
||||||
: 'Failed to clean temporary files')),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectFolder() {
|
void _selectFolder() {
|
||||||
FilePicker.getDirectoryPath().then((value) {
|
FilePicker.instance.getDirectoryPath().then((value) {
|
||||||
setState(() => _path = value);
|
setState(() => _paths = [value]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new MaterialApp(
|
return MaterialApp(
|
||||||
home: new Scaffold(
|
home: Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('File Picker example app'),
|
title: const Text('File Picker example app'),
|
||||||
),
|
),
|
||||||
body: new Center(
|
body: Center(
|
||||||
child: new Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
|
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
|
||||||
child: new SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: new Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 20.0),
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
child: new DropdownButton(
|
child: DropdownButton(
|
||||||
hint: new Text('LOAD PATH FROM'),
|
hint: Text('LOAD PATH FROM'),
|
||||||
value: _pickingType,
|
value: _pickingType,
|
||||||
items: <DropdownMenuItem>[
|
items: <DropdownMenuItem>[
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('FROM AUDIO'),
|
child: Text('FROM AUDIO'),
|
||||||
value: FileType.audio,
|
value: FileType.audio,
|
||||||
),
|
),
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('FROM IMAGE'),
|
child: Text('FROM IMAGE'),
|
||||||
value: FileType.image,
|
value: FileType.image,
|
||||||
),
|
),
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('FROM VIDEO'),
|
child: Text('FROM VIDEO'),
|
||||||
value: FileType.video,
|
value: FileType.video,
|
||||||
),
|
),
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('FROM MEDIA'),
|
child: Text('FROM MEDIA'),
|
||||||
value: FileType.media,
|
value: FileType.media,
|
||||||
),
|
),
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('FROM ANY'),
|
child: Text('FROM ANY'),
|
||||||
value: FileType.any,
|
value: FileType.any,
|
||||||
),
|
),
|
||||||
new DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
child: new Text('CUSTOM FORMAT'),
|
child: Text('CUSTOM FORMAT'),
|
||||||
value: FileType.custom,
|
value: FileType.custom,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -129,87 +115,76 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
new ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints.tightFor(width: 100.0),
|
constraints: const BoxConstraints.tightFor(width: 100.0),
|
||||||
child: _pickingType == FileType.custom
|
child: _pickingType == FileType.custom
|
||||||
? new TextFormField(
|
? TextFormField(
|
||||||
maxLength: 15,
|
maxLength: 15,
|
||||||
autovalidate: true,
|
autovalidate: true,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
decoration:
|
decoration: InputDecoration(labelText: 'File extension'),
|
||||||
InputDecoration(labelText: 'File extension'),
|
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
textCapitalization: TextCapitalization.none,
|
textCapitalization: TextCapitalization.none,
|
||||||
)
|
)
|
||||||
: new Container(),
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
new ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints.tightFor(width: 200.0),
|
constraints: const BoxConstraints.tightFor(width: 200.0),
|
||||||
child: new SwitchListTile.adaptive(
|
child: SwitchListTile.adaptive(
|
||||||
title: new Text('Pick multiple files',
|
title: Text('Pick multiple files', textAlign: TextAlign.right),
|
||||||
textAlign: TextAlign.right),
|
onChanged: (bool value) => setState(() => _multiPick = value),
|
||||||
onChanged: (bool value) =>
|
|
||||||
setState(() => _multiPick = value),
|
|
||||||
value: _multiPick,
|
value: _multiPick,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 50.0, bottom: 20.0),
|
padding: const EdgeInsets.only(top: 50.0, bottom: 20.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () => _openFileExplorer(),
|
onPressed: () => _openFileExplorer(),
|
||||||
child: new Text("Open file picker"),
|
child: Text("Open file picker"),
|
||||||
),
|
),
|
||||||
new RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () => _selectFolder(),
|
onPressed: () => _selectFolder(),
|
||||||
child: new Text("Pick folder"),
|
child: Text("Pick folder"),
|
||||||
),
|
),
|
||||||
new RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () => _clearCachedFiles(),
|
onPressed: () => _clearCachedFiles(),
|
||||||
child: new Text("Clear temporary files"),
|
child: Text("Clear temporary files"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Builder(
|
Builder(
|
||||||
builder: (BuildContext context) => _loadingPath
|
builder: (BuildContext context) => _loadingPath
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 10.0),
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
child: const CircularProgressIndicator())
|
child: const CircularProgressIndicator(),
|
||||||
: _path != null || _paths != null
|
)
|
||||||
? new Container(
|
: _paths != null
|
||||||
|
? Container(
|
||||||
padding: const EdgeInsets.only(bottom: 30.0),
|
padding: const EdgeInsets.only(bottom: 30.0),
|
||||||
height: MediaQuery.of(context).size.height * 0.50,
|
height: MediaQuery.of(context).size.height * 0.50,
|
||||||
child: new Scrollbar(
|
child: Scrollbar(
|
||||||
child: new ListView.separated(
|
child: ListView.separated(
|
||||||
itemCount: _paths != null && _paths.isNotEmpty
|
itemCount: _paths != null && _paths.isNotEmpty ? _paths.length : 1,
|
||||||
? _paths.length
|
|
||||||
: 1,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
final bool isMultiPath =
|
final bool isMultiPath = _paths != null && _paths.isNotEmpty;
|
||||||
_paths != null && _paths.isNotEmpty;
|
final String name =
|
||||||
final String name = 'File $index: ' +
|
'File $index: ' + (isMultiPath ? _paths.map((e) => e.name).toList()[index] : _fileName ?? '...');
|
||||||
(isMultiPath
|
final path = _paths.map((e) => e.path).toList()[index].toString();
|
||||||
? _paths.keys.toList()[index]
|
|
||||||
: _fileName ?? '...');
|
|
||||||
final path = isMultiPath
|
|
||||||
? _paths.values.toList()[index].toString()
|
|
||||||
: _path;
|
|
||||||
|
|
||||||
return new ListTile(
|
return ListTile(
|
||||||
title: new Text(
|
title: Text(
|
||||||
name,
|
name,
|
||||||
),
|
),
|
||||||
subtitle: new Text(path),
|
subtitle: Text(path),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder:
|
separatorBuilder: (BuildContext context, int index) => const Divider(),
|
||||||
(BuildContext context, int index) =>
|
|
||||||
new Divider(),
|
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
: new Container(),
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// This is a basic Flutter widget test.
|
||||||
|
//
|
||||||
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
|
// utility that Flutter provides. For example, you can send tap and scroll
|
||||||
|
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||||
|
// tree, read text, and verify that the values of widget properties are correct.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:example/main.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(MyApp());
|
||||||
|
|
||||||
|
// Verify that our counter starts at 0.
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(find.text('1'), findsNothing);
|
||||||
|
|
||||||
|
// Tap the '+' icon and trigger a frame.
|
||||||
|
await tester.tap(find.byIcon(Icons.add));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Verify that our counter has incremented.
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(find.text('1'), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 917 B |
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||||
|
<meta name="description" content="A new Flutter project.">
|
||||||
|
|
||||||
|
<!-- iOS meta tags & icons -->
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="example">
|
||||||
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
|
<title>example</title>
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||||
|
application. For more information, see:
|
||||||
|
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
||||||
|
<script>
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
navigator.serviceWorker.register('flutter_service_worker.js');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="main.dart.js" type="application/javascript"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"short_name": "example",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "minimal-ui",
|
||||||
|
"background_color": "#0175C2",
|
||||||
|
"theme_color": "#0175C2",
|
||||||
|
"description": "A new Flutter project.",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"prefer_related_applications": false,
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -253,7 +253,7 @@
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{
|
||||||
[self.documentPickerController dismissViewControllerAnimated:YES completion:nil];
|
[self.documentPickerController dismissViewControllerAnimated:YES completion:nil];
|
||||||
NSString * path = (NSString *)[url path];
|
NSString * path = (NSString *)[url path];
|
||||||
_result(path);
|
_result(@[path]);
|
||||||
_result = nil;
|
_result = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,12 +267,7 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls{
|
||||||
|
|
||||||
[self.documentPickerController dismissViewControllerAnimated:YES completion:nil];
|
[self.documentPickerController dismissViewControllerAnimated:YES completion:nil];
|
||||||
NSArray * result = [FileUtils resolvePath:urls];
|
NSArray * result = [FileUtils resolvePath:urls];
|
||||||
|
_result(result);
|
||||||
if([result count] > 1) {
|
|
||||||
_result(result);
|
|
||||||
} else {
|
|
||||||
_result([result objectAtIndex:0]);
|
|
||||||
}
|
|
||||||
_result = nil;
|
_result = nil;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -323,7 +318,7 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_result([pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl path]);
|
_result(@[[pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl path]]);
|
||||||
_result = nil;
|
_result = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,131 +1,5 @@
|
||||||
import 'dart:async';
|
library file_picker;
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:file_picker_platform_interface/file_picker_platform_interface.dart';
|
export './src/file_picker.dart';
|
||||||
import 'package:file_picker_platform_interface/method_channel_file_picker.dart';
|
export './src/platform_file.dart';
|
||||||
|
export './src/file_picker_result.dart';
|
||||||
export 'package:file_picker_platform_interface/file_picker_platform_interface.dart'
|
|
||||||
show FileType;
|
|
||||||
|
|
||||||
final MethodChannelFilePicker _filePickerPlatform = FilePickerPlatform.instance;
|
|
||||||
|
|
||||||
class FilePicker {
|
|
||||||
FilePicker._();
|
|
||||||
|
|
||||||
/// Returns an absolute file path from the calling platform.
|
|
||||||
///
|
|
||||||
/// Extension filters are allowed with [FileType.custom], when used, make sure to provide a [List]
|
|
||||||
/// of `allowedExtensions` (e.g. [`pdf`, `svg`, `jpg`].).
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// If you plan on picking images/videos and don't want them to be compressed automatically by OS,
|
|
||||||
/// you should set `allowCompression` to [false]. Calling this on Android won't have any effect, as
|
|
||||||
/// it already provides you the original file (or integral copy).
|
|
||||||
///
|
|
||||||
/// Defaults to [FileType.any] which will display all file types.
|
|
||||||
static Future<String> getFilePath({
|
|
||||||
FileType type = FileType.any,
|
|
||||||
List<String> allowedExtensions,
|
|
||||||
Function(FilePickerStatus) onFileLoading,
|
|
||||||
bool allowCompression,
|
|
||||||
}) async =>
|
|
||||||
await _filePickerPlatform.getFiles(
|
|
||||||
type: type,
|
|
||||||
allowedExtensions: allowedExtensions,
|
|
||||||
onFileLoading: onFileLoading,
|
|
||||||
allowCompression: allowCompression,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Returns an iterable [Map<String,String>] where the `key` is the name of the file
|
|
||||||
/// and the `value` the path.
|
|
||||||
///
|
|
||||||
/// A [List] with `allowedExtensions` can be provided to filter the allowed files to picked.
|
|
||||||
/// If provided, make sure you select [FileType.custom] as type.
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// If you plan on picking images/videos and don't want them to be compressed automatically by OS,
|
|
||||||
/// you should set `allowCompression` to [false]. Calling this on Android won't have any effect, as
|
|
||||||
/// it already provides you the original file (or integral copy).
|
|
||||||
///
|
|
||||||
/// Defaults to `FileType.any`, which allows any combination of files to be multi selected at once.
|
|
||||||
static Future<Map<String, String>> getMultiFilePath({
|
|
||||||
FileType type = FileType.any,
|
|
||||||
List<String> allowedExtensions,
|
|
||||||
Function(FilePickerStatus) onFileLoading,
|
|
||||||
bool allowCompression,
|
|
||||||
}) async =>
|
|
||||||
await _filePickerPlatform.getFiles(
|
|
||||||
type: type,
|
|
||||||
allowMultiple: true,
|
|
||||||
allowedExtensions: allowedExtensions,
|
|
||||||
onFileLoading: onFileLoading,
|
|
||||||
allowCompression: allowCompression,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Returns a [File] object from the selected file path.
|
|
||||||
///
|
|
||||||
/// This is an utility method that does the same of [getFilePath] but saving some boilerplate if
|
|
||||||
/// you are planing to create a [File] for the returned path.
|
|
||||||
static Future<File> getFile({
|
|
||||||
FileType type = FileType.any,
|
|
||||||
List<String> allowedExtensions,
|
|
||||||
Function(FilePickerStatus) onFileLoading,
|
|
||||||
bool allowCompression,
|
|
||||||
}) async {
|
|
||||||
final String filePath = await _filePickerPlatform.getFiles(
|
|
||||||
type: type,
|
|
||||||
allowedExtensions: allowedExtensions,
|
|
||||||
onFileLoading: onFileLoading,
|
|
||||||
allowCompression: allowCompression,
|
|
||||||
);
|
|
||||||
return filePath != null ? File(filePath) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a [List<File>] object from the selected files paths.
|
|
||||||
///
|
|
||||||
/// This is an utility method that does the same of [getMultiFilePath] but saving some boilerplate if
|
|
||||||
/// you are planing to create a list of [File]`s for the returned paths.
|
|
||||||
static Future<List<File>> getMultiFile({
|
|
||||||
FileType type = FileType.any,
|
|
||||||
List<String> allowedExtensions,
|
|
||||||
Function(FilePickerStatus) onFileLoading,
|
|
||||||
bool allowCompression,
|
|
||||||
}) async {
|
|
||||||
final Map<String, String> paths = await _filePickerPlatform.getFiles(
|
|
||||||
type: type,
|
|
||||||
allowMultiple: true,
|
|
||||||
allowedExtensions: allowedExtensions,
|
|
||||||
onFileLoading: onFileLoading,
|
|
||||||
allowCompression: allowCompression,
|
|
||||||
);
|
|
||||||
|
|
||||||
return paths != null && paths.isNotEmpty
|
|
||||||
? paths.values.map((path) => File(path)).toList()
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
static Future<String> getDirectoryPath() async {
|
|
||||||
return _filePickerPlatform.getDirectoryPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asks the underlying platform to remove any temporary files created by this plugin.
|
|
||||||
///
|
|
||||||
/// This typically relates to cached files that are stored in the cache directory of
|
|
||||||
/// 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.
|
|
||||||
static Future<bool> clearTemporaryFiles() async {
|
|
||||||
return _filePickerPlatform.clearTemporaryFiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
import 'file_picker_io.dart';
|
||||||
|
import 'file_picker_result.dart';
|
||||||
|
|
||||||
|
enum FileType {
|
||||||
|
any,
|
||||||
|
media,
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
audio,
|
||||||
|
custom,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FilePickerStatus {
|
||||||
|
picking,
|
||||||
|
done,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The interface that implementations of file_picker must implement.
|
||||||
|
///
|
||||||
|
/// Platform implementations should extend this class rather than implement it as `file_picker`
|
||||||
|
/// 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
|
||||||
|
/// [FilePicker] methods.
|
||||||
|
abstract class FilePicker extends PlatformInterface {
|
||||||
|
FilePicker() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static FilePicker _instance = FilePickerIO();
|
||||||
|
|
||||||
|
static FilePicker get instance => _instance;
|
||||||
|
|
||||||
|
static set instance(FilePicker instance) {
|
||||||
|
PlatformInterface.verifyToken(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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`].).
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
Future<FilePickerResult> pickFiles({
|
||||||
|
FileType type = FileType.any,
|
||||||
|
List<String> allowedExtensions,
|
||||||
|
Function(FilePickerStatus) onFileLoading,
|
||||||
|
bool allowCompression,
|
||||||
|
bool allowMultiple = false,
|
||||||
|
}) async =>
|
||||||
|
throw UnimplementedError('pickFiles() has not been implemented.');
|
||||||
|
|
||||||
|
/// Asks the underlying platform to remove any temporary files created by this plugin.
|
||||||
|
///
|
||||||
|
/// This typically relates to cached files that are stored in the cache directory of
|
||||||
|
/// 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.');
|
||||||
|
|
||||||
|
/// 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<PlatformFile> getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.');
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:file_picker/src/platform_file.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
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');
|
||||||
|
|
||||||
|
/// An implementation of [FilePicker] that uses method channels.
|
||||||
|
class FilePickerIO extends FilePicker {
|
||||||
|
static const String _tag = 'MethodChannelFilePicker';
|
||||||
|
static StreamSubscription _eventSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FilePickerResult> pickFiles({
|
||||||
|
FileType type = FileType.any,
|
||||||
|
List<String> allowedExtensions,
|
||||||
|
Function(FilePickerStatus) onFileLoading,
|
||||||
|
bool allowCompression,
|
||||||
|
bool allowMultiple = false,
|
||||||
|
}) =>
|
||||||
|
_getPath(type, allowMultiple, allowCompression, allowedExtensions, onFileLoading);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> clearTemporaryFiles() async => _channel.invokeMethod<bool>('clear');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PlatformFile> getDirectoryPath() async {
|
||||||
|
try {
|
||||||
|
String result = await _channel.invokeMethod('dir', {});
|
||||||
|
if (result != null) {
|
||||||
|
return PlatformFile(path: result, isDirectory: true);
|
||||||
|
}
|
||||||
|
} on PlatformException catch (ex) {
|
||||||
|
if (ex.code == "unknown_path") {
|
||||||
|
print(
|
||||||
|
'[$_tag] Could not resolve directory path. Maybe it\'s a protected one or unsupported (such as Downloads folder). If you are on Android, make sure that you are on SDK 21 or above.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FilePickerResult> _getPath(
|
||||||
|
FileType fileType,
|
||||||
|
bool allowMultipleSelection,
|
||||||
|
bool allowCompression,
|
||||||
|
List<String> allowedExtensions,
|
||||||
|
Function(FilePickerStatus) onFileLoading,
|
||||||
|
) 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.');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
_eventSubscription?.cancel();
|
||||||
|
if (onFileLoading != null) {
|
||||||
|
_eventSubscription = _eventChannel.receiveBroadcastStream().listen(
|
||||||
|
(data) => onFileLoading((data as bool) ? FilePickerStatus.picking : FilePickerStatus.done),
|
||||||
|
onError: (error) => throw Exception(error),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> result = await _channel.invokeListMethod(type, {
|
||||||
|
'allowMultipleSelection': allowMultipleSelection,
|
||||||
|
'allowedExtensions': allowedExtensions,
|
||||||
|
'allowCompression': allowCompression,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilePickerResult(result.map((file) => PlatformFile(name: file.split('/').last, path: 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');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:file_picker/src/platform_file.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class FilePickerResult {
|
||||||
|
const FilePickerResult(this.files);
|
||||||
|
|
||||||
|
/// Picked files.
|
||||||
|
final List<PlatformFile> files;
|
||||||
|
|
||||||
|
/// If this pick contains only a single resource.
|
||||||
|
bool get isSinglePick => files.length == 1;
|
||||||
|
|
||||||
|
/// The length of picked files.
|
||||||
|
int get count => files.length;
|
||||||
|
|
||||||
|
/// A `List<String>` containing all paths from picked files.
|
||||||
|
///
|
||||||
|
/// This may or not be available and will typically reference cached copies of
|
||||||
|
/// 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();
|
||||||
|
|
||||||
|
/// A `List<String>` containing all names from picked files with its extensions.
|
||||||
|
List<String> get names => files.map((file) => file.name).toList();
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:html' as html;
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||||
|
|
||||||
|
import 'file_picker_result.dart';
|
||||||
|
import 'platform_file.dart';
|
||||||
|
|
||||||
|
class FilePickerWeb extends FilePicker {
|
||||||
|
FilePickerWeb._();
|
||||||
|
static final FilePickerWeb platform = FilePickerWeb._();
|
||||||
|
|
||||||
|
static void registerWith(Registrar registrar) {
|
||||||
|
FilePicker.instance = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FilePickerResult> pickFiles({
|
||||||
|
FileType type = FileType.any,
|
||||||
|
List<String> allowedExtensions,
|
||||||
|
bool allowMultiple = false,
|
||||||
|
Function(FilePickerStatus) onFileLoading,
|
||||||
|
bool allowCompression,
|
||||||
|
}) async {
|
||||||
|
final Completer<List<PlatformFile>> filesCompleter = Completer<List<PlatformFile>>();
|
||||||
|
|
||||||
|
String accept = _fileType(type, allowedExtensions);
|
||||||
|
html.InputElement uploadInput = html.FileUploadInputElement();
|
||||||
|
uploadInput.draggable = true;
|
||||||
|
uploadInput.multiple = allowMultiple;
|
||||||
|
uploadInput.accept = accept;
|
||||||
|
uploadInput.click();
|
||||||
|
|
||||||
|
uploadInput.onChange.listen((e) {
|
||||||
|
final files = uploadInput.files;
|
||||||
|
final reader = html.FileReader();
|
||||||
|
|
||||||
|
List<PlatformFile> pickedFiles = [];
|
||||||
|
|
||||||
|
reader.onLoadEnd.listen((e) {
|
||||||
|
pickedFiles.add(
|
||||||
|
PlatformFile(
|
||||||
|
name: uploadInput.value.replaceAll('\\', '/'),
|
||||||
|
bytes: Base64Decoder().convert(reader.result.toString().split(",").last),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedFiles.length >= files.length) {
|
||||||
|
filesCompleter.complete(pickedFiles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
files.forEach((element) {
|
||||||
|
reader.readAsDataUrl(element);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return FilePickerResult(await filesCompleter.future);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _fileType(FileType type, List<String> allowedExtensions) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType.any:
|
||||||
|
return '';
|
||||||
|
|
||||||
|
case FileType.audio:
|
||||||
|
return 'audio/*';
|
||||||
|
|
||||||
|
case FileType.image:
|
||||||
|
return 'image/*';
|
||||||
|
|
||||||
|
case FileType.video:
|
||||||
|
return 'video/*';
|
||||||
|
|
||||||
|
case FileType.media:
|
||||||
|
return 'video/*|image/*';
|
||||||
|
|
||||||
|
case FileType.custom:
|
||||||
|
return allowedExtensions.fold('', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class PlatformFile {
|
||||||
|
PlatformFile({
|
||||||
|
this.path,
|
||||||
|
this.uri,
|
||||||
|
this.name,
|
||||||
|
this.bytes,
|
||||||
|
this.isDirectory = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The absolute path for this file instance.
|
||||||
|
///
|
||||||
|
/// Typically whis will reflect a copy cached file and not the original source,
|
||||||
|
/// also, it's not guaranteed that this path is always available as some files
|
||||||
|
/// can be protected by OS.
|
||||||
|
///
|
||||||
|
/// Available on IO only. On Web is always `null`.
|
||||||
|
final String path;
|
||||||
|
|
||||||
|
/// The URI (Universal Resource Identifier) for this file.
|
||||||
|
///
|
||||||
|
/// This is the original file resource identifier and can be used to
|
||||||
|
/// manipulate the original file (read, write, delete).
|
||||||
|
///
|
||||||
|
/// Available on IO only. On Web is always `null`.
|
||||||
|
final String uri;
|
||||||
|
|
||||||
|
/// File name including its extension.
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Byte data for this file.
|
||||||
|
final Uint8List bytes;
|
||||||
|
|
||||||
|
/// Whether this file references a directory or not.
|
||||||
|
final bool isDirectory;
|
||||||
|
|
||||||
|
/// File extension for this file.
|
||||||
|
String get extension => name.split('/').last;
|
||||||
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
name: file_picker
|
name: file_picker
|
||||||
description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
|
description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
|
||||||
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
|
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
|
||||||
version: 1.13.3
|
version: 2.0.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_web_plugins:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
flutter_plugin_android_lifecycle: ^1.0.6
|
flutter_plugin_android_lifecycle: ^1.0.6
|
||||||
file_picker_platform_interface: ^1.3.1
|
plugin_platform_interface: ^1.0.1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0 <3.0.0"
|
sdk: ">=2.0.0 <3.0.0"
|
||||||
|
@ -22,4 +25,5 @@ flutter:
|
||||||
ios:
|
ios:
|
||||||
pluginClass: FilePickerPlugin
|
pluginClass: FilePickerPlugin
|
||||||
web:
|
web:
|
||||||
default_package: file_picker_web
|
pluginClass: FilePickerWeb
|
||||||
|
fileName: src/file_picker_web.dart
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
## [2.0.0] - Deprecates interface
|
||||||
|
|
||||||
|
Deprecates interface in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms.
|
||||||
|
|
||||||
## [1.3.1] - Rollback `allowCompression`
|
## [1.3.1] - Rollback `allowCompression`
|
||||||
|
|
||||||
Removes `allowCompression` from interface as it should only be used from `file_picker` (Android & iOS).
|
Removes `allowCompression` from interface as it should only be used from `file_picker` (Android & iOS).
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# MUST READ!
|
||||||
|
|
||||||
|
The interface is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms where an interface is integrated. This should be the one used as this package is not longer mantained.
|
||||||
|
|
||||||
# file_picker_platform_interface
|
# file_picker_platform_interface
|
||||||
|
|
||||||
A common platform interface for the [`file_picker`][1] plugin.
|
A common platform interface for the [`file_picker`][1] plugin.
|
||||||
|
@ -6,6 +10,7 @@ This interface allows platform-specific implementations of the `file_picker`
|
||||||
plugin, as well as the plugin itself, to ensure they are supporting the
|
plugin, as well as the plugin itself, to ensure they are supporting the
|
||||||
same interface.
|
same interface.
|
||||||
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
To implement a new platform-specific implementation of `file_picker`, extend
|
To implement a new platform-specific implementation of `file_picker`, extend
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: file_picker_platform_interface
|
name: file_picker_platform_interface
|
||||||
description: A common platform interface for the file_picker plugin that must be used to share commom features
|
description: A common platform interface for the file_picker plugin that must be used to share commom features
|
||||||
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker/file_picker_platform_interface
|
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker/file_picker_platform_interface
|
||||||
version: 1.3.1
|
version: 2.0.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.1.0 <3.0.0"
|
sdk: ">=2.1.0 <3.0.0"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
Deprecates plugin in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms.
|
||||||
|
|
||||||
## 1.0.2+1
|
## 1.0.2+1
|
||||||
|
|
||||||
Fix custom filter String creation.
|
Fix custom filter String creation.
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# MUST READ!
|
||||||
|
|
||||||
|
The web standalone plugin is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. This should be the one used as this package is not longer mantained.
|
||||||
|
|
||||||
# file_picker_web
|
# file_picker_web
|
||||||
|
|
||||||
The web implementation of [`file_picker`][1].
|
The web implementation of [`file_picker`][1].
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
name: file_picker_web
|
name: file_picker_web
|
||||||
description: Web platform implementation of file_picker. Provides a way to pick files with filter support for Web.
|
description: Web platform implementation of file_picker. Provides a way to pick files with filter support for Web.
|
||||||
homepage: https://github.com/miguelpruivo/flutter_file_picker/tree/master/file_picker_web
|
homepage: https://github.com/miguelpruivo/flutter_file_picker/tree/master/file_picker_web
|
||||||
version: 1.0.2+1
|
version: 2.0.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: ">=2.7.0 <3.0.0"
|
||||||
flutter: ">=1.10.0"
|
flutter: ">=1.10.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
file_picker_platform_interface: ^1.3.1
|
file_picker_platform_interface: ^2.0.0
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
|
|
Loading…
Reference in New Issue